Nginx proxy_read_timeout vs. proxy_connect_timeout

15

Saya sudah mulai menggunakan Nginx sebagai proxy terbalik untuk satu set server yang menyediakan semacam layanan.

Layanan bisa agak lambat di kali (berjalan di Jawa dan JVM kadang-kadang terjebak dalam "pengumpulan sampah penuh" yang mungkin memakan waktu beberapa detik), jadi saya telah mengatur proxy_connect_timeoutke 2 detik, yang akan memberi Nginx cukup waktu untuk mencari mengetahui bahwa layanan macet di GC dan tidak akan merespons dalam waktu, dan harus meneruskan permintaan ke server yang berbeda.

Saya juga telah mengatur proxy_read_timeoutuntuk mencegah proksi terbalik macet jika layanan itu sendiri membutuhkan terlalu banyak waktu untuk menghitung respons - sekali lagi, itu harus memindahkan permintaan ke server lain yang seharusnya cukup bebas untuk mengembalikan respons tepat waktu.

Saya telah menjalankan beberapa tolok ukur dan saya dapat melihat dengan jelas bahwa beberapa proxy_connect_timeoutberfungsi dengan baik karena beberapa permintaan kembali tepat pada waktu yang ditentukan untuk waktu habis koneksi, karena layanan macet dan tidak menerima koneksi masuk (layanan menggunakan Jetty sebagai tertanam wadah servlet). Ini proxy_read_timeoutjuga berfungsi, karena saya dapat melihat permintaan yang kembali setelah batas waktu yang ditentukan di sana.

Masalahnya adalah bahwa saya akan mengharapkan untuk melihat beberapa permintaan yang habis setelah itu proxy_read_timeout + proxy_connect_timeout, atau hampir sepanjang waktu itu, jika layanan macet dan tidak akan menerima koneksi ketika Nginx mencoba mengaksesnya, tetapi sebelum Nginx dapat kehabisan waktu - itu akan dirilis dan mulai memproses, tetapi terlalu lambat dan Nginx akan dibatalkan karena batas waktu baca. Saya percaya bahwa layanan memiliki kasus seperti itu, tetapi setelah menjalankan beberapa tolok ukur, total jutaan permintaan - saya gagal melihat satu permintaan yang kembali dalam apa pun di atas proxy_read_timeout(yang merupakan batas waktu lebih besar).

Saya akan sangat menghargai komentar tentang masalah ini, meskipun saya pikir itu bisa disebabkan oleh bug di Nginx (saya belum melihat kode, jadi ini hanya asumsi) bahwa penghitung waktu habis tidak mendapatkan reset setelah koneksi berhasil, jika Nginx tidak membaca apa pun dari server hulu.

Guss
sumber
1
Versi NGINX apa? Saya pikir saya ingat sesuatu yang serupa di versi yang lebih lama (sekitar 0,6 / 7 mungkin) tetapi diperbaiki dalam versi yang lebih baru (Versi stabil terbaru adalah 1.0.5), tetapi itu mungkin salah. Masih mengetahui versi Anda akan membantu
Smudge
Perhatikan bahwa dokumen mengatakan proxy_read_timeoutbukan "batas waktu global", tetapi antara 2 operasi baca.
poige
@ Sam: Saya menggunakan Nginx 1.0.0. @poige - ya, saya sadar akan hal itu, itulah sebabnya saya berharap waktu tunggu total menjadi proxy_read_timeout + proxy_connect_timeout.
Guss
1
Sebagai catatan, Anda mungkin harus meneliti beberapa penyetelan pengumpulan sampah bersamaan untuk JVM Anda: en.wikipedia.org/wiki/…
polinomial
@polynomial: kami lakukan tetapi menurut tolok ukur kami, fitur pengumpulan sampah bersamaan menghasilkan lebih banyak waktu CPU yang hilang dari GC secara keseluruhan dibandingkan dengan "hentikan dunia" GC, oleh karena itu kami lebih suka berinvestasi dalam penyetelan Nginx :-)
Guss

Jawaban:

18

Saya sebenarnya tidak dapat mereproduksi ini di:

2011/08/20 20:08:43 [notice] 8925#0: nginx/0.8.53
2011/08/20 20:08:43 [notice] 8925#0: built by gcc 4.1.2 20080704 (Red Hat 4.1.2-48)
2011/08/20 20:08:43 [notice] 8925#0: OS: Linux 2.6.39.1-x86_64-linode19

Saya mengatur ini di nginx.conf saya:

proxy_connect_timeout   10;
proxy_send_timeout      15;
proxy_read_timeout      20;

Saya kemudian menyiapkan dua server uji. Satu yang hanya akan habis pada SYN, dan yang akan menerima koneksi tetapi tidak pernah menanggapi:

upstream dev_edge {
  server 127.0.0.1:2280 max_fails=0 fail_timeout=0s; # SYN timeout
  server 10.4.1.1:22 max_fails=0 fail_timeout=0s; # accept but never responds
}

Lalu saya mengirim satu koneksi tes:

[m4@ben conf]$ telnet localhost 2480
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.1
Host: localhost

HTTP/1.1 504 Gateway Time-out
Server: nginx
Date: Sun, 21 Aug 2011 03:12:03 GMT
Content-Type: text/html
Content-Length: 176
Connection: keep-alive

Kemudian tonton error_log yang menunjukkan ini:

2011/08/20 20:11:43 [error] 8927#0: *1 upstream timed out (110: Connection timed out) while connecting to upstream, client: 127.0.0.1, server: ben.dev.b0.lt, request: "GET / HTTP/1.1", upstream: "http://10.4.1.1:22/", host: "localhost"

