RPC基本概念
本地函数调用
- 参数压栈
- 通过函数指针找到函数,进入函数后取出栈内值,赋值给形参。
- 计算形参,结果存入一个变量。
- 将变量的值压栈,从函数指针返回。
- 从栈中取出值,赋值给res。
远程函数调用(Remote Proceduce Calls)
例如:订单服务调用支付服务,就是一个远程调用。
RPC要解决的三个问题:
- 函数映射:在远程调用中,两个进程的地址空间是不一样的,所以我们给每个函数一个ID,还要有一个ID和函数的对照表。
- 如何传参:客户端将参数转换为字节流,传给服务端。
- 网络传输
RPC概念模型
下面是1984年一篇论文中的模型。
一次RPC的完整过程
- IDL(Interface description language)文件:通过一种中立的方式来描述接口,使得不同平台上运行的对象和不同语言编写的程序可以互相通信。
- 生成代码:把IDL文件转换成语言对应的静态标准库。
- 编解码:也叫序列化和反序列化。
- 通信协议:规范了数据在网络中传输的格式。
- 网络传输:基于成熟的网络库走TCP/UDP传输。
RPC好处
- 单一职责,开发、部署以及运维都是独立的。
- 可扩展性强:底层基础服务可以复用。
- 故障隔离,整体服务可靠性提高。
RPC带来的问题
- 服务可能宕机,对方要如何处理?
- 跨网络,如何保证消息可达性?
- 请求量突增有何应对措施?
RPC的分层设计
这块由于我没有学过计网,看那些协议底层的字段含义感觉很懵逼,就没有过多介绍了。
分层设计
编解码层的数据格式
- 语言特定的格式:如java的序列化接口,方便但是和某个编程语言绑定。
- 文本格式:JSON、XML、CSV等。但是XML和CSV不能区分数字和字符串,JSON不区分整数和浮点数。JSON在一些语言的序列化和反序列中用反射机制,所以效率性能比较差。
- 二进制编码:
- 具备跨语言和高性能的优势
- Protobuf、Thrift
编解码层--选型
- 兼容性:支持自动增加新字段,不影响老的服务。
- 通用性:跨语言跨平台、协议流行度。
- 性能:时间和空间。
- 空间:序列化会加描述字段,需要考虑网络和磁盘压力。额外空间成本高。
- 时间:解析时间。可能成为整个系统的瓶颈。
协议层
网络通信层
网络库:
- 提供易用API
- 封装底层socket API
- 连接管理和事件分发
- 功能
- 协议支持
- 优雅退出、 异常处理
- 性能
- 应用层buffer减少copy
- 高性能定时器、对象池
RPC框架核心层:编解码层、协议层和网络传输层。
关键指标
稳定性
- 熔断:
- 情景:A调用B,B调用C,C超时,这会因为B依赖C而B超时,这个时候A继续频繁调用B,服务B就会因为大量请求堆积而宕机。导致了服务雪崩。
- 目的:保护调用方,防止被调用的服务出现问题影响整个链路。
- 限流:
- 场景:被调用方执行业务逻辑之前先检查限流逻辑,如果发现访问量太大并且超出了限流条件,就让服务器降级处理或者返回给调用方一个限流异常。
- 目的:保护被调用方,防止大流量把服务压垮。
- 超时控制:
- 场景:下游服务响应过慢,下游服务主动停掉一些不太重要的业务,释放出服务器资源,避免资源浪费。
- 长尾请求处理
- 注册中间件:Kitex客户端和服务端接口都采用Option模式,很方便注入这些稳定性策略。
易用性
- 开箱即用:合理的默认参数配置、丰富的文档
- 周边工具:脚手架、代码生成。