Wydajność Apache znacznie spada powyżej ~ 256 jednoczesnych żądań


14

Prowadzę witrynę o stosunkowo niskim natężeniu ruchu, która raz w tygodniu doświadcza dużego wzrostu liczby odwiedzających po aktualizacji witryny. Podczas tego wzrostu wydajność witryny jest bardzo słaba w porównaniu z resztą tygodnia. Rzeczywiste obciążenie serwerów pozostaje bardzo niskie, niezawodnie poniżej 10% CPU i poniżej 30% RAM (sprzęt powinien być całkowicie przesadny w stosunku do tego, co faktycznie robimy), ale z jakiegoś powodu Apache wydaje się nie być w stanie poradzić sobie z ilością wniosków. Uruchamiamy apache 2.2.3 na RHEL 5.7, jądro 2.6.18-274.7.1.el5, x86_64.

Próbując odtworzyć to zachowanie poza godzinami pracy z ab, zauważam znaczny spadek wydajności po przekroczeniu około 256 użytkowników. Uruchomienie testu z najmniejszym możliwym przypadkiem użycia, jaki mogłem wymyślić (pobierany statyczny plik tekstowy, łącznie 223 bajty), wydajność jest normalna z 245 jednoczesnymi żądaniami:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       15   25   5.8     24      37
Processing:    15   65  22.9     76      96
Waiting:       15   64  23.0     76      96
Total:         30   90  27.4    100     125

Percentage of the requests served within a certain time (ms)
  50%    100
  66%    108
  75%    111
  80%    113
  90%    118
  95%    120
  98%    122
  99%    123
 100%    125 (longest request)

Ale gdy tylko podniosę liczbę do 265 równoczesnych żądań, ich część zaczyna zajmować absurdalnie dużo czasu:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       13  195 692.6     26    3028
Processing:    15   65  21.3     72     100
Waiting:       15   65  21.3     71      99
Total:         32  260 681.7    101    3058

Percentage of the requests served within a certain time (ms)
  50%    101
  66%    108
  75%    112
  80%    116
  90%    121
  95%   3028
  98%   3040
  99%   3044
 100%   3058 (longest request)

Wyniki te są bardzo spójne dla wielu przebiegów. Ponieważ do tego pola dociera inny ruch, nie jestem pewien, gdzie dokładnie byłaby twarda granica, jeśli taka istnieje, ale wydaje się, że jest podejrzanie zbliżona do 256.

Oczywiście założyłem, że jest to spowodowane limitem wątków w preforkach, więc posunąłem się do przodu i dostosowałem konfigurację, aby podwoić liczbę dostępnych wątków i zapobiec niepotrzebnemu powiększaniu się i zmniejszaniu puli wątków:

<IfModule prefork.c>
StartServers     512
MinSpareServers  512
MaxSpareServers  512
ServerLimit      512
MaxClients       512
MaxRequestsPerChild  5000
</IfModule>

mod_status potwierdza, że ​​działam teraz z 512 dostępnymi wątkami

8 requests currently being processed, 504 idle workers

Jednak próba 265 jednoczesnych żądań nadal daje prawie identyczne wyniki jak wcześniej

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:       25  211 714.7     31    3034
Processing:    17   94  28.6    103     138
Waiting:       17   93  28.5    103     138
Total:         57  306 700.8    138    3071

Percentage of the requests served within a certain time (ms)
  50%    138
  66%    145
  75%    150
  80%    161
  90%    167
  95%   3066
  98%   3068
  99%   3068
 100%   3071 (longest request)

Po przeszukaniu dokumentacji (i wymianie stosów) brakuje mi dalszych ustawień konfiguracji, aby spróbować rozwiązać ten problem. Czy coś mi brakuje? Czy powinienem zacząć szukać odpowiedzi poza apache? Czy ktoś jeszcze widział takie zachowanie? Każda pomoc byłaby bardzo mile widziana.

EDYTOWAĆ:

