老早前给公司内部写了一个 gRPC 小框架叫 aipod,主要是做模型的搬运工,数据流向大概是这样:
model <-->[train/predict/log]<--> aipod <--> web app
本来是个小玩意,基本跑的还算愉快,但是随着业务发展,传输的数据到M再到G级别时,问题出现了,各种花式断连,不得不重构之,主要干了这么些事情:
- 改进默认配置
- 改变传输模式:从简单
Unary RPC
到Bidirectional Streaming RPC
- 同步改异步,支持更高的并发
- zstd 压缩
- 引入万能方法,解决多 aipod 实例无法被同 Nginx 分流&负载均衡的问题
改配置
实际部署中其实你根本不知道前面会套多少层类似 Nginx 的代理,任意一层超时链接就会被掐掉,所以这里的配置主要是为了降低不必要的断连而准备的,另外增加了超时重试的机制。我直接贴代码,就不解释了
|
|
改传输模式
还是为了解决长阻塞任务被中断的问题,关于 Unary RPC 转 Bidirectional Streaming RPC 官方文档都有详细的解释,这里不赘述。本来正常的操作应该是想办法避免、降低长阻塞任务,尽量在超时范围内返回任务结果,但实际上你根本不知道调用方的任务是什么玩意,比如一个预测任务,抠搜客户只给配 CPU 那跑个大几十秒十几分钟都是稀松平常,完全无法控制,也根本无法定一个相对安全的超时时间。所以只能从自己身上下刀了:**分一个线程出来,专门负责给客户端响应心跳,另一个线程等待阻塞任务结束。**这样你甚至不用关心中间到底过多少 Nginx,每个节点的超时时间是多少,再低也不会低于1秒吧?只要下游真正写好异步任务,不阻塞 IO,那么就不会被掐掉链接。
|
|
改异步
这个没说的,别让 CPU 闲着嘛,多浪费。gRPC 官网有很好的示例代码,我顺路增加了 health check 以及 graceful shutdown 支持。
|
|
zstd 压缩
这玩意比 gzip 压缩率大还快,没理由不用,但 gRPC 暂时还没有支持,咋办?魔改呗,把大二机制参数值用 zstd 自己压一遍,收的时候再解就 OK,虽然费了手续,但能省可观的网络 IO,可太值了。
|
|
多实例 Nginx 均衡
鉴于本框架带载能力太强,实测单次超4GB都稳如老狗,所以渐渐有他组产品也来套用,这时候问题来了:protocol 是固定的,假如我接了 xxx yyy zzz 三个模型,这三个模型不巧又同时被一个 Nginx 代理,他们的 location 是一样的/ai.AI
那咋分流呢?改 protocol 是不可能改的,我懒,走了另一条道,其实包括万能方法chaos
以内,都已经体现在上面代码里了。剩下 Nginx 的配置简单提一下:
|
|
gRPC Health Checking
参考:https://github.com/grpc/grpc/tree/master/examples/python/xds 实现了 Health Checking 支持,可以通过命令行工具grpcurl
进行健康检查
|
|
使用 Nginx 时,可以参考:https://www.nginx.com/blog/nginx-plus-r23-released/#New-Features-in-Detail 为服务添加健康检查
# NOTE: I am not responsible for any expired content.
create@2023-01-24T01:23:27+08:00
update@2023-12-25T03:26:04+08:00
comment@https://github.com/ferstar/blog/issues/71