微服务架构和代码复用是不是本身就是有冲突的?

2021-03-16 12:21:46 +08:00
 RedrumSherlock

微服务架构里,每个微服务彼此都应该是黑盒,自己内部实现逻辑,业务逻辑不干涉。但是现实里哪有切那么完美的用例?总是会有一些类和方法多个微服务里都会用到,哪怕不是完全一样也是高度类似,在传统设计里肯定要重构复用的。但是微服务里要是分开单独写就相当于冗余代码高维护,抽出来给多个模块共用又破坏了微服务设计理念。是不是微服务设计的时就这种情况就是要牺牲可复用性的?

6589 次点击
所在节点    程序员
50 条回复
volatileSpark
2021-03-16 21:38:02 +08:00
如果是那些可能需要公用的 entity 之类的,那需要单独建立一个模块引入到工程中。如果你是说公共逻辑中有一些操作可能会经常出现在几个不同的微服务中时造成代码重复,那没有办法。如果要考虑降低重复代码,你就考虑引入领域驱动,也就是 DDD,来构建值对象。将那些重复操作再归拢一遍!
matrix67
2021-03-16 22:00:56 +08:00
可以参考一下 B 站

1. 约 329 个 Go 服务, 历史约 170 人左右贡献过 Go 代码. 其中 admin 目录下 54 个, infra 目录下 5 个基础组件服务, interface 下 77 个, job 目录下 80 个, service 目录下 113 个.
2. 代码和目录规范性比较好, 代码生成工具建设比较好, 大家可以借鉴一下.
3. 对于一个 Golang 开发者来说, 入职 B 站, 我觉得大概 2-3 天就可以 copy&&paste 开始贡献业务代码了. 其他语言开发者, 3-4 天吧, 因为学习 Golang 花一天.
4. B 站 Go 不依赖 CGO, 业务代码可以在 windows 编译通过! 启动!
5. 组件基本是基于开源组件封装.
6. RPC 基于 grpc 封装, 协议编码为 proto, 没有我们通常那样的包头.
7. 服务注册与发现已经包装在 RPC 中. 注册使用自研的 discovery, 基于类似 url 的方式去注册和寻址.
8. 数据存储多使用 memcache, redis 和 DB.
9. hbase 也使用比较多. 用于鉴权, 用户数据存储. 对于一些 kv 数据, 外部没有支持冷热分离的 kv 存储, hbase 是一个非常好的选择: 基于 HDFS, 热数据加载到内存, 列式存储, 强一致, 可配置副本数.
10. 消息队列为使用基于 kakfa, 实现了 redis 协议的 databus.
11. 小文件存储: B 站自己实现的 bfs
12. 监控上报使用的是 prometheus, 对于中小公司, 没法建设自己的监控组件, prometheus 是很不错的选择.
13. 简单浏览了下, 这份代码在 SQL 上没有注入风险. 生产环境的配置并没有在这份代码中. 一个合格的开发者, 即使所有源码流出去, 也不会对系统造成任何危害.
14. 不过 B 站的代码似乎打点监控做的不是很多(可能没有太多的去强调?)
yzbythesea
2021-03-16 22:07:51 +08:00
@RedrumSherlock 你这个 customer 肯定是单独一个 service 啊。然后调 customer 这个类是从一个公有库里调,不会订单和记账各自声明一个 customer 的。
leewi9coder
2021-03-16 22:45:26 +08:00
@matrix67 更详细点的在哪里看啊
matrix67
2021-03-16 22:53:20 +08:00
@leewi9coder #44 搜这篇 《一探 B 站后台架构, 他山之石, 何以攻玉?》
matrix67
2021-03-16 22:55:26 +08:00
@matrix67 #45 他的源码我看过,有些服务,投币还是 coupon 的就没几个接口。但也是一套全的。感觉就是毛剑等大佬搭好框架,业务小弟开始填逻辑!!!

关键基础设施要好啊,不然 329 个服务你部署一套测试环境,部署一套演练都要吐血了。。
generic
2021-03-16 23:02:34 +08:00
@RedrumSherlock 就像楼上说的,你两个微服务用了相同的日志库,难道它们就耦合了?显然不是。只要微服务 A 不依赖于微服务 B 用了和自己一样的日志库,那用哪个日志库就是实现细节。同样的,你的微服务依赖于别的微服务内部用了同一个公共模块么?不依赖,那就没有耦合。
cjwfuture666
2021-03-17 09:20:18 +08:00
不同领域的问题,虽然逻辑一样但业务不同,如果不分开,将来业务扩展起来是无尽的灾难,
论领域驱动设计之 限界上下文的重要性
qpily
2021-03-17 09:47:36 +08:00
如果有多个服务都使用同一段业务代码,那这段代码就应该单独抽出来成为一个服务供其他服务调用。不存在什么比较小的类,比较小的类就不能成为微服务了?谁规定的?
如果只有少数服务使用的业务代码,那应该把逻辑放在主要使用的服务里面,这个服务应该开放出接口供其他服务调用
出现任何你无法理解的状况,一般要么就是做微服务设计的时候没分清,要么就是业务场景根本不适合微服务
namelosw
2021-03-17 12:31:51 +08:00
我搞这些东西搞了很多年. 我的建议是小重复无所谓, 大重复要重新审视服务划分. 大重复又改不动划分的话可以用 monorepo 凑合, 无论如何业务代码不要用包的形式复用.


最理想的方式:

正确分割上下文(其实就是所谓战略设计), 每个微服务之间尽量不要有重叠. 一旦划分错了重叠就会多, 后面选哪条路都有问题, 是无解的.


几种实践中的方式:

1. 重复比较少的时候忍一忍, 抄代码有重复是没事的.

2. 重复多的时候要想的第一件事是分割有错, 应该先考虑能不能把服务合并回去.

3. 重复多的时候抽公共服务, 但是公共服务的问题是会分层, 公共服务之间也会有公共服务, 最后变成金字塔, 服务数量指数级爆炸, 而且经常需要调整重构, 需要公用的地方会越来越多(因为服务变太小了), 对服务的单元测试质量也会下降(业务覆盖率太低). 所以公共服务非到万不得已不抽. 抽出来的都是业务很稳定的“中台”. 对于中台我的建议也是没有必要不搞.


不推荐的方式:

1. 包管理, 如果跟业务完全没有关系可以用包. 跟业务沾边就绝对不要用. 如果想公用, 又没有足够的人力像上述第 2 条那样合并服务, 可以简单把代码库合成一个 monorepo, 但是还部署成多个服务, 这样的好处是没有版本问题.

为什么不建议用包? 因为假设两个组的服务 A 和 B 都用了包 X, A 用的比较频繁, A 升级 X 比较频繁, B 升级不频繁, A 一年升级了 50 个版本. 某题 B 想升级 X, 发现 X 已经被修改了很多次, 要升级就要把之前的 API 全升级成 X 的最新版, 但是 B 在这一年里完全是基于旧的 X 写的新代码, 如果想升级则不得不把这年跟 X 相关的代码全部重写.

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/762072

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX