1. 程式人生 > >用netty4實現http伺服器

用netty4實現http伺服器

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不會,需要手動釋放,如不釋放,會造成記憶體洩露