58

踩坑记:临界区内要小心

 5 years ago
source link: http://www.10tiao.com/html/161/201807/2247484137/1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

点击上方蓝色文字“后端技术小黑屋”,关注茄子拯救世界的公众号吧~



这周对一个服务进行了升级,结果踩了一个不大不小的坑。

先介绍下这个服务的背景:

这是一个数据接收的服务,通过http协议接收到json数据之后进行解析,然后落地到本地文件;
之后再由其他服务读取这些文件,进行后续的处理。

最开始的实现是会把接收到的数据保存在一个成员变量里,然后等积攒到一定数据量后,写入一个文件内。

这样做最简单,但是有这么一个问题:

如果服务器重启,那么当前hold在内存中的这些数据,就永久的丢失了。  

所以这一版呢,我就把它改成了实时append写入文件的方式,写够一定的条目数,就切换一个文件来写。

既然要实时落地,那么这里在接受数据的线程和落地文件的线程之间,就需要一个数据队列来作为缓冲。

针对这个数据队列的操作,则应该加锁避免竞态出现。

那么接受数据的伪代码如下:

// Thread receiver
ConstructContentFromJson(json_value, &content_elem);
{
  MutexLocker locker(&mutex_);
  contents_.emplace(std::move(content_elem));
}

而落地文件的伪代码则是:

// Thread writer
while(!quit_) {
  TryWriteContent();
}

//...

int TryWriteContent() {
  ContentElem content_elem;
  {
    MutexLocker locker(&mutex_);
    if (contents_.empty) {
      usleep(1000);
      return ERROR_NO_ELEM;
    }
    content_elem = contents_.pop_front();
  }
  return DoWriteContent(content_elem);
}

看上去一切都很合理:

  • 接受到数据,就在锁的保护下塞到队列中;

  • 写数据时,则在锁的保护下拿出一个数据来拷贝到局部变量,然后就可以放心写入文件了;

  • 如果队列中没数据可获取了,那就原地等待1ms。

临界区也基本控制的很小,应该也没有性能问题。

DUANG!!!

但是,当我升级完后台服务器群中的一台后,看监控,却发现请求量在更新服务器之后,暴跌了90%。

但是问题是,我这才只更新了一台服务器啊。还有好几台没更新,跑的也是旧版本程序,它们上面接收到的请求也暴跌了。

那一定不是我的锅!

于是我向前去查了nginx log,发现确实nginx接收到的请求数量就少了很多。

难道是服务的调用方也同时发了新版本?

于是赶紧电话联系千里之外的网友同事,查出来的结果是:

由于服务超时严重,所以调用方主动限流了。

那看来还是我的锅……是你的,总是逃不掉。毕竟调用方看来,nginx代理屏蔽了后台所有服务器细节,所以后台一台服务器超时严重,调用方就会认为整个服务超时严重。

再回头去看上面的核心代码,问题很快就浮出水面了:

usleep放在了锁的临界区范围内!

这会导致写文件线程在没数据时,一直占据着锁。虽然这个线程在sleep,但是它却占据着锁,其他线程也没办法往队列里填充数据,等于这部分时间服务啥也干不了。多来几次,就大面积超时了。

问题找到了,解决方法也很简单,把usleep挪到锁外即可。

总结

  • 锁的临界区内,一定不能出现sleep这种阻塞操作(包括但不限于文件IO、网络IO等)。

  • 需要sleep时,可以考虑主动将线程的控制权让出,从而避免使用sleep。




推荐阅读:

再谈右值引用与移动语义

机器学习中的AUC是什么?

关于内存泄露的一场虚惊


题图:茄子的坚果Pro2

授权:CC0协议




About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK