public class HttpServer {
private static final Logger logger = LogManager.getLogger("default");
public static void main(String[] args) throws InterruptedException, IOException {
try {
MetricKafkaHttpServer server = new MetricKafkaHttpServer();
server.run();
} finally {
logger.error("Server shutting down.");
}
}
public void run() throws InterruptedException {
EventLoopGroup bossGroup = new NioEventLoopGroup();
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
ServerBootstrap b = new ServerBootstrap();
b.group(workerGroup, bossGroup)
.channel(NioServerSocketChannel.class)
.childHandler(new ChannelInitializer<SocketChannel>() {
@Override
public void initChannel(SocketChannel ch) throws Exception {
ch.pipeline()
.addLast("inboundDecoder", new HttpRequestDecoder())
.addLast("outboundEncoder", new HttpResponseEncoder()) // 1
.addLast("inboundAggregator", new HttpObjectAggregator(50 * 1024 * 1024))
.addLast("inboundHandler", new HttpRequestHandler());
}
})
//TODO options
.option(ChannelOption.SO_BACKLOG, 1024)
.childOption(ChannelOption.SO_KEEPALIVE, true); // 2
ChannelFuture f = b.bind(ServerProperties.PORT).sync();
logger.info("Server started in port " + ServerProperties.PORT + ".");
f.channel().closeFuture().sync();
} finally {
workerGroup.shutdownGracefully();
bossGroup.shutdownGracefully();
}
}
}
1. 一个channel,inbound handler由注册顺序从上往下排列,outbound handler由注册顺序从下往上排列。
2.server端接收请求,没接收一个请求生成一个子channel,处理这个request
public class HttpRequestHandler extends SimpleChannelInboundHandler<FullHttpRequest> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FullHttpRequest msg) throws UnsupportedEncodingException, IOException {
//do something with msg, for example
ByteBuf m = msg.content();
StringBuilder sb = new StringBuilder();
while (m.isReadable()) {
sb.append((char) m.readByte());
}
String content = sb.toString();
//do something with content
DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, Unpooled.wrappedBuffer("some_body_content".getBytes()));
response.headers().add(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
response.headers().add(HttpHeaderNames.CONTENT_TYPE, "text/plain");
ctx.writeAndFlush(response);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
logger.error("Exception caught in netty channel.", cause); ctx.close();
}
}
注意点:netty默认使用ByteBuf存储request和response,它使用直接内存,不在GC堆内,所以不会被自动回收,需要手动回收。request和response一般继承ReferenceCounted接口,维持一个refCnt,可以通过msg.release(),msg.retain()修改,也可以用ReferenceCountUtil.release(msg)来释放。SimpleChannelInboundHandler在ChannelRead0方法退出时会自动释放未释放的资源,而ChannelInboundHandlerAdapter不会,需要手动释放,如不释放,会造成内存泄露