写点什么

消灭微服务的坏味道 之 共享库

用户头像
码猿外
关注
发布于: 2021 年 02 月 28 日
消灭微服务的坏味道 之 共享库

继续来聊一聊微服务的坏味道,这次的主题是共享库(Shared Library)。说起共享库,相信很多同学都在自己的项目上看到过类似 common.jar 或者 common.so 等类似的共享库,如果说它是个坏味道,估计有些同学会说这种方法应该没啥问题吧,大家都这么干。

共享库带来的问题

给大家举一个真实项目上的例子,当时我们的项目有很多个微服务,也有这么一个叫 common 的工程,几乎所有的微服务都要依赖它,目的是共享代码,减少重复,这种做法看起来貌似是非常合理的。


我们来看看这个 common 库里都包含了哪些内容:

  • 代码的基础架构相关

- 权限控制

- 日志

- Redis/MessageQueue/Slack 等集成

  • DTO 相关

  • 枚举值

  • 异常处理

  • 工具类(如日期转换、价格计算等)

  • 业务逻辑代码


这样的库职责不单一,承载了太多知识,当微服务数量变多,依赖一个过大的共享库会是一个灾难:

微服务不再独立

共享库在微服务之间建立了一个隐藏的耦合关系,当一个服务需要对共享库进行升级时,很难确定修改是否会对其他服务产生影响,这无疑增加了微服务开发团队之间的沟通成本。

意外的馈赠

当一个服务需要日志的功能依赖 common 库时,即使不需要其他功能,这个服务也会获得这个库里面所有的功能,这会产生一些我们不期望的后果。


举个例子,当前工程中有和 common 库中相同命名的工具类,在增加新代码时很容易选择了错误的依赖,导致代码没有按预期的工作。

版本冲突

不同的服务需求往往是不同的,随着业务的变化,共享库中的代码也会不断更新,如下图的更新方式:


假设服务 A 和服务 B 最初都依赖common_1.0.0.jar,随着服务 A 的需求变化,common 库从 1.0.0 版本升级到 1.0.2 版本,这时服务 B 还依赖 1.0.0 版本中的工具类 V1 和枚举 V1,如果服务 B 想通过升级 common 库来更新到枚举 V2,只能一步升级到 1.0.2 版本,但 1.0.2 版本中附带的工具类 V2 是服务 B 不期望的,会产生版本冲突。


在微服务比较多的情况下,这种大而全的共享库版本的管理是非常困难的。

如何在微服务间正确的共享代码

那么该如何在微服务之间共享代码呢?我们可以参照上文中提到的共享库中可能出现的不同内容分别说明:

职责单一的基础架构库

对于功能稳定通用且和业务本身无关的代码,推荐以职责单一的共享库的形式存在,比如日志、权限控制等分别以单独的日志库和权限库的形式存在,各个微服务按需添加依赖即可。

复制代码

针对 DTO、枚举、工具类和异常处理等,在不同的微服务上下文中并不一定能完全复用,因此建议以复制代码的方式存在在不同的微服务中,且可以根据微服务自己的特点进行修正。


比如上下游服务间的 API 通信,上游服务在接口的 DTO 中提供了 10 个字段,下游服务只需要其中的 5 个,下游服务的 DTO 定义没有必要完全按上游服务的来定义;同样对于枚举值,如果枚举的定义来自某个上游服务,我更倾向于建议下游服务不要以枚举的形式(而以字符串的形式)来接收这个字段,因为一旦上游系统增加了枚举值的种类,下游系统很可能就会遇到问题。

不要共享业务逻辑代码片段

有时候还会在共享库中出现一些业务逻辑代码片段,一旦这样的共享发生,这是一个信号,很可能微服务之间的边界并不清楚,这时候要回来审视下微服务的划分是否合理或者微服务间的职责是否清晰。


小结

微服务一直强调独立自治,滥用共享库让微服务之间的耦合变的紧密,微服务的优势也就不复存在。对于稳定通用且和业务无关的功能模块,可以分别放到独立的共享库中;对于业务相关的代码,不要共享,这部分的冗余是可接受的。


发布于: 2021 年 02 月 28 日阅读数: 17
用户头像

码猿外

关注

功不求戾,但求有恒 2018.10.07 加入

ThoughtWorks Lead Consultant,拥有10多年的架构设计和敏捷软件开发管理经验,专注于分布式软件架构设计、微服务架构设计和敏捷软件开发。 个人主页:https://maguangguang.xyz

评论

发布
暂无评论
消灭微服务的坏味道 之 共享库