1.操作系统主流的线程模型

https://en.wikipedia.org/wiki/Thread_(computing))

二.高性能的TCP服务的线程模型是什么 - 图1

1:1 线程模型

  • 一个用户线程对应一个内核线程

    N:1 线程模型

  • N个用户线程对应一个内核线程

    M:N线程模型

  • M个用户线程对应N个内核线程

  • golang的goroutine 就是这种模型

    2.不同线程模型如何编写高性能TCP服务

    适合1:1 线程模型的编程模型

    1:1的线程模型, 如果使用BIO的那套网络服务编写方式, 会创造大量线程
    而我们现在是1:1的线程模型, 对机器的负载会变得越来越高, 大量的CPU时间片会用在线程切换上

  • 所以1:1线程模型, 想要有高性能的TCP服务就要用事件驱动的编程模型

    N:1

    略, 这个不是用来做TCP服务的

    适合 M:N 线程模型的编程模型

    这里有个典型代表就是golang, golang的goroutine是一个轻量级线程, 也就是协程
    goroutine之间切换, 内核并不知道, 非常轻量级, 单核CPU可以很轻松的创造几十万的协程
    这种线程模型就没有必要搞那些花里胡哨的, 直接大力出奇迹, 就用BIO的编程模型就行

    3.Java的线程模型

  • Java的线程与操作系统的线程是 一一对应的, 也就是经典的 1:1 模型

  • 在Linux中实际上process和thread是一回事, 区别就在于是否共享地址空间
  • 这也就是说在Java中创建线程代价是很大的
  • 基于上面的原因, 如果想在Java中开发高性能的TCP服务, 就只有一条路子了, 就是使用事件驱动的编程模型
  • JavaNIO是事件驱动模型, 但是使用起来太麻烦, 模板代码一大坨

    4.Netty的编程模型

  • Netty在JavaNIO的基础上再套了一层, 抽象了自己的API, 这套API与平台无关,与协议无关; 使用的也是事件驱动的编程模型

二.高性能的TCP服务的线程模型是什么 - 图2

4.1 事件

传统的线性编程思路是在一个线程中, 依次执行:

  • 建立连接
  • 读取数据(处理半包,粘包)
  • 协议解码
  • 组装报文
  • 处理业务 (这一步不在Netty的EventLoop中执行)
  • 接收返回值
  • 拆解报文
  • 协议编码
  • 写入数据
  • 断开连接

现在Netty的做法是把这些全部拆分开来, 每一步操作当作一个事件
只要是事件,就丢进EventLoop中处理
只要处理事件的代码没有耗时操作, 那么性能就能保持非常高的水准

4.2 BossGroup

  • 在有WorkGroup的情况下, BossGroup专门处理IO连接事件
  • 在有WorkGroup的情况下, BossGroup只会使用一个线程, 即使你分配再多的线程也没用

    4.3 WorkGroup

  • 专门处理IO读写事件

  • 可以有多个线程进行处理