Komunikacja asynchroniczna między serwisami i callback

Komunikacja asynchroniczna między serwisami i callback
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

Mamy serwis A i serwis B komunikujące się restem. Serwis A zleca coś do B po czym B odpowiada 201/202 następnie a kończy transakcję i po pewnym
czasie B wraca z callbackiem do A, że zrobił co miał zrobić. Pytanie co w przypadku gdy B zrobi callback szybciej niż A zakończy transakcję (wtedy callback nie zostanie poprawnie obsłużony i pewnie będzie 404 bo A jeszcze nie dokończył transakcji i np. nie zapisał orderu)?
Powinniśmy się na coś takiego zabezpieczać? Wiem, że jest outbox pattern itd. ale załóżmy, że wszystko jest restem i nie bieżemy pod uwagę tego patternu.


Shalom
  • Rejestracja:około 21 lat
  • Ostatnio:prawie 3 lata
  • Lokalizacja:Space: the final frontier
  • Postów:26433
2

Zlecaj akcje w B dopiero kiedy A jest w stanie obsłużyć callback? Tym bardziej że co jak transakcja w A się wywali i request do B w ogóle nie powinien był pójść bo teraz masz niespójny stan, w A operacja sie wycofała w B nie.


"Nie brookliński most, ale przemienić w jasny, nowy dzień najsmutniejszą noc - to jest dopiero coś!"
edytowany 1x, ostatnio: Shalom
W1
@Shalom: 77.22.112.42 to twoje zewnętrzne IP? Chcę pójść w Twoje ślady i pobawić się w tropienie multikont.
Shalom
opanie, jakbym był w stanie nabić 26350 postów jednocześnie cisnąć na wiele kont, to by mi chyba życia nie starczyło xD
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:11 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
2

Nie pamiętam już sytuacji ale kiedyś w zespole przekonaliśmy się że robienie strzału do innego serwisu w transakcji to nie jest dobry pomysł. Ogólnie jeśli chcesz żeby twoja baza danych wytrzymała duże obciążenie to transakcje powinny być maksymalnie krótkie. A strzały do innych mikroserwisów mogą zająć dużo czasu

Jak chcesz coś pobrać z serwisu B to pobierz to przed transakcją. Jak chcesz powiedzieć że A zapisał to zrób to po transakcji. Oczywiście jest pytanie co jak A padnie i nie poinformuje B. Wszystko zależy jak ważna to jest informacja. czy B pyta się sa co jakiś czas.
Jak się B nie pyta i to jest ważna informacja to możliwe że ta informacja powinna trafić na jakąś kolejkę lub inną dedykowaną usługę do powiadamiania


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
edytowany 4x, ostatnio: cerrato
Shalom
Ale on to chyba robi i tak asynchronicznie?
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

W sumie to nie jest relacyjna baza danych tylko mongo i na końcu jest save do bazy:

  1. A -> B
  2. A got response
  3. A save
  4. B -> A (callback)

I teraz punkt 4 wykona się szybciej niż 3.


edytowany 1x, ostatnio: lukascode
Skoq
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Kraków
  • Postów:255
0

Możesz w serwisie B napisać joba kompensacyjnego, który będzie ponawiał próby uderzenia callbackiem, gdy w odpowiedzi dostał 404. Zapisujesz taki message do jakiegoś storage i co jakiś interwał łyka go ten job.


I tak to właśnie jest
SL
  • Rejestracja:około 7 lat
  • Ostatnio:dzień
  • Postów:857
0

Bez szczegółów chyba nie da się doradzić nic idealnego. Najprościej by było jakby nie było żadnego callbacka, tylko A spokojnie czeka na B. Albo kolejka

LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

@Skoq: właśnie o to chodzi, że nie mam kontroli nad serwisem B i tych serwisów może być wiele (wiele integratorów)
i nie chce im narzucać tego jak mają implementować chociaż api każdy z nich będzie wystawiał takie samo. Jeden integrator
np. może przetworzyć w ciągu 5min i zrobić callback jak tak zaimplementuje a drugi licząc w ms jeżeli jest bardzo szybki.


KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:11 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
1
lookacode1 napisał(a):

W sumie to nie jest relacyjna baza danych tylko mongo i na końcu jest save do bazy:

  1. A -> B
  2. A got response
  3. A save

No to ja nie rozumie w czym problem. W Scali było by to np (Zakłądając ze wszystko zwraca Future'y)

Kopiuj
for {
  response <- clientB.call
  result   <- repo.save(response)
} yield result

lub

Kopiuj
clientB.call.flatMap { response =>
  repo.save(response)
}

Jak używasz mongo to pewnie piszesz w JS i trzeba zamienienić flatMap na then.
Daj przykładowy kod bo tak z opisu słowno muzycznego nie rozumiem gdzie jest problem


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

@KamilAdam: Ok ale co jak np. baza będzie obciążona w związku z tym save potrwa trochę dłużej
i callback od B przyjdzie szybciej i baza nie jest "gotowa" na jego obsłużenie bo jeszcze nie ma tam danych,
które powinny być?

Jak używasz mongo to pewnie piszesz w JS

Niee to java jest aż tak mi nie odbiło xd


edytowany 1x, ostatnio: lukascode
Charles_Ray
Jak „save” potrwa „trochę dłużej”, to blokujesz wątek serwera na wskutek czego pewnego dnia wieczorem Twój serwis padnie :)
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:11 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
1

