Поддержание сходства между группой подписок и сервером почтовых ящиков в Exchange

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

Сходство — это связь последовательности запросов и ответных сообщений с определенным сервером почтовых ящиков. Для большинства функциональных возможностей Exchange сходство обрабатывается сервером. Уведомления, однако, являются исключением. Клиент отвечает за поддержание сходства с сервером почтовых ящиков для подписок на уведомления. Это сходство позволяет подсистеме балансировки нагрузки и серверам клиентского доступа между клиентом и сервером направлять подписки на уведомления и связанные запросы на сервер почтовых ящиков, который обслуживает подписку. Без сопоставления запрос может быть перенаправлен на другой сервер почтовых ящиков, который не включает подписки клиента, что может привести к возврату ошибки ErrorSubscriptionNotFound .

Как поддерживается сходство?

Сходство в Exchange основано на файлах cookie. Клиент активирует создание файла cookie, включив определенные заголовки в запрос подписки, а затем ответ подписки содержит файл cookie. Затем клиент отправляет этот файл cookie в последующих запросах, чтобы убедиться, что запрос направляется на правильный сервер почтовых ящиков.

В частности, сходство в Exchange обрабатывается следующими способами:

  • X-AnchorMailbox — http-заголовок, включенный в первоначальный запрос на подписку. Он определяет первый почтовый ящик в группе почтовых ящиков, которые совместно используют сходство с тем же сервером почтовых ящиков.

  • X-PreferServerAffinity — заголовок HTTP, который включается в первоначальный запрос подписки с заголовком X-AnchorMailbox и имеет значение true, чтобы указать, что клиент запрашивает сохранение сходства с сервером почтовых ящиков.

  • X-BackEndOverrideCookie — файл cookie, включенный в первоначальный ответ подписки и содержащий файл cookie, который подсистема балансировки нагрузки и сервер клиентского доступа используют для маршрутизации последующих запросов на тот же сервер почтовых ящиков.

Разделы справки поддерживать сходство с помощью управляемого API EWS или EWS?

