DreamMesh架构设计(1)-系统核心
对于一个微服务系统,无论是传统的侵入式,还是新兴的Service Mesh,其核心骨干,主要是三大模块:服务注册,配置,SDK。
这是我自己经过这些年的探索与实践,总结下来的对微服务体系本质的一个个人感悟。而这个思路,会贯彻在接下来的Dream Mesh的架构设计中。
服务注册
包含服务注册与服务发现,终极目标:解决服务间感知的问题。
即,当我们希望实现服务A到服务B的调用时,我们必然需要获知服务B的所在。考虑到分布式系统,服务B存在多个实例,因此在调用时需要得知服务B的所有实例信息。由于存在诸如就近访问等优化需求,因此有时也需要提供作为调用发起者A的实例信息。
服务注册的终极三连问:
- 我是谁?(答:调用的发起者服务A)
- 我要访问谁?(答:服务B)
- 它在哪里? (答:服务B的实例列表)
事情开始总是简单,然后慢慢变的复杂:
- 由于服务实例是动态变化的,因此需要一个存储机制来保存服务实例以便查询
- 为了保证服务信息的一致性,因此通常会引入分布式一致性方案如zookeeper/consul/etcd等
- 我们需要设计服务注册的模型,然后转换为一个数据结构来保存
- 最开始的时候,信息只包含基本的内容,如ID,IP地址,端口等
- 为了支持多版本,将服务版本加入到注册信息,然后基于版本信息做版本过滤和隔离,或者实现语义化版本
- 为了支持多机房和就近访问,加入region/zone等信息
- 为了实现灵活的实例刷选,加入label信息,然后实现基于label的过滤
- 我们便有了很多强大的服务路由功能,实现诸如灰度等特性
- 为了方便的携带其他非label信息,加入annotation
- 我们开始考虑把事情做的更好
- 为了保存实例信息的有效性,需要对服务实例做健康检查
- 为了及时通知服务实例的动态变化,需要实现监听和通知
- ……
一步一步的,我们就得到了一个完善的服务注册实现,然后我们将代码打包进SDK,分发下去。当系统中所有的服务都实现了服务注册时,那么我们就可以通过服务发现来查找所有我们需要调用的服务。就这样,服务间感知的使命得以完成。
后面在服务发现(项目名:Dream Registry)的详细设计中,我们会引入更多更复杂的特性:
- 我们要统一服务注册的模型
- 我们需要兼容多种服务注册的实现
- 我们需要跨多个集群打通服务注册
- 我们需要将service mesh和非service mesh连接起来
- 为各种基于服务注册机制的服务治理功能做好准备
- 提供更友好更简单而又更强大的客户端
但是,事情的本质没有变化:服务注册与服务发现,始终是聚焦于实现服务间彼此感知。
配置
服务注册完成了服务间的感知,现在我们的服务A知道了服务B的所在,我们可以发起请求。
然后在发起请求时,我们发现需要有一些输入来决定事情具体该如何做:
- 负载均衡用什么算法?如果支?持权重,则各个实例的权重是多少?
- 版本策略是什么?是严格一致?是忽略版本?还是语义化版本?
- 要不要开启熔断?熔断的阈值是什么?
- 要不要重试?重试多少次? 判断可以重试的条件有哪些?
- 要不要做灰度?要不要做路由?
所有这些配置,都和服务间通讯有关,有些和服务注册非常紧密,有些和后面要谈到的SDK非常紧密。总而言之,在服务A调用服务B的过程中,有一些操作细节是可以定制和修改的。我将这些内容称为"服务配置"。
之所以要在配置前面加上"服务"二字,是因为通常大家理解的配置,往往是另外一种:和服务间通讯无关,而与应用的业务逻辑和实现细节有关。这一类配置我称为"业务配置"。
在这两者之外,还有一种常见配置:对各种资源的配置,比如数据库访问信息,连接池设置,Redis/Mongo等外部资源配置。这些配置即和服务间通讯无关,也和业务逻辑无关,因此我将它们单独出来称为"资源配置"。
这里有一个非常微妙的关系:服务配置从代码实现上说是配置的一种,但是服务配置和服务注册的关系又特别的紧密,完全是注册中心不可分割的一部分。因此,注册中心和配置中心该如何划分界限,就成为一个需要权衡的问题:服务配置,究竟应该放在注册中心,还是应该放在配置中心?还是干脆单独拉出来?
曾经在某个汇集国内诸多家服务化框架核心开发者的微信群中讨论过这个问题,最后大家达成的一致如上图所示:
- 实现层面上,服务配置应该重用配置中心的代码
- 管理层面上,服务配置应该归属注册中心
在Dream Mesh的设计中,我们遵循这个做法,Dream Mesh的服务注册/服务配置/服务治理中心的关系如下:
配置中心实现如下:
应用和注册中心/配置中心的关系如下:
最后,总结一下配置在微服务体系中的核心价值:
- 服务配置:定制和改变服务间通讯的行为
- 业务配置:定制和改变服务的业务逻辑
- 资源配置:定制和改变对外部资源的访问
SDK
有了服务注册和服务配置,我们服务A对服务B的调用,就基本准备好了:
- 服务A已经通过服务发现机制感知到了服务B的所在
- 服务A已经通过服务配置获取了访问过程中的行为方式
现在该发起实质性的调用了,将请求从服务A发送到服务B:
- 以什么样的形式体现请求的内容?是发送一段内存,还是发送一个消息?
- 如果是消息,选择何种方案做序列化的编解码?
- 网络请求协议如何选择?TCP/UDP? HTTP/1.1,HTTP/2,自定义TCP?
- 客户端使用什么编程语言,选用何种类库/框架?
- NIO/AIO等的时候
- 负载均衡的实现,熔断的实现,重试的实现,还有路由,灰度,认证,加密等……
- 以及前面提到的,对服务注册的使用,和对服务配置的遵守
所有这些的实现,我都统称为SDK。在这里,我们可以选择简单的方案如httpclient发送json,可以选择成熟的RPC方案如thrift,可以直接采用各种框架如dubbo/spring cloud,甚至我们念念叨叨的service mesh也可以理解为一种特别的SDK。
无论选择何种SDK方案,最终,我们的目标都是通过SDK,将我们的请求从服务A发送到服务B。
SDK在微服务体系中的核心价值也就清晰了:完成请求发送的具体过程。
总结
服务注册/配置/SDK,三者配合,就形成了微服务体系的骨架。
Dream Mesh在接下来的设计和实现中,都将围绕这三个核心模块来完成。当然这里SDK的内容会比较多,平常大家的关注点也会更多的聚焦于SDK的实现,对于服务注册和配置的重要性往往认识不足。
从个人的感悟上说,也是在摸爬滚打这些年,在重新认真的审视服务注册和配置之后,才体会到他们的重要。
讨论和反馈
TBD:等收集后整理更新
后记
有兴趣的朋友,请联系我的微信,加入Dream Mesh内部讨论群。