我同意接受的答案
你不能轻易获得 Flask 使用的服务器套接字,因为它隐藏在标准库的内部(Flask 使用 Werkzeug,其开发服务器基于 stdlib 的 BaseHTTPServer)。
但是,我刚刚发现 Werkzeug 在运行werkzeug.serving.BaseWSGIServer 时公开了一个选项来传递文件描述符以用作它的套接字(这就是flask 的run() 函数最终所做的事情)。使用此功能,可以选择一个随机可用端口,然后告诉 Werkzeug 使用它。
这是我的应用程序的启动:
import logging
import pprint
import sys
from flask import Flask
applog = logging.getLogger(__name__)
applog.setLevel(logging.INFO)
app = Flask(__name__)
if __name__ == '__main__':
app.config.from_object('myapp.default_settings')
try:
app.config.from_envvar('MYAPP_SETTINGS')
except RuntimeError:
applog.warning("MYAPP_SETTINGS environment variable not set or invalid. Using default settings.")
# SERVER_NAME should be in the form of localhost:5000, 127.0.0.1:0, etc...
# So, split on : and take the second element as the port
# If the port is 0, we need to pick a random port and then tell the server to use that socket
if app.config['SERVER_NAME'] and int(app.config['SERVER_NAME'].split(':')[1]) == 0:
import socket, os
# Chose a random available port by binding to port 0
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.bind((app.config['SERVER_NAME'].split(':')[0], 0))
sock.listen()
# Tells the underlying WERKZEUG server to use the socket we just created
os.environ['WERKZEUG_SERVER_FD'] = str(sock.fileno())
# Update the configuration so it matches with the port we just chose
# (sock.getsockname will return the actual port being used, not 0)
app.config['SERVER_NAME'] = '%s:%d' % (sock.getsockname())
# Optionally write the current port to a file on the filesystem
if app.config['CREATE_PORT_FILE']:
with open(app.config['PORT_FILE'], 'w') as port_file:
port_file.write(app.config['SERVER_NAME'].split(':')[1])
applog.info("Running app on %s" % (app.config['SERVER_NAME']))
applog.info("Active Settings:%s" % pprint.pformat(app.config, compact=True))
app.run()
myapp.default_settings的内容:
SERVER_NAME = '127.0.0.1:0'
PORT_FILE = 'current_port'
CREATE_PORT_FILE = True
这里重要的一点是设置os.environ['WERKZEUG_SERVER_FD']。 Werkzeug 在run_simple 函数期间查看此环境变量,如果已定义,则将其作为fd 参数传递给make_server 函数。这最终被用作通信的套接字。
虽然我不能保证这种方法的稳定性(我不知道WERKZEUG_SERVER_FD 环境变量的支持程度),但到目前为止,我更喜欢它而不是建议的解决方案,因为:
- 我不必遍历一系列捕获异常的端口,我只需直接从操作系统获取第一个可用端口。
- 在我绑定随机端口和运行应用程序之间,我选择的随机端口不可能被占用,因为我绑定的端口是我的应用程序最终使用的端口。
上面的代码还记录正在使用的端口,并可选择将当前正在使用的端口写入配置选项指定的文件中。