Archive for the ‘C++’ Category

线上存储服务崩溃问题分析记录

上周我们的存储服务在某个线上项目频繁出现崩溃,花了几天的时间来查找解决该问题。在这里,将这个过程做一下记录。

加入调试信息

由于问题在线上发生,较难重现,首先想到的是能不能加上更多的信息,在问题出现时提供更多的解决思路。

首先,我们的代码里,在捕获到进程退出的信号比如SIGABRT、SIGSEGV、SIGILL等信号时,会打印出主线程的堆栈,用于帮助我们发现问题。

但是在崩溃的几次情况中,打印出来的信息并不足以帮助我们解决问题,因为打印的崩溃堆栈只有主线程,猜测是不是在辅助线程中发生的异常,于是采取了两个策略:

  1. ulimit命令打开线上一台服务器的coredump,当再次有崩溃发生时有core文件产生,能够帮助发现问题。
  2. 加入了一些代码,用于在崩溃的时候同时也打印出所有辅助线程的堆栈信息。

在做这两部分工作之后,再次发生崩溃的情况下,辅助线程的堆栈并无异常,core文件由于数据错乱也看不出来啥有用的信息来。

复现问题

由于第一步工作受挫,接下来我的思路就在考虑怎么能在开发环境下复现这个问题。

我们的存储服务在其他项目上已经上线了有一段时间了,但是并没有出现类似的问题。那么,出现问题的项目,与其他已经上线的服务有啥不同,这里也许是一个突破口。

经过咨询业务方,该业务的特点是:

  • 单条数据大:有的数据可能有几KB,而之前的项目都只有几百字节。
  • 读请求并发大,而其他业务是写请求远大于读请求。

由于我们的存储服务兼容memcached协议,出现问题时也是以memcached协议进行访问的,所以此时我的考虑是找一个memcached压测工具,模拟前面的数据和请求特点来做模拟压测。

最后选择的是twitter出品的工具twemperf,其特点是可以指定写入缓存的数据范围,同时还可以指定请求的频率。

有了这个工具,首先尝试了往存储中写入大量数据量分布在4KB~10KB的数据,此时没有发现服务有core的情况出现。

然后,尝试构造大量的读请求,果然出现了core情况,重试了几次,都能稳定的重现问题了。

有了能稳定重现问题的办法,总算给问题的解决打开了一个口子。

首次尝试

此时,可以正式的在代码中查找问题的原因了。

来大概说明一下该存储服务的架构:

  1. 主线程负责接收客户端请求,并且进行解析。
  2. 如果是读请求,将分派给读请求处理线程,由这个线程与存储引擎库进行交互,查询数据。此时该线程数量配置为2。
  3. 存储引擎库负责存储落地到磁盘的数据,类似leveldb,只不过这部分是我们自己写的存储引擎。
  4. 在读线程从存储引擎中查询数据返回后,将把数据返回给主线程,由主线程负责应答客户端。

shared_ptr真能防止内存泄漏吗?

这个命题有些诡异,因为shared_ptr设计的初衷就是为了防止内存泄漏,但是先别急,等我把问题描述清楚.

事出缘由是这几天项目出现一个内存泄漏的bug,之前这部分是使用shared_ptr封装了很多指针的操作,后来出于效率的考虑,改回了裸指针.由于我们使用的google

全局定义结构体名称冲突导致的问题(C++)

上周项目代码中出现一个诡异的问题,我在某个数据结构中新增了一个string类型的成员变量,但是每次到了赋值的时候必然出现core dump.跟踪了好久,也猜想到应该是string的函数指针表之类的被破坏了,但是始终找不到原因.最后突然想到搜索代码中该结构体定义的地方,发现同时有两个.cc文件中都定义了该名字的结构体(其中一个也是这一次新增的),于是恍然大悟,应该是结构体的定义名称冲突导致的.

我将这段出错的代码,抽取出来写了个demo,放在

C/C++相关文章收集

1) 详解extern “C”

2) 重载,覆盖,和隐藏

3) Callback在C\C++中的实现

4) 解读google C++ code style谈对C++的理解