1. 程式人生 > 實用技巧 >flask-sockiet+Gunicorn+Nginx實戰

flask-sockiet+Gunicorn+Nginx實戰

  在flask框架下,服務端和客戶端通過websocket的通訊方式主要有兩種,一種是原生的websocket通訊,通過引入flask-websockets來實現,這個包只是簡單的對websocket協議進行了簡單的封裝;另外一種就是本文主要講的flask-socketio方式。

  先談談自己遇到的坑,因為專案需要websocket通訊,起初為了前後端通用性選擇了flask-websockets,但是好不容易測試通過可以互傳資訊了,結果發現只要啟動通訊,flask框架下的其他介面統統用不了,響應超時,後來想到的原因可能是因為websocket通訊使用了和flask介面同樣的埠,導致埠阻塞造成的。於是根據網上建議改用flask-socketio。

服務端

  首先是安裝,通過命令列完成:

pip install flask-socketio

  然後,在你的flask專案中引入,並初始化:

from flask import Flask, render_template
from flask_socketio import SocketIO

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

if __name__ == '__main__':
    socketio.run(app)

注意:這裡的程式啟動方式要更改為socket特有的模式,用socketio.run(app)代替app.run()。

socketio還支援init_app的初始化方式,可以在建立socketio物件時,先不傳入app例項,啟動程式前再呼叫init_app進行初始化。

客戶端

客戶端可以用網頁前端連線

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/socket.io/1.3.6/socket.io.min.js"></script>
<script type="text/javascript" charset="utf-8">
    var socket = io.connect('http://' +
document.domain + ':' + location.port); socket.on('connect', function() { socket.emit('my event', {data: 'I\'m connected!'}); }); </script>

也可用python客戶端連線

#-*-coding:utf-8-*-
from socketIO_client import SocketIO, BaseNamespace
import time


class MonitorNamespace(BaseNamespace):

    def on_connect(self):
        print('[Connected]')

    def on_reconnect(self):
        print('[Reconnected]')

    def on_disconnect(self):
        print('[Disconnected]')


socketIO = SocketIO('http://127.0.0.1', 5000)
monitor_namespace = socketIO.define(MonitorNamespace, '/your_namespace')
monitor_namespace.emit('join', {'room': 'your room'})
for i in range(30):
    monitor_namespace.emit('new_log', {'data': 'info ---{}'.format(i)})
    time.sleep(1)
monitor_namespace.emit('leave', {'room': 'your room'})

注意場景send()用於匿名事件;emit()用於自定義事件。傳送引數支援廣播broadcast=True和room='room_name'兩種。指定room後,只有加入到room的客戶端才能接收到訊息。加入room的方法:

from flask_socketio import join_room, leave_room
send(username + ' has entered the room.', room=room)

訪問Flask全域性變數

在呼叫事件函式前,應用上下文就已經被推入棧,以此確保current_appg在事件函式中可用,在socket連線之前,或者傳送訊息之前:

        with app.app_context():
            init_database()
            self.db = g.db

另外,request多了sid成員,用於為連線設定的session ID,其預設值為客戶端房間號。連線事件函式可以選擇返回False來拒接連線請求。實際應用中,可以通過這種方式來驗證使用者許可權。

部署

最簡單的部署方式,就是安裝eventlet或gevent,然後呼叫socketio.run(app)

gunicorn部署

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

由於gunicorn演算法的問題,伺服器啟動只能使用一個worker程序。因此,必須加上-w 1

使用nginx作為WebSocket反向代理

server {
    listen 80;
    server_name _;

    location / {
        include proxy_params;
        proxy_pass http://127.0.0.1:5000;
    }

    location /socket.io {
        include proxy_params;
        proxy_http_version 1.1;
        proxy_buffering off;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        proxy_pass http://127.0.0.1:5000/socket.io;
    }
}

上述的ip和埠地址根據自己gunicorn設定的來定。

如果服務端啟用了ssl,socket連線時埠號要改為443。