作者:潘吉祥
自从厌倦于追寻,我已学会一觅即中。
通常微服务面临的问题
分布式会话
分布式配置
服务注册和服务发现
负载均衡
分布式数据一致性
熔断、限流、降级
网关建设
服务监控和链路追踪
……
面对这些问题,目前业界已经有了许多成熟的解决方案,如SpringcloudNetflix系列和SpringcloudAlibaba系列,以及其它一些开源项目组件,这些系列组件能够让大多数开发者做少量工作以解决上述微服务构建面临的问题。
一般来说,作为开发者,我们也认为这一切没什么可说的,当我们发现微服务的设计热火朝天,于是一顿操作,将传统的单体改造为“微服务”,我们熟练且有成就感地使用着这些技术,解决着高大上的问题,觉得我们正走在微服务的康庄大道。
事实上,大多开发者没有注意到的是(包括作者本人):这些技术本身是那些开源公司自己内部业务、文化技术体系的逐步发展产生的一种结果,开发出这些框架本身不是他们原始的目的,那些只是在解决公司发展遇到的问题过程中最终产出的客观结果,然后他们将这种瓜熟蒂落的结果开源出来。
这就是像欧洲发明了蒸汽火车,传到中国,中国用驴子拉着车厢在轨道上走。我们也用了火车,事实上,这基本上是两码事了。
蒸汽火车
马拉火车
我想说明的是:大公司的成功从来不是技术,那是我们外人眼里看到的东西,真正重要的因素是整个服务群体内部的体系设计,设计之下出解决方案,这是微服务的灵魂。车厢和轨道是外在,蒸汽动力才是核心,同样,治理组件只是外在,服务设计才是灵魂。
微服务的“客户端”和“服务”
上面提到了微服务开发所面临的问题,问题客观存在,这毋庸置疑。然而这些问题所涉及到的范围远远没有我们所认为的那样大,它不应该涉及整套服务的犄角旮旯,良好的设计将它们阻断在一层边界。就像冬天的湖面,湖面之上寒风凛冽,寸草不生,湖面之下,阳光明媚,一切正好。
通常,我们把一组应用程序实例所表示的单独的逻辑实体,我们称之为一个服务。但是,我们在微服务中使用了请求响应(request-response)的模式,这种模式下,一个服务直接被其它服务请求调用(阻塞调用),同时也会直接去请求调用另外的服务,这样看来,我们的服务承担这两个角色:服务和客户端。从设计上来说,这有违单一职责。
同时承担两种角色的服务
按照职责分明的设计,服务只被动接受请求,而不负责主动发出请求,这是高内聚,低耦合,因为一旦阻塞式request,就证明你已经强依赖了外部,提高了自身出现问题的风险,同时大型系统数以万计的服务调用全部依托统一的注册中心,这有违去中心化的原则,这个大多的依赖地方容易成为整个系统的瓶颈(即使客户端存储地址列表的缓存,不是每次调用都去请求注册中心,更多情况下,这种中心化的设计就是不合理的,据说亚马逊每秒钟都有上万和服务容器销毁重建,这也说明了一般规模公司不建议微服务的必要)。
好了,接下来是问题的重点,基于两种角色的设计,我们来说明一下服务发现。
用户服务要调用订单服务,订单服务也会调用用户服务,二者实例众多,为了方便服务调用,需要一个保存服务地址的第三方组件,每个服务的所有实例都需要注册到服务管理组件,并且随着实例的启停动态增减注册信息。我们觉得微服务的服务注册和发现组件就是这么用的。
让我们产生这种错觉的重要原因是服务承担了客户端的角色,事实上我们认为服务既是服务端又是客户端这本身就没有什么不妥,然后无限套娃。实际上,我们能够让服务只承担服务端的角色。注意,这不意味着我们不需要服务发现和注册中心,我们只是将它入侵的范围进行了缩小,如下图所示:
相对服务之外的客户端和服务内部之间的服务端依然用到了注册中心,而且这样的注册中心服务于特定的服务,不再会成为系统的瓶颈,符合云原生架构的原则。
到这里,如果你明白我说的意思,你大概就能想到类似网关这种在原来的微服务设计中容易成为系统瓶颈的组件也同样适合这样的改造。
要分离这种双重职责的服务设计,我们需要使用事件驱动架构。再次重申。我一直在强调事件驱动,这不是说微服务应该全盘事件驱动化,用下面的图来说明:
我们通常认为这种事件驱动是解耦的,而且我们通常会使用 消息中间件的方式,这不是说消息中间件就是唯一的选择,我们事实上想要的是类似消息中间件的一种通信方式,这里给出一个可能不太被开发者熟知的一个技术:rsocket。有兴趣的小伙伴可自行搜索资料进行了解。笔者后续也将给出相关介绍。
这是笔者使用springcloud-stream实现的一个事件驱动demo,有兴趣的可以查看:
https://github.com/rockit-ba/eventdriven-simple-demo
关注不迷路。