ZeroC Ice发送大数据
继上文,我们使用ZeroC Ice传递大块数据时,通常有两种做法,一种是一次请求,另一种就是分多次请求(,这种做法在官方文档有例子)。选哪一种根据需要而定。
当分多次请求来完成一大块数据,到底选择每次请求多大的数据合适呢?
首先清楚下面几点,每次请求应该用 two-way进行,也就是 request - reply 模式来确保数据发送。one-way模式是只管单向发送的。那么就有下面几个点
一个最小的Ice reply包大小为 79
一个Ice心跳包大小为 68
一个分段的ack包大小为 60
无论是返回 void的 reply包,还是心跳包,都必须通过 Ice环境和 Ice协议,也就是必须通过 POLL_OUT事件,唤醒线程池去发送。这里有隐藏的问题,在某些机器上的linux,POLL_OUT事件并不正常工作。
分段的ack动作,完全由协议栈的tcp驱动完成,不会像应用层依赖epoll事件去发送数据,受POLL_OUT事件不正常而影响。
如果一次请求过小,我们假定为了迎合链路去发送数据,将一次请求的数据量定为小MTU,假设是1K,那么每发送1K就要接收端在应用层响应并通过 Ice环境(epoll线程池)发送一个reply包,最小体积为 79。
为了减少接收端频繁在应用层响应,(最主要还是epoll 等待的POLL_OUT事件),我们就要适当增加一次请求的数据体积。这时候tcp层就需要将请求包分段发送,当请求包大小刚好跨越 MSS限制时,这样就会造成浪费,剩下的小部分数据不得不进行一次分段。为了提高利用,最好就是一次请求在 MSS 限制内尽可能的大, 最好是MTU的倍数。
从上面的例图,一个10K分段的ack包,在1300地递增,也就是分段以多个ip包,每个1300地到达接收端。并且到达是不按时间顺序的,但是接收端可以重组这个分段的ip包。分段的ack包的seq并没有任何改变,窗口也不会变化。
这里就有一问题,我们必须查询 socket 的 MTU 以及 MSS,但是 ZeroC Ice完全将底层 socket 与使用用户隔离,所以当你使用这个中间件后,你就不得不忍受这种封装。
另外再次声讨一下,sequence<byte> 在所有平台上工作良好,除子php平台上,是鸡肋的整数数组,这最少要损耗10倍的空间。
2018.02.14 补充:
Ice.MessageSizeMax是接收方的限制属性,当一个twoway请求,client 和 server都扮演一次接收方。client request to server,server 就是 request message 的接收方。server reply to client,client 就是 reply message 的接收方。当请求或响应的消息包的数据太大时,都要调整两边的 Ice.MessageSizeMax。当你在一方捕捉到关于这个属性的异常,不是限制了发送,而是限制了接收,换句话说不是发不出去,而是拒绝了接收从对方来的消息。