如何用Netty写一个高性能的分布式服务框架?( 四 )

 

  • 在不 bind 多端口的情况下 BossEventLoopGroup 中只需要包含一个 EventLoop , 也只能用上一个 , 多了没用 。
 
  • WorkerEventLoopGroup 中一般包含多个 EventLoop , 经验值一般为 cpu cores * 2(根据场景测试找出最佳值才是王道) 。
 
  • Channel 分两大类 ServerChannel 和 Channel , ServerChannel 对应着监听套接字(ServerSocketChannel) , Channel 对应着一个网络连接 。
 
2 Netty4 Thread Model
如何用Netty写一个高性能的分布式服务框架?

文章插图
 
3 ChannelPipeline
如何用Netty写一个高性能的分布式服务框架?

文章插图
 
4 Pooling&reuse
 
PooledByteBufAllocator
 
  • 基于 jemalloc paper (3.x)
 
  • ThreadLocal caches for lock free:这个做法导致曾经有坑——申请(Bytebuf)线程与归还(Bytebuf)线程不是同一个导致内存泄漏 , 后来用一个mpsc_queue解决 , 代价就是牺牲了一点点性能 。
 
  • Different size classes 。
 
Recycler
 
  • ThreadLocal + Stack 。
 
  • 曾经有坑 , 申请(元素)线程与归还(元素)线程不是同一个导致内存泄漏 。
 
  • 后来改进为不同线程归还元素的时候放入一个 WeakOrderQueue 中并关联到 stack 上 , 下次 pop 时如果 stack 为空则先扫描所有关联到当前 stack 上的 weakOrderQueue 。
 
  • WeakOrderQueue 是多个数组的链表 , 每个数组默认size=16 。
 
  • 存在的问题:思考一下老年代对象引用新生代对象对 GC 的影响?
 
5 Netty Native Transport
 
相比 Nio 创建更少的对象 , 更小的 GC 压力 。
 
针对 linux 平台优化 , 一些 specific features:
 
  • SO_REUSEPORT - 端口复用(允许多个 socket 监听同一个 IP+端口 , 与 RPS/RFS 协作 , 可进一步提升性能):可把 RPS/RFS 模糊的理解为在软件层面模拟多队列网卡 , 并提供负载均衡能力 , 避免网卡收包发包的中断集中的一个 CPU core 上而影响性能 。
 
  • TCP_FASTOPEN - 3次握手时也用来交换数据 。
 
  • EDGE_TRIGGERED (支持Epoll ET是重点) 。
 
  • Unix 域套接字(同一台机器上的进程间通信 , 比如Service Mesh) 。
 
6 多路复用简介
 
select/poll
 
  • 本身的实现机制上的限制(采用轮询方式检测就绪事件 , 时间复杂度: O(n) , 每次还要将臃肿的 fd_set 在用户空间和内核空间拷贝来拷贝去) , 并发连接越大 , 性能越差 。
 
  • poll 相比 select 没有很大差异 , 只是取消了最大文件描述符个数的限制 。
 
  • select/poll 都是 LT 模式 。
 
epoll
 
  • 采用回调方式检测就绪事件 , 时间复杂度: O(1) , 每次 epoll_wait 调用只返回已就绪的文件描述符 。
 
  • epoll 支持 LT 和 ET 模式 。
 
7 稍微深入了解一点 Epoll
 
LT vs ET
 
概念:
 
  • LT:level-triggered 水平触发
  • ET:edge-triggered 边沿触发
 
可读:
 
  • buffer 不为空的时候 fd 的 events 中对应的可读状态就被置为1 , 否则为0 。
 
可写:
 
  • buffer 中有空间可写的时候 fd 的 events 中对应的可写状态就被置为1 , 否则为0 。
 
图解:
如何用Netty写一个高性能的分布式服务框架?

文章插图
 
epoll 三个方法简介
 
1)主要代码:
linux-2.6.11.12/fs/eventpoll.c
 
2)int epoll_create(int size)
 
创建 rb-tree(红黑树)和 ready-list (就绪链表):


推荐阅读