Устранение неполадок MaxConcurrentSessions, превышенных в службе WCF, размещенной в IIS - PullRequest
0 голосов
/ 10 января 2019

Я ухожу из своей зоны комфорта, так что будьте терпеливы с предоставлением соответствующей информации. Мы только что переместили службу WCF, размещенную на IIS, на новый сервер, и клиенты, вызывающие эту службу, начали испытывать тайм-ауты. После утилизации пула приложений все работает нормально в течение примерно 10 минут, а затем все начинается по тайм-ауту. Мы включили трассировку WCF, где я вижу, что превышение MaxConcurrentSessions превышено. В документации сказано, что по умолчанию используется значение 2 x [число процессоров], поэтому для нас должно быть 200.

Сервер находится за балансировщиком нагрузки, но в настоящее время является единственным сервером. Мы замечаем, что в Performance Monitor скорость соединения составляет около 6 в секунду, но после истечения времени ожидания он увеличивается до 30, и оттуда продолжает расти.

Клиенты подключаются с использованием wsHttpBinding TransportWithMessageCredential безопасности. Служба проверяет учетные данные, указанные в сообщении, с помощью поставщика членства asp.net в пользовательском UserNamePasswordValidator, настроенном для использования при привязке к серверу. Клиенты не включают reliableSession на своих привязках. Служба использует значения по умолчанию SessionMode и InstanceContextMode, которые, по моему мнению, Allowed и PerSession соответственно? Мы не вызываем Close для прокси служб, потому что в прошлом исследовании я обнаружил, что это только устанавливает флажок для опции, предотвращающей его повторное использование, и наши всегда все равно выходят из области видимости ... но сейчас делаем тестирование чтобы проверить, закрывает ли это соединение.

Если я правильно интерпретирую журнал трассировки WCF (и я не понимаю большую часть того, что я там читаю), то получается, что мы обрабатываем около 30-40 сообщений в минуту, и что каждый запрос выполняется за меньшее чем 300 мс (обычно намного меньше, в редких случаях почти 1 с.) Я определил количество сообщений, посчитав Processing message n сообщений за несколько минутных промежутков. Так что, если мы получаем 40 в минуту, и для этих соединений / сеансов требуется 100 секунд, чтобы истечь и закрыть тайм-аут, у нас все равно будет только около 68 открытых одновременно, прежде чем начнутся первые тайм-ауты. Не близко к пределу 200. Получает ли соединение для одного запроса клиента более одного сеанса?

Странно то, что у нас раньше не было тайм-аутов, и мы скопировали сервис и файл web.config прямо на новый сервер. Я полагаю, что были обновлены версии сервера и IIS (сервер 2016, IIS 10.) Не могли бы вы помочь мне определить и предоставить соответствующую информацию, чтобы отследить проблему, вызвавшую эти тайм-ауты?

Редактировать
Из моего прочтения кажется, что все указывает на то, что клиент должен вызвать Close, иначе сервер оставит соединение открытым до истечения времени ожидания. Однако в нашем тесте мы видим одно соединение, созданное в perf. ПН но он остается открытым после того, как Close был вызван в любом случае. Поэтому я не могу определить, является ли необходимость звонить по закрытому слуху или мы неправильно истолковываем наш мониторинг. Настоящим тестом было бы позвонить Close везде и посмотреть, устранит ли оно наши таймауты.

После увеличения нашего MaxConcurrentSessions до 400 в мониторе производительности мы увидели, что число одновременных сеансов и экземпляров неуклонно увеличивается примерно на 1 в секунду до примерно 225, где оно, наконец, выровнялось, и оно там колеблется. Похоже, сессии не закрываются.

1 Ответ

0 голосов
/ 14 января 2019

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

  1. Включена трассировка WCF. Прошел по следам и был в состоянии понять достаточно, чтобы в основном видеть, что движение не выглядело необычным. Все события, казалось, были для ожидаемого количества и типов сервисных звонков. Просмотр в svctraceviewer , похоже, это не атака DOS или что-то в этом роде. Мы только что использовали конфигурацию по умолчанию из этой ссылки, но похоже, что она может быть очень настроена для предоставления конкретной информации, которая вам нужна, если вы знаете, что это такое.

  2. Что действительно помогло в этом случае, так это поиск Счетчиков производительности WCF . Первоначально мы использовали счетчики производительности ASP.NET для просмотра открытых сессий, что было неправильной метрикой. Это руководство по проекту кода помогло нам включить счетчики производительности WCF, чтобы дать нам представление о количестве сеансов и ограничении в реальном времени.

  3. Это также помогло понять, как связаны сеансы и экземпляры WCF, а также создать контекст безопасности:

