2009-11-05 8 views
8

Mam aplikację na pulpicie, która działa w sieci i każda instancja łączy się z tą samą bazą danych.Jak używać Mutex w sieci?

W jaki więc sposób mogę zaimplementować muteks działający we wszystkich działających instancjach, które są podłączone do tej samej bazy danych?

Innymi słowy, nie mam dwóch takich przypadków, aby uruchomić tę samą funkcję w tym samym czasie. Jeśli ktoś już korzysta z tej funkcji, pozostałe instancje nie powinny mieć do niej dostępu.


PS: Transakcja bazy danych nie rozwiąże problemu, ponieważ funkcja I wan't mutex nie korzysta z bazy danych. Wspomniałem o bazie danych tylko dlatego, że można jej używać do wymiany informacji między działającymi instancjami.

PS2: Ta funkcja trwa około 30 minut, więc jeśli druga instancja próbuje uruchomić tę samą funkcję, chciałbym wyświetlić miły komunikat, że nie można go teraz wykonać, ponieważ komputer "X" jest już działa ta funkcja.

PS3: Ta funkcja musi zostać przetworzona na komputerze klienta, więc nie mogę korzystać z procedur przechowywanych.

+2

Jaka jest twoja baza danych i czy obsługuje transakcje? – ephemient

Odpowiedz

2

Myślę, że szukasz transakcji bazy danych. Transakcja wyodrębni Twoje zmiany ze wszystkich innych klientów.

Aktualizacja: Wspomniał Pan (i), że funkcja obecnie nie zapisuje danych w bazie danych. Jeśli chcesz muteksować tę funkcję, będzie musiało istnieć centralne miejsce przechowywania aktualnego posiadacza muteksu. Baza danych może działać w tym celu - wystarczy dodać nową tabelę zawierającą nazwę komputera obecnego właściciela. Sprawdź tabelę przed uruchomieniem funkcji.

Myślę, że twoje pytanie może być jednak mylące. Mutexy powinny chronić zasoby. Jeśli twoja funkcja nie ma dostępu do bazy danych, to jakie zasoby współdzielone chronisz?

+0

Właściwie to nie szukam transakcji z bazą danych. Zobacz aktualizacje w pytaniu. –

+4

Aby wykonać muteks, potrzebujesz * jakiejś formy komunikacji między klientami a punktem centralnym. Jeśli masz już połączenie z bazą danych, które obejmuje ** bardzo ** dobrą implementację mutex (bazy danych są przeznaczone dla tego typu rzeczy), dlaczego nie używać? – Wim

0

umieść kod wewnątrz transakcji - w aplikacji lub lepiej - w trybie przechowywanym i wywołaj procedurę zapisaną w bazie. Mechanizm transakcji wyodrębni kod między dzwoniącymi.

0

Odwrotnie rozważ kolejkę komunikatów. Jak już wspomniano, DB powinna zarządzać tym wszystkim w przypadku transakcji lub seryjnego dostępu do tabel (alias MyISAM).

0

W przeszłości Zrobiłem następujące:

  1. Utwórz tabelę, która w zasadzie ma dwa pola FUNCTION_NAME i is_running
  2. nie wiem co RDBMS używasz, ale większość z nich ma sposób blokowania poszczególnych rekordów w celu aktualizacji.Oto pseduocode oparty na Oracle:

    BEGIN TRANS

    SELECT FOR UPDATE is_running OD function_table GDZIE function_name = 'foo';

    - Sprawdź tutaj, aby sprawdzić, czy to działa, czy nie, można ustawić działa na 'true'

    UPDATE function_table zestaw is_running = 'Y', gdzie function_name = 'foo';

    COMMIT TRANS

Teraz nie mam w dokumentacji Oracle PSQL ze mną, ale masz pomysł. Klauzula "DO AKTUALIZACJI" blokuje tam zapis po przeczytaniu, aż do zatwierdzenia, więc inne procesy będą blokować tę instrukcję SELECT dopóki nie zostanie zatwierdzony bieżący proces.

+1

Co się stanie, jeśli uruchomiona instancja zawiesza się w środku funkcji i nigdy nie ma szansy na ser is_running = false? –

+0

W takich przypadkach zazwyczaj umieszczam znacznik czasu w tabeli, a jeśli wiersz ma znacznik czasu, który jest zbyt stary, zakładam, że następne wystąpienie może uruchomić funkcję od początku. –

0

Możesz użyć Terracotta do wdrożenia takiej funkcjonalności, jeśli masz stos Java.

+0

Co to jest terakota? –

+0

Wydaje się za dużo. Poza tym Java nie jest dostępna. –

0

Nawet jeśli twoja funkcja nie korzysta obecnie z bazy danych, nadal możesz rozwiązać problem z określoną tabelą w celu synchronizacji tej funkcji. Specyfika będzie zależeć od twojego DB i jak radzi sobie z poziomami izolacji i blokowaniem. Na przykład w SQL Server ustawisz izolację transakcji na powtarzalny odczyt, odczytaj wartość z wiersza blokującego i zaktualizuj ją w transakcji. Nie zatwierdzaj transakcji, dopóki twoja funkcja nie zostanie wykonana. Można także użyć jawnych blokad tabel w transakcji na większości baz danych, co może być prostsze. Jest to prawdopodobnie najprostsze rozwiązanie, biorąc pod uwagę, że korzystasz już z bazy danych.

Jeśli nie chcesz polegać na bazie danych z jakiegokolwiek powodu, możesz napisać prostą usługę akceptującą połączenia TCP od klienta. Każdy klient zażądałby zezwolenia na uruchomienie i zwrócił odpowiedź po zakończeniu. Serwer byłby w stanie zagwarantować, że tylko jeden klient uzyska zezwolenie na uruchomienie w tym samym czasie. Nieżywi klienci ostatecznie zerwaliby połączenie TCP i zostali wykryte, o ile posiadasz prawidłowe ustawienie podtrzymywania aktywności.

Rozwiązanie kolejki komunikatów sugerowane przez Xepoch również działałoby. Możesz użyć czegoś takiego jak MSMQ lub Java Message Queue i mieć jedną wiadomość, która będzie działać jako token uruchamiania. Wszyscy Twoi klienci będą prosić o wiadomość, a następnie odśwież ją, kiedy skończysz. Ryzykujesz zakleszczenie, jeśli klient umrze przed repostowaniem, więc będziesz musiał opracować jakąś logikę, aby to wykryć i może się to skomplikować.

+0

Rozwiązanie DB przypomina to, co opublikował mjmarsh, ale nie polega na wartości flagi, która musi zostać zresetowana. Twoja funkcja działa, gdy transakcja jest otwarta, a wartość w tabeli nie ma znaczenia.Aby zmodyfikować swój przykład: BEGIN TRANS WYBIERZ ZAKTUALIZACJE blokad FROM function_table WHERE function_name = 'foo'; AKTUALIZACJA function_table set locks = locks + 1 gdzie function_name = 'foo'; - Dokonaj tutaj przetwarzania funkcji COMMIT TRANS –