飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

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

时间:2021-12-16  作者:lulight  

问题描述

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

HTTP 成功 HTTPS 失败

 

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

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

问题解决

使用 域名ey_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

--------域名

----域名

----域名

Templates/域名

<!DOCTYPE HTML>
<html>
<head>
    <title>Socket-Test</title>
    <script src="https://域名/ajax/libs/jquery/3.4.1/域名"></script>
    <script src="https://域名/ajax/libs/域名/3.1.3/域名.js"></script>
    <script type="text/javascript" charset="utf-8">
        $(document).ready(function() {

            namespace = \'/test\';
            var socket = io(namespace);

            域名(\'connect\', function() {
                域名(\'my_event\', {data: \'connected to the SocketServer...\'});
            });

            域名(\'my_response\', function(msg, cb) {
                $(\'#log\').append(\'<br>\' + $(\'<div/>\').text(\'logs #\' + 域名t + \': \' + 域名).html());
                if (cb)
                    cb();
            });
            $(\'form#emit\').submit(function(event) {
                域名(\'my_event\', {data: $(\'#emit_data\').val()});
                return false;
            });
            $(\'form#broadcast\').submit(function(event) {
                域名(\'my_broadcast_event\', {data: $(\'#broadcast_data\').val()});
                return false;
            });
            $(\'form#disconnect\').submit(function(event) {
                域名(\'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>

域名

import eventlet
域名ey_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__)

域名ig[\'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 = 域名ron["PORT"] 
serverIP = "0.0.0.0"

# # Used by Local debug.
# PORT = 5000 
# serverIP = "127.0.0.1"

@域名e(\'/\')
def index():
    return render_template(\'域名\', async_mode=域名c_mode)


@域名(\'my_event\', namespace=\'/test\')
def test_message(message):
    print(\'receive message:\' + message[\'data\'],)
    session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1
    emit(\'my_response\',
         {\'data\': message[\'data\'], \'count\': session[\'receive_count\']})


@域名(\'my_broadcast_event\', namespace=\'/test\')
def test_broadcast_message(message):
    print(\'broadcast message:\' + message[\'data\'],)
    session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1
    emit(\'my_response\',
         {\'data\': message[\'data\'], \'count\': session[\'receive_count\']},
         broadcast=True)


@域名(\'disconnect_request\', namespace=\'/test\')
def disconnect_request():
    @copy_current_request_context
    def can_disconnect():
        disconnect()

    session[\'receive_count\'] = 域名(\'receive_count\', 0) + 1
    emit(\'my_response\',
         {\'data\': \'Disconnected!\', \'count\': session[\'receive_count\']},
         callback=can_disconnect)


if __name__ == \'__main__\':
    域名(app,port=PORT, host=serverIP, debug=True)
    print(\'socket io start\')

域名

Flask==2.0.2
Flask-SocketIO==5.1.1
eventlet==域名.2

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

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

配置方法可见:https://域名/lulight/p/域名 (第五段:修改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 域名.1 or higher

2021-12-14T03:31:域名51185Z Error: class uri \'eventlet\' invalid or not found: 
2021-12-14T03:31:域名56185Z 
2021-12-14T03:31:域名59786Z [Traceback (most recent call last):
2021-12-14T03:31:域名63086Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 10, in <module>
2021-12-14T03:31:域名67386Z     import eventlet
2021-12-14T03:31:域名70686Z ModuleNotFoundError: No module named \'eventlet\'
2021-12-14T03:31:域名73986Z 
2021-12-14T03:31:域名77187Z During handling of the above exception, another exception occurred:
2021-12-14T03:31:域名81587Z 
2021-12-14T03:31:域名84787Z Traceback (most recent call last):
2021-12-14T03:31:域名88187Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/域名", line 99, in load_class
2021-12-14T03:31:域名07988Z     mod = 域名rt_module(\'.\'.join(components))
2021-12-14T03:31:域名11389Z   File "/opt/python/3.7.9/lib/python3.7/importlib/域名", line 127, in import_module
2021-12-14T03:31:域名14489Z     return 域名_import(name[level:], package, level)
2021-12-14T03:31:域名17589Z   File "<frozen 域名tstrap>", line 1006, in _gcd_import
2021-12-14T03:31:域名20689Z   File "<frozen 域名tstrap>", line 983, in _find_and_load
2021-12-14T03:31:域名23789Z   File "<frozen 域名tstrap>", line 967, in _find_and_load_unlocked
2021-12-14T03:31:域名26890Z   File "<frozen 域名tstrap>", line 677, in _load_unlocked
2021-12-14T03:31:域名30090Z   File "<frozen 域名tstrap_external>", line 728, in exec_module
2021-12-14T03:31:域名33190Z   File "<frozen 域名tstrap>", line 219, in _call_with_frames_removed
2021-12-14T03:31:域名36290Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 12, in <module>
2021-12-14T03:31:域名39490Z     raise RuntimeError("eventlet worker requires eventlet 域名.1 or higher")
2021-12-14T03:31:域名42690Z RuntimeError: eventlet worker requires eventlet 域名.1 or higher

2:cannot import name \'ALREADY_HANDLED\' from \'域名\'

2021-12-14T05:14:域名82566Z Error: class uri \'eventlet\' invalid or not found: 
2021-12-14T05:14:域名89566Z 
2021-12-14T05:14:域名94867Z [Traceback (most recent call last):
2021-12-14T05:14:域名99767Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/域名", line 99, in load_class
2021-12-14T05:14:域名12168Z     mod = 域名rt_module(\'.\'.join(components))
2021-12-14T05:14:域名17368Z   File "/opt/python/3.7.9/lib/python3.7/importlib/域名", line 127, in import_module
2021-12-14T05:14:域名22569Z     return 域名_import(name[level:], package, level)
2021-12-14T05:14:域名39170Z   File "<frozen 域名tstrap>", line 1006, in _gcd_import
2021-12-14T05:14:域名44870Z   File "<frozen 域名tstrap>", line 983, in _find_and_load
2021-12-14T05:14:域名49471Z   File "<frozen 域名tstrap>", line 967, in _find_and_load_unlocked
2021-12-14T05:14:域名54171Z   File "<frozen 域名tstrap>", line 677, in _load_unlocked
2021-12-14T05:14:域名58771Z   File "<frozen 域名tstrap_external>", line 728, in exec_module
2021-12-14T05:14:域名63371Z   File "<frozen 域名tstrap>", line 219, in _call_with_frames_removed
2021-12-14T05:14:域名68172Z   File "/opt/python/3.7.9/lib/python3.7/site-packages/gunicorn/workers/域名", line 20, in <module>
2021-12-14T05:14:域名72972Z     from 域名 import ALREADY_HANDLED as EVENTLET_ALREADY_HANDLED
2021-12-14T05:14:域名77472Z ImportError: cannot import name \'ALREADY_HANDLED\' from \'域名\' (/tmp/8d9bebfefe8421c/antenv/lib/python3.7/site-packages/eventlet/域名)
2021-12-14T05:14:域名82173Z ]

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

参考资料

App Service for Linux 中实现 WebSocket 功能 (Python SocketIO) : https://域名/lulight/p/域名

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。