【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Http
时间:2021-12-16 作者:lulight
问题描述
在上篇博文“【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)”中,实现了通过 HTTP 方式访问部署在Azure App Service For Linux上的Python Flask Web Socket项目, 但是当使用HTTPS访问时候,域名所发送的GET请求都能正常。
HTTP 成功 | HTTPS 失败 |
|
但是POST请求全部返回400 Bad Request
那么,如何来解决并实现HTTPS呢?
问题解决
使用 域名ey_patch() : 猴子补丁,在运行时动态修改已有的代码,而不需要修改原始代码。对应 “模块运行时替换的功能” 来进行理解。
- Monkey patch就是在运行时对已有的代码进行修改,达到hot patch的目的。
- Eventlet中大量使用了该技巧,以替换标准库中的组件,比如socket。
在创建socketio对象时候,指定使用eventlet模块,然后设置cors_allowed_origins为*。
socketio = SocketIO(app, async_mode="eventlet",cors_allowed_origins=\'*\')
一个简单的Socket Test项目(服务端+客户端)实例代码如下:
pythonsockettest(Folder Name) ----templates --------域名 ----域名 ----域名
Templates/域名
<!DOCTYPE HTML> <html> <head> <title>Socket-Test</title> <script src="https://域名/ajax/libs/jquery/3.4.1/域名"></script> <script src="https://域名/ajax/libs/域名/3.1.3/域名.js"></script> <script type="text/javascript" charset="utf-8"> $(document).ready(function() { namespace = \'/test\'; var socket = io(namespace); 域名(\'connect\', function() { 域名(\'my_event\', {data: \'connected to the SocketServer...\'}); }); 域名(\'my_response\', function(msg, cb) { $(\'#log\').append(\'<br>\' + $(\'<div/>\').text(\'logs #\' + 域名t + \': \' + 域名).html()); if (cb) cb(); }); $(\'form#emit\').submit(function(event) { 域名(\'my_event\', {data: $(\'#emit_data\').val()}); return false; }); $(\'form#broadcast\').submit(function(event) { 域名(\'my_broadcast_event\', {data: $(\'#broadcast_data\').val()}); return false; }); $(\'form#disconnect\').submit(function(event) { 域名(\'disconnect_request\'); return false; }); }); </script> </head> <body style="background-color:white;"> <h1 style="background-color:white;">Socket</h1> <form id="emit" method="POST" action=\'#\'> <input type="text" name="emit_data" id="emit_data" placeholder="Message"> <input type="submit" value="Send Message"> </form> <form id="broadcast" method="POST" action=\'#\'> <input type="text" name="broadcast_data" id="broadcast_data" placeholder="Message"> <input type="submit" value="Send Broadcast Message"> </form> <form id="disconnect" method="POST" action="#"> <input type="submit" value="Disconnect Server"> </form> <h2 style="background-color:white;">Logs</h2> <div id="log" ></div> </body> </html>
域名
import eventlet 域名ey_patch() from flask import Flask, render_template, session, copy_current_request_context from flask_socketio import SocketIO, emit, disconnect from threading import Lock import os async_mode = None app = Flask(__name__) 域名ig[\'SECRET_KEY\'] = \'secret!\' ## For http #socketio = SocketIO(app, async_mode=async_mode) ## For https socketio = SocketIO(app, async_mode="eventlet",cors_allowed_origins=\'*\') thread = None thread_lock = Lock() ## Used by App Service For linux PORT = 域名ron["PORT"] serverIP = "0.0.0.0" # # Used by Local debug. # PORT = 5000 # serverIP = "127.0.0.1" @域名e(\'/\') def index(): return render_template(\'域名\', async_mode=域名c_mode) @域名(\'my_event\', namespace=\'/test\') def test_message(message): print(\'receive message:\' + message[\'data\'],) session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1 emit(\'my_response\', {\'data\': message[\'data\'], \'count\': session[\'receive_count\']}) @域名(\'my_broadcast_event\', namespace=\'/test\') def test_broadcast_message(message): print(\'broadcast message:\' + message[\'data\'],) session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1 emit(\'my_response\', {\'data\': message[\'data\'], \'count\': session[\'receive_count\']}, broadcast=True) @域名(\'disconnect_request\', namespace=\'/test\') def disconnect_request(): @copy_current_request_context def can_disconnect(): disconnect() session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1 emit(\'my_response\', {\'data\': \'Disconnected!\', \'count\': session[\'receive_count\']}, callback=can_disconnect) if __name__ == \'__main__\': 域名(app,port=PORT, host=serverIP, debug=True) print(\'socket io start\')
域名
Flask==2.0.2 Flask-SocketIO==5.1.1 eventlet==域名.2
部署在Azure App Service后,需要设置启动命令:
gunicorn --bind=0.0.0.0 --timeout 600 --worker-class "eventlet" app:app
配置方法可见:https://域名/lulight/p/域名 (第五段:修改App Service的启动命令)
附录:部署上Azure App的代码
#设置登录环境为中国区Azure az cloud set -n AzureChinaCloud az login #部署代码,如果pythonlinuxwebsocket01不存在,则自动创建定价层位B1的App Service az webapp up --sku B1 --name pythonlinuxwebsocket01
测试效果
遇见的问题
1: eventlet worker requires eventlet 域名.1 or higher
2021-12-14T03:31:域名51185Z Error: class uri \'eventlet\' invalid or not found: 2021-12-14T03:31:域名56185Z 2021-12-14T03:31:域名59786Z [Traceback (most recent call last): 2021-12-14T03:31:域名63086Z File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 10, in <module> 2021-12-14T03:31:域名67386Z import eventlet 2021-12-14T03:31:域名70686Z ModuleNotFoundError: No module named \'eventlet\' 2021-12-14T03:31:域名73986Z 2021-12-14T03:31:域名77187Z During handling of the above exception, another exception occurred: 2021-12-14T03:31:域名81587Z 2021-12-14T03:31:域名84787Z Traceback (most recent call last): 2021-12-14T03:31:域名88187Z File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/域名", line 99, in load_class 2021-12-14T03:31:域名07988Z mod = 域名rt_module(\'.\'.join(components)) 2021-12-14T03:31:域名11389Z File "/opt/python/3.7.9/lib/python3.7/importlib/域名", line 127, in import_module 2021-12-14T03:31:域名14489Z return 域名_import(name[level:], package, level) 2021-12-14T03:31:域名17589Z File "<frozen 域名tstrap>", line 1006, in _gcd_import 2021-12-14T03:31:域名20689Z File "<frozen 域名tstrap>", line 983, in _find_and_load 2021-12-14T03:31:域名23789Z File "<frozen 域名tstrap>", line 967, in _find_and_load_unlocked 2021-12-14T03:31:域名26890Z File "<frozen 域名tstrap>", line 677, in _load_unlocked 2021-12-14T03:31:域名30090Z File "<frozen 域名tstrap_external>", line 728, in exec_module 2021-12-14T03:31:域名33190Z File "<frozen 域名tstrap>", line 219, in _call_with_frames_removed 2021-12-14T03:31:域名36290Z File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 12, in <module> 2021-12-14T03:31:域名39490Z raise RuntimeError("eventlet worker requires eventlet 域名.1 or higher") 2021-12-14T03:31:域名42690Z RuntimeError: eventlet worker requires eventlet 域名.1 or higher
2:cannot import name \'ALREADY_HANDLED\' from \'域名\'
2021-12-14T05:14:域名82566Z Error: class uri \'eventlet\' invalid or not found: 2021-12-14T05:14:域名89566Z 2021-12-14T05:14:域名94867Z [Traceback (most recent call last): 2021-12-14T05:14:域名99767Z File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/域名", line 99, in load_class 2021-12-14T05:14:域名12168Z mod = 域名rt_module(\'.\'.join(components)) 2021-12-14T05:14:域名17368Z File "/opt/python/3.7.9/lib/python3.7/importlib/域名", line 127, in import_module 2021-12-14T05:14:域名22569Z return 域名_import(name[level:], package, level) 2021-12-14T05:14:域名39170Z File "<frozen 域名tstrap>", line 1006, in _gcd_import 2021-12-14T05:14:域名44870Z File "<frozen 域名tstrap>", line 983, in _find_and_load 2021-12-14T05:14:域名49471Z File "<frozen 域名tstrap>", line 967, in _find_and_load_unlocked 2021-12-14T05:14:域名54171Z File "<frozen 域名tstrap>", line 677, in _load_unlocked 2021-12-14T05:14:域名58771Z File "<frozen 域名tstrap_external>", line 728, in exec_module 2021-12-14T05:14:域名63371Z File "<frozen 域名tstrap>", line 219, in _call_with_frames_removed 2021-12-14T05:14:域名68172Z File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 20, in <module> 2021-12-14T05:14:域名72972Z from 域名 import ALREADY_HANDLED as EVENTLET_ALREADY_HANDLED 2021-12-14T05:14:域名77472Z ImportError: cannot import name \'ALREADY_HANDLED\' from \'域名\' (/tmp/8d9bebfefe8421c/antenv/lib/python3.7/site-packages/eventlet/域名) 2021-12-14T05:14:域名82173Z ]
以上两个问题都是通过修改 域名 中 eventlet==域名.2 后,重新部署。问题解决。
参考资料
App Service for Linux 中实现 WebSocket 功能 (Python SocketIO) : https://域名/lulight/p/域名