เหตุใดเซิร์ฟเวอร์ Flask dev จึงรันสองครั้ง


111

ฉันใช้Flaskเพื่อพัฒนาเว็บไซต์และในระหว่างการพัฒนาฉันเรียกใช้ flask โดยใช้ไฟล์ต่อไปนี้:

#!/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')

เมื่อฉันเริ่มเซิร์ฟเวอร์หรือเมื่อรีสตาร์ทอัตโนมัติเนื่องจากไฟล์ได้รับการอัปเดตมันจะแสดงบรรทัดการพิมพ์สองครั้งเสมอ:

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

แม้ว่ามันจะไม่ใช่ปัญหาจริงๆ (ส่วนที่เหลือทำงานได้ตามที่คาดไว้) แต่ฉันก็สงสัยว่าทำไมมันถึงเป็นแบบนี้? ความคิดใด ๆ ?

คำตอบ:


162

ตัวโหลดซ้ำ Werkzeug สร้างกระบวนการลูกเพื่อให้สามารถเริ่มกระบวนการนั้นใหม่ทุกครั้งที่โค้ดของคุณเปลี่ยนไป Werkzeug app.run()เป็นห้องสมุดที่ให้ขวดกับเซิร์ฟเวอร์การพัฒนาเมื่อคุณเรียก

ดูrestart_with_reloader()รหัสฟังก์ชัน ; สคริปต์ของคุณทำงานอีกครั้งด้วยsubprocess.call()ไฟล์.

หากคุณตั้งค่าuse_reloaderเป็นFalseคุณจะเห็นพฤติกรรมหายไป แต่คุณจะสูญเสียฟังก์ชันการโหลดซ้ำ:

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

คุณสามารถปิดใช้งานตัวโหลดซ้ำได้เมื่อใช้flask runคำสั่งเช่นกัน:

FLASK_DEBUG=1 flask run --no-reload

คุณสามารถค้นหาWERKZEUG_RUN_MAINตัวแปรสภาพแวดล้อมหากคุณต้องการตรวจจับเมื่อคุณอยู่ในกระบวนการลูกรีโหลด:

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

อย่างไรก็ตามหากคุณต้องการตั้งค่าโมดูล globals คุณควรใช้@app.before_first_requestมัณฑนากรกับฟังก์ชันแทนและตั้งค่าฟังก์ชันดังกล่าว จะถูกเรียกเพียงครั้งเดียวหลังจากโหลดซ้ำทุกครั้งเมื่อมีคำขอแรกเข้ามา:

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

ทำคำนึงว่าถ้าคุณทำงานนี้ในเซิร์ฟเวอร์ WSGI เต็มรูปแบบที่ใช้ฟอร์กหรือกระบวนการย่อยใหม่ที่จะร้องขอจับว่าbefore_first_requestรถขนอาจถูกเรียกสำหรับแต่ละกระบวนการย่อยใหม่


2
อาโอเค. ขอบคุณสำหรับคำอธิบาย! มันถือว่าเป็นพฤติกรรมปกติ? อย่างน้อยก็ยังดีที่ไม่มีอะไรผิดพลาดกับรหัสของฉัน .. :)
kramer65

1
@ kramer65: เป็นพฤติกรรมปกติและคาดหวัง :-)
Martijn Pieters

1
มีวิธีปฏิบัติในการเรียกใช้รหัสเริ่มต้นที่ช้าเพียงครั้งเดียวเพื่อให้แน่ใจว่าได้รับการเรียกเมื่อทำงานภายใต้ wsgi (เช่นไม่ใช่จากapp.run) แต่ไม่รอคำขอแรก ฉันไม่ต้องการให้คำขอแรกนั้นต้องรับภาระค่าใช้จ่ายในการเริ่มต้น
Kylotan

1
@ ไคโลตัน: คุณต้องตรวจสอบสภาพแวดล้อม หากคุณตั้งค่าการแก้ปัญหาเฉพาะเมื่อทำงานในการพัฒนาคุณสามารถมองหาWERKZEUG_RUN_MAINตัวแปรสภาพแวดล้อมและเรียกใช้โค้ดของคุณเมื่อDEBUGเป็นเท็จหรือWERKZEUG_RUN_MAINตั้งค่าไว้เท่านั้น ค่อนข้างน่าเบื่อ
Martijn Pieters

เพื่อชี้แจงฉันคิดว่า "ฟังก์ชันการโหลดซ้ำ" หมายถึงการเกิดปฏิกิริยา (ซึ่งจะทำลายวัตถุประสงค์ทั้งหมดของการใช้งานdashสำหรับฉัน) สำหรับคนอื่น ๆ ที่noobsเหมือนตัวเองนี่หมายถึงเฉพาะฟังก์ชันการแก้ไข / บันทึกไฟล์ที่ทำให้เกิดการอัปเดตแบบสด
Hendy

