10

我有这种情况.... 一台服务器与数万个客户端之间的客户端发起的 SOAP 1.1 通信。客户端是外部的,通过我们的防火墙进入,通过证书、https 等进行身份验证。它们可以在任何地方,并且通常有自己的防火墙、NAT 路由器等……它们是真正的外部,而不仅仅是远程公司办公室。它们可以在公司/校园网络、DSL/Cable 甚至拨号中。

客户端使用 Delphi(2005 + 2007 年的 SOAP 修复),服务器是 C#,但从架构/设计的角度来看,这无关紧要。

目前,客户端以 15 分钟的轮询循环将新数据推送到服务器并从服务器拉取新数据。服务器当前不推送数据——客户端点击“messagecount”方法,查看是否有新数据要拉取。如果为 0,它会再休眠 15 分钟并再次检查。

我们正试图将其缩短到 7 秒。

如果这是一个内部应用程序,有一个或几十个客户端,我们会编写一个 cilent“监听器”soap 服务,并将数据推送到它。但由于它们是外部的,位于自己的防火墙后面,有时位于 NAT 路由器后面的专用网络,这是不切实际的。

所以我们只剩下一个更快的循环轮询。10K 个客户端,每个客户端每 10 秒检查一次他们的消息计数,将是 1000 条/秒的消息,这将主要浪费带宽、服务器、防火墙和身份验证器资源。

因此,我正在尝试设计一些比自我造成的 DoS 攻击更好的东西。

我认为让服务器向客户端发送肥皂消息(推送)是不切实际的,因为这需要在客户端进行太多配置。但我认为还有一些我不知道的替代方案。如:

1) 有没有办法让客户端通过 Soap 1.1 请求 GetMessageCount() 并获得响应,然后也许“在线”5-10 分钟以获得额外的响应以防新数据到了?即服务器说“0”,然后一分钟后响应一些SQL触发器(服务器是Sql Server上的C#,顺便说一句),知道这个客户端仍然“在线”并发送更新的消息计数“5 “?

2) 是否有其他协议可以用来“ping”客户端,使用从他们上次 GetMessageCount() 请求中收集的信息?

3)我什至不知道。我想我正在寻找一些魔术协议,客户端可以在其中发送 GetMessageCount() 请求,其中将包括“哦,顺便说一句,如果答案在接下来的一个小时内发生变化,请在这个地址上 ping 我...... ”。

另外,我假设任何这些“保持线路开放”方案都会严重影响服务器的大小,因为它需要同时保持数千个连接打开。我认为这也可能会影响防火墙。

那里有这样的东西吗?还是我几乎被民意调查所困?

TIA,
克里斯

2010 年 4 月 30 日更新:
已经证明拥有 7 秒通知既不容易也不便宜,尤其是在不超出 HTTPS/SOAP/防火墙的企业标准的情况下,我们可能会提出一个两阶段的解决方案。Phase1 将让客户端通过 SOAP 执行 GetMessageCount 来“按需”轮询,这里没什么特别的。会有一个“刷新”按钮来拉取新数据(这在这里是合理的,因为用户通常会有理由怀疑新数据已经准备好,即他们只是在在线系统中更改了面料颜色,所以他们知道点击在桌面上查看运输清单之前刷新,现在他们会看到描述中的颜色。)(这不是真正的服装/时尚应用程序,但你明白了)。让两个应用程序始终保持同步的概念,从主机推送实时更新,仍然在桌面上,使用这里讨论的技术。但我预计它将推迟到另一个版本,因为我们可以提供 85% 的功能而不必这样做。但是,我希望我们能够进行概念验证,并证明它会起作用。我会回来并发布未来的更新。感谢大家对此的帮助。

4

10 回答 10

7

考虑“玩”一下 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;

