问题描述

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

2021-10-28T02:39:51.812Z INFO  - docker run -d -p 1764:8000 --name test_0_c348bc62 -e WEBSITE_SITE_NAME=sockettest -e WEBSITE_AUTH_ENABLED=False -e WEBSITE_ROLE_INSTANCE_ID=0 -e WEBSITE_HOSTNAME=sockettest.chinacloudsites.cn -e WEBSITE_INSTANCE_ID=08307498aa991c84523184617d17f074bad5139bd2c0710fdf2b1a0ad3d3a9b7 -e HTTP_LOGGING_ENABLED=1 appsvc/python:3.8_20210709.2 python socket_server.py 

2021-10-28T02:39:55.922Z INFO  - Initiating warmup request to container test_0_c348bc62 for site sockettest
2021-10-28T02:40:11.177Z INFO - Waiting for response to warmup request for container test_0_c348bc62. Elapsed time = 15.2556084 sec
...
2021-10-28T02:43:33.439Z INFO - Waiting for response to warmup request for container test_0_c348bc62. Elapsed time = 217.5175373 sec
2021-10-28T02:43:46.644Z ERROR - Container test_0_c348bc62 for site sockettest did not start within expected time limit. Elapsed time = 230.7221775 sec
2021-10-28T02:43:46.645Z ERROR - Container test_0_c348bc62 didn't respond to HTTP pings on port: 8000, failing site start. See container logs for debugging.
2021-10-28T02:43:46.672Z INFO - Stopping site sockettest because it failed during startup.

PS:应用上云的需求。

问题解决

这是因为App Service Linux使用Container的启动需要Python代码中对HTTP进行正确的响应,否则Site无法启动。而这次的Python代码并不包含对HTTP请求的响应(需要Web框架),所以无法正确启动。

在Azure App Service for Linux - Python的文档中,主要介绍的两种Web框架为 Flask 和 Django。 接下来,就通过Flask 和SocketIO来实现WebSocket功能。

实现 Python SocketIO 代码及步骤

1)创建 app.py 文件,并复制以下内容,作为Socket的服务端及Flask应用的启动

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!'
socketio = SocketIO(app, async_mode=async_mode)
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')

2)创建 template/index.html,并复制以下内容,作为Socket的客户端,验证WebSocket的正常工作

<!DOCTYPE HTML>
<html>
<head>
<title>Socket-Test</title>
<script src="//code.jquery.com/jquery-1.12.4.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/socket.io/2.2.0/socket.io.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>

3)创建 requirements.txt 文件,并包含以下module及版本,如果版本不适合,可以适当修改。

Flask==1.0.2
Flask-Login==0.4.1
Flask-Session==0.3.1
itsdangerous==1.1.0
Jinja2==2.10
MarkupSafe==1.1.0
six==1.11.0
Werkzeug==0.14.1
Flask-SocketIO==4.3.1
python-engineio==3.13.2
python-socketio==4.6.0
eventlet==0.30.2

以上三个就是整个项目的源文件,VS Code中的文件结构为:

4)在VS Code中使用az webapp up来部署Python Web应用

#设置登录环境为中国区Azure
az cloud set -n AzureChinaCloud
az login #部署代码,如果pythonlinuxwebsocket01不存在,则自动创建定价层位B1的App Service
az webapp up --sku B1 --name pythonlinuxwebsocket01

效果展示:

5)修改App Service的启动命令

gunicorn --worker-class eventlet -w 1 app:app

注:为了避免 flask-socketIO 服务器部署 400 Bad Request 问题,所以需要使用 eventlet 作为工作进程。详细说明可见:https://blog.csdn.net/weixin_43958804/article/details/109024348

6)  开启WebSocket, 启用HTTP,  设置PORT参数

注:修改后,重启App Service。如果重启后使用HTTP请求,但是发生了302跳转到HTTPS的情况,就可以考虑重新部署依次站点。使用第四步方法,az webapp up然container重新生成项目信息。

7)验证Web Socket

使用HTTP访问刚刚部署的App Service URL。

附录一:解决flask-socketIO 服务器部署 400 Bad Request 问题

使用eventlet,设置启动命令:gunicorn --worker-class eventlet -w 1 app:app

附录二:Gunicorn ImportError: cannot import name 'ALREADY_HANDLED' from 'eventlet.wsgi' in docker

Installing older version of eventlet solved the problem: pip install eventlet==0.30.2

参考资料:

