Wiele małych żądań a kilka dużych żądań (API Design)


49

Obecnie pracuję nad projektem z organizacją w następujący sposób:

  • Klient - Pobiera dane z głównego serwera za pośrednictwem interfejsu API REST.
  • Serwer - żąda danych z różnych innych serwerów za pośrednictwem interfejsów API innych firm
  • Interfejsy API innych firm - Usługi poza moją kontrolą, które dostarczają dane do serwera (Reddit, Hackernews, Quora itp.)

Dla argumentu załóżmy, że klient najpierw potrzebuje listy elementów z każdego interfejsu API innej firmy. Z tej listy zostanie wybrany element, w którym to momencie klient musi zobaczyć pełną treść elementu, a także odpowiedzi (tj. Komentarze) na element. Próbuję zdecydować między trzema opcjami:

A la carte

W tym podejściu miałbym 3 oddzielne punkty końcowe na moim serwerze: jeden, aby uzyskać listę elementów, jeden, aby uzyskać główną zawartość elementu, i jeden, aby uzyskać odpowiedzi elementu.

  • Plusy: nigdy nie składam więcej próśb niż potrzebuję, prośby powinny być małe, więc ogólnie powinny być szybsze.
  • Minusy: muszę składać wiele próśb. Po wybraniu elementu z listy użytkownik może poczekać, zanim zobaczy główną zawartość, a następnie poczekać jeszcze dłużej, aby zobaczyć odpowiedzi

Pamięć podręczna po stronie serwera

W tym żądaniu wykonałbym jedno wywołanie do mojego serwera, aby „pobrać” wszystkie dane ze wszystkich źródeł. Dane byłyby następnie buforowane na serwerze. Klient miałby wtedy te same punkty końcowe REST, co wcześniej, z tym wyjątkiem, że między połączeniami nie byłoby wiele oczekiwania, ponieważ mój serwer ma już dane i musi je tylko przekazać klientowi.

  • Plusy: Wciąż łatwe do wdrożenia po stronie klienta, ale bez problemów z opóźnieniami
  • Minusy: nieco bardziej zaangażowana strona serwera, a pierwsze połączenie może trwać naprawdę długo.

Pamięć podręczna po stronie klienta

Ten scenariusz jest podobny do poprzedniego, z tym wyjątkiem, że klient wysyła tylko jedno żądanie do serwera: podaj mi wszystkie dane. Od tego momentu obowiązkiem klienta jest zapisanie danych i właściwe ich wykorzystanie.

  • Plusy: łatwa implementacja serwera, bardzo szybka po pierwszym wywołaniu
  • Minusy: pierwsze połączenie będzie bardzo wolne, bardziej skomplikowane po stronie klienta

Nie jestem pewien, które podejście jest najlepsze, a może brakuje mi oczywistego rozwiązania. Wszelkie porady będą mile widziane!


Wydaje mi się, że jest to wybór między świeżością a szybkością. Co preferują twoi interesariusze i użytkownicy końcowi?
Erk

Odpowiedzi:


27

Należy pamiętać o spodziewanym opóźnieniu sieci (tj. Czasie pingowania) między klientami a serwerem. W sytuacji dużego opóźnienia z dobrą przepustowością wiele małych żądań będzie działać znacznie gorzej niż jedno duże.

Niedawno współpracowałem przy wielozadaniowym projekcie aplikacji internetowej opartym na bazie danych, w którym jeden z zespołów znajduje się w Indiach (reszta znajduje się w Stanach Zjednoczonych). Mamy jedno wystąpienie bazy danych hostowane w naszym amerykańskim biurze, z którym programiści łączą nasze lokalne wystąpienia serwera WWW. Moje biurko jest może pięćdziesiąt stóp i dwa przeskoki LAN od instancji bazy danych, a wydajność jest w porządku.

Kiedy po raz pierwszy zaczęliśmy pracę z programistami w Indiach, mieli oni ogromne oczekiwania na uruchomienie aplikacji i poruszanie się między stronami. Rozmawiamy tutaj o dziesięć minut oczekiwania. Okazuje się, że było to spowodowane tym, że czas pingowania ~ 200ms z ich pulpitów do naszego serwera bazy danych deweloperów był mnożony przez wiele krótkich zapytań do bazy danych. Mój lokalny ping 0,5 ms był tak trywialny, że nigdy nie miało znaczenia ocieplenie między serwerem WWW a serwerem bazy danych. To był pierwszy raz, gdy mieliśmy geograficzną separację między serwerem WWW a serwerem bazy danych.

