flask 使用 gevent-websocket + gunicorn 部署 (python 实时日志开发+部署)
1 我的falsk websocket 环境 pip install -r ******.txt
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | eventlet==0.24.1 flake8==3.8.4 Flask==0.11.1 Flask-Cors==3.0.10 Flask-Script==2.0.5 Flask-SocketIO==2.7.2 Flask-Sockets==0.2.1 gevent==20.6.2 gevent-websocket==0.10.1 gpg==1.13.1 greenlet==1.1.1 gunicorn==19.10.0 python-socketio==5.4.0 websockets==8.1 Werkzeug==1.0.0 |
2 python 后端代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | # -*- coding: utf-8 -*- from flask import Flask,request,render_template,redirect,session import uuid,datetime import subprocess import sys from geventwebsocket.handler import WebSocketHandler from gevent.pywsgi import WSGIServer import json,os from log.log import create_app, setup_log # 配置日志 from config.config import Config import logging from flask import Flask, views, render_template, send_file, request, session, current_app app = create_app( "development" ) app.secret_key = ';lkjnfdidiclsjek' from flask_cors import CORS # 跨域 cors = CORS(app, resources={r "/api/*" : { "origins" : "*" }}) from flask_sockets import Sockets sockets = Sockets(app) def log_path(modle): return path_data. get (modle) # 返回日志路径 WEBSOCKET_DICT = {} @app.route( '/login' ,methods=[ 'GET' , 'POST' ]) def login(): if request.method == 'GET' : return render_template( 'login.html' ) else : uid = str(uuid.uuid4()) session[ 'user_info' ] = { 'id' : uid, 'name' : request.form. get ( 'user' )} return 'w' def _decode_data(byte_data: bytes): "" " 解码数据 :param byte_data: 待解码数据 : return : 解码字符串 "" " try : return byte_data.decode( 'UTF-8' ) except UnicodeDecodeError: return byte_data.decode( 'GB18030' ) @sockets.route( '/message' ) def message(ws): # modle = request.args.get("modle", None) # print('123',modle) uid = str(uuid.uuid4()) # session['user_info'] = uid # . 判断是否为Websocket请求,http不包含wsgi.websocket # ws = request.environ.get('wsgi.websocket') # print(ws) if not ws: return 'use websocket' # 此处连接成功 print( 'ok' ,session,uid) # session.clear() current_user_id =uid # 自定义websocket发送对象 WEBSOCKET_DICT[current_user_id] = ws while True: # print(WEBSOCKET_DICT,'WEBSOCKET_DICT.values()') # . 等待用户发送消息,并接受 try : message = ws.receive() # 对应的模块密码数据 print(message, 'message' ) is_t,modle=is_password(message) # 验证密码 if not is_t: print( '密码错误' ) return '密码错误' path=log_path(modle) # 关闭 mesaage = None if not message: del WEBSOCKET_DICT[current_user_id] break print( "---------------" ) for u_id,conn in WEBSOCKET_DICT.items(): # print(conn,'conn',uid) if u_id==uid: # print(conn) print(path, 'test' )# 获取模块日志路径 # path='/tmp/echo_stdout.log' # path='D:/zhongan/framework/myframework/logs/framework.log' cmd= 'tail -f %s' %path print( '\033[1;32m************** START **************\033[0m' ,cmd) p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stderr=subprocess.PIPE, stdout=subprocess.PIPE, universal_newlines=True, shell=True, bufsize=1) # 实时输出 while True: line = p.stdout.readline() print(line, end= '' ) conn.send(line) # 发送前端 if subprocess.Popen.poll(p) == 0: # 判断子进程是否结束 break return p.returncode except Exception as e: print(e) return '完毕' if __name__ == '__main__' : # 如果是http请求走app使用原有的wsgi处理,如果是websocket请求走WebSocketHandler处理 http_server = WSGIServer(( '0.0.0.0' ,8000 ), app, handler_class=WebSocketHandler) http_server.serve_forever() |
3 前端代码 index.vue
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | < template > < div > <!-- <span>flask返回的数据{{modle}} --> <!-- </span> --> < br /> < div v-for="i in new_data"> {{ i }} </ div > </ div > </ template > < script > export default { name : 'test', data() { return { websock: null, new_data:[], pwd:this.$route.params.pwd } }, created() { this.initWebSocket(); }, destroyed() { this.websock.close() //离开路由之后断开websocket连接 }, methods: { initWebSocket(){ //初始化weosocket const wsuri = "ws://192.168:8000/message"; this.websock = new WebSocket(wsuri); this.websock.onmessage = this.websocketonmessage; this.websock.onopen = this.websocketonopen; this.websock.onerror = this.websocketonerror; this.websock.onclose = this.websocketclose; }, websocketonopen(){ //连接建立之后执行send方法发送数据 this.websocketsend(this.pwd); }, websocketonerror(){//连接建立失败重连 // this.initWebSocket(); // alert('not ok') }, add(data){ let mes_data=[] mes_data.push(data) this.new_data=mes_data console.log(this.new_data) }, websocketonmessage(e){ //数据接收 this.new_data.push(e.data) // this.add(e.data) console.log(e) }, websocketsend(Data){//数据发送 this.websock.send(Data); }, websocketclose(e){ //关闭 console.log('断开连接',e); alert('密码错误') }, }, mounted() { this.pwd=this.$route.params.pwd }, } </ script > |
4 部署方案 nginx 部署前端 websocket 做后端服务 (不代理方式)
conf文件夹下 nginx 配置
1 2 3 4 5 6 7 8 9 10 11 | server { listen 8080; server_name ****; root /usr/share/nginx/www/; # 静态文件 location / { try_files $uri $uri/ @router; } location @router { rewrite ^.*$ /index.html last; } } |
使用 gunicorn 启动 指定用 gevent-websocket
5 部署后端服务 环境安装完成之后 4进程 nohup 异步启动
1 | hohup gunicorn -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 4 -b 0.0.0.0:8000 manage:app & |
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux glibc自带哈希表的用例及性能测试
· 深入理解 Mybatis 分库分表执行原理
· 如何打造一个高并发系统?
· .NET Core GC压缩(compact_phase)底层原理浅谈
· 现代计算机视觉入门之:什么是图片特征编码
· 手把手教你在本地部署DeepSeek R1,搭建web-ui ,建议收藏!
· Spring AI + Ollama 实现 deepseek-r1 的API服务和调用
· 数据库服务器 SQL Server 版本升级公告
· C#/.NET/.NET Core技术前沿周刊 | 第 23 期(2025年1.20-1.26)
· 程序员常用高效实用工具推荐,办公效率提升利器!
2020-10-27 ant-design 基础格式
2020-10-27 qq获取验证码接口
2020-10-27 【Django】django.core.exceptions.ImproperlyConfigured: mysqlclient 1.4.0 or newer is required;
2020-10-27 redis 存储验证码 基本使用