这段代码背后的想法:它在你自己的服务器上的一个线程中运行,它可以测试消息计数,大概,成本很低:

  • 它在等待时不会导致网络流量。
  • 它在睡眠时不使用 CPU。
  • 它会让用户很快知道它的消息。
  • 它让客户端控制等待的时间(客户端将增加服务器可能延迟答案的时间,直到它不再收到答案,然后退后一点——这样协议就可以适应任何有问题的 NAT客户端使用的路由器)。
  • 您可以摆脱长时间没有 TCP/IP 通信的情况,并且仍然能够立即提供答案。30 秒很容易完成,对于拥有良好 NAT 路由器的客户端来说,它可以更长。

这将是对服务器的要求,但我想说它们是可行的:

  • 服务器的 TCP/IP 实现需要跟踪相当多的同时连接(每个客户端都会有一个始终处于活动状态的 HTTP 请求)。我的 Linux NAT 机器现在正在跟踪 15K 连接,它基本上是空闲的,所以它可能会工作。
  • 服务器将始终为每个客户端 HTTP 请求打开一个线程:同样,我用来编写此内容的 Server 2008“工作站”(感谢 MSDN 允许我做这些离谱的事情)大约有 1500 个线程活跃,它也基本上是空闲的......
  • 根据您用于服务器端代码的技术,MEMORY 可能是限制因素。
于 2010-04-21T10:43:09.173 回答
4

我会看看kbmMW

我可能会使用类似于 MS Exchange 的方法 - 通过 tcp/ip 进行连接和身份验证,然后通过 udp 从服务器向客户端通知更新,然后客户端接收 udp 请求并通过 tcp/ip 下载更新。

(至少这就是我理解 MS Exchange 的工作方式)

于 2010-04-20T15:11:53.937 回答
3

Delphi 中多层开发的两大方是 components4developers(他们的kbmMW产品在 Mark Robinson 的回答中描述)和 RemObjects 及其产品RemObjects SDK(他们有一个很好的例子,可能与您想要的类似:Push notifications for苹果手机)。

在您的复杂环境中,多播 UDP 可能无法解决问题,但从开销的角度来看,它是无与伦比的。

如果连接打开,则可以双向使用(.NET 远程处理和 WCF 也使用它),但有额外的开销。

您需要在保持连接有效(锁定资源)和创建新连接(花费时间和延迟)之间找到平衡。

——杰伦

于 2010-04-20T19:49:08.170 回答
3

