<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title><![CDATA[Felix021]]></title> 
<link>https://www.felix021.com/blog/index.php</link> 
<description><![CDATA[So far so good]]></description> 
<language>zh-cn</language> 
<copyright><![CDATA[Felix021]]></copyright>
<ttl>10</ttl>
<item>
    <title><![CDATA[记 python 超时的一个坑]]></title> 
    <link>https://www.felix021.com/blog/read.php?2209</link>
    <description><![CDATA[# 背景<br/><br/>有一个 python 脚本调用 A 服务的 x 接口获取若干 GB 的数据（大量对象），读取和解析大约需要 5 分钟。<br/><br/>由于 x 接口的改造，需要改成调用 B 服务的 y 接口。<br/><br/>A、B 服务都是基于字节跳动的 KITE 框架开发的（<a href="https://36kr.com/p/5073181" target="_blank">今日头条Go建千亿级微服务的实践</a>），通信协议是 thrift 0.9.2 。<br/><br/># 现象<br/><br/>改成调用 B 服务，在测试过程中发现，每次大约到 3 分钟以后就会出现报错 TTransportException(TSocket read 0 bytes)；之后会重试，但第一次报错后，之后每次大约1分钟内就会再次报同样的错误，重试 3 次后放弃、进程退出。<br/><br/># 排查<br/><br/>1. 由于测试的时间是晚高峰，初步判断可能是晚高峰服务端压力太大导致。次日平峰期测试仍然复现。<br/><br/>2. 搜索，发现有人遇到类似问题，是从 thrift 0.9 升级到 1.0，服务端没有进行 utf8 编码导致客户端的解析问题，他通过修改服务端代码解决。然而服务端显然不存在问题，因为其他的服务调用该接口表现稳定。此外我遇到的问题并没有升级thrift版本。<br/><br/>3. 还是从报错信息入手，在代码里搜索 "TSocket read 0 bytes"，来自于 python2.7/site-packages/thrift/transport/TSocket.py<br/><br/><div class="code">&nbsp; def read(self, sz):<br/>&nbsp; &nbsp; try:<br/>&nbsp; &nbsp; &nbsp; buff = self.handle.recv(sz)<br/>&nbsp; &nbsp; except socket.error, e:<br/>&nbsp; &nbsp; &nbsp; if (e.args&#91;0&#93; == errno.ECONNRESET and<br/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (sys.platform == &#039;darwin&#039; or sys.platform.startswith(&#039;freebsd&#039;))):<br/>&nbsp; &nbsp; &nbsp; &nbsp; self.close()<br/>&nbsp; &nbsp; &nbsp; &nbsp; buff = &#039;&#039;<br/>&nbsp; &nbsp; &nbsp; elif e.args&#91;0&#93; == errno.EINTR:<br/>&nbsp; &nbsp; &nbsp; &nbsp; buff = self.handle.recv(sz)<br/>&nbsp; &nbsp; &nbsp; &nbsp; if len(buff) &gt; 0:<br/>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; return buff<br/>&nbsp; &nbsp; &nbsp; else:<br/>&nbsp; &nbsp; &nbsp; &nbsp; raise<br/>&nbsp; &nbsp; if len(buff) == 0:<br/>&nbsp; &nbsp; &nbsp; raise TTransportException(type=TTransportException.END_OF_FILE, message=&#039;TSocket read 0 bytes&#039;)<br/>&nbsp; &nbsp; return buff</div><br/><br/>4. 通过插入调试代码，发现并没有抛出异常，说明确实只读到了 0 字节，因此可以大致判断问题发生在 server 端。<br/><br/>5. 查看 B 服务的 log，发现确实有“客户端超时” 的报错信息。通过查看 KITE 框架的文档，发现默认的超时时间是 3 秒，A服务在配置文件里指定了 20s 的超时时间，而 B 服务没有指定。<br/><br/>6. 通过修改 B 服务的超时时间，调用成功。但为什么 python 作为一个客户端，会出现长达 3s 的停顿导致超时呢，尤其是在局域网甚至本机环境，不可能是网络原因。<br/><br/>7. 联想到曾经踩过的一个坑（详见：<a href="https://www.felix021.com/blog/read.php?2142" target="_blank">https://www.felix021.com/blog/read.php?2142</a>），猜测是python的gc导致。虽然python是引用计数，但为了避免循环引用导致的内存泄漏，还是有一个 stw 的 gc 扫描。通过关闭这个扫描，就解决了这个超过 3s 的停顿<br/><br/><div class="code">import gc<br/>gc.disable()</div><br/><br/># 吐槽<br/><br/>python真是慢。同样一个api，golang只要17s就完成了调用、反序列化，而python需要长达5分钟。<br/><br/># 吐槽 #2<br/><br/>大概过了半个月，python把内存用爆了，最后只好用 go 重写了。]]></description>
    <pubDate>Sat, 10 Aug 2019 05:34:49 +0000</pubDate> 
    <category><![CDATA[其他]]></category>
    <author>felix021 &lt;i[#at]felix021.com&gt;</author>
    <guid>https://www.felix021.com/blog/read.php?2209</guid> 
</item>
<item>
    <title><![CDATA[[评论] 记 python 超时的一个坑]]></title> 
    <link>https://www.felix021.com/blog/read.php?2209#blogcomment2700</link>
    <description><![CDATA[Python的gc真的很坑]]></description>
    <pubDate>Tue, 15 Oct 2019 02:50:06 +0000</pubDate> 
    <category><![CDATA[评论]]></category>
    <author>Rye &lt;user@domain.com&gt;</author>
    <guid>https://www.felix021.com/blog/read.php?2209#blogcomment2700</guid> 
</item>

</channel>
</rss>