问一个 Spring 微服务依赖的最佳实践

2021-08-14 09:23:00 +08:00
 timi
初学 Spring 全家桶,举例:A 微服务请求 B 微服务,返回一个类 User,那么这个 User.java 应该定义在 A 里,还是 B 里,还是抽一个 Common 的依赖包

如果 10+微服务之间都有理不清的依赖呢

我理解的,如果是定义在 A 或 B 里,则会产生依赖问题,如果定义在 common 里,那 common 会变成一个怪物

这块的最佳实践是什么
2799 次点击
所在节点    问与答
21 条回复
aircjm
2021-08-14 09:33:02 +08:00
我理解自己定义自己的 没必要做公共依赖
yidinghe
2021-08-14 09:41:04 +08:00
可以考虑定义在 B 服务的 API 包里面,这个包专门定义接口和传输类而不实现,依赖最小化。这是 dubbo 的做法。
vishun
2021-08-14 09:45:53 +08:00
A 微服务分为两个模块,其中模块 A1 只放放公用的 entity,feign 等类,A2 模块是具体的业务逻辑,同样 B 微服务也分 B1,B2,这样 A2 引用 B1,或者是 B2 引用 A1 都可以。
ljchengx
2021-08-14 09:47:57 +08:00
同 @yidinghe 目前实现的方式某一业务 拆分成 api 和实现 api 只有实体类和接口 实现工程引入 api 如果有需要实体类用到的其他的业务,只需引入 api 即可 依赖很小 也易于管理。
abcbuzhiming
2021-08-14 10:15:47 +08:00
你这么理解就明白了,这个类 User 仅仅是你的 A 服务为了映射请求结果而本地自定义的一个映射数据结构,这个映射数据结构和 B 服务可以说是没关系的。所以你当然应该放在 A 这里

你很纠结无非是你觉得这个东西似乎是可以复用的,所以纠结放 A 还是放 B,以及是不是要抽出来做个公共依赖。

我很久以前也很纠结这个东西,但是踩了太多坑以后我的想法就变了,高内聚低耦合本质的意义,就是把和一个服务(组件,应用,包,等等等等)相关的代码全部包在一起,不要和外界有牵扯,你有牵扯就会引发修改时的依赖地狱。


Java 这个语言在诞生的时候不管是发明者,业界,都很非常强调设计模式,设计模式中的一个需求来源就是代码复用,但是这是历史;历史上 Java 被开发出来,是希望写基础设施,来取代 C++的位置,基础设施离业务比较远,需求相对稳定,因此比较容易抽象和复用。但是 Java 发展到今天,形势已经变了,就像 Go 为了满足社区里大量写业务的人的需求,不得不加泛型一样,Java 现在和业务靠的非常近,和基础设施相对距离远,而业务又是复杂多变,这就导致抽象和复用变的困难而价值降低,能适应修改变成了硬需求,这种情况下。IOS 那种把一个程序的依赖全部聚合在包的内部的做法,才是最适宜的。

诚然,不抽象和复用,代价就是代码会膨胀,以及相似代码到处 copy 的问题。但是什么事情没有代价,无非是你选哪头而已。以 Java 目前这种更靠近业务的使用环境,项目初期我是完全不赞成去考虑抽象和复用的,至少要等到业务成型并稳定后,再去抽象和考虑复用进行重构
passer9527
2021-08-14 10:16:30 +08:00
我用 springcloud 的 feign 调用,是放在 b 里,a 调用 b 就需要引入一个 b 的 client API 包
timi
2021-08-14 10:33:29 +08:00
@yidinghe
@vishun
我司也是类似的实现,拆 A1 和 A2,但是由于屎山问题,担心会产生 A1 依赖 B2,B1 依赖 C2,C1 依赖 A2 的问题,从而导致打包灾难,虽然还没出现,但是有什么技术手段保证
acmore
2021-08-14 12:12:26 +08:00
微服务很多地方是不适用 SPOT 原则的,所以各放一份,不必同步,放心冗余就好了。
假如出现了循环和深层依赖,那么这两个微服务或许本不应该切开,应该重新考虑设计。
yidinghe
2021-08-14 13:08:43 +08:00
@timi 这么理解:接口 B 对返回值的封装,是站在 B 的角度去理解这个接口,而 A 服务通常对其是有不同的理解的,所以拿到对象后还要转成自己的 bean,而不应该直接拿 B 服务的 bean 在自己的业务里面流转。
Variazioni
2021-08-14 14:04:20 +08:00
放在 common 里比较方便。。如果 b 接口有改动。。连带着 a 的就一起改动了。。
各放一份等改动的时候很费劲。。特别是上了一定体量的微服务。。宁愿依赖多一些。也要容易维护。
timi
2021-08-14 17:15:26 +08:00
@yidinghe 是的,但是实际上,A 和 B 可能是不同团队开发的,如果是依赖模型的话,TeamB 修改了 B,至少有技术上的约束和保障,互相独立模型的话,就取决于组织内沟通效率了😂
timi
2021-08-14 17:16:42 +08:00
@abcbuzhiming 我们近期讨论了一个议题,Java 是不是不适合现代微服务了,要不要用 GO 重构已经提上日程。。。
dengsq
2021-08-14 18:30:35 +08:00
@timi 不懂就问,这种 A,B 依赖的场景 GO 是怎么实现的?
tangtj
2021-08-14 19:04:01 +08:00
@timi 可是在 go 上,你依然也有这个问题。我们现在也是遇到依赖管理的问题,目前做法是 使用 中心化管理依赖的版本号。借助 CI 控制打包时不得存在,低于设定的最低版本的依赖。
fmumu
2021-08-14 19:15:32 +08:00
这种请求响应参数封装可以放在一个单独的包里面,叫 user-api
wanlion
2021-08-14 19:54:40 +08:00
劳动仲裁, 老板故意让手机欠费怎么办?
wangxiaoaer
2021-08-14 22:16:30 +08:00
拆久必合 合久必拆 哈哈哈
aguesuka
2021-08-14 22:34:51 +08:00
Java9 的模块化, A 只 exports B requires 的接口
aguesuka
2021-08-14 22:53:57 +08:00
上面写反了.
优先级应该是 不拆开 > 到拆开一个 B-api 的模块 > 到 common-api 的模块. 按理来说应该首先避免循环依赖, 如果避免不了则是最小化依赖, 只有在项目依赖关系非常混乱的时候在.
A 和 B 各写一个 User 把编译时异常留到运行时了, 我觉得不太好
passerbytiny
2021-08-15 04:32:58 +08:00
划重点:当把 B 视为独立服务时,它不能返回 User 类 /实例,只能返回一种契约数据(可用 JSON 或 XML 来承载)。

微服务之间通过 REST 接口读写的数据,应一律视为可以跨语言的契约数据。接口提供方可以将原始对象自动 JSON 序列化后再返回,接口使用方可以用类似 Jsonpath 的东西来手动读取(当然偷懒又不怕麻烦的话,可以再定义个类来做自动 JSON 读取)。

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

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

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

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

© 2021 V2EX