Pekerja di Django yang dijalankan di heroku terhenti di pos ketika klien memiliki koneksi yang tidak stabil

Kami menjalankan server Django/gunicorn di heroku. Sebagian besar pengguna kami berada di negara yang jaringan selulernya tidak terlalu bagus, sehingga sering kali koneksi mereka tidak stabil.

Sebagian besar permintaan kami adalah "postingan mentah" dari perangkat seluler dan tampaknya meskipun permintaan POST tidak sepenuhnya dikirimkan, permintaan tersebut sudah dikirim untuk ditangani oleh pekerja gunicorn. Ketika pekerja mencoba memproses permintaan dan membaca data, pekerja tersebut hanya hang menunggu data yang tersisa. Meskipun perilaku ini masuk akal untuk membaca data file/gambar dalam mode "streaming", hal ini tidak masuk akal dalam kasus kami karena semua postingan kami relatif kecil dan dapat dengan mudah dibaca oleh server web secara keseluruhan dan baru kemudian diteruskan ke gunicorn kami. pekerja.

Penyerahan awal ini menyebabkan masalah ketika kita mempunyai banyak permintaan seperti itu secara paralel - karena semua pekerja mungkin diblokir. Saat ini kami memecahkan masalah tersebut dengan menambah jumlah pekerja/dyno namun biayanya cukup mahal. Saya tidak dapat menemukan cara untuk memaksa server web atau gunicorn menunggu dan hanya meneruskan permintaan ke pekerja setelah dikirimkan sepenuhnya.

Apakah ada cara untuk membuat server web/gunicorn heroku hanya mentransfer permintaan ke pekerja gunicorn ketika permintaan tersebut telah dikirim sepenuhnya dari sisi klien (diterima sepenuhnya oleh server)?

Beberapa contoh kode (kami telah menambahkan pelacakan 'per-instruksi' newrelic untuk memastikan bahwa ini adalah baris yang tepat yang menyebabkan masalah):

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)

Berikut adalah pengukuran New Relic untuk contoh permintaan lambat ini (itu adalah POST dengan data 7K). Membaca POST membutuhkan 99% waktu metode....

masukkan deskripsi gambar di sini


person Jarek Potiuk    schedule 30.08.2012    source sumber
comment
Menurut saya batas waktu heroku adalah 10 detik. Saya tidak mengujinya, tapi mungkin Anda dapat mengubah batas waktu gunicorn di Procfile: web: gunicorn hellodjango.wsgi -t 3 -b 0.0.0.0:$PORT   -  person dani herrera    schedule 30.08.2012
comment
Ini 30 detik secara default. Kami sudah menetapkan batas waktu di gunicorn ... tetapi masalahnya adalah dalam hal ini saya akan membatalkan permintaan yang jika tidak (meskipun setelah 20 detik) akan berhasil, saya tidak benar-benar ingin melakukannya. Apa yang ingin saya lakukan adalah tidak memblokir pekerja saya saat permintaan dikirimkan melalui jaringan.   -  person Jarek Potiuk    schedule 30.08.2012
comment
Aku bingung dengan postinganmu. Ketika Anda berbicara tentang pekerja, Anda berbicara tentang 'web dynos' atau tentang 'pekerja dynos'? 'Pekerja dyno' tidak boleh dikunci saat data ditransfer karena 'web dyno' yang mengumpulkan data. Bukan itu? (Juga, tolong, Maukah Anda memberi tahu saya negara apa yang lambat itu? )   -  person dani herrera    schedule 30.08.2012
comment
Saya berbicara tentang pekerja gunicorn. Ada beberapa dyno dan pada setiap dyno satu gunicorn berjalan tetapi biasanya memunculkan beberapa pekerja dengan opsi -w ‹workers›: konfigurasi gunicorn. Dalam kasus saya, saya mengaturnya ke -w 12 sekarang. Itu adalah proses yang terpisah (anak-anak gunicorn). Setiap permintaan yang disalurkan ke dyno tertentu disalurkan melalui gunicorn ke salah satu pekerjanya. Dan tampaknya pekerja setelah menerima permintaan untuk menangani ditahan membaca data (saat membaca properti raw_post_data).   -  person Jarek Potiuk    schedule 31.08.2012
comment
Mungkin Anda dapat menyetel alarm sebelum penugasan data posting mentah dan menonaktifkannya di akhir penugasan. docs.python.org/library/signal.html#example . Sinyal batas waktu adalah sinyal SIGTERM sederhana.   -  person dani herrera    schedule 31.08.2012
comment
Tidak, itu tidak akan berhasil. Permintaan sudah dimatikan setelah 30 detik oleh gunicorn (saya memiliki batas waktu yang disetel ke 30 detik - sama dengan batas waktu heroku). Masalahnya adalah saya tidak ingin hang sama sekali - saya lebih suka gunicorn atau - lebih baik - server web (nginx rupanya) di heroku untuk menampung permintaan, karena mereka jauh lebih cocok untuk itu - dalam kasus saya keseluruhan Proses python dengan xx MB RAM dibiarkan menganggur selama 30 detik. Jika permintaan ini ditahan hingga siap di gunicorn atau nginx, itu akan jauh lebih efisien...   -  person Jarek Potiuk    schedule 31.08.2012
comment
Hai @JarekPotiuk, sudahkah Anda memperbaiki masalah Anda?   -  person dani herrera    schedule 12.09.2012
comment
Tidak - ini belum diperbaiki. Ini masih diatasi seperti dijelaskan di atas.   -  person Jarek Potiuk    schedule 13.09.2012


Jawaban (2)


Menurut saya, masalah sebenarnya di sini adalah gunicorn memblokir. Ini karena gunicorn (secara default) menggunakan pekerja sinkron untuk menjalankan tugas Anda. Ini berarti bahwa ketika permintaan web mengenai gunicorn, permintaan tersebut akan diblokir hingga memberikan respons--dalam kasus Anda, waktu yang lama.

Untuk mengatasi masalah ini, Anda dapat menggunakan gevent dengan gunicorn untuk melakukan IO non-pemblokiran. Karena sebagian besar waktu Anda dihabiskan untuk melakukan hal-hal IO, ini akan memastikan gunicorn dapat menangani lebih banyak permintaan web secara paralel.

Untuk menggunakan gevent dengan gunicorn Anda perlu menginstal gevent (pip install -U gevent), dan mengubah perintah startup gunicorn Anda dengan menambahkan: gunicorn -k gevent (ini akan memberitahu gunicorn untuk menggunakan gevent sebagai pekerja).

person rdegges    schedule 18.09.2012
comment
Memang ini arah yang benar (tetapi tidak persis), tetapi menempuh rute itu akan membuat aplikasi menjadi lebih kompleks. Saya harus memisahkan proses membaca permintaan (jadi mendapatkan data posting dari permintaan) dari pemrosesan permintaan. Hal ini akan memungkinkan untuk menjalankan lebih banyak thread pembacaan paralel (tidak memerlukan terlalu banyak memori) dan sejumlah kecil proses pemrosesan. Pengaturan default yang Anda usulkan tidak akan menyediakan hal itu, karena pada saat saya memiliki proses yang sama melakukan membaca dari posting dan memprosesnya - saya memiliki masalah yang sama (saya tidak dapat menambah jumlah proses terlalu banyak) - person Jarek Potiuk; 29.12.2012

Anda mungkin ingin membaca artikel ini dan selidiki permintaan buffering server HTTP seperti Waitress.

person grokpot    schedule 16.03.2015