资讯专栏INFORMATION COLUMN

Socket Error 104 bug

keithyau / 2625人阅读

摘要:概述技术栈错误详情报警机器人经常有如下警告过程确定报错位置有日志就很好办首先看日志在哪里打的从三个地方入手我们自己的代码没有的代码从上下来没有的代码在容器中执行

bug概述 技术栈

nginx

uwsgi

bottle

错误详情

报警机器人经常有如下警告:

<27>1 2018-xx-xxT06:59:03.038Z 660ece0ebaad admin/admin 14 - - Socket Error: 104
<31>1 2018-xx-xxT06:59:03.038Z 660ece0ebaad admin/admin 14 - - Removing timeout for next heartbeat interval
<28>1 2018-xx-xxT06:59:03.039Z 660ece0ebaad admin/admin 14 - - Socket closed when connection was open
<31>1 2018-xx-xxT06:59:03.039Z 660ece0ebaad admin/admin 14 - - Added: {"callback": >, "only": None, "one_shot": True, "arguments": None, "calls": 1}
<28>1 2018-xx-xxT06:59:03.039Z 660ece0ebaad admin/admin 14 - - Disconnected from RabbitMQ at xx_host:5672 (0): Not specified
<31>1 2018-xx-xxT06:59:03.039Z 660ece0ebaad admin/admin 14 - - Processing 0:_on_connection_closed
<31>1 2018-xx-xxT06:59:03.040Z 660ece0ebaad admin/admin 14 - - Calling > for "0:_on_connection_closed"
debug过程 确定报错位置

有日志就很好办, 首先看日志在哪里打的. 从三个地方入手.

我们自己的代码

没有.

uwsgi的代码

root@660ece0ebaad:/# uwsgi --version
2.0.14
从github上co下来, 没有.

python library的代码

在容器中执行

>>> import sys
>>> sys.path
["", "/usr/lib/python2.7", "/usr/lib/python2.7/plat-x86_64-linux-gnu", "/usr/lib/python2.7/lib-tk", "/usr/lib/python2.7/lib-old", "/usr/lib/python2.7/lib-dynload", "/usr/local/lib/python2.7/dist-packages", "/usr/lib/python2.7/dist-packages", "/usr/lib/python2.7/dist-packages/PILcompat", "/usr/lib/python2.7/dist-packages/gtk-2.0"]

在这些目录下grep, 在pika中找到

root@660ece0ebaad:/usr/local/lib/python2.7# grep "Socket Error" -R .
Binary file ./dist-packages/pika/adapters/base_connection.pyc matches
./dist-packages/pika/adapters/base_connection.py:            LOGGER.error("Fatal Socket Error: %r", error_value)
./dist-packages/pika/adapters/base_connection.py:            LOGGER.error("Socket Error: %s", error_code)

确定pika版本.

>>> import pika
>>> pika.__version__
"0.10.0"
确定错误逻辑

通过代码可以看到, Socket Error是errno的错误码, 确定错误码含义是对端发送了RST.

>>> import errno
>>> errno.errorcode[104]
"ECONNRESET"

怀疑rabbitmq server地址错误, 一个未listen的端口是会返回RST的, 验证后发现不是.
接着怀疑链接超时断开未通知客户端之类. 看rabbitmq server日志, 发现大量:

=ERROR REPORT==== 7-Dec-2018::20:43:18 ===
closing AMQP connection <0.9753.18> (172.17.0.19:27542 -> 192.168.44.112:5672):
missed heartbeats from client, timeout: 60s
--
=ERROR REPORT==== 7-Dec-2018::20:43:18 ===
closing AMQP connection <0.9768.18> (172.17.0.19:27544 -> 192.168.44.112:5672):
missed heartbeats from client, timeout: 60s

发现rabbitmq server和 admin docker的链接已经全部断开

root@xxxxxxx:/home/dingxinglong# netstat -nap | grep 5672  | grep "172.17.0.19"

那么, 为什么rabbitmq server会踢掉 pika建立的链接呢? 看pika代码注释:

    :param int heartbeat_interval: How often to send heartbeats.
                              Min between this value and server"s proposal
                              will be used. Use 0 to deactivate heartbeats
                              and None to accept server"s proposal.

