问题描述

在上篇博文“【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

【Azure 应用服务】Azure App Service For Linux 上实现 Python Flask Web Socket 项目 Http/Https的更多相关文章

  1. 【Azure 应用服务】App Service For Linux 如何在 Web 应用实例上住抓取网络日志

    问题描述 在App Service For Windows的环境中,我们可以通过ArmClient 工具发送POST请求在Web应用的实例中抓取网络日志,但是在App Service For Linu ...

  2. 【Azure 应用服务】App Service For Linux 部署PHP Laravel 项目,如何修改首页路径为 wwwroot\public\index.php

    问题描述 参考官方文档部署 PHP Laravel 项目到App Service for Linux环境中,但是访问应用时候遇见了500 Server Error 错误. 从部署的日志中,可以明确看出 ...

  3. 【Azure 应用服务】App Service For Linux 部署Java Spring Boot应用后,查看日志文件时的疑惑

    编写Java Spring Boot应用,通过配置logging.path路径把日志输出在指定的文件夹中. 第一步:通过VS Code创建一个空的Spring Boot项目 第二步:在applicat ...

  4. 【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)

    问题描述 使用 python websockets 模块作为Socket的服务端,发布到App Service for Linux环境后,发现Docker Container无法启动.错误消息为: 2 ...

  5. 【Azure 应用服务】App Service中,为Java应用配置自定义错误页面,禁用DELETE, PUT方法

    问题定义 使用Azure应用服务(App Service),部署Java应用,使用Tomcat容器,如何自定义错误页面呢?同时禁用DELETE, PUT方法 解决办法 如何自定义错误页面呢?需要在 J ...

  6. 【Azure 应用服务】App Service 通过配置web.config来添加请求返回的响应头(Response Header)

    问题描述 在Azure App Service上部署了站点,想要在网站的响应头中加一个字段(Cache-Control),并设置为固定值(Cache-Control:no-store) 效果类似于本地 ...

  7. 【Azure 应用服务】App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot。 这个是因为什么?

    问题描述 App Service 在使用GIt本地部署,上传代码的路径为/home/site/repository,而不是站点的根目录/home/site/wwwroot. 这个是因为什么? 并且通过 ...

  8. 【Azure 应用服务】App Service .NET Core项目在Program.cs中自定义添加的logger.LogInformation,部署到App Service上后日志不显示Log Stream中的问题

    问题描述 在.Net Core 5.0 项目中,添加 Microsoft.Extensions.Logging.AzureAppServices 和 Microsoft.Extensions.Logg ...

  9. 【Azure 应用服务】App Service For Container 配置Nginx,设置/home/site/wwwroot/目录为启动目录,并配置反向代理

    问题描述 通过Docker Desktop for Linux,配置Nginx镜像后,自定义nginx.conf文件,修改启动目录和对 /out 路径的反向代理到博客园的博文地址 (https://w ...

随机推荐

  1. Dapr-绑定构建块

    前言: 前篇-发布订阅文章对Dapr的订阅/发布进行了解,本篇继续对 绑定 构建块进行了解. 一.简介: Dapr 资源绑定使服务能够跨即时应用程序外部的外部资源集成业务操作. 来自外部系统的事件可能 ...

  2. Winform开发的快速、健壮、解耦的几点建议

    在Winform开发领域开发过十多年的项目中,见证着形形色色的架构和官方技术的应用,从最早类似Winform模式的WebForm技术,到接着的JQuery+界面组件,再到Asp.net Core的技术 ...

  3. 洛谷 P4183 - [USACO18JAN]Cow at Large P(点分治)

    洛谷题面传送门 点分治 hot tea. 首先考虑什么样的点能够对以 \(u\) 为根的答案产生 \(1\) 的贡献.我们考虑以 \(u\) 为根对整棵树进行一遍 DFS.那么对于一个点 \(v\), ...

  4. Codeforces 407E - k-d-sequence(单调栈+扫描线+线段树)

    Codeforces 题面传送门 & 洛谷题面传送门 深感自己线段树学得不扎实-- 首先特判掉 \(d=0\) 的情况,显然这种情况下满足条件的区间 \([l,r]\) 中的数必须相同,双针扫 ...

  5. PDO的好处

    产生原因 普通的SQL执行语句,由于研发人员对前端请求参数过滤不严谨,导致SQL被注入,从而影响数据库,带来风险 使用PDO后形成的语句 SELECT * FROM test WHERE id in ...

  6. selenium+chrome抓取数据,运行js

    某些特殊的网站需要用selenium来抓取数据,比如用js加密的,破解难度大的 selenium支持linux和win,前提是必须安装python3,环境配置好 抓取代码: #!/usr/bin/en ...

  7. 【模板】缩点(Tarjan算法)/洛谷P3387

    题目链接 https://www.luogu.com.cn/problem/P3387 题目大意 给定一个 \(n\) 个点 \(m\) 条边有向图,每个点有一个权值,求一条路径,使路径经过的点权值之 ...

  8. Linux修改默认挂载NFS协议版本

    系统版本:CentOS Linux release 7.4.1708 (Core) $ vi /etc/nfsmount.conf # # /etc/nfsmount.conf - see nfsmo ...

  9. C#数据库连接方式【简版】

    using System;using System.Collections.Generic;using System.ComponentModel;using System.Drawing;using ...

  10. linux RPM/YUM包管理

    linux RPM/YUM包管理 目录 linux RPM/YUM包管理 RPM RPM包管理 查询rpm包 卸载rpm包 安装rpm包 YUM 查看yum服务器是否有需要安装的软件 下载安装指定的y ...