Какой сетевой протокол использовать для легкого уведомления удаленных приложений?


У меня такая ситуация.... Инициируемое клиентом SOAP 1.1 взаимодействие между одним сервером и, скажем, десятками тысяч клиентов. Клиенты являются внешними, входящими через наш брандмауэр, аутентифицированными по сертификату, https и т. д.. Они могут быть где угодно, и обычно имеют свои собственные брандмауэры, маршрутизаторы NAT и т. д... Они действительно внешние, а не просто удаленные корпоративные офисы. Они могут быть в корпоративной / кампусной сети, DSL / кабель, даже Dialup.

Клиент использует Delphi (2005 + SOAP fixes начиная с 2007 года), и сервер является C#, но с точки зрения архитектуры/дизайна это не должно иметь значения.

В настоящее время клиенты передают Новые данные на сервер и извлекают новые данные с сервера в 15-минутном цикле опроса. Сервер в настоящее время не выталкивает данные - клиент нажимает метод "messagecount", чтобы увидеть, есть ли новые данные для вытягивания. Если 0, он спит еще 15 минут и проверяет снова.

Мы пытаемся сократить это время до 7 секунд.

Если бы это было внутреннее приложение, с один или всего несколько десятков клиентов, мы бы написали cilent" слушатель " soap-сервис и отправили бы ему данные. Но поскольку они являются внешними, сидят за своими собственными брандмауэрами, а иногда и частными сетями за маршрутизаторами NAT, это не практично.

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

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

Я не думаю, что это практично, чтобы сервер отправлял soap-сообщения клиенту (push), поскольку это потребовало бы слишком большой конфигурации на клиентском конце. Но я думаю, что есть альтернативы, о которых я не знаю. Например:

1) Есть ли способ для клиента сделать запрос на GetMessageCount () через Soap 1.1, и получить ответ, а затем, возможно, "остаться на линии" возможно, в течение 5-10 минут, чтобы получить дополнительные ответы в случае поступления новых данных? то есть сервер говорит "0", а через минуту в ответ на какой-то SQL триггер (сервер C# на Sql Server, кстати), знает, что этот клиент все еще" на линии "и отправляет обновленное сообщение кол-во"5"?

2) существует ли какой-то другой протокол, который мы могли бы использовать для "пинга" клиента, используя информацию, собранную из их последнего запроса GetMessageCount ()?

3) я даже не знаю. Наверное, я ищу ... какой-то волшебный протокол, где клиент может отправить запрос GetMessageCount (), который будет включать информацию для "О, кстати, если ответ изменится в течение следующего часа, пингуйте меня по этому адресу...".

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

Есть ли там что-нибудь подобное? Или есть Я в значительной степени застрял с опросом?

ТИА,
Крис

Обновление 4/30/2010:
Продемонстрировав, что 7-секундное уведомление не является ни простым, ни дешевым, особенно без выхода за рамки корпоративного стандарта HTTPS/SOAP/Firewalls, мы, вероятно, предложим двухфазное решение. Фаза 1 будет иметь опрос клиентов "по требованию" с GetMessageCount выполняется через SOAP, ничего необычного здесь нет. Там будет кнопка "обновить", чтобы вытащить новые данные (который является разумно здесь, поскольку пользователь обычно будет иметь основания подозревать, что новые данные готовы, т. е. они просто изменили цвет ткани в онлайн-системе, поэтому они знают, чтобы нажать кнопку Обновить перед просмотром манифеста доставки на рабочем столе, и теперь они видят цвет в описании.) (На самом деле это не приложение для одежды/моды, но вы понимаете идею). Идея о том, чтобы два AP всегда были синхронизированы, с обновлениями в реальном времени, выталкиваемыми из хоста, все еще находится на столе, используя технологии обсуждаемый здесь. Но я ожидаю, что он будет отодвинут на другой релиз, так как мы можем обеспечить 85% функциональности без необходимости делать это. Однако я надеюсь, что мы получим доказательство концепции и сможем продемонстрировать, что она будет работать. Я вернусь и опубликую будущие обновления. Спасибо всем за помощь в этом деле.

10 10

10 ответов:

Двумя крупными участниками многоуровневой разработки в Delphi являются components4developers (с их продуктом kbmMW, описанным в ответе Марка Робинсона) и RemObjects с их продуктомRemObjects SDK (у них есть хороший пример, который может быть похож на то, что вы хотите: Push-уведомления для iPhone ).

В вашей сложной среде многопролитный UDP может не сократить его, но с точки зрения накладных расходов он непобедим.

Если соединение открыто, его можно использовать в двух направлениях (это также используется .NET remoting и WCF), но имеет дополнительные накладные расходы.

Вам нужно будет найти баланс между поддержанием соединения в рабочем состоянии (блокировка ресурсов) и созданием новых соединений (затраты времени и задержки).

