向客户端推送数据,如何处理慢速客户端?
Push data to client, how to handle slow clients?
在推送模型中,服务器将数据推送到客户端,如何处理带宽较低或可变的客户端?
例如,我从生产者那里接收数据并将数据发送给我的客户(推送)。如果我的一位客户决定下载 linux iso,该客户端的可用带宽变得太少,无法下载我的数据怎么办。
现在当我的生产者生产数据,服务器推送给客户端时,所有客户端都必须等到所有客户端都下载完数据。当有一个或多个带宽很小的慢客户端时,这是一个问题。
我可以为每个客户端缓存要发送的数据,但是因为数据量很大,所以这不是一个真正的选择(很多客户端 * 数据量 = 巨大的内存需求)。
一般是怎么解决的?无需代码,只需几个 thoughts/ideas 就已经很受欢迎了。
只缓存数据一次,并让每个客户端处理程序跟踪它在下载中的位置,所有这些都使用相同的缓存。一旦所有客户端都拥有所有数据,就可以删除缓存数据。
Now when my producers produces data and the server pushes it to the
client, all clients will have to wait until all clients have
downloaded the data.
以上情况不应该如此——您的客户端应该能够相互异步下载,每个客户端都保持自己独立的下载状态。也就是说,客户端 A 永远不必等待客户端 B 完成,反之亦然。
I can cache the data to be send for every client, but because the data
size is big this isn't really an option (lots of clients * data size =
huge memory requirements).
正如沃伦在他的回答中所说,可以通过只保留一份数据副本而不是每个客户端一份副本来减少这个问题。引用计数(例如,通过 shared_ptr,如果您使用的是 C++,或其他语言中的等效内容)是一种确保共享数据仅在所有客户端完成下载后才被删除的简单方法。如有必要,您可以通过将数据分成块来使共享更细粒度(例如,不是所有客户端都持有对单个 800MB linux iso 的引用,您可以将其分成 800 个 1MB 块,这样您就可以在所有客户端下载它们后立即从内存中删除较早的块,而不是必须将整个 800MB 数据保存在内存中,直到每个客户端都下载了整个数据)
当然,这种优化只能让你到此为止——例如如果两个客户端各自请求一个不同的 800MB 文件,那么除非您想出更聪明的解决方案,否则您最终可能会使用 1.6GB 的 RAM 进行缓存。
这里有一些您可以尝试的方法(从不那么复杂到更复杂)。您可以单独或组合尝试其中任何一个:
监控每个客户端的 "backlog" 是多少——也就是说,统计您缓存的等待发送给该客户端的数据量。还要跟踪服务器当前保存的缓存数据的字节数;如果该数字太高,则强制断开具有最大积压的客户端,以释放内存。 (当然,这不会给客户端带来良好的用户体验;但如果客户端有故障或连接速度慢,他无论如何都不太可能获得良好的用户体验。它确实可以防止您的服务器崩溃或自行切换到死亡,因为单个客户端连接不良)
跟踪您的服务器缓存了多少数据并等待发送。如果您缓存的数据量太大(对于某些适当的 "too large" 值),请暂时停止从将数据推送给您的套接字中读取(或者如果您正在内部生成数据,暂时停止生成)。一旦缓存数据量再次下降到 acceptable 水平,您可以恢复接收(或生成)更多数据以推送。
(这可能适用于您的用例,也可能不适用于您的用例)修改您的数据模型,使其不再面向通信,而是面向状态。例如,如果您的目标是更新客户端的状态以匹配数据源的状态,并且您可以将数据源的状态组织成一组 key/value 对,那么您可以要求数据-source 在它发送的每条数据中包含一个密钥。每当从数据源接收到 key/value 对时,只需将该键值对放入每个客户端的映射(或散列 table 或其他面向 key/value 的数据结构)(再次,在这里使用 shared_ptr's 或类似的东西来保持合理的内存使用)。每当给定客户端耗尽其传出 TCP 数据队列时,从该客户端的 key/value 映射中删除最旧的项目,将其转换为要发送的 TCP 字节,并将它们添加到传出 TCP 数据队列。根据需要重复。这样做的好处是给定键的 "obsolete" 值会自动在服务器内部删除,因此永远不需要发送给慢速客户端;相反,慢速客户端只会获得给定键的 "latest" 值。这样做的好处是给定客户端的最大值 "backlog" 将受到状态模型中密钥数量的限制,无论客户端的带宽有多慢或间歇性如何。因此,一个缓慢的客户端可能会看到较少的更新(根据 second/minute/hour),但它看到的更新仍然是尽可能最近的,因为它的带宽。
在推送模型中,服务器将数据推送到客户端,如何处理带宽较低或可变的客户端?
例如,我从生产者那里接收数据并将数据发送给我的客户(推送)。如果我的一位客户决定下载 linux iso,该客户端的可用带宽变得太少,无法下载我的数据怎么办。
现在当我的生产者生产数据,服务器推送给客户端时,所有客户端都必须等到所有客户端都下载完数据。当有一个或多个带宽很小的慢客户端时,这是一个问题。
我可以为每个客户端缓存要发送的数据,但是因为数据量很大,所以这不是一个真正的选择(很多客户端 * 数据量 = 巨大的内存需求)。
一般是怎么解决的?无需代码,只需几个 thoughts/ideas 就已经很受欢迎了。
只缓存数据一次,并让每个客户端处理程序跟踪它在下载中的位置,所有这些都使用相同的缓存。一旦所有客户端都拥有所有数据,就可以删除缓存数据。
Now when my producers produces data and the server pushes it to the client, all clients will have to wait until all clients have downloaded the data.
以上情况不应该如此——您的客户端应该能够相互异步下载,每个客户端都保持自己独立的下载状态。也就是说,客户端 A 永远不必等待客户端 B 完成,反之亦然。
I can cache the data to be send for every client, but because the data size is big this isn't really an option (lots of clients * data size = huge memory requirements).
正如沃伦在他的回答中所说,可以通过只保留一份数据副本而不是每个客户端一份副本来减少这个问题。引用计数(例如,通过 shared_ptr,如果您使用的是 C++,或其他语言中的等效内容)是一种确保共享数据仅在所有客户端完成下载后才被删除的简单方法。如有必要,您可以通过将数据分成块来使共享更细粒度(例如,不是所有客户端都持有对单个 800MB linux iso 的引用,您可以将其分成 800 个 1MB 块,这样您就可以在所有客户端下载它们后立即从内存中删除较早的块,而不是必须将整个 800MB 数据保存在内存中,直到每个客户端都下载了整个数据)
当然,这种优化只能让你到此为止——例如如果两个客户端各自请求一个不同的 800MB 文件,那么除非您想出更聪明的解决方案,否则您最终可能会使用 1.6GB 的 RAM 进行缓存。
这里有一些您可以尝试的方法(从不那么复杂到更复杂)。您可以单独或组合尝试其中任何一个:
监控每个客户端的 "backlog" 是多少——也就是说,统计您缓存的等待发送给该客户端的数据量。还要跟踪服务器当前保存的缓存数据的字节数;如果该数字太高,则强制断开具有最大积压的客户端,以释放内存。 (当然,这不会给客户端带来良好的用户体验;但如果客户端有故障或连接速度慢,他无论如何都不太可能获得良好的用户体验。它确实可以防止您的服务器崩溃或自行切换到死亡,因为单个客户端连接不良)
跟踪您的服务器缓存了多少数据并等待发送。如果您缓存的数据量太大(对于某些适当的 "too large" 值),请暂时停止从将数据推送给您的套接字中读取(或者如果您正在内部生成数据,暂时停止生成)。一旦缓存数据量再次下降到 acceptable 水平,您可以恢复接收(或生成)更多数据以推送。
(这可能适用于您的用例,也可能不适用于您的用例)修改您的数据模型,使其不再面向通信,而是面向状态。例如,如果您的目标是更新客户端的状态以匹配数据源的状态,并且您可以将数据源的状态组织成一组 key/value 对,那么您可以要求数据-source 在它发送的每条数据中包含一个密钥。每当从数据源接收到 key/value 对时,只需将该键值对放入每个客户端的映射(或散列 table 或其他面向 key/value 的数据结构)(再次,在这里使用 shared_ptr's 或类似的东西来保持合理的内存使用)。每当给定客户端耗尽其传出 TCP 数据队列时,从该客户端的 key/value 映射中删除最旧的项目,将其转换为要发送的 TCP 字节,并将它们添加到传出 TCP 数据队列。根据需要重复。这样做的好处是给定键的 "obsolete" 值会自动在服务器内部删除,因此永远不需要发送给慢速客户端;相反,慢速客户端只会获得给定键的 "latest" 值。这样做的好处是给定客户端的最大值 "backlog" 将受到状态模型中密钥数量的限制,无论客户端的带宽有多慢或间歇性如何。因此,一个缓慢的客户端可能会看到较少的更新(根据 second/minute/hour),但它看到的更新仍然是尽可能最近的,因为它的带宽。