13

หากคุณใช้งานที่ทันสมัย flask runคำสั่งไม่มีการใช้ตัวเลือกapp.runใด ๆ หากต้องการปิดใช้งานตัวโหลดซ้ำโดยสมบูรณ์ให้ส่งผ่าน--no-reload:

FLASK_DEBUG=1 flask run --no-reload

นอกจากนี้__name__ == '__main__'จะไม่เป็นจริงเนื่องจากแอปไม่ได้ทำงานโดยตรง ใช้แนวคิดเดียวกันจากคำตอบของ Martijnยกเว้นไม่ต้อง__main__ปิดกั้น

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

8

ฉันมีปัญหาเดียวกันและฉันแก้ไขได้โดยตั้งค่าapp.debugเป็นFalse. การตั้งค่าTrueเป็นทำให้ฉัน__name__ == "__main__"ถูกเรียกสองครั้ง


ของฉัน__main__ยังคงทำงานสองครั้งด้วยทั้งสองapp.debug = Falseและapp.run_server(debug=False). แน่ใจหรือว่าทำเพื่อคุณหรือโพสต์โค้ดที่ทำซ้ำได้เพื่อลองใช้
Hendy

การเปลี่ยน app.debug คือทั้งหมดที่ฉันทำเพื่อแก้ปัญหาให้ฉัน คุณสามารถยืนยันได้หรือไม่ว่า main ทำงานเพียงสองครั้งเมื่อเซิร์ฟเวอร์ขวดเริ่มทำงาน ลองเรียกใช้ตัวอย่างการทำงานขั้นต่ำและดูว่าปัญหาเกิดขึ้นหรือไม่ ลองเรียกใช้ตัวอย่างเล็กน้อยที่ล้มเหลวใน python หลายเวอร์ชันซึ่งอาจเป็นปัญหา ตั้งแต่นั้นฉันได้ย้ายโปรเจ็กต์ของฉันไปที่ Java และ SparkJava แทนที่จะเป็น python และ flask ดังนั้นฉันจำไม่ได้ว่าอะไรที่แก้ไขปัญหาได้
Carvell Wakeman

ฉันใช้flaskผ่านplotly dashและพบว่าพวกเขาเมื่อเร็ว ๆ นี้มีการเปลี่ยนแปลงเริ่มต้น อาร์กิวเมนต์ส่งผ่านไปยังdebug flaskฉันจะเดาว่าฉันเข้าใจผิดข้างต้นและอาจจะทำapp.debug=False(ซึ่งอาจถูกแทนที่โดยอาร์กิวเมนต์เริ่มต้นrun_server) หรือพยายามเพียงอย่างเดียวโดยไม่ผ่านTrueไม่ใช่การตั้งค่าอย่างชัดเจนดังที่แสดงด้านบน ตอนนี้กำลังทำงานอย่างถูกต้องสำหรับฉัน (ตรวจสอบให้แน่ใจdebug=False) ขอบคุณ!
Hendy

2

จากขวด 0.11 ก็แนะนำให้ใช้ app ของคุณด้วยมากกว่าflask run python application.pyการใช้อย่างหลังอาจส่งผลให้โค้ดของคุณทำงานสองครั้ง

ตามที่ระบุไว้ที่นี่ :

... ตั้งแต่ขวด 0.11 เป็นต้นไปขอแนะนำให้ใช้วิธีการขวด สาเหตุนี้เป็นเพราะกลไกการรีโหลดมีผลข้างเคียงที่แปลกประหลาดบางอย่าง (เช่นการเรียกใช้รหัสบางอย่างสองครั้ง ... )


0

สาเหตุหนึ่งที่เป็นไปได้ว่าทำไมแอป Flask จึงทำงานสองครั้งคือการกำหนดค่าWEB_CONCURRENCYการตั้งค่าบน Heroku ในการตั้งค่าเป็นหนึ่งคุณสามารถเขียนในคอนโซล heroku config:set WEB_CONCURRENCY=1


-1

ฉันมีปัญหาเดียวกัน ฉันแก้ไขโดยแก้ไข main ของฉันและใส่ use_reloader = False เข้าไป หากร่างกายใด ๆ กำลังมองหาวิธีแก้ปัญหาสำหรับปัญหานี้จากนั้นโค้ดด้านล่างจะช่วยให้คุณเริ่มต้นได้อย่างไรก็ตามคุณจะตรวจพบการทำงานของการเปลี่ยนแปลงในโค้ดโดยอัตโนมัติและการรีสตาร์ทแอปพลิเคชันจะไม่ทำงาน คุณจะต้องหยุดและรีสตาร์ทแอปพลิเคชันของคุณด้วยตนเองหลังจากแก้ไขโค้ดแต่ละครั้ง

if __name__ == '__main__':
    app.run(debug=True, use_reloader=False)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.