2010-02-05 11 views
15

Mam proces, który może mieć wiele domen aplikacji. Każda AppDomain zbiera pewne statystyki. Po określonym czasie chcę zgromadzić te statystyki i zapisać je w pliku.Współużytkowanie danych między domenami AppDomains

Jednym ze sposobów na to jest Remoting, którego chcę uniknąć.

Jedyną inną techniką, którą mam na myśli jest zapisanie danych każdego AppDomain w pliku, a po określonym czasie jedna z AppDomain zbiera wszystkie dane i gromadzi je.

Ale byłoby idealnie, gdyby to wszystko można było zrobić w pamięci, bez kosztów serializacji informacji przekazywanych między AppDomains. Ktoś ma jakieś pomysły?

Odpowiedz

12

Jedynym sposobem uniknięcia serializacji jest reprezentowanie danych przy użyciu obiektów pochodzących z MarshalByRefObject, ale w takim przypadku koszty zestawiania będą nadal przekraczać granice AppDomain. Może to również obejmować refaktoryzację/ponowne napisanie dużej części twojego kodu.

Zakładając, że zestawianie przez odniesienie nie jest opcją, będziesz musiał serializować w pewnym momencie. Tego po prostu nie da się uniknąć. Jednym ze sposobów, aby to zrobić, jak sugeruje Neil Barnwell, z bazą danych, inny mógłby być z plikiem lokalnym, jak sam sugerujesz.

Innym sposobem, który może, ale nie musi, w zależności od osi czasu dostawy i/lub przyjęcia .NET 4.0, byłoby użycie pliku odwzorowanego w pamięci, patrz .Net Framework 4.0: Using memory mapped files.

+0

Nie napisałem jeszcze kodu. Po prostu pracuję nad projektem. Czy możesz powiedzieć mi o jakimś artykule wyjaśniającym udostępnianie danych za pomocą pierwszego opublikowanego przez Ciebie podejścia? – ata

+0

Marshaling przez odniesienie będzie również szeregować dane, ale w małych kawałkach. Każde wywołanie metody zwróci pewną ilość informacji, które sprawnie przetasują część danych. Jest to prawdopodobnie dobry pomysł, jeśli potrzebujesz tylko niewielkiej części danych. Ale jeśli musisz przetworzyć (prawie) całe dane, pobieranie bit po wielu wywołań w wielu domenach będzie niewiarygodnie powolne w porównaniu do serializowania i przesyłania danych naraz. –

+2

Jeśli podążasz tą drogą, nie zapomnij zastąpić metody InitializeLifetimeService; to doprowadzało mnie do szału kilka dni temu ("Obiekt" ... "został odłączony lub nie istnieje na serwerze.") –

3

Doceniam, że chcesz zachować to w pamięci, ale moją pierwszą propozycją byłoby zapisanie danych w bazie danych i zapytanie z tego miejsca. Remoting nadal jest połączeniem zdalnym, w którym bierze się znaczny "koszt" korzystania z serwera bazy danych, i musiałbyś wbudować obsługę transakcji, aby upewnić się, że nie stracisz danych. Jeśli piszesz do bazy danych SQL Server, masz gotową obsługę transakcji i czeka na ciebie, i jest szybka i szybka dla zapytań.

+0

Choć może to być dobry pomysł, aby korzystać z bazy danych i mieć dane utrzymywały i rozwiązany problem komunikacji z ustalonej technologii, nie sądzę transakcje byłyby kluczową zaletą . Jeśli źródłowe domeny aplikacji ulegną awarii, dane zostaną utracone, bez względu na to, czy po prostu były podłączone do bazy danych, czy do strumienia w pamięci. –

4

Zazwyczaj mówię, że używam zdalnego połączenia. Zapis danych do pliku wymaga również serializacji. Serializacja wydaje się prawie nieunikniona, jakiejkolwiek technologii używasz. Musisz przesłać dane z jednej domeny aplikacji do drugiej za pomocą jakiegoś kanału, a będziesz musiał serializować dane, aby dostać się do kanału.

Jedynym sposobem na uniknięcie serializacji wydaje się być używanie pamięci współdzielonej, aby obie domeny aplikacji mogły uzyskać dostęp do danych bez przechodzenia przez kanał. Nawet głębokie klonowanie danych z jednej domeny aplikacji do pamięci drugiej jest w jej istocie niczym więcej niż serializacją binarną (gdzie wynik niekoniecznie jest zapisywany w kolejnych lokalizacjach pamięci).

