如题,公司团队此前比较缺乏微服务开发的经验,开发前期也只是简单按照模块划分的思路设计了微服务,思路简述为:在业务上呈现内聚的资源将被整合为一个服务,服务的业务逻辑实现是需要频繁访问这部分资源的;而如果仍然需要额外的资源(这个资源划分到了其它服务),通过服务间通信解决(使用 Restful 请求或者中间件);一些服务间通用的资源通过 jar 包依赖的形式引入。
一段时间的开发之后产生了一些阻碍,主要发生在服务间资源调用中,如下:
如何针对性解决两个问题?重新设计一类面向内部服务调用的 Restful 接口?还是服务间调用引入 MQ 中间件?
想请教一下 V 友们有没有相关的解决思路,由于公司团队包括我自己也没有太多开发微服务的经验,还望大家可以详细讲讲。
1
kkk9 2023-11-01 18:39:01 +08:00 1
1 、参数处理以及鉴权验证逻辑应该由网关前置处理,后面的相关服务默认网关给定的数据是完全可信的。同时拆分中间服务的参数和结果,只保留必须的传递。最后由网关后置处理进行统一输出。
2 、优化调用就需要具体问题具体分析了。比如:服务 A 的某一业务逻辑需要同一时间需要重复访问服务 B 五到六次,是否可以优化成一次全部读取,只在服务内各取所需。 |
2
emSaVya 2023-11-01 18:39:06 +08:00
rpc 调用怎么会慢? 直接上 brpc 又快又好。
内部服务不需要鉴权。 同时访问的服务 整理一个 tag 出来, 没有依赖的直接并行请求。 |
3
devopsdogdog 2023-11-01 18:40:08 +08:00 via Android 2
不合适就不拆,拆出毛病才是正常的。
业务量真的很大?大到每个模块都得独立? |
4
Chad0000 2023-11-01 18:40:23 +08:00 via iPhone
我也在给公司拆单体,我的方案更柔和:
- 先确保每个服务(我这里是模块)的独立性,尽量不要引用其他模块 - 模块功能抽象成接口,功能定义和实现分离 - 提出运行环境的概念,即负责跑这些模块的层 - 运行环境也负责对调用者返回实际实例 - 将大部分常见操作抽象,实现放运行层,即避免模块接触实现细节 - 模块只管触发事件,不订阅 - 设定一个统一的业务模块负责订阅所有消息,再通过运行层调用其他模块 关键部分: - 一个运行环境可以托管任意个模块,如果模块调研在同一个环境则直接本地调用。 - 迁移过程中基本上只有一个运行环境就是单体本身,本地调用不影响性能 - 模块和它抽象的接口在单体完成依赖注入,这样单体可以直接调用新的模块 - 之后你可以实现跨运行环境通讯,也就是 RPC - 可以将经常调用的模块放同一个运行环境中,以提升性能 - 模块所需基础能力(数据库访问/配置读取/缓存访问等)都抽象并由运行环境提供后,那么模块基本上可以动态加载了 基于此: - 如果运行环境只跑一个模块,那就是微服务 - 如果运行环境跑所有模块,那就是单体 - 介于两者之间的,那就是混合模式 - 上述设计保障了只要你在运行环境你就可以访问所有模块 - 考虑到业务模块订阅了所有消息,那么每个运行环境都部署一个业务模块会避免更多的 RPC 目前这个计划实施得相当好。 |
5
teble 2023-11-01 18:42:26 +08:00
一般来说鉴权不应该是微服务网关统一鉴权吗,所有微服务处于同一内网的情况理论上应该是相互可信的,不太清楚具体业务,个人感觉服务间通信的鉴权开销是完全没必要的
|
6
kkk9 2023-11-01 18:52:16 +08:00
@Chad0000 #4 你这个感觉就是容器概念啊,单独一个模块的时候是微服务,多个的时候是单体。感觉出问题就是 All in BOOM 😅 个人见解,也可能我没理解透彻
|
7
wu00 2023-11-01 18:55:40 +08:00
先搞个简单粗暴的改造,外层网关 => BFF => 业务服务
网关处理鉴权认证,请求只转发到 BFF ,BFF 内网并行请求各服务&&整合数据,业务服务不对外暴露,一开始不要把服务拆的太细,尽可能保证独立。 最后,单体改造成支持分布式再铲铲屎山就行了,最好别盲目搞微服务,如果是为了“练手”搞“履历”,当我没说。 |
8
iyaozhen 2023-11-01 18:58:01 +08:00
其实没遇到实际问题不用拆,我们都开始合了。上千个服务,维护起来也受不了
|
9
Chad0000 2023-11-01 19:16:40 +08:00 via iPhone
@kkk9 #6
问题他它可以让你随时回到单体或者微服务架构。“后悔”来得及。谷歌好像也在搞一个类似的框架,可独立也可同进程,go 写的。忘了名字了。 |
10
franktopplus 2023-11-01 19:22:16 +08:00 via Android
领域切分清晰,单一职责原则;
不相干的服务不依赖; 一个微服务的异常不会影响其他服务 |
12
4kingRAS 2023-11-01 19:47:21 +08:00 2
无状态的服务才适合做微服务,如果状态很多,还是单体好
|
13
4kingRAS 2023-11-01 19:49:20 +08:00
另外,从单体切换到微服务这么大的架构变动,应该是一点点的切,而不是一下子打散了。微服务的目的是做到平滑水平扩容,牢记自己的目的
|
14
chinaguaiu OP @wu00 项目的要求,有可能是为了后期移交或者整合第三方系统。肯定不是技术团队的要求,团队成员大多不具备微服务开发的经验,能选的话肯定不会写成微服务的。
|
15
chinaguaiu OP @teble 应该这么做,但是开发前期团队其实脑子里都不存在微服务网关这个概念,所以采用的是每个服务单独调用鉴权 SDK 包来实现鉴权,实话说就是单体应用暴露 API 接口的写法,认为每一个暴露的 API 均为外部调用。后面虽然加上了微服务网关,但是之前写的 Restful 接口签名已经很难改了,要改的话等于重构,那就是下一个开发周期的事情了
|
16
chinaguaiu OP @4kingRAS #13 霸王硬上弓,上面有意通过这个项目积累团队开发微服务的经验
|
17
lanlanye 2023-11-01 20:13:15 +08:00 via iPhone
1. 添加 Gateway ,把验证和鉴权放进去,其他服务中去掉(要保障内网安全的话另说)。更重要的是首先检查你的服务间通信路径,不要内网通信先去公网绕一圈,那样肯定慢…
2. 看起来更像一个设计问题,需要具体分析,没法直接给建议。 |
19
ZZ74 2023-11-01 20:43:39 +08:00
别加 gateway ,到时会成为瓶颈。
鉴权做简单,比如只验证 token 是否合法,拦截器层就能做掉。参考 jwt ,或者验证下是否内部 ip ,或者干脆不做!!!内部调用何必呢。 第二点属于服务没拆好,别为了所谓的微服务,真的拆的稀碎。,建议耦合度相对高的合并就行了。所谓的宏服务。 RPC 就不用试了也解决不了你的问题,最终都是网络请求,和鉴权要不要啥的两码事。 |
21
tool2d 2023-11-01 21:01:47 +08:00 via Android
rest 短链接对于维护复杂状态还是挺麻烦的,最好就是改成长链接的 rpc 来维护。
|
22
wyx119911 2023-11-01 21:08:29 +08:00 1
1. 微服务之间调用鉴权改为验证加密票据,票据由对外网关统一验证用户信息后办法,后续判断票据有效跳过没必要的验证逻辑。
2. 首先问题可能是不熟悉微服务的架构模式,导致调用不合理。另外有些调用确实很多无法避免的情况,可以用并发请求来解决(例如多协程),这里要注意瞬时请求量,保护被调服务不被冲垮。 |
23
lsk569937453 2023-11-01 21:12:57 +08:00
并发不到 200 的业务还搞这么复杂吗???确定不是过度设计?
|
24
xiangyuecn 2023-11-01 21:21:53 +08:00
话说天下大势,分久必合,合久必分。
|
26
chinaguaiu OP @lanlanye 2 首先肯定是设计问题,然后又由于 1 加剧了状况,需要写大量的冗余代码拼装请求去进行服务调用,响应时间还很长
|
27
chinaguaiu OP @ZZ74 #19 “内部调用何必呢。” 是的内部调用没有必要鉴权,现在问题是暴露出的所有接口都是面向外部调用设计的,例如说 api 方法签名里包含请求参数 appid 、userid 、sign 、time 等,就没考虑内部调用的场景,导致现在内部调用都是先拼接参数字段再发起请求,和维持一个外部用户 agent 没有区别。所以现在是想绕过这些已经定义的 Restful api 去进行内部请求,但是没有思路。
|
28
chinaguaiu OP @lsk569937453 项目要求,不得不上,当时选取微服务架构的原因就不是技术因素
|
29
chinaguaiu OP @wyx119911 1. 思路不错,感谢; 2. 可以考虑。并发指的是同步并发还是异步并发?这种内部业务逻辑要求即时处理数据的场景不能使用异步吧。
|
30
tangAtang 2023-11-01 22:12:05 +08:00
@chinaguaiu 应该说的是同步并发,比如需要拿 A+B+C 数据,数据之间没依赖就并发拿。但是感觉这也治标不治本
|
31
wmper 2023-11-01 22:18:18 +08:00
这样才不会失业啊,啥搞才有搞头。
|
32
Chad0000 2023-11-02 02:24:50 +08:00 via iPhone
@Sean46
嗯,就是这个。我都把方案搞出来了,开始写代码了,才看到这个框架。当然我也用不到因为我是 C# |
33
xuanbg 2023-11-02 07:49:04 +08:00
1 、内部 Restful API 调用不可能慢,慢一定是别的原因造成,必须要把这个原因找出来
2 、内部访问不需要鉴权,所以,鉴权放到网关上去实现。具体可以参考我的开源项目,点我头像自己找 3 、服务 A 的某一业务逻辑需要同时访问服务 B 、C 、D 、E 、F 等,这种情况一定是设计问题,需要重新设计才能解决 |
34
yinmin 2023-11-02 08:33:42 +08:00 via iPhone
跨服务器的 1 次请求至少 1 毫秒。微服务不是函数,别在循环里几百/几千次去调用,一定是慢的。
用户一次 click 操作,调用 1 次 a 服务,每次 a 服务调用 10 次 b 服务,每次 b 服务调用 10 次 c 服务,每次 c 服务运行了 10 条 sql 数据库服务,就是妥妥的耗时>1 秒。 |
35
yinmin 2023-11-02 08:54:21 +08:00 via iPhone 1
如果运行太慢,项目无法交付,短平快的方式是:
1. 去掉用户权鉴改 token 直接校验 2. 将调用密度高的微服务安装在一台机器里,内部使用 unix socket 替代 tcp 。然后依样画葫芦安装出多台机器跑负载均衡。 用以上 2 条之后,速度可能会有十倍,甚至百倍的提升。 |
36
fkdog 2023-11-02 09:41:04 +08:00
乱拆的结果就是这样的。
如果现在连这种 controller 层的问题都能难倒你们,后边各种事物、同步延迟、一致性岂不是能把你们搞炸锅? 你们业务量多大要拆出服务来啊 |
37
dahuahua 2023-11-02 09:42:48 +08:00 1
在第一层校验通过后,往请求头里写个 verify-pass 的标志,内部服务先判断是否有这个标志,再来决定是否进行二次校验,将来单独交付内部服务的时候,也不会缺失这部分校验能力。
|
38
cp19890714 2023-11-02 10:01:08 +08:00 1
* 服务对外暴露的 Restful API 全部是直接面向外部调用设计的.
内部接口与外部接口需要分开. * 需要重新走一次参数处理和鉴权验证的逻辑 鉴权应该在 gateway. 既然现在已经在每个服务中都有鉴权了, 那可以为内部 API 添加一个统一前缀, 来跳过鉴权. 参数处理本来就是必要的, 省不掉. * 重复访问服务 B 五到六次 如果是同一个功能, 应该考虑 多个接口合并到一个接口 远程调用是 IO, 不能循环调用, 应该改为批量. 对于数据变化不大的, 使用本地缓存. * 或者说服务 A 的某一业务逻辑需要同时访问服务 B 、C 、D 、E 、F 等。又由于 1 的问题,效率极差。 大部分情况下, 一个功能不会同时调用 BCEDF 这么多的服务接口. 可以考虑下是否是服务拆得太细. |
39
jackwv 2023-11-02 10:41:44 +08:00
最上层做一个 API 聚合,下面的 RPC 服务 循环依赖就循环依赖,简单快捷。
|
40
wwwz 2023-11-02 10:53:08 +08:00
好家伙,自创微服务是吧
上 Spring Cloud Alibaba 全家桶 |