Kiedy powinienem używać kontrolerów Async w ASP.NET MVC?


216

Mam pewne obawy dotyczące używania akcji asynchronicznych w ASP.NET MVC. Kiedy poprawia wydajność moich aplikacji, a kiedy nie ?

  1. Czy dobrze jest używać akcji asynchronicznej wszędzie w ASP.NET MVC?
  2. Odnośnie oczekiwanych metod: czy powinienem używać słów kluczowych asynchronizujących / oczekujących, gdy chcę wykonać zapytanie do bazy danych (przez EF / NHibernate / inną ORM)?
  3. Ile razy mogę użyć słowa kluczowe czekają przeszukiwania bazy danych asynchronicznie w jednej pojedynczej metody działania?

Odpowiedzi:


109

Metody akcji asynchronicznej są użyteczne, gdy akcja musi wykonać kilka niezależnych długich operacji.

Typowym zastosowaniem klasy AsyncController są długotrwałe wywołania usługi sieci Web.

Czy moje połączenia z bazą danych powinny być asynchroniczne?

Pula wątków IIS często może obsłużyć o wiele więcej jednoczesnych żądań blokowania niż serwer bazy danych. Jeśli baza danych stanowi wąskie gardło, wywołania asynchroniczne nie przyspieszą odpowiedzi bazy danych. Bez mechanizmu dławienia efektywne wysyłanie większej ilości pracy do przytłoczonego serwera bazy danych za pomocą wywołań asynchronicznych po prostu przenosi większe obciążenie bazy danych. Jeśli twoje DB jest wąskim gardłem, asynchroniczne połączenia nie będą magiczną kulą.

Ci powinni przyjrzeć się 1 i 2 referencje

Pochodzi z komentarzy @PanagiotisKanavos:

Ponadto asynchronizacja nie oznacza równoległości. Wykonanie asynchroniczne uwalnia cenny wątek puli wątków od blokowania zasobu zewnętrznego, bez komplikacji i kosztów wydajności. Oznacza to, że ta sama maszyna IIS może obsłużyć więcej jednoczesnych żądań, a nie że będzie działać szybciej.

Należy również wziąć pod uwagę, że blokowanie połączeń zaczyna się od intensywnego oczekiwania CPU. W okresach stresu blokowanie połączeń spowoduje eskalację opóźnień i ponowne wykorzystanie puli aplikacji. Połączenia asynchroniczne po prostu tego unikają


27
Jeśli chodzi o asynchronię, IIS i bazę danych, post na blogu jest bardzo stary, a wyciągnięte tutaj wnioski są błędne. Usługi IIS muszą obciążać serwer znacznie większym obciążeniem niż baza danych, wątki pul wątków są ograniczone, a długa kolejka żądań może prowadzić do 500. Wszystko, co zwalnia wątek podczas oczekiwania na IO, jest korzystne. Nawet linki nie dotyczą tego, o co poprosił PO. W rzeczywistości ci sami autorzy napisali, że należy używać metod asynchronicznych DB, gdziekolwiek się da
Panagiotis Kanavos

30
Ponadto asynchronizacja nie oznacza równoległości. Wykonanie asynchroniczne uwalnia cenny wątek puli wątków od blokowania zasobu zewnętrznego, bez komplikacji i kosztów wydajności. Oznacza to, że ta sama maszyna IIS może obsłużyć więcej jednoczesnych żądań, a nie że będzie działać szybciej.
Panagiotis Kanavos

5
Należy również wziąć pod uwagę, że blokowanie połączeń zaczyna się od intensywnego oczekiwania CPU. W okresach stresu blokowanie połączeń spowoduje eskalację opóźnień i ponowne wykorzystanie puli aplikacji. Połączenia asynchroniczne po prostu tego unikają
Panagiotis Kanavos

1
Ponieważ jest to zaakceptowana odpowiedź, a zatem na górze dla większości ludzi, rozsądnie może być usunięcie drugiego akapitu pierwszego dotyczącego czasu wykonania i uruchamiania równoległego, co nie ma nic wspólnego z asynchronizacją. Zdaję sobie sprawę, że na końcu jest nuta, ale jest ona dość myląca.
Rob

1
Wykonanie asynchroniczne uwalnia wątek tylko w przypadkach, gdy jest on naprawdę asynchroniczny aż do samego końca implementacji i używa mechanizmu wywołania zwrotnego do sygnalizowania gotowości oraz do rozwijania nowego wątku w celu przełączenia kontekstu i przetworzenia wyniku. Nie jestem więc pewien, w jakich przypadkach to przełączanie kontekstu / tworzenie nowego wątku jest bardziej wydajne niż czekanie na wyniki w jednym wątku? Długotrwałe połączenia z usługami internetowymi są zresztą złym pomysłem, wolę przenieść je do niezawodnej usługi działającej w tle i pozwolić klientowi sprawdzić wyniki, które mogą przyjść za kilka minut, godzin lub dni.
JustAMartin

