vue-socket.io + flask-socketio + nginx

基本 demo,列出重要的一些問題

1 vue-socket.io 配置

1.1 main.js 文件

...
import VueSocketio from 'vue-socket.io'

Vue.use(new VueSocketio({
  debug: true,
  connection: 'https://domain/chatroom'
}))

...

注意點:

  • chatroom 爲後端 flask-socketionamespace(此處爲靜態命令空間,動態命令空間設置較爲複雜)

    根據官方文檔,可以額外設置 options: {path: '/chatroom'} ,則後端 nginx/socketio 改爲 /chatroom;不設置,後端默認 /socketio

1.2 app.vue

<script>
import axios from 'axios'

export default{
    data() {
        return {
            messages: []
        }
    },
    created() {
        const url = 'https://domain/chatroom/index'
        axios({
            method: 'get',
            url: url,
            data: {
                msg: 'hello'
            }
        }).then(res => {
            console.log(res)
        })
    },
    sockets: {
        connect: function() {
          console.log('socket connected')
        },
        # 監聽後端傳來數據(自定義提示)
        response: function(res) {
          this.$message.success(res.msg)
        },
        # 監聽後端傳來數據(自定義消息)
        chat_message: function(msg) {
          this.messages.push(msg)
    },
    methods: {
        onMsgSubmit(msg){
            # 用戶點擊,提交用戶輸入
            this.$socket.emit('user_input', msg)
        }
    }
    }
}
</script>
  • 創建頁面時,axios 進行 HTTP/HTTPS 請求
  • sockets 中自動創建 WebSocket 連接,responsechat_message 分別用來監聽後端不同回覆,並分別做處理
  • onMsgSubmit 用戶輸入後,向後端 user_input 傳輸數據

2 flask-socketio 配置

2.1 demo 代碼

from threading import Lock
from flask import Flask, request
from flask_socketio import SocketIO, emit, join_room, \
    leave_room, close_room, rooms, disconnect
from flask_cors import CORS

app = Flask(__name__)
app.config['SECRET_KEY'] = 'chatroom'
CORS(app, supports_credentials=True)
socketio = SocketIO(app, cors_allowed_origins="*")

def background_chat(msg, sid):
    for _ in range(3):
        socketio.emit('chat_message', {'content': msg + "?"},
                      namespace='/chatroom',
                      room=sid)
       	socketio.sleep(3)

@app.route('/index', methods=['GET'])
def index():
    return 'welcome to the chatroom!'

@socketio.on('join', namespace='/chatroom')
def join_chat(message):
    """創建聊天室
    """
    join_room(request.sid)

    thread = socketio.start_background_task(background_chat, msg,request.sid)
    emit('response', {'msg': '創建聊天室成功'})

@socketio.on('leave', namespace='/chatroom')
def leave_chat(message):
    """離開聊天室,僅刪除當前用戶
    """

@socketio.on('close', namespace='/chatroom')
def close_chat(message):
    """關閉聊天室,將所有用戶移出
    """

@socketio.on('user_input', namespace='/chatroom')
def user_input(message):
    """獲取用戶輸入
    """
    sid = request.sid
	# TODO

@socketio.on('connect', namespace='/chatroom')
def connect():
    """創建socket鏈接
    用戶進入瀏覽器頁面,自動加入
    """
    print('connect', request.sid)


@socketio.on('disconnect', namespace='/chatroom')
def disconnect():
    """關閉socket鏈接
    用戶關閉瀏覽器頁面,自動退出
    """
    print('Client disconnected', request.sid)

if __name__ == '__main__':
    socketio.run(app, debug=True, host="127.0.0.1", port=5000)

  • 跨域,在 flaskflask-socketio 中設置後,nginx 中不用再設置

  • request.sid 獲取 session_id,每個用戶單獨一個聊天室

  • 向特定用戶 sid 回覆

    socketio.emit('chat_message', {},namespace='/chatroom',room=sid)

2.2 啓動

gunicorn -b 127.0.0.1:8211 -k eventlet -w 1 wss_app:app
  • pip install eventlet
  • -w 必須設爲 1,否則報錯 invalid session

3 nginx 配置

server {
        listen 80;
        server_name api.mcc.khay.site;
        rewrite ^(.*)$ https://$host$1 permanent;
}

server {
        listen 443 ssl http2;
        server_name domain;

        access_log  /var/log/nginx/domain.access.log;
        error_log /var/log/nginx/domain.error.log;

        ssl on;
        ssl_certificate /etc/letsencrypt/live/domain/cert.pem;
        ssl_certificate_key /etc/letsencrypt/live/domain/privkey.pem;

        ssl_session_timeout 5m;

        ssl_ciphers HIGH:!aNULL:!MD5;
        ssl_protocols SSLv3 TLSv1 TLSv1.1 TLSv1.2;
        ssl_prefer_server_ciphers on;

        location /static {
                alias /var/www/mcc/website/static;
        }

  		location /chatroom/ {
                include proxy_params;
                proxy_pass http://127.0.0.1:8211/;
        }

        location /socket.io {
          #add_header Access-Control-Allow-Credentials true;
                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:8211/socket.io;
        }
}

注意點:

  • 同一個 flask 應用中,HTTP/HTTPSWebSocket 需要路由到不同端口
  • HTTP/HTTPS 通過 domain/chatroom/xxx 訪問,其中 /chatroom/ 中後一個 / 不能省略
  • WebSocket 通過 domain/socket.io 訪問,/socket.io 後面不需要添加 /

4 相關問題

  • 跨域設置

    • nginxflask/flask-socketio 只需要其中一個設置即可

    • nginx 設置 WebSocket 各種報錯,未成功;HTTP/HTTPS 設置

      location / {
      	root /var/www/html;
      	index index.html index.htm;
      
          add_header Access-Control-Allow-Origin * always;
          add_header Access-Control-Allow-Headers "DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Cont
          ent-Type,Authorization, user-id" always;
          add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, PATCH, DELETE, HEAD" always;
          add_header Access-Control-Max-Age 86400 always;
      
      
          if ($request_method = 'OPTIONS') {
          return 204;
          }
      
          proxy_set_header Host $host;
          proxy_set_header X-Real-IP $remote_addr;
          proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
          proxy_pass http://127.0.0.1:8200/;
      }
      
      • 如果 Header 中攜帶其他數據,直接在 Access-Control-Allow-Headers 中添加即可
    • flask/flask-socketio 設置

      pip install flask-cors

      from flask_cors import CORS
      ...
      app = Flask(__name__)
      CORS(app, supports_credentials=True) # HTTP/HTTPS 跨域
      socketio = SocketIO(app, cors_allowed_origins="*") # websocket 跨域
      
  • Invalid session

    • gunicorn 其中-w (即 workers) 必須設爲 1

      參考官方文檔說明

      Due to the limited load balancing algorithm used by gunicorn, it is not possible to use more than one worker process when using this web server. For that reason, all the examples above include the -w 1 option.

5 參考文檔

參考官方文檔,Github issues 即可

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章