Implement a WebSocket Using Flask and Socket-IO(Python): https://medium.com/swlh/implement-a-websocket-using-flask-and-socket-io-python-76afa5bbeae1

解决flask-socketIO 服务器部署 400 Bad Request 问题:https://blog.csdn.net/weixin_43958804/article/details/109024348

【Azure 应用服务】App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)的更多相关文章

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

    问题描述 在上篇博文"[Azure 应用服务]App Service for Linux 中实现 WebSocket 功能 (Python SocketIO)"中,实现了通过 HT ...

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

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

  3. 【应用服务 App Service】快速获取DUMP文件(App Service for Windows(.NET/.NET Core))

    问题情形 当应用在Azure 应用服务App Service中运行时,有时候出现CPU,Memory很高,但是没有明显的5XX错误和异常日志,有时就是有异常但是也不能明确的指出具体的代码错误.当面临这 ...

  4. 【Azure 应用服务】PHP应用部署在App Service for Linux环境中,上传文件大于1MB时,遇见了413 Request Entity Too Large 错误的解决方法

    问题描述 在PHP项目部署在App Service后,上传文件如果大于1MB就会遇见 413 Request Entity Too Large 的问题. 问题解决 目前这个问题,首先需要分析应用所在的 ...

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

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

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

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

  7. 【Azure 应用服务】部署Jar到App Service for Linux,因启动命令路径配置错误而引起:( Application Error 问题

    问题描述 App Service for Linux 资源创建完成后,通过FTP方式把 .jar包(logdemo.jar)包上传到 /site/wwwroot/ 文件夹后,在App Service的 ...

  8. 【应用服务 App Service】在Azure App Service中使用WebSocket - PHP的问题 - 如何使用和调用

    问题描述 在Azure App Service中,有对.Net,Java的WebSocket支持的示例代码,但是没有成功的PHP代码. 以下的步骤则是如何基于Azure App Service实现PH ...

  9. 【应用服务 App Service】App Service中抓取网络日志

    问题描述 众所周知,Azure App Service是一种PaaS服务,也就是说,IaaS层面的所有内容都由平台维护,所以使用App Service的我们根本无法触碰到远行程序的虚拟机(VM), 所 ...

随机推荐

  1. 一文让你彻底搞懂 vue-Router

    路由是网络工程里面的专业术语,就是通过互联把信息从源地址传输到目的地址的活动.本质上就是一种对应关系.分为前端路由和后端路由. 后端路由: URL 的请求地址与服务器上的资源对应,根据不同的请求地址返 ...

  2. Docker系列(10)- 常用命令小结

    #橙色前面笔记已记录,黑色后面笔记将完善#勤加练习!!!attach Attach to a running container # 当前 shell 下 attach 连接指定运行镜像 build ...

  3. Shell系列(3)- 命令别名

    前言 使用alias命令创建命令别名,是Bash的一个基本功能:别名有两种形式,一种暂时的,Linux重启后失效.另外一种永久的通过该配置文件实现 使用更改别名 临时 命令格式:alias 别名='原 ...

  4. 博客主题-Next风格

    适配方法 下载压缩包,按照文件名将内容复制粘贴到对应框中即可. 注意事项 请将主题设置为custom 禁用默认css 下载连接 Next.rar version:2020-07-10 next.rar ...

  5. python学习笔记(十四)python实现发邮件

    import smtplib from email.mime.text import MIMEText from email.mime.multipart import MIMEMultipart u ...

  6. html jquery操作

    $(document).on('事件','元素',function(参数){ // 函数体 }) 元素获取方式:https://www.cnblogs.com/lixiuran/p/5316727.h ...

  7. Docker小白到实战之Docker网络简单了解一下

    前言 现在对于Docker容器的隔离性都有所了解了,但对容器IP地址的分配.容器间的访问等还是有点小疑问,如果容器的IP由于新启动导致变动,那又怎么才能保证原有业务不会被影响,这就和网络有挂钩了,接下 ...

  8. SSA

    wikipedia上关于SSA的定义如下: In compiler design, static single assignment form (often abbreviated as SSA fo ...

  9. Centos7 配置JDK 提示 /lib/ld-linux.so.2: bad ELF interpreter: No such file or direct

    解决办法:yum install glibc.i686

  10. 每日总结:Java基本语法 (2021.9.23)

         对象:对象是类的一个实例,有状态和行为. 类:类是一个模板,它描述一类对象的行为和状态. 方法:方法就是行为,一个类可以有很多方法. 实例变量:每个对象都有独特的实例变量,对象的状态由这些实 ...