服务化演进的一些问题探讨
前两天在 go-krotas 的微信群里,看到一哥们儿提问:
在 k8s 中,不理解为啥还需要 registry ? 直接用 service 不就行了吗?
我回答道:
我觉得直接用 service 主要的问题是 扩展性问题。业务上会有各种对服务分组的需求,比如版本、地域等等。 但是四层的负载均衡比较难做到。所以基于注册中心 + subset 的方式就有了生存空间。
不过,如果是比较简单的场景,直接用 service 也是够了的。
之后,一哥们儿回到:
把这些需求沉到 istio 运维层,如果你用 registry,那就是开发者自己管了
这个问题其实很多人都会问到,之前和文浩也讨论过这个话题,当时我的想法大概是,把很多东西下沉到 运维层 当然没问题,但实际上这是一个需要看 成本 和 公司阶段 的问题。
对于一个初期的项目来说,使用最原始的 单体结构
实际上是成本最低的,毕竟一个项目能发展到什么阶段,对于绝大多数项目而言,都是玄学,难以参透,所以 保持软件架构的简洁
是非常重要的。进,可以按模块拆分;退,可以推倒重来;大不了项目一黄,扔掉完事儿。
当产品流量不错,服务逻辑也开始逐渐膨胀,就该有所规划地进行 模块的解耦设计,表现在代码侧,就是把不同 类型的接口 区分前缀
、把实现逻辑独立成包
、把 model 内联查询拆开
等等。
回到上面的问题,我们又该如何抉择 使用 registry 还是 使用 istio 呢?
我认为还是应当比较成本。 isito 等一系列服务治理的体系,在运维上的成本是不低的,如果要用其去替换 registry ,至少需要考虑以下几点:
- 我们是否有人才能够做好 istio 的维护 甚至开发?
- 假设我们能维护好,那么这套体系是否真的能够降低 业务开发同学的心智负担?
- 使用 registry 的时候,他们需要花多少精力去关注 服务治理 的东西?
- 他们是否真的能够做到不用再关心 服务分组(负载均衡)、认证、限流 等等?
- 这些价值,是否能 cover 住有专人去维护另一套复杂的体系带来的成本?
不得不说,当我们重新审视完上面几点,就会发现实际上两者很难说谁优谁劣,对小点的公司而言,几乎毫不犹豫选择使用 registry
( 当然,如果业务简单,会毫不犹豫地使用 service ); 对于很大的公司而言,可能会考虑使用 side car
的流量治理模式;对于中等大小的公司,可能就处于摇摆状态了……
一个很实际的例子,如果一家原来是用的 dubbo 或者 spring cloud
这一套的公司,他们已经有非常完善的生态了,不论是 服务注册发现 还是 降级限流熔断,或者是 负载均衡策略 等等,本就是业务开发同学应当知道的,成本也没有很高。 和上面说的这一套 ( side car ) 相比,他们又有什么动力转到这个方向上去呢?
毛剑老师 之前在一场直播中也简要回答过这个问题: B站几乎是统一语言的(golang),用的统一的框架( go-kratos ),任何服务治理上的问题,都只需要在框架层改动即可,几乎都是一次性工作,一劳永逸的事。
总结一下,技术的发展日新月异,云原生
、service mesh
、side car
、istio
、服务运行时
等等概念如日中天,甚至于有些年轻人生来就在 微服务 + 云原生 的环境中,认为 单体架构
甚至 SOA
已经是上个时代的产物了。
诚然,技术的发展给 社会生产力 带来了更高的效率,因此我们应当崇敬技术,应当拥抱新技术,甚至推动新技术的发展。但在工程上时,也需要考虑新技术的场景适用性,对比成本和收益,姑且压制住 技术人对于新技术的好奇和冲动,用更理性的思维去做抉择。
我实际上也是一个比较有技术人普标性格的人,好奇
、爱折腾
、喜欢尝试新技术
、较真
……,之所以会有上面的想法,可能也和一些经历有关。
公司有一个前端的项目,做的是官网,用的是 nuxt 框架,为的是用 服务端渲染(SSR)
。之后我们有一波比较大型的推广活动,就需要组织各端同学做压测,以保证容量和服务稳定性。这个服务没有太引起我的关注,因为按以往对官网的理解,就是一个静态页,只要资源往 CDN 一扔,啥事儿都不会有。
但出乎意料的是,组织压测的各小组汇报情况,官网的 qps 到 5k 左右时怎么都上不去了,各 pod 资源利用率也不高,就是不知道啥情况。搞了很久,踩了很多坑,最后还是没解决。
主要的坑有:
- ① 前端同学对压测的工具和流程不熟悉,花费较多时间学习相关操作
- ② 前端同学对在 linux 容器中如何排查问题不熟悉
- ③ 前端同学对服务间调用的网络链路等不熟悉
- ④ 基于 vue3 的 nuxt 会有 .mjs 文件格式,这在一些网关或者浏览器中会有坑( 需要设置特定的 mime type )
- ⑤ 没有同学能够 hold 住 ssr 的常见问题。
倒不是在踩前端同学有多菜,我也是从前端转过来的,我明白从日常工作的技能上来看,熟练掌握 linux 操作能进行问题排查 以及 提前预测方案问题 的同学毕竟是更少的。
后来勉强过了这次活动,总结复盘时,有同学也提出了,官网的场景 似乎 用 全站静态化 + 懒加载 的方式更加合适。 后面听说要改成静态化的方式,不过我也没继续跟了。
再举一个后端这边的例子。
我们有一个业务场景,是把 cavas 画布内的各项操作,进行合并,并转发给协同者。这条链路上,我们原来是 一个 消息服务
+ 一个 合并服务
,前者处理 客户端连接
、鉴权
、消息分发
等功能,后者承担 画布数据合并
的功能。
画布的合并实际上有两个步骤,一个是 索引,用来校验操作的合法性以及落盘,另一个是 合并,用来做真实的数据合并操作。 某一次规划索引的功能时,这个服务被拆成了两个服务,把 索引 和 数据合并 分开了。
由于我不是直接负责这块儿的,也没有怎么太关注具体的内容。
后来做压测,我负责这条链路的性能测试 以及 调优工作,才细致地去看了里面的设计与实现,之后也通过压测去验证一些想法,大致的情况是这样的:
服务被拆分成两个,他们之间通过 kafka
进行异步通信,由于 kafka 消息大小的限制,他们之间使用了 mongodb
做大消息存储;由于 版本的强约束
,两个服务之间又用了 mysql 做统一版本管理;由于 索引服务
在一些情况下,需要使用 全量数据
,他们之间又提供了 rpc 调用
,以获取数据。
当我去询问这样的价值时,得到的回答是 解耦
、错误隔离
、异步提升性能
、定向优化
等等理由……
当我表明我的想法,认为他们合在一个进程中更合理时,得到的回答是: 你要更 open 一些、大家都在做微服务,合在一起是反模式的
、他们现在运行得很好……
可能由于应对这种场景的经验比较欠缺,我一时也不知道该怎么说这事儿。 但我心里非常清楚,合在一起对 性能
、稳定性
、成本
、开发负担
等各方面都有非常大的价值。然后也不管一些反对的声音,花了几个周末的时间,在独立的环境中,对两个服务做合并,做优化,然后做压测…… ,最后的结果是 我所预期的 和 压测的结果 几乎一样,性能提升超过 10 倍,代码删减了接近一半,数据库调用从 4 次 降到 1 次,同样 qps 下,资源占用降低到原来的 40% ……
后来一次会议上我提出,微服务
是需要遵循一些原则的,我们对微服务的理解不能仅停留在 “微服务” 这个 名词 上,不是所有能拆的服务都该被拆分,至少要基于 DDD
的一些基本原则。
后来,看到一个架构的设计原则,认为:
我们做系统架构设计,宗旨就是
降低服务复杂性
,复杂性是万恶之源!
这和 istio 从原来的微服务模型 回归到 单体服务时,官方设计文档的第一句话不谋而和
Complexity is the root of all evil or: How I Learned to Stop Worrying and Love the Monolith
复杂性是万恶之源,不然我怎么会爱上单体,并且从此不再焦虑呢?
更多信息可以参考 istio-as-an-example-of-when-not-to-do-microservices
中文翻译可以参考 istio 为什么不再使用微服务
用一句很让人警醒的话来结束这篇文章,希望我们能以此共勉:
真正的大佬,都是能把复杂问题简单化的人
Wisdom begins at the end.
— Daniel Webster
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!