【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

 

相关文章
|
1月前
|
应用服务中间件 Linux nginx
【Azure App Service】基于Linux创建的App Service是否可以主动升级内置的Nginx版本呢?
基于Linux创建的App Service是否可以主动升级内置的Nginx版本呢?Web App Linux 默认使用的 Nginx 版本是由平台预定义的,无法更改这个版本。
137 77
|
5月前
|
Python
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
使用Python的socket库实现客户端到服务器端的图片传输,包括客户端和服务器端的代码实现,以及传输结果的展示。
232 3
Socket学习笔记(二):python通过socket实现客户端到服务器端的图片传输
|
5月前
|
JSON 数据格式 Python
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
本文介绍了如何使用Python的socket模块实现客户端到服务器端的文件传输,包括客户端发送文件信息和内容,服务器端接收并保存文件的完整过程。
271 1
Socket学习笔记(一):python通过socket实现客户端到服务器端的文件传输
|
4月前
【Azure App Service】PowerShell脚本批量添加IP地址到Web App允许访问IP列表中
Web App取消公网访问后,只允许特定IP能访问Web App。需要写一下段PowerShell脚本,批量添加IP到Web App的允许访问IP列表里!
|
4月前
|
Kubernetes 网络协议 Python
Python网络编程:从Socket到Web应用
在信息时代,网络编程是软件开发的重要组成部分。Python作为多用途编程语言,提供了从Socket编程到Web应用开发的强大支持。本文将从基础的Socket编程入手,逐步深入到复杂的Web应用开发,涵盖Flask、Django等框架的应用,以及异步Web编程和微服务架构。通过本文,读者将全面了解Python在网络编程领域的应用。
76 1
|
5月前
|
消息中间件 监控 网络协议
Python中的Socket魔法:如何利用socket模块构建强大的网络通信
本文介绍了Python的`socket`模块,讲解了其基本概念、语法和使用方法。通过简单的TCP服务器和客户端示例,展示了如何创建、绑定、监听、接受连接及发送/接收数据。进一步探讨了多用户聊天室的实现,并介绍了非阻塞IO和多路复用技术以提高并发处理能力。最后,讨论了`socket`模块在现代网络编程中的应用及其与其他通信方式的关系。
496 3
|
5月前
|
安全 网络协议 网络安全
Python Socket编程大揭秘:从菜鸟到黑客的进阶之路,你准备好了吗?
【10月更文挑战第4天】在编程领域,Python Socket编程犹如一把开启网络世界的钥匙,带领开发者从简单数据传输迈向复杂应用构建。本文将引导你从零开始,逐步掌握Socket编程的核心技巧,包括基本概念、TCP服务器与客户端的搭建、并发处理及异常管理、SSL/TLS加密通信,直至深入了解网络协议与安全漏洞。通过实战演练与理论学习,助你成为驾驭网络世界的高手。
60 1
|
5月前
|
存储 网络协议 Linux
聊一聊 Python 的 socket,以及 select、poll、epoll 又是怎么一回事?
聊一聊 Python 的 socket,以及 select、poll、epoll 又是怎么一回事?
274 2
|
6月前
|
网络协议 Python
告别网络编程迷雾!Python Socket编程基础与实战,让你秒变网络达人!
在网络编程的世界里,Socket编程是连接数据与服务的关键桥梁。对于初学者,这往往是最棘手的部分。本文将用Python带你轻松入门Socket编程,从创建TCP服务器与客户端的基础搭建,到处理并发连接的实战技巧,逐步揭开网络编程的神秘面纱。通过具体的代码示例,我们将掌握Socket的基本概念与操作,让你成为网络编程的高手。无论是简单的数据传输还是复杂的并发处理,Python都能助你一臂之力。希望这篇文章成为你网络编程旅程的良好开端。
84 3
|
6月前
|
网络协议 Python
网络世界的建筑师:Python Socket编程基础与进阶,构建你的网络帝国!
在数字宇宙中,网络如同复杂脉络连接每个角落,Python Socket编程则是开启这一世界的钥匙。本文将引导你从基础概念入手,逐步掌握Socket编程,并通过实战示例构建TCP/UDP服务器与客户端。你将学会使用Python的socket模块进行网络通信,了解TCP与UDP的区别,并运用多线程与异步IO提升服务器性能。跟随本文指引,成为网络世界的建筑师,构建自己的网络帝国。
52 2