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

Различные формы интерфейса контракта службы WCF

Кажется, я могу свободно переключаться между тремя различными версиями одного и того же API интерфейса контракта WCF, не нарушая клиентов:

[ServiceContract]
interface IService
{
    // Either synchronous
    // [OperationContract]
    // int SomeMethod(int arg);

    // Or TAP
    [OperationContract]
    Task<int> SomeMethodAsync(int arg);

    // Or APM
    // [OperationContract(AsyncPattern = true)]
    // IAsyncResult BeginSomeMethod(int arg, AsyncCallback callback, object state);
    // int EndSomeMethod(IAsyncResult ar);
}

Существующее тестовое клиентское приложение продолжает работать без перекомпиляции или трогания. Если я перекомпилирую службу и повторно импортирую ее ссылку в клиентское приложение, определение WSDL останется тем же самым, 1:1.

Мои вопросы:

  • Является ли это законным поведением, на которое я могу положиться? Насколько он документирован?

Идея состоит в том, чтобы преобразовать набор синхронных методов SomeMethod -style в методы TAP SomeMethodAsync -style, использовать async/await в своей реализации и тем самым улучшить масштабируемость службы WCF, не нарушая существующих клиентов.

Кроме того, были известны проблемы с масштабированием службы WCF в .NET 3.5 и .NET 4.0. Они задокументированы в статье MSKB "Служба WCF может медленно увеличиваться при загрузке" и в статье CodeProject "Тонкая настройка WCF для сборки высоко масштабируемый асинхронный REST API" . В принципе, было недостаточно для того, чтобы API-интерфейсы сервисных контрактов были естественно асинхронными, среда выполнения WCF все еще блокировала поток запросов.

  • Кто-нибудь знает , если эта проблема была исправлена ​​для .NET 4.5.x, из коробки? Или необходимы дополнительные настройки?
4b9b3361

Ответ 1

Операции WCF могут быть определены с использованием либо синхронного, EAP, либо (как .NET 8.5) TAP. Из MSDN:

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

Фактически вы можете иметь все 3 шаблона в одном интерфейсе контракта, и все они будут относиться к одному и тому же сообщению.

На проводе нет никакой разницы в том, как вы выполняете операции. WSDL (который WCF создает из каждой конечной точки ABC-адрес, привязка и контракт) не содержит этой информации. Он создается из описаний операций.

Если вы посмотрите на класс OperationDescription, который используется в ContractDescription, вы увидите, что каждая операция имеет следующие свойства: SyncMethod, BeginMethod, EndMethod и TaskMethod. При создании описания WCF объединяет все методы в соответствии с именем операции в одну операцию. Если существует некоторая несогласованность между операциями с одним и тем же именем в разных шаблонах (например, разными параметрами), WCF генерирует исключение, в котором точно указано, что именно. WCF автоматически предполагает (необязательно) суффикс "Async" для методов на основе задач и префикс Begin/End для APM.

В этом смысле клиентская и серверная стороны совершенно не связаны. Утилита, которая генерирует прокси-классы из WSDL (svcutil), может создавать прокси для любого шаблона выполнения. Это даже не должно быть WCF-сервисом.

На стороне сервера, если реализовано несколько шаблонов, WCF будет использовать только один из следующих порядков приоритета: Task, Sync и APM. Это документировано где-то в MSDN, я просто не могу найти его прямо сейчас. Но вы можете посмотреть исходный источник здесь.

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

Что касается масштабирования (должен быть другой вопрос IMO)

  • Стандартные значения WCF для дросселирования обновлены в .NET 4.5 до гораздо более разумных значений и теперь зависят от процессора (см. здесь).
  • Никаких изменений в отношении проблемы с пулом потоков. Проблема связана с первоначальным размером пула потоков портов завершения, который изначально устанавливается в 4 раза больше количества логических процессоров. Вы можете использовать ThreadPool.SetMinThreads, чтобы увеличить количество на некоторый коэффициент (см. этот пост). Этот параметр также может быть полезен на стороне клиента.

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

Лучше всего в этих ситуациях сделать много тестов.