@lookacode1: daj kod bo nie rozumiem o czym mówisz. W moim przypadku najpier robię call do B a dopiero jak dostanę dane to robię zapis.

callback od B przyjdzie szybciej i baza nie jest "gotowa" na jego obsłużenie bo jeszcze nie ma tam danych,

Czemu baza ma być gotowa na obsłużenie? przecież callbacka obsługuje A.

Dobra chyba rozumiem.
Mamy dwudziesty pierwszy wiek (wiem że dopiero początek, i na świecie dalej istnieją barbarzyńcy), więc zapomnij słowo calback. Skoro masz Javę to najlepiej byłoby jakbyś miał api które zwraca ci CompletableFuture, a nie jakieś prymitywne callbacki. czyli:

Kopiuj
CompletableFuture responceFuture = clientB.call();
repo.save(someData);
responceFuture.thenApply(response -> tenCallback co potrzebuje czytać z bazy);// bogowie czemu ta metod anie może być nazwana `map`?

Teraz nie wykonasz callbacka dopóki nie zapiszesz do bazy


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
edytowany 4x, ostatnio: KamilAdam
LU
to dobrze bo nie chciało mi się pisać kodu ;)
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

@KamilAdam: Z tym, że to dalej jest synchronicznie. A jak B będzie miało taką logikę, że przetwarza wszystkie ordery pod koniec dnia
i robi callbacki? Chodzi o to, że A zleca do B jakiś order do zrobienia i B odpowiada od razu, że ok przyjąłem przetworzę to w swoim czasie.
Wtedy A dostaję od razu odpowiedź i dopiero zapisuje to bazy (bo w response jest id od B, które musi być zapisane). I takich serwisów B spiętych z
A może być wiele i nie możemy narzucać to jak one pod spodem mają zaimplementować to api. Dlatego np. B1 jeśli zechce to może przetwarzać ordery
pod koniec dnia i robić callbacki (i tu nie ma problemu bo do bazy po stronie A już dawno dane się zapisały) a B2 może instantowo przetworzyć i może powstać
wyścig, że callback do A będzie szybszy.


edytowany 2x, ostatnio: lukascode
KamilAdam
  • Rejestracja:ponad 6 lat
  • Ostatnio:11 dni
  • Lokalizacja:Silesia/Marki
  • Postów:5505
0

Żeś wymyślił. Teraz rozumiem trochę więcej ale danej nie wiem czym nazywasz callbacki czy są to strzały z B do A? Bo jak dpobrze zrozumiałem to B odpowiada A od razu okejką

Hm, to chyba tylko ponawianie pozostaje


Mama called me disappointment, Papa called me fat
Każdego eksperta można zastąpić backendowcem który ma się douczyć po godzinach. Tak zostałem ekspertem AI, Neo4j i Nest.js . Przez mianowanie
edytowany 2x, ostatnio: KamilAdam
YA
OK, ale dla złożenia zlecenia requestu :-)
Skoq
  • Rejestracja:około 6 lat
  • Ostatnio:3 miesiące
  • Lokalizacja:Kraków
  • Postów:255
2

No to może jakiś serwis z boku, który byłby fasadą do tego serwisu A i wszystkie serwisu typu B uderzałyby do tej fasady a ona by miała logikę ewentualnego ponawiania.


I tak to właśnie jest
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

Tak callback to jest strzał z B do A na jakiś endpoint i wtedy A po swojej stronie zmienia status na processed.


YA
  • Rejestracja:prawie 10 lat
  • Ostatnio:około 3 godziny
  • Postów:2363
0

Z tego co zrozumiałem, to kolega ma jakiś szerzy proces (wszak skądś się wzięła potrzeba zapisu w A i zainicjowania procesu w B), w ramach którego wykonywane są czynności w A i czynności w B.
Te czynności mogą się wykonywać równolegle (więc możliwe są przeploty AB i BA).

Dla ilustracji diagram. W A zaczyna się jakiś proces, który w pewnym momencie rozdziela się na 2 gałęzie , które trzeba kiedyś złączyć. Brakuje elementu kontrolującego przebieg tego procesu.
np. prostego automatu stanowego, czy "sagi" (używając nomenklatury DDD).

Bez kontroli przebiegu procesu zawsze będą jakieś techniczne problemy.

screenshot-20220407163312.png

Charles_Ray
  • Rejestracja:prawie 17 lat
  • Ostatnio:minuta
  • Postów:1873
0

Brzmi jak „process manager” - jeżeli (4) wykona się przed (3), to odkładasz taki „request” na kolejce(*) i próbujesz wykonać później. Można to zrobić przynajmniej na 3 sposoby:

  1. Zadanie do przetworzenia zapisuje u siebie A w jakiejś kolejce
  2. A odpowiada jakimś 5xx i B ponawia po jakimś czasie (retry backoff) - czyli kolejka jest u B
  3. Robisz to asynchronicznie poprzez event bus, np. Kafkę - B zapisuje tam zadanie, A pobierze je, kiedy będzie gotowy

