踩坑记:临界区内要小心

栏目: 服务器 · Nginx · 发布时间: 6年前

内容简介:这周对一个服务进行了升级,结果踩了一个不大不小的坑。先介绍下这个服务的背景:最开始的实现是会把接收到的数据保存在一个成员变量里,然后等积攒到一定数据量后,写入一个文件内。

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

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

这是一个数据接收的服务,通过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。

转载请注明出处: http://blog.guoyb.com/2018/07/28/mutex-sleep/

欢迎使用微信扫描下方二维码,关注我的微信公众号TechTalking,技术·生活·思考:

踩坑记:临界区内要小心

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

软件构架实践

软件构架实践

林·马斯 / 车立红 / 清华大学出版社 / 2012-6 / 49.00元

软件构架实践(第2版),ISBN:9787302080428,作者:(美)林·巴斯(Len Bass),(美)保罗·克莱门茨(Paul Clements),(美)瑞克·凯兹曼(Rick Kazman)著;车立红译;车立红译一起来看看 《软件构架实践》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码