多线程运行多个Flask类实例仅第一个生效的解决方案
问题根因
多Flask实例启动失败核心是代码逻辑错误,导致第一个实例启动后直接阻塞主线程,后续实例根本没有执行机会:
- 服务启动逻辑错放在类构造函数
__init__中
构造函数会在类实例化时于当前线程同步执行,你写threading.Thread(target=runFlaskApp(xxx))时,runFlaskApp(xxx)这一步实例化就会直接触发app.run()阻塞主线程,根本不会把启动逻辑交给子线程调度,后面三个实例的实例化代码永远不会运行。 - 多线程类用法冲突
你的hook_server类已经继承了threading.Thread,不需要再在外层额外套一层threading.Thread,重复包装会导致线程调度逻辑完全失效。 - 额外配置错误:第四个实例的token和第三个重复写为
GW3,后续路由注册会冲突。
修复方案
调整类结构,把Flask服务启动逻辑从构造函数挪到线程类标准的run()方法中,修正启动逻辑即可:
- 修正类定义部分,补全线程类初始化调用,把服务启动逻辑移到
run()方法:
import os import sys import signal import logging import threading from flask import Flask, request, jsonify # Defined by team SNL from Constants import * from Utils import * logger = logging.getLogger(__name__) handler = logging.StreamHandler(stream=sys.stdout) # Parent class for running hook_server (with some preprocessing). class hook_server(threading.Thread): def __init__ (self, token, port): # 调用父类Thread的初始化方法,原代码漏写 super().__init__() # Saved value from gateway response. self.value = [] self.token = token self.port = port # Setup for a Flask application instants. self.server_app = Flask(__name__, static_folder='./media') self.server_app.debug = False # Ignore web-hook server tracking information. log = logging.getLogger('werkzeug') log.disabled = True @self.server_app.route('/' + self.token, methods=['GET', 'POST']) def index1(): # If 'GET' request. if request.method == 'GET': # JSON LONG convert to the dictionary. content = request.args.to_dict() # If 'POST' request. elif request.method == 'POST': # The below conditions are always True. Because we set-up the transmit data type to JSON-LONG. if request.is_json is True: # Read the JSON-LONG data. content = request.get_json() # Received beacon information (dict) per time slot (1 sec). MAX_EXTRACT_LOOP = len(content) # Unwrapping of i-beacon dictionary <dict>. Pkt = 0 for i in range(0, MAX_EXTRACT_LOOP): # <list of list> self.value = list(func_combine(content[i].get('mac'), int(func_kalman_filter(content[i].get('rssi'))), content[i].get('ibeaconTxPower'))) # Get received beacon packet length. Pkt = len(self.value[0]) # Export acquired beacon information from each gateway. if (EXPORT_ACTIVE and Pkt % MAX_PKT == 0): # Save the received beacon information every MAX_Pkt '5000'. func_export(self.token, self.value[0], self.value[1], self.value[2]) del self.value[0][0:-AT_LEAST_INFO] # Save the last received 10 beacon MAC. del self.value[1][0:-AT_LEAST_INFO] # Save the last received 10 beacon RSSI. del self.value[2][0:-AT_LEAST_INFO] # Save the last received 10 beacon TX POWER. # Only remove tuple of list elements. elif (Pkt % MAX_PKT == 0): del self.value[0][0:-AT_LEAST_INFO] # Save the last received 10 beacon MAC. del self.value[1][0:-AT_LEAST_INFO] # Save the last received 10 beacon RSSI. del self.value[2][0:-AT_LEAST_INFO] # Save the last received 10 beacon TX POWER. # Return the binded value <dict of list of list> like that {"GW1":[["AC233FAA45E5"], [-29], [-59]]}. return jsonify({"{}".format(self.token): self.value}) # A child class that inherits and executes hook_server. class runFlaskApp(hook_server): def __init__(self, token, port): # 仅做初始化继承,不在构造函数里启动服务 super().__init__(token, port) # 线程启动后自动执行run方法内的逻辑 def run(self): try: # Where host IP 0.0.0.0 that grants external access. print('[{}] Server open successfully !!'.format(self.token)) self.server_app.run(host='0.0.0.0', port=self.port, use_reloader=False, threaded=True) except: print('[{}] Server occured error !!'.format(self.token)) os.kill(os.getpid(), signal.SIGTERM)
- 修正主函数启动逻辑,去掉重复的线程包装,修正重复的token配置:
if __name__ == '__main__': thread_pool = [] # 直接实例化自定义线程类,不需要额外套threading.Thread G1 = runFlaskApp(token = 'GW1', port = 2998) G2 = runFlaskApp(token = 'GW2', port = 2999) G3 = runFlaskApp(token = 'GW3', port = 3000) G4 = runFlaskApp(token = 'GW4', port = 3001) # 修正之前重复写为GW3的错误 for server in [G1, G2, G3, G4]: server.start() thread_pool.append(server) # 阻塞等待所有服务线程运行 for t in thread_pool: t.join()
排查提示
- 如果修正后仍有实例启动失败,先检查2998-3001端口是否被其他进程占用:Linux/macOS系统执行
netstat -tulpn查看,Windows系统执行netstat -ano查看。 - 调试阶段可以先单独启动单个实例,确认路由、依赖逻辑正常后再批量启动多实例,方便快速定位问题。
- 注意Flask自带的
run()方法仅适合开发调试使用,生产环境多实例部署建议换成gunicorn等专业WSGI服务器,稳定性和并发能力更强。
内容的提问来源于stack exchange,提问作者youngwoo Oh
相关产品推荐
相关产品推荐