Вы можете использовать те же действия для поддержания сходства для нескольких подписок почтовых ящиков и серверов почтовых ящиков, независимо от того, используете ли вы потоковую передачу, вытягивание или push-уведомления, а также независимо от того, используете ли вы локальный сервер Exchange или Exchange Online.

  1. Для каждого почтового ящика вызовите автообнаружение и получите параметры пользователя GroupingInformation и ExternalEwsUrl. Для автообнаружения SOAP используется элемент Setting , а для автообнаружения POX — элемент GroupingInformation .

  2. Используя параметры GroupingInformation и ExternalEwsUrl из ответов автообнаружения, поместите почтовые ящики с одинаковыми значениями ExternalEwsUrl и GroupingInformation в одну группу. Если какие-либо группы имеют более 200 почтовых ящиков, разбейте группы, чтобы в каждой группе было не более 200 почтовых ящиков.

  3. Создайте и используйте один объект ExchangeService для остальной части процедуры. При использовании одного и того же объекта ExchangeService файлы cookie и заголовки (если они заданы) автоматически сохраняются. Обратите внимание, что если вы не планируете группировать подписки потоковой передачи в одно подключение, вы можете создать другой объект ExchangeService для каждого олицетворенного пользователя.

  4. Отправьте запрос на подписку для пользователя, имя которого отображается первым, когда все пользователи в группе отсортированы в алфавитном порядке (мы будем называть этого пользователя пользователем почтового ящика привязки). Выполните указанные ниже действия.

  • Включите заголовок X-AnchorMailbox со значением SMTP-адреса пользователя почтового ящика привязки.

  • Включите заголовок X-PreferServerAffinity со значением true.

  • Используйте роль ApplicationImpersonation (тип ExchangeImpersonation ).

  1. В ответе подписки получите значение X-BackEndOverrideCookie. Включите это значение в каждый из последующих запросов на подписку для пользователей в этой группе.

  2. Для каждого дополнительного пользователя в группе отправьте запрос на подписку и выполните следующие действия:

  • Добавьте заголовок X-AnchorMailbox со значением SMTP-адреса пользователя почтового ящика привязки для группы.

  • Включите заголовок X-PreferServerAffinity со значением true.

  • Включите X-BackEndOverrideCookie, возвращенный в ответ на подписку пользователя почтового ящика привязки.

  • Используйте роль ApplicationImpersonation (тип ExchangeImpersonation ).

    Обратите внимание, что сервер использует значения X-PreferServerAffinity и X-BackendOverrideCookie вместе для маршрутизации к серверу почтовых ящиков. Заголовок X-AnchorMailbox также является обязательным, но игнорируется сервером, если два других значения допустимы. Если X-AnchorMailbox и X-PreferServerAffinity находятся в запросе и X-BackendOverrideCookie не включены, для маршрутизации запросов используется значение X-AnchorMailbox.

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

  1. Отправьте один запрос GetStreamingEvents или GetEvents для группы и выполните следующие действия.
  • Включите значения SubscriptionId , возвращаемые в каждый отдельный ответ подписки для почтовых ящиков в группе.

  • Если для группы существует более 200 подписок, создайте несколько запросов. Максимальное число значений SubscriptionId для включения в запрос составляет 200.

  • Если требуется больше подключений, чем доступно для целевого почтового ящика, используйте учетную запись службы для олицетворения почтового ящика привязки для группы. В противном случае не используйте олицетворение. В идеале вы хотите олицетворить уникальный почтовый ящик для каждого запроса GetStreamingEvents или GetEvents , чтобы никогда не сталкиваться с ограничениями регулирования.

  • Используйте ApplicationImpersonation, если требуется больше подключений, чем доступно для целевого почтового ящика; В противном случае не используйте ApplicationImpersonation.

  • Включите заголовок X-PreferServerAffinity и присвойте ему значение true. Это значение автоматически включается, если используется объект ExchangeService , созданный на шаге 2.

  • Включите X-BackEndOverrideCookie для группы (X-BackEndOverrideCookie, возвращенный в ответе на подписку пользователя почтового ящика привязки). Это значение автоматически включается, если используется объект ExchangeService , созданный на шаге 2.

  1. Передайте возвращенные события в отдельный поток для обработки.

Какие значения регулирования необходимо учитывать?

При планировании реализации уведомлений необходимо учитывать два значения: количество подключений и количество подписок. В следующей таблице перечислены значения по умолчанию для каждого параметра регулирования и способы их использования. Для каждого значения бюджет выделяется целевому почтовому ящику. По этой причине использование олицетворения для получения дополнительных подключений является обязательным шагом во многих сценариях.

Таблица 1. Значения регулирования по умолчанию

Область рассмотрения Параметр регулирования Значение по умолчанию Описание
Потоковая передача подключений
Ограничение зависания подключения по умолчанию
10 для Exchange Online
3 для Exchange 2013
Максимальное количество одновременных потоковых подключений, которое может одновременно открыть учетная запись на сервере. Для работы в пределах этого ограничения используйте учетную запись службы с ролью ApplicationImpersonation, назначенной для целевых почтовых ящиков, и олицетворяйте первого пользователя в каждой группе идентификаторов подписки при получении потоковой передачи событий.
Подключения по запросу или отправке
EWSMaxConcurrency
27
Максимальное количество одновременных подключений по запросу или отправке (запросов, которые были получены, но еще не ответили), которые учетная запись может одновременно открыть на сервере.
Подписки
EWSMaxSubscriptions
20 для Exchange Online
5000 для Exchange 2013
Максимальное количество невыеченных подписок, которое может одновременно иметь учетная запись. Это значение уменьшается при создании подписки на сервере.

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

  • ServiceAccount1 (sa1) олицетворяет большое количество пользователей (m1, m2, m3 и т. д.) и создает подписки для каждого почтового ящика. Обратите внимание, что при создании подписок владельцем подписки является sa1, поэтому, когда sa1 открывает соединение с подписками, EWS принудительно определяет, что подписки принадлежат sa1.

  • Sa1 может открыть подключение следующими способами:

  1. Без олицетворения, поэтому соединение взимается за sa1.

  2. Путем олицетворения любого из пользователей, например m1, чтобы подключение выставлялось в счет копии бюджета m1. (Сам M1 может открыть десять подключений с помощью Exchange Online, а все учетные записи служб, олицетворяющие M1, могут открывать десять подключений с помощью скопированного бюджета.)

  • Если превышено ограничение подключения, доступны следующие обходные пути:

    • Если используется вариант 1, администратор может создать несколько учетных записей служб для олицетворения дополнительных пользователей.

    • Если используется вариант 2, код может олицетворить другого пользователя, например m2.

