• 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html
  • 极客专栏正式上线!欢迎访问 https://www.jikewenku.com/topic.html

基于Netty的简单HTTP服务例子

技术杂谈 勤劳的小蚂蚁 4个月前 (01-10) 101次浏览 已收录 0个评论 扫描二维码


例子是基于 Netty 4.1.25.Final + Spring Boot 2 + JDK 1.8 + Maven

demo:

AppInitializer.java

  1. package io.github.emacsist.netty.httpdemo.config;
  2. import io.github.emacsist.netty.httpdemo.handler.AppHandler;
  3. import io.netty.channel.Channel;
  4. import io.netty.channel.ChannelHandler;
  5. import io.netty.channel.ChannelInitializer;
  6. import io.netty.channel.ChannelPipeline;
  7. import io.netty.handler.codec.http.HttpObjectAggregator;
  8. import io.netty.handler.codec.http.HttpServerCodec;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.stereotype.Component;
  11. /**
  12. * @author emacsist
  13. */
  14. @Component
  15. @ChannelHandler.Sharable
  16. publicclassAppInitializerextendsChannelInitializer{
  17.    privatestaticfinalint MB =1024*1024;
  18.    @Autowired
  19.    privateAppHandler appHandler;
  20.    @Override
  21.    protectedvoid initChannel(finalChannel channel){
  22.        finalChannelPipeline p = channel.pipeline();
  23.        p.addLast(newHttpServerCodec());
  24.        p.addLast(newHttpObjectAggregator(1* MB));
  25.        p.addLast(appHandler);
  26.    }
  27. }

AppHandler.java

  1. package io.github.emacsist.netty.httpdemo.handler;
  2. import io.netty.buffer.Unpooled;
  3. import io.netty.channel.ChannelHandler;
  4. import io.netty.channel.ChannelHandlerContext;
  5. import io.netty.channel.SimpleChannelInboundHandler;
  6. import io.netty.handler.codec.http.*;
  7. import io.netty.util.AsciiString;
  8. import org.slf4j.Logger;
  9. import org.slf4j.LoggerFactory;
  10. import org.springframework.stereotype.Component;
  11. /**
  12. * @author emacsist
  13. */
  14. @Component
  15. @ChannelHandler.Sharable
  16. publicclassAppHandlerextendsSimpleChannelInboundHandler<FullHttpRequest>{
  17.    privatestaticfinalLogger log =LoggerFactory.getLogger(AppHandler.class);
  18.    privatestaticfinalAsciiString CONTENT_TYPE =AsciiString.cached("Content-Type");
  19.    privatestaticfinalAsciiString TEXT_PLAIN =AsciiString.cached("text/plain; charset=utf-8");
  20.    privatestaticfinalAsciiString CONTENT_LENGTH =AsciiString.cached("Content-Length");
  21.    @Override
  22.    protectedvoid channelRead0(finalChannelHandlerContext ctx,finalFullHttpRequest fullHttpRequest){
  23.        finalString uri = fullHttpRequest.uri();
  24.        finalQueryStringDecoder queryStringDecoder =newQueryStringDecoder(uri);
  25.        finalString requestPath = queryStringDecoder.path();
  26.        String body ="";
  27.        switch(requestPath){
  28.            case"/hello":
  29.                log.info("in hello => {}", queryStringDecoder.parameters());
  30.                //请自行检测参数, 这里假设  /hello 是会带上 ?name=world 类似这参数值的
  31.                body ="Hello "+ queryStringDecoder.parameters().get("name").get(0);
  32.                break;
  33.            case"/netty":
  34.                log.info("in netty => {}", queryStringDecoder.parameters());
  35.                body ="Hello Netty.";
  36.                break;
  37.            default:
  38.                break;
  39.        }
  40.        finalDefaultFullHttpResponse defaultFullHttpResponse =newDefaultFullHttpResponse(HttpVersion.HTTP_1_1,HttpResponseStatus.OK,Unpooled.copiedBuffer(body.getBytes()));
  41.        defaultFullHttpResponse.headers().set(CONTENT_TYPE, TEXT_PLAIN);
  42.        defaultFullHttpResponse.headers().set(CONTENT_LENGTH, defaultFullHttpResponse.content().readableBytes());
  43.        ctx.write(defaultFullHttpResponse);
  44.    }
  45.    @Override
  46.    publicvoid channelReadComplete(finalChannelHandlerContext ctx){
  47.        ctx.flush();
  48.    }
  49.    @Override
  50.    publicvoid exceptionCaught(finalChannelHandlerContext ctx,finalThrowable cause){
  51.        log.error(cause.getMessage(), cause);
  52.        ctx.close();
  53.    }
  54. }