我们没有传入心跳间隔, 理论上应该使用服务端默认的60S. 实际上, 客户端从来没有发出过心跳包. 于是继续看代码:
通过打印, 确认了HeartbeatChecker对象成功创建, 也成功地创建了timer, 但是timer从来没有回调过.
从代码一路跟下去, 我们用的是blocking_connections, 在其add_timeout注释中看到:

def add_timeout(self, deadline, callback_method):
    """Create a single-shot timer to fire after deadline seconds. Do not
    confuse with Tornado"s timeout where you pass in the time you want to
    have your callback called. Only pass in the seconds until it"s to be
    called.

    NOTE: the timer callbacks are dispatched only in the scope of
    specially-designated methods: see
    `BlockingConnection.process_data_events` and
    `BlockingChannel.start_consuming`.

    :param float deadline: The number of seconds to wait to call callback
    :param callable callback_method: The callback method with the signature
        callback_method()

timer的触发要靠process_data_events, 而我们没有调用. 所以客户端的heartbeat从来没被触发. 简单地将heartbeat关掉以解决这个问题.

具体触发点

调用代码如下: 没有跑main_loop, 故, 没处理 rabbitmq_server的FIN包, 无法跟踪链接状态.
一路跟踪basic_publish接口的代码.
在发送时, 收到RST, 最终跑到 base_connection.py:452, _handle_error函数中打印socket_error.

def connect_mq():
    mq_conf = xxxxx
    connection = pika.BlockingConnection(
        pika.ConnectionParameters(mq_conf["host"],
                                  int(mq_conf["port"]),
                                  mq_conf["path"],
                                  pika.PlainCredentials(mq_conf["user"],
                                                        mq_conf["pwd"]),
                                  heartbeat_interval=0))
    channel = connection.channel()
    channel.exchange_declare(exchange=xxxxx, type="direct", durable=True)
    return channel

channel = connect_mq()

def notify_xxxxx():
    global channel

    def _publish(product):
        channel.basic_publish(exchange=xxxxx,
                              routing_key="xxxxx",
                              body=json.dumps({"msg": "xxxxx"}))

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/42991.html

相关文章

  • 如何一步步在生产环境上部署django和vue

    摘要:默认情况下,它是。它也是一个安全度量,所以调整为你的应用需要,而不是最大输出。在运行的时候会把中的静态文件拷贝到这个目录中达到从开发环境到生产环节过程中移植静态文件的作用。 本文由云+社区发表本文主要讲述了如何一步步在生产环境上部署django和vue,操作系统默认为centos 说明:后文中出现的以下字符串均表示具体的路径或者名称,含义如下: DJANGO_DIR----表示dj...

    wendux 评论0 收藏0
  • 如何一步步在生产环境上部署django和vue

    摘要:默认情况下,它是。它也是一个安全度量,所以调整为你的应用需要,而不是最大输出。在运行的时候会把中的静态文件拷贝到这个目录中达到从开发环境到生产环节过程中移植静态文件的作用。 本文由云+社区发表本文主要讲述了如何一步步在生产环境上部署django和vue,操作系统默认为centos 说明:后文中出现的以下字符串均表示具体的路径或者名称,含义如下: DJANGO_DIR----表示dj...

    weknow619 评论0 收藏0
  • Node.js 中遇到含空格 URL 的神奇“Bug”——小范围深入 HTTP 协议

    摘要:开始重现客户端指令其实这次请求的一些猫腻很容易就能发现在中有空格。而在函数中,做的主要事情就是来解析数据包,在解析完成后执行一下回调函数。具体的一些回调函数就不细讲了,有兴趣的童鞋可自行翻阅。如代码片段所示,前文中所对应的函数就是了。 本文首发于知乎专栏蚂蚁金服体验科技。 首先声明,我在Bug字眼上加了引号,自然是为了说明它并非一个真 Bug。 问题抛出 昨天有个童鞋在看后台监控的时候...

    edgardeng 评论0 收藏0
  • rancher1.6高可用集群搭建

    摘要:停掉一台,查看负载均衡健康检查是否正常搭建集群创建需使用的数据库启动访问增加访问控制。给配置负载均衡,增加配置重启访问添加主机,添加容器,测试是否正常,恶意掉容器或者关闭机器,查看整个集群是否正常。 rancher高可用集群搭建 一、搭建环境 1.安装系统 下载centos最新版, http://mirrors.sohu.com/centos/7/isos/x86_64/CentOS...

    gghyoo 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<