Пример. Сохранение сходства между группой подписок и сервером почтовых ящиков

Ладно, давайте посмотрим на это в действии. В следующем примере кода показано, как группировать пользователей и использовать заголовки X-AnchorMailbox и X-PreferServerAffinity и файл cookie X-BackendOverrideCookie для поддержания сходства с сервером почтовых ящиков. Поскольку заголовки и файлы cookie имеют первостепенное значение в истории сопоставления, в этом примере основное внимание уделяется XML-запросам и ответам EWS. Сведения об использовании управляемого API EWS для создания текста запросов и ответов на подписку см. в разделах Потоковая передача уведомлений о событиях почтового ящика с помощью EWS в Exchange и Уведомления по запросу о событиях почтовых ящиков с помощью EWS в Exchange. В этом разделе содержатся дополнительные действия, связанные с поддержанием сходства и добавлением заголовков в запросы.

В этом примере имеется четыре пользователя: alfred@contoso.com, alisa@contoso.com, ronnie@contoso.comи sadie@contoso.com. На следующем рисунке показаны параметры автообнаружения GroupingInformation и ExternalEwsUrl для пользователей.

Рис. 1. Параметры автообнаружения, используемые для группирования почтовых ящиков

Таблица, в которой показаны значения параметров GroupingInformation и ExternalEwsUrl для каждого из пользователей.

Используя параметры из ответов автообнаружения, почтовые ящики группируются по объединенным значениям параметров GroupingInformation и ExternalEwsUrl. В этом примере Альфред и Сэди имеют одинаковые значения, поэтому они находятся в одной группе, а Алиса и Ронни имеют одинаковые значения, поэтому они находятся в другой группе.

Рис. 2. Создание групп почтовых ящиков

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

В этом примере мы сосредоточимся на группе А. Мы будем использовать те же действия для группы B, но для этой группы используется другое значение X-AnchorMailbox.

С помощью ApplicationImpersonation создайте запрос на подписку для почтового ящика привязки (alfred@contoso.com), задав для заголовка X-AnchorMailbox адрес электронной почты, а для параметра X-PreferServerAffinity — значение true. Установка этих двух значений заголовка приведет к тому, что сервер создаст X-BackEndOverrideCookie для ответа.

Если вы используете управляемый API EWS, используйте метод HttpHeadersAdd , чтобы добавить два заголовка в запрос подписки, как показано ниже.

service.HttpHeaders.Add("X-AnchorMailbox", Mailbox.SMTPAddress);
service.HttpHeaders.Add("X-PreferServerAffinity", "true");

Запрос на подписку Альфреда выглядит следующим образом.

POST https://outlook.office365.com/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0516.014
X-AnchorMailbox: alfred@contoso.com
X-PreferServerAffinity: true
Host: outlook.office365.com
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013" />
    <t:ExchangeImpersonation>
      <t:ConnectingSID>
        <t:SmtpAddress>alfred@contoso.com</t:SmtpAddress>
      </t:ConnectingSID>
    </t:ExchangeImpersonation>
  </soap:Header>
  <soap:Body>
    <m:Subscribe>
      <m:StreamingSubscriptionRequest>
        <t:FolderIds>
          <t:DistinguishedFolderId Id="inbox" />
        </t:FolderIds>
        <t:EventTypes>
          <t:EventType>NewMailEvent</t:EventType>
        </t:EventTypes>
      </m:StreamingSubscriptionRequest>
    </m:Subscribe>
  </soap:Body>
</soap:Envelope>