-- Йерун

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

Попросите каждого клиента выполнить простой HTTP-запрос для подсчета количества сообщений таким образом, чтобы запретить любой вид кэширования (пример: GET http://yourserver.org/getcount/nodeid/timeofday/sequence ). в серверной реализации задержки HTTP-сервера, предоставляющей ответ, если "count" является то же самое, что и раньше (то есть: никаких новых сообщений).

Я сделал это для приложения в стиле Ajax, которое работает в браузере и ведет себя немного как приложение для чата, но ваше решение может быть еще быстрее. Я реализовал материал на стороне сервера, используя сервер TIdHttp, и это позволило мне фактически отложить предоставление ответа на клиентский материал, просто спящий () - ing в его потоке. Со стороны клиента это выглядело как сервер, который иногда очень медленно дает ответ.

Псевдокод для серверные вещи:

function ClientHasMessages(ClientID:Integer; MaxWait:TDateTime):Boolean;
var MaxTime:TDateTime;
begin
  if ClientActuallyHasMessage(ClientID) then Result := True
  else
    begin
      MaxTime := Now + MaxWait;
      while Now < MaxTime do
      begin
        if ClientActuallyHasMessage(ClientID) then
          begin
            Result := True;
            Exit;
          end
        else
          Sleep(1000);
      end;
      Result := False; // TimeOut
    end;
end;

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

  • он не вызывает сетевого трафика во время ожидания.
  • он не использует процессор во время сна.
  • это позволит пользователю узнать о его сообщении очень быстро.
  • это позволяет клиенту контролировать, как долго может длиться ожидание (клиент увеличит время, в течение которого сервер может задержать ответ до тех пор, пока он больше не получит ответа, а затем немного отступит - таким образом, протокол адаптируется к любому глючному маршрутизатору NAT, который использует клиент).
  • вы можете уйти от длительных периодов отсутствия TCP / IP-связи и все еще быть в состоянии дать ответ мгновенно. 30 секунд легко сделать, и для клиентов с хорошими маршрутизаторами NAT это может быть намного дольше.

Меньший размер этого был бы требованиями на сервере, но я склонен сказать, что они выполнимо:

    Реализация TCP/IP сервера должна отслеживать большое количество одновременных подключений (каждый клиент будет иметь активный HTTP-запрос в любое время). Моя машина Linux NAT отслеживает 15K соединений прямо сейчас, и она в основном простаивает, так что это может сработать.
  • сервер будет иметь поток, открытый для каждого отдельного запроса клиента HTTP, в любое время: опять же, сервер 2008 "Рабочая станция", которую я использую, чтобы написать это (спасибо MSDN за разрешение мне сделать это возмутительные вещи) имеет около 1500 активных потоков, и он также в основном простаивает...
  • в зависимости от технологии, которую вы используете для серверной части кода, память может быть ограничивающим фактором.

Я бы посмотрел на kbmMW

Я бы, возможно, использовал метод, аналогичный MS Exchange-подключение и аутентификация через tcp / ip, затем уведомление об обновлении(обновлениях) от сервера к клиенту через udp, затем клиент получает запрос udp и загружает Обновление через tcp/ip.

(по крайней мере, так я понимаю работу MS Exchange)

Вы можете попробовать позвонить на сервер и подождать на сервере некоторое время (1 минута?) до тех пор, пока у вас не появятся некоторые обновления. Таким образом, вам не нужно подключение обратно от сервера к клиенту, и вы получаете почти мгновенные результаты для клиента (если у вас есть обновления в течение 1 минуты, вы заканчиваете ожидание вызова). Это относительно легко и широко(?) используется веб-приложениями (например, Gmail: у него есть фоновое соединение, например: если приходит новое письмо, вы мгновенно видите его в своем почтовом ящике!). Я использую что-то вроде этого (RemObjects):

function TLoggingViewService.GetMessageOrWait: TLogMessageArray;
begin
  if (Session.Outputbuffer.Count > 0) or
     //or wait till new message (max 3s)
     Session.Outputbuffer.WaitForNewObject(3 * 1000)
  then
    //get all messages from list (without wait)
    Result := PeekMessage;
end;

Отрицательный момент: Вы держите соединение открытым в течение относительно длительного времени (что делать, если соединение потеряно из-за Wi-Fi и т. д.?) и высокая серверная "нагрузка" (каждое соединение имеет поток, держится открытым: если у вас много клиентов, вы можете выйти из ресурсов).

Мы используем RemObjects здесь, и используем TCP + Binmessage, который имеет гораздо более низкие накладные расходы, чем SOAP + HTTP, и очень быстр! Так что, если вы можете использовать это, я действительно могу рекомендовать это! (в вашем случае вам нужны Remobjects для Delphi и RemObjects для .Net). используйте SOAP только в том случае, если вам нужно подключить сторонние устройства, и используйте HTTP только в том случае, если это необходимо из-за интернета/брандмауэра. Мыло приятно, но имеет высокие накладные расходы и проблемы с производительностью.

Вы также можете использовать комбинацию из них: простое (RemObjects) TCP-соединение (с низкими накладными расходами) в фоновом потоке, опрашивая каждые 10s и ожидая 5s для новых данных.

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

Если клиентам нужно только "спросить, есть ли что-то новое", самый легкий протокол, который легко реализовать, - это UDP, следующим самым легким будет чистый TCP, оба с использованием Indy клиенты.

Сам протокол может быть на самом деле так же прост, как отправка "всего нового, начиная с [гггг-ММ-ДД чч:мм:СС]" на сервер, и он отвечает с номером 1 байт (возможно 256 ответов).

С TCP у вас будет дополнительное преимущество в том, что вы будете держать "трубу" открытой в течение нескольких минут, и вы можете отправлять сообщение "что-нибудь новое" каждые x секунд. Также с TCP сервер может "толкать" информацию в канал (клиент(Ы)), когда что-то происходит, учитывая, что клиент проверяет данные в канале. трубите периодически.

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

    Клиенты регистрируются в вашем сервисе для получения уведомлений. Они получают идентификатор сеанса, действительный в течение определенного времени (15 минут).
  1. серверы будут периодически проверять, у какого зарегистрированного клиента есть входящее сообщение, и генерировать список таких клиентов (технически я бы поместил его в другую БД в вашей DMZ).
  2. Ты запустите несколько серверов" push notification", которые следуют очень простому протоколу: они получают URL-запрос, содержащий идентификатор сеанса, и отвечают коротким HTTP-ответом: либо 404, либо 200 с (подписанным) URL-адресом SOAP-сервера, чтобы получить сообщения. Для дополнительной производительности можно использовать HTTP 1.1 и постоянные соединения.
  3. клиент будет объединять эти серверы push-уведомлений так часто, как они хотят. Поскольку они очень просты и строго доступны только для чтения, они может отвечать на запросы очень быстро и будет легко масштабироваться.
  4. Если клиент получает ответ 302, он может подключиться к правильному серверу SOAP (вы также можете использовать его для распределения нагрузки, если хотите) и получить сообщения.

Вы должны быть осторожны с безопасностью здесь. Во-первых, я предлагаю вам не использовать HTTPS для серверов push-уведомлений. Вместо этого можно подписать содержимое ответа сеансовым ключом, которым обмениваются при запросе клиентом уведомлений. Затем клиент несет ответственность за проверку правильности ответа. Не забывайте, что вам нужно подписать не только статус, но и URL-адрес SOAP-сервиса.

Это немного сложно, но, разделив статус и фактический трафик сообщений, вы можете значительно легче масштабировать свое решение. Кроме того, вам не нужно будет проходить дорогостоящее согласование SSL, пока вы действительно не захотите обмениваться данными.

Мы используем RemObjects SDK "события" для этого, но это может быть не подходит для вас, потому что

A: он работает только с собственным двоичным протоколом RemObjects, а не SOAP (т. е. клиенты должны включать код RO)

B: в основном это подход "держать линию открытой". таким образом, масштабируемость до 10 тыс. клиентов является потенциальной проблемой.

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

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

Вы можете уменьшить накладные расходы на опрос, упростив вызов. Используйте простое веб-действие, чтобы вернуть клиенту самый высокий номер сообщения и заставить клиента выполнить простой HTTP GET, чтобы получить этот номер. Это уменьшает объем полосы пропускания и упрощает его. Если тогда клиент должен получить обновленные данные, полный вызов soap может быть сделан.

В любое время, когда у вас есть один сервер и более 10 000 клиентов, и вам нужно получать обновления каждые несколько секунд, вы столкнетесь с проблемами. Я бы получил еще несколько серверов и держал клиентов подключенными в фоновом потоке в клиенте, который сначала подключается, а затем ждет уведомлений от сервера со встроенным механизмом keep alive.

Если вы пытаетесь перейти с сервера на не подключенный в данный момент клиент, то удачи, если у вас нет контроля над среда клиентов. Мне кажется, что вы вынуждены подключаться по инициативе клиента.

Что-то немного не так в левом поле.

Почему аутентификация требуется только для получения флага, который говорит, что обновления готовы? Почему бы не иметь машину вне брандмауэра аутентификации...или даже в облаке... это ничего не делает, но обрабатывает эти запросы "есть что-нибудь доступное". Затем, если что-то доступно, пусть клиент пройдет через обручи, чтобы получить реальные данные. Этот сервер запросов может выполнить 7-секундный getcount с реального сервера.

Мы сейчас говорим очень мало данных и очень мало времени на настройку для простого "флага", даже не считающегося.

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