一、proxy配置(ratis自带例子 - counter)
ratis proxy利用构建者模式 - Builder构建proxy实例,如图65~ 68行,分别设置proxy服务的分组、配置信息、节点信息、状态机。
分组信息封装于:RaftGroup 对象中,包含对应的分组标识符 - RaftGroupId 、构成分组节点集。
配置信息封装于:RaftProperties对象中,采用K-V模式将配置ratis需要的配置信息传递给Bulider构建器。
节点信息封装于:RaftPeerId对象中,包含当前节点的组内唯一命名。
状态机信息:StateMachine具体实现实例,是当前分组业务处逻辑处理的具体实现类,也是被分离在raft协议之外的具体业务实现逻辑。
二、proxy静态模型
如图2.1所示,RaftServerProxy 实现了接口 RaftServer,即proxy就是一个raft服务。
同时 proxy包含了九个主要属性,proxy所有功能都是依赖于这九个属性实现的。
1、stateMachineRegistry:StateMachine.Regisstry - 状态机注册服务
registry是proxy对所有状态机的管理服务,但是直到ratis - 2.3.0版本,该注册服务都是名不副实。registr只是简单实现了Function的一个接口定义。具体实体类在RaftServer接口中采用了lambda表达式(gid ->stateMachine)作为其实现类。没有起到其状态机注册器的作用,应为该注册器始终只有一个状态机被注册。具体代码可见 RaftServer$Builder#setStateMachine方法。
2、properties:RaftProperties - RaftServer配置项
简单的K-V模式的封装对象,对Ratis具体配置项的封装。为Raft服务的运行提供系统、应用层面的变量。Ratis服务通过简单的 key 获取配置的value,决定具体的运行方式。配置项详情见RaftServerConfigKeys类(包含了配置项及其默认值)
3、liftCycle:LifeCycle - RaftServer生命周期状态机
RaftServer生命周期状态机,是对RaftServer整体状态的控制器。包含了对RaftServer状态规则信息变更、运行期间服务状态的获取判定的等操作。
其中状态的变更必须符合以下规则
其规则控制就在LifeCycle中。
4、peerSupplier:Supplier<RaftPeer> - Raft节点协议通信封装实体
peer是对RaftServer当前节点的通信必要信息的封装。包含:唯一名称、三组协议通信地址的包装。为客户端请求提供通信地址信息。
同时peer实例化通过MemoizedSupplier包裹,MemoizedSupplier是Supperlier的一个具体实现类,实现了对Supperlier执行结果单例缓存通过proxy final的修饰实现peer属性的不可变性。
5、implExecutor:ExecutorService - 线程池(异步任务执行器)
implExecutor单独针对RaftServer执行动态添加节点或者动态添加分组操作的执行线程池。将RaftServer写类型的管理与业务逻辑分离开。
implExecutor采用Executors.newSingleThreadExecutor()工具构建一个只包含单个线程、无限工作队列的线程池。
主要包含两个操作:
1、节点的动态添加 - org.apache.ratis.server.impl.RaftServerProxy.ImplMap#addNew
2、异步动态添加分组 - org.apache.ratis.server.impl.RaftServerProxy#groupAddAsync
6、executor:ExecutorService - 具体业务逻辑执行线程池
executor通过配置项
1、raft.server.threadpool.proxy.cached - 是否采用缓存线程池【默认值为:true】
2、raft.server.threadpool.proxy.size - 线程池最大线程数【默认值为:0】
通过这个两个配置项,构建具体的业务逻辑处理线程池,配置项对应关系如下图
7、serverRpc:RaftServerRpc - reft服务底层通信组件
serverRpc是对RaftServer的底层通信组件,包含三种协议的RPC的具体实现逻辑。涉及到Raft节点的底层通信,这里不想洗介绍。
8、impls:ImplMap - raft代理服务具体代理对象
impls就是对proxy具体代理对象的封装,通过一个CucurrentHashMap对象注册分组和具体的Raft服务。为后期具体的业务逻辑提供指定分组的具体服务对象。proxy在处理客户端请求时,通过请求中包含的分组信息选择具体的服务处理请求。这也决定了所有的客户端请求都必须要包含请求处理的所在分组。
9、id:RaftPeerId - 节点信息,就是一个简单的节点唯一命名
单纯的节点名称,只是当前服务节点的标识符。
三、初始化
如图2.4所示,proxy的启动涉及到8个类共32个操作。
1、Counter通过RaftServer内部静态类Builder构建一个RaftServer构建器
2、第3~11通过RaftServer构建器设置:所属分组、配置信息、节点信息、分组对应的状态机,并与最后执行build进行构建。
3、第12步是通过构建器的静态方法执行proxy的构建
4、第13步RaftServer$Builder使用反射的方式调用ServerImplUtils#newRaftServer方法。将porxy的构建交由ServerImplUtils执行。
5、从ServerImplUtils的生命线看,ServerImplUtils主要做了两件事
1、第15步通过RaftServerProxy的构建器,构建proxy的实例。
在执行proxy的构造器是,主要初始化上边介绍的属性。
2、第23步初始化proxy的分组,在初始化分组时,通过proxy内部类ImplMap属性注册分组对的真实Raft服务(RaftServerImpl)
添加分组实际上也是proxy的初始化的Raft服务的核心服务RaftServerImpl,proyx只是起到了一个代理服务和服务管理的作用,真实的业务处理器还是交给了RaftServerImpl执行。
四、启动
proxy的启动比较简单,只是启动proxy中的各个组件
1、第2步执行图2.6中第一行业务逻辑,通过getImpls()获取所有分组信息,并在executor线程池中分别执行每个分组中注册的RaftServerImpl实例的start方法,异步并行的启动多个分组(可惜的是,到现在为止还未看到有直接多分组启动的方式)。
2、第3步设置lifeCycle生命周期状态控制器的状态,在设置状态变更之前通过方法参数传递需要执行的定制操作(这里的定制操作就是启动对应的底层通信组件),在组件正常启动后设置lifeCycle的状态。
3、第8步启动Raft服务的监控器-pauseMonitor,pasuseMonitor只是收集Raft在运行中的状态和执行情况信息,和业务无关。