Клиентский прокси Silverlight 4.0 и WCF - как создавать и закрывать экземпляры - PullRequest
29 голосов
/ 19 августа 2011

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

В настоящее время я использую настраиваемое двоичное связывание в Silverlight 4.0.

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

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

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

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

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

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

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

Ответы [ 2 ]

8 голосов
/ 24 августа 2011

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

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

У меня приличный опыт использования веб-сервисов, и я использую прокси и создаю их экземплярыпрямо перед использованием, а затем позвольте им собирать мусор.У меня никогда не было проблем с этим подходом.Я читал в блог Вэньлонга Донга , в котором говорилось, что создание прокси было дорогостоящим, но даже он говорит, что производительность улучшилась в .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() использование памяти сократилось до половины этой суммы.Это далеко не идеальный тест, и мне кажется, что он подтверждает, что память не просачивалась или пренебрежимо мала (учитывая, что, вероятно, нечасто вызывать 10 000 вызовов веб-служб, используя все экземпляры клиентов).Кроме того, это гораздо более простая модель, чем хранение прокси-объекта и необходимость беспокоиться о его сбое и повторном открытии.

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

0 голосов
/ 27 августа 2011

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

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

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

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

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

...