Zgodnie z radą Ladadadady uciekałem przed apaczami. Próbowałem kilka razy z -tt i -T i nie mogłem znaleźć niczego niezwykłego. Następnie spróbowałem uruchomić strace -c na wszystkich aktualnie uruchomionych procesach Apache i otrzymałem:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 22.09    0.317836           5     62128      4833 open
 19.91    0.286388           4     65374      1896 lstat
 13.06    0.187854           0    407433           pread
 10.70    0.153862           6     27076           semop
  7.88    0.113343           3     38598           poll
  6.86    0.098694           1    100954     14380 read

(... porwany)

Jeśli czytam to dobrze (i nie przejmuj się, ponieważ nie używam zbyt często strace), żadne z wywołań systemowych nie może uwzględniać czasu, jaki zajmują te żądania. Wygląda na to, że wąskie gardło występuje, zanim żądania dotrą nawet do wątków roboczych.

EDYCJA 2:

Jak sugerowało kilka osób, ponownie uruchomiłem test na samym serwerze internetowym (wcześniej test był przeprowadzany z neutralnej lokalizacji internetowej). Wyniki były zaskakujące:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0   11   6.6     12      21
Processing:     5  247 971.0     10    4204
Waiting:        3  245 971.3      7    4204
Total:         16  259 973.3     21    4225

Percentage of the requests served within a certain time (ms)
  50%     21
  66%     23
  75%     24
  80%     24
  90%     26
  95%   4225
  98%   4225
  99%   4225
 100%   4225 (longest request)

Czas końcowy jest podobny do testu internetowego, ale wydaje się być nieco gorszy, gdy jest uruchamiany lokalnie. Co ciekawsze, profil zmienił się dramatycznie. Podczas gdy wcześniej większość długoterminowych żądań była poświęcana na „łączenie”, teraz wąskie gardło wydaje się być w trakcie przetwarzania lub oczekiwania. Podejrzewam, że może to być osobny problem, który wcześniej był maskowany przez ograniczenia sieciowe.

Ponownie uruchamiając test z innego komputera w tej samej sieci lokalnej co host Apache, widzę znacznie bardziej rozsądne wyniki:

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   0.8      2       4
Processing:    13  118  99.8    205     222
Waiting:       13  118  99.7    204     222
Total:         15  121  99.7    207     225

Percentage of the requests served within a certain time (ms)
  50%    207
  66%    219
  75%    220
  80%    221
  90%    222
  95%    224
  98%    224
  99%    225
 100%    225 (longest request)

Te dwa testy razem rodzą szereg pytań, ale niezależnie od tego, należy teraz przekonująco uzasadnić pewne poważne wąskie gardło sieci występujące pod pewnym obciążeniem. Myślę, że następnym krokiem będzie osobne zbadanie warstwy sieci.


Opcje do rozważenia: CloudFlare, drupal.org/project/boost , CDN, pamięć podręczna Varnish.
ceejayoz

Nie mówisz nam nic o tym, co robi ten serwer (w świecie rzeczywistym) poza obsługą żądań HTTP. Czy w grę wchodzi baza danych (lub inny wspólny zasób, który może cierpieć z powodu niezgodności blokady)? Jeśli problem pojawia się nagle przy DOKŁADNIE 256 żądaniach (OK przy 255), prawdopodobnie zasypane są jakieś zasoby zewnętrzne. (Twój skok służący do wyświetlania strony statycznej też jest zdecydowanie nienormalny - zobacz tam odpowiedź Ladadadady, aby uzyskać tam wskazówki dotyczące debugowania)
voretaq7

ceejayoz: Doceniam sugestie, ale zasadniczo uważam, że Apache nie powinien być taki powolny. Jest wiele rzeczy, które możemy zrobić, aby złagodzić efekt problemu, ale wolałbym to naprawić lub przynajmniej zrozumieć.
cmckendry,

voretaq7: Początkowo myślałem w ten sam sposób, ponieważ typowe żądanie dotyczyłoby również php / mysql, ale problem utrzymuje się na tym samym progu, nawet przy podawaniu zawartości całkowicie statycznej.
cmckendry

1
Czy to prawdziwy serwer czy maszyna wirtualna? Czy wykonujesz test z hosta lokalnego, sieci lokalnej lub Internetu? Minimalne czasy odpowiedzi w zakresie 100 ms sugerują testy z Internetu. Spróbuj przeprowadzić test z lokalnego hosta - być może twój dostawca cię dusi.
Tometzky

