Flask-SocketIO - WebSocket 在建立连接之前关闭。 [英雄联盟]

Flask-SocketIO - WebSocket is closed before the connection is established. [Heroku]

虽然 运行 Flask 在我的本地服务器上一切正常,但是在移动到 Heroku 之后我开始收到“WebSocket 在建立连接之前关闭”。 在我的本地服务器上,websocket 连接了一次,但在 heroku 上它一直连接到 websocket。

Heroku 日志

2021-10-26T08:23:02.637338+00:00 app[web.1]: [2021-10-26 08:23:02 +0000] [4] [CRITICAL] WORKER TIMEOUT (pid:43)
2021-10-26T08:23:02.637839+00:00 app[web.1]: [2021-10-26 08:23:02 +0000] [43] [INFO] Worker exiting (pid: 43)
2021-10-26T08:23:02.729197+00:00 app[web.1]: [2021-10-26 08:23:02 +0000] [45] [INFO] Booting worker with pid: 45
2021-10-26T08:23:03.105363+00:00 heroku[router]: at=info method=GET path="/socket.io/?EIO=4&transport=websocket" host=chatroom-private.herokuapp.com request_id=c6a836a6-5fbd-4e6b-8d21-bd97d72513e4 fwd="103.86.182.226" dyno=web.1 connect=0ms service=15569ms status=101 bytes=129 protocol=https
2021-10-26T08:23:03.105683+00:00 app[web.1]: Traceback (most recent call last):
2021-10-26T08:23:03.105716+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/gevent/pywsgi.py", line 999, in handle_one_response
2021-10-26T08:23:03.105717+00:00 app[web.1]:     self.run_application()
2021-10-26T08:23:03.105719+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/geventwebsocket/handler.py", line 75, in run_application
2021-10-26T08:23:03.105719+00:00 app[web.1]:     self.run_websocket()
2021-10-26T08:23:03.105728+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/geventwebsocket/handler.py", line 52, in run_websocket
2021-10-26T08:23:03.105729+00:00 app[web.1]:     list(self.application(self.environ, lambda s, h, e=None: []))
2021-10-26T08:23:03.105731+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 2091, in __call__
2021-10-26T08:23:03.105731+00:00 app[web.1]:     return self.wsgi_app(environ, start_response)
2021-10-26T08:23:03.105742+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask_socketio/__init__.py", line 43, in __call__
2021-10-26T08:23:03.105742+00:00 app[web.1]:     return super(_SocketIOMiddleware, self).__call__(environ,
2021-10-26T08:23:03.105749+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/middleware.py", line 63, in __call__
2021-10-26T08:23:03.105750+00:00 app[web.1]:     return self.engineio_app.handle_request(environ, start_response)
2021-10-26T08:23:03.105757+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/socketio/server.py", line 589, in handle_request
2021-10-26T08:23:03.105758+00:00 app[web.1]:     return self.eio.handle_request(environ, start_response)
2021-10-26T08:23:03.105760+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/server.py", line 379, in handle_request
2021-10-26T08:23:03.105760+00:00 app[web.1]:     r = self._handle_connect(environ, start_response,
2021-10-26T08:23:03.105769+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/server.py", line 554, in _handle_connect
2021-10-26T08:23:03.105769+00:00 app[web.1]:     ret = s.handle_get_request(environ, start_response)
2021-10-26T08:23:03.105776+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/socket.py", line 103, in handle_get_request
2021-10-26T08:23:03.105776+00:00 app[web.1]:     return getattr(self, '_upgrade_' + transport)(environ,
2021-10-26T08:23:03.105783+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/socket.py", line 158, in _upgrade_websocket
2021-10-26T08:23:03.105783+00:00 app[web.1]:     return ws(environ, start_response)
2021-10-26T08:23:03.105790+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/async_drivers/eventlet.py", line 16, in __call__
2021-10-26T08:23:03.105791+00:00 app[web.1]:     raise RuntimeError('You need to use the eventlet server. '
2021-10-26T08:23:03.105807+00:00 app[web.1]: RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.
2021-10-26T08:23:03.105880+00:00 app[web.1]: 2021-10-26T08:23:03Z {'REMOTE_ADDR': '10.1.95.245', 'REMOTE_PORT': '29183', 'HTTP_HOST': 'chatroom-private.herokuapp.com', (hidden keys: 39)} failed with RuntimeError
2021-10-26T08:23:03.105881+00:00 app[web.1]: 
2021-10-26T08:23:03.515546+00:00 heroku[router]: at=info method=GET path="/socket.io/?EIO=4&transport=websocket" host=chatroom-private.herokuapp.com request_id=27a8989f-fda3-46c1-9e5f-c9e3a5354c49 fwd="103.86.182.226" dyno=web.1 connect=0ms service=1ms status=101 bytes=129 protocol=https
2021-10-26T08:23:03.512553+00:00 app[web.1]: Traceback (most recent call last):
2021-10-26T08:23:03.512578+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/gevent/pywsgi.py", line 999, in handle_one_response
2021-10-26T08:23:03.512579+00:00 app[web.1]:     self.run_application()
2021-10-26T08:23:03.512581+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/geventwebsocket/handler.py", line 75, in run_application
2021-10-26T08:23:03.512581+00:00 app[web.1]:     self.run_websocket()
2021-10-26T08:23:03.512590+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/geventwebsocket/handler.py", line 52, in run_websocket
2021-10-26T08:23:03.512590+00:00 app[web.1]:     list(self.application(self.environ, lambda s, h, e=None: []))
2021-10-26T08:23:03.512592+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask/app.py", line 2091, in __call__
2021-10-26T08:23:03.512593+00:00 app[web.1]:     return self.wsgi_app(environ, start_response)
2021-10-26T08:23:03.512613+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/flask_socketio/__init__.py", line 43, in __call__
2021-10-26T08:23:03.512613+00:00 app[web.1]:     return super(_SocketIOMiddleware, self).__call__(environ,
2021-10-26T08:23:03.512620+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/middleware.py", line 63, in __call__
2021-10-26T08:23:03.512620+00:00 app[web.1]:     return self.engineio_app.handle_request(environ, start_response)
2021-10-26T08:23:03.512628+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/socketio/server.py", line 589, in handle_request
2021-10-26T08:23:03.512628+00:00 app[web.1]:     return self.eio.handle_request(environ, start_response)
2021-10-26T08:23:03.512630+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/server.py", line 379, in handle_request
2021-10-26T08:23:03.512630+00:00 app[web.1]:     r = self._handle_connect(environ, start_response,
2021-10-26T08:23:03.512639+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/server.py", line 554, in _handle_connect
2021-10-26T08:23:03.512639+00:00 app[web.1]:     ret = s.handle_get_request(environ, start_response)
2021-10-26T08:23:03.512646+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/socket.py", line 103, in handle_get_request
2021-10-26T08:23:03.512646+00:00 app[web.1]:     return getattr(self, '_upgrade_' + transport)(environ,
2021-10-26T08:23:03.512653+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/socket.py", line 158, in _upgrade_websocket
2021-10-26T08:23:03.512653+00:00 app[web.1]:     return ws(environ, start_response)
2021-10-26T08:23:03.512660+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/engineio/async_drivers/eventlet.py", line 16, in __call__
2021-10-26T08:23:03.512661+00:00 app[web.1]:     raise RuntimeError('You need to use the eventlet server. '
2021-10-26T08:23:03.512675+00:00 app[web.1]: RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.
2021-10-26T08:23:03.512745+00:00 app[web.1]: 2021-10-26T08:23:03Z {'REMOTE_ADDR': '10.1.23.225', 'REMOTE_PORT': '13370', 'HTTP_HOST': 'chatroom-private.herokuapp.com', (hidden keys: 39)} failed with RuntimeError
2021-10-26T08:23:03.512746+00:00 app[web.1]: 
2021-10-26T08:23:03.513320+00:00 app[web.1]: Traceback (most recent call last):
2021-10-26T08:23:03.513345+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/eventlet/hubs/hub.py", line 476, in fire_timers
2021-10-26T08:23:03.513345+00:00 app[web.1]:     timer()
2021-10-26T08:23:03.513352+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/eventlet/hubs/timer.py", line 59, in __call__
2021-10-26T08:23:03.513353+00:00 app[web.1]:     cb(*args, **kw)
2021-10-26T08:23:03.513361+00:00 app[web.1]:   File "/app/.heroku/python/lib/python3.9/site-packages/eventlet/semaphore.py", line 152, in _do_acquire
2021-10-26T08:23:03.513362+00:00 app[web.1]:     waiter.switch()
2021-10-26T08:23:03.513363+00:00 app[web.1]:   File "src/gevent/greenlet.py", line 910, in gevent._gevent_cgreenlet.Greenlet.run
2021-10-26T08:23:03.513373+00:00 app[web.1]:   File "src/gevent/greenlet.py", line 875, in gevent._gevent_cgreenlet.Greenlet._Greenlet__report_result
2021-10-26T08:23:03.513381+00:00 app[web.1]:   File "src/gevent/_gevent_cgreenlet.pxd", line 45, in gevent._gevent_cgreenlet.get_my_hub
2021-10-26T08:23:03.513393+00:00 app[web.1]: TypeError: Cannot convert greenlet.greenlet to gevent._gevent_c_greenlet_primitives.SwitchOutGreenletWithLoop

Python 服务器

app = Flask(__name__,instance_relative_config=False)
CORS(app)
app.config.from_object('config.Config')
app.config['CORS_HEADERS'] = 'Content-Type'
socketio = SocketIO(app,cors_allowed_origins='*')

@app.route("/",methods=['GET', 'POST'])
def index():
  return render_template('index.html',)

@socketio.on('message')
def handleMessage(msg):
    print('Message: ' + msg)
    send(msg, broadcast=True)

if __name__ == '__main__':
    port = int(os.environ.get('PORT', 5000))
    socketio.run(app,port=port)  

客户端

<script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/4.3.2/socket.io.js" crossorigin="anonymous">
var connectionOptions =  {
            "force new connection" : true,
            "reconnectionAttempts": "Infinity", //avoid having user reconnect manually in order to prevent dead clients after a server restart
            "timeout" : 10000, //before connect_error and connect_timeout are emitted.
            "transports" : ["websocket"]
        };
$(document).ready(function() {
    const socket = io(connectionOptions);

Procfile

web: gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app

你看到差异了吗?你是 运行 Gunicorn with the gevent-websocket worker,但是 Flask-SocketIO 认为它被配置为使用 eventlet:

RuntimeError: You need to use the eventlet server. See the Deployment section of the documentation for more information.

您想使用哪一个?如果你想要 gevent,那么将 async_mode='gevent' 添加到你的 SocketIO() 对象,以覆盖默认值。或者作为替代方案,请确保您的 virtualenv 中未安装 eventlet,然后 Flask-SocketIO 将使用下一个选项,即 gevent。