(*) - ta kolejka to w istocie może być jakaś tabelka + cron

Czy jest jakiś konkretny powód, dla którego w tym wypadku komunikacja B->A jest synchroniczna?


”Engineering is easy. People are hard.” Bill Coughran
edytowany 4x, ostatnio: Charles_Ray
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

B to jest oddzielna usługa poza naszą infrastrukturą trochę jak np. korzystasz z bramki płatności to też jakiś callback dostajesz.


Charles_Ray
  • Rejestracja:prawie 17 lat
  • Ostatnio:minuta
  • Postów:1873
0
lookacode1 napisał(a):

B to jest oddzielna usługa poza naszą infrastrukturą trochę jak np. korzystasz z bramki płatności to też jakiś callback dostajesz.

Nie znam całości procesu, ale w takim razie pozostaje opcja (A), co ma swoje zalety.

Jeszcze nie podoba mi się motyw z jakaś asynchroniczna transakcja - możesz rozwinąć?


”Engineering is easy. People are hard.” Bill Coughran
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

Tak opcja, w której B ponawia wydaję się najprostsza więc pójdę w tym kierunku z tym, że A raczej będzie zwracać 400 Bad request z jakimś kodem not found. 5xx mi raczej nie pasuje bo to jest raczej błąd klienta, że podał jakiś identyfikator, który nie istnieje.

Asynchroniczna transakcja? Nie do końca rozumiem o co pytasz. Serwis A po prostu wykonuje strzał do B po czym zapisuje jakieś wyniki do mongo. To wszystko jest synchronicznie.


Charles_Ray
  • Rejestracja:prawie 17 lat
  • Ostatnio:minuta
  • Postów:1873
0

To Ci się może przydać: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After

Uwaga: Generalnie „błąd klienta” to 4xx. Takich requestow nie ponawia się by default, bo to bez sensu (np. błędna autoryzacja).

Asynchroniczna transakcja? Nie do końca rozumiem o co pytasz. Serwis A po prostu wykonuje strzał do B po czym zapisuje jakieś wyniki do mongo. To wszystko jest synchronicznie.

Ale mam nadzieje, że serwis A nie czeka na ten callback z serwisu B? Co jeśli callback się nie wydarzy?


”Engineering is easy. People are hard.” Bill Coughran
edytowany 1x, ostatnio: Charles_Ray
LU
Niee nie czeka na callback.
LU
  • Rejestracja:około 11 lat
  • Ostatnio:około miesiąc
  • Lokalizacja:Gdańsk
0

A raczej będzie zwracać 400 Bad request z jakimś kodem not found. 5xx mi raczej nie pasuje bo to jest raczej błąd klienta, że podał jakiś identyfikator, który nie istnieje.

Tutaj mówiąc o błędzie klienta odniosłem się właśnie do 4xx tj. nie możemy przetworzyć callbacku bo podano jakiś id, którego A nie znalazł. Akurat w tym przypadku nawet nie wiem jak miałbym sprawdzić i rozróżnić czy nie znalazł dlatego, że po prostu nie ma czy dlatego, że A jeszcze nie zdążył przetworzyć i zapisać. Wiem, że zwykle 4xx się nie ponawia ale myślę, że dla 404 spokojnie można by było zrobić wyjątek i pewnie często tak się robi ze względu na czas propagacji pomiędzy systemami.


Aventus
  • Rejestracja:prawie 9 lat
  • Ostatnio:ponad 2 lata
  • Lokalizacja:UK
  • Postów:2235
5

Widzę że dużo się tutaj dzieje. Mamy komunikację między serwisami, race conditions, HTTP requests chaining, proces asynchroniczny obejmujący więcej niż jeden serwis który nie jest koordynowany.

Właśnie do takich wyzwań powstały pewne dobre praktyki stosowane w systemach rozproszonych, i warto żebyś rozważył ich zastosowanie jeśli masz taką możliwość. Mowa przede wszystkim o zastosowaniu jakiejś kolejki i message-based communication oraz orkiestracja tego za pomocą process managera.

Ewentualnie możesz po stronie serwisu A użyć retry przy przyjmowaniu requestu od B, skoro wiesz że dane mogą nie być jeszcze dostępne. Ale to będzie jak leczenie objawów a nie przyczyny, bo- zakładając że Twój system ma więcej rozproszonych operacji- będziesz narażał się na szereg innych problemów. Podstawowym problemem jest to że jak sam piszesz masz jakąś transakcje w serwisie, a między czasie rozpoczynasz operacje w innym serwisie licząc na to że transakcja zdąży się wykonać (i zakończy się powodzeniem). To jedno z podstawowych wyzwań systemów rozproszonych, i do tego właśnie stosuje się process managers (lub sagas jeśli chcesz mieć również jakieś operacje kompensujące niepowodzenie).


Na każdy złożony problem istnieje rozwiązanie które jest proste, szybkie i błędne.
edytowany 2x, ostatnio: Aventus
Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)