从零手写实现 nginx-03-nginx 基于 Netty 实现

打印 上一主题 下一主题

主题 838|帖子 838|积分 2529

前言

各人好,我是老马。很高兴遇到你。
我们希望实现最简单的 http 服务信息,可以处理静态文件。
假如你想知道 servlet 如那边理的,可以参考我的另一个项目:
手写从零实现浅易版 tomcat minicat
netty 相关

假如你对 netty 不是很熟悉,可以读一下
Netty 权势巨子指南-01-BIO 案例
Netty 权势巨子指南-02-NIO 案例
Netty 权势巨子指南-03-AIO 案例
Netty 权势巨子指南-04-为什么选择 Netty?Netty 入门教程
手写 nginx 系列

假如你对 nginx 原理感爱好,可以阅读:
从零手写实现 nginx-01-为什么不能有 java 版本的 nginx?
从零手写实现 nginx-02-nginx 的核心本领
从零手写实现 nginx-03-nginx 基于 Netty 实现
从零手写实现 nginx-04-基于 netty http 出入参优化处理
从零手写实现 nginx-05-MIME类型(Multipurpose Internet Mail Extensions,多用途互联网邮件扩展类型)
从零手写实现 nginx-06-文件夹自动索引
从零手写实现 nginx-07-大文件下载
从零手写实现 nginx-08-范围查询
从零手写实现 nginx-09-文件压缩
从零手写实现 nginx-10-sendfile 零拷贝
从零手写实现 nginx-11-file+range 合并
从零手写实现 nginx-12-keep-alive 连接复用
从零手写实现 nginx-13-nginx.conf 配置文件先容
从零手写实现 nginx-14-nginx.conf 和 hocon 格式有关系吗?
从零手写实现 nginx-15-nginx.conf 如何通过 java 剖析处理?
从零手写实现 nginx-16-nginx 支持配置多个 server
根本实现

思路

上一节我们实现了基于 serverSocket 的处理监听,谁人性能究竟一般。
这一次我们一起通过 netty 对代码进行改造升级。
核心实现

启动类

INginxServer 接口不变,我们加一个 netty 的实现类。
这里针对 EventLoopGroup 的配置我们暂时利用默认值,后续可以考虑可以让用户自定义。
  1. /**
  2. * netty 实现
  3. *
  4. * @author 老马啸西风
  5. * @since 0.2.0
  6. */
  7. public class NginxServerNetty implements INginxServer {
  8.     private static final Log log = LogFactory.getLog(NginxServerNetty.class);
  9.     private NginxConfig nginxConfig;
  10.     @Override
  11.     public void init(NginxConfig nginxConfig) {
  12.         this.nginxConfig = nginxConfig;
  13.     }
  14.     @Override
  15.     public void start() {
  16.         // 服务器监听的端口号
  17.         String host = InnerNetUtil.getHost();
  18.         int port = nginxConfig.getHttpServerListen();
  19.         EventLoopGroup bossGroup = new NioEventLoopGroup();
  20.         //worker 线程池的数量默认为 CPU 核心数的两倍
  21.         EventLoopGroup workerGroup = new NioEventLoopGroup();
  22.         try {
  23.             ServerBootstrap serverBootstrap = new ServerBootstrap();
  24.             serverBootstrap.group(bossGroup, workerGroup)
  25.                     .channel(NioServerSocketChannel.class)
  26.                     .childHandler(new ChannelInitializer<SocketChannel>() {
  27.                         @Override
  28.                         protected void initChannel(SocketChannel ch) throws Exception {
  29.                             ch.pipeline().addLast(new NginxNettyServerHandler(nginxConfig));
  30.                         }
  31.                     })
  32.                     .option(ChannelOption.SO_BACKLOG, 128)
  33.                     .childOption(ChannelOption.SO_KEEPALIVE, true);
  34.             // Bind and start to accept incoming connections.
  35.             ChannelFuture future = serverBootstrap.bind(port).sync();
  36.             log.info("[Nginx4j] listen on http://{}:{}", host, port);
  37.             // Wait until the server socket is closed.
  38.             future.channel().closeFuture().sync();
  39.         } catch (InterruptedException e) {
  40.             log.error("[Nginx4j] start meet ex", e);
  41.             throw new Nginx4jException(e);
  42.         } finally {
  43.             workerGroup.shutdownGracefully();
  44.             bossGroup.shutdownGracefully();
  45.             log.info("[Nginx4j] shutdownGracefully", host, port);
  46.         }
  47.     }
  48. }
复制代码
处理类

核心的处理逻辑都在 NginxNettyServerHandler 这个类。
这里主要做 3 件事

  • 剖析哀求类
  • 根据哀求获取对应的内容
  • 返回相应内容
  1. /**
  2. * netty 处理类
  3. * @author 老马啸西风
  4. * @since 0.2.0
  5. */
  6. public class NginxNettyServerHandler extends ChannelInboundHandlerAdapter {
  7.     private static final Log logger = LogFactory.getLog(NginxNettyServerHandler.class);
  8.     private final NginxConfig nginxConfig;
  9.     public NginxNettyServerHandler(NginxConfig nginxConfig) {
  10.         this.nginxConfig = nginxConfig;
  11.     }
  12.     @Override
  13.     public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
  14.         ByteBuf buf = (ByteBuf) msg;
  15.         byte[] bytes = new byte[buf.readableBytes()];
  16.         buf.readBytes(bytes);
  17.         String requestString = new String(bytes, nginxConfig.getCharset());
  18.         logger.info("[Nginx] channelRead requestString={}", requestString);
  19.         // 请求体
  20.         final NginxRequestConvertor requestConvertor = nginxConfig.getNginxRequestConvertor();
  21.         NginxRequestInfoBo nginxRequestInfoBo = requestConvertor.convert(requestString, nginxConfig);
  22.         // 分发
  23.         final NginxRequestDispatch requestDispatch = nginxConfig.getNginxRequestDispatch();
  24.         String respText = requestDispatch.dispatch(nginxRequestInfoBo, nginxConfig);
  25.         // 结果响应
  26.         ByteBuf responseBuf = Unpooled.copiedBuffer(respText.getBytes());
  27.         ctx.writeAndFlush(responseBuf)
  28.                 .addListener(ChannelFutureListener.CLOSE); // Close the channel after sending the response
  29.         logger.info("[Nginx] channelRead writeAndFlush DONE");
  30.     }
  31.     @Override
  32.     public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
  33.         ctx.flush();
  34.     }
  35.     @Override
  36.     public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
  37.         logger.error("[Nginx] exceptionCaught", cause);
  38.         ctx.close();
  39.     }
  40. }
复制代码
其中哀求的剖析为对象,便于后续开发中利用。
分发处理只是加了一层抽象,整体实现和上一节类似。
感爱好可以阅读源码
小结

本节我们利用 netty 大幅度提升一下相应性能。
到这里我们实现了一个简单的 http 服务器,当然这是远远不够的。
后续我们会继承一起实现更多的 nginx 特性。
我是老马,期待与你的下次重逢。
开源地址

为了便于各人学习,已经将 nginx 开源
https://github.com/houbb/nginx4j

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

大连全瓷种植牙齿制作中心

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表