kemudian:

2011/08/20 20:12:03 [error] 8927#0: *1 upstream timed out (110: Connection timed out) while reading response header from upstream, client: 127.0.0.1, server: ben.dev.b0.lt, request: "GET / HTTP/1.1", upstream: "http://127.0.0.1:2280/", host: "localhost"

Dan kemudian access.log yang memiliki batas waktu 30-an yang diharapkan (10 + 20):

504:32.931:10.003, 20.008:.:176 1 127.0.0.1 localrhost - [20/Aug/2011:20:12:03 -0700] "GET / HTTP/1.1" "-" "-" "-" dev_edge 10.4.1.1:22, 127.0.0.1:2280 -

Berikut adalah format log yang saya gunakan yang mencakup batas waktu hulu individu:

log_format  edge  '$status:$request_time:$upstream_response_time:$pipe:$body_bytes_sent $connection $remote_addr $host $remote_user [$time_local] "$request" "$http_referer" "$http_user_agent" "$http_x_forwarded_for" $edge $upstream_addr $upstream_cache_status';
jumlahnya banyak
sumber
1
Pertanyaan saya di atas, dalam skenario Anda, lebih seperti ini: anggaplah server uji yang menerima koneksi setelah waktu acak antara 0 dan 20 detik, dan kemudian menunggu waktu acak antara 19 detik dan 21 detik sebelum menjawab. Kemudian jalankan patokan sederhana untuk melawannya. Saya berharap untuk melihat sekitar 50% dari hasil permintaan dengan batas waktu 10 detik, hasil 25% dengan batas waktu 20 ~ 30 detik dan 25% akan menerima respons yang berhasil. Dalam kasus seperti itu, berapa banyak permintaan yang berhasil akan membutuhkan waktu lebih dari 20 detik untuk diselesaikan? Dalam tolok ukur saya, tidak satupun dari mereka - dan itu mengganggu saya.
Guss
Saya diuji dengan mengatur kerugian acak pada SYN dan kemudian memiliki CGI yang meludahkan garis sangat lambat selama sekitar 50 detik. Saya dapat melihat permintaan membutuhkan waktu lebih lama daripada gabungan waktu habis tetapi masih berhasil: box.access.log 200: 69.814: 67.100:.: 1579 33 127.0.0.1 test.host - [21 / Agu / 2011: 20: 30:52 -0700] "DAPATKAN / huugs HTTP / 1.1" "-" "-" "-" dev_edge 127.0.0.1:2280 -
polinomial
Ok, itu aneh pada level yang berbeda :-). Satu penjelasan yang mungkin adalah bahwa Nginx membutuhkan waktu untuk menulis permintaan ( proxy_send_timeout) dan ketika Anda telah mengaturnya menjadi lebih tinggi proxy_connection_timeout, itu sebenarnya dapat menjelaskan keterlambatan selama 20 detik proxy_read_timeout. Ketika Anda mengatakan "meludahkan garis sangat lambat" - apa maksud Anda?
Guss
tidur 1 di antara garis-garis pencetakan HTML di badan respons. Hanya mengekspos bagaimana proxy_read_timeout berada di antara bacaan, bukan keseluruhan bacaan.
polinomial
1
Ah, begitu. Yah, ini jelas bukan kasus saya dan saya minta maaf karena tidak menjelaskannya di OP saya. Dalam kasus saya, server aplikasi menyelesaikan seluruh pemrosesan sebelum mengembalikan segala jenis respons, dan kemudian mengembalikan semuanya sekaligus - sehingga proxy_read_timeoutsalah satu permintaan gagal sepenuhnya atau memungkinkan sepenuhnya. Ini juga menjelaskan perbedaan antara perilaku yang Anda lihat dan perilaku yang saya lihat.
Guss
3

Masalahnya adalah bahwa saya akan mengharapkan untuk melihat beberapa permintaan timeout setelah proxy_read_timeout + proxy_connect_timeout, atau hampir sepanjang waktu itu, jika layanan macet dan tidak akan menerima koneksi ketika Nginx mencoba mengaksesnya, tetapi sebelum Nginx dapat timeout - itu akan dirilis dan mulai diproses, tetapi terlalu lambat dan Nginx akan dibatalkan karena batas waktu baca.

Connect timeout berarti kios TCP saat berjabat tangan (misalnya, tidak ada SYN_ACKs). TCP akan mencoba mengirim ulang SYN, tetapi Anda hanya memberikan 2 detik. ke Nginx untuk pergi menggunakan Server lain, sehingga tidak punya waktu untuk mengirim kembali SYNs.

UPD. : Tidak dapat menemukan di dokumen, tetapi tcpdump menunjukkan bahwa ada 3 detik. keterlambatan antara 1 mengirim SYN dan upaya 2 untuk mengirim SYN.

poige
sumber
Saya tidak berpikir ini persis yang saya tanyakan - pertanyaannya adalah: jika upstream macet dan mengembalikan SYN_ACK setelah 1,999 detik, mengapa tidak akan melanjutkan proses dengan upstream saat ini?
Guss
Nah, Anda bisa menggunakan sniffer jika Anda ingin memastikan dengan tepat. Mungkin ternyata tidak ada ACK dalam <2 detik sama sekali.
poige
Saya benar-benar tidak dapat menggunakan sniffer karena saya berharap untuk melihat perilaku ini terjadi di mana ada beban yang tinggi pada sistem. Penjelasan tentang tidak pernah ada ACK kemudian dari beberapa X tetapi lebih awal dari 2 detik, bahkan ketika mempertimbangkan jutaan permintaan, tampaknya tidak masuk akal.
Guss