+0

Remoting obejmuje również odbicie. To jest Serializacja + Refleksja. Z drugiej strony moje dane to tylko kilka długich i podwójnych wartości, które mogę zapisać w pliku bez większych kosztów. – ata

+6

Patrzysz na złe miejsca. Wąskim gardłem używania pliku jest dostęp do dysku, który zajmuje kilka milisekund, a szybkość transferu pozwala przenieść poniżej sto megabajtów na sekundę. Nie jestem pewien, jakie jest rzeczywiste wąskie gardło zdalnego dostępu (o ile pamiętam, wydajność jest ograniczona liczbą wywołań w wielu domenach, a nie ilością przesłanych danych), ale możliwe jest przesyłanie kilkuset megabajtów na sekundę między domenami aplikacji . Remotowanie ciągów przy użyciu szybkiej ścieżki pozwala uzyskać szybkość transferu rzędu kilku gigabajtów na sekundę. –

20

Możliwe jest udostępnianie danych między AppDomains bez kosztów Marshallingu. Ale jest to dość hackowaty sposób. Można utworzyć obiekt danych źródłowych, który jest współużytkowany przez odniesienie między wszystkimi domenami aplikacji. W ten sposób otrzymasz wszystkie dane w jeden wspólny obiekt bez kosztów Marshallingu. Brzmi zbyt łatwo, aby mogło być prawdziwe?

Pierwszą rzeczą jest wiedzieć, jak udostępniać dane między AppDomains bez Marshalling. W tym celu otrzymujemy adres obiektu twojego obiektu źródła danych przez Marshal.UnsafeAddrOfPinnedArrayElement. Następnie przekazujesz tę IntPtr do wszystkich domen aplikacji, które są tym zainteresowane. W docelowej domenie AppDomain musisz odrzucić tę IntPtr z powrotem do referencji do obiektu, którą można wykonać JIT :: CastAny, która jest wykonywana, jeśli zwrócisz obiekt z metody i popchniesz wskaźnik na stos.

Viola, któremu udostępniłeś obiekt jako zwykły wskaźnik między AppDomains, a otrzymasz InvalidCastExceptions. Problem polega na tym, że musisz ustawić dla wszystkich AppDomains LoaderOptimization.MultiDomain w celu zapewnienia, że ​​zestaw definiujący udostępniony typ danych zostanie załadowany jako typ neutralny AppDomain, który ma ten sam wskaźnik tabeli metod między wszystkimi domenami aplikacji.

Możesz znaleźć przykładową aplikację, która robi to dokładnie jako część WMemoryProfiler. Zobacz ten link, aby uzyskać więcej kodu detailed explanation and download link.

Kod podstawowy jest

[LoaderOptimization(LoaderOptimization.MultiDomain)] 
static public void Main(string[] args) 
{ 

    // To load our assembly appdomain neutral we need to use MultiDomain on our hosting and child domain 
    // If not we would get different Method tables for the same types which would result in InvalidCastExceptions 
    // for the same type. 
    var other = AppDomain.CreateDomain("Test"+i.ToString(), AppDomain.CurrentDomain.Evidence, new AppDomainSetup 
     { 
      LoaderOptimization = LoaderOptimization.MultiDomain, 
     }); 

    // Create gate object in other appdomain 
    DomainGate gate = (DomainGate)other.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, typeof(DomainGate).FullName); 

    // now lets create some data 
    CrossDomainData data = new CrossDomainData(); 
    data.Input = Enumerable.Range(0, 10).ToList(); 

    // process it in other AppDomain 
    DomainGate.Send(gate, data); 

    // Display result calculated in other AppDomain 
    Console.WriteLine("Calculation in other AppDomain got: {0}", data.Aggregate); 
    } 
} 
+0

Naprawdę ładna odpowiedź. Po raz pierwszy miałem wątpliwości, czy dzielenie tych samych obiektów między dwie aplikacje takie jak ta spowodowałoby spustoszenie z przyczyną GC niepoprawionych obiektów (przypięty jest tylko jeden obiekt udostępniony). Ale jak ładnie opisujesz w swoim artykule, istnieje tylko jedna GC we wszystkich AppDomains, więc to się udało. Fajne rzeczy! – nitrogenycs

Powiązane problemy