零售商端tb_0031中具有相同时序控制键(f023v_0031)的单据消息(可能是按每一部分包含一张单据的不同明细抽取的,并指定以增量修改方式处理),发送状态全部为SS_ACK(6).
时序控制的核心逻辑是:端-端确认,具有相同时序键的消息只有在上一个消息确认送达(SS_ACK2=100)后才能发送下一个消息.
这个现象应该隐藏着一个bug.
SEMQ队列元素的发送状态定义如下:
enum {
SS_UNSEND=0, ///< 未发送
SS_SEND = 4, ///< 已发送
SS_FWD = 5, ///< 已转发
SS_ACK = 6, ///< 已送出
SS_SUSPEND = 10,///人工挂起
SS_SUSPEND2= 12,///自动挂起
SS_ABANDON=15,///放弃
SS_ACK2 = 100 ///< 已送达
};
各种状态解释如下:
SS_UNSEND:未发送,进入发送队列
SS_SEND表示应用进程尝试了发送,只是进入内存中的待发送任务队列中,甚至可能没有执行网络层的发送.
SS_ACK:目标或中转返回的确认消息
SS_ACK2:目标方的接受确认
SS_SUSPEND,SS_SUSPEND2,SS_ABANDON保留.
相同时序键的多条单据消息,在发送端的发送状态都是SS_ACK.这是非预期的情况.
按设计逻辑,这些消息应当串行地改变,只有上一条消息(相同时序键的消息按object_id升序排序)变成SS_ACK2后,下一消息才开始发送.
808-MT_INDICATION处理
int CBBoxPlugin::OnInquiry2(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {
...
unsigned short status = ISEMQ::SS_ACK2;
if(src_t == PT_CENTRAL && qe.dest_type_ != PT_CENTRAL)
status = ISEMQ::SS_ACK;
...
}
目标为平台的消息的最终状态ISEMQ::SS_ACK.
***应该统一状态图.
中转节点只负责执行一次发送,发送状态为SS_SEND就不再处理了.后续由发送端检查和控制.
以下代码应该是可以消除的:
if(src_t == PT_CENTRAL && qe.dest_type_ != PT_CENTRAL)
status = ISEMQ::SS_ACK;
810-MT_CONFIRMATION处理
int CBBoxPlugin::OnAck(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or)
///< 发送给PT_APP的消息,只要已到达平台(写入网关应用队列),就算已经送达
///< 如果确认消息来自平台,但是队列记录的目标不是平台,表示可能并没有发送到目标方(非平台)
if(src_t == PT_CENTRAL && qe.dest_type_ != PT_CENTRAL && qe.dest_type_!=PT_PAPP){ \
status = ISEMQ::SS_ACK;
}
else {
status = ISEMQ::SS_ACK2;
}
GETDBC_RETURN(pdbor,this->local_dbc_.c_str(),-1);
pdbor->BeginTrans();
if (semq_.UpdateStatus(ssr.record_id_,status)) {
pdbor->RollbackTrans();
return -2;
}
///< 如果目标是平台(且status==6)或者已送达,该确认的是有时序控制的记录,则取下一条
if ((qe.dest_type_==PT_CENTRAL||status==ISEMQ::SS_ACK2)&&!qe.sc_key_.empty()) {
CQQ_OBJECT_ID next_object_id=qe.object_id_;
int ret = semq_.GetNextSeqCtrlRecord(qe.sc_key_.c_str(),next_object_id);
if (ret==1) {
pdbor->SetTXHook(TxHook,&semq_);
semq_tss_->v_rec.push_back(new SEMQ_RECORD_DATA_PAIR(next_object_id, qe.sc_key_));
}
else if (ret==-1) {
pdbor->RollbackTrans();
return -3;
}
}
发送状态改变:
确认消息的发送端是待发送消息的目标时表示已经送达(判定条件为:src_t==qe.dest_type&&src_id==qe.dest_id_)
status = ISEMQ::SS_ACK2;
否则,只表示已经被中转节点(如平台)存储或者转发(目前没有细分这2种情况,由于目标不在线,平台存储后返回一个确认消息)
status = ISEMQ::SS_ACK;
目前的判定条件
if(src_t == PT_CENTRAL && qe.dest_type_ != PT_CENTRAL && qe.dest_type_!=PT_PAPP){
存在问题:
.逻辑不严谨:未比较端点类型
.PT_CENTRAL,PT_PAPP特殊处理:这种特殊处理是不必要的,除非这些端点没有遵循统一的传输确认机制.
---考虑影响面大,测试量大,完善这种问题需要等待合适的时机.
CSEMQ::GetData
int CSEMQ::GetData(string &sql)
发送时进行时序控制.
f017c_0031标志
f017c_0031被定义为确认标志,几经版本变化,现在已经没有意义不需要了. (一旦修改semq中使用f017c_0031的代码后需要对整体分布式通信环境测试)
f016c_0031可以区分可靠性级别和相应的传输确认机制.不再需要f017c_0031.
废除f017c_0031可简化逻辑,提高性能.如问询和时序控制条件:
inquiry_expr_ = "((f016c_0031=3 and f009n_0031=4) or (f017c_0031=1 and f009n_0031=6))"; ///< f017c_0031=1实际上包含了f016c_0031(必须为3)
seqctrl_expr_ = "((f016c_0031=3 and f009n_0031 in (4,10,12)) or (f017c_0031=1 and f009n_0031=6))"; ///< 时序控制时判定是否还有未处理的先前记录的过滤条件(如一张单据的2次改变产生的2条记录,第1条未确认则后面的不发送)
处理:问题待测试和定位
.专门针对此现象进行测试:发送方tb_0031中2条相同f023v_0031的记录,设置f009n_0031=0.目标机构服务器不在线.发送方处理后检查这2条记录的发送状态(f009n_0031).
.SEMQ可优化