Подтвердить что ты не робот

Silverlight 4.0 и клиентский прокси WCF - как создавать и как закрывать экземпляры

Тема жизненного цикла прокси-сервера Silverlight WCF для меня не очень понятна. Я читал различные материалы, ресурсы, ответы здесь, но все же я не совсем понимаю предполагаемый лучший способ их использования.

Я использую собственную бинарную привязку в Silverlight 4.0 в настоящее время.

Является ли создание прокси в silverlight дорогостоящей операцией? Должны ли мы попытаться поделиться экземпляром прокси в коде или создать новое лучше? Должны ли мы делать блокировку, если мы разделяем ее, если к ней обращаются несколько потоков?

Так как ошибка прокси-сервера приведет к ошибке состояния прокси-сервера, я думаю, что совместное использование прокси-сервера - это не очень хорошая идея, но я прочитал, что создание дорогое, поэтому его не на 100% понятно, что делать здесь.

И с закрытием - клиенты службы Silverlight WCF предоставляют только метод CloseAsync. Кроме того, прокси-серверы требуют, чтобы определенная логика использовалась, когда они были закрыты (если они ошибочны, мы должны вызывать Abort(), которая является синхронной в Silverlight, и если мы не должны закрывать, что не синхронно или что?).

Во многих официальных образцах Silverlight от MS-прокси не закрыты вообще, это только недостаток материалов или ожидаемый подход к ним?

Тема очень важна для меня, и я хочу получить четкое представление обо всех вещах, которые следует учитывать, которых я в настоящее время не имею.

(Я видел, что этот вопрос Какой правильный жизненный цикл прокси-сервера службы WCF в Silverlight 3? появляется рядом с моим, но я не могу сказать, что я я удовлетворен качеством ответов)

Мне бы очень хотелось увидеть пример кода, который использует, создает, закрывает и т.д. WCF-прокси, и, самое главное, объясняет, почему это наилучший способ. Я также думаю (в настоящее время считаю), что из-за природы проблемы должен быть единый подход к использованию (создание, повторное использование, закрытие) WCF-прокси в Silverlight.

4b9b3361

Ответ 1

Резюме. Я считаю, что наилучшей практикой является создание экземпляра вашего клиента веб-сервиса, когда вы собираетесь его использовать, а затем пусть он выходит из сферы действия и собирает мусор. Это отражено в образцах, которые вы видите в Microsoft. Обоснование следует...

Полный. Лучшее полное описание процесса, который я нашел, находится в Как получить доступ к службе из Silverlight. Здесь пример показывает типичный шаблон создания экземпляра клиента веб-службы и позволяет ему выйти из сферы действия (без необходимости его закрытия). Клиенты веб-сервиса наследуют от ClientBase, у которого есть метод Finalize, который должен освобождать любые неуправляемые ресурсы, если это необходимо, когда объект мусор собирается.

У меня есть приличный опыт работы с веб-службами, и я использую прокси-серверы и создаю их прямо перед использованием, а затем позволяю им собирать мусор. У меня никогда не было проблемы с этим подходом. Я читал в Wenlong Dong Blog, в котором говорилось, что создание прокси было дорого, но даже он говорит, что производительность улучшилась в .NET 3.5 (возможно, с тех пор она улучшилась?). Я могу сказать вам, что производительность - относительный термин, и если ваши данные не будут получены меньше, чем тривиально, гораздо больше времени будет потрачено на сериализацию/десериализацию и транспортировку, чем на создание соединения. Это, безусловно, был мой опыт, и вам лучше оптимизировать в этих областях в первую очередь.

Наконец, поскольку я считаю, что мое мнение пока может быть недостаточным, я написал быстрый тест. Я создал веб-службу с поддержкой Silverlight, используя шаблон, предоставленный с помощью Visual Web Developer 2010 Express (с использованием метода void по умолчанию DoWork()). Затем в моем примере Silverlight клиент я вызвал его, используя следующий код:

int counter=0;
public void Test()
{
    ServiceReference1.Service1Client client = new ServiceReference1.Service1Client();
    client.DoWorkCompleted += (obj, args) => 
    { 
        counter++;
        if (counter > 9999)
        {
            for(int j=0;j<10;j++) GC.Collect();
            System.Windows.MessageBox.Show("Completed");
        }
    };
    client.DoWorkAsync();
}

