Tại sao chạy máy chủ nhà phát triển Flask lại tự chạy hai lần?


106

Tôi đang sử dụng Flask để phát triển một trang web và trong khi phát triển, tôi chạy flask bằng cách sử dụng tệp sau:

#!/usr/bin/env python
from datetime import datetime
from app import app
import config

if __name__ == '__main__':
    print '################### Restarting @', datetime.utcnow(), '###################'
    app.run(port=4004, debug=config.DEBUG, host='0.0.0.0')

Khi tôi khởi động máy chủ hoặc khi nó tự động khởi động lại vì các tệp đã được cập nhật, nó luôn hiển thị dòng in hai lần:

################### Restarting @ 2014-08-26 10:51:49.167062 ###################
################### Restarting @ 2014-08-26 10:51:49.607096 ###################

Mặc dù nó không thực sự là một vấn đề (phần còn lại hoạt động như mong đợi), tôi chỉ đơn giản là tự hỏi tại sao nó lại hoạt động như thế này? Bất kỳ ý tưởng?

Câu trả lời:


152

Trình tải lại Werkzeug tạo ra một quy trình con để nó có thể khởi động lại quy trình đó mỗi khi mã của bạn thay đổi. Werkzeug là thư viện cung cấp Flask cho máy chủ phát triển khi bạn gọi app.run().

Xem restart_with_reloader()mã chức năng ; tập lệnh của bạn được chạy lại với subprocess.call().

Nếu bạn đặt use_reloaderthành, Falsebạn sẽ thấy hành vi biến mất, nhưng sau đó bạn cũng mất chức năng tải lại:

app.run(port=4004, debug=config.DEBUG, host='0.0.0.0', use_reloader=False)

Bạn cũng có thể tắt trình tải lại khi sử dụng flask runlệnh:

FLASK_DEBUG=1 flask run --no-reload

Bạn có thể tìm kiếm WERKZEUG_RUN_MAINbiến môi trường nếu bạn muốn phát hiện khi nào bạn đang trong quá trình tải lại con:

import os
if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    print '################### Restarting @ {} ###################'.format(
        datetime.utcnow())

Tuy nhiên, nếu bạn cần thiết lập các hình cầu mô-đun, thì thay vào đó, bạn nên sử dụng trình @app.before_first_requesttrang trí trên một hàm và để hàm đó thiết lập các hình cầu như vậy. Nó sẽ được gọi chỉ một lần sau mỗi lần tải lại khi có yêu cầu đầu tiên:

@app.before_first_request
def before_first_request():
    print '########### Restarted, first request @ {} ############'.format(
        datetime.utcnow())

Hãy lưu ý rằng nếu bạn chạy điều này trong một máy chủ WSGI quy mô đầy đủ sử dụng phân nhánh hoặc các quy trình con mới để xử lý các yêu cầu, thì before_first_requesttrình xử lý đó có thể được gọi cho mỗi quy trình con mới.


2
À được rồi. Cảm ơn vì lời giải thích! Vì vậy, nó được coi là hành vi bình thường? Ít nhất tốt mà sai gì với mã của tôi .. :)
kramer65

1
@ kramer65: đó là hành vi hoàn toàn bình thường và được mong đợi. :-)
Martijn Pieters

1
Có cách nào thực tế để chạy mã khởi tạo chậm chỉ một lần, đảm bảo rằng nó cũng được gọi khi chạy dưới wsgi (tức là không phải từ app.run), nhưng không phải đợi yêu cầu đầu tiên? Tôi không muốn yêu cầu đầu tiên đó bị gánh nặng với chi phí khởi tạo.
Kylotan

1
@Kylotan: bạn phải kiểm tra môi trường; nếu bạn chỉ đặt DEBUG khi chạy trong quá trình phát triển, bạn có thể tìm kiếm WERKZEUG_RUN_MAINbiến môi trường và chỉ chạy mã của bạn khi DEBUGsai hoặc WERKZEUG_RUN_MAINđược đặt, chẳng hạn. Có một chút tẻ nhạt.
Martijn Pieters

Chỉ để làm rõ, tôi nghĩ "chức năng tải lại" có nghĩa là phản ứng (sẽ đánh bại toàn bộ mục đích sử dụng dashđối với tôi). Đối với bất kỳ người nào khác noobsnhư tôi, điều này chỉ có nghĩa là chức năng chỉnh sửa / lưu tệp kích hoạt cập nhật trực tiếp.
Hendy

12

