Netty 3.95频道编写和使用setTrafficClass

时间:2014-12-12 21:59:17

标签: netty channel

我有一台Netty 3.95客户端和服务器。客户端将任务计划发送到服务器。可以发送的计划数量基本上没有限制,有些计划比其他计划更重要。我有三个优先级(高,中,低),我为每个任务计划分配一个优先级(DSCP值,因为我将数据读入客户端。)我希望这允许更重要的任务计划首先到达服务器。 / p>

我已经将路由器的出站数据速率调低到最低限度,我已经将路由器充斥了大约1000个计划。我可以看到任务计划到达服务器并且它们到达的速度非常慢。这告诉我,我已经有效地锁定了路由器的出站数据速率。

在我为每个任务计划执行channel.write之前,我获得了频道的配置并设置了流量等级。

private void setTrafficClass(int code) {
    comLink = connection.getChannel();
    cfg = (NioSocketChannelConfig) comLink.getConfig();
    cfg.setTrafficClass(code);
}

然后我用

跟着这个
comLink.write();

任务计划总是按我发送给他们的顺序到达。几乎看起来DSCP代码被忽略了。路由器中启用了DSCP。我使用Wireshark来检查客户端和服务器之间的流量,我注意到并非所有的DSCP代码都是正确的。看起来似乎一组任务计划是立即编写的,无论最后一个DSCP代码是什么,都成为该组的代码。然而,有些是不正确的。

问题

  1. 有没有更有效的方法来使用channel.write()?我想在每次写入后刷新通道,但我没有看到这种方法。是否存在我没​​有偶然发现的事情?
  2. 有没有理由在每次写入之前正确编写trafficClass?
  3. 我尝试使用相同的DSCP对任务计划进行分组,并将它们作为一个单元编写。我会写低,然后是中,然后是高,认为路由器会调整交付,但这也不起作用。

  4. 解决

    我已经解决了这个问题或更好,但同事看了我的代码,我们发现了问题。我的代码中有一个竞争条件,它不允许设置正确的DSCP代码。如果你查看我的setTrafficClass方法,你可以看到我传入了我想要设置的代码,但重要的是要意识到这个代码是设置连接而不是实际的数据包DSCP。由于我实际上尽可能快地发送了100个任务计划,因此代码不断改变连接的流量类别。如果有足够的时间,它就无法跟上,因此我会看到它的行为。

    由于Netty API,解决方案非常简单。在我的情况下,我只使用2个DSCP代码,所以我声明了两个连接。一个连接将处理DSCP代码0分组,另一个连接将处理DSCP代码46分组。一旦我将此代码付诸实践,我就能看到两个DSCP代码都按预期到达发件人。

2 个答案:

答案 0 :(得分:0)

我认为,我并没有为所有人做出回答。

  1. 在Netty 3.9中,您可以等待channel.write()返回的未来,或者更好地开始对您自己的ChannelFutureListener进行下一次写作:

    channel.write().addListener(yourChannelFutureListener);
    
  2. 您不应该阻止IO Worker线程,因此使用侦听器的原因。但是,如果它太复杂,您可以在管道中添加OrderedMemoryAwareThreadPoolExecutor,就在应用程序处理程序之前,这样它就会在IO工作线程之外执行,然后使用await()。

    在Netty> = 4中,您将拥有一个更简单的writeAndFLush()方法。

    1. 首先,据我所知,trafficClass实际上依赖于套接字的Java实现。它可能会被忽略(如Java文档中所述(参见http://docs.oracle.com/javase/7/docs/api/java/net/Socket.html#setTrafficClass(int))。
    2. 另请注意,IPV4中的值是有限的(请参阅Javadoc)。

      但是,我不是这方面的专家,但我认为顺序保留在任何代码中,因为从TCP的角度来看,这是我所知道的强制性。我相信在UDP中它可以按你的需要工作(因为订购时没有确保)。

      1. 由于上述原因(订购),我认为如果您希望高优先级首先达到目标,则必须以相反的顺序书写。

答案 1 :(得分:0)

就个人而言,我会在代码中执行优先级排序,而不是依靠网络来执行此操作。这样,您就可以将自己与数据可能必须遍历的任何特殊网络配置隔离开来。

有几种方法可以实现这一点,所以以下只是一个例子。

  • 在单独的线程中读取计划并放在优先级队列中
  • 从第二个线程中的优先级队列中读取项目并写入通道
  • 如果channel.isWritable返回它不再可写,则可以阻止返回的未来,从而保证Netty已将所有数据刷新到套接字缓冲区,或者监听channelInterestOpsChanged事件以尽快恢复写入由于Netty将足够的数据刷新到低水位以下。这可能更高效,因为它允许您恢复准备数据,而Netty正在刷新以前的最后一个数据。

Java至少从Java 6开始提供PriorityQueue实现。它甚至提供了如何使用序列号在不同优先级内维护优先级的示例。如果您的网络速度很慢,您可能希望限制队列中未完成计划的数量。

您可能还需要考虑饥饿是否存在问题 - 即是否产生了许多低优先级计划永远不会被发送的高优先级计划。