单体应用拆分为微服务遭遇水土不服,服务间资源调用不畅,如何修改架构?

190 天前
 chinaguaiu

如题,公司团队此前比较缺乏微服务开发的经验,开发前期也只是简单按照模块划分的思路设计了微服务,思路简述为:在业务上呈现内聚的资源将被整合为一个服务,服务的业务逻辑实现是需要频繁访问这部分资源的;而如果仍然需要额外的资源(这个资源划分到了其它服务),通过服务间通信解决(使用 Restful 请求或者中间件);一些服务间通用的资源通过 jar 包依赖的形式引入。

一段时间的开发之后产生了一些阻碍,主要发生在服务间资源调用中,如下:

  1. Restful 接口方式调用服务间资源十分缓慢和繁琐。服务对外暴露的 Restful API 全部是直接面向外部调用设计的,每一次调用都需要走完完整的参数处理以及鉴权验证逻辑。服务间之间互相调用依然是使用这种 API ,每一次调用都基本等同于外部调用,需要重新走一次参数处理和鉴权验证的逻辑,运行效率很差,也导致方法实现中充斥大量冗余参数和冗余返回结果(对于服务间调用来说) 。
  2. 某些功能的实现需要频繁进行服务间资源调用。这个意思不是服务调用链路太长,而是诸如服务 A 的某一业务逻辑需要同一时间需要重复访问服务 B 五到六次,或者说服务 A 的某一业务逻辑需要同时访问服务 B 、C 、D 、E 、F 等。又由于 1 的问题,效率极差。

如何针对性解决两个问题?重新设计一类面向内部服务调用的 Restful 接口?还是服务间调用引入 MQ 中间件?

想请教一下 V 友们有没有相关的解决思路,由于公司团队包括我自己也没有太多开发微服务的经验,还望大家可以详细讲讲。

2996 次点击
所在节点    程序员
40 条回复
kkk9
190 天前
1 、参数处理以及鉴权验证逻辑应该由网关前置处理,后面的相关服务默认网关给定的数据是完全可信的。同时拆分中间服务的参数和结果,只保留必须的传递。最后由网关后置处理进行统一输出。

2 、优化调用就需要具体问题具体分析了。比如:服务 A 的某一业务逻辑需要同一时间需要重复访问服务 B 五到六次,是否可以优化成一次全部读取,只在服务内各取所需。
emSaVya
190 天前
rpc 调用怎么会慢? 直接上 brpc 又快又好。

内部服务不需要鉴权。

同时访问的服务 整理一个 tag 出来, 没有依赖的直接并行请求。
devopsdogdog
190 天前
不合适就不拆,拆出毛病才是正常的。
业务量真的很大?大到每个模块都得独立?
Chad0000
190 天前
我也在给公司拆单体,我的方案更柔和:

- 先确保每个服务(我这里是模块)的独立性,尽量不要引用其他模块
- 模块功能抽象成接口,功能定义和实现分离
- 提出运行环境的概念,即负责跑这些模块的层
- 运行环境也负责对调用者返回实际实例
- 将大部分常见操作抽象,实现放运行层,即避免模块接触实现细节
- 模块只管触发事件,不订阅
- 设定一个统一的业务模块负责订阅所有消息,再通过运行层调用其他模块


关键部分:
- 一个运行环境可以托管任意个模块,如果模块调研在同一个环境则直接本地调用。
- 迁移过程中基本上只有一个运行环境就是单体本身,本地调用不影响性能
- 模块和它抽象的接口在单体完成依赖注入,这样单体可以直接调用新的模块
- 之后你可以实现跨运行环境通讯,也就是 RPC
- 可以将经常调用的模块放同一个运行环境中,以提升性能
- 模块所需基础能力(数据库访问/配置读取/缓存访问等)都抽象并由运行环境提供后,那么模块基本上可以动态加载了

基于此:

- 如果运行环境只跑一个模块,那就是微服务
- 如果运行环境跑所有模块,那就是单体
- 介于两者之间的,那就是混合模式
- 上述设计保障了只要你在运行环境你就可以访问所有模块
- 考虑到业务模块订阅了所有消息,那么每个运行环境都部署一个业务模块会避免更多的 RPC

目前这个计划实施得相当好。
teble
190 天前
一般来说鉴权不应该是微服务网关统一鉴权吗,所有微服务处于同一内网的情况理论上应该是相互可信的,不太清楚具体业务,个人感觉服务间通信的鉴权开销是完全没必要的
kkk9
190 天前
@Chad0000 #4 你这个感觉就是容器概念啊,单独一个模块的时候是微服务,多个的时候是单体。感觉出问题就是 All in BOOM 😅 个人见解,也可能我没理解透彻
wu00
190 天前
先搞个简单粗暴的改造,外层网关 => BFF => 业务服务
网关处理鉴权认证,请求只转发到 BFF ,BFF 内网并行请求各服务&&整合数据,业务服务不对外暴露,一开始不要把服务拆的太细,尽可能保证独立。

最后,单体改造成支持分布式再铲铲屎山就行了,最好别盲目搞微服务,如果是为了“练手”搞“履历”,当我没说。
iyaozhen
190 天前
其实没遇到实际问题不用拆,我们都开始合了。上千个服务,维护起来也受不了
Chad0000
190 天前
@kkk9 #6
问题他它可以让你随时回到单体或者微服务架构。“后悔”来得及。谷歌好像也在搞一个类似的框架,可独立也可同进程,go 写的。忘了名字了。
franktopplus
190 天前
领域切分清晰,单一职责原则;
不相干的服务不依赖;
一个微服务的异常不会影响其他服务
Sean46
190 天前
@Chad0000 Service Weaver ?
4kingRAS
190 天前
无状态的服务才适合做微服务,如果状态很多,还是单体好
4kingRAS
190 天前
另外,从单体切换到微服务这么大的架构变动,应该是一点点的切,而不是一下子打散了。微服务的目的是做到平滑水平扩容,牢记自己的目的
chinaguaiu
190 天前
@wu00 项目的要求,有可能是为了后期移交或者整合第三方系统。肯定不是技术团队的要求,团队成员大多不具备微服务开发的经验,能选的话肯定不会写成微服务的。
chinaguaiu
190 天前
@teble 应该这么做,但是开发前期团队其实脑子里都不存在微服务网关这个概念,所以采用的是每个服务单独调用鉴权 SDK 包来实现鉴权,实话说就是单体应用暴露 API 接口的写法,认为每一个暴露的 API 均为外部调用。后面虽然加上了微服务网关,但是之前写的 Restful 接口签名已经很难改了,要改的话等于重构,那就是下一个开发周期的事情了
chinaguaiu
190 天前
@4kingRAS #13 霸王硬上弓,上面有意通过这个项目积累团队开发微服务的经验
lanlanye
190 天前
1. 添加 Gateway ,把验证和鉴权放进去,其他服务中去掉(要保障内网安全的话另说)。更重要的是首先检查你的服务间通信路径,不要内网通信先去公网绕一圈,那样肯定慢…

2. 看起来更像一个设计问题,需要具体分析,没法直接给建议。
julyclyde
190 天前
@emSaVya 对,内部服务不需要鉴权,留着等将来内卷的时候再改造
ZZ74
190 天前
别加 gateway ,到时会成为瓶颈。
鉴权做简单,比如只验证 token 是否合法,拦截器层就能做掉。参考 jwt ,或者验证下是否内部 ip ,或者干脆不做!!!内部调用何必呢。

第二点属于服务没拆好,别为了所谓的微服务,真的拆的稀碎。,建议耦合度相对高的合并就行了。所谓的宏服务。

RPC 就不用试了也解决不了你的问题,最终都是网络请求,和鉴权要不要啥的两码事。
ZZ74
190 天前
@iyaozhen
你们这算是瞎拆还是 KPI 拆啊。某全球在用的协同办公软件(系统)都没有这么多

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

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

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

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

© 2021 V2EX