Sprawdzone metody dotyczące aplikacji RTB

W tym przewodniku omawiamy sprawdzone metody, które warto wziąć pod uwagę podczas tworzenia aplikacji zgodnie z protokołem RTB.

Zarządzanie połączeniami

Podtrzymuj kontakty

Ustanowienie nowego połączenia wydłuża czas oczekiwania i zabiera znacznie więcej zasobów po obu stronach niż ponowne wykorzystanie istniejącego. Zamykając mniej połączeń, możesz zmniejszyć liczbę połączeń, które trzeba ponownie otworzyć.

Po pierwsze, każde nowe połączenie wymaga dodatkowej transmisji danych w obie strony. Ponieważ nawiązujemy połączenia na żądanie, pierwsze żądanie dotyczące połączenia ma krótszy efektywny termin i częściej przekracza limit czasu niż kolejne żądania. Dodatkowe limity czasu zwiększają odsetek błędów, co może prowadzić do ograniczenia systemu licytującego.

Po drugie, wiele serwerów WWW tworzy dedykowany wątek instancji roboczej dla każdego nawiązanego połączenia. Oznacza to, że aby zamknąć i ponownie utworzyć połączenie, zanim w końcu przetworzy żądanie, serwer musi wyłączyć i odrzucić wątek, przydzielić nowy, zapewnić jego działanie i utworzyć stan połączenia. To dużo zbędnych zadań.

Unikaj zamykania połączeń

Zacznij od dostrajania działania połączeń. Większość ustawień domyślnych serwera jest dostosowywana do środowisk z dużą liczbą klientów, a każdy z nich generuje niewielką liczbę żądań. Z kolei w przypadku RTB niewielka pula komputerów wysyła żądania w imieniu dużej liczby przeglądarek. W takich sytuacjach ma sens jak najwięcej razy korzystać z połączenia. Zalecamy ustawienie:

  • Limit czasu bezczynności do 2,5 minuty.
  • Maksymalna liczba żądań połączenia o najwyższej możliwej wartości.
  • Maksymalna liczba połączeń z najwyższą wartością, jaką może obsłużyć Twoja pamięć RAM, jednocześnie sprawdzając, czy liczba połączeń nie jest zbyt zbliżona do tej wartości.

Na przykład w Apache wymagane jest ustawienie KeepAliveTimeout na 150, MaxKeepAliveRequests na 0, a MaxClients na wartość zależną od typu serwera.

Po dostosowaniu działania połączenia sprawdź też, czy kod licytującego nie zamyka połączeń niepotrzebnie. Jeśli na przykład masz kod frontendu, który w przypadku błędów backendu lub przekroczenia limitu czasu zwraca domyślną odpowiedź „brak stawki”, dopilnuj, aby kod zwracał odpowiedź bez zamykania połączenia. W ten sposób unikniesz sytuacji, w której w przypadku przeciążenia systemu licytującego połączenia zaczynają się zamykać i zwiększa się liczba limitów czasu, co spowoduje ograniczenie możliwości licytującego.

Dbaj o równowagę połączeń

Jeśli Authorized Buyers łączy się z serwerami Twojego licytującego za pomocą serwera proxy, zrównoważone połączenia mogą się z czasem zmieniać, ponieważ znając tylko adres IP serwera proxy, nie może określić, który serwer licytującego otrzymuje każde wywołanie. W miarę jak Authorized Buyers nawiązuje i zamyka połączenia, a serwery licytującego uruchamiają się ponownie, liczba przypisanych do każdego z nich połączeń może znacznie się zmieniać.

