Gunicorn: OSError: [Errno 0] 错误

创建于 2018-05-08  ·  30评论  ·  资料来源: benoitc/gunicorn

我正在运行应用程序
gunicorn -w 2 -b ' localhost:8585 ' --timeout=200 --certfile=crt.crt --keyfile=key.key service:app

我得到以下,但我并不总是得到这样的答案,大多数请求都被正确处理,但有时会发生错误

[2018-05-08 14:53:36 +0500] [11227] [ERROR] Socket error processing request.
Traceback (most recent call last):
  File "/usr/lib/python3/dist-packages/gunicorn/workers/sync.py", line 134, in handle
    req = six.next(parser)
  File "/usr/lib/python3/dist-packages/gunicorn/http/parser.py", line 41, in __next__
    self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 153, in __init__
    super(Request, self).__init__(cfg, unreader)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 53, in __init__
    unused = self.parse(self.unreader)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 165, in parse
    self.get_data(unreader, buf, stop=True)
  File "/usr/lib/python3/dist-packages/gunicorn/http/message.py", line 156, in get_data
    data = unreader.read()
  File "/usr/lib/python3/dist-packages/gunicorn/http/unreader.py", line 38, in read
    d = self.chunk()
  File "/usr/lib/python3/dist-packages/gunicorn/http/unreader.py", line 65, in chunk
    return self.sock.recv(self.mxchunk)
  File "/usr/lib/python3.5/ssl.py", line 922, in recv
    return self.read(buflen)
  File "/usr/lib/python3.5/ssl.py", line 799, in read
    return self._sslobj.read(len, buffer)
  File "/usr/lib/python3.5/ssl.py", line 585, in read
    v = self._sslobj.read(len)