229

Pomocny może być mój artykuł MSDN na ten temat ; W tym artykule poświęciłem dużo miejsca opisując, kiedy powinieneś używać asyncna ASP.NET, a nie tylko jak używać asyncna ASP.NET.

Mam pewne obawy dotyczące używania akcji asynchronicznych w ASP.NET MVC. Kiedy poprawia to wydajność moich aplikacji, a kiedy - nie.

Po pierwsze, zrozum, że async/ awaitdotyczy uwolnienia wątków . W aplikacjach GUI chodzi przede wszystkim o zwolnienie wątku GUI, aby wygoda użytkownika była lepsza. W aplikacjach serwerowych (w tym ASP.NET MVC) chodzi głównie o zwolnienie wątku żądania, aby serwer mógł się skalować.

W szczególności nie będzie:

  • Spraw, aby Twoje indywidualne zamówienia były kompletne szybciej. W rzeczywistości ukończą (tylko trochę młodości) wolniej.
  • Wróć do dzwoniącego / przeglądarki, gdy trafisz await. await„ustępuje” tylko puli wątków ASP.NET, a nie przeglądarce.

Pierwsze pytanie brzmi - czy dobrze jest używać akcji asynchronicznej wszędzie w ASP.NET MVC?

Powiedziałbym, że dobrze jest używać go wszędzie tam, gdzie robisz operacje wejścia / wyjścia. Jednak niekoniecznie może być korzystne (patrz poniżej).

Jednak źle jest używać go do metod związanych z procesorem. Czasami deweloperzy myślą, że mogą czerpać korzyści, asyncpo prostu dzwoniąc Task.Rundo kontrolerów, a to jest okropny pomysł. Ponieważ ten kod ostatecznie zwalnia wątek żądania przez zajęcie innego wątku, więc nie ma żadnej korzyści (i w rzeczywistości ponoszą karę za dodatkowe przełączanie wątków)!

Czy powinienem używać słów kluczowych asynchronicznie / oczekując na zapytanie do bazy danych (poprzez EF / NHibernate / inną ORM)?

Możesz użyć wszelkich dostępnych metod. Obecnie większość głównych graczy obsługuje async, ale jest kilka, którzy tego nie robią. Jeśli twój ORM nie obsługuje async, nie próbuj go owijać Task.Runani nic w tym stylu (patrz wyżej).

Zauważ, że powiedziałem „ty mógł użyć”. Jeśli mówisz o ASP.NET MVC z pojedynczym zapleczem bazy danych, to (prawie na pewno) nie odniesiesz żadnych korzyści ze skalowalności async. Wynika to z faktu, że IIS może obsługiwać znacznie więcej współbieżnych żądań niż jedno wystąpienie serwera SQL (lub innego klasycznego RDBMS). Jednakże, jeśli backend jest bardziej nowoczesny - klaster SQL Server, SQL Azure, NoSQL, etc - a backend można skalować, a skalowalność gardłem jest IIS, a następnie można uzyskać korzyści ze skalowalności async.

Trzecie pytanie - ile razy mogę używać słów kluczowych do asynchronicznego przeszukiwania bazy danych w JEDNEJ metodzie pojedynczego działania?

Tyle, ile chcesz. Należy jednak pamiętać, że wiele ORM ma regułę „jedna operacja na połączenie”. W szczególności EF pozwala tylko na jedną operację na DbContext; dotyczy to zarówno operacji synchronicznej, jak i asynchronicznej.

Pamiętaj również o skalowalności swojego backendu. Jeśli trafiasz w jedną instancję SQL Server, a Twój IIS jest już w stanie utrzymać pełną wydajność SQLServera, to podwojenie lub potrojenie nacisku na SQLServera wcale ci nie pomoże.


2
@seebiscuit: Podczas wykonywania (poprawnego asynchronicznego) We / Wy nie są używane żadne wątki. Mam wpis na blogu, który zawiera bardziej szczegółowe informacje.
Stephen Cleary,

2
Ile żądań może obsłużyć IIS i pojedyncza instancja SQL Server? Jak mogę znaleźć te liczby?
MOD,

1
@MOD: To zależy od konfiguracji i sprzętu. Jedynym sposobem na znalezienie tych liczb jest wykonanie testu obciążenia na własnym serwerze.
Stephen Cleary

1
@Flezcano: metody, które wykonują się w pełni na procesorze. Tzn. Nie wykonują operacji wejścia / wyjścia ani blokowania.
Stephen Cleary

