RPC 框架 3 | 青训营笔记

92 阅读5分钟

RPC 框架

这是我参与「第五届青训营 」伴学笔记创作活动的第 16 天

4 更新

4.1 整体架构 — Kitex

  • Kitex Core 核心组件
  • Kitex Byted 与公司内部基础设施集成
  • Kitex Tool 代码生成工具

image.png

core是它的的主干逻辑,定义了框架的层次结构、接口,还有接口的默认实现,如中间蓝色部分所示,最上面client和server,是对用户暴露的,client/server option的配置都是在这两个package中提供的,还有client/server的初始化,在第二节介绍kitex_gen:生成代码时,大家应该注意到里面有client..go和server..go,虽然我们在初始化client时调用的是kitex_gen中的方法,其实大家看下kitex._gen下service package代码就知道,里面是对这里的client/server的封装。

client/server下面的是框架治理层面的功能模块和交互元信息,remote:是与对端交互的模块,包括编解码和网络通信。

右边绿色的bytd是对字节内部的扩展,集成了内部的二方库还有与字节相关的非通用的实现,在第二节高级特性中关于如何扩展kitex里有介绍过,byted部分是在生成代码中初始化client和server时通过suite集成进来的,这样实现的好处是与字节的内部特性解耦,方便后续开源拆分。

左边的too则是与生成代码相关的实现,我们的生成代码工具就是编译这个包得到的,里面包括引d解析、校验、代码生成、插件支持、自更新等,未来生成代码逻辑还会做一些拆分,便于给用户提供更友好的扩展

4.2 自研网络库 — 背景

  • 原生库无法感知连接状态

    在使用连接池时,池中存在失效连接,影响连接池的复用。

  • 原生库存在go routine暴涨的风险

    一个连接一个go routine的模式,由于连接利用率低下,存在大量goroutine占用调度开销,影响性能。

  1. Go Net使用Epoll ET,Netpoll使用LT。
  2. Netpoll在大包场景下会占用更多的内存。
  3. Go Net只有一个Epoll事件循环(因为ET模式被唤醒的少,且事件循环内无需负责读写,所以干的活少),而Netpoll允许有多个事件循环(循环内需要负责读写,干的活多,读写越重,越需要开更多Looρs)
  4. Go Net一个连接一个Goroutine,Netpoll连接数和Goroutine数量没有关系,和请求数有一定关系,但是有Gopool重用。
  5. Go Net不支持Zero Copy,甚至于如果用户想要实现BufferdConnection这类缓存读取,还会产生二次拷贝。Netpoll支持管理个Buffer池直接交给用户,且上层用户可以不使用Read(p []byte)接口而使用特定零拷贝读取接口对Buffer进行管理,实现零拷贝能力的传递。

4.3 自研网络库 — Netpoll

  • 解决无法感知连接状态问题

    引入epol主动监听机制,感知连接状态

  • 解决goroutine暴涨的风险

    建立goroutine池,复用goroutine

  • 提升性能引入Nocopy Buffer,向上层提供

    引入NoCopy的调用接口,编解码层面零拷贝

  1. gonet无法检测连接对端关闭(无法感知连接状态)

    • 在使用长连接池时,池中存在失效连接,严重影响了连接池的使用和效率。

    • 希望通过引入epoll 主动监听机制,感知连接状态。

  2. gonet缺乏对协程数量的管理

    • Kite采取一个连接一个goroutine 模式,由于连接利用率低,服务存在较多无用的goroutine,占用调度开销,影响性能。
    • 希望建立协程池,提升性能。
  3. netpol基于epoll,同时采用Reactor模型,对于服务端则是主从Reactor模型,如右图所示:服务端的主reactor用于接受调用端的连接,然后将建立好的连接注册到某个从Reactor上,从Reactor负 责监听连接上的读写事件,然后将读写事件分发到协程池里进行处理。

  4. 为了提升性能,引入了Nocopy Buffer,向上层提供NoCopy的调用接口,编解码层面零拷贝

4.4 拓展性设计

支持多协议,也支持灵活的自定义拓展

kitex支持多协议的并且也是可扩展的,交互方式上前面已经说过支持ping-pong、streaming、 oneway

编解码支持thrift、Protobufo

应用层协议支持TTHeader、Http2、 也支持裸的thrift协议

传输层目前支持TCP,未来考虑支持UDP、kernel-bypass的RDMA

框架内部不强依赖任何协议和网络模块, 可以基于接口扩展, 在传输层上则可以集成其他库进行扩展。

目前集成的有自研的Netpoll, 基于netpoll实现的http2库, 用于mesh场景通过共享内存高效通信的shm-ipc,以后也可以增加对RDMA支持的扩展。

4.5 性能优化 — 网络库优化

  • 调度优化 epoll wait在调度上的控制 gopool重用goroutine降低同时运行协程数
  • LinkBuffer 读写并行无锁,支持nocopy地流式读写高效扩缩容 Nocopy Buffer池化,减少GC
  • Pool 引入内存池和对象池,减少GC开销

4.6 性能优化 — 编解码优化

  • Codegen 预计算并预分配内存,减少内存操作次数,包括内存分配和拷贝 Inline减少函数调用次数和避免不必要的反射操作等 自研了Go语言实现的Thrift IDL解析和代码生成器,支持完善的Thrift IDL语法和语义检查,并支持了插件机制- Thriftgo
  • JIT 使用JIT编译技术改善用户体验的同时带来更强的编解码性能,减轻用户维护生成代码的负担基于JIT编译技术的高性能动态Thrift 编解码器- Frugal