Gdy niektóre połączenia są intensywnie wykorzystywane, inne otwarte połączenia mogą pozostawać nieaktywne, ponieważ w danym momencie nie są potrzebne. W miarę zmian ruchu w Authorized Buyers nieaktywne połączenia mogą stać się aktywne, a aktywne połączenia mogą przechodzić w stanie bezczynności. Może to spowodować nierównomierne obciążenie serwerów licytującego, jeśli połączenia są źle skonstruowane. Google stara się temu zapobiec,zamykając wszystkie połączenia po osiągnięciu 10 tys. żądań, aby w miarę upływu czasu automatycznie ponownie równoważyć połączenia „gorące”. Jeśli ruch w Twoim środowisku się nie zrównoważył, możesz podjąć dalsze kroki:

  1. Jeśli korzystasz z serwerów proxy frontendu, wybierz backend dla każdego żądania, a nie raz na połączenie.
  2. Określ maksymalną liczbę żądań na połączenie, jeśli przekazujesz połączenia przez sprzętowy system równoważenia obciążenia lub zaporę sieciową, a mapowanie zostanie naprawione po nawiązaniu połączeń. Pamiętaj, że Google określa już górny limit 10 000 żądań na połączenie, więc musisz podać bardziej rygorystyczną wartość tylko wtedy, gdy połączenia „gorące” nadal są klastrowane w Twoim środowisku. Na przykład w Apache ustaw MaxKeepAliveRequests na 5000
  3. Skonfiguruj serwery licytującego, aby monitorowały częstotliwość żądań i zamknąły część własnych połączeń, jeśli stale obsługują zbyt wiele żądań w porównaniu z innymi kontrahentami.

Doskonale radzi sobie z przeciążeniem

W idealnej sytuacji limity będą na tyle wysokie, aby system licytujący mógł otrzymywać wszystkie możliwe żądania, ale nie więcej. W praktyce utrzymanie limitów na optymalnych poziomach jest trudnym zadaniem, a przeciążenia zdarzają się z wielu powodów: mogą się zdarzyć, że backend nie działa w godzinach szczytu, zmienia się zróżnicowanie ruchu, aby każde żądanie mogło przetworzyć więcej danych, lub gdy wartość limitu jest zbyt wysoka. Dlatego warto zastanowić się, jak licytujący zachowa się w przypadku zbyt dużego ruchu.