1
Przepraszam proszę pana, ale NIE zaakceptuję tego pytania z 2 liniami, które próbuję zrozumieć. Kiedy szukałem tego, na przykład, jeśli mam 1 punkt końcowy interfejsu API, który jest wywoływany przez 1000 użytkowników w tym samym czasie, to czy ten punkt końcowy byłby w stanie do serwera każdego z tych tysięcy żądań, czy powinienem sprawić, aby asynchronizacja obsługiła 1000 żądań?
Uczenie się overtinker-mylić

21

czy dobrze jest używać akcji asynchronicznej wszędzie w ASP.NET MVC?

Jak zwykle w programowaniu, to zależy . Zawsze idzie się na kompromis, gdy idzie się określoną ścieżką.

async-awaitświeci w miejscach, w których wiesz, że będziesz otrzymywać równoległe żądania do swojej usługi i chcesz mieć możliwość skalowania . Jak async-awaitpomaga skalowanie? W przypadku synchronicznego wywoływania asynchronicznego wywołania we / wy , takiego jak połączenie sieciowe lub trafienie do bazy danych, bieżący wątek odpowiedzialny za wykonanie jest blokowany w oczekiwaniu na zakończenie żądania. Gdy używasz async-await, umożliwiasz frameworkowi utworzenie dla ciebie automatu stanów, który zapewnia, że ​​po zakończeniu wywołania We / Wy, twoja metoda kontynuuje wykonywanie od momentu, w którym została przerwana.

Należy zauważyć, że ta maszyna stanu ma subtelny narzut. Uczynienie metody asynchroniczną nie przyspiesza jej wykonania , co jest ważnym czynnikiem do zrozumienia i nieporozumieniem wielu osób.

Inną rzeczą, którą należy wziąć pod uwagę podczas korzystania, async-awaitjest fakt, że jest on asynchroniczny do końca , co oznacza, że ​​zobaczysz, że asynchronizacja przenika cały stos wywołań, od góry do dołu. Oznacza to, że jeśli chcesz udostępnić synchroniczne interfejsy API, często zdarza się, że duplikujesz pewną ilość kodu, ponieważ asynchronizacja i synchronizacja nie mieszają się zbyt dobrze.

Czy powinienem używać słów kluczowych asynchronicznie / oczekując na zapytanie do bazy danych (poprzez EF / NHibernate / inną ORM)?

Jeśli zdecydujesz się pójść ścieżką korzystania z asynchronicznych wywołań we / wy, to tak, async-awaitbędzie dobrym wyborem, ponieważ coraz więcej nowoczesnych dostawców baz danych ujawnia metodę asynchroniczną implementującą TAP (Task Asynchronous Pattern).

Ile razy mogę użyć słowa kluczowego oczekującego do asynchronicznego przeszukiwania bazy danych w JEDNEJ metodzie pojedynczego działania?

Tyle, ile chcesz, pod warunkiem przestrzegania zasad określonych przez dostawcę bazy danych. Nie ma ograniczeń co do liczby połączeń asynchronicznych, które możesz wykonać. Jeśli masz zapytania, które są od siebie niezależne i mogą być wykonywane jednocześnie, możesz obrócić nowe zadanie dla każdego z nich i await Task.WhenAllpoczekać, aż oba zostaną zakończone.


2
Asynchroniczne wywołania db nie są wykonywane szybciej, ale pozwalają tej samej maszynie IIS na serwery o wiele więcej żądań, ponieważ wątki puli wątków nie są marnowane. Obniża to także koszty procesora, ponieważ blokowanie połączeń faktycznie rozpoczyna się od intensywnego oczekiwania procesora przed faktycznym blokowaniem. W sytuacjach dużego obciążenia może to być różnica między zmaganiem się maszyny lub jej awarią a recyklingiem
Panagiotis Kanavos

@PanagiotisKanavos Wiem, czy to było niejasne z tego, co powiedziałem?
Yuval Itzchakov

1
Twoja odpowiedź nie wspomina o szczegółach IIS - scenariusz krachu / recyklingu jest głównym powodem, dla którego należy używać asynchronizacji. Ktoś, kto tego nie doświadczył, prawdopodobnie nie zrozumie, że to scale out wellmoże znaczyć your server won't die under load. Lub może to być przypadekonce burned ...
Panagiotis Kanavos

7

Moje 5 centów:

  1. Użyj async/awaittylko wtedy, gdy wykonujesz operację IO, taką jak DB lub usługa zewnętrzna.
  2. Zawsze preferuj połączenia asynchroniczne z DB.
  3. Za każdym razem, gdy przeszukujesz bazę danych.