Nếu bạn đang sử dụng flask runlệnh hiện đại , không có tùy chọn app.runnào được sử dụng. Để tắt hoàn toàn trình tải lại, hãy chuyển --no-reload:

FLASK_DEBUG=1 flask run --no-reload

Ngoài ra, __name__ == '__main__'sẽ không bao giờ đúng vì ứng dụng không được thực thi trực tiếp. Sử dụng các ý tưởng tương tự từ câu trả lời của Martijn , ngoại trừ không có __main__khối.

if os.environ.get('WERKZEUG_RUN_MAIN') != 'true':
    # do something only once, before the reloader

if os.environ.get('WERKZEUG_RUN_MAIN') == 'true':
    # do something each reload

7

Tôi đã gặp vấn đề tương tự và tôi đã giải quyết nó bằng cách đặt app.debugthành False. Đặt nó thành Truekhiến tôi __name__ == "__main__"bị gọi hai lần.


My __main__vẫn chạy hai lần với cả hai app.debug = Falseapp.run_server(debug=False). Bạn có chắc điều đó đã làm cho bạn hay bạn có thể đăng một số mã có thể tái tạo để thử không?
Hendy

Thay đổi app.debug là tất cả những gì tôi đã làm để giải quyết nó cho tôi. Bạn có thể xác nhận rằng main chỉ chạy hai lần khi máy chủ bình được khởi động không? Hãy thử chạy một ví dụ làm việc tối thiểu và xem sự cố có xảy ra không. Ngoài ra, hãy thử chạy một ví dụ tối thiểu bị lỗi trong nhiều phiên bản python, đó có thể là một vấn đề. Kể từ đó, tôi đã di chuyển dự án của mình sang Java và SparkJava thay vì python và flask, vì vậy tôi không nhớ chính xác điều gì đã khắc phục sự cố.
Carvell Wakeman,

Tôi đang sử dụng flaskqua plotly dashvà phát hiện ra rằng gần đây họ đã thay đổi debug đối số mặc định được chuyển đến flask. Tôi sẽ đoán rằng tôi đã nhầm lẫn ở trên và có lẽ đã làm app.debug=False(điều này có thể bị ghi đè bởi args mặc định thành run_server), hoặc chỉ thử mà không vượt qua True, không thiết lập rõ ràng như được hiển thị ở trên. Điều này đang hoạt động chính xác đối với tôi bây giờ (đảm bảo rằng debug=False). Cảm ơn!
Hendy

2

Từ Flask 0.11, bạn nên chạy ứng dụng của mình flask runhơn là chạy python application.py. Sử dụng cái sau có thể dẫn đến việc chạy mã của bạn hai lần.

Như đã nêu ở đây :

... từ Bình 0.11 trở đi, phương pháp bình được khuyến nghị. Lý do cho điều này là do cách cơ chế tải lại hoạt động, có một số tác dụng phụ kỳ lạ (như thực thi một số mã nhất định hai lần ...)


0

Một trong những lý do có thể khiến ứng dụng Flask tự chạy hai lần là do cấu hình WEB_CONCURRENCYcài đặt trên Heroku. Để đặt thành một, bạn có thể viết trong bảng điều khiển heroku config:set WEB_CONCURRENCY=1


-1

Một quan sát về chủ đề

Điều này đặc biệt khó chịu khi ứng dụng của bạn sử dụng các luồng vì chúng sẽ được kích hoạt hai lần khi khởi động. Theo như tôi đã thử các đĩa đơn cũng không khắc phục được điều này (điều này thật đáng ngạc nhiên). Tuy nhiên, thêm độ trễ ban đầu vài giây trước khi chuỗi của bạn bắt đầu có thể giải quyết vấn đề.

Nếu ứng dụng khởi động lại nhanh hơn trước khi thời gian trì hoãn của bạn vượt quá thì chuỗi đã cho chỉ được tạo một lần sau khi khởi động lại.


@Dowvoter: quan tâm giải thích tại sao?
pfabri

-1

Tôi gặp vấn đề tương tự. Tôi đã giải quyết nó bằng cách sửa đổi main của mình và chèn use_reloader = False vào nó. Nếu bất kỳ cơ quan nào ở đây đang tìm cách giải quyết vấn đề này thì mã bên dưới sẽ giúp bạn bắt đầu, tuy nhiên, chức năng của các thay đổi trong mã sẽ được tự động phát hiện và khởi động lại ứng dụng sẽ không hoạt động. Bạn sẽ phải dừng và khởi động lại ứng dụng của mình theo cách thủ công sau mỗi lần chỉnh sửa mã.

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.