V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
justdoit123
V2EX  ›  程序员

微服务之间,如何处理常量、DTO 冗余问题?

  •  
  •   justdoit123 · 11 小时 21 分钟前 · 1778 次点击
    我们的微服务(都是 python3),每个服务单独一个 repo 。在写一些跨服务的代码的时候,会遇到常量代码共享的问题。


    比如,某个实体某字段的 flag 。这样的常量,在本服务的代码里,肯定是定义了一堆这个字段的 flag 。但是,到了另一个服务的时候,没有这些定义。以往都是在别的服务就地再写一遍。


    我感觉这种做法实在是很不方便,而且很脏。

    我的想法是,应该把这些没有什么逻辑的“常量”代码(不限于常量,还有各种数据封装的 class )放在一个 repo 里,单独发包,让其它服务引用。至于是每个服务单独有一个常量库,还是所有服务共享一个常量库,则可以视情况而定。

    想请教各位,这种做法如何?以及有没有更好的实践。感谢!
    第 1 条附言  ·  6 小时 20 分钟前
    微服务是既定现状,至少已经快 8 年了。要变成单体,感觉工作量太大。公司也降本了,平时业务开发已经压的比较紧。基本不太可能改变微服务的现状。
    22 条回复    2025-09-22 20:23:35 +08:00
    Maboroshii
        1
    Maboroshii  
       11 小时 8 分钟前
    protobuf 和 grpc 呗
    kapr1k0rn
        2
    kapr1k0rn  
       11 小时 7 分钟前
    monorepo
    qxmqh
        3
    qxmqh  
       10 小时 26 分钟前   ❤️ 3
    换成单体。
    pxiphx891
        4
    pxiphx891  
       9 小时 15 分钟前
    没写过 python ,不过 java 一直是这样的,打一个单独的包,所有人依赖这个包
    pigspy
        5
    pigspy  
       9 小时 13 分钟前
    服务不多的情况下直接用 monorepo
    MIUIOS
        6
    MIUIOS  
       8 小时 48 分钟前
    参考 java 暴露出 dto do vo 这些单独一个包,其他模块随便引入
    NoKey
        7
    NoKey  
       8 小时 47 分钟前
    团队执行力强(盖的住成本)的话,搞公共包,内网搭 maven 仓,拉下来就能用,不过这需要额外的人力去维护这一套;一般的团队,重复写就重复写,要么就是相互复制,除了消耗人力,又不会怎么样😂
    justdoit123
        8
    justdoit123  
    OP
       8 小时 7 分钟前
    @Maboroshii grpc 可以上。但是需要共享的代码,不局限于 protobuf 描述的数据。
    justdoit123
        9
    justdoit123  
    OP
       8 小时 4 分钟前
    monorepo 倒是也可以。但是我感觉组员有时候写得很野,跨服务乱引用。综合来看,不太适用我们。
    justdoit123
        10
    justdoit123  
    OP
       8 小时 3 分钟前
    综合上面的回答,可能适合的方案还是服务的“常量”代码抽成一个包。
    blackmatch
        11
    blackmatch  
       7 小时 37 分钟前
    1.抽成一个公共包,各个服务引入调用即可。
    2.做一个配置中心服务,所有服务统一读取同一份配置,做好更新和兜底策略。好处是修改配置不用发版,在配置中心改一下就可以了。
    patrickpu
        12
    patrickpu  
       7 小时 17 分钟前
    需要一个 common 的基础包
    iyaozhen
        13
    iyaozhen  
       6 小时 57 分钟前
    一般是吧这些和 idl 绑定,idl (比如 pb 描述)生成这个仓库,各方来引用

    不过怎么说呢,没必要强行微服务。特别是 python 。python 4 个服务一个分 1c 。还不如一起在 4c 的机器性能好
    sampeng
        14
    sampeng  
       6 小时 32 分钟前 via iPhone
    你这就属于,想用微服务规范和隔离开发,但又已经看到微服务最大的蛋疼。
    最好 code reviewer ,你说的问题都不是问题。monorepo 又不是想怎么写怎么写
    justdoit123
        15
    justdoit123  
    OP
       6 小时 18 分钟前
    @iyaozhen 微服务是既定现状,前人拆分的,至少已经快 8 年了,暂时没资源去改回单体。
    Rickkkkkkk
        16
    Rickkkkkkk  
       6 小时 15 分钟前
    common 包用起来其实没那么方便,远远不如代码里直接写一个 const 来的简单。
    justdoit123
        17
    justdoit123  
    OP
       6 小时 8 分钟前
    @sampeng 几乎没有 code review 。

    说得难听点,现在最好的 "code review" 就是测试 + 一上生产环境就挂掉。没埋雷,半夜爆炸就不错。

    所以,我才想多引入一下非人工的花销来尽量保证稳定。比如:

    1. type hint 。有点作用。
    2. 写 DTO 而不是一个 dict 满天飞。不然回头维护的时候很慢热,记不住 key ,经常导致 KeyError 。也算有点作用。
    3. 常量共享而不是用到就“手抄”一遍,这种手抄还会抄错。


    不怕各位笑话。我们还有一个“运行时的常量共享方案”是这样的:每个服务提供一个接口返回本服务的一些常量定义,然后服务启动的时候,去请求一遍这些常量定义。 我感觉是很难用。运行时、纯动态,你写代码的时候,根本不知道自己引用的值存不存在。IDE 也无法给你补全。
    midsolo
        18
    midsolo  
       5 小时 47 分钟前
    抽出来打一个 common 包,每个服务引入即可。

    common/
    ├── inner/
    │ ├── dto
    │ ├── vo
    │ └── ...
    ├── outer/
    │ ├── dto
    │ ├── vo
    │ └── ...
    └──
    183shl
        19
    183shl  
       5 小时 46 分钟前 via iPhone
    我的偏好是只把用到的或可能用到的再写一遍,可以硬编码的就直接写再放个注释。费点力气但是感觉体验很好。如果复用很强的再抽到 common ,因为不喜欢跳来跳去😁
    justdoit123
        20
    justdoit123  
    OP
       5 小时 22 分钟前
    @183shl 这个方案的阻力就在这里。

    我们其实有一个 common 库,里面放了一堆 utils 、架构 base 之类的代码。之前的 leader 强调,这里面不允许放入任何业务逻辑相关的代码。所以以前常量也没放这里。

    团队的人会觉得改这里面的代码,还要去升版本,很麻烦。

    有时候就直接在业务 repo 里,也写个 xxx utils 。


    我觉得你说的最后一句是有道理的,复用很强的再抽到 common 。这样就比较平衡。偶尔一两次的依赖,就直接抄一遍。依赖次数多了,再抽到 common 。很明显一定会被依赖很多次的,一开始就直接写 common 里。
    kuanat
        21
    kuanat  
       3 小时 22 分钟前
    如果是同一个语言的微服务,最起码还可以直接引入代码。如果是不同语言的项目混用,那几乎就只有 dsl 代码生成这一条路了。

    抽象出来 common 包存放数据结构之类的定义,这个思路是对的。我大概几年前也经历过类似改造,尽管项目本身不大,但这个过程挺痛苦的,因为这个改造看的其实是自动化的水平。

    我印象当时选定的方案是 git submodules ,因为这个功能在之前的工作流中几乎用不到,还专门开会做团队培训……然后是几个人花了几天抽象了一个公共数据结构定义,写了个 common 包,准备一个一个微服务重构。

    这个过程中发现的各种不一致的 bug 就不提了,最大的障碍是没人敢直接用新实现替代旧的版本,尽管初期改的几个包都是挑的代码少的软柿子。而且 submodules 方案也增加了开发人员的心智负担,因为一般的特性分支工作流,需要经常在维护旧代码和开发新功能上来回切换,用 submodules 就要注意每个分支和 common 依赖版本的对应。最后是自动化运维的部门出了大力,在生产环境做了个流量镜像,复制了一部分请求到新版本部署的测试服务器上,然后看执行结果和原版是否一致。

    在这之后又增加了一个人为规定,就是所有项目 repo 都必须包含构建脚本,其实就是几个 hooks 检查 submodules 是否更新或者版本对应正确。因为我们用的是特性分支工作流,只要核心的 master/dev/staging 几个分支提前做好与 common 分支的对应(技术上是对应到 commit ),common 的更新就可以通过特性分支简单合并到每个项目中,实现由上至下的传播,开发人员只要知道在当前项目的哪个分支上工作即可,不需要关心依赖的手动更新。

    这样改完了效果还是挺明显的,没有各种重复、不一致的定义,ide 补全也可用。只是增加管理成本,特别是负责 common 维护的。因为基础数据结构的改变,可能影响 API ,还可能影响 ABI ,即便是结构体增加一个字段,都有可能不兼容。同时 common 的改动要伴随所有微服务模块的改动,测试一定要跟得上才行。

    至于常量的部分,我认为需要根据常量本身来区分。如果是那种界面上的文字肯定是放到直接使用的包比较好,剩下大部分常量,比如服务器地址这些,我认为还是在环境变量中设置比较好。体现到项目中就是 dotenv 这种,环境不同设置也不同。这个方案需要额外处理两个细节,一个是如何集中管理或者持久化,二是一些类似密钥的不适合用明文的要如何处理。

    对于单语言项目,简化处理可以用公共 constant 包,对于多语言项目来说,一般是集中定义,然后通过 sed 之类的工具在源码级别上做替换。
    mightofcode
        22
    mightofcode  
       3 小时 14 分钟前
    人生苦短 没什么好办法
    关于   ·   帮助文档   ·   自助推广系统   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2411 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 15:38 · PVG 23:38 · LAX 08:38 · JFK 11:38
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.