用netty4實現http伺服器
阿新 • • 發佈:2019-01-02
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不會,需要手動釋放,如不釋放,會造成記憶體洩露