一个golang的RPC框架设计中的坑点

本文首发于我的个人博客
本文记录了作者在使用公司的RPC框架时,由于RPC框架本身的设计以及程序员的投机取巧而遇到的一个坑点。

简单来说,RPC框架没有能够做足够好的封装,程序员(不是作者,是前人)也没有按照RPC框架约定的方式进行API调用。

背景提要

我们知道,RPC调用是不同主机间的进程通信的方式,如果想要额外传递消息,我们往往需要修改RPC调用的接口,通过新增参数的方式来新增传递信息。

但是这样修改参数,修改接口的方式侵入性太强,需要进行上下游调用接口的适配,很麻烦。

当我们面对一些遍布在各个RPC服务的需求(也就是很多人喜欢提及的编程"切面"Aspect),比如这回我接到的日志系统的需求,将所有的接口都修改一遍,显然是不现实的。

在这里,golang的RPC框架可以通过传递context.Context来实现,也就是说,这些额外的,遍布各个RPC服务的消息,通过context.Context来传递。

// rpc 上游调用时,传递一个`context.Context`和本来必要的下游rpc接口参数
rpcClient.remoteMethod(ctx, request)

// rpc 下游接受请求时,接受一个`context.Context`和本来必要的接口参数
func methodHandler(ctx context.Context, request MethodRequest)

类比HTTP解释

上面提及的RPC调用传递context.Context和本来的接口参数,其实可以类比HTTP协议:

  • context.Context -> HTTP Request Headers
  • 接口参数 -> HTTP Request Body

二者都是传递信息的手段,但是接口参数和Request Body往往是明面上的写出来的主要业务逻辑需要的消息,context.Context和Request Headers往往是一些元数据(metadata)。

需求场景

这次的日志系统,需要我记录RPC运行时的动态调用链,也就是说,如果有一条RPC调用链路是

RPC1 -> RPC2 -> RPC3

那么实时的日志里,会有如下条目:

RPC1:
stack : []

RPC2:
stack : [RPC1]

RPC3:
stack : [RPC1, RPC2]

解决过程中遇到的问题

对于这个功能,我们发现RPC框架提供了三个接口:

// 向一个context.Context加入key-val键值对
func AddInfo(ctx context.Context, key string, val string) context.Context

// 获取上游通过AddInfo传来的key对应的val
func GetUpstreamInfo(ctx context.Context, key string) string

// 获取所有上游通过AddInfo传来的键值对,组织成一个map[string][string]
func GetAllUpstreamInfo(ctx context.Context) map[string]string

为此,我们的解决方案是,将stack做成[]RPC,其中struct RPC记录RPC的信息,通过JSON将[]RPC转化成string,而后用context.Context里的"stack" - JSON([]RPC)的键值对进行传递。

在我之前编码的程序员,没有遵守API调用规则,不使用AddInfo,而是使用的是如下方式进行stack的传递的

// 获取所有的键值对
m := util.GetAllUpstreamInfo(ctx)
// 取出stack并使用JSON解析
stack, err := json.Unmarshal(m["stack"])
if err != nil {
   </
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值