أنا أقوم بتشغيل التطبيق
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
من ذاكرتي ، يحدث هذا الخطأ عندما يحاول العميل الاتصال بدون SSL. هل يمكن أن يكون هذا هو الحال بالنسبة لك؟
أرى رسالتك حول المشكلة الأخرى التي أغلقتها. اعتذاري إذا لم يكن تعليقي هو السبب.
هل هناك نمط تفشل فيه الطلبات بهذه الطريقة؟
usmetanina ما نوع العملاء الذين يتصلون بـ Gunicorn أيضًا؟ هل لديك أي خيارات SSL مستخدمة صراحة للاتصال به؟
هل تم حل هذا بالفعل؟ usmetanina ، لأن لدي نفس المشكلة بالضبط
benoitc أرى خطأusmetanina الدقيق كثيرًا باستخدام python3.6 و gunicorn 19.9.0
.
أستخدم المعلومات أدناه لبدء تشغيل برنامج gunicorn باستخدام تطبيق flask يعمل داخل حاوية عامل إرساء.
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؟
عندما قلت "Load Balancer" ، كان علي حقًا أن أقول CDN أو طبقة التخزين المؤقت. على وجه التحديد: إنها Amazon Cloudfront. يقوم فقط بإعادة توجيه الطلبات إلى خادم Gunicorn الخاص بنا (يعمل على مثيل EC2) ويخزن النتائج مؤقتًا لفترة من الوقت.
ألا يجب على شركة أمازون كلاود فرونت إنهاء طلب SSL نيابةً عنك؟ تضمين التغريدة لماذا يجب على Gunicorn الاستماع على ssl behind؟
benoitc إذن ، هناك طبقتان مع SSL متضمنة في البنية. يتصل أعضاء الجمهور بموقعنا على الويب عبر مجال CloudFront الخاص بنا عبر HTTPS ، ثم تقدم CloudFront طلبًا إلى العقدة الخلفية التي تشغل Gunicorn ، باستخدام HTTPS أيضًا (باسم مجال وشهادة مختلفين) ، وتخزين النتيجة مؤقتًا ، وتقديمها إلى عام.
أعتقد أنك ربما تتساءل ما هو الهدف من استخدام طبقة المقابس الآمنة للطلب الداخلي الثاني؟ من المؤكد أنه لا جدوى من الجدال (على الرغم من أنه قد لا يكون كذلك - فهو يوقف تطفل أمازون على اتصالاتنا في شبكتهم الداخلية ، وهناك أيضًا أسباب تنظيمية لن أخوض في السبب ، نظرًا لصناعة شركتي ، قد نحتاج إلى التأكد من حصولنا على التشفير على طول خط الأنابيب). سواء كان ذلك بلا معنى أم لا ، نحن نفعل ذلك. ¯ \ _ (ツ) _ / ¯
هل يمكن أن تكون واجهة Cloudfront ترسل طلب HTTP عاديًا إلى نقطة النهاية الخاصة بك؟ إذا كان لديك وصول إلى سجلات cloudfront ، فيجب أن تكون قادرًا على رؤيتها.
benoitc لا أعتقد أن CloudFront تعرض أي سجلات قد تكون مفيدة ، لكنني متأكد من أنها لم تكن تحاول الاتصال عبر HTTP ، حيث:
ExplodingCabbage حسنًا ،
3.6.8
أدرك أنني تركت بعض التفاصيل من قصتي أعلاه: قبل إعادة تشغيل Gunicorn ، قمت أيضًا بتحديث شهادة SSL التي يستخدمها Gunicorn مع LetsEncrypt. لم أفكر في ذكر هذا لأنني خلصت بشكل خاطئ بالأمس إلى أنه لا توجد طريقة لانتهاء صلاحية الشهادة في اليوم الذي بدأت فيه الأخطاء وأن تحديث الشهادة لم يكن في الواقع ذا صلة بإصلاح المشكلة.
ومع ذلك ، من خلال فحص بعض السجلات ، أدرك الآن أن الأخطاء بدأت في الواقع في اليوم الذي كان من المقرر أن تنتهي صلاحية الشهادة السابقة.
لا يزال هناك بعض الغموض هنا ، وهناك مجال محتمل للتحسين (ما الذي يشير إليه هذا الخطأ بالضبط ، ولماذا لا يستطيع Gunicorn تقديم رسالة أكثر فائدة؟) ، ولكن السرد الذي قدمته من قبل - حيث بدأ هذا الخطأ فجأة بدون سبب واضح - ليس صحيحًا. أعتقد أن CloudFront كان ينهي الاتصال ردًا على رؤية شهادة منتهية الصلاحية من خادم Gunicorn ، وأن Gunicorn ، بدلاً من أن يكون قادرًا على فهم ذلك والإبلاغ عنه بشكل هادف ، يسمح لظهور خطأ OSE بلا رسائل.
أعتذر عن عدم وجود بطي على التوالي قبل الإبلاغ. من ناحية أخرى ، ربما سيسهل هذا إعادة إنتاج هذا الاستثناء حسب الرغبة إذا كنت ترغب في محاولة التعامل مع السيناريو بشكل أكثر أناقة.
ExplodingCabbage أوه هذا
لقد تعاملت للتو مع نفس المشكلة بشكل متكرر وأنا واثق إلى حد ما من أنها نتيجة لنوع من استنفاد الموارد.
بالنسبة لي ، تم تشغيله عن طريق نسيان مهلة مكالمة حظر وطلبات تتراكم.
HTH
أهلا! أواجه هذه المشكلة بالضبط. لدي خدمة gunicorn / flask تعمل على مجموعة ECS خلف موازن تحميل الشبكة. بعض تفاصيل الإصدار:
python - 3.7.4
gunicorn - 19.9.0
flask - 1.0.4
الخدمة قادرة على الاستجابة للطلبات الواردة من عميل يستخدم TLS بدون مشكلة ، ومع ذلك فإن سجلاتي مغمورة بأدوات OSErrors. بقدر ما أستطيع أن أقول ، هذه ناتجة عن طلبات فحص الصحة الواردة من موازن التحميل (TCP).
تمكنت من إعادة إنتاج الخطأ محليًا عن طريق فتح اتصال TCP وإغلاقه يدويًا على منفذ الاستماع (8000 في هذه الحالة):
$ 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
مع EOFs الممزقة على نظام Linux: https://bugs.python.org/issue31122
كما ذكر shevisjohnson إذا قمت بتنفيذ "nc -vz hostname port_no" يظهر هذا الخطأ.
يمكننا منع هذا الخطأ في ملف السجل باستخدام آلية التسجيل أدناه.
$ القط 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]
ها هو ملف بيثون.
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!"
آمل أن يساعد.
لم يستغرق الأمر سوى لحظة للتوصل إلى إعادة إنتاج موثوقة: استخدام hey
لإرسال 100 طلب متزامن إلى أحدث Gunicorn (20.0.4) باستخدام العامل gthread
:
$ hey -n 100 -c 100 https://127.0.0.1:8000
""
تطبيق gunicorn $
...
[2020-07-11 19:10:58 +0000] [3628247] [خطأ] طلب معالجة خطأ مأخذ التوصيل.
Traceback (آخر مكالمة أخيرة):
عودة self._sslobj.read (لين)
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.8 و 3.9 فرعين ، لكنهم يفكرون في <= 3.7 EOL وما زلنا عالقين نوعًا ما عند 3.6 في الوقت الحالي. هل هناك حل بديل معروف لهذه المشكلة في الوقت الحالي في gunicorn نفسه؟ هل هناك شيء مخطط له؟
نحن نبحث في ما يمكن أن يطلق عليه الخدمة كثيرًا لتشغيل هذا ، لكنني أحاول فقط معرفة ما يمكن فعله ، لأن هذا يؤدي إلى طفرات هائلة في الموارد على العقد المتأثرة.
بالإضافة إلى تعليق jriddy بشأن عدم وجود نية لـ backport قبل 3.8 ، إذا كان لدى أي شخص آخر هذه المشكلة ، لاحظ أيضًا أنه تم تعيين الإصلاح ليتم تضمينه في CPython 3.8.6 .
أواجه مشكلة في معرفة المكان الذي ينبثق منه هذا التتبع بالضبط - في حالتي ، باستخدام gevent
كخادم تطبيق WSGI مباشرة ، لذلك بافتراض أنها مكالمة تسجيل في مكان ما داخل gevent / greenlet ، ولكن لا يمكنك العثور عليها حتى الآن. بالنسبة إلى Gunicorn ، يحدث هذا هنا ، بالنسبة للعاملين المتزامنين:
في حالة 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://github.com/gevent/gevent/issues/1671
التعليق الأكثر فائدة
حسنًا ، بعد إجراء بعض الأبحاث الإضافية ، يبدو أن هذا قد يكون في الواقع خطأ في الطريقة التي تتعامل بها مكتبة python
ssl
مع EOFs الممزقة على نظام Linux: https://bugs.python.org/issue31122