คนงานใน django ทำงานบน heroku ค้างอยู่บนโพสต์เมื่อไคลเอนต์มีการเชื่อมต่อที่ไม่สม่ำเสมอ

เรากำลังเรียกใช้เซิร์ฟเวอร์ django/gunicorn บน heroku ผู้ใช้ส่วนใหญ่ของเราอยู่ในประเทศที่เครือข่ายมือถือไม่ค่อยดีนัก ดังนั้นบ่อยครั้งที่พวกเขามีการเชื่อมต่อที่ไม่สม่ำเสมอ

คำขอส่วนใหญ่ของเราเป็น "การโพสต์ดิบ" จากอุปกรณ์มือถือ และดูเหมือนว่าแม้ว่าคำขอ POST จะไม่ได้ส่งอย่างสมบูรณ์ คำขอก็ถูกส่งออกไปเพื่อให้คนงานของ gunicorn จัดการแล้ว เมื่อผู้ปฏิบัติงานพยายามประมวลผลคำขอและอ่านข้อมูล พนักงานก็จะค้างเพื่อรอข้อมูลที่เหลือ แม้ว่าพฤติกรรมนี้เหมาะสมสำหรับการอ่านไฟล์/ข้อมูลรูปภาพในโหมด "สตรีมมิ่ง" แต่ก็ไม่สมเหตุสมผลในกรณีของเรา เนื่องจากโพสต์ทั้งหมดของเรามีขนาดค่อนข้างเล็กและเว็บเซิร์ฟเวอร์โดยรวมสามารถอ่านได้ง่าย จากนั้นจึงส่งต่อไปยัง gunicorn ของเราเท่านั้น คนงาน

การแฮนด์ออฟก่อนกำหนดนี้ทำให้เกิดปัญหาเมื่อเรามีคำขอดังกล่าวจำนวนมากพร้อมกัน เนื่องจากผู้ปฏิบัติงานทั้งหมดอาจถูกบล็อก ขณะนี้เราแก้ไขปัญหาด้วยการเพิ่มจำนวนคนงาน/ไดโน แต่มีค่าใช้จ่ายค่อนข้างสูง ฉันไม่พบวิธีใดที่จะบังคับให้เว็บเซิร์ฟเวอร์หรือ gunicorn รอและส่งต่อคำขอไปยังผู้ปฏิบัติงานเมื่อมีการส่งโดยสมบูรณ์เท่านั้น

มีวิธีที่จะทำให้เว็บเซิร์ฟเวอร์ / gunicorn ของ heroku โอนคำขอไปยังคนงาน gunicorn เท่านั้นเมื่อมีการส่งจากฝั่งไคลเอ็นต์อย่างสมบูรณ์ (เซิร์ฟเวอร์ได้รับอย่างเต็มที่)

โค้ดตัวอย่างบางส่วน (เราได้เพิ่มการติดตาม 'ตามคำสั่ง' newrelic เพื่อให้แน่ใจว่านี่คือบรรทัดที่แน่นอนที่ทำให้เกิดปัญหา):

def syncGameState(request):
    transaction = agent.current_transaction()
    with agent.FunctionTrace(transaction, "syncGameState_raw_post_data", 'Python/EndPoint'):
        data = request.raw_post_data
    with agent.FunctionTrace(transaction, "syncGameState_gameStateSyncRequest", 'Python/EndPoint'):
        sync_request = sync_pb2.gameStateSyncRequest()
    with agent.FunctionTrace(transaction, "syncGameState_ParseFromString", 'Python/EndPoint'):
        sync_request.ParseFromString(data)

นี่คือการวัด New Relic สำหรับตัวอย่างคำขอที่ช้านี้ (เป็น POST ที่มีข้อมูล 7K) การอ่าน POST ใช้เวลา 99% ของวิธีการ....

ป้อนคำอธิบายรูปภาพที่นี่


