Какой сетевой протокол использовать для облегченного уведомления удаленных приложений? - PullRequest
10 голосов
/ 20 апреля 2010

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

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

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

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

Если бы это было внутреннее приложение с одним или несколькими десятками клиентов, мы бы написали молчаливый сервис мыла «слушатель» и отправили бы в него данные. Но поскольку они внешние, находятся за своими собственными брандмауэрами, а иногда и за частными сетями за NAT-маршрутизаторами, это нецелесообразно.

Итак, у нас остался опрос по более быстрой петле. Каждый из 10 000 клиентов, каждый из которых проверяет свой счетчик сообщений каждые 10 секунд, будет представлять собой 1000 / сек. Сообщений, которые в основном будут просто тратить ресурсы полосы пропускания, сервера, брандмауэра и аутентификатора.

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

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

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

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

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

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

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

ТИА
Chris

ОБНОВЛЕНИЕ 30.04.2010:
Продемонстрировав, что 7-секундное уведомление не является ни простым, ни дешевым, особенно без выхода за пределы корпоративного стандарта HTTPS / SOAP / Firewalls, мы, вероятно, собираемся предложить двухфазное решение. Phase1 будет проводить опрос клиентов «по требованию», при этом GetMessageCount будет выполняться через SOAP, здесь ничего особенного. Будет кнопка «обновить» для извлечения новых данных (что вполне разумно, поскольку у пользователя обычно есть основания подозревать, что новые данные готовы, т.е. они просто изменили цвет ткани в онлайн-системе, поэтому они знают, что нужно нажать ОБНОВИТЕ перед просмотром грузового манифеста на рабочем столе, и теперь они видят цвет в описании.) (Это на самом деле не приложение для одежды / моды, но вы поняли идею).Понятие о том, что два aps всегда синхронизированы, а обновления в режиме реального времени отправляются с хоста, все еще обсуждается с использованием технологий, обсуждаемых здесь. Но я ожидаю, что он будет перенесен на другой выпуск, поскольку мы можем предоставить 85% функциональности без необходимости делать это. Тем не менее, я надеюсь, что мы получим подтверждение концепции и сможем продемонстрировать, что это будет работать. Я вернусь и выложу будущие обновления. Спасибо всем за помощь.

Ответы [ 10 ]

7 голосов
/ 21 апреля 2010

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

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

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

Псевдокод для серверной части:

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 отслеживает 15К-соединения прямо сейчас, и она в основном простаивает, поэтому она может работать.
  • На сервере всегда будет открыт поток для каждого отдельного клиентского HTTP-запроса: Опять же, «Рабочая станция» Server 2008, которую я использую, чтобы написать это (спасибо MSDN за предоставленную мне возможность делать такие возмутительные действия), имеет около 1500 активных потоков, и это в основном бездействует ...
  • В зависимости от технологии, которую вы используете для серверного кода, MEMORY может быть ограничивающим фактором.
4 голосов
/ 20 апреля 2010

Я бы посмотрел на кБмМВт

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

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

3 голосов
/ 24 апреля 2010

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

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

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

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

3 голосов
/ 21 апреля 2010

Вы можете попробовать позвонить на сервер и подождать на сервере некоторое время (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 только в том случае, если вам это нужно из-за Интернета / брандмауэра. SOAP хорош, но имеет большие накладные расходы и проблемы с производительностью.

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

3 голосов
/ 20 апреля 2010

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

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

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

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

- Йерун

2 голосов
/ 29 апреля 2010

Для этого мы используем «события» RemObjects SDK, но это может не подойти вам, потому что

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

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

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

2 голосов
/ 29 апреля 2010

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

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

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

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

1 голос
/ 21 апреля 2010

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

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

1 голос
/ 21 апреля 2010

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

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

0 голосов
/ 11 ноября 2010

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

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

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

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

...