HttpDemoApplication.java

  1. package io.github.emacsist.netty.httpdemo;
  2. import io.github.emacsist.netty.httpdemo.config.AppInitializer;
  3. import io.netty.bootstrap.ServerBootstrap;
  4. import io.netty.channel.Channel;
  5. import io.netty.channel.ChannelOption;
  6. import io.netty.channel.EventLoopGroup;
  7. import io.netty.channel.nio.NioEventLoopGroup;
  8. import io.netty.channel.socket.nio.NioServerSocketChannel;
  9. import io.netty.handler.logging.LogLevel;
  10. import io.netty.handler.logging.LoggingHandler;
  11. import org.springframework.beans.factory.annotation.Autowired;
  12. import org.springframework.beans.factory.annotation.Value;
  13. import org.springframework.boot.CommandLineRunner;
  14. import org.springframework.boot.SpringApplication;
  15. import org.springframework.boot.autoconfigure.SpringBootApplication;
  16. /**
  17. * @author emacsist
  18. */
  19. @SpringBootApplication
  20. publicclassHttpDemoApplicationimplementsCommandLineRunner{
  21.    @Value("${server.port}")
  22.    privateint port;
  23.    privatestaticfinalint KB =1024;
  24.    @Autowired
  25.    privateAppInitializer appInitializer;
  26.    publicstaticvoid main(finalString[] args){
  27.        SpringApplication.run(HttpDemoApplication.class, args);
  28.    }
  29.    @Override
  30.    publicvoid run(finalString... args){
  31.        finalServerBootstrap serverBootstrap =newServerBootstrap();
  32.        finalEventLoopGroup master =newNioEventLoopGroup();
  33.        finalEventLoopGroup worker =newNioEventLoopGroup();
  34.        try{
  35.            serverBootstrap
  36.                    .option(ChannelOption.SO_BACKLOG,4* KB)
  37.                    .option(ChannelOption.TCP_NODELAY,true)
  38.                    .group(master, worker)
  39.                    .channel(NioServerSocketChannel.class)
  40.                    .handler(newLoggingHandler(LogLevel.DEBUG))
  41.                    .childHandler(appInitializer);
  42.            finalChannel ch = serverBootstrap.bind(port).sync().channel();
  43.            System.out.println("start app ok...");
  44.            ch.closeFuture().sync();
  45.        }catch(finalInterruptedException e){
  46.            //ignore
  47.        }finally{
  48.            master.shutdownGracefully();
  49.            worker.shutdownGracefully();
  50.            System.out.println("stop app ok...");
  51.        }
  52.    }
  53. }

注意

编写自己的HTTP业务时, 请记住千万不要阻塞EventLoop线程~, 不然会导致 Netty 的性能急剧下降.
当要处理一些阻塞操作时, 请用其他的线程池来处理。

Netty中一般有两种方式:

ChannelPipeline 指定 executor:

例如这样子:
  1. /**
  2. * @author emacsist
  3. */
  4. @Component
  5. @ChannelHandler.Sharable
  6. publicclassAppInitializerextendsChannelInitializer{
  7.    privatestaticfinalint MB =1024*1024;
  8.    @Autowired
  9.    privateAppHandler appHandler;
  10.    privatestaticfinalDefaultEventExecutorGroup executorGroup =newDefaultEventExecutorGroup(Runtime.getRuntime().availableProcessors()*2);
  11.    @Override
  12.    protectedvoid initChannel(finalChannel channel){
  13.        finalChannelPipeline p = channel.pipeline();
  14.        p.addLast(newHttpServerCodec());
  15.        p.addLast(newHttpObjectAggregator(1* MB));
  16.        p.addLast(executorGroup, appHandler);
  17.    }
  18. }

创建一个全局的 ExecutorGroup , 自己手工创建任务

  1. executorGroup.submit(()-> maxService.dealClick(queryStringDecoder));
这个是公司项目代码的一个片段, 要将HTTP参数 接收-> 解密 -> 进队和处理 Redis, 这些都是阻塞操作来的, 所以这里创建了一个异步任务来处理这些耗时的操作, 以免阻塞 EventLoop 线程.

性能

经过测试, 还是手工这种按需处理异步任务的性能更高点. 差不多高1倍. 当然, 具体问题要具体分析, 总之就是不要阻塞 EventLoop 线程就好, 毕竟Netty高性能的核心, 就在于不要阻塞 EventLoop.


丨极客文库, 版权所有丨如未注明 , 均为原创丨
本网站采用知识共享署名-非商业性使用-相同方式共享 3.0 中国大陆许可协议进行授权
转载请注明原文链接:基于Netty的简单HTTP服务例子
喜欢 (0)
[247507792@qq.com]
分享 (0)
勤劳的小蚂蚁
关于作者:
温馨提示:本文来源于网络,转载文章皆标明了出处,如果您发现侵权文章,请及时向站长反馈删除。

您必须 登录 才能发表评论!

  • 精品技术教程
  • 编程资源分享
  • 问答交流社区
  • 极客文库知识库

客服QQ


QQ:2248886839


工作时间:09:00-23:00