V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
timi
V2EX  ›  问与答

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

  •  
  •   timi · 2021-08-14 09:23:00 +08:00 · 3005 次点击
    这是一个创建于 1188 天前的主题,其中的信息可能已经有所发展或是发生改变。
    初学 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 重构已经提上日程。。。
    dengsq
        13
    dengsq  
       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   ·   实用小工具   ·   1056 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 22:35 · PVG 06:35 · LAX 14:35 · JFK 17:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.