将数据推送到客户端,如何处理慢客户端?

时间:2015-05-26 16:55:18

标签: sockets network-programming client-server

在推送模型中,服务器将数据推送到客户端,如何处理带宽较低或可变的客户端?

例如,我从生产者那里接收数据并将数据发送给我的客户(推送)。如果我的一个客户决定下载一个linux iso,那么该客户端的可用带宽太少,无法下载我的数据。

现在,当我的生产者生成数据并且服务器将其推送到客户端时,所有客户端都必须等到所有客户端都下载了数据。当存在一个或多个带宽较少的慢客户端时,这是一个问题。

我可以为每个客户端缓存要发送的数据,但由于数据量很大,这实际上不是一个选项(许多客户端*数据大小=巨大的内存需求)。

这一般如何解决?不需要代码,只有一些想法/想法已经受到欢迎。

2 个答案:

答案 0 :(得分:1)

仅缓存数据一次,让每个客户端处理程序跟踪下载的位置,所有这些都使用相同的缓存。一旦所有客户端都拥有所有数据,就可以删除缓存的数据。

答案 1 :(得分:1)

  

现在,当我的生产者生成数据并且服务器将其推送到   客户,所有客户都必须等到所有客户都有   下载了数据。

以上情况不应该是这样 - 您的客户应该能够彼此异步下载,每个客户端都保持自己的独立下载状态。也就是说,客户端A永远不必等待客户端B完成,反之亦然。

  

我可以缓存每个客户端要发送的数据,但是因为数据   大小很大这不是一个真正的选择(很多客户*数据大小=   巨大的内存要求)。

正如Warren在他的回答中所说,只需保留一份数据而不是每个客户一份副本,就可以减少这个问题。引用计数(例如,通过shared_ptr,如果您使用的是C ++,或者使用其他语言中的等效项),这是确保仅在所有客户端完成下载后才删除共享数据的简单方法。如果需要,您可以通过将数据分成块来使共享更细粒度(例如,不是所有客户端都持有对单个800MB linux iso的引用,您可以将其分解为800个1MB块,以便您可以所有客户端下载后立即从内存中删除较早的块,而不是必须将整个800MB的数据保存在内存中,直到每个客户端都下载了整个内容为止。

当然,这种优化只能让你到目前为止 - 例如如果两个客户端各自请求一个不同的800MB文件,那么除非你想出一个更聪明的解决方案,否则你最终可能会有1.6GB的RAM用于缓存。

以下是您可以尝试的一些可能方法(从较不复杂到更复杂)。您可以单独或组合尝试其中任何一种:

  1. 监控每个客户"积压"是 - 也就是说,保持缓存等待发送到该客户端的数据量。还要跟踪服务器当前持有的缓存数据的字节数;如果该数字太高,请使用最大的积压强制断开客户端,以释放内存。 (当然,这并不能为客户带来良好的用户体验;但如果客户端有错误或缓慢的连接,他就不太可能获得良好的用户体验。它确实可以防止服务器崩溃或交换因为单个客户连接不良而导致死亡

  2. 跟踪服务器已缓存并等待发送的数据量。如果您缓存的数据量太大(对于某些适当的值"太大"),请暂时停止从将数据推送给您的套接字读取(或者如果您是在内部生成数据,暂时停止生成数据)。一旦缓存数据量再次降至可接受的水平,您就可以继续接收(或生成)更多数据以进行推送。

  3. (这可能适用于您的用例也可能不适用)修改您的数据模型,使其不再面向通信,而是面向状态。例如,如果您的目标是更新客户端'状态以匹配数据源的状态,并且您可以将数据源的状态组织成一组键/值对,然后您可以要求数据源包含每个数据的键它发送。每当从数据源接收到键/值对时,只需将该键值对放入每个客户端的映射(或散列表或其他一些面向键/值的数据结构)中(再次使用shared_ptr' s)或类似的,以保持内存使用合理)。每当给定客户端耗尽其传出TCP数据的队列时,从该客户端的键/值映射中删除最旧的项,将其转换为要发送的TCP字节,并将它们添加到传出TCP数据队列。根据需要重复。这样做的好处是"过时"给定密钥的值会自动丢弃在服务器内部,因此永远不需要发送给慢速客户端;相反,慢客户只会得到最新的"该给定键的值。这样做的有益结果是给定客户的最大"积压"无论客户端的带宽有多慢或间歇,都将受到状态模型中密钥数量的限制。因此,慢速客户端可能会看到更少的更新(每秒/分钟/小时),但考虑到其带宽,它所看到的更新仍将尽可能新。