高性能服务器线程模型演进 Tomcat Netty Vert.x

Hello World

  • 让我们从最简单的单线程阻塞模型开始
ServerSocket server = new ServerSocket(8080);
    
Socket socket = server.accept();

InputStream inputStream = socket.getInputStream();

byte[] bytes = new byte[1024];
int len;
StringBuilder sb = new StringBuilder();
while ((len = inputStream.read(bytes)) != -1) {
    sb.append(new String(bytes, 0, len,"UTF-8"));
}

System.out.println("get message from client: " + sb);

OutputStream outputStream = socket.getOutputStream();
outputStream.write("Hello World".getBytes("UTF-8"));


outputStream.close();
inputStream.close();
socket.close();
server.close();
  • 这个初始版本只能单线程处理,其中的IO会堵塞,性能很差


    单线程.jpg

tomcat

  • tomcat是我们比较熟悉的传统web容器。提供了多线程的线程模型和NIO多路复用
    • 用一个Acceptor线程统一处理连接
    • 连上后交给Poller处理底层协议和一般性的IO,Poller线程数为CPU个数
    • Poller线程处理完底层的协议之后把servlet相关的请求交给worker处理, worker线程数默认是200个
tomcat2.jpg
  • 但是tomcat的异步处理只是占一部分, 并不彻底
    tomcat io 对比
    tomcat_io.jpg
  • 另外servlet模型本身对异步的支持到3.1才开始。按照我的理解servlet3.1本质上是提供了脱离tomcat线程的回调语法。[这篇文件写得极好](https://www.cnblogs.com/mianteno/p/10780257.html)

netty

  • netty提供了更高效率的线程模型。当然他也使用了DirectBuffer, JCTools的高性能无锁队列。这里只介绍线程模型:
netty.jpg
  • EventLoop类似于tomcat中的Poller,主要处理连接之外的全部IO。线程数是CPU*2
  • 另外Eventloop也会处理协议和业务回调。参考源码:NIOEventLoop#run
        final long ioStartTime = System.nanoTime();
        try {
               processSelectedKeys(); // 处理IO
        } finally {
               // Ensure we always run tasks.
               final long ioTime = System.nanoTime() - ioStartTime;
               // 用相同的时间处理回调业务
               ranTasks = runAllTasks(ioTime * (100 - ioRatio) / ioRatio);                         
        }
  • IO处理和任务处理的默认比例是1:1
  • Netty这样做有几个好处
    • 使用较少的线程意味着减少上下文切换
    • 使用较少内存,java每个线程栈默认1M开销
    • 一个EventLoop绑定多个Channel和对应的业务处理。从channel的角度看是单线程的,不存在线程切换,不需要处理并发问题。
  • 当然Netty的线程处理有个大前提。不能block Eventloop!!!
  • 但是传统的业务处理都是阻塞型的,必须开另外的线程池处理,这就和tomcat比优势不明显了

vert.x

  • vert.x是对netty的高级别封装,其核心还是EventLoop
  • 他通过全异步的方式使得IO全异步处理而通过少量线程性能最大化
vertx.jpg
  • 这个例子里通过Vert.x的EventLoop接管http/redis的IO和回调
  • 理论上所有的代码都可以异步,vert.x提供了大量的常用框架的异步IO适配。
  • 对于不能或不方便的阻塞代码, 通过worker线程池处理,幷提供异步回调接口(例子中的mysql调用)

为什么netty/nginx之类的高性能服务器不用AIO呢?

  • LinuxAIO不支持Socket
  • NettyIssue:为何不用AIO

你可能感兴趣的:(高性能服务器线程模型演进 Tomcat Netty Vert.x)