person Jarek Potiuk    schedule 30.08.2012    source แหล่งที่มา
comment
ฉันคิดว่าการหมดเวลาของ heroku คือ 10 วินาที ฉันไม่ได้ทดสอบ แต่บางทีคุณอาจเปลี่ยน การหมดเวลา gunicorn ใน Procfile: web: gunicorn hellodjango.wsgi -t 3 -b 0.0.0.0:$PORT   -  person dani herrera    schedule 30.08.2012
comment
โดยค่าเริ่มต้นจะเป็น 30 วินาที เราได้กำหนดเวลาหมดเวลาใน gunicorn แล้ว ... แต่ประเด็นก็คือในกรณีนี้ ฉันจะยกเลิกคำขอ ซึ่งไม่เช่นนั้น (แม้ว่าจะผ่านไป 20 วินาที) ก็จะสำเร็จ แต่ฉันก็ไม่อยากทำเลย สิ่งที่ฉันต้องการทำคือไม่ปิดกั้นพนักงานของฉันในขณะที่คำขอถูกส่งผ่านเครือข่าย   -  person Jarek Potiuk    schedule 30.08.2012
comment
ฉันสับสนกับข้อความของคุณ เมื่อคุณพูดถึงคนงาน คุณพูดถึง 'web dynos' หรือเกี่ยวกับ 'worker dynos'? ไม่ควรล็อก 'Worker dynos' ขณะถ่ายโอนข้อมูล เนื่องจาก 'web dyno' เป็นผู้รวบรวมข้อมูล มันไม่ใช่? (กรุณาช่วยบอกฉันหน่อยได้ไหมว่าประเทศที่เชื่องช้านั้นคืออะไร? )   -  person dani herrera    schedule 30.08.2012
comment
ฉันกำลังพูดถึงคนงานกูนิคอร์น มีไดโนหลายตัวและบนไดโนแต่ละตัวมี gunicorn ตัวเดียวทำงานอยู่ แต่โดยปกติแล้วมันจะสร้างคนงานหลายคนด้วยตัวเลือก -w ‹workers›: การกำหนดค่า gunicorn ในกรณีของฉัน ตอนนี้ฉันตั้งค่าเป็น -w 12 แล้ว นั่นเป็นกระบวนการที่แยกจากกัน (ลูกของกูนิคอร์น) ทุกคำขอที่ถูกส่งไปยัง dyno โดยเฉพาะจะถูกส่งผ่าน gunicorn ไปยังหนึ่งในผู้ปฏิบัติงาน และดูเหมือนว่าผู้ปฏิบัติงานหลังจากได้รับคำขอให้จัดการจะถูกระงับการอ่านข้อมูล (ในขณะที่อ่านคุณสมบัติ raw_post_data)   -  person Jarek Potiuk    schedule 31.08.2012
comment
บางทีคุณอาจตั้งค่าการเตือนก่อนกำหนดข้อมูลโพสต์ดิบและปิดใช้งานเมื่อสิ้นสุดการกำหนดได้ docs.python.org/library/signal.html#example สัญญาณหมดเวลาเป็นสัญญาณ SIGTERM แบบธรรมดา   -  person dani herrera    schedule 31.08.2012
comment
ไม่นั่นจะไม่ทำงาน คำขอถูกฆ่าแล้วหลังจากผ่านไป 30 วินาทีโดย gunicorn (ฉันตั้งค่าการหมดเวลาไว้ที่ 30 วินาที - เช่นเดียวกับการหมดเวลาของ heroku) ประเด็นก็คือฉันไม่ต้องการให้แฮงค์เลย - ฉันอยากได้ gunicorn หรือ - ดีกว่า - เว็บเซิร์ฟเวอร์ (เห็นได้ชัดว่า nginx) บน heroku เพื่อระงับคำขอเนื่องจากพวกมันเหมาะสมกว่ามากสำหรับสิ่งนั้น - ในกรณีของฉันทั้งหมด กระบวนการ python ที่มี RAM ขนาด xx MB ถูกพักไว้เป็นเวลา 30 วินาที หากคำขอนี้ถูกระงับจนกว่าจะพร้อมใน gunicorn หรือใน nginx นั่นจะมีประสิทธิภาพมากกว่ามาก...   -  person Jarek Potiuk    schedule 31.08.2012
comment
สวัสดี @JarekPotiuk คุณได้แก้ไขปัญหาของคุณแล้วหรือยัง?   -  person dani herrera    schedule 12.09.2012
comment
ไม่ มันยังไม่ได้รับการแก้ไข ยังคงได้รับการแก้ไขตามที่อธิบายไว้ข้างต้น   -  person Jarek Potiuk    schedule 13.09.2012


คำตอบ (2)


สำหรับฉันดูเหมือนว่าปัญหาที่แท้จริงที่นี่คือ gunicorn กำลังบล็อกอยู่ นี่เป็นเพราะว่า gunicorn (โดยค่าเริ่มต้น) ใช้คนงานแบบซิงโครนัสเพื่อรันงานของคุณ ซึ่งหมายความว่าเมื่อคำขอของเว็บไปถึง gunicorn มันจะบล็อกจนกว่าจะมีการตอบกลับ ในกรณีของคุณ มันจะใช้เวลานาน

เพื่อแก้ไขปัญหานี้ คุณสามารถใช้ gevent กับ gunicorn เพื่อทำ IO แบบไม่บล็อกได้ เนื่องจากคุณใช้เวลาส่วนใหญ่ไปกับการทำ IO สิ่งนี้จะช่วยให้ gunicorn สามารถรองรับคำขอเว็บได้มากขึ้นในแบบคู่ขนาน

หากต้องการใช้ gevent กับ gunicorn คุณต้องติดตั้ง gevent (pip install -U gevent) และเปลี่ยนคำสั่งเริ่มต้น gunicorn ของคุณโดยเพิ่ม: gunicorn -k gevent (ซึ่งจะเป็นการบอกให้ gunicorn ใช้ gevent เป็นผู้ปฏิบัติงาน)

person rdegges    schedule 18.09.2012
comment
ฉันมาถูกทางแล้ว (แต่ไม่ตรงทั้งหมด) แต่การไปตามเส้นทางนั้นจะทำให้แอปซับซ้อนมากขึ้น ฉันจะต้องแยกกระบวนการอ่านคำขอ (เพื่อรับข้อมูลโพสต์จากคำขอ) ออกจากการประมวลผลคำขอ สิ่งนี้จะช่วยให้สามารถรันเธรดการอ่านแบบขนานได้มากขึ้น (ไม่ต้องการหน่วยความจำมากเกินไป) และกระบวนการประมวลผลจำนวนน้อยลง การตั้งค่าเริ่มต้นที่คุณเสนอจะไม่ให้สิ่งนั้นจริงๆ เพราะในขณะที่ฉันมีกระบวนการเดียวกันในการอ่านจากการโพสต์และประมวลผล - ฉันมีปัญหาเดียวกัน (ฉันไม่สามารถเพิ่มจำนวนกระบวนการได้มากเกินไป) - person Jarek Potiuk; 29.12.2012

คุณอาจต้องการอ่านบทความนี้ และ ตรวจสอบคำขอบัฟเฟอร์เซิร์ฟเวอร์ HTTP เช่น Waitress

person grokpot    schedule 16.03.2015