代码之家  ›  专栏  ›  技术社区  ›  Chris Thornton

哪种网络协议用于远程应用的轻量级通知?

  •  10
  • Chris Thornton  · 技术社区  · 14 年前

    我有这种情况…… 客户机在一台服务器和上万个客户机之间发起了SOAP1.1通信。客户端是外部的,通过我们的防火墙进入,通过证书、https等进行身份验证。它们可以在任何地方,通常有自己的防火墙、NAT路由器等…它们实际上是外部的,而不仅仅是远程公司办公室。他们可能在公司/校园网,DSL/有线电视,甚至拨号。

    客户机使用delphi(2007年的2005+soap补丁),服务器是c,但是从架构/设计的角度来看,这不重要。

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

    我们试着把时间缩短到7秒。

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

    所以我们只能在更快的循环上进行投票了。10k个客户端,每10秒检查一次他们的消息计数,将是每秒1000条消息,这将主要浪费带宽、服务器、防火墙和验证器资源。

    所以我试图设计出比自己造成的dos攻击更好的东西。

    我认为让服务器向客户机发送soap消息(push)并不实际,因为这需要在客户机端进行过多的配置。但我认为还有其他的选择我不知道。例如:

    1)客户机是否有方法通过SOAP1.1请求getMessageCount(),并获得响应,然后可能“保持在线”5-10分钟,以便在新数据到达时获得其他响应?也就是说,服务器说“0”,然后一分钟后响应某个SQL触发器(顺便说一句,服务器是SQL Server上的C),知道这个客户端仍然“在线”,并发送更新的消息计数“5”?

    2)是否有其他协议可用于“ping”客户端,使用从其最后一次getMessageCount()请求中收集的信息?

    3)我都不知道。我想我正在寻找一个神奇的协议,在那里客户端可以发送一个getMessageCount()请求,其中包括“哦,顺便说一句,如果答案在下个小时内改变,在这个地址ping我…”的信息。

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

    外面有这样的东西吗?还是说我几乎被投票困住了?

    蒂亚
    克里斯

    2010年4月30日更新:
    已经证明了拥有7秒的通知既不容易也不便宜,特别是如果不超出https/soap/firewall的公司标准,我们可能会提出一个两阶段的解决方案。phase1将让客户机“按需”轮询getMessageCount,它是通过soap执行的,这里没有什么特别之处。将有一个“刷新”按钮来拉取新数据(这在这里是合理的,因为用户通常有理由怀疑新数据已经准备好,即他们刚刚在联机系统中更改了结构颜色,所以他们知道在桌面上查看装运清单之前要单击刷新,并且他们如何看到描述中的颜色。)(这并不是一个真正的服装/时尚应用程序,但你知道了)。 使用这里讨论的技术,让两个ap始终同步,并从主机推送实时更新的概念仍然存在。但我预计它将被推迟到另一个版本,因为我们可以交付85%的功能,而不必这样做。不过,我希望我们能做一个概念证明,并能证明它会起作用。我会回来发布未来的更新。谢谢大家的帮助。

    10 回复  |  直到 14 年前
        1
  •  3
  •   Jeroen Wiert Pluimers    14 年前

    德尔福的两大多层开发团队是组件4开发人员(他们的 kbmMW 马克·罗宾逊的回答中描述的产品)和他们的产品 RemObjects SDK (他们有一个很好的例子,可能与你想要的相似: Push notifications for iPhone )

    在您的复杂环境中,多播udp可能不会削减它,但从开销的角度来看,它是无与伦比的。

    如果一个连接是开放的,它可以双向使用(这也被.net remoting和wcf使用),但是会有额外的开销。

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

    ——杰罗恩

        2
  •  7
  •   Cosmin Prund    14 年前

    可以考虑“播放”一下http协议,以获得您想要的内容,同时仍然可以浏览客户端可能拥有的所有代理、nat和防火墙。

    让每个客户机对消息计数执行一个普通的http请求,以禁止任何类型的缓存(例如:get http://yourserver.org/getcount/nodeid/timeofday/sequence )。在http服务器端实现中,如果“count”与以前相同(即:没有新消息),则提供答案。

    我这样做是为了一个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请求)。我的LinuxNAT机器现在正在跟踪15K连接,它基本上是空闲的,所以它可能工作。
    • 服务器在任何时候都会为每个客户端的http请求打开一个线程:再说一次,我用来写这篇文章的服务器2008“工作站”(感谢msdn让我做了这么离谱的事情)大约有1500个线程处于活动状态,而且基本上也是空闲的……
    • 根据您用于服务器端代码内存的技术,可能是限制因素。
        3
  •  4
  •   Mark Robinson    14 年前

    我想看看 kbmMW

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

    (至少我是这样理解ms exchange的工作原理的)

        4
  •  3
  •   André    14 年前

    你可以试着给服务器打个电话,然后在服务器上等一段时间(1分钟?)直到你有更新。这样,您就不需要从服务器返回到客户端的连接,而且您几乎可以立即获得到客户端的结果(如果您在1分钟内有更新,则结束等待呼叫)。这是一个相对容易和广泛的(?)被网络应用使用(比如gmail:它有一个这样的后台连接:如果一封新邮件到达,你会立即在收件箱中看到它!).I使用类似于以下内容(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的removobjects和.net的removobjects)。只有在需要连接第三方时才使用soap,只有在由于internet/firewall原因需要时才使用http。soap很好,但是有很高的开销和性能问题。

    您还可以结合使用这些方法:在后台线程中使用简单的(remobjects)tcp连接(开销低),每10秒轮询一次,然后等待5秒以获取新数据。

        5
  •  3
  •   K.Sandell    14 年前

    我在比你的10K客户机还要大的系统上做过性能测试,当你达到所提到的每秒请求量时,你很可能会遇到每秒连接数、并发开放连接、防火墙变慢等问题(这与Torrent Tracker可能面临的问题几乎相同)。

    如果客户端只需要“询问是否有新的东西”,那么最容易实现的轻量级协议是udp,下一个轻量级协议是纯tcp,这两个协议都使用indy客户端。

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

    有了tcp,你就可以让“管道”保持几分钟的开放,并且你可以每隔x秒发送“任何新的”消息。同样,在tcp中,如果客户端定期检查管道中的数据,服务器可以在发生某些事情时将信息“推送”到管道(客户端)。

        6
  •  2
  •   Stephane    14 年前

    我会尽量在多个服务器之间分配负载。为此,我将执行以下操作:

    1. 客户向您的服务注册以获取通知。他们得到的会话id在给定的时间(15分钟)内有效。
    2. 服务器将定期检查哪个注册的客户机有传入消息,并生成此类客户机的列表(从技术上讲,我会将其推送到dmz中的另一个db中)。
    3. 运行许多“推送通知”服务器,这些服务器遵循一个非常非常简单的协议:它们得到一个包含会话id的url查询,并用一个简短的http响应进行响应:要么是404,要么是200,用soap服务器的(签名的)url来寻址,以便获取消息。对于其他性能,可以使用http 1.1和持久连接。
    4. 客户机将根据需要将这些推送通知服务器集中在一起。因为它们非常简单并且是严格的只读的,所以它们可以非常快速地回答查询,并且很容易扩展。
    5. 如果客户机收到302响应,它可以连接到正确的soap服务器(如果需要,也可以将其用于负载分配)并提取消息。

    你得小心这里的安全。首先,我建议您不要对推送通知服务器使用https。相反,您可以使用在客户端请求通知时交换的会话密钥对响应的内容进行签名。然后客户负责验证答案。不要忘记,您不仅需要签署状态,还需要签署soap服务url。

    这有点复杂,但是通过分离状态和实际的消息通信量,您可以更容易地扩展解决方案。另外,在您真正想要交换数据之前,不需要进行昂贵的ssl协商。

        7
  •  2
  •   Roddy    14 年前

    我们使用remobjects sdk“events”进行此操作,但这可能不适合您,因为

    答:它只适用于remobjects自己的二进制协议,而不是soap(即客户机必须包含ro代码)

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

    我试着做一些测试,看看保持10K插座打开的开销到底有多大。如果你只需要一些额外的服务器内存,那将是一个廉价的解决方案。而且因为套接字是从客户端打开的,所以它不应该导致防火墙问题。防火墙所能做的最糟糕的事情就是关闭套接字,因此当这种情况发生时,您的客户端需要重新打开它。

        8
  •  1
  •   skamradt    14 年前

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

    您可以通过简化调用来减少轮询的开销。使用一个简单的web操作将最高的消息编号返回给客户机,并让客户机执行一个简单的http get来接收这个编号。这减少了带宽的数量,并使其保持简单。如果客户机需要获取更新的数据,则可以进行完整的soap调用。

        9
  •  1
  •   Darian Miller    14 年前

    任何时候,只要你有一台服务器和10000多个客户端,你需要每隔几秒钟更新一次,你就会遇到问题。我会得到更多的服务器,并保持客户机连接在一个后台线程的客户机,最初连接,然后等待通知来从服务器与内置的保持活动机制。

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

        10
  •  0
  •   Gregor Brandt    14 年前

    左外野有些东西。

    为什么只需要验证就可以得到一个表示更新准备就绪的标志?为什么不让一台机器在认证防火墙之外…甚至在云中…这只会处理那些“任何可用的”请求。然后,如果有什么可用的话,让客户仔细检查以获得真实的数据。此请求服务器可以从实际服务器执行7秒的getcount。

    我们现在谈论的是非常少的数据和非常少的设置时间为一个简单的'旗帜'甚至没有计数。

    它仍然有数千个请求,但与完全验证的请求相比,它的开销最小。