dxi_chaneg_log支持同步模式
dxi_change_log支持直接调用dxi_ddd和dd,而不写入tb_change_log后异步处理
----异步方式在DAS中存在多个内部服务器时如何分配,协调tb_change_log记录任务存在问题
假设A,B两个内部服务器,写入tb_change_log后,这些未处理的记录由谁处理?
方案:
----写入者处理:在tb_change_log增加处理者ID信息(如内部服务器ID)
如果某个内部服务器宕机,则该服务器的记录在其重新启动前无法被处理
----任务协调者:这是一种常见的模式。需要增加一个协调者,在多个工作者协调分配任务。(需要开发,有一定的复杂度)
***也许有开源实现!
采用直接调用dxi_ddd的方式,则可避免上述问题。
通常数据库操作的开销较大。
缺点是,如果接收队列太小,则可能导致通信速率降低。
dxi_change_log.conf
增加以下配置
<handle_mode>3</handle_mode> <!-- 处理模式: bit0-1:同步 0-异步 bit1-同步模式下是否写变更日志表.默认:0 -->
dxi_chaneg_log.h
CDxkChangeLog
增加处理模式配置:
short handle_mode_; ///< 处理模式: bit0-1:同步 0-异步 bit1-同步模式下是否写变更日志表.默认:0
HandleLogRecord增加参数flag
///< flag=1直接调用,因没有写入变更日志所以不需要修改状态
int HandleLogRecord(CChangeLogRecord *record,short flag);
HandleBillAccept中判定handle_mode_,如果是直接调用,则直接生成CChangeLogRecord对象,调用CDxkChangeLog::HandleLogRecord函数.
int CDxkChangeLog::HandleBillAccept(CBillAcceptEvent *e) {
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CDxkChangeLog::HandleBillAccept...\n");
ISheetTypeInfo *sti = ibiz_->GetSheetTypeInfo(e->sheet_type_);
if (sti==0) { ///< 可assert(sti!=0)
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_ERROR,"CDxkChangeLog::HandleBillAccept失败,无效的单据类型%lu.\n",e->sheet_type_);
return -1;
}
GETDBC(pdbor,this->local_dbc_.c_str());
FAIL_RETURN(pdbor->BeginTrans(),,-1);
char now[24];
GetCurSystemTime(now);
int cnt = sti->get_key_field_num();
CRowset* row = e->data_->GetRowset(0);
int rowcnt = row->GetRSMeta(RST_ROW_CNT);
for(int i = 0; i < rowcnt; i++)
{
if(!bcatcherall)
{
int j = 0;
for(; j < catch_rule_.size(); j++)
{
CCatchRule *cr = catch_rule_.at(j);
if (cr->filter_.src_orgid_.size() > 0 && cr->filter_.src_orgid_.find(e->src_orgid_) == cr->filter_.src_orgid_.end() )
continue;
if (cr->filter_.dest_orgid_.size() > 0 && cr->filter_.dest_orgid_.find(e->orgid_) == cr->filter_.dest_orgid_.end() )
continue;
if (cr->filter_.sheet_type_.size() > 0 && cr->filter_.sheet_type_.find(e->sheet_type_) == cr->filter_.sheet_type_.end() )
continue;
///< 检查单据内容是否满足条件
if (cr->filter_.cond_.is_valid_) {
int ret = cr->filter_.cond_.Check(row,i);
if (ret!=1)
continue;
}
break;
}
if(j >= catch_rule_.size())
continue;
}
string app_key = "";
bool bsrc_orgid = true;
bool bsheet_id = true;
if(sti->GetFieldInfo(1,"src_orgid") == NULL)
bsrc_orgid = false;
if(sti->GetFieldInfo(1,"sheet_id") == NULL)
bsheet_id = false;
for(int j = 0; j < cnt; j++)
{
CBillFieldInfo* field = sti->get_key_field(j);
if(row->GetFieldInfo(field->name.c_str()) == NULL)
continue;
if(stricmp(field->name.c_str(),"src_orgid") == 0)
bsrc_orgid = false;
else if(stricmp(field->name.c_str(),"sheet_id") == 0)
bsheet_id = false;
char ctype_ = 'C';
if(field->type.length() > 0)
ctype_ = field->type[0];
app_key = app_key + field->name + "=" + fieldtypebyinterfacetype(ctype_) + "[" + row->GetFieldValueByName(i,field->name.c_str()) +"],";
}
if(bsrc_orgid) app_key = app_key + LogMsg("src_orgid=N[%lu],",e->src_orgid_);
if(bsheet_id) app_key = app_key + LogMsg("sheet_id=C[%s],",e->sheet_id_.c_str());
if(app_key.length() > 0) app_key = app_key.substr(0,app_key.length()-1);
if (handle_mode_&0x01) { ///< 如果是直接调用(不经过变更日志表)
CChangeLogRecord record;
record.app_pk_ = app_key;
record.data_type_ = e->sheet_type_;
if (HandleLogRecord(&record,1)) {
return -1;
}
}
if ((handle_mode_&0x01)==0||(handle_mode_&0x02)) {
ITableHandler *th = CBasePluginModule::db_helper_->NewTableHandler(pdbor,changelog_cfg_.tableConfig._tableName.c_str());
AUTO_POINTER_NODECLARE(ITableHandler,th);
if (handle_mode_&0x02) { ///< 如果是直接处理且需要记录变更日志,则需要把logflag置为已处理
int flag=1;
th->BindField("logflag",(char**)&flag,sizeof(flag));
}
th->BindField("datatype",(char**)&e->sheet_type_,sizeof(e->sheet_type_));
th->BindField("app_pk",app_key);
th->BindField("logtime",now);
if (th->Insert()) {
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_ERROR,"CDxkChangeLog::HandleBillAccept失败.目标orgid=%lu,源orgid=%lu,错误:%s.\n",e->orgid_,e->src_orgid_,pdbor->GetLastError());
return -1;
}
}
}
GetThisLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CDxkChangeLog::HandleBillAccept ok.\n");
return 0;
}
int CDxkChangeLog::HandleLogRecord(CChangeLogRecord *record,short flag) {
CQQ_BILLTYPE bill_type = record->data_type_;
CHANGELOG_CFG::BILLTYPE_MAP *pBILLTYPE_MAP = changelog_cfg_.Find(bill_type);
assert(pBILLTYPE_MAP!=0);
GetThisLogger()->log(LO_FILE|LO_STDOUT,SEVERITY_DEBUG,"HandleLogRecord正在处理:sheet_type=%d,rule_id=%d,app_pk=%s...\n",bill_type,pBILLTYPE_MAP->rule_id_,record->app_pk_.c_str());
RESULT vecResult;
try
{
vecResult = PKParser(record->app_pk_.c_str());
}
catch(PKPException Err)
{
GetThisLogger()->log(LO_FILE|LO_STDOUT,SEVERITY_ERROR,"记录ID:%I64d、业务主键:%s、单据类型:%d、机构编码:%s的单据中获取%d规则对象失败,错误原因:.\n",
record->id_,record->app_pk_.c_str(),bill_type,record->mis_no_.c_str(),pBILLTYPE_MAP->rule_id_,Err.GetError().c_str());
return -2;
}
string update_sql;
int handle_status = 0;
{ ///< @note 此括号有些怪异.它的作用是确保下面操作使用的连接能被释放,有机会在连接中断后继续完成log_flag的修改.见函数尾部的说明
int ret = 0;
USEDBC(pdbor,dd_src_.c_str());
if (pBILLTYPE_MAP->handle_mode_==1) {
IRule *rule = changelog_tss_->NewRule(pBILLTYPE_MAP->rule_->GetID());
if (rule==0)
return -1;
string strWhere;
string alias;
alias = rule->GetMasterTableAliasName();
if (!alias.empty()) {
alias +=".";
}
for (unsigned long k=0; k<vecResult.size(); k++)
{
PKVal &pk_val = vecResult[k];
string key_field = alias+pk_val.PKName;
rule->AddKey(pk_val.PKName.c_str(),pk_val.PKValue.c_str());
switch(vecResult[k].PKType[0]) {
case 'N':
case 'I':
strWhere += LogMsg("%s=%s and ",key_field.c_str(),pk_val.PKValue.c_str());
break;
case 'D':
//strWhere += LogMsg("%s=%s and ",key_field.c_str(), pdbor->GetDBExt()->ToDate(pk_val.PKValue.c_str(),1).c_str());
//break;
case 'S':
strWhere += LogMsg("(%s>=%s and %s<%s) and ",
key_field.c_str(),
pdbor->GetDBExt()->ToDate(pk_val.PKValue.c_str(),1).c_str(),
key_field.c_str(),
pdbor->GetDBExt()->Date_Add(pdbor->GetDBExt()->ToDate(pk_val.PKValue.c_str(),1).c_str(), 1,DP_DAY).c_str());
break;
case 'T':
strWhere += LogMsg("%s=%s and ",key_field.c_str(), pdbor->GetDBExt()->ToTime(pk_val.PKValue.c_str(),1).c_str());
break;
default:
strWhere += LogMsg("%s='%s' and ",key_field.c_str(),pk_val.PKValue.c_str());
break;
}
}
if (!strWhere.empty()) {
strWhere.erase(strWhere.length()-5,5);
}
if (!record->mis_no_.empty())
rule->SetProvider(record->mis_no_.c_str());
else
rule->SetSpecProvider(false);
rule->SetOpType(record->op_type_);
if (record->op_type_==0||record->op_type_==1||record->op_type_==3||record->op_type_==5||record->op_type_==6) { ///< 新增,全量修改,增量修改
///< @note 对于主表修改,按理只需要查和取变化的字段.为了简化,取所有字段
rule->SetQryCond(strWhere.c_str());
unsigned long count = 0;
ret = rule->Run(&count);
}
else if ( record->op_type_== 2) { /// 删除
ret = rule->HandleDelete();
}
else {
< 无效的变更类型
handle_status = 51;
ret = -1;
}
}
else if (pBILLTYPE_MAP->handle_mode_==2) {
dxi_ddd_ns::ITask *task = iddd_->NewTask(pBILLTYPE_MAP->rule_id_);
for (unsigned long k=0; k<vecResult.size(); k++) {
PKVal &pk_val = vecResult[k];
task->SetKeyValue(pk_val.PKName.c_str(),pk_val.PKValue.c_str());
}
ret = task->Run(handle_status);
task->Release();
}
if (flag==0) {
char now[32];
sens::CDateTime::GetDateTime(now);
if (ret) {///< 处理失败
update_sql = LogMsg("update %s set logflag=0,try_times=try_times+1,handle_time=%s,handle_status=%d where logid=%I64d",changelog_cfg_.tableConfig._tableName.c_str(),
pdbor->GetDBExt()->ToDateTime(now,1).c_str(),handle_status,record->id_);
GetThisLogger()->log(LO_FILE|LO_STDOUT,SEVERITY_ERROR,"记录ID:%I64d、业务主键:%s、单据类型:%d、机构编码:%s的单据中执行抽取%d规则失败.\n",
record->id_,record->app_pk_.c_str(),bill_type,record->mis_no_.c_str(),pBILLTYPE_MAP->rule_id_);
}
else {
update_sql = LogMsg("update %s set logflag=1,try_times=try_times+1,handle_time=%s,handle_status=%d where logid=%I64d",changelog_cfg_.tableConfig._tableName.c_str(),
pdbor->GetDBExt()->ToDateTime(now,1).c_str(),handle_status,record->id_);
}
if (pdbor->Execute(adCmdText,update_sql.c_str()))
return 0;
}
} ///< 离开此作用域,pdbor已经被释放.否则,如果由于当前连接已断开导致update始终失败,而不会重新连接.
if (flag==0) {
///< 如果上面的update失败,则反复执行直到成功.可以处理连接中断的情形.
while(1) {
USEDBC(pdbor,dd_src_.c_str());
if (pdbor->Execute(adCmdText,update_sql.c_str()))
break;
ACE_OS::sleep(1);
};
}
return 0;
}