Go微服务之间模块依赖管理的思考

分类: 工作

场景

工作上从 Java 来到了 Go 。项目架构也彻底进入了微服务架构,目前使用的是 k8s + istio 实现的服务伸缩与服务发现。

各个服务之间使用 gRpc 承载Json来交换数据进行通信的,是的json格式。大致结构如下,主要还是参考http api接口的模式。

message Request {
	bytes data = 1;
}

message Response {
	bytes data = 1;
}

service internal {
  rpc Call(url,data)returns(Response) {};
}

其中的 data就是json格式的数据。

服务间的互相调用也是使用一个路径,像http一样的互相调用。

在早期设计考量中是这样思考的 :gRpc 默认是使用 protobuf 进行数据序列化的。数据的结构的更新需要更新 proto.pb 文件重新生成代码,服务端新增了返回字段,客户端也需要更新。对于还在初期,快速迭代,需求早期不确定时,json 这种极度宽容的序列化方式还是非常好的。代价就是没有了原本的强类型与性能优势。

每个服务都写了一堆自己提供的方法,什么使用什么路径、需要什么参数、会返回什么数据类型。提供的方法内写好了json转换的一大堆操作,方便其他服务进行调用。这部分是类似sdk方法一样提供写在external包中,被其他服务应用,好处,简化调用。弊端,服务本身无法控制其他服务升级sdk版本号,版本管理混乱。

问题

慢慢的,服务到了100多个了。服务之间形成了复杂的互相依赖,甚至是不同版本的依赖,举个例子。

有 A;B,C三个服务。

A 依赖 B服务(V1.1) 和 C服务(V1.0)

B 依赖 C模块(V0.9)

A -> B(V1.1) -> C(V0.9)
  -> C(V1.0) 

那样问题来了,A 依赖的 C 和 B 依赖的 C 版本不一致有冲突了,当出现这种情况。目前我们的处理方案是,升级B服务中C的版本号。带来的风险,B服务没有其他更改,只升级了依赖。B还是需要走一遍测试,来测试是否有影响。

除开这一个问题,还有另一个场景:决定废弃C服务的a方法但是,无法知道那个服务使用了 C.a 方法,即使有新方法替代也不敢直接删除,谁知道哪个上古服务会一瞬间爆炸呢。

基于以上这些点我在思考,如何更好的管理服务之间的互相依赖,版本混乱的问题。

想法

回顾一下,我们的持续部署模式。我们是自建的gitlab,每次有提交或合并时都会使用gitlab runner去编译并部署到对应环境的k8s上面去。

所以我想我们是否能能在CI/CD阶段来处理这个问题?

每次有提交时,去解析 go.mod 文件是否应用了其他的服务,应用的服务所使用的版本是否高于最低的版本要求。如果低于版本要求则直接停止流程,不进行编译部署。来保证服务所依赖的Stub(本地存根)都是处于一个比较新的版本。

干预CI/CD流程,管控服务版本。

目前想到的:

  1. 可以需要一个repo,里面记录需要进行版本的项目。有弃用限制,最小版本限制等等信息。
{
    "xxxx":{
        "minVer":"v1.0.1",
        "deprecated":false
    }
}
  1. 在 CI/CD 脚本中加入版本限制,根据 repo 中项目的配置,比对服务版本号,敢于编译部署流程
  2. 可以做个定时任务,去扫描所有 repo 他们的依赖关系,可以输出成数据,了解依赖关系。谁依赖了A,A又依赖了谁。

上一篇: Spring Controller方法入参@Validated注解实现
下一篇: Depmod CI流程下Go module依赖最低版本的控制