【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() : 猴子补丁,在运行时动态修改已有的代码,而不需要修改原始代码。对应 “模块运行时替换的功能” 来进行理解。
- 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 --------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
【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https的更多相关文章
- 【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志
问题描述 在App Service For Windows的环境中,我们可以通过ArmClient 工具发送POST请求在Web应用的实例中抓取网络日志,但是在App Service For Linu ...
- 【Azure 应用服务】App Service For Linux 部署PHP Laravel 项目,如何修改首页路径为 wwwroot\public\index.php
问题描述 参考官方文档部署 PHP Laravel 项目到App Service for Linux环境中,但是访问应用时候遇见了500 Server Error 错误. 从部署的日志中,可以明确看出 ...
- 【Azure 应用服务】App Service For Linux 部署Java Spring Boot应用后,查看日志文件时的疑惑
编写Java Spring Boot应用,通过配置logging.path路径把日志输出在指定的文件夹中. 第一步:通过VS Code创建一个空的Spring Boot项目 第二步:在applicat ...
- 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)
问题描述 使用 python websockets 模块作为Socket的服务端,发布到App Service for Linux环境后,发现Docker Container无法启动.错误消息为: 2 ...
- 【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法
问题定义 使用Azure应用服务(App Service),部署Java应用,使用Tomcat容器,如何自定义错误页面呢?同时禁用DELETE, PUT方法 解决办法 如何自定义错误页面呢?需要在 J ...
- 【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)
问题描述 在Azure App Service上部署了站点,想要在网站的响应头中加一个字段(Cache-Control),并设置为固定值(Cache-Control:no-store) 效果类似于本地 ...
- 【Azure 应用服务】App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot。 这个是因为什么?
问题描述 App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot. 这个是因为什么? 并且通过 ...
- 【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题
问题描述 在.Net Core 5.0 项目中,添加 Microsoft.Extensions.Logging.AzureAppServices 和 Microsoft.Extensions.Logg ...
- 【Azure 应用服务】App Service For Container 配置Nginx,设置/home/site/wwwroot/目录为启动目录,并配置反向代理
问题描述 通过Docker Desktop for Linux,配置Nginx镜像后,自定义nginx.conf文件,修改启动目录和对 /out 路径的反向代理到博客园的博文地址 (https://w ...
随机推荐
- <C#任务导引教程>练习四
//27,创建一个控制台应用程序,声明两个DateTime类型的变量dt,获取系统的当前日期时间,然后使用Format格式化进行规范using System;class Program{ sta ...
- vite2 + vite.config.js 比较坑的环境变量,vite2模式的使用
想在vite.config.js 里面判断一下环境,看看是不是开发环境,查了一下官网(https://cn.vitejs.dev/guide/env-and-mode.html),说是 可以使用 im ...
- 单元测试NUnit,mock组件NSubstitute,信号量SemaphoreSlim,异步lock等例子
public class LockTest { private IDatabase _database; private readonly Random _random = new Random(); ...
- [cf700D]Huffman Coding on Segment
令$tot_{i}$为区间$[l,r]$中满足$a_{j}=i$的$j$的个数,将所有非0的$tot_{i}$取出,得到可重集$S$ 显然,有以下贪心:不断取出$S$中最小的两个元素,删除这两个元素并 ...
- java的String参数格式化
String类的format()方法用于创建格式化的字符串以及连接多个字符串对象.熟悉C语言的同学应该记得C语言的sprintf()方法,两者有类似之处.format()方法有两种重载形式. form ...
- Elasticsearch分布式搜索和数据分析引擎-ElasticStack(上)v7.14.0
Elasticsearch概述 **本人博客网站 **IT小神 www.itxiaoshen.com Elasticsearch官网地址 https://www.elastic.co/cn/elast ...
- linux结束进程命令
在linux中,进程之间通过信号来通信.进程的信号就是预定义好一个消息,进程能识别它并决定忽略还是做出反应. 信号 名称 描述 1 HUP 挂起 2 INT 中断 3 QUIT 结束运行 9 KILL ...
- SA 复习笔记
大家好,由于蒟蒻 tzc 最近被动态点分治这个学也学不会的毒瘤玩意儿虐得不轻,所以就准备换换脑筋来 Van 同样学也学不会的后缀数组了. 考虑一个非常经典的问题:[模板]后缀排序. 一些定义(very ...
- 【R】调整ggplot图例大小
图例太多时,会挤压正图,显得正图展示区域很小,这时有必要缩小图例. ################# # 减小ggplot图例 ################# library(ggplot2) ...
- python 新闻管理系统——启示
mysql取整函数: mysql函数ceil.floor.round mysql 取整 1.ceil() / ceiling() 向上取整 ex: ceil(1.2) = 2 2.floor() 向下 ...