Rozwiązaniem w naszym przypadku było sklonowanie serwera bazy danych i postawienie kopii w Indiach, ale chodzi o to, aby pamiętać, że jeśli twój klient i serwer są daleko od siebie, opóźnienie sieci zostanie pomnożone przez liczbę komunikacji w całym drut. Przepustowość po ustanowieniu połączenia jest zwykle znacznie mniej istotna.


2

Te trzy opcje nie wykluczają się wzajemnie, można użyć kombinacji pamięci podręcznej po stronie klienta i po stronie serwera. Jednak niektóre dane, takie jak komentarze, mogą stać się nieaktualne, jeśli będą przechowywane w pamięci podręcznej zbyt długo. Biorąc pod uwagę, że nie możesz sprawdzić, czy tak jest, powinieneś prawdopodobnie w ogóle nie przechowywać. Z drugiej strony zawartość zwykle nie zmienia się drastycznie, więc buforowanie jej po stronie serwera, a następnie wstępne pobieranie części po stronie klienta nie zaszkodzi, aby zmniejszyć opóźnienie.


1

Na podstawie tylko podanych informacji, opcja 1, ponieważ

  • przy jednym żądaniu klienta mieszałbyś jabłka i pomarańcze, a kosz owoców może być bardzo duży.

  • buforowanie jest kompromisem, w którym zwiększa się wydajność, ale potencjalnie traci się spójność (nieaktualne dane). Jeśli nie masz żadnych zidentyfikowanych problemów z wydajnością, problemy z synchronizacją zwykle nie są warte ryzyka.


0

Zawsze znajdowałem kilka dużych żądań, które byłyby bardziej wydajne i skalowalne. Są jednak kompromisy we wszystkich podejściach, więc zależy to od potrzeb serwera i klienta. Możesz użyć innej opcji, która polega na tym, aby klient określił cały zakres lub zestaw danych do pobrania - niekoniecznie wszystkie dane, ale jakiś zakres, który jest dostosowywany w czasie, aby pasował do dostępnej przepustowości.


0

Chciałbym (prawie) obniżyć opcję 3. Wybór między 1 a 2 zależy od dwóch rzeczy:

  • (A) jak duży jest wynik pojedynczego, całkowitego pobrania
  • (B) ile szczegółów wyniku klient / użytkownik zwykle wykorzysta w tej sesji.

Łatwo jest podjąć decyzję, jeśli A i B są skrajnościami:

  • Jeśli A jest duże, a B jest małe, zdecydowanie wybierz opcję 1 (A la Carte).
  • Jeśli A jest małe, a B jest duże, wybierz 2 (pamięć podręczna po stronie serwera) lub nawet 3 (pamięć podręczna po stronie klienta).

W przypadku wszelkich innych odmian A / B (dużych / małych) będziesz musiał zastosować dyskrecję. Często zapewniam zarówno gruboziarniste, jak i drobne punkty końcowe, aby uwzględnić różne przypadki użycia od różnych klientów.


0

Jak zawsze w programowaniu, to zależy.

Tak więc prawdziwe pytanie brzmi: co należy wziąć pod uwagę, decydując się na A / B / C lub kombinację tych trzech?

Powiedziałbym, że prawdziwymi czynnikami dyskryminującymi są szczegóły implementacji interfejsów API innych firm, których używasz. Jako przykład powinieneś rozważyć: czy są szybkie czy wolne? Czy dane zmieniają się często i nieoczekiwanie? Czy są „rozmowni” czy odpoczywają?

W przypadku szybkich, łatwych do wywoływania usług, przy zmienianiu danych tak często, że pamięć podręczna po stronie serwera spowoduje problemy ze starzejącą się pamięcią podręczną, zdecydowanie skorzystaj z opcji 1: więcej żądań, brak pamięci podręcznej, tylko w razie potrzeby.

Jeśli Twoje dane zewnętrzne zmienią się w przewidywalny sposób, korzystanie z nich jest ograniczone lub po prostu możesz uzyskać lepsze wrażenia z buforowania danych na serwerze, przejdź do 2. Pamiętaj jednak, że pamięć podręczna nie jest bezpłatna: ma koszty związane z debugowaniem, a czasem użytkownicy skarżą się, że nie widzą aktualizacji.

Opcja 3, rozważałabym tylko wtedy, gdy danych jest niewiele, ale w takim przypadku nawet opcje 1 lub 2 mogą działać, a ty zachowujesz większą logikę na serwerze, więc trzymałbym się 1 lub 2.

Tylko mój 2c.

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.