Затем я вызвал метод Test с помощью for(int i=0;i<10000;i++) Test(); и запустил приложение. Потребовалось чуть более 20 секунд, чтобы загрузить приложение и завершить вызов веб-службы (все 10000 из них). По мере того, как делались вызовы веб-служб, я видел, что использование памяти для процесса переходит на более чем 150 МБ, но как только завершенные вызовы и GC.Collect() были названы, использование памяти упало до менее половины этой суммы. Мне кажется, что это далеко не идеальный тест, который подтверждает мне, что память не протекала, или она была незначительной (учитывая, что, как правило, нередко вызывать 10000 вызовов веб-сервисов с использованием отдельных клиентских экземпляров). Кроме того, это гораздо более простая модель, чем поддержание прокси-объекта и необходимость беспокоиться о его сбое и его повторном открытии.

Обоснование методологии тестирования: Мой тест был посвящен двум потенциальным проблемам. Одна из них - утечка памяти, а другая - время процессора, затрачиваемое на создание и уничтожение объектов. Моя рекомендация заключается в том, что безопасно следовать примерам, предоставленным компанией (Microsoft), которая поставляет эти классы. Если вас беспокоит эффективность сети, то у вас не должно быть проблем с моим примером, поскольку правильное создание/удаление этих объектов не повлияет на латентность сети. Если 99% времени тратится на сетевое время, то оптимизация для теоретического улучшения в 1%, вероятно, является расточительной с точки зрения времени разработки (при условии, что есть даже выигрыш, который, как мне кажется, мой тест ясно показывает, что мало/никто). Да, сетевые вызовы были локальными, а это означает, что в течение 10 000 вызовов службы будет потрачено всего около 20 секунд на ожидание объектов. Это составляет ~ 2 миллисекунды за вызов службы, потраченный на создание объектов. Что касается необходимости вызова Dispose, я не хотел подразумевать, что вы не должны его называть, просто потому, что это не представляется необходимым. Если вы забудете (или просто не захотите), мои тесты заставили меня поверить, что Dispose вызывается в Finalize для этих объектов. Тем не менее, вероятно, было бы более эффективно вызывать Dispose самостоятельно, но все же эффект пренебрежимо мал. Для большинства программных разработок вы получаете больше преимуществ от разработки более эффективных алгоритмов и структур данных, чем путем уклонения от таких проблем (если нет серьезной утечки памяти). Если вам нужна более эффективная работа, возможно, вам не следует использовать веб-службы, поскольку существуют более эффективные параметры транзита данных, чем система, основанная на XML.

Ответ 2

Создание прокси-сервера не дорого по сравнению с обратным вызовом. Я видел комментарий, который говорит, чтобы вызвать CloseAsync сразу после ваших вызовов другим асинхронным методам, но это кажется ошибочным. Теоретически закрытие переходит в состояние ожидания и происходит после завершения других асинхронных вызовов. На практике, я видел, что вызовы заканчиваются неожиданно быстро с ошибкой, а затем сам CloseAsync вызывает ошибки, поскольку канал уже сработал.

То, что я сделал, это создать общий Caller<TResult> класс, который имеет несколько перегруженных методов CallAsync, которые принимают аргументы и обратный вызов типа Action<TResult>. Под обложками Caller будет подключаться к событию XxxCompleted и проверять, была ли причина завершения ошибкой или нет. Если ошибка, это будет Abort канал. Если нет ошибки, она вызовет CloseAsync, а затем вызовет обратный вызов. Это предотвращает "глупые" ошибки, например, попытку использования канала в неисправном состоянии.

Все вышесказанное предполагает модель create-proxy-make-call-discard-use-of-proxy. Если вам нужно было сделать много звонков быстро, вы можете адаптировать этот подход для подсчета количества вызовов в полете, а по завершении последнего - закрыть или прервать.

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

Что касается вашего вопроса о том, что примеры не закрываются... ну, это примеры и обычно одномерные и неполные, потому что они иллюстрируют общее использование, а не все особенности того, что вам нужно делать в течение всего срок службы приложения.