Следующее XML-сообщение является ответом на запрос подписки Альфреда и содержит X-BackEndOverrideCookie. Повторно отправьте этот файл cookie для всех последующих запросов для пользователей в этой группе. Обратите внимание, что ответ также содержит дополнительные файлы cookie, например файл cookie exchangecookie, используемый Exchange 2010. Exchange Online, Exchange Online как часть Office 365, и версии Exchange, начиная с Exchange 2013, игнорируют exchangecookie, если она включена в последующие запросы на подписку.

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Set-Cookie: exchangecookie=ddb8c383aef34c7694132aa679744feb; expires=Thu, 25-Sep-2014 18:42:45 GMT; path=/;
    HttpOnly
Set-Cookie: X-BackEndOverrideCookie=CO1PR06MB222.namprd06.prod.outlook.com~1941996295; path=/; secure; HttpOnly
Set-Cookie: X-BackEndCookie=alfred@contoso.com=Ox8XKzcXLxg==; 
    expires=Wed, 25-Sep-2013 18:52:49 GMT; path=/EWS; secure; HttpOnly
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="15"
                         MinorVersion="0"
                         MajorBuildNumber="775"
                         MinorBuildNumber="7"
                         Version="V2_4"
                         xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SubscribeResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
                         xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:SubscribeResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:SubscriptionId>JgBjbzFwcjA2bWIyMjIubmFtcHJkMDYucHJvZC5vdXRsb29rLmNvbRAAAAAUeGk+7JFdSaFM8/NI/gQQpVdgZX6H0Ag=</m:SubscriptionId>
        </m:SubscribeResponseMessage>
      </m:ResponseMessages>
    </m:SubscribeResponse>
  </s:Body>
</s:Envelope>

Используя X-BackEndOverrideCookie из ответа Альфреда и заголовок X-AnchorMailbox, запрос на подписку создается для Сэди, а другой участник группы A. Запрос на подписку Сэди выглядит следующим образом.

POST https://outlook.office365.com/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0516.014
X-AnchorMailbox: alfred@contoso.com
X-PreferServerAffinity: true
Host: outlook.office365.com
Cookie: X-BackEndOverrideCookie=CO1PR06MB222.namprd06.prod.outlook.com~1941996295
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013" />
    <t:ExchangeImpersonation>
      <t:ConnectingSID>
        <t:SmtpAddress>sadie@contoso.com </t:SmtpAddress>
      </t:ConnectingSID>
    </t:ExchangeImpersonation>
  </soap:Header>
  <soap:Body>
    <m:Subscribe>
      <m:StreamingSubscriptionRequest>
        <t:FolderIds>
          <t:DistinguishedFolderId Id="inbox" />
        </t:FolderIds>
        <t:EventTypes>
          <t:EventType>NewMailEvent</t:EventType>
        </t:EventTypes>
      </m:StreamingSubscriptionRequest>
    </m:Subscribe>
  </soap:Body>
</soap:Envelope>

Ответ на подписку Сэди выглядит следующим образом. Обратите внимание, что он не включает X-BackEndOverrideCookie. Клиент отвечает за кэширование этого значения для будущих запросов.

HTTP/1.1 200 OK
Content-Type: text/xml; charset=utf-8
Set-Cookie: exchangecookie=640ea858f69d47ff8cce8b44c337f6d9; path=/
Set-Cookie: X-BackEndCookie=alfred@contoso.com=Ox8XKzcXLxg==; 
   expires= Wed, 25-Sep-2013 18:53:06 GMT; path=/EWS; secure; HttpOnly
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="https://schemas.xmlsoap.org/soap/envelope/">
  <s:Header>
    <h:ServerVersionInfo MajorVersion="15"
                         MinorVersion="0"
                         MajorBuildNumber="775"
                         MinorBuildNumber="7"
                         Version="V2_4"
                         xmlns:h="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns="https://schemas.microsoft.com/exchange/services/2006/types"
                         xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
  </s:Header>
  <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <m:SubscribeResponse xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages"
                         xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types">
      <m:ResponseMessages>
        <m:SubscribeResponseMessage ResponseClass="Success">
          <m:ResponseCode>NoError</m:ResponseCode>
          <m:SubscriptionId>JgBjbzFwcjA2bWIyMjIubmFtcHJkMDYucHJvZC5vdXRsb29rLmNvbRAAAAB4EQOy2pfrQJfM3hzs/nZJIZssan6H0Ag=</m:SubscriptionId>
        </m:SubscribeResponseMessage>
      </m:ResponseMessages>
    </m:SubscribeResponse>
  </s:Body>
