【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天前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
83 1
|
1月前
|
JavaScript Java 微服务
现代化 Java Web 在线商城项目技术方案与实战开发流程及核心功能实现详解
本项目基于Spring Boot 3与Vue 3构建现代化在线商城系统,采用微服务架构,整合Spring Cloud、Redis、MySQL等技术,涵盖用户认证、商品管理、购物车功能,并支持Docker容器化部署与Kubernetes编排。提供完整CI/CD流程,助力高效开发与扩展。
305 63
|
2月前
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
145 0
|
4月前
|
人工智能 安全 程序员
用 Colab 和 ngrok 免费部署你的 Web UI 项目,随时随地访问!
用 Colab 和 ngrok 免费部署你的 Web UI 项目,随时随地访问!
|
10月前
|
JSON 前端开发 API
使用Python和Flask构建简易Web API
使用Python和Flask构建简易Web API
573 3
|
10月前
|
JSON API 数据格式
使用Python和Flask构建简单的Web API
使用Python和Flask构建简单的Web API
|
10月前
|
开发框架 前端开发 JavaScript
利用Python和Flask构建轻量级Web应用的实战指南
利用Python和Flask构建轻量级Web应用的实战指南
608 2
|
10月前
|
JSON API 数据格式
如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架
本文介绍了如何使用Python和Flask构建一个简单的RESTful API。Flask是一个轻量级的Web框架,适合小型项目和微服务。文章从环境准备、创建基本Flask应用、定义资源和路由、请求和响应处理、错误处理等方面进行了详细说明,并提供了示例代码。通过这些步骤,读者可以快速上手构建自己的RESTful API。
602 2
|
10月前
|
JSON API 数据格式
构建RESTful APIs:使用Python和Flask
构建RESTful APIs:使用Python和Flask
111 1
|
11月前
|
JSON API 数据格式
构建RESTful APIs:使用Python和Flask
【10月更文挑战第12天】本文介绍了如何使用Python和Flask构建一个简单的RESTful API。首先概述了API的重要性及RESTful API的基本概念,接着详细讲解了Flask框架的特性和安装方法。通过创建一个基本的Flask应用,定义了处理“图书”资源的GET、POST、PUT和DELETE方法的路由,展示了如何处理请求和响应,以及如何进行错误处理。最后,提供了运行和测试API的方法,总结了Flask在构建RESTful API方面的优势。
96 1

热门文章

最新文章