项目作者: wangzihaogithub

项目描述 :
一个基于netty实现的servlet容器, 可以快速与springboot集成
高级语言: Java
项目地址: git://github.com/wangzihaogithub/netty-servlet.git


netty-servlet

简介

  • 基于Netty实现的servlet容器, 可以替代tomcat或jetty. (jdk1.8+)
  • 解决Netty在EventLoop线程里写繁忙后不返回数据的BUG.
  • 解决Netty的Http遇到请求参数携带%号会报错的问题.
  • 从19年开始,一直跑在作者公司某产线的线上环境运行.

优势

  • 1.针对spring项目# 可以替代tomcat或jetty. 导包后一个@EnableNettyEmbedded注解即用.

  • 2.针对非spring项目# 本项目可以只依赖一个netty(举个使用servlet的例子)

  1. StartupServer server = new StartupServer(80);
  2. ServletContext servletContext = new ServletContext();
  3. servletContext.setDocBase("D://static", "/webapp");
  4. servletContext.addServlet("myServlet", new MyHttpServlet()).addMapping("/test");
  5. server.addProtocol(new HttpServletProtocol(servletContext));
  6. server.start();
  • 3.支持# http请求聚合, 然后用 select * from id in (httpRequestList).
  1. 示例代码:com.github.netty.http.example.HttpGroupByApiController.java
  • 4.支持# h2c (注: 不建议用h2,h2c当rpc, 原因在文档最底部有说明)

  • 5.支持# 异步零拷贝。sendFile, mmap.

    1. 示例代码:com.github.netty.http.example.HttpZeroCopyController.java
    2. ((NettyOutputStream)servletResponse.getOutputStream()).write(new File("c://123.txt"));
    3. ((NettyOutputStream)servletResponse.getOutputStream()).write(MappedByteBuffer);
    4. com.github.netty.protocol.servlet.DefaultServlet#sendFile
  • 6.性能# HttpServlet比tomcat的NIO2高出25%/TPS。

    1. 1. Netty的池化内存,减少了GCCPU的消耗
    2. 2. TomcatNIO2, 注册OP_WRITE后,tomcat会阻塞用户线程等待, 并没有释放线程.
    3. 3. tomcat不同,支持两种IO模型,可供用户选择

作者邮箱 : 842156727@qq.com

github地址 : https://github.com/wangzihaogithub/netty-servlet


优势:

  1. 1.支持异步http请求聚合, 然后用 select * from id in (httpRequestList).
  2. 示例:https://github.com/wangzihaogithub/spring-boot-protocol# com.github.netty.http.example.HttpGroupByApiController.java
  3. 2.支持异步零拷贝。sendFile, mmap.
  4. 示例:https://github.com/wangzihaogithub/spring-boot-protocol# com.github.netty.http.example.HttpZeroCopyController.java

测试信息 : 笔记本[4g内存,4代I5(4核cpu) ], JVM参数 : -Xms300m -Xmn300m -Xmx500m -XX:+PrintGCDetails

1.单体应用,连接复用qps=10000+ , tomcat=8000+

2.单体应用,连接不复用qps达到5100+, tomcat=4600+


使用方法 - 添加依赖

如果需要集成spring就用这个 Maven Central

  1. <!-- https://github.com/wangzihaogithub/spring-boot-protocol -->
  2. <!-- https://mvnrepository.com/artifact/com.github.wangzihaogithub/spring-boot-protocol -->
  3. <dependency>
  4. <groupId>com.github.wangzihaogithub</groupId>
  5. <artifactId>spring-boot-protocol</artifactId>
  6. <version>2.3.23</version>
  7. </dependency>

如果不需要集成spring就用这个 Maven Central

  1. <!-- https://github.com/wangzihaogithub/netty-servlet -->
  2. <!-- https://mvnrepository.com/artifact/com.github.wangzihaogithub/netty-servlet -->
  3. <dependency>
  4. <groupId>com.github.wangzihaogithub</groupId>
  5. <artifactId>netty-servlet</artifactId>
  6. <version>2.3.23</version>
  7. </dependency>

3.写个main方法,启动服务

  1. public class HttpBootstrap {
  2. public static void main(String[] args) {
  3. StartupServer server = new StartupServer(80);
  4. server.addProtocol(newHttpProtocol());
  5. server.start();
  6. }
  7. private static HttpServletProtocol newHttpProtocol() {
  8. ServletContext servletContext = new ServletContext();
  9. servletContext.setDocBase("D://demo", "/webapp"); // 静态资源文件夹(非必填,默认用临时目录)
  10. servletContext.addServlet("myHttpServlet", new MyHttpServlet())
  11. .addMapping("/test");
  12. return new HttpServletProtocol(servletContext);
  13. }
  14. }

4.打开浏览器

  1. 访问http://localhost:8080/test
  2. 页面显示 hi! doGet 测试成功!

补充: Servlet的概念

1.Servlet是一个接受Request实体类, 并填充Response实体类的过程. 整个过程是为了处理一次http交互, 这个过程分为5种类型( 参考DispatcherType )

注: 一次http交互, 可能会经历多次执行类型;

2.servlet中大部分类型, 都会遵守这样的流程: filterChain(N Filter) -> Servlet(1);

通过ServletRequest#getDispatcherType可以获得当前的执行类型.

3.执行类型逻辑如下

  1. public enum javax.servlet.DispatcherType {
  2. FORWARD (
  3. 执行: 根据urlname找到servlet, 并执行filterChain(N Filter) -> Servlet(1)
  4. 触发: 调用RequestDispatcher#forward
  5. 特性: 可以对request,Response进行任何操作
  6. INCLUDE (
  7. 执行: 根据urlname找到servlet, 并执行 filterChain(N Filter) -> Servlet(1)
  8. 触发: 调用RequestDispatcher#include
  9. 特性: 不能修改Responseheader, status code , 重置body. 只能写入body.
  10. REQUEST(
  11. 执行: 根据url找到servlet, 并执行 filterChain(N Filter) -> Servlet(1)
  12. 触发: 收到客户端的请求后
  13. 特性: 可以对request,Response进行任何操作 (正常的流程)
  14. ASYNC(
  15. 执行: 返回AsyncContext(本质是个装有tcp长连接的实体类)
  16. 触发: 调用ServletRequest#startAsync
  17. 特性: 无阻塞并同时释放了当前线程, 不会关闭tcp连接, 并且返回AsyncContext,
  18. 用户可以将AsyncContext装在集合中, 在定时任务或者单线程中操作AsyncContext同时批量处理大量的请求),
  19. 如果一直不调用AsyncContext#complete, 则客户端阻塞(如果不是异步客户端), 服务端非阻塞.
  20. ERROR(
  21. 执行: 根据Responsestatus code Exception url找到servlet, 不执行Filter-> 执行Servlet(1)
  22. 触发: 出现连Filter都没有捕获的异常, ErrorPageManager#handleErrorPage. 注: spring是个Servlet, Servlet被Filter包裹
  23. 特性: 当的filter或者servlet都没有捕获异常, 那么会转发到错误页servlet去构造时错误页面的响应
  24. 备注: 当进入DispatcherType 执行流程后, 会被容器的try,catch代码包裹.
  25. }

4.注意: 重定向(sendRedirect)的跳转是由客户端实现的, 并不由Servlet实现.
Servlet只负责设置Response的status为302 与设置header的Location字段.