</s:Envelope>

Используя значения SubscriptionId из ответов подписки, для всех подписок в группе был создан запрос на операцию GetStreamingEvents . Так как в этой группе менее 200 подписок, все они отправляются в одном запросе. Заголовок X-PreferServerAffinity имеет значение true и включается X-BackEndOverrideCookie.

POST https://outlook.office365.com/EWS/Exchange.asmx HTTP/1.1
Content-Type: text/xml; charset=utf-8
Accept: text/xml
User-Agent: ExchangeServicesClient/15.00.0516.014
X-AnchorMailbox: alfred@contoso.com
X-PreferServerAffinity: true
Host: outlook.office365.com
Cookie: X-BackEndOverrideCookie=CO1PR06MB222.namprd06.prod.outlook.com~1941996295
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="https://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="https://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="https://schemas.xmlsoap.org/soap/envelope/">
  <soap:Header>
    <t:RequestServerVersion Version="Exchange2013" />
    <t:ExchangeImpersonation>
      <t:ConnectingSID>
        <t:SmtpAddress>sadie@contoso.com</t:SmtpAddress>
      </t:ConnectingSID>
    </t:ExchangeImpersonation>
  </soap:Header>
  <soap:Body>
    <m:GetStreamingEvents>
      <m:SubscriptionIds>
        <t:SubscriptionId>JgBjbzFwcjA2bWIyMjIubmFtcHJkMDYucHJvZC5vdXRsb29rLmNvbRAAAAB4EQOy2pfrQJfM3hzs/nZJIZssan6H0Ag=</t:SubscriptionId>
        <t:SubscriptionId>JgBjbzFwcjA2bWIyMjIubmFtcHJkMDYucHJvZC5vdXRsb29rLmNvbRAAAAAUeGk+7JFdSaFM8/NI/gQQpVdgZX6H0Ag=</t:SubscriptionId>
      </m:SubscriptionIds>
      <m:ConnectionTimeout>10</m:ConnectionTimeout>
    </m:GetStreamingEvents>
  </soap:Body>
</soap:Envelope>

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

Как изменилось сходство?

В Exchange 2010 подписки поддерживаются на сервере клиентского доступа, как показано на рис. 3. В версиях Exchange, более поздних, чем Exchange 2010, подписки поддерживаются на сервере почтовых ящиков, как показано на рис. 4.

Рис. 3. Процесс поддержания сходства в Exchange 2010

Иллюстрация, показывающая принцип поддержки таблицы активных подписок на сервере клиентского доступа в Exchange 2010.

Рис. 4. Процесс поддержания сходства в Exchange Online и Exchange 2013

Иллюстрация, показывающая, как балансировщик нагрузки и сервер клиентского доступа перенаправляют запросы на сервер почтовых ящиков, на котором хранится таблица активных подписок Exchange Server и Exchange Online.

В Exchange 2010 клиент знает только адрес подсистемы балансировки нагрузки, а exchangecookie, возвращенный сервером, гарантирует, что запрос направляется на правильный сервер клиентского доступа. Однако в более поздних версиях подсистема балансировки нагрузки и роли сервера клиентского доступа должны направлять запросы соответствующим образом, прежде чем они попадают на сервер почтовых ящиков. Для этого требуются дополнительные сведения, поэтому появились новые заголовки и файлы cookie. В статье Подписки уведомлений, события почтовых ящиков и EWS в Exchange объясняется, как подписки поддерживаются в Exchange 2013.

Вы можете заметить, что exchangecookie, который использует Exchange 2010, по-прежнему возвращается более поздними версиями. Включение этого файла cookie в запросы не повредит, но более поздние версии Exchange игнорируют его.

См. также