8#2014-7-13 队列阻塞导致服务器无响应问题

1.现象

2014-7-13日8号托管再现了近期频繁的现象:队列阻塞导致对客户端无响应,最后或提示超时。

所幸该托管的日志级别是调试级,该级别会输出无空闲连接时连接使用情况,因此才得以继续分析解决.


2.分析

围绕日志进行分析,发现:

hotfox配置的本地连接数是35个.日志中显示,在阻塞之前各个scheduler处理线程出现了等待数据库连接却永远无法获得的记录。


连接被什么线程占用了呢?
日志中有下列记录:    
[2014-07-13 08:15:18:784][线程7276][1][20][0][][32]连接id=32,使用状态=已使用,取用时间=25(秒),拥有者线程=7276.
[2014-07-13 08:15:18:800][线程7276][1][20][0][][33]连接id=33,使用状态=已使用,取用时间=24(秒),拥有者线程=6868.
[2014-07-13 08:15:18:800][线程7276][1][20][0][][34]连接id=34,使用状态=已使用,取用时间=25(秒),拥有者线程=5164.
[2014-07-13 08:15:18:800][线程7276][1][20][0][][35]连接id=35,使用状态=已使用,取用时间=24(秒),拥有者线程=4572.

[2014-07-13 08:15:09:269][线程6868][1][20][0][][33]连接id=33,使用状态=已使用,取用时间=15(秒),拥有者线程=6868.
[2014-07-13 08:15:34:753][线程4572][1][20][0][][35]连接id=35,使用状态=已使用,取用时间=40(秒),拥有者线程=4572.

线程7276已经占用了32号连接,同时又在等待一个新的连接。
其他占用连接的线程也是如此,如其中4572,6868线程。


理论上,配置的大小为35的连接池,如果已经被35个处理线程分别占用,这些线程同时处于等待同一数据库的另外的连接对象时,由于超时几乎全部是采用INFINITE的,死锁因此产生.
   
一般情况下,一个线程路径上获取连接会得到同一个连接对象。在一个线程已经占有一个连接对象的情况下,需要获取另外的新的数据库连接的情况有:
。效率问题:
。逻辑问题:
    
获取新的连接是使用以下宏实现的:
    GETNEWDBC:插件函数中使用
    USENEWDBC:非插件函数中使用    
这2个宏在本地服务器的所有插件中出现了4次,分别为:
    
int CNNE::Notify(CMessage *nm,CHANNEL_ID handle)
逻辑问题.

通知只有在成功写入数据库后才执行,采用事务挂钩的异步方式处理.

代码中的注释说明是为了避免事务hook产生递归.(现在有些不确定了).


int CSEMQ::Save(CSEMQItem &qe,vector &tgt,bool auto_release)

int CSEMQ::Save(vector &msgs)     
效率问题.
第1个Save是多目标消息的版本,第2个是多消息版本.
Save过程调用序列生成器,由于Save操作可能产生较大的开销(消息存储和磁盘文件操作)。
为了减少并发,避免序列生成器(CSequencer作用是生成object_id)和SEMQ操作在一个事务上,序列生成器生成序列号时需要一个新的连接.使用后立即释放.否则的话,CSequencer的NextVal会成为瓶颈.

CSequencer的使用逻辑:
        USENEWDBC(new_pdbor,this->dbc_name_.c_str());
        CSequencer sequencer(new_pdbor);
        __int64 object_id ;
        if (sequencer.NextVal(object_id,31)) { ///< 获取tb_0031的序列值
            return -1;
        }     

int CJJY_DailySaleSheet::AfterCommitLoadFromMsg(CMsg *msg,CSheetDealResult *r)
逻辑问题.为了调试问题。曾出现过写入数据库操作没有任何错误提示,数据却没有写入的情况。增加此操作的目的是通过另外的连接(会话)检查数据是否写入。
近期频繁出现此类问题,不知是否和此处修改有关。该代码应该是后期排查问题加上的。另一个现象是JJY等零售商服务器几乎从没有出现此问题。

3.解决方案

程序解决办法有以下选择:
(1)GETNEWDBD/USENEWDBC增加超时参数,调用时检查返回对象是否为NULL.
缺点是:
.死锁时牺牲本次事务,错误和失败的情况没有减少
.需要增加新的宏,增加三个参数:超时时间,失败时的动作和返回值.

(2)建立专用于CSequencer的连接池.
缺点是:
.增加hotfox中连接池配置.(语义上不好解释)
.专用有浪费的嫌疑

(3)允许在连接不够用时自动扩大连接池
这是最终采用的方案.
hotfox.conf的连接池配置增加3个属性:auto_new,max_count,new_timeout。

.在获取连接无超时机制时,当无空闲连接时是否自动创建新的连接对象.(auto_new=true/false)
.可扩大连接总数的上限值(max_count:-1无限制)
.自动创建新连接对象前等待的超时时间(new_timeout),单位:秒.默认:10.

连接池配置示例如下:
相关定义:
    bool auto_new_;///< 是否允许自动创建新的连接对象(当没有超时限制时避免死锁),默认:true
    unsigned long max_count_; ///< 可扩大连接池时,连接数可达到的最大数量. -1表示无限制.默认:-1
    unsigned short new_timeout_; ///< 自动创建新连接对象前等待的超时时间,单位:秒.默认:10.
  

4.非程序解决方案

 调整连接池的的大小,设置为35对于数据库事务型应用有些偏小.

这种方法可以在不升级程序的情况下解决问题.


值大于以下数的和:

.

.使用数据库的后台线程数.

如对于以下配置,最好大于70。

 
      
    20
    
    50  
 

 

你可能感兴趣的:(问题处理)