2011-09-29 7 views
8

Ogłosiłem Spring bean, który odpytuje mój serwer e-mail co sekundę. Jeśli istnieje poczta, pobiera ją i próbuje wyodrębnić dołączone do niej pliki. Pliki te są następnie przesyłane do programu do przesyłania, który bezpiecznie je przechowuje. Przesyłający jest również zadeklarowany jako komponent Spring. Trzeci komponent bean kojarzy nadawcę wiadomości e-mail z nazwą pliku i przechowuje go w bazie danych.Przy domyślnym zasięgu komponentu bean jako pojedynczym, czy nie będzie źle, gdy będą występowały połączenia równoległe?

Okazało się, że gdy kilka osób próbowało wysyłać wiadomości e-mail w tym samym czasie, wydarzyło się mnóstwo bałaganu. Rekordy w DB mają błędne nazwy plików. Niektóre z nich nie otrzymywały nazw plików itp.

Przypisałem problem temu, że fasola ma domyślnie zasięg do singletonu. Oznacza to, że kilka wątków prawdopodobnie miesza się z jednym i tym samym wystąpieniem w tym samym czasie. Pytanie brzmi, jak to rozwiązać.

Jeśli zsynchronizuję wszystkie wrażliwe metody, wszystkie wątki będą układać się w stos i czekać na siebie nawzajem, co jest sprzeczne z całą koncepcją wielowątkowości.

Z drugiej strony, określenie zakresu fasolę na „żądanie” zamierza stworzyć nowe instancje każdego z nich, co nie jest naprawdę dobre albo, jeśli mówimy o zużyciu pamięci i harmonogram wątku

jestem zmieszany. Co powinienem zrobić?

+0

trzeba zsynchronizować swoje metody, ale co ważniejsze, musisz upewnić się, że wywoływane metody nie modyfikują danych instancji, na których opierają się inne wątki. Zgaduję tylko dlatego, że nie dostarczyłeś kodu, ale to chyba znacznie więcej niż prosta synchronizacja metod. Wydaje się, że masz systemowy problem nadpisywania danych, zanim skończysz. –

Odpowiedz

8

Zgadzam się z odpowiedziami @Bozho i @stivio.

Preferowanymi opcjami są: przekazywanie stanu bez magazynu w komponentach o pojedynczej skali i przekazywanie obiektu kontekstowego metodom lub stosowanie złożonych z planszy komponentów, które są tworzone dla każdego cyklu przetwarzania. Zwykle można uniknąć synchronizacji, wybierając jedno z tych podejść i uzyskujesz znacznie większą wydajność, unikając jednocześnie zakleszczeń. Upewnij się, że nie modyfikujesz żadnego stanu współdzielonego, na przykład statycznych członków.

Są plusy i minusy dla każdego podejścia:

  1. Singlton ziarna są czyn jako klasa usług podobnego, co niektórzy mogą powiedzieć, nie są dobrym projektowanie obiektowe.
  2. Przekazywanie kontekstu metodom w długim łańcuchu metod może sprawić, że twój kod będzie brudny, jeśli nie będziesz ostrożny.
  3. Fasolki prototypowe mogą przechowywać dużo pamięci dłużej niż zamierzano i mogą powodować wyczerpanie pamięci. Musisz być ostrożny z cyklem życia tych ziaren.
  4. Fasolka prototypowa może upiększyć Twój projekt. Upewnij się jednak, że nie używasz ponownie fasoli przez wiele wątków.

Mam tendencję do podejścia do usług w najprostszych przypadkach. Możesz również pozwolić, aby te pojedyncze komponenty utworzyły obiekt przetwarzania, który może zachować swój stan do obliczeń. Jest to rozwiązanie, które może Ci najlepiej służyć w przypadku bardziej złożonych scenariuszy.

Edit: Istnieją przypadki, gdy masz singleton fasoli w zależności od prototypu scoped fasoli i chcesz nową instancję fasoli prototypu dla każdego wywołania metody. Spring dostarcza kilka rozwiązań:

Pierwsza z nich to Method Injection, zgodnie z opisem w źródłowej dokumentacji referencyjnej. Nie podoba mi się to podejście, ponieważ zmusza twoją klasę do abstrakcji.

Drugi to użycie ServiceLocatorFactoryBean lub własnej klasy fabrycznej (która musi zostać wstrzyknięta z zależnościami i wywołać konstruktora). To podejście sprawdza się bardzo dobrze w większości przypadków i nie łączy cię z Wiosną.

