【Azure 應(yīng)用服務(wù)】App Service for Linux 中實現(xiàn) WebSocket 功能 (Python SocketIO)

問題描述

使用 python websockets 模塊作為Socket的服務(wù)端恢共,發(fā)布到App Service for Linux環(huán)境后,發(fā)現(xiàn)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:應(yīng)用上云的需求。

問題解決

這是因為App Service Linux使用Container的啟動需要Python代碼中對HTTP進(jìn)行正確的響應(yīng)对室,否則Site無法啟動拉宗。而這次的Python代碼并不包含對HTTP請求的響應(yīng)(需要Web框架),所以無法正確啟動渡冻。

在Azure App Service for Linux - Python的文檔中戚扳,主要介紹的兩種Web框架為 Flask 和 Django。 接下來族吻,就通過Flask 和SocketIO來實現(xiàn)WebSocket功能帽借。

實現(xiàn) Python SocketIO 代碼及步驟

1)創(chuàng)建 app.py 文件,并復(fù)制以下內(nèi)容超歌,作為Socket的服務(wù)端及Flask應(yīng)用的啟動

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)創(chuàng)建 template/index.html砍艾,并復(fù)制以下內(nèi)容,作為Socket的客戶端巍举,驗證WebSocket的正常工作

<!DOCTYPE HTML>
<html>
<head>
   <title>Socket-Test</title>
   <script src="http://code.jquery.com/jquery-1.12.4.min.js"></script>
   <script src="http://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)創(chuàng)建 requirements.txt 文件辐董,并包含以下module及版本,如果版本不適合禀综,可以適當(dāng)修改。

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中的文件結(jié)構(gòu)為:


image.png

4)在VS Code中使用az webapp up來部署Python Web應(yīng)用

#設(shè)置登錄環(huán)境為中國區(qū)Azure
az cloud set -n AzureChinaCloud
az login

#部署代碼定枷,如果pythonlinuxwebsocket01不存在,則自動創(chuàng)建定價層位B1的App Service
az webapp up --sku B1 --name pythonlinuxwebsocket01

效果展示:


image.gif

5)修改App Service的啟動命令

gunicorn --worker-class eventlet -w 1 app:app
image.png

注:為了避免 flask-socketIO 服務(wù)器部署 400 Bad Request 問題届氢,所以需要使用 eventlet 作為工作進(jìn)程欠窒。詳細(xì)說明可見:https://blog.csdn.net/weixin_43958804/article/details/109024348

6) 開啟WebSocket, 啟用HTTP, 設(shè)置PORT參數(shù)

image.png

注:修改后退子,重啟App Service岖妄。如果重啟后使用HTTP請求,但是發(fā)生了302跳轉(zhuǎn)到HTTPS的情況寂祥,就可以考慮重新部署一次站點荐虐。使用第四步方法,az webapp up然container重新生成項目信息丸凭。

7)驗證Web Socket

使用HTTP訪問剛剛部署的App Service URL福扬。


image.png

附錄一:解決flask-socketIO 服務(wù)器部署 400 Bad Request 問題

使用eventlet,設(shè)置啟動命令: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 服務(wù)器部署 400 Bad Request 問題:https://blog.csdn.net/weixin_43958804/article/details/109024348

當(dāng)在復(fù)雜的環(huán)境中面臨問題惜犀,格物之道需:濁而靜之徐清铛碑,安以動之徐生。 云中虽界,恰是如此!

分類: 【Azure 應(yīng)用服務(wù)】

標(biāo)簽: App Service, Azure Developer, azure python, 云中實現(xiàn)web socket

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末汽烦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子莉御,更是在濱河造成了極大的恐慌撇吞,老刑警劉巖俗冻,帶你破解...
    沈念sama閱讀 219,110評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異梢夯,居然都是意外死亡言疗,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,443評論 3 395
  • 文/潘曉璐 我一進(jìn)店門颂砸,熙熙樓的掌柜王于貴愁眉苦臉地迎上來噪奄,“玉大人,你說我怎么就攤上這事人乓∏诶海” “怎么了?”我有些...
    開封第一講書人閱讀 165,474評論 0 356
  • 文/不壞的土叔 我叫張陵色罚,是天一觀的道長碰缔。 經(jīng)常有香客問我,道長戳护,這世上最難降的妖魔是什么金抡? 我笑而不...
    開封第一講書人閱讀 58,881評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮腌且,結(jié)果婚禮上梗肝,老公的妹妹穿的比我還像新娘。我一直安慰自己铺董,他們只是感情好巫击,可當(dāng)我...
    茶點故事閱讀 67,902評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著精续,像睡著了一般坝锰。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上重付,一...
    開封第一講書人閱讀 51,698評論 1 305
  • 那天顷级,我揣著相機(jī)與錄音,去河邊找鬼确垫。 笑死愕把,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的森爽。 我是一名探鬼主播恨豁,決...
    沈念sama閱讀 40,418評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼爬迟!你這毒婦竟也來了橘蜜?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,332評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎计福,沒想到半個月后跌捆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,796評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡象颖,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,968評論 3 337
  • 正文 我和宋清朗相戀三年佩厚,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片说订。...
    茶點故事閱讀 40,110評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡抄瓦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出陶冷,到底是詐尸還是另有隱情钙姊,我是刑警寧澤,帶...
    沈念sama閱讀 35,792評論 5 346
  • 正文 年R本政府宣布埂伦,位于F島的核電站煞额,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沾谜。R本人自食惡果不足惜膊毁,卻給世界環(huán)境...
    茶點故事閱讀 41,455評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望基跑。 院中可真熱鬧婚温,春花似錦、人聲如沸涩僻。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,003評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽逆日。三九已至,卻和暖如春萄凤,著一層夾襖步出監(jiān)牢的瞬間室抽,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,130評論 1 272
  • 我被黑心中介騙來泰國打工靡努, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留坪圾,地道東北人。 一個月前我還...
    沈念sama閱讀 48,348評論 3 373
  • 正文 我出身青樓惑朦,卻偏偏與公主長得像兽泄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子漾月,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,047評論 2 355

推薦閱讀更多精彩內(nèi)容