OSError: [Errno 0] Error
( FeaturSSL

最有用的评论

嗯,经过一些额外的研究,这似乎实际上可能是 python ssl库在 linux 上处理参差不齐的 EOF 的方式中的一个错误: https ://bugs.python.org/issue31122

所有30条评论

根据我的记忆,当客户端尝试在没有 SSL 的情况下进行连接时会发生此错误。 你会是这种情况吗?

我看到你关于我关闭的另一个问题的帖子。 如果我的评论不是原因,我深表歉意。

是否存在请求以这种方式失败的模式?

@usmetanina什么样的客户端也连接到 Gunicorn? 您是否有任何明确用于连接到它的 SSL 选项?

这已经解决了吗? @usmetanina ,因为我有完全相同的问题

@benoitc我经常使用 python3.6 和 gunicorn 19.9.0看到@usmetanina的确切错误。

我使用以下信息通过在 docker 容器中运行的 Flask 应用程序启动 gunicorn。

gunicorn --workers=3 --bind=0.0.0.0:8000 --config=gunicorn_config.py --preload main

配置文件看起来像这样(domain-with-cert.com 当然是实际域名的占位符):

workers = 3
bind = '0.0.0.0:443'
certfile = '/etc/letsencrypt/live/domain-with-cert.com/fullchain.pem'
keyfile = '/etc/letsencrypt/live/domain-with-cert.com/privkey.pem'

关于调试的任何想法都会有所帮助。 如果您需要更多信息,请告诉我。

@willpatera ,看我的评论:

根据我的记忆,当客户端尝试在没有 SSL 的情况下进行连接时会发生此错误。 你会是这种情况吗?

@tilgovi我看到了上面的评论。 我很确定客户端是通过 SSL 连接的。 任何调试建议?

@willpatera我会说,打开访问日志,看看您是否可以确定导致问题的请求。 如果您在 gunicorn 前面有一个反向代理,请确保它有访问日志,这样即使 gunicorn 从未记录过,您也可以查看哪个请求会导致 gunicorn 出错。

@tilgovi我遇到了同样的问题。 由于不正确,不得不稍微编辑以下信息:
向 gunicorn 提出的请求始终是完全相同的请求(但具有不同的主体)。 所以毫无疑问是https而不是http。
我注意到的是,它总是在请求量增加时发生。 当服务器繁忙时,它似乎无法正确处理请求。

也许这与工人或类似的事情有关? 如果您有任何配置建议,我很乐意测试它们。

大家好,我还在寻找解决这个问题的方法。 目前我们唯一的选择是降级到纯 HTTP,这根本不可行。

我亲眼目睹了同样的事情。 有一个运行 Gunicorn + Flask(在负载均衡器后面)的生产服务器,它工作了好几个月,然后突然每个请求都产生这个错误,直到我重新启动 Gunicorn:

[2019-11-21 07:27:36 +0000] [24245] [ERROR] Socket error processing request.
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/workers/sync.py", line 134, in handle
    req = six.next(parser)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/parser.py", line 41, in __next__
    self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 181, in __init__
    super(Request, self).__init__(cfg, unreader)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 54, in __init__
    unused = self.parse(self.unreader)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 193, in parse
    self.get_data(unreader, buf, stop=True)
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/message.py", line 184, in get_data
    data = unreader.read()
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/unreader.py", line 38, in read
    d = self.chunk()
  File "/usr/local/lib/python3.6/dist-packages/gunicorn/http/unreader.py", line 65, in chunk
    return self.sock.recv(self.mxchunk)
  File "/usr/lib/python3.6/ssl.py", line 997, in recv
    return self.read(buflen)
  File "/usr/lib/python3.6/ssl.py", line 874, in read
    return self._sslobj.read(len, buffer)
  File "/usr/lib/python3.6/ssl.py", line 633, in read
    v = self._sslobj.read(len)
OSError: [Errno 0] Error

这些错误之前的日志中没有任何内容暗示触发器可能是什么。

这是 Gunicorn 19.9.0 在单核服务器上运行 3 个工人的情况。

由于这是我第一次看到这个问题,我不能保证我会重现它。 但是,如果有人希望我将任何类型的日志记录或其他诊断代码添加到我们的服务器上,以便在再次发生这种情况时提供一些有用的信息,我会全力以赴。

您的 LB 是否调用特定端点? 它如何响应 LB 请求?

当我说“负载均衡器”时,我真的应该说 CDN 或缓存层。 具体来说:它是 Amazon Cloudfront。 它只是将请求转发到我们的 Gunicorn 服务器(在 EC2 实例上运行)并将结果缓存一段时间。

人力资源管理不应该 amazon cloudfront 终止你的 ssl 请求吗? @爆炸卷心菜。 为什么gunicorn必须在ssl后面听?

@benoitc因此,架构中有两层涉及 SSL。 公众成员通过我们的 CloudFront 域通过 HTTPS 连接到我们的网站,然后 CloudFront 向我们运行 Gunicorn 的后端节点发出请求,使用 HTTPS(具有不同的域名和证书),缓存结果,并将其提供给民众。

我想您可能想知道在第二个内部请求中使用 SSL 的意义是什么? 这当然是毫无意义的(虽然可能不是 - 它阻止亚马逊窥探我们内部网络中的通讯,还有监管方面的原因我不会说为什么,考虑到我公司的行业,我们可能需要确保我们有沿管道一直加密)。 不管有没有意义,我们都会去做。 ¯\_(ツ)_/¯

会不会是 cloudfront 正在向您的端点发送一个普通的 HTTP 请求? 如果您有权访问 cloudfront 日志,则应该能够看到它。

@benoitc我认为 CloudFront 不会公开任何有用的日志,但我确信它没有尝试通过 HTTP 连接,因为:

  • 我们的分配在 CloudFront 控制台中配置为通过“仅 HTTPS”连接到 Gunicorn 源
  • Gunicorn 没有监听 80 端口
  • 如果尝试通过 HTTP 连接到我们的后端服务器(包括在端口 443 上强制使用 HTTP),它不会重现上面引用的 OSError
  • 当我得到上面引用的 OSError 时,在后端服务器上重新启动 Gunicorn 立即解决了问题,这表明 Gunicorn 端出现问题,而不是 Cloudfront 端

@ExplodingCabbage好的,我会在 20.0.1 出来后看看它。 最后一件事,您使用的是哪个版本的 Python?

3.6.8

我意识到我在上面的故事中遗漏了一个细节:在重新启动 Gunicorn 之前,我还更新了 Gunicorn 使用 LetsEncrypt 使用的 SSL 证书。 我没想到要提及这一点,因为我昨天错误地得出结论,证书不可能在错误开始的那天过期,并且证书更新实际上与解决问题无关。

但是,通过检查一些日志,我现在意识到错误实际上始于前一个证书到期的那一天。

这里还有一些谜团,还有一些潜在的改进空间(这个错误究竟意味着什么,为什么 Gunicorn 不能给出更有用的信息?),但是我之前给出的叙述 - 这个错误是突然开始的没有明显的原因 - 不对。 我猜想 CloudFront 正在终止连接以响应看到来自 Gunicorn 服务器的过期证书,而 Gunicorn 不是能够理解并有意义地报告它,而是让无消息的 OSError 冒出来。

我很抱歉在报告之前没有让我的鸭子排成一排。 另一方面,如果您想尝试更优雅地处理场景,这可能会使随意重现此异常变得更容易。

@ExplodingCabbage哦,这很有趣,它应该在某个时候可以重现。 感谢您提供更多详细信息!

我刚刚重复遇到了同样的问题,我有点确信这是某种资源耗尽的结果。

对我来说,它是由于忘记阻塞调用超时和请求堆积而触发的。

HTH

你好! 我正在经历这个确切的问题。 我有一个在网络负载均衡器后面的 ECS 集群上运行的 gunicorn/flask 服务。 一些版本细节:

python    - 3.7.4
gunicorn  - 19.9.0
flask     - 1.0.4

该服务能够毫无问题地响应来自使用 TLS 的客户端的请求,但是我的日志充斥着 OSErrors。 据我所知,这些是由来自负载均衡器 (TCP) 的健康检查请求引起的。

通过在侦听端口(在本例中为 8000)上手动打开和关闭 TCP 连接,我能够在本地重现错误:

$ nc -vz 127.0.0.1 8000
localhost [127.0.0.1] 8000 (irdmi) open

这导致抛出以下错误:

Traceback (most recent call last):
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/workers/sync.py" line 134 in handle
        req = six.next(parser)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/parser.py" line 41 in __next__
        self.mesg = self.mesg_class(self.cfg, self.unreader, self.req_count)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 181 in __init__
        super(Request, self).__init__(cfg, unreader)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 54 in __init__
        unused = self.parse(self.unreader)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 193 in parse
        self.get_data(unreader, buf, stop=True)
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/message.py" line 184 in get_data
        data = unreader.read()
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/unreader.py" line 38 in read
        d = self.chunk()
    File "/nix/store/nh3v0c2nipihwblkdn0mh2kqyv3jq9nz-python3-3.7.4-env/lib/python3.7/site-packages/gunicorn/http/unreader.py" line 65 in chunk
        return self.sock.recv(self.mxchunk)
    File "/nix/store/azwzsm1pkbzjxpkiq88w68p4jdghgasl-python3-3.7.4/lib/python3.7/ssl.py" line 1056 in recv
        return self.read(buflen)
    File "/nix/store/azwzsm1pkbzjxpkiq88w68p4jdghgasl-python3-3.7.4/lib/python3.7/ssl.py" line 931 in read
        return self._sslobj.read(len)
OSError: [Errno 0] Error

希望这可以帮助!

嗯,经过一些额外的研究,这似乎实际上可能是 python ssl库在 linux 上处理参差不齐的 EOF 的方式中的一个错误: https ://bugs.python.org/issue31122

正如@shevisjohnson所提到的,如果您执行“nc -vz hostname port_no”,则会出现此错误。
我们可以使用以下日志记录机制在日志文件中抑制此错误。

$cat logging_config.yml

version: 1

formatters:
  simple:
    format: " %(asctime)s || %(name)s || %(levelname)s || %(message)s"

  test_api:
    format: "[%(asctime)s] [%(process)s] [%(levelname)s] %(message)s"

handlers:

  console:
    class: logging.StreamHandler
    level: DEBUG
    formatter: simple
    stream: ext://sys.stdout

  test_api_file_handler:
     class: logging.handlers.RotatingFileHandler
     level: DEBUG
     formatter: test_api
     filename: logs/test.log
     maxBytes: 2000000000
     backupCount: 1
     encoding: utf8

loggers:

  test_api: 
    level: DEBUG
    handlers: [test_api_file_handler]
    propagate: 0

root:
  level: DEBUG
  handlers: [console]

这是python文件。

import logging
import yaml
from flask import Flask

app = Flask(__name__)

def logSetter(logger_name:str) -> logging:
    with open("logging_config.yml", 'r') as f:
        config = yaml.safe_load(f)
    logging.config.dictConfig(config)
    logger = logging.getLogger(logger_name)
    return logger

logger=logSetter(logger_name="test_api")

@app.route("/api/test")
def hello():
     app.logger.info("hey from api")
     return "Hello from Python!"

希望能帮助到你。

我们间歇性地观察到几个 Gunicorn 应用程序在并发负载下在生产中因此错误而失败。

只花了一点时间就想出了一个可靠的复制:使用hey使用gthread工作程序向最新的 Gunicorn (20.0.4) 发送 100 个并发请求:

$ hey -n 100 -c 100 https://127.0.0.1:8000

``
$ gunicorn app:app -k gthread --certfile=... --keyfile=...
...
[2020-07-11 19:10:58 +0000] [3628247] [ERROR] 套接字错误处理请求。
回溯(最近一次调用最后一次):
返回 self._sslobj.read(len)
OSError: [Errno 0] 错误


Using a Debian 9 / Linux 4.14.67 based environment.

The WSGI app to reproduce need not be anything beyond:
```python
# app.py
def app(environ, start_response):
    start_response("200 OK", [])
    return ""

如果这也有帮助!

如果根本原因实际上是https://bugs.python.org/issue31122

  • 3 月 4 日提交了修复(python/cpython#18772),但仍未得到核心开发人员的认可。 也许 Gunicorn 维护者在那里或在 BPO-31122 上发表评论说它影响了 gunicorn 用户会有所帮助吗?
  • 对于在该修复程序发布之前的受支持 Python 版本,Gunicorn 仍需要解决此问题。 还值得一问,在同一条评论中是否有解决方法?

这也影响了我的组织生产。

我注意到错误修正出现在 3.8 和 3.9 分支中,但他们正在考虑 <= 3.7 EOL 并且我们暂时仍然停留在 3.6 上。 目前,gunicorn 本身是否有解决此问题的已知解决方法? 有什么计划吗?

我们正在研究什么可能会调用服务来触发这个,但我只是想弄清楚可以做什么,因为这会导致受影响节点上的资源激增。

除了 jriddy 关于无意在 3.8 之前向后移植的评论之外,如果其他人遇到此问题,还请注意该修复程序将包含在 CPython 3.8.6 中

无法准确说明此回溯源自何处 - 在我的情况下,直接使用gevent作为 WSGI 应用程序服务器,因此假设它是 gevent/greenlet 中某处的日志记录调用,但目前还找不到它。 对于 Gunicorn,它发生在这里,对于同步工作者:

https://github.com/benoitc/gunicorn/blob/e636bf81989bb833d2b99104feb11e86c3f2c43a/gunicorn/workers/sync.py#L150

在 Gunicorn 的情况下,如果您只关心日志中的噪音,也许可以执行以下操作:

import logging

class HandshakeFilter(logging.Filter):
    # example: https://docs.python.org/3/howto/logging-cookbook.html
    # I have not tested this
    def filter(self, record):
        return "socket error processing request" in record.msg.casefold()

logging.getLogger("gunicorn").addFilter(HandshakeFilter())

相关 gevent 问题: https :

此页面是否有帮助?
0 / 5 - 0 等级