Są przypadki, gdy chcesz, aby prototypowe komponenty bean miały zależności środowiska wykonawczego. Dobry przyjaciel napisał dobry post na ten temat tutaj: http://techo-ecco.com/blog/spring-prototype-scoped-beans-and-dependency-injection/.

+0

Zgadzam się z tobą i przegłosowałem, z wyjątkiem pkt. 1. Nie poleca się fasoli pojedynczej, ponieważ utrudniają testowanie jednostek, a ponieważ wprowadzają magiczne zależności, ale mówimy o Java Singleton, z prywatnym konstruktorem i statyczną metodą zwrócić instancję. Wiosenne singlety nie mają takich problemów. – stivlo

+0

@stivio kiedy napisałem fasolkę Singleton, miałem na myśli fasolę wiosenną w zakresie pojedynczym, a nie klasę java implementującą wzorzec projektu singleton. Zgadzam się, że późniejsze jest prawdopodobnie najbardziej problematycznym wzorcem w książce GoF, ale pojedyncza fasola sprężyn pojedynczych nie jest wcale trudna do przetestowania - można je utworzyć, wprowadzić interfejsy i pozorować zależności. i przetestuj je jak każdą inną klasę. –

+1

Dokładnie tak właśnie powiedziałem: "Wiosenne singletony nie mają takich problemów". - dlatego nie zgadzam się z punktem 1, zgadzam się, jeśli mówisz o singletonach GoF. – stivlo

12

Fasola o pojedynczej średnicy nie powinna mieć żadnego stanu - zwykle rozwiązuje problem. Jeśli przekazujesz dane tylko jako parametry metody i nie przypisujesz ich do pól, będziesz bezpieczny.

2

W przeciwnym razie po prostu zadeklaruj swoje ziarna jako prośbę, nie martw się o zużycie pamięci, garbage collection wyczyści to, o ile będzie wystarczająco dużo pamięci, nie będzie to również problem z wydajnością.

+0

Jedynym moim problemem jest to, że nie mogę ustawić zakresu "żądania" MailManagera, ponieważ odpytuję pocztę za pomocą aktywatora usług Spring Integration, tj. Nie istnieje tak naprawdę żądanie WWW. Czy nadal mogę oznaczyć program MailManager jako "wątek"? – user802232

+0

użyj "prototypu". – stivlo

+0

Możesz także użyć SimpleThreadScope Spring Core (http://static.springsource.org/spring/docs/3.0.x/javadoc-api/org/springframework/context/support/SimpleThreadScope.html), który wiąże komponent bean z wątkiem -local –

1

Singleton powinien być stanowy i bezpieczny dla wątków.

Jeśli singleton jest bezpaństwowcem, jest to zdegenerowany przypadek bycia stanowym, a wątek bezpieczny jest banalnie prawdziwy. Ale jaki jest sens bycia singletonem? Po prostu stwórz nową za każdym razem, gdy ktoś poprosi.

Jeśli wystąpienie jest stanowe, a nie wątkowe, to nie może to być pojedyncza; każdy wątek powinien mieć wyłącznie inną instancję.

+1

Myślę, że stanowe singletony są dość niebezpieczne (są to skutecznie zmienne globalne). Dlaczego wierzysz, że singletony powinny być państwowe? – sleske

2

Mówiąc abstrakcyjnie: jeśli używasz Spring Integration, powinieneś zbudować swój kod pod względem samych wiadomości. Np. Cały ważny stan powinien być propagowany wraz z wiadomościami. To sprawia, że ​​łatwo jest skalować, dodając więcej instancji Integracji sprężyn do obsługi obciążenia. Jedynym stanem (tak naprawdę) w Spring Integration jest komponent taki jak agregator, który czeka i zbiera komunikaty, które mają korelację. W takim przypadku można przekazać do magazynu kopii, takiego jak MongoDB, aby obsłużyć przechowywanie tych wiadomości, a to jest oczywiście bezpieczne dla wątków.

Mówiąc bardziej ogólnie, jest to przykład architektury opartej na zdarzeniach sterowanych zdarzeniami - komponenty muszą mieć status bezstanowy (N (1) niezależnie od liczby wiadomości), a następnie przesyłać je do kanału w celu wykorzystania przez inny komponent, który nie wie o poprzednim komponencie, z którego pochodzi wiadomość.

razie napotkania problemów wątku bezpieczeństwa Korzystając z integracji wiosna, można robić coś nieco inaczej niż to było zamierzone i być może warto revisiting swoje podejście ...

Powiązane problemy