【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https

简介: 【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https

问题描述

在上篇博文“【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)”中,实现了通过 HTTP 方式访问部署在Azure App Service For Linux上的Python Flask Web Socket项目, 但是当使用HTTPS访问时候,socket.io所发送的GET请求都能正常。

HTTP 成功 HTTPS 失败

 

 

但是POST请求全部返回400 Bad Request

 

 

那么,如何来解决并实现HTTPS呢?

 

问题解决

使用 eventlet.monkey_patch() : 猴子补丁,在运行时动态修改已有的代码,而不需要修改原始代码。对应 “模块运行时替换的功能” 来进行理解。

  1. Monkey patch就是在运行时对已有的代码进行修改,达到hot patch的目的。
  2. Eventlet中大量使用了该技巧,以替换标准库中的组件,比如socket。

在创建socketio对象时候,指定使用eventlet模块,然后设置cors_allowed_origins为*。

socketio = SocketIO(app, async_mode="eventlet",cors_allowed_origins='*')

一个简单的Socket Test项目(服务端+客户端)实例代码如下:

pythonsockettest(Folder Name)
----templates
--------index.html
----app.py
----requirements.txt

Templates/Index.html

<!DOCTYPE HTML>
<html>
<head>
    <title>Socket-Test</title>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/socket.io/3.1.3/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {
            namespace = '/test';
            var socket = io(namespace);
            socket.on('connect', function() {
                socket.emit('my_event', {data: 'connected to the SocketServer...'});
            });
            socket.on('my_response', function(msg, cb) {
                $('#log').append('<br>' + $('<div/>').text('logs #' + msg.count + ': ' + msg.data).html());
                if (cb)
                    cb();
            });
            $('form#emit').submit(function(event) {
                socket.emit('my_event', {data: $('#emit_data').val()});
                return false;
            });
            $('form#broadcast').submit(function(event) {
                socket.emit('my_broadcast_event', {data: $('#broadcast_data').val()});
                return false;
            });
            $('form#disconnect').submit(function(event) {
                socket.emit('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>

 

app.py

import eventlet
eventlet.monkey_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__)
app.config['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 = os.environ["PORT"] 
serverIP = "0.0.0.0"
# # Used by Local debug.
# PORT = 5000 
# serverIP = "127.0.0.1"
@app.route('/')
def index():
    return render_template('index.html', async_mode=socketio.async_mode)
@socketio.on('my_event', namespace='/test')
def test_message(message):
    print('receive message:' + message['data'],)
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {'data': message['data'], 'count': session['receive_count']})
@socketio.on('my_broadcast_event', namespace='/test')
def test_broadcast_message(message):
    print('broadcast message:' + message['data'],)
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {'data': message['data'], 'count': session['receive_count']},
         broadcast=True)
@socketio.on('disconnect_request', namespace='/test')
def disconnect_request():
    @copy_current_request_context
    def can_disconnect():
        disconnect()
    session['receive_count'] = session.get('receive_count', 0) + 1
    emit('my_response',
         {'data': 'Disconnected!', 'count': session['receive_count']},
         callback=can_disconnect)
if __name__ == '__main__':
    socketio.run(app,port=PORT, host=serverIP, debug=True)
    print('socket io start')

 

requirements.txt

Flask==2.0.2

Flask-SocketIO==5.1.1

eventlet==0.30.2

 

部署在Azure App Service后,需要设置启动命令:

gunicorn --bind=0.0.0.0 --timeout 600 --worker-class "eventlet" app:app

配置方法可见:https://www.cnblogs.com/lulight/p/15501015.html (第五段:修改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 0.24.1 or higher

2021-12-14T03:31:30.581051185Z Error: class uri 'eventlet' invalid or not found: 
2021-12-14T03:31:30.581056185Z 
2021-12-14T03:31:30.581059786Z [Traceback (most recent call last):
2021-12-14T03:31:30.581063086Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/geventlet.py", line 10, in <module>
2021-12-14T03:31:30.581067386Z     import eventlet
2021-12-14T03:31:30.581070686Z ModuleNotFoundError: No module named 'eventlet'
2021-12-14T03:31:30.581073986Z 
2021-12-14T03:31:30.581077187Z During handling of the above exception, another exception occurred:
2021-12-14T03:31:30.581081587Z 
2021-12-14T03:31:30.581084787Z Traceback (most recent call last):
2021-12-14T03:31:30.581088187Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/util.py", line 99, in load_class
2021-12-14T03:31:30.581107988Z     mod = importlib.import_module('.'.join(components))
2021-12-14T03:31:30.581111389Z   File "/opt/python/3.7.9/lib/python3.7/importlib/__init__.py", line 127, in import_module
2021-12-14T03:31:30.581114489Z     return _bootstrap._gcd_import(name[level:], package, level)
2021-12-14T03:31:30.581117589Z   File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
2021-12-14T03:31:30.581120689Z   File "<frozen importlib._bootstrap>", line 983, in _find_and_load
2021-12-14T03:31:30.581123789Z   File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
2021-12-14T03:31:30.581126890Z   File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
2021-12-14T03:31:30.581130090Z   File "<frozen importlib._bootstrap_external>", line 728, in exec_module
2021-12-14T03:31:30.581133190Z   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
2021-12-14T03:31:30.581136290Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/geventlet.py", line 12, in <module>
2021-12-14T03:31:30.581139490Z     raise RuntimeError("eventlet worker requires eventlet 0.24.1 or higher")
2021-12-14T03:31:30.581142690Z RuntimeError: eventlet worker requires eventlet 0.24.1 or higher

2:cannot import name 'ALREADY_HANDLED' from 'eventlet.wsgi'

2021-12-14T05:14:28.142182566Z Error: class uri 'eventlet' invalid or not found: 
2021-12-14T05:14:28.142189566Z 
2021-12-14T05:14:28.142194867Z [Traceback (most recent call last):
2021-12-14T05:14:28.142199767Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/util.py", line 99, in load_class
2021-12-14T05:14:28.142212168Z     mod = importlib.import_module('.'.join(components))
2021-12-14T05:14:28.142217368Z   File "/opt/python/3.7.9/lib/python3.7/importlib/__init__.py", line 127, in import_module
2021-12-14T05:14:28.142222569Z     return _bootstrap._gcd_import(name[level:], package, level)
2021-12-14T05:14:28.142239170Z   File "<frozen importlib._bootstrap>", line 1006, in _gcd_import
2021-12-14T05:14:28.142244870Z   File "<frozen importlib._bootstrap>", line 983, in _find_and_load
2021-12-14T05:14:28.142249471Z   File "<frozen importlib._bootstrap>", line 967, in _find_and_load_unlocked
2021-12-14T05:14:28.142254171Z   File "<frozen importlib._bootstrap>", line 677, in _load_unlocked
2021-12-14T05:14:28.142258771Z   File "<frozen importlib._bootstrap_external>", line 728, in exec_module
2021-12-14T05:14:28.142263371Z   File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
2021-12-14T05:14:28.142268172Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/geventlet.py", line 20, in <module>
2021-12-14T05:14:28.142272972Z     from eventlet.wsgi import ALREADY_HANDLED as EVENTLET_ALREADY_HANDLED
2021-12-14T05:14:28.142277472Z ImportError: cannot import name 'ALREADY_HANDLED' from 'eventlet.wsgi' (/tmp/8d9bebfefe8421c/antenv/lib/python3.7/site-packages/eventlet/wsgi.py)
2021-12-14T05:14:28.142282173Z ]

以上两个问题都是通过修改 requirements.txt 中  eventlet==0.30.2 后,重新部署。问题解决。

 

参考资料

App Service for Linux 中实现 WebSocket 功能 (Python SocketIO) : https://www.cnblogs.com/lulight/p/15501015.html

 

相关文章
|
8月前
|
存储 安全 Linux
【Azure App Service】在App Service中查看CA证书
在 Azure App Service 中,使用自签名或私有 CA 证书的远程服务可能会导致 SSL 握手失败。解决方法包括使用受信任 CA 签发的证书,或通过 App Service Environment 加载自定义根证书,实现安全连接。
187 4
|
6月前
|
Java 应用服务中间件 API
【App Service】部署War包到Azure云上遇404错误
Java应用部署至Azure App Service for Windows后报404,本地运行正常。经排查,日志提示类文件版本不兼容:应用由Java 17(class file version 61.0)编译,但环境仅支持到Java 11(55.0)。错误根源为Java版本不匹配。调整App Service的Java版本至17后问题解决,成功访问接口。
568 3
|
7月前
|
异构计算 Python
ERROR: pip’s dependency resolver does not currently take into 报错-Python项目依赖冲突的解决方案-优雅草优雅草卓伊凡
ERROR: pip’s dependency resolver does not currently take into 报错-Python项目依赖冲突的解决方案-优雅草优雅草卓伊凡
548 1
|
6月前
|
存储 Linux 网络安全
【Azure App Service】Root CA on App Service
Azure App Service for Windows应用连接外部SSL服务时,需确保其证书由受信任的根CA颁发。多租户环境下无法修改根证书,但ASE(单租户)可加载自定义CA证书。若遇证书信任问题,可更换为公共CA证书或将应用部署于ASE并导入私有CA证书。通过Kudu的PowerShell(Windows)或SSH(Linux)可查看当前受信任的根证书列表。
150 13
|
7月前
|
网络协议 Java Linux
【App Service】在Azure环境中如何查看App Service实例当前的网络连接情况呢?
在 Azure App Service(Windows 和 Linux)中部署应用时,分析网络连接状态是排查异常、验证端口监听及确认后端连接的关键。本文介绍如何在 Linux 环境中使用 `netstat` 命令查看特定端口(如 443、3306、6380)的连接情况,并解析输出结果。同时说明在 Windows App Service 中 `netstat` 被禁用的情况下,如何通过门户抓包等替代方法进行网络诊断。内容涵盖命令示例、操作步骤及附录说明,帮助开发者快速掌握云环境中的网络分析技巧。
190 11
|
7月前
|
API 语音技术 开发者
Python 项目打包,并上传到 PyPI,分享项目
本文介绍了如何使用 Poetry 打包并发布一个 Python 项目至 PyPI。内容包括:项目创建、配置 `pyproject.toml` 文件、构建软件包、上传至 PyPI、安装与使用。通过实例 iGTTS 展示了从开发到发布的完整流程,帮助开发者快速分享自己的 Python 工具。
|
8月前
|
网络协议 关系型数据库 Linux
【App Service Linux】在Linux App Service中安装 tcpdump 并抓取网络包
在App Service for Linux环境中,无法像Windows一样直接使用网络排查工具抓包。本文介绍了如何通过TCPDUMP在Linux环境下抓取网络包,包括SSH进入容器、安装tcpdump、执行抓包命令及下载分析文件的完整操作步骤。
406 5
|
7月前
|
人工智能 Shell Python
ERROR: pip’s dependency resolver does not currently take into 报错-Python项目依赖冲突的解决方案-优雅草优雅草卓伊凡
ERROR: pip’s dependency resolver does not currently take into 报错-Python项目依赖冲突的解决方案-优雅草优雅草卓伊凡
303 0
|
JSON 前端开发 API
使用Python和Flask构建简易Web API
使用Python和Flask构建简易Web API
970 86

推荐镜像

更多
下一篇
开通oss服务