问一个 Spring 微服务依赖的最佳实践 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
timi
V2EX    问与答

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

  •  
  •   timi 2021-08-14 09:23:00 +08:00 3544 次点击
    这是一个创建于 1533 天前的主题,其中的信息可能已经有所发展或是发生改变。
    初学 Spring 全家桶,举例:A 微服务请求 B 微服务,返回一个类 User,那么这个 User.java 应该定义在 A 里,还是 B 里,还是抽一个 Common 的依赖包

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

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

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

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

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


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

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

    微服务之间通过 REST 接口读写的数据,应一律视为可以跨语言的契约数据。接口提供方可以将原始对象自动 JSON 序列化后再返回,接口使用方可以用类似 Jsonpath 的东西来手动读取(当然偷懒又不怕麻烦的话,可以再定义个类来做自动 JSON 读取)。
    vindurriel
        21
    vindurriel  
       2021-08-15 10:55:08 +08:00 via iPhone
    输入输出的结构是接口的一部分 当然定义在 b b 有责任做好向前和向后兼容性 a 为了保持核心逻辑的稳定 不要直接使用 b 的输出 需要做一些转换逻辑 将来 b 变了 先动转换
    以上思路和语言或框架无关
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2698 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 08:06 PVG 16:06 LAX 01:06 JFK 04:06
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86