1.目的:
以添加合作机构,解除合作机构操作为例说明需要进行时序控制的情形:
用户执行了以下2个顺序操作:
(1).A添加B
(2).A删除B
在分布式的并行系统中,在传输和调度处理过程中如果没有时序控制则其结果不可预料。
其结果可能是A-B的合作关系被记录或者没有记录。系统和用户都没有得到预期的结果。
处理时序性是目标结果,要得到正确的结果,传输的时序需要同时得到保证。
---不排除存在传输无序,但处理能保证有序的向传输效率倾斜的可选方案。
2.应用
还是以上面的示例说明,在采用时序控制后,以下操作序列:
(1).A添加B
(2).A删除B
(3).A添加C
则(1,2)需要保证顺序,而不需要包含(3).即(1,2)可以由任务T1处理,而(3)可以由其它任务处理。
这样即保证了时序性,也没有降低并行效率。
3.定义
在UMX-T中增加时序控制属性。
.SEQ_CONTROL: 时序控制标志 1/0 1-需要时序控制 0-不需要时序控制 参数@SeqCtrl 默认:0
.SEQ_CONTROL_KEY:时序控制键,当SEQ_CONTROL=1时有效.参数@SeqCtrlKey.
.时序控制键:域,数据类型,子类型,控制子键
域为扩展保留,支持客户化开发和第三方开发。默认为0,表示供应宝标准域。
数据类型,子类型和UMX-T的应用关联键中的定义类似,可参考但独立定义。
两者不能完全相同是因为各自的目的不同,应用关联键是作为传输层和应用层的连接对象。
而时序控制是在传输层内部使用,在并行系统中保证消息的时序。
控制子键根据不同的数据类型分别定义。
以用户上下线为例,假设100081001用户上线,时序控制键为:0,30,10,100081001。
对于单据传输,可以顺序跟踪一张单据的多次操作,保证应用语义的正确。其时序控制键为:0,200,1050,SP0001。分别表示供应宝标准域,单据数据,订单,单据编号为SP0001.
具体编码见<<SES全局资源分配规则>>中的“时序控制键编码”。
4.时序控制的实现策略
(1)数据接收方:根据消息的时序控制键把消息分发给特定的任务。
(2)数据发送方:如当存在多个SEMQ发送任务时
从SEMQ DB读取消息时,根据时序控制键分派给特定的任务。
.对于不需要时序控制的消息由专门的任务处理,线程个数为原来hotfox.conf的<scheduler><thread>指定。
.处理时序消息的任务数:同上(以后考虑单独配置)
分发算法可以采用:
.基于键值的分发:把键值转换为一个数值,按任务个数取模
.其它算法:如把任务消息队列的元素数量和权值考虑进来
5.实现
5.1 hotfox
增加HTX_Dispatcher类,由该类管理插件/消息调度器,包括:
.HTX_Scheduler *schedulers_; ///< 多个处理有时序控制消息的调度器,数量可配置
.HTX_Scheduler def_scheduler_; ///< 默认消息处理任务(处理无时序要求的消息)
.HTX_Scheduler control_scheduler_; ///< 控制消息调度器
5.2UMX-T:
增加时序控制属性并实现.
bool seq_ctrl_flag_; ///< 时序控制标志
string seq_ctrl_key_; ///< 时序控制键
public:
void SetSeqControl(bool seq_ctrl_flag);
void SetSeqControlKey(const char *seq_ctrl_key);
const char* GetSeqControlKey();
unsigned long HashSeqControlKey(); ///< 把时序控制键映射为一个数值
5.3 SEMQ
修改CSEMQ基类,使本地服务器和平台支持消息的时序控制。
CSEMQ的send_task_,由多线程单任务变为多个动态单线程任务.
SEMQ DB增加2个字段:是否做时序控制,时序控制键.
CSEMQ在GetData后,根据时序控制属性,选择一个任务处理.
5.4调度算法
if (msg->GetMsgAttr()&CONTROL_MASK) { ///< 如果是控制消息,则进入控制消息任务队列
return control_scheduler_.enqueue(wm);
}
else {
///< 选择一个处理任务(调度插件处理)
HTX_Scheduler *scheduler = 0;
if (!msg->IsSeqCtrl()) {
scheduler = &def_scheduler_;
}
else {
unsigned long seq_key = msg->HashSeqControlKey();
scheduler = &schedulers_[seq_key%this->seq_task_num_];
}
scheduler->enqueue(wm);
}
6.使用示例:
以平台检测到连接实体断开采用时序控制为例,说明如何修改。
下面代码的兰色标记的部分为修改部分:
int CBRSP::handle_input(HTX_HANDLE fd, HTX_Reactor_Mask masks,void *arg,Sock_Peer_Base *entity) {
CMsg *msg = new CMsg;
CUMXT *tmsg = CUMXT::New(msg);
string sc_key;
if (client_type==CLIENT_TYPE_USER) {
sc_key = LogMsg("%d,%d,%d,%d",this->GetDomain(),30,10,ulUserId);
}
else { /// client_type==CLIENT_TYPE_SERVER
sc_key = LogMsg("%d,%d,%d,%d",this->GetDomain(),30,15,server->GetServerID());
}
tmsg->SetSeqControlKey(sc_key.c_str());
CWrappedMsg<> *pwm = new CWrappedMsg<>;
pwm->msg = tmsg;
pwm->SetFlag(CWrappedMsg<>::ML_FAKE);
...
}
对于客户端,对于登录消息,以UMX-T传输,参考上述形式指定时序控制键。
这同样适用于单据传输,IM,多人会话等功能。不再需要应用层来处理时序问题。
7.备忘
7.1其它方案
服务端集中配置,使用和应用关联键相同的数据分类方法,服务端配置时序控制规则。
时序控制规则用来规定哪些类型的数据是需要在一个时序上控制的。
如服务端可以把(30,505),(30,506)配置为需要进行时序控制的组,如果控制子键相同,则进行时序控制。
7.2暂时未实现SEMQ的部分