Master-Slave通用基础框架
一、设计目的
设计出一个通用的Master-Slave基础框架,然后可以基于这个框架来实现特定的业务需求,比如实现多节点并行计算等。
二、设计理念
基于经典的命令模式,Master和Slave之间通过相互发送命令(Command)实现交互,命令是一个抽象的概念,Command可以用来分发任务,也可以用来传输数据,
这完全由业务来决定怎么处理,
框架只定义了一个实际的命令-心跳检测命令(Heartbeat
Command)。可以通过定义自己的命令,并提供响应的命令处理器,来实现任何形式的业务。框架提供的核心功能其实只有,底层的网络通信,Master/Slave关系的维系,命令的分发功能。
三、详细设计
系统类图
核心接口和类:
名称
|
类型
|
方法
|
说明
|
Node
|
interface
扩展
Runnable接口
|
void
init()
|
节点初始化
|
接口Node:
代表分布式节点,Master或Slave节点,分布式节点,Master节MasterNodel和Slave节SlaveNode实现本接口
|
void
start()
|
节点启动
|
void
stop()
|
节点停止
|
void
reset()
|
节点重启
|
名称
|
类型
|
方法
|
说明
|
MasterNode
|
class
|
实现Node接口
|
Master节点,负责管理Slave节点,分发命令给Slave
|
名称
|
类型
|
方法
|
说明
|
SlaveNode
|
class
|
实现Node接口
|
Slave节点,负责接收Master节点分发的命令,并执行命令,将结果封装成命令发送到Master节点
|
名称
|
类型
|
方法
|
说明
|
SlaveNode
|
class
|
实现Node接口
|
Slave节点,负责接收Master节点分发的命令,并执行命令,将结果封装成命令发送到Master节点
|
名称
|
类型
|
方法
|
说明
|
CommandProvider
|
interface
|
Command
produce()
|
产生Command
|
Command提供者接口,业务代码提供实现该接口的Provider,因为采用Slave主动拉取Command的机制,CommandProvider为SlaveNode用来生产Command。
后面考虑支持Master主动推送Command到Slave.
|
List<Command>
produce(long count)
|
批量产生Commnad
|
名称
|
类型
|
方法
|
说明
|
NetWorkClient
|
interface
|
void
init()
|
初始化
|
Slave端负责底层网络通信的,网络服务客户端,负责和Master节点通信。采用nio实现。
|
void
start()
|
启动
|
void
stop()
|
停止
|
void
reset()
|
重启
|
名称
|
类型
|
方法
|
说明
|
Session
|
class
|
void
init()
|
初始化会话
|
Master和Slave之间的会话,Master通过Session来跟中,管理Slave。Session通过一个线程来接收Command,执行Command,发送Command。SessionManager负责管理,
分配, 回收这个Session.
SessionManager会定时转换Session的状态,如果Session的状态转换到MS_SESSION_STATE_DEAD 状态,就会在在下次被回收.但是如果Master接收到Slave的心跳命令,就会将Session的状态置为MS_SESSION_STATE_ALIVE ,以表示Slave还存活.
|
void
start()
|
启动会话
|
void
destroy()
|
销毁会话
|
void
alive()
|
当Master接收到Slave的心跳Command时,会将调用本函数,将当前的Session置为“存活”状态
|
void
free()
|
释放会话,已被以后重新利用,并不是销毁,防止频繁创建,销毁会话(线程)
|
void
isDead
|
会话是否已经“死掉”,这意味着Master长时间没有收到对应的Slave的心跳Command,Master会认为这个Slave已经“死掉”。
|
void
onRead()
|
NetWorkServer会在会话的Socket有数据可读时,会调用Session的这个函数,让Session接收Slave发送的数据包
|
void
onWrite()
|
NetWorkServer会在会话的Socket可写时,调用Session的这个函数,Session可以将自己的数据输出缓存队列的数据通过网络发送到Slave。
|
void
run
|
Runnable接口的函数,循环从自己的输入缓存队列中解析出Command并通过CommandDispatcher分发Command并将结果Command写到自己的输出缓冲队列.
|
void
transitState()
|
根据Session当前的状态转换到下一个状态,SessionManager会有一个定时任务,负责调用本函数,来转换Session的状态.
|
名称
|
类型
|
方法
|
说明
|
SessionManager
|
interface
|
void
init()
|
初始化
|
Master端负责底层网络通信的,网络服务器,负责和Slave节点通信。采用nio实现一个线程处理所有的网络操作。
|
void
freeSession(Session session)
|
释放Session,实际上是被回收,以备再分配
|
Session
newSession(java.nio.channels.SocketChannel channel)
|
分配Session
|
void
destroy()
|
SessionManager的销毁,会销毁所有的Session
|
名称
|
类型
|
方法
|
说明
|
Command
|
class
|
ByteBuffer
getPayLoad()
|
获得Comamnd的负载,业务相关的数据
|
Master和Slave之间交换的命令,业务可以定义的自己的Command并提供对应的Command处理器,Command可以用来分发任务,也可以用来传输数据,这完全由业务来决定怎么处理
|
void
setPayLoad(ByteBuffer payLoad)
|
设置Comamnd的负载,业务相关的数据
|
Long
getType()
|
获得Command的类型,业务可以定义自己的Command类型,并负责处理
|
void
setType(Long type)
|
设置Command的类型,业务可以定义自己的Command类型,并负责处理
|
Session
getSession()
|
Master端,获得Command对应的Session
|
void
setSession(Session session)
|
Master端,设置Command对应的Session
|
名称
|
类型
|
方法
|
说明
|
CommandHandler
|
interface
|
Command
handle(Command command)
|
处理命令,并返回以Command封装的结果
|
负责处理Command的接口,
业务定义新的Command需要提供实现该接口的Command处理器
|
名称
|
类型
|
方法
|
说明
|
CommandDispatcher
|
interface
|
void
int()
|
初始化
|
Master和Slave负责分发Command的接口,通过业务配置的命令路由表,分发Command到具体的Command处理器
|
|
Command
dispatch(Command command)
|
通过业务配置的命令路由表,分发Command到具体的Command处理器
|
时序图
Master工作时序图
Slave工作时序图
状态迁移图
Session一共有5种状态,设计这么多的中间等待状态,或许没有这必要。
Session分配时默认状态为alive,存活状态,然后SessionManager的定任务会定时将Session的状态沿着alive->waiting_0->waiting_1->waiting_2->dead路线迁移,但是同时Master在收到Slave的心跳Command时会将Session的状态置为alive.SessionManager的定任务会在Session的状态被置为dead后,下一次定时任务执行是回收该Session,即认为相应的Slave已经“死掉”。
四、实现
请参考代码
五、代码
请见附件
ms.zip
六、总结
目前只实现了基础的功能,还有很有一些值得去思考与实现的,如:
Slave的管理,包括Slave的存活,负载,等管理。
到底是Master主动将Command推送到Slave,
还是Slave主动拉,这也是值得考虑的,不过这个实现起来还是比较简单的,目前采用Slave主动拉的机制,主要考虑到这样实现更简单也更健壮。
Command分配策略的考虑,是将一个Command分配给一个Slave呢,还是一个Command可以分配个多个Slave呢,这个可以考虑用策略模式来处理。
CommandHandler支持异步,这个通过回调可以很好的处理。
Master单点故障的问题,怎么考虑,怎么处理
|