Aby uwzględnić tymczasowe zmiany w ruchu (maksymalnie 1 tydzień) między regionami (zwłaszcza między Azją i Zachodnią częścią USA oraz wschodem i zachodem Stanów Zjednoczonych oraz USA, zalecamy zmniejszenie o 15% między szczytem w ciągu 7 dni a liczbą zapytań na sekundę przypadającą na lokalizację transakcji.

Pod względem działania przy dużym obciążeniu system licytujący dzielą się na 3 ogólne kategorie:

Licytujący „odpowiadaj na wszystko”

Chociaż implementacja jest prosta, ale ten licytujący uzyskuje najgorszą cenę, gdy jest przeciążony. Po prostu stara się odpowiedzieć na każde pytanie o stawkę, niezależnie od tego, co się dzieje, i umieszcza w kolejce te, których nie można od razu wyświetlić. Często dzieje się tak:

  • Wraz ze wzrostem liczby żądań rosną opóźnienia żądań, aż do chwili zakończenia limitu czasu dla wszystkich żądań
  • Opóźnienia szybko rosną, gdy współczynniki objaśnień zbliżają się do szczytu
  • Wprowadzenie ograniczania powoduje gwałtowne zmniejszenie liczby dozwolonych wywołań
  • Opóźnienia zaczynają się odnawiać, przez co ograniczanie
  • Cykl rozpocznie się od nowa.

Wykres czasu oczekiwania w przypadku tego licytującego przypomina bardzo stromy wzór piłki. Z kolei żądania umieszczone w kolejce powodują, że serwer zaczyna pamięć stronicowania lub robi coś innego, co powoduje długotrwałe spowolnienie, a opóźnienia nie przywracają się do momentu zakończenia czasu szczytowego, co doprowadzi do obniżenia częstotliwości wywołań w całym okresie szczytu. W obu przypadkach pojawia się mniej objaśnień lub na nie odpowiada niż gdyby limit został po prostu ustawiony na niższą wartość.

Licytujący „Błąd związany z przeciążeniem”

Licytujący akceptuje wywołania do określonego współczynnika, a potem w przypadku niektórych z nich zaczyna zwracać błędy. Mogą to być wewnętrzne limity czasu, wyłączenie kolejkowania połączeń (kontrolowane przez ListenBackLog w Apache), wdrożenie prawdopodobnego trybu porzucenia, gdy wykorzystanie lub opóźnienia okażą się zbyt wysokie, albo inny mechanizm. Jeśli odnotujemy odsetek błędów na poziomie powyżej 15%, rozpoczniemy ograniczanie. W przeciwieństwie do systemu licytującego „Odpowiedz na wszystko” ten licytujący „zmniejsza straty”, aby przywrócić mu ochronę natychmiast, gdy liczba żądań spadnie.

Wykres czasu oczekiwania w przypadku tego licytującego przypomina płytki wzór piłki podczas przeciążeń, zlokalizowany wokół maksymalnej dopuszczalnej stawki.

Licytujący bez ustalania stawek w przypadku przeciążenia

Licytujący akceptuje objaśnienia do określonej częstotliwości, a następnie w przypadku przeciążenia zaczyna zwracać odpowiedzi „brak stawki”. Podobnie jak w przypadku błędu związanego z przeciążeniem licytującego, tę opcję można zaimplementować na wiele sposobów. Różnica polega na tym, że żaden sygnał nie jest zwracany do Google, więc nigdy nie ograniczamy wyświetlania objaśnień. Przeciążenie jest wchłaniane przez maszyny frontendu, przez co do backendów może trafiać tylko ten ruch, jaki mogą obsłużyć.

Wykres czasu oczekiwania w przypadku tego licytującego przedstawia płaskowyż, który (sztucznie) zatrzymuje równoległe działanie częstotliwości żądań w godzinach szczytu, i odpowiadający mu spadek odsetka odpowiedzi zawierających stawkę.

Zalecamy połączenie metody „błąd przy przeciążeniu” i „brak stawki w przypadku przeciążenia” w taki sposób:

  • Zapewnienie nadmiarowej liczby interfejsów i ustawienie błędu w przypadku przeciążenia pozwala zmaksymalizować liczbę połączeń, na które mogą odpowiadać w określonej formie.
  • W przypadku błędów w przypadku przeciążenia maszyny frontendu mogą używać gotowych odpowiedzi „no-bid” i nie muszą w ogóle analizować żądania.
  • Zaimplementuj kontrolę stanu backendów – jeśli żaden z nich nie ma wystarczającej mocy obliczeniowej, zwracał odpowiedź „bez stawki”.

Umożliwia to wchłonięcie części przeciążenia i pozwala backendom odpowiadać na dokładnie tyle żądań, ile są w stanie obsłużyć. Możesz to traktować jako „brak ustalania stawek w przypadku przeciążenia”, gdy maszyny frontendu wracają do „błędu przy przeciążeniu”, gdy liczba żądań jest znacznie wyższa od oczekiwanej.

Jeśli masz licytującego typu „odpowiadaj na wszystko”, rozważ przekształcenie tego licytującego w „błąd przy przeciążeniu” przez dostrojenie jego działania w celu uniknięcia przeciążenia. Powoduje to zwracanie większej liczby błędów, ale skraca czas oczekiwania i zapobiega sytuacji, w której serwer nie może odpowiadać na żadne żądania.

Odpowiadanie na pingi

Zaskakująco ważne na potrzeby debugowania jest pewność, że licytujący będzie mógł odpowiadać na żądania ping, ale nie będzie zarządzać połączeniami. Google używa żądań ping do dokładnego sprawdzenia i debugowania stanu licytującego, zamknięcia połączenia, opóźnienia itp. Żądania ping mają taką postać:

Google

id: "\3503cg\3023P\364\230\240\270\020\255\002\231\314\010\362\347\351\246\357("
is_test: true
is_ping: true

Plik JSON OpenRTB

"id": "4YB27BCXimH5g7wB39nd3t"

protobuf OpenRTB,

id: "7xd2P2M7K32d7F7Y50p631"

Pamiętaj, że wbrew oczekiwaniom żądanie ping nie zawiera żadnych boksów reklamowych. Zgodnie z opisem powyżej nie należy zamykać połączenia po odpowiedzi na żądanie ping.

Rozważ połączenie równorzędne

Innym sposobem na ograniczenie opóźnień lub zmienności w sieci jest połączenie równorzędne z Google. Połączenie równorzędne pomaga zoptymalizować ścieżkę, jaką ruch dociera do systemu licytującego. Punkty końcowe połączenia pozostaną bez zmian, ale połączenia pośrednie ulegną zmianie. Więcej informacji znajdziesz w przewodniku po połączeniach równorzędnych. Powodem, dla którego warto myśleć o połączeniach równorzędnych, jest:

  • W internecie linki transportu publicznego są wybierane głównie przez „routing ziemniaczany”, który znajduje najbliższy link poza naszą siecią, który może pobrać pakiet do miejsca docelowego, i kieruje pakiet przez ten link. Gdy ruch przechodzi przez sekcję szkieletową należącą do dostawcy, z którym mamy wiele połączeń równorzędnych, wybrany link znajduje się prawdopodobnie w pobliżu miejsca, w którym zaczyna się pakiet. Poza tym etapem nie mamy wpływu na trasę, którą pakiet dociera do systemu licytującego, więc może on być po drodze odesłany do innych systemów autonomicznych (sieci).

  • W przeciwieństwie do tego, gdy obowiązuje umowa bezpośredniego połączenia równorzędnego, pakiety są zawsze wysyłane wraz z połączeniem równorzędnym. Bez względu na to, skąd pochodzi pakiet, przechodzi on przez linki należące do Google lub dzierżawionych, aż dotrze do współdzielonego punktu połączenia równorzędnego, które powinno znajdować się w pobliżu lokalizacji licytującego. Odwrotna podróż zaczyna się od krótkiego przeskoku do sieci Google, a do końca pozostaje w sieci Google. Przechowywanie większości podróży w infrastrukturze zarządzanej przez Google zapewnia, że pakiet korzysta z trasy z niewielkimi opóźnieniami i pozwala uniknąć dużej potencjalnej zmienności.

Prześlij statyczny DNS

Zalecamy, aby kupujący zawsze przesyłali do Google 1 statyczny wynik DNS i ufali, że to Google odpowiada za dostarczanie ruchu.

Oto 2 typowe metody stosowane przez serwery DNS licytującego podczas próby obciążenia systemu równoważenia obciążenia lub zarządzania dostępnością:

  1. Serwer DNS przekazuje jeden adres lub podzbiór adresów w odpowiedzi na zapytanie, a następnie cyklizuje tę odpowiedź w określony sposób.
  2. Serwer DNS zawsze odpowiada, podając ten sam zestaw adresów, ale zmieniając kolejność adresów w odpowiedzi.

Pierwsza metoda jest słaba przy równoważeniu obciążenia, ponieważ na wielu poziomach stosu pamięci podręcznej jest dużo pamięci podręcznej, a próby ominięcia buforowania prawdopodobnie nie przyniosą również preferowanych wyników, ponieważ Google nalicza licytującemu czas rozpoznawania nazw DNS.

Druga metoda w ogóle nie umożliwia równoważenia obciążenia, ponieważ Google losowo wybiera adres IP z listy odpowiedzi DNS, więc kolejność odpowiedzi nie ma znaczenia.

Jeśli licytujący wprowadzi zmianę w systemie DNS, Google zachowa wartość TTL(czas życia danych) określoną w rekordach DNS, ale okres odświeżania pozostanie niepewny.