PS Istnieją wyjątkowe przypadki dla punktu 1, ale w tym celu musisz dobrze zrozumieć wewnętrzne elementy asynchroniczne.

Dodatkową zaletą jest możliwość jednoczesnego wykonywania kilku połączeń We / Wy w razie potrzeby:

Task task1 = FooAsync(); // launch it, but don't wait for result
Task task2 = BarAsync(); // launch bar; now both foo and bar are running
await Task.WhenAll(task1, task2); // this is better in regard to exception handling
// use task1.Result, task2.Result

6

asyncakcje pomagają najlepiej, gdy akcje wykonują pewne operacje We / Wy do DB lub niektóre wywołania sieciowe, w których wątek przetwarzający żądanie zostanie zatrzymany, zanim otrzyma odpowiedź z wywołania DB lub wywołania sieciowego, które właśnie wywołałeś. Najlepiej, gdy używasz z nimi opcji oczekiwania i naprawdę poprawi to responsywność twojej aplikacji (ponieważ mniej wątków wejściowych / wyjściowych ASP zostanie zablokowanych podczas oczekiwania na DB lub jakąkolwiek inną operację tego typu). We wszystkich moich aplikacjach, ilekroć wiele połączeń do DB jest bardzo potrzebnych, zawsze zawijałem je w przewidywalną metodę i nazwałem to awaitsłowem kluczowym.


5

Jak wiesz, MVC obsługuje kontrolery asynchroniczne i powinieneś z niego skorzystać. W przypadku, gdy kontroler wykonuje długą operację (może to być dyskowe we / wy lub połączenie sieciowe z inną usługą zdalną), jeśli żądanie jest obsługiwane w sposób synchroniczny, wątek IIS jest cały czas zajęty. W rezultacie wątek czeka tylko na zakończenie długiej operacji. Można go lepiej wykorzystać, obsługując inne żądania, gdy operacja żądana w pierwszej kolejności jest w toku. Pomoże to w obsłudze większej liczby równoczesnych żądań. Twoja usługa będzie wysoce skalowalna i nie będzie łatwo napotkać problem z C10k . Dobrym pomysłem jest użycie async / czekaj na zapytania db. i tak, możesz użyć ich tyle razy, ile uznasz za stosowne.

Spójrz tutaj, aby uzyskać doskonałe porady.


3

Z mojego doświadczenia wynika, że ​​dziś wielu programistów używa async/awaitdomyślnie kontrolerów.

Moją sugestią byłoby, abyś używał go tylko wtedy, gdy wiesz, że ci to pomoże .

Powodem jest to, że, jak już wspomniano Stephen Cleary i inni, może wprowadzać problemy z wydajnością, zamiast je rozwiązywać, i pomoże ci tylko w konkretnym scenariuszu:

  • Kontrolery o dużym natężeniu ruchu
  • Skalowalny backend

2

Czy dobrze jest używać akcji asynchronicznej wszędzie w ASP.NET MVC?

Warto to zrobić wszędzie tam, gdzie można zastosować metodę asynchroniczną, zwłaszcza gdy występują problemy z wydajnością na poziomie procesu roboczego, które zdarzają się w przypadku masowych operacji na danych i obliczeniach. W przeciwnym razie nie ma takiej potrzeby, ponieważ testy jednostkowe będą wymagać odlewania.

Odnośnie oczekiwanych metod: czy powinienem używać słów kluczowych asynchronizujących / oczekujących, gdy chcę wykonać zapytanie do bazy danych (przez EF / NHibernate / inną ORM)?

Tak, lepiej jest użyć asynchronizacji dla dowolnej operacji DB, aby uniknąć problemów z wydajnością na poziomie procesów roboczych. Zauważ, że EF stworzył wiele alternatyw asynchronicznych dla większości operacji, takich jak:

.ToListAsync()
.FirstOrDefaultAsync()
.SaveChangesAsync()
.FindAsync()

Ile razy mogę używać oczekujących słów kluczowych do asynchronicznego przeszukiwania bazy danych za pomocą metody pojedynczego działania?

Niebo jest granicą


Logika biznesowa dla większości aplikacji internetowych zwykle wymaga wysoce sekwencyjnych operacji, więc przeskakiwanie między równoległymi procesami w większości przypadków jest wykonalne tylko na najwyższym poziomie zarządzania żądaniami internetowymi / procesami roboczymi, które i tak nie obsługują bezpośrednio żądań bazy danych. Naprawdę chciałbym zobaczyć przykłady typowej aplikacji internetowej w świecie rzeczywistym, w których dobrym pomysłem byłoby przetwarzanie zapytań DB w sposób asynchroniczny.
JustAMartin
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.