Seperti yang dikatakan masalah lama 1210 ini, gunicorn mencatat kesalahan saat klien terputus, dan lingkungan saya:
Debian GNU/Linux 7.8
nginx
Python3.4
gunicorn(19.8.1) (dengan satu atau beberapa pekerja)
Flask-SocketIO, klien menentukan transportasi websocket
Semuanya berfungsi dengan baik termasuk klien, kecuali untuk log kesalahan ini, dua instance produksi independen cloud, keduanya terus-menerus mencatat, tetapi saya tidak dapat mereproduksinya di mesin pengembangan saya, yang merupakan mac.
Terima kasih banyak atas bantuan Anda.
Kesalahan saat menangani permintaan /socket.io/?EIO=3&transport=websocket
Traceback (panggilan terakhir terakhir):
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/workers/async.py", baris 56, di handle
self.handle_request(listener_name, req, client, addr)
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/workers/async.py", baris 116, di handle_request
resp.close()
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py", baris 409, di dekat
self.send_headers()
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py", baris 325, di send_headers
tosend = self.default_headers()
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py", baris 306, di default_headers
elif self.should_close():
File "/opt/apps/lms/virtualenv/lib/python3.4/site-packages/gunicorn/http/wsgi.py", baris 229, di should_close
jika self.status_code < 200 atau self.status_code di (204, 304):
AttributeError: objek 'Response' tidak memiliki atribut 'status_code'
apakah Anda punya contoh sederhana untuk mereproduksinya? Coba juga dengan master terbaru jika memungkinkan.
Sebelumnya saya telah mencoba beberapa kali di lingkungan pengembangan lokal saya, yang merupakan kode aplikasi yang sama dengan lingkungan produksi, tetapi saya tidak dapat mereproduksinya.
Dan saya telah memeriksa log rilis versi 19.9.0,tidak menemukan sesuatu yang terkait,Saya akan menyimpannya
menonton log kesalahan ini, jika menemukan sesuatu yang baru, saya akan memposting di sini.
Saya juga memiliki masalah ini, khususnya ketika saya memaksa semua koneksi saya dari klien ke protokol websocket. Pengaturan saya sama dengan BoWuGit. Jika izinkan protokol polling sebelum memutakhirkan, ini tidak muncul, tetapi kesalahan lain:
`
[ERROR] Kesalahan menangani permintaan /socket.io/?EIO=3&transport=polling&t=MPRHUoV&sid=cd64be7c940e474d8728b114c3fb9bbe
Traceback (panggilan terakhir terakhir):
File "/usr/local/lib/python3.6/site-packages/gunicorn/workers/async.py", baris 56, di handle
self.handle_request(listener_name, req, client, addr)
File "/usr/local/lib/python3.6/site-packages/gunicorn/workers/async.py", baris 107, di handle_request
respiter = self.wsgi(environ, resp.start_response)
File "/usr/local/lib/python3.6/site-packages/flask/app.py", baris 1994, di __call__
kembalikan self.wsgi_app(environ, start_response)
File "/usr/local/lib/python3.6/site-packages/flask_socketio/__init__.py", baris 43, di __call__
start_response)
File "/usr/local/lib/python3.6/site-packages/engineio/middleware.
py", baris 47, di __call__
kembalikan self.engineio_app.handle_request(environ, start_response)
File "/usr/local/lib/python3.6/site-packages/socketio/server.py", baris 360, di handle_request
kembalikan self.eio.handle_request(lingkungan, start_response)
File "/usr/local/lib/python3.6/site-packages/engineio/server.py", baris 279, di handle_request
socket = self._get_socket(sid)
File "/usr/local/lib/python3.6/site-packages/engineio/server.py", baris 439, di _get_socket
menaikkan KeyError('Sesi terputus')
`
Tapi saya curiga mungkin ada hubungannya satu sama lain, karena saya memaksa koneksi menjadi websocket, kesalahan ini tidak terlihat lagi.
Mengalami masalah ini juga dengan gunicorn 19.9.0 dan Flask-socketIO 3.0.2, saat menggunakan eventlet 0.24.1
AttributeError: objek 'Response' tidak memiliki atribut 'status_code'
Juga mengalami masalah ini dengan persyaratan berikut:
Flask==1.0.2
gunicorn==19.5.0
python-socketio==2.0.0
eventlet==0.24.1
Pesan kesalahan saat menutup browser web yang memiliki koneksi soket terbuka:
Error handling request /socket.io/?EIO=3&transport=websocket&sid=d43ec0ae0bb946debc51f1ca2e5b8a94
Traceback (most recent call last):
File "/usr/lib/python2.7/dist-packages/gunicorn/workers/async.py", line 52, in handle
self.handle_request(listener_name, req, client, addr)
File "/usr/lib/python2.7/dist-packages/gunicorn/workers/async.py", line 114, in handle_request
resp.close()
File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 403, in close
self.send_headers()
File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 319, in send_headers
tosend = self.default_headers()
File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 300, in default_headers
elif self.should_close():
File "/usr/lib/python2.7/dist-packages/gunicorn/http/wsgi.py", line 233, in should_close
if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'
Sepertinya masalah ini telah diperbaiki di versi terbaru python-engineio ..
Telah diuji dengan python-engineio versi terbaru (2.3.2), masih tidak berfungsi.
Ada berita tentang masalah ini? Saya mendapatkan kesalahan yang sama saat menggunakan penjaga-python
Saya memiliki masalah yang sama
acara: 0.25.1
termos-socketio: 4.2.1
gunicorn: 19.9.0
bagaimana cara memperbanyaknya? cna Anda memberikan contoh sederhana?
saya juga tidak yakin bagaimana mereproduksinya, tetapi itu sering terjadi ketika saya me-refresh halaman di aplikasi gunicorn saya
Mengalami masalah yang sama, dan lingkungan saya sama dengan @eazow sementara gunicorn == 20.0.4.
Tampaknya masalah terjadi setelah saya menginstal penjaga untuk pelacakan bug.
Masalah dapat direproduksi oleh
Menariknya, membuka halaman baru tidak akan menghasilkan masalah. Tidak yakin mengapa. Terima kasih!
Saya memiliki masalah yang sama dengan @cowbonlin . Versi gunicorn yang sama juga.
Setelah menginstal penjaga, kami mendapatkan banyak kesalahan ini. Meskipun saya merasa sulit untuk mengatakan apakah ini selalu terjadi atau tidak - karena kami tidak melacak kesalahan sebelum penjaga.
Meskipun tampaknya tidak memengaruhi fungsionalitas sebenarnya dari server kami, ini hanyalah satu ton spam.
Kami mengalami hal yang sama. Sentry diinstal tetapi dinonaktifkan. Ada ide?
Masalah yang sama dengan penjaga terpasang.
Apakah Anda memiliki contoh yang mereproduksinya tanpa penjaga (dinonaktifkan atau tidak)?
Selain itu, alih-alih namespace saya secara manual menekan /api.
Selain itu, alih-alih namespace saya secara manual menekan /api.
apa artinya ? Apakah penjaga ini terkait?
Selain itu, alih-alih namespace saya secara manual menekan /api.
apa artinya ? Apakah penjaga ini terkait?
Tidak, ini terkait dengan ruang nama socket.io. Saya mencoba menghapusnya, dan bahkan setelah menghapusnya, saya mendapatkan kesalahan. Saya mendapatkan kesalahan lain ini di mesin lokal tanpa gunicorn atau nginx, yang mungkin terkait.
Ini adalah persyaratan saya:
sentry_sdk == 0.14.3
Flask_SocketIO == 4.2.1
eventlet == 0.25.1
Ini adalah kode flask-socketio saya di sisi server:
socketio = SocketIO(engineio_logger=True, logger=True, debug=True, cors_allowed_origins="*", path='/socket.io')
...
socketio.init_app(app, async_mode="eventlet")
Dan ini adalah kode io soket React saya di sisi klien:
this.socket = io.connect(`http://localhost:5000?info=${someInfo}`, {
transports: ['websocket', 'polling'] // an attempt to keep polling as a fallback but start on websockets
});
Beri tahu saya jika ini membantu. Di Ubuntu kesalahannya terlihat seperti di atas, dan secara lokal di Windows terlihat seperti ini:
```Traceback (panggilan terakhir terakhir):
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 599, di handle_one_response
tulis (b'')
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 491, di tulis
naikkan AssertionError("tulis() sebelum start_response()")
AssertionError: tulis() sebelum start_response()
Selama penanganan pengecualian di atas, pengecualian lain terjadi:
Traceback (panggilan terakhir terakhir):
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 357, di __init__
menangani sendiri()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 390, di pegangan
self.handle_one_request()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 466, di handle_one_request
self.handle_one_response()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 609, di handle_one_response
tulis(err_body)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 538, di tulis
wfile.flush()
File "C:\ProgramData\Anaconda3\lib\socket.py", baris 607, di tulis
kembalikan diri._sock.send(b)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\greenio\base.py", baris 397, di kirim
kembalikan self._send_loop(self.fd.send, data, flags)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\greenio\base.py", baris 384, di _send_loop
kembalikan send_method(data, *args)
ConnectionAbortedError: [WinError 10053] Koneksi yang dibuat dibatalkan oleh perangkat lunak di mesin host Anda
Selama penanganan pengecualian di atas, pengecualian lain terjadi:
Traceback (panggilan terakhir terakhir):
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\hubs\hub.py", baris 461, di fire_timers
pengatur waktu()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\hubs\timer.py", baris 59, di __call__
cb( args, * kw)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\semaphore.py", baris 147, di _do_acquire
pelayan.switch()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\greenthread.py", baris 221, di main
hasil = fungsi ( args, * kwargs)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 818, di process_request
proto.__init__(conn_state, self)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 359, di __init__
mandiri.selesai()
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\wsgi.py", baris 732, selesai
BaseHTTPServer.BaseHTTPRequestHandler.finish(mandiri)
File "C:\ProgramData\Anaconda3\lib\socketserver.py", baris 784, selesai
diri.wfile.close()
File "C:\ProgramData\Anaconda3\lib\socket.py", baris 607, di tulis
kembalikan diri._sock.send(b)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\greenio\base.py", baris 397, di kirim
kembalikan self._send_loop(self.fd.send, data, flags)
File "C:\ProgramData\Anaconda3\lib\site-packages\eventlet\greenio\base.py", baris 384, di _send_loop
kembalikan send_method(data, *args)
ConnectionAbortedError: [WinError 10053] Koneksi yang dibuat dibatalkan oleh perangkat lunak di mesin host Anda```
Dapat mengkonfirmasi kesalahan ini hilang ketika penjaga sepenuhnya dinonaktifkan. Akan lebih bagus jika gunicorn cukup kuat untuk menghadapi ini.
Bump @benoitc
Dapat mengkonfirmasi kesalahan ini hilang ketika penjaga sepenuhnya dinonaktifkan. Akan lebih bagus jika gunicorn cukup kuat untuk menghadapi ini.
Saya menemukan bahwa menonaktifkan FlaskIntegration
Sentry juga membuat kesalahan hilang.
Melihat perilaku serupa. Menggunakan New Relic dalam produksi menyebabkan kesalahan ini dengan flask-socketio. Dalam pengembangan, middleware debugger werkzeug perlu dimuat sebelum flask-socketio diinisialisasi (jadi ini tidak diterapkan ke aplikasi wsgi engineio). Masalahnya adalah produksi adalah tempat saya benar-benar tidak ingin kesalahan tersandung.
Tidak dapat mengganti respons di post_request konfigurasi gunicorn, tetapi saya mencoba memaksakan kode status ke resp.status_code. Itu tidak memakan waktu.
Kesalahan ini dapat direproduksi dengan menggunakan Sentry's FlaskIntegration bersama dengan Gunicorn dan Flask-SocketIO. Apakah mungkin untuk segera menyelesaikannya?
@Canicio kami berpikir untuk mencobanya untuk menghilangkan kesalahan dan bahkan setelah menonaktifkan integrasi, kesalahan tetap ada.
Adakah yang punya kode yang dapat dibagikan/contoh minimal untuk @benoitc untuk dimatikan?
Tentu:
import sentry_sdk
from flask import Flask
from flask_socketio import SocketIO
from sentry_sdk.integrations.flask import FlaskIntegration
sentry_sdk.init(
dsn="https://[email protected]/0",
integrations=[FlaskIntegration()]
)
app = Flask(__name__)
socketio = SocketIO(app)
@app.route('/')
def index():
return '''
<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.js"></script>
<script>
var socket = io()
</script>
persyaratan:
flask
sentry-sdk[flask]
flask-socketio
eventlet
contoh konfigurasi gunicorn:
bind = '[::]:4444'
worker_class = 'eventlet'
accesslog = '-'
Saat memuat /
itu akan terhubung ke soket web. Pada websocket disconnect (mis. navigasi pergi, segarkan), akan menghasilkan pengecualian seperti ini:
[2020-09-23 07:24:49 +0000] [16303] [ERROR] Error handling request /socket.io/?EIO=3&transport=websocket&sid=29f4c1adfac343d6bc6db56acf8fd0ee
Traceback (most recent call last):
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/workers/base_async.py", line 55, in handle
self.handle_request(listener_name, req, client, addr)
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/workers/base_async.py", line 115, in handle_request
resp.close()
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 402, in close
self.send_headers()
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 318, in send_headers
tosend = self.default_headers()
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 299, in default_headers
elif self.should_close():
File "/home/ziddey/projects/sentry/venv_sentry/lib/python3.8/site-packages/gunicorn/http/wsgi.py", line 219, in should_close
if self.status_code < 200 or self.status_code in (204, 304):
AttributeError: 'Response' object has no attribute 'status_code'
2001:470:1f07:7eb:9dd4:254c:35d7:236c - - [23/Sep/2020:07:24:49 +0000] "GET /socket.io/?EIO=3&transport=websocket&sid=29f4c1adfac343d6bc6db56acf8fd0ee HTTP/1.1" 500 0 "-" "-"
Catatan: Saya sendiri tidak pernah benar-benar menggunakan penjaga. Ini hanya dari halaman memulai penjaga. Contoh dsn
berfungsi dengan baik untuk pengujian kami.
Mengomentari integrations=[FlaskIntegration()]
kemudian akan menghilangkan kesalahan (tentu saja secara efektif menonaktifkan penjaga).
Untuk apa nilainya, gevent-websocket dapat digunakan sebagai pengganti eventlet tanpa kesalahan. Namun, tampaknya menangani semua permintaan..
Ok, melakukan beberapa bermain-main. Sepertinya penjaga/newrelic membungkus tanggapan. Tanpa penjaga, kami mendapatkan <eventlet.wsgi._AlreadyHandled object at 0x7fd0f5b1c0d0>
seperti yang diharapkan dan EventletWorker.is_already_handled() gunicorn akan menghentikan iterasi.
Namun, saat menggunakan penjaga, ini menjadi seperti <sentry_sdk.integrations.wsgi._ScopedResponse object at 0x7f30155a5100>
sebagai gantinya, gagal dalam pemeriksaan
Sebaliknya, kita bisa mengintip respiter untuk melihat apakah itu kosong. Akan melihat lebih jauh besok.
Baiklah, inilah solusi yang saya buat:
eventlet_fix.py:
lihat edit di bawah
Dan di gunicorn config.py saya: worker_class = 'eventlet_fix.EventletWorker
.
Masalahnya adalah bahwa penjaga/newrelic membungkus tanggapan, jadi kami tidak bisa begitu saja memeriksanya dengan ALREADY_HANDLED
eventlet. Karena sifat dari permintaan yang sudah ditangani adalah bahwa start_response
gunicorn tidak dipanggil, kita dapat memeriksa keberadaan status respons.
Jadi saya telah membajak panggilan wsgi untuk kemudian memeriksa status respons, dan meretas nilai respons seperlunya. Ini memungkinkan permintaan untuk tetap dicatat oleh gunicorn. Jika sebaliknya, diinginkan untuk mempertahankan perilaku asli, StopIteration
dapat dinaikkan sebagai gantinya.
Meretas status ke 101 sesuai untuk kasus penggunaan kami di sini (websocket flask-socketio), tetapi sebaliknya, membiarkannya sebagai Tidak ada juga berfungsi karena headers_sent
dan should_close
dipaksa ke True.
Sekali lagi, ini membuat asumsi bahwa jika status
tidak disetel, start_response
tidak dipanggil, dan oleh karena itu permintaan harus "sudah ditangani" secara eksternal.
edit: Tidak bagus. Akan perlu untuk mengevaluasi kembali. Jika permintaan membutuhkan waktu untuk dijalankan, start_response
tidak akan dipanggil sebelum resp.status
diperiksa.
edit2: Ini adalah versi tetap dengan iterator respons yang diretas:
from functools import wraps
from gunicorn.workers.geventlet import EventletWorker as _EventletWorker
class HackedResponse:
def __init__(self, respiter, resp):
self.respiter = iter(respiter)
self.resp = resp
if hasattr(respiter, "close"):
self.close = respiter.close
def __iter__(self):
return self
def __next__(self):
try:
return next(self.respiter)
except StopIteration:
if not self.resp.status:
self.resp.status = "101" # logger derives status code from status instead of using status_code
self.resp.status_code = 101 # not actually needed since headers_sent/force_close result in status_code not being checked anymore
self.resp.headers_sent = True
self.resp.force_close()
raise
def wsgi_decorator(wsgi):
@wraps(wsgi)
def wrapper(environ, start_response):
respiter = wsgi(environ, start_response)
resp = start_response.__self__
return HackedResponse(respiter, resp)
return wrapper
class EventletWorker(_EventletWorker):
def load_wsgi(self):
super().load_wsgi()
self.wsgi = wsgi_decorator(self.wsgi)
Jelas ini hanya tambalan monyet. Perbaikan sebenarnya berpotensi masuk handle_request
di base_async.py. Kuncinya mungkin untuk (secara tidak langsung) memeriksa apakah start_response
dipanggil setelah mengulangi respiter
, baik dengan memeriksa resp.status
(hanya start_response
yang dipanggil) atau resp.headers_sent
(konfirmasi bahwa kami benar-benar telah menanggapi permintaan tersebut).
@benoitc
@ziddey telah menemukan cara untuk memecahkan masalah.
@ziddey pertanyaan cepat untuk contoh Anda (karena saya tidak menggunakan penjaga).
@benoitc tidak dapat menguji saat ini, tetapi melihat traceback di atas https://github.com/benoitc/gunicorn/issues/1852#issuecomment -697189261 dan https://github.com/benoitc/gunicorn/blob/4ae2a05c37b332773997f90ba7542713b9bf8274/ gunicorn/workers/base_async.py#L107 -L140
Biasanya, is_already_handled
akan mengembalikan True dan hanya akan berakhir di sini.
Namun, karena responsnya terbungkus, metode itu tidak berfungsi. Sebaliknya, eksekusi berlangsung, gagal pada baris 115: resp.close()
mencoba mengirim header, tetapi start_response
tidak pernah dipanggil, jadi tidak ada kode status. Bahkan jika itu terjadi, pada akhirnya tetap saja gagal.
Ini menghasilkan AttributeError yang dinaikkan kembali dan dianggap ditangani oleh handle_error
. Karena permintaan sudah ditangani secara eksternal, tidak ada salahnya di sini selain mencatat spam.
Saya tidak bisa mengatakan terlalu banyak tentang Sentry-- Saya juga tidak menggunakannya.
Namun satu detail: mekanisme yang sudah ditangani saat ini tidak menghasilkan pencatatan akses. Saya kira ini secara teknis masuk akal karena tidak ada cara untuk mengetahui bagaimana itu ditangani secara eksternal. Dalam respons saya yang diretas, saya memaksa kode status ke 101, dengan headers_sent
sebagai True sehingga pawang dapat melanjutkan dan permintaan masih mendapatkan akses yang dicatat.
Memeriksa resp.status
adalah tes definitif untuk menentukan apakah start_response
dipanggil.
@benoitc meninjau kembali ini. Untuk menyimpulkan secara lebih pasti bahwa permintaan sudah ditangani, environ['gunicorn.socket']
malah bisa menjadi semacam proxy untuk objek yang mendasarinya. Dengan begitu, dapat direkam ketika soket diakses secara langsung (misalnya membungkus get_socket()
untuk eventlet), dan digunakan untuk sesuatu seperti is_already_handled
Itu masih membutuhkan peretasan status respons meskipun jika akses logging diinginkan.
Komentar yang paling membantu
Bump @benoitc