云计算之路-阿里云上:原来“黑色0.1秒”发生在socket读取数据时
在昨天的博文(云计算之路-阿里云上:读取缓存时的“黑色0.1秒”)中我们犯了一个很低级的错误——把13ms算成了130ms(感谢陈硕发现这个错误!),从而对问题的原因作出了错误的推断,望大家谅解!
从中我们吸取到了一个教训:趁热打铁要小心,容易失去冷静,作出错误的判断。
今天我们痛定思痛,用了一个下午的时间重新分析了“黑色0.1秒”问题,这次从EnyimMemcached的源代码下手(https://github.com/enyim/EnyimMemcached)。
怎么下手呢?我们用了最粗鲁、最原始的方法,在EnyimMemcached源代码中反复找点写日志,运行程序,观察执行时间,逐步缩小范围。
。。。
经过一次又一次的努力,终于锁定了“黑色0.1秒”的引发点:
2014-05-10 15:29:29,903 Enyim.Caching.Memcached.Protocol.Binary.BinaryResponse
MemcachedBinaryResponse-header_socket_read: 103.5174ms 2014-05-10 15:29:29,904 Enyim.Caching.Memcached.MemcachedNode
$MemcachedExecuteOperation-InitPool: 104.4935ms
MemcachedBinaryResponse-header_socket_read的日志记录代码是添加在BinaryResponse的Read()方法中的:
public unsafe bool Read(PooledSocket socket)
{
this.StatusCode = -; if (!socket.IsAlive)
return false; var header = new byte[HeaderLength]; var startTime = DateTime.Now;
socket.Read(header, , header.Length);
LogExecutionTime("header_socket_read", startTime, ); int dataLength, extraLength;
DeserializeHeader(header, out dataLength, out extraLength); if (dataLength > )
{
var data = new byte[dataLength];
socket.Read(data, , dataLength);
} return this.StatusCode == ;
} private void LogExecutionTime(string title, DateTime startTime, int thresholdMs)
{
var duration = (DateTime.Now - startTime).TotalMilliseconds;
if (duration > thresholdMs)
{
log.ErrorFormat("MemcachedBinaryResponse-{0}: {1}ms", title, duration);
}
}
原来“黑色0.1秒”发生在执行socket.Read(header, 0, header.Length);的时候,而且从日志中发现EnyimMemcached读取缓存数据时,超过10ms的操作绝大多数都发生在这个地方。
我们来看一下socket.Read的实现代码:
public void Read(byte[] buffer, int offset, int count)
{
this.CheckDisposed(); int read = ;
int shouldRead = count; while (read < count)
{
try
{
int currentRead = this.inputStream.Read(buffer, offset, shouldRead);
if (currentRead < )
continue; read += currentRead;
offset += currentRead;
shouldRead -= currentRead;
}
catch (IOException)
{
this.isAlive = false;
throw;
}
}
}
inputStream的类型是BufferedStream,socket.Read的操作很简单,就是从BufferedStream中读取指定长度的Byte数据。
这个操作为什么会超过100ms?以我们有限的知识目前无法作出进一步的推断。
为了排除BufferedStream读取方式的原因,我们采用一种更简洁的读取方式——BinaryReader。
用下面的代码取代了原先的socket.Read:
private BinaryReader binReader;
public PooledSocket(...)
{
//...
this.inputStream = new BufferedStream(new BasicNetworkStream(socket));
binReader = new BinaryReader(this.inputStream);
} public byte[] ReadBytes(int count)
{
this.CheckDisposed(); try
{
return binReader.ReadBytes(count);
}
catch (IOException)
{
this.isAlive = false;
throw;
}
}
调用代码变成了这样:
public unsafe bool Read(PooledSocket socket)
{
this.StatusCode = -; if (!socket.IsAlive)
return false; var startTime = DateTime.Now;
var header = socket.ReadBytes(HeaderLength);
LogExecutionTime("header_socket_read", startTime, ); int dataLength, extraLength;
DeserializeHeader(header, out dataLength, out extraLength); if (dataLength > )
{
var data = socket.ReadBytes(dataLength);
} return this.StatusCode == ;
}
采用BinaryReader之后,“黑色0.1秒”问题依旧。
这次我们再三确认,没有犯低级错误, “黑色0.1秒”的确是发生在socket读取数据时。
这次我们依然发出和上次一样的疑问:究竟是微软Windows的原因,还是阿里云虚拟机的原因?
期待有经验的朋友的指点!
【补充】
1. 前面所说的socket.Read中只是读取了前24个字节,而后面真正读取数据时(剩下的字节),超过10ms的情况却少很多。日志中对比了一下,在16:38-18:10期间,超过10ms的header_socket_read出现了2254次,data_socket_read只出现了129次(超过100ms的只出现了1次)。
var data = new byte[dataLength];
socket.Read(data, , dataLength);
2. 连执行System.Net.Sockets.Socket.Send(IList<ArraySegment<byte>> buffers, SocketFlags socketFlags, out SocketError errorCode)都会出现超过50ms的情况。
【附】
修改后的EnyimMemcached代码:https://github.com/cnblogs/EnyimMemcached
【推荐学习材料】
1. BufferedStream Improves .NET Sockets Performance
2. 《TCP/IP Sockets in C#: Practical Guide for Programmers》中的内容:

云计算之路-阿里云上:原来“黑色0.1秒”发生在socket读取数据时的更多相关文章
- 云计算之路-阿里云上:“黑色1秒”问题与2009年Xen一个补丁的故事
在之前对“黑色1秒”问题的分析博文中,我们将最大嫌疑对象锁定在了Xen,在这篇博文我们将从Xen的角度进行分析.也许有人会问,为什么不知道天多高地多厚地去研究不属于自己范围的问题?只因我们对一个问题的 ...
- 云计算之路-阿里云上:“黑色1秒”最新线索——w3tp与w3dt
向大家分享一下最近排查“黑色1秒”问题的进展,“黑色1秒”的问题表现详见什么是黑色1秒. 1. 发生在w3wp进程内 判断依据:“黑色1秒”期间,http.sys的HTTP Service Reque ...
- 云计算之路-阿里云上:从ASP.NET线程角度对“黑色30秒”问题的全新分析
在这篇博文中,我们抛开对阿里云的怀疑,完全从ASP.NET的角度进行分析,看能不能找到针对问题现象的更合理的解释. “黑色30秒”问题现象的主要特征是:排队的请求(Requests Queued)突增 ...
- 云计算之路-阿里云上:Web服务器遭遇奇怪的“黑色30秒”问题
今天下午访问高峰的时候,主站的Web服务器出现奇怪的问题,开始是2台8核8G的云服务器(ECS),后来又加了1台8核8G的云服务器,问题依旧. 而且3台服务器特地使用了不同的配置:1台是禁用了虚拟内存 ...
- 云计算之路-阿里云上:基于Xen的IO模型进一步分析“黑色0.1秒”问题
在发现云服务器读取OCS缓存的“黑色0.1秒”是发生在socket读取数据时,而且是发生在读取开始的字节,甚至在socket写数据时(比如写入缓存key)也会出现超过50ms的情况,我们的好奇心被激发 ...
- 云计算之路-阿里云上:SLB会话保持的一个坑
冒着被大家厌烦的风险,今天再发一篇“云计算之路-阿里云上”.这是在前一篇发过之后真实发生的事情,我们觉得定位问题的过程值得分享.而且估计园子里不少朋友被这个问题骚扰过,我们有责任让大家知道问题的真正原 ...
- 云计算之路-阿里云上-容器难容:容器服务故障以及自建 docker swarm 集群故障
3月21日,由于使用阿里云服务器自建 docker swarm 集群的不稳定,我们将自建 docker swarm 集群上的所有应用切换阿里云容器服务 swarm 版(非swarm mode). 3月 ...
- 云计算之路-阿里云上-新发现:又一种与虚拟内存有关的CPU波动情况
在云上真是无奇不有,昨天偶然间发现在IIS的应用程序池回收设置中,仅仅设置了一下基于虚拟内存限制的回收,就引发了CPU有规律的波动.在这篇博文中,我们将向大家汇报一下云计算之路上的这个小发现. 在之前 ...
- 云计算之路-阿里云上:启用Windows虚拟内存引发的CPU 100%故障
今天上午11:35~11:40左右,由于负载均衡中的两台云服务器CPU占用突然飚至100%,造成网站5分钟左右不能正常访问,请大家带来了麻烦,请谅解! (上图中红色曲线表示CPU占用) 经过分析,我们 ...
随机推荐
- 【转】有关onpropertychange事件
<div style="border:1px solid #fc0;height:24px;width:300px;" id="target">&l ...
- 六、修改 IntelliJ IDEA 模板注释中的 user 内容
咱们进一步了解了 IntelliJ IDEA 的个性化设置功能,包括主题和字体的常用设置等,修改后,具体的效果,如下图所示: 观察上图,不知道大家有没有注意到:IntelliJ IDEA 自带模板注释 ...
- js函数在frame中的相互的调用
框架编程概述一个HTML页面可以有一个或多个子框架,这些子框架以<iframe>来标记,用来显示一个独立的HTML页面.这里所讲的框架编程包括框架的自我控制以及框架之间的互相访问,例如从一 ...
- js点赞效果图
点赞时点赞图标会发生变化. html部分: <img src="img/icon_thumb_up.png" id="imgs1" style=" ...
- echarts图标相关
图标类型参考地址: http://echarts.baidu.com/echarts2/doc/doc.html 知识点一: 堆叠柱状图与普通柱状图的区别在于: 堆叠柱状图 在series中需要设置 ...
- zepto 基础知识(2)
20.append append(content) 类型:self 在每个匹配的元素末尾插入内容(内部插入).内容可以为html 字符串.dom节点,或者节点组成的数组. $('ul').append ...
- jquery mobile 移动web(6)
jquery mobile 针对移动端设备的事件类型. 1.touch 事件. tap 快速触摸屏幕并且离开,类似一种完整的点击操作. taphold 触摸屏幕并保持一段时间. swipe 在1秒内水 ...
- 基于layer封装的异步加载分部视图弹出层
背景:之前一直用的artdialog,但是样式不是很好看,后来偶然看到layer,觉得不错,但是对于.net mvc来说,不能像artdialog一样弹出分部视图是很难受的.所以下面的方法就解决了. ...
- JS apply 和 call 的实现
很早之前的一篇博客写了bind的实现,是基于apply的,感兴趣的朋友看完这篇文章可以接着看看bind的实现. apply 和 call 主要就是传参的区别.这里就不多说了,直接看代码. //call ...
- 洛谷P3804 【模板】后缀自动机
题目描述 给定一个只包含小写字母的字符串 SS , 请你求出 SS 的所有出现次数不为 11 的子串的出现次数乘上该子串长度的最大值. 输入输出格式 输入格式: 一行一个仅包含小写字母的字符串 SS ...