grpc优雅的退出
这篇文章是笔者针对 GRPC C++ 异步 server 析构流程爬坑后的记录。
写在前面的一些事
一般关注是 grpc 的使用,封装等。但对于 c++ 这门玄学的语言来说,grpc_call 的 delete 机制、退出机制,对于一个追求优雅风格的程序员而言也是非常重要的。
基于项目的必要,我们需要在自己的系统中对 grpc 封装一层,便于实际业务代码的开发。
先简述一下必要的前提:
- grpc_call,抽象出的关于一次通信的概念,包含了一次通信的内容、特定 call 的回调函数等关于通信所必须的组件。
- grpc_client,抽象出的 grpc 的 client,用于上层直接调用,屏蔽相应的通信细节,包含若干关于一次 rpc 通信的基本组件。
- grpc_server,抽象出的 grpc 的 server,用于服务器端上层调用,屏蔽响应的通信细节,包含一个 server 端通信所需的基本组件。
- 便于系统工程的实现,call、client、server 都分为两层封装,最底层的接口,上层的有类型的类实现。对于用户层而言,只需继承有类型的类,实现回调函数等即可使用。
shutdown 的正确流程
傻瓜也知道的细节:
- shutdown 流程被调用一般处于类的生命周期最后阶段或者程序最后阶段。
- 一般需要正确的释放资源,避免因 不恰当释放造成的内存泄漏和端错误等。
下面针对 server 和 client 分别进行描述
client 的析构
- client 析构时需要保证所有存在于 client 中的 call 全部被释放(源于 grpc 兼容 c 的实现,所有保存于 completed_queue 中的 call 指针是 void* 类型,无法使用智能指针管理)
- client 析构时保证 completed_queue 正确 shutdown、监测 completed_queue 的线程正确被释放
- client 所占的物理资源的释放( grpc 基于 http2 实现的 rpc 通信,http2 一个特点是共享链路,即:若存在一个常驻的 client,同线程中所有另外的 cient 如果 socket 一致,则共用底层,很棒的实现,但不正确的使用,有坑!!)
server 的析构
- server 析构需要调用 grpc::server 的 Shutdown 方法和所有 CompletedQueue 的 shutdown 方法,且一定是 server 先 shutdown,与之绑定的 completed_queue 后 shutdown。
- 所有保存于 completed_queue 中的 call 必须全部释放
- 假设有比较耗时的 call 回调调用,需要考虑到结束阶段所有 call 可能处于的状态,正确处理,避免出现端错误造成进程崩溃
- server 所占用的物理资源的释放
流程示意图如下图