您可以尝试调用服务器并在服务器上等待一段时间(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;

不利的一点是:您将连接保持打开相对较长的时间(如果连接由于 wifi 等而丢失怎么办?)和高服务器“负载”(每个连接都有一个线程,保持打开:如果您有很多客户端,您可以获得资源不足)。

我们在这里使用 RemObjects,并使用 TCP + Binmessage,它的开销比 SOAP + HTTP 低得多,而且速度非常快!因此,如果您可以使用它,我真的可以推荐它!(在您的情况下,您需要用于 Delphi 的 Remobjects 和用于 .Net 的 RemObjects)。仅当您需要连接第 3 方时才使用 SOAP,并且由于 Internet/防火墙而仅在需要时才使用 HTTP。SOAP 很好,但有很高的开销和性能问题。

您还可以使用这些组合:后台线程中的简单 (RemObjects) TCP 连接(开销低),每 10 秒轮询一次,并等待 5 秒以获取新数据。

于 2010-04-21T06:18:52.177 回答
3

我已经在比你的 10K 客户端更大的系统上进行了性能测试,当你达到提到的每秒请求数量时,你很可能会遇到每秒连接数、并发打开连接、防火墙变慢等问题。(大致相同Torrent Tracker 可能面临的问题)。

如果客户端只需要“询问是否有任何新内容”,最容易实现的最轻量级协议是 UDP,下一个最轻量级的协议将是纯 TCP,两者都使用 Indy 客户端。

协议本身实际上可以像向服务器发送“自 [yyyy-mm-dd hh:mm:ss] 以来的任何新内容”一样简单,并回复一个 1 字节数(可能有 256 个答案)。

使用 TCP,您将获得额外的好处,即让“管道”保持打开几分钟,并且您可以每 x 秒发送一次“任何新的”消息。同样使用 TCP,服务器可以在发生某些事情时将信息“推送”到管道(客户端),因为客户端正在定期检查管道中的数据。

于 2010-04-24T18:03:04.823 回答
2

我会尝试在多台服务器之间尽可能多地分配负载。为此,我将执行以下操作:

  1. 客户注册您的服务以获得通知。他们获得在给定时间(15 分钟)内有效的会话 ID。
  2. 服务器将定期检查哪个注册客户端有传入消息并生成此类客户端的列表(从技术上讲,我会将其推送到您的 DMZ 中的不同数据库中)。
  3. 您运行多个“推送通知”服务器,它们遵循非常非常简单的协议:它们获取包含会话 ID 的 URL 查询并以简短的 HTTP 响应进行响应:404 或带有(签名)URL 的 200 SOAP 服务器地址以便抓取消息。对于其他性能,您可以使用 HTTP 1.1 和持久连接。
  4. 客户端将根据需要将这些推送通知服务器池化。由于它们非常简单并且是严格只读的,因此它们可以非常快速地回答查询并且很容易向外扩展。
  5. 如果客户端收到 302 响应,它可以连接到正确的 SOAP 服务器(如果需要,您也可以将其用于负载分配)并拉取消息。

您必须注意这里的安全性。首先,我建议您不要将 HTTPS 用于您的推送通知服务器。相反,您可以使用客户端请求通知时交换的会话密钥对响应内容进行签名。然后客户端负责验证答案。不要忘记,您不仅需要对状态进行签名,还需要对 SOAP 服务 URL 进行签名。

这有点复杂,但通过将状态和实际消息流量解耦,您可以更轻松地扩展您的解决方案。此外,在您真正想要交换数据之前,您无需进行昂贵的 SSL 协商。

于 2010-04-29T13:34:51.313 回答
2

我们为此使用 RemObjects SDK“事件”,但这可能不适合您,因为

a:它只适用于 RemObjects 自己的二进制协议,不适用于 SOAP(即客户端必须包含 RO 代码)

b:基本上这是一种“保持线路畅通”的方法。因此,10K 客户端的可扩展性是一个潜在问题。

我会尝试一些测试,看看保持 10K 套接字打开的实际开销是多少。如果您只需要几场额外的服务器内存,那将是一个便宜的解决方案。而且因为套接字是从客户端打开的,所以它不应该引起防火墙问题。防火墙可以做的最糟糕的事情是关闭套接字,因此您的客户端需要在发生这种情况时重新打开它。

于 2010-04-29T13:43:37.687 回答
1

iPhone 的推送通知仅在您的远程设备是 iPhone 时才有效。唯一的其他选项是保持连接打开(尽管大部分时间是空闲的)或从客户端轮询。

您可以通过简化调用来减少轮询的开销。使用简单的 Web 操作将最高的消息编号返回给客户端,并让客户端执行简单的 HTTP GET 来接收此编号。这减少了带宽量,并保持简单。如果此时客户端需要获取更新的数据,则可以进行完整的肥皂调用。

于 2010-04-20T23:44:58.943 回答
1

任何时候,只要您拥有一台服务器和 10,000 多个客户端,并且您需要每隔几秒钟进行一次更新,您就会遇到问题。我会得到更多的服务器,并让客户端在客户端的后台线程上保持连接,该线程最初连接,然后通过内置的保持活动机制等待来自服务器的通知。

如果您尝试从服务器推送到当前未连接的客户端,那么如果您无法控制客户端环境,那么祝您好运。在我看来,您被迫进入客户端发起的连接。

于 2010-04-21T03:38:02.323 回答
0

左外野有些东西。

为什么只需要进行身份验证才能获得表明更新已准备就绪的标志?为什么不在身份验证防火墙之外......甚至在云中......除了处理那些“任何可用的”请求之外什么都不做。然后,如果有可用的东西,让客户端通过箍来获取真实数据。此请求服务器可以从真实服务器执行 7 秒 getcount。

我们现在谈论的数据非常少,而且对于一个简单的“标志”甚至没有计数的设置时间也非常少。

它仍然有数千个请求,但与完整的经过身份验证的请求相比,数千个请求的开销最小。

于 2010-11-11T04:58:44.060 回答