1、概述
前面几篇文章给大家详细的介绍了Zookeeper的基础概念以及应用的领域,今天我们讨论的话题是如何自研一套分布式服务框架。早些年有很多基于Dubbo和Zookeeper的分布式系统,这篇文章我们就来聊下如何设计一个分布式服务框架。
2、系统间交互
2.1、问题引入
首先我们想象一下,在一个分布式系统的环境中肯定是会有多个子系统(服务),每个子系统都负责处理和自己相关的业务,当发现自己不具备处理某项业务的能力的时候 这个时候就需要依赖(耦合)其他系统提供的功能了。也就是我们老生常谈的系统间交互的过程。
正如上图所示,Jerry想吃奶酪,但是需要依赖Tom才能拿到奶酪,这个过程就是一个系统间交互的过程,在这个过程里我们需要关注以下几点
1、Jerry需要找Tom
2、找到Tom后需要告诉Tom自己想要吃个奶酪
3、Tom需要接受到Jerry的需求
4、Tom需要给Jerry一个奶酪,或者告诉他怎么找到奶酪
2.2、服务之间怎么沟通
如果我们需要设计一个系统,完成上述的交互过程,我们就需要考虑以下几点内容
1、Jerry怎么找到Tom,去哪里找
2、Jerry通过什么方式告诉Tom自己的需求
3、Tom通过什么方式接收Jerry的请求
4、Tom将奶酪或者奶酪的位置以什么样的形式返回给Jerry
这个时候我们就需要制定一套规范,Tom每次外出活动的时候 需要把自己的位置告诉Spike,同样的Jerry每次出去玩的时候也需要将自己的位置告诉Spike。这样的话大家不仅可以自由的活动,并且还能够通过Spike联系到对方。
2.3、服务注册中心
如上图所示 Spike 记录了 Tom和Jerry的位置。当Jerry 需要调用Tom对外提供的服务的时候,就可以先询问Spike,通过Spike获取到Tom的ip,同样的Tom需要访问Jerry的时候也可以通过Spike获取到Jerry的ip。这样Tom 和Jerry 就都知道了对方的位置,也就能找到对方了。在这个过程中 Spike扮演的角色就是服务注册中心。
服务注册中心的作用之一就是记录各个服务的注册信息,并且还提供服务发现的功能呢,就类似Spike会记录Tom And Jerry 的IP,还能告诉Jerry Tom的ip,或者告诉Tom Jerry的位置,
2.4、通信协议
在这个过程中Jerry可以获取到Tom的ip,这样Jerry 就能够往Tom那里发送一个数据包,告诉Tom自己的需求,但是 这个数据包要怎么发出去呢?以什么样的组织形式发出去呢?Tom是否能够正常的解析?
这个时候就需要引入了网络通信协议了,通信协议是个比较庞杂的东西,后面我们再单独聊。我们先假设Tom和Jerry之间都是使用HTTP协议进行通信。
2.5、Jerry 拿到了奶酪
我们重新回到2.1章节的4个问题上面来
1、Jerry 可以从 Spike 哪里获取到 Tom的ip
2、Jerry 通过HTPP协议 将自己的需求 发送给Tom
3、Tom接收到Jerry 发送的HTTP请求后按照HTTP协议的格式和规范解析
4、Tom 将奶酪或者奶酪的信息 放在请求主体中返回给Jerry
好了,经过上面的4个步骤,Jerry 就能够拿到一块奶酪了。
3、顶层设计
我们大体上已经梳理清楚了Jerry 拿到一块奶酪的整个流程了,现在我们就来将这个流程落地,形成一款优秀的框架,首先我们需要做技术选型和架构设计的工作
3.1、功能规划
我们本次实现的是一款简洁优雅的分布式服务框架,前期我们需要实现的功能主要是 服务注册、服务发现、远程调用 这三块,他们是一个分布式服务框架的核心,后面我们再基于这核心的三大块逐步优化和完善,让我们的这款框架达到生产可用的要求。
项目以 jar 包的形式发布出去,第三方应用引入了这个 jar 就能基于它构建一套分布式系统。
这里先给我们的这个服务框架取个名字,暂时就先命名为 cheese(奶酪) 吧。
3.2、技术选型
基础架构我们采用 SpringBoot 和 Zookeeper 来实现,这里Zookeeper的客户端工具我们使用Apache Curator。相关的版本信息如下
SpringBoot | 3.2.9 |
Zookeeper | 3.9.1 |
Apache Curator | 5.7.0 |
3.3、架构设计
我们已经知道了系统间交互的过程了,现在我们需要对上述过程进行架构设计,将前面整个交互的过程设计成一套可落地的方案。
cheese 是一款分布式服务框架,我们从最顶层来看,cheese 需要实现远程调用的功能,这里我将它称为RemoteServer。
对于服务消费者(Jerry)来说,我需要知道服务提供者(Tom)的IP和端口这些信息。所以cheese需要提供服务发现的能力,同样的服务提供者Tom需要将自己的IP和端口对外开放,cheese需要具备服务注册的能力。这里需要分别设计 RegistServer 和 DiscoveryServer
最后我们需要和Zookeeper交互,这里就将这个模块叫做CuratorExecute吧,整体的架构如下图所示
当然,我们还需要一个框架内部的监听机制,当应用启动的时候,所有的需要对外暴露的服务初始化完成后,需要将这个服务的信息 通过 CuratorExecute 存放 到 Zookeeper上。这里我将这个组件设计成一个ApplicationListener。
4、详细设计
从顶层设计上我们已经知道 Cheese 作为一款分布式服务框架 必须包含 RemoteServer 、RegistServer、DiscoveryServer、ApplicationListener以及CuratorExecute 这些组件,下面我们就一起来详细的聊聊这些组件要实现的功能 以及相互之间需要怎么协作。
4.1、RemoteServer设计
RemoteServer 的主要 职责是远程调用,也就是承载服务消费者(Jerry)在获取到服务提供者(Tom)的ip和端口之后 将自己的请求发过去的能力。
关于应用层协议 这里我们先采用HTTP协议,因此我们可以选择相对简单的HttpClient实现。
这部分我们直接使用SpringBoot里面的 RestTemplate。我们可以直接将 RestTemplate 注入到 RemoteServer 中即可。
4.2、RegistServer 设计
RegistServer 是一个服务注册的组件,功能上看 它需要承载的能力是 将自己对外暴露的服务通过 CuratorExecute 组件注册到Zookeeper上去。那么怎么在应用启动的时候获取到需要外暴露的服务呢。
一种可行的方案是 当应用启动的时候,我们设计一个 ApplicationListener 组件,由这个组件去寻找需要对外暴露的服务,然后交给 RegisterServer
4.3、DiscoveryServer 设计
服务发现组件 需要实现的功能就是 从zookeeper上获取一个有效的服务对应的IP和端口,然后交给 RemoteServer 组件。支撑 RemoteServer 远程调用的能力。在Cheese 里面 DiscoveryServer 的功能 需要CuratorExecute来支撑
4.4、CuratorExecute设计
CuratorExecute 是cheese与Zookeeper交互的核心, cheese 需要通过 CuratorExecute 操作
Zookeeper。并且他需要为 RegistServer 组件和 DiscoveryServer 组件 保驾护航。
CuratorExecute 的具体功能 我们使用 Apache Curator 来实现
4.5、ApplicationListener设计
ApplicationListener 组件的功能是 需要在应用程序启动后 找到要对外暴露的服务,然后将它交给 RegisterServer 组件,该组件需要关注的是 怎么获取到需要对外暴露的服务,这里需要大家对Spring有一定基础了。Cheese 里面采用了Spring 内置的监听器机制实现的。
4.6、整体流程设计
我们已经分别设计了 RemoteServer 、RegistServer、DiscoveryServer、ApplicationListener以及CuratorExecute 这些组件。并且详细的规划了他们各自需要具备的能力,最后还提供了他们支撑的解决方案。
5、总结
Cheese 是本篇文章的主角,关于她的雏形和项目架构相信大家已经有了很清晰的认知了,目前的设计虽然十分简陋,但我们的目标是先把Cheese生产出来,然后用起来,后续我们在使用的过程中发现了问题 在逐步的解决,让Cheese 持续的演进。
关于设计理论篇就先告一段落,接下来我们就需要进行编码落地了,具体可查阅
标签:需要,服务,奶酪,Jerry,分布式服务,抽丝剥茧,Tom,组件,设计 From: https://blog.csdn.net/qq_38701478/article/details/143086305