Обмен данными между доменами приложений

У меня есть процесс, который может иметь несколько доменов приложений. Каждый AppDomain собирает некоторую статистику. По истечении указанного времени я хочу накапливать эти статистические данные и сохранять их в файл.

Один из способов сделать это - удаленное взаимодействие, которого я хочу избежать.

Единственная другая техника, которую я имею в виду, - это сохранить данные каждого AppDomain в файл, и по истечении определенного времени один из AppDomain собирает все данные и накапливает их.

Но было бы идеально, если бы все это можно было делать в памяти без затрат на сериализацию информации, передаваемой между доменами приложений. У кого-нибудь есть идеи?

4 ответа

Решение

Единственный способ избежать сериализации - это представить ваши данные, используя объекты, которые являются производными от MarshalByRefObject, но в этом случае вам все равно придется расплачиваться за границы AppDomain. Это может также включать рефакторинг / переписывание большей части вашего кода.

Предполагая, что сортировка по ссылке не является вариантом, вам придется сериализоваться в какой-то момент. Этого просто невозможно избежать. Один из способов сделать это, как предполагает Нил Барнвелл, с базой данных, другой - с локальным файлом, как вы предлагаете сами.

Другим способом, который может или не может быть осуществимым в зависимости от вашей временной шкалы доставки и / или принятия.NET 4.0, было бы использование файла отображения памяти, см. .Net Framework 4.0: Использование файлов отображения памяти.

Можно обмениваться данными между доменами приложений без затрат на Marshalling. Но это довольно хакерский способ. Вы можете создать исходный объект данных, который по ссылке будет использоваться всеми доменами приложений. Таким образом, вы получаете все данные в один общий объект без затрат на Marshalling. Звучит слишком легко, чтобы быть правдой?

Прежде всего, нужно знать, как обмениваться данными между доменами приложений без Marshalling. Для этого вы получите адрес объекта вашего источника данных через Marshal.UnsafeAddrOfPinnedArrayElement. Затем вы передаете этот IntPtr всем доменам приложений, которые заинтересованы в этом. В целевом AppDomain вам нужно привести этот IntPtr обратно к ссылке на объект, что можно сделать JIT::CastAny, что делается, если вы возвращаете объект из метода и помещаете его указатель в стек.

Виола, вы разделили объект как простой указатель между доменами приложений, и вы получите InvalidCastExceptions. Проблема заключается в том, что вы должны установить для всех ваших доменов приложений LoaderOptimization.MultiDomain, чтобы гарантировать, что сборка, определяющая общий тип данных, загружается как нейтральный тип AppDomain, имеющий одинаковый указатель таблицы методов между всеми доменами приложений.

Вы можете найти пример приложения, которое делает именно это как часть WMemoryProfiler. Посмотрите эту ссылку для более подробного объяснения и загрузите ссылку на пример кода.

Основной код

[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);
    }
}

Я имею в виду просто использовать удаленное взаимодействие. Запись данных в файл также требует сериализации. Сериализация кажется почти неизбежной, какую бы технологию вы ни использовали. Вы должны передавать данные из одного домена приложения в другой, используя какой-либо канал, и вам придется сериализовать данные, чтобы получить их через канал.

Похоже, что единственный способ избежать сериализации - использовать разделяемую память, чтобы оба домена приложений могли обращаться к данным, даже не проходя через канал. Даже глубокое клонирование данных из памяти одного домена приложения в память другого по своей сути представляет собой не что иное, как двоичную сериализацию (где результат не обязательно сохраняется в последовательных ячейках памяти).

Я действительно ценю, что вы хотите сохранить это в памяти, но моим первым предложением будет записать данные в базу данных и запросить оттуда. Удаленное взаимодействие по-прежнему является удаленным вызовом, из которого берется большая часть "затрат" на использование сервера базы данных, и вам придется встроить обработку транзакций, чтобы не потерять данные. Если вы пишете в базу данных SQL Server, у вас есть готовая и ожидающая поддержка транзакций, и она быстро-быстро-быстро для запросов.

Другие вопросы по тегам