Odpowiedzi:


4

To, co zrobiłbym w tej sytuacji, jest uruchomione

strace -f -p <PID> -tt -T -s 500 -o trace.txt

na jednym z procesów Apache podczas testu ab, aż do przechwycenia jednej z wolnych odpowiedzi. Następnie przejrzyj trace.txt.

-ttI -Topcji daje znacznika czasu rozpoczęcia i czas trwania każdego wywołania systemowego, aby zidentyfikować te powolne.

Możesz znaleźć pojedyncze wolne połączenie systemowe, takie jak open()lub, stat()lub możesz znaleźć szybkie połączenie z (prawdopodobnie wieloma) poll()połączeniami bezpośrednio po nim. Jeśli znajdziesz taki, który działa na pliku lub połączeniu sieciowym (całkiem prawdopodobne), przejrzyj ślad do tyłu, aż znajdziesz ten uchwyt pliku lub połączenia. Wcześniejsze wywołania tego samego uchwytu powinny dać ci wyobrażenie o tym, na co poll()czekał.


Dobry pomysł, patrząc na tę -copcję. Czy upewniłeś się, że śledzone dziecko Apache zrealizowało co najmniej jedno wolne żądanie w tym czasie? (Nie jestem nawet pewien, jak byś to zrobił, oprócz stracejednoczesnego uruchamiania na wszystkich dzieciach).

Niestety stracenie daje nam pełnego obrazu tego, co robi uruchomiony program. Śledzi tylko połączenia systemowe. Wiele może się wydarzyć w programie, który nie wymaga pytania jądra o nic. Aby dowiedzieć się, czy tak się dzieje, możesz spojrzeć na znaczniki czasu rozpoczęcia każdego wywołania systemowego. Jeśli widzisz znaczące luki, tam właśnie zmierza czas. Nie jest to łatwe do grepowania, a między wywołaniami systemowymi zawsze występują niewielkie luki.

Ponieważ powiedziałeś, że użycie procesora pozostaje niskie, prawdopodobnie nie jest to nadmierne zjawisko pomiędzy wywołaniami systemowymi, ale warto to sprawdzić.


Przyglądając się bliżej wynikom z ab:

Nagły skok w czasach odpowiedzi (wygląda na to, że nie ma żadnych czasów odpowiedzi w przedziale od 150 ms do 3000 ms) sugeruje, że zdarza się określony limit czasu, który jest uruchamiany powyżej około 256 jednoczesnych połączeń. Bardziej płynna degradacja byłaby oczekiwana, gdyby zabrakło pamięci RAM lub cykli procesora normalnych operacji we / wy.

Po drugie, powolna abodpowiedź pokazuje, że 3000 ms zostało wydane w connectfazie. Prawie wszystkie zajęły około 30ms, ale 5% zajęło 3000ms. Sugeruje to, że problem stanowi sieć.

Skąd uciekasz ab? Czy możesz wypróbować go z tej samej sieci co komputer Apache?

Aby uzyskać więcej danych, spróbuj uruchomić tcpdumpna obu końcach połączenia (najlepiej ntpna obu końcach, aby zsynchronizować oba przechwytywanie.) I poszukaj retransmisji TCP. Wireshark jest szczególnie dobry do analizy zrzutów, ponieważ wyróżnia retransmisje TCP w innym kolorze, dzięki czemu można je łatwo znaleźć.

Warto również przejrzeć dzienniki dowolnych urządzeń sieciowych, do których masz dostęp. Niedawno napotkałem problem z jedną z naszych zapór ogniowych, która mogła obsługiwać przepustowość pod względem kb / s, ale nie mogła obsłużyć liczby odbieranych pakietów na sekundę. Osiągnęło to prędkość 140 000 pakietów na sekundę. Kilka szybkich obliczeń w abbiegu prowadzi mnie do wniosku, że widziałbyś około 13 000 pakietów na sekundę (ignorując 5% powolnych żądań). Może to jest wąskie gardło, które osiągnąłeś. Fakt, że dzieje się to około 256, może być czystym zbiegiem okoliczności.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.