gyb优化事项(2)

1.SEMQ流量控制(已修改trunk 2014-7-22)

DAS测试过程中,5.17下午开始就没有新的单据导入了,tb_0031在此后没有新的记录生成。
检查日志:发现 没有可用数据库连接。
[2014-05-17 23:59:41:685][线程6152][1][20][0][][33]连接id=33,使用状态=已使用,取用时间=66986(秒),拥有者线程=5384.
[2014-05-17 23:59:41:685][线程6152][1][20][0][][34]连接id=34,使用状态=已使用,取用时间=66986(秒),拥有者线程=4028.

检查占用连接的线程(如4028),看为什么没有释放。

int CBBoxPlugin::OnRespInquiry(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {
    if (ack==0)
        result = semq_.Resend(ref_object_id);
       
        4028线程占用了数据库连接.
        从日志看,该线程阻塞在semq_.Resend调用上。

        
        
////////////////////////////////////////////////////////////////////////////////
int CSEMQ::SendRecord(CQQ_OBJECT_ID object_id) {
    CSendTaskItem *sti = new CSendTaskItem;
    sti->action_ = 1;
    sti->type_ = 1;
    sti->object_id_ = object_id;
    ACE_Message_Block *mb = 0;
    ACE_NEW_RETURN(mb,ACE_Message_Block(sizeof(CSendTaskItem*)),-1);
    memcpy(mb->rd_ptr(),&sti,sizeof(CSendTaskItem*));
    pre_send_task_.putq(mb);
    return 0;
}
        
    ////////////////////////////////////////////////////////////////////////////////
int CSEMQ::Resend(CQQ_OBJECT_ID object_id) {
    string sql = LogMsg("update %s set f009n_0031=0 where object_id=%I64d",this->GetTableName(),object_id);

    USEDBC(pdbor,this->dbc_name_.c_str());
    int ret = pdbor->Execute(adCmdText,sql.c_str()) ? 0 : -1;
    if (ret==0) {
        SendRecord(object_id);
    }
    return ret;
}
    
        ////////////////////////////////////////////////////////////////////////////////
int CPreSendTask::handle_message_block(ACE_Message_Block *mb) {
    CSendTaskItem *sti = 0;
    memcpy(&sti, mb->rd_ptr(), sizeof(CSendTaskItem*));
    mb->rd_ptr(sizeof(CSendTaskItem*));
    if (sti->action_==1) {
        string sql;
        if (sti->type_==1) {//////< 直接根据object_id发送
            sql = LogMsg("select object_id,dest_type,dest_id,f018c_0031,f005n_0031,f019n_0031,f023v_0031 from %s where object_id=%I64d and f009n_0031=0 order by object_id",semq_->GetTableName(),sti->object_id_);
        }
        else {
            sql = sti->sql_;
        }
        ///< @note semq_.GetData必须成功.
        short report_flag = 0;
        while(semq_->GetData(sql)) { ///< GetData失败只有在数据库故障,或与数据库服务器通信的网络故障时才发生
            ///< 此时间检查数据库服务是否有效
            short db_svc_flag = 0; ///< 数据库服务是否有效
            do {
                USEDBC(pdbor,semq_->GetDBCName());
                db_svc_flag = pdbor->GetDBExt()->CheckDBService();
                if (db_svc_flag!=0&&report_flag==0) { ///< @todo 报告给监控者
                    GetLogger()->log(LO_STDOUT|LO_FILE,SEVERITY_EMERGENCY,"CPreSendTask::handle_message_block检测到%s数据库故障.\n",semq_->GetDBCName());
                    report_flag = 1;
                }
                ACE_OS::sleep(5); ///< 出现一次故障后至少等待5秒
                if (db_svc_flag==0) {
                    break;
                }
            } while(1);
        }

    }

    delete sti;

    return 0;
}


   阻塞在GetData函数上,此函数一直没有结束.
int CSEMQ::GetData(string &sql)

和发送队列有关。
        int cnt = send_task_[st_index].putq(mb);
        处理很慢,处于堆积状态。但仍在执行。
        [2014-05-19 13:44:11:093][线程7000][2][20][0][]CSEMQ::GetData 发送队列序号0, 队列个数32769
       
为什么send_task_这么慢呢?


int CSEMQ::Send(SHORT_SEMQ_RECORD *ssr)---此函数慢
        
每次TraceAction要6分钟。流量控制导致的,大量的消息没有返回。      

SEMQ的默认水标:300。超时300秒(5分钟)。

测试环境下bbox.conf的配置

<inquiry_timer_interval>60</inquiry_timer_interval>
<inquiry_interval>1</inquiry_interval>
问询间隔为每分种一次。(这是在调试时为了加快速度设置的参数)


3090219 [2014-05-17 23:40:10:902][线程7096][1][20][0][]CSEMQ::TraceAction acquire 2_1_1_49276...
3091299 [2014-05-17 23:46:10:901][线程4832][1][20][0][]CSEMQ::TraceAction ok,num of action_trace:1 .

3091310 [2014-05-17 23:46:10:913][线程4832][1][20][0][]CSEMQ::TraceAction acquire 1_1_1_47656...
3092410 [2014-05-17 23:52:10:901][线程7096][1][20][0][]CSEMQ::TraceAction ok,num of action_trace:1 .

3092420 [2014-05-17 23:52:10:902][线程7096][1][20][0][]CSEMQ::TraceAction acquire 2_1_1_49277...
3093482 [2014-05-17 23:58:10:901][线程4832][1][20][0][]CSEMQ::TraceAction ok,num of action_trace:1 .
3093493 [2014-05-17 23:58:10:909][线程4832][1][20][0][]CSEMQ::TraceAction acquire 1_1_1_47657...


可见acquire要执行6分钟。流量控制下没有可用的信号量,只有在超时后才释放。(为什么不是默认的5分钟呢?)

是流量控制中的一个缺陷导致的。

int CSEMQ::TraceAction(char action,MQ_LOC_KEY *lock_key) {
    char key[128];
    sprintf(key,ACTION_KEY_FORMAT,action,lock_key->mq_id_,lock_key->mq_db_id_,lock_key->record_id_);

    DEBUG_LOG(GetLogger(),(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CSEMQ::TraceAction acquire %s...\n",key));
    fc_sema_->acquire();
    CActionTraceItem *ati = new CActionTraceItem;
    ati->ts_ = clock();
    action_trace_.bind(key,ati);

    DEBUG_LOG(GetLogger(),(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CSEMQ::TraceAction ok,num of action_trace:%d .\n",action_trace_.current_size()));

    return 0;
}

修改后的代码: (已修改trunk 2014-7-22)

int CSEMQ::TraceAction(char action,MQ_LOC_KEY *lock_key) {
    char key[128];
    sprintf(key,ACTION_KEY_FORMAT,action,lock_key->mq_id_,lock_key->mq_db_id_,lock_key->record_id_);

    DEBUG_LOG(GetLogger(),(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CSEMQ::TraceAction acquire %s...\n",key));
    fc_sema_->acquire();
    CActionTraceItem *ati = new CActionTraceItem;
    ati->ts_ = clock();
    if (action_trace_.bind(key,ati)==1) { ///< 已经存在跟踪项
        fc_sema_->release();
    }

    DEBUG_LOG(GetLogger(),(LO_STDOUT|LO_FILE,SEVERITY_DEBUG,"CSEMQ::TraceAction ok,num of action_trace:%d .\n",action_trace_.current_size()));

    return 0;
}


2.SEMQ 记录状态

故障现象:测试中发现f009n_0031=100的记录,会变成4.

与消息的处理顺序有关。
由于测试时问询的频率比较短。发送后,目标接收方返回的810-Indication已经被处理,f009n_0031状态为100.但此时,已经发出了809-Request问询。
在目标方处理809-Request时还没有接收到单据,返回的消息指示“没有收到”。发送方接收到809-Confirmation后,把状态修改为0,重新发送。

***机制不严谨,有可能出现重复发送。(可以在Resend时排除已经是100状态的记录)

流量控制:   

控制有多少个等待确认的消息。

int TraceAction(char action,MQ_LOC_KEY *lock_key); ///< 跟踪一个活动

action

含义 

TraceAck时机

1

发送数据,跟踪tb_0031

接收到810-Indication

2

问询(809-Request),跟踪tb_0031

接收到809-Confirmation

3

反向确认(808-Indication),跟踪tb_0030

接收到800-Response







action=1:

如果网络原因或者接收方处理失败,没有接收到810-Indiction:超时会删除
  如果接收到2个810-Indication(分别来自平台和目标接收方):只能释放一个信号量


action=2    
  如果问询速度非常快(如压力或破坏性测试时),远大于返回消息的速度。
  则同一条记录可能会发送多次,但信号量只能有一个。
 
  如果同一个消息执行多次问询,每次执行fc_sema_->acquire();
  但第1个该消息的确认返回时,只执行1次fc_semq_->release();----这会导致可用信号量减少,直到没有信号量可用。只能等待超时,释放信号量后才可继续。
  ---这和每6分钟执行一次是吻合的。

actino=3,


int CBBoxPlugin::OnRespInquiry(CWrappedMsg<> *in,vector<CWrappedMsg<> *> &out,DISPATCH_RESULT &or) {    
        result = semq_.Resend(ref_object_id);
           
Resend函数增加参数force,表示是否强制发送。force=false,如果消息已经确认则不发送 。     

semq.h    

    int Resend(CQQ_OBJECT_ID object_id,bool force); ///< 重发一条消息            


semq.cpp

   问询条件表达式(部分条件项)

    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<>100))";  ///< 时序控制时判定是否还有未处理的先前记录的过滤条件(如一张单据的2次改变产生的2条记录,第1条未确认则后面的不发送)
--->

    inquiry_expr_ = "((f016c_0031=3 and f009n_0031 in (4,5,6)))"; ///< 忽略,废弃f017c_0031
    seqctrl_expr_ = "((f016c_0031=3 and f009n_0031 in (4,5,6,10,12)))";  ///< 时序控制时判定是否还有未处理的先前记录的过滤条件(如一张单据的2次改变产生的2条记录,第1条未确认则后面的不发送)
  
*** f017c_0031此字段已不使用.(废弃此字段)

f016c_0031的含义如下:
enum {
    SF_NONE=1,SF_SAVE_FWD,SF_ACK,
    RL_DISCARDABLE=1,///< 目标不在线时丢弃
    RL_NODISCARDABLE,///< 目标不在线时保存在待发送消息队列中
    RL_STRICT,///< 要求确认(已送达)
};
  

  UMXT默认RL_NODISCARDABLE.
 
  假设发送给机构A的用户100681004的某个消息,不同消息可靠属性的含义分别如下:
  •   对于RL_DISCARDABLE,消息到达机构A时,如果用户在线则发送给用户,如果不在线则丢弃。***发送端并不需要知道用户是否在线,在发送端就决定是否删除.
  •   对于RL_NODISCARDABLE,消息到达机构A时,如果用户在线则发送给拥护,否则,保存在机构A的数据库中,等待用户上线后发送。***不能保证消息被送达
  •   对于RL_STRICT,必须确保目标接收到.
 

  对于UMXT消息,传输采用TCP。没有属性指示采用TCP或UDP。

 

CSEMQ::Resend实现:

////////////////////////////////////////////////////////////////////////////////
int CSEMQ::Resend(CQQ_OBJECT_ID object_id,bool force) {
	string sql;
	if (force)
		sql = LogMsg("update %s set f009n_0031=0 where object_id=%I64d",this->GetTableName(),object_id);
	else 
		sql = LogMsg("update %s set f009n_0031=0 where object_id=%I64d and %s",this->GetTableName(),object_id,inquiry_expr_);

	USEDBC(pdbor,this->dbc_name_.c_str());
	int ret = pdbor->Execute(adCmdText,sql.c_str()) ? 0 : -1;
	if (ret==0) {
		SendRecord(object_id);
	}
	return ret;
}
    

要求目标为平台的消息最后的状态应该为100.(已送达),而不能是6。

810-Indication增加flag参数:

       const char *sz_handle_flag = msg->GetParam("flag"); ///< 处理标志 0-未指定 1-已存储 2-已转发 3-已处理(处理仅限于目标方)
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){ ///< 如果确认消息来自平台,但是队列记录的目标不是平台,表示可能并没有发送到目标方(非平台)
            if (handle_flag==1||handle_flag==2) {
                status = ISEMQ::SS_ACK; ///< @note 不区分平台是转发还是存储了----平台代码目前不支持
            }
            else {
                status = ISEMQ::SS_ACK2;
            }   



    


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值