gyb优化事项(2)

来源:互联网 发布:js中的this详解 编辑:程序博客网 时间:2024/05/16 17:31

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;            }   



    


0 0
原创粉丝点击