Мы смогли увидеть процент использованных максимальных сессий WCF и наблюдали, как он поднимается все выше и выше, достигая предельного значения по умолчанию 200 (100 на процессор), но в конечном итоге выравнивается между 150 и 200. Это выравнивание вместе с гораздо большим количеством сеансов, существующих в данное время, чем среднее число запросов в минуту, наблюдаемое в нашей трассировке WCF, указывало, что сеансы закрывались, но, казалось, оставались открытыми до истечения времени ожидания, а не закрытия, как только сервер завершил запрос.

Где-то в stackoverflow, которое мне не удалось найти, я однажды спросил о назначении метода [ClientBase<TChannel>.Close][4] (он же метод close прокси-сервера службы WCF) и, несколько ошибочно, пришел к выводу, что все он установил флаг прокси-объекта, отмечающий его закрытие, чтобы его нельзя было использовать снова. Представленное в документации описание метода выглядит следующим образом:

Заставляет объект ClientBase переходить из его текущего состояние в закрытое состояние.

Хорошо, в тот момент, когда я бы назвал Close, мои ссылки всегда просто выходят из области видимости, так что сборщик мусора может очистить его так, что это кажется бессмысленным. Но я думаю, что ключевым фактором было то, что это касалось базовых HttpBindings, которые не имеют состояния. В этом случае мы используем wsHttpBindings, которые сохраняют состояние, что означает, что сервер оставляет сеанс и оставляет соединение открытым после завершения запроса, чтобы последующие вызовы от клиента могли быть сделаны в том же соединении. Итак, хотя я не смог найти никакой документации или отследить в исходном коде, где это происходит, кажется, что клиенты WCF должны вызвать Close на своем прокси-сервере службы после того, как они сделали последний запрос, чтобы сообщить сервер может закрыть соединение и освободить этот сеанс. У меня не было возможности искать сообщение, отправленное на сервер при вызове Close, чтобы сделать это, но мы смогли наблюдать, используя счетчик производительности, количество сеансов, уменьшившихся с 1 до 0, где раньше это было бы оставайтесь на 1 после того, как наш клиент позвонил в службу.

Но мы говорим, что клиент WCF, который мы не можем контролировать, способен нанести ущерб производительности сервера и, возможно, создать отказ в обслуживании, если он не усердно работает в своем коде и не забывает звонить Close и сервер не имеет контроля над собственной производительностью ?? Это звучит как рецепт катастрофы. Ну, есть две вещи, которые вы можете сделать на сервере, чтобы смягчить это. Сначала вы можете увеличить максимальное количество сеансов. В нашем случае мы колеблемся около 175, но иногда при пиках трафика, превышающих 200. Мы временно увеличили его до 800, чтобы не превысить максимум. Компромисс состоит в том, чтобы выделять больше серверных ресурсов для проведения тех сеансов, которые, вероятно, никогда не будут использоваться снова, пока не истечет время ожидания. К счастью, сервер также контролирует время ожидания. Служба может контролировать продолжительность этих сеансов, используя ReceiveTimeout и InactivityTimeout. Оба по умолчанию 10 минут, но будет использоваться меньшее из двух. Если вы думаете: «Тайм-аут приема звучит неправильно. Это определяет количество времени, которое может занять служба для получения большого сообщения», вы не одиноки. Тем не менее, это неправильно . На стороне сервера:

ReceiveTimeout - используется уровнем Service Framework для инициализации тайм-аута простоя сеанса, который контролирует, как долго сеанс может простаивать до истечения времени ожидания.

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

Нужно остерегаться использования Dispose в прокси вашего сервиса. Я всегда пытался набрать .dispo, чтобы посмотреть, не вызовет ли intellisense метод Dispose на моем прокси-сервере, и обнаружил, что он не предполагал, что он не реализует IDisposable и его не нужно закрывать или удалять. Оказывается, он реализует IDisposable, но делает это явно, поэтому вам нужно будет привести его как IDisposable для вызова Dispose. Но ждать! Пока не вставляйте свой прокси в оператор using. Реализация Dispose просто вызывает Close на прокси, который вызывает исключение, если прокси находится в состоянии сбоя (то есть, если вызов службы вызвал исключение). Поэтому вы не можете безопасно делать что-то вроде этого:

using(MyWcfClient proxy = new MyWcfClient())
{
    try
    {
        proxy.Calculate();
    }
    catch(Exception)
    {
    }
}

, поскольку, если Calculate выдает исключение, закрывающая скобка блока using также выдает исключение при попытке утилизировать ваш прокси. Вместо этого вам просто нужно вызвать Close после вашего последнего вызова метода сервиса. Очевидно, вы также можете позвонить Abort в catch, но я не уверен, действительно ли он связывается с сервером для завершения сеанса.

MyWcfClient proxy = new MyWcfClient

try
{
    proxy.Calculate();
    proxy.Close();
}
catch(Exception)
{
    proxy.Abort();
}

Надеюсь, это поможет кому-то в подобной ситуации!

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...