Chapter 19. 流控制

流控制是指对客户端与服务器之间,或者服务器之间的数据流量进行限制,目的是防止通迅双方由于大量数据而过载。

19.1. 接收者(consumer)流控制

这是指对客户端的接收者接收消息流的控制。通常为了提高效率,在客户端通常将消息放入缓存,然后再将缓存中 的消息传递给接收者(consumer)。当接收者处理消息的速度小于服务器向其发送消息的速度时,就可能造成消息在 客户端不断积累,最終引起内存溢出的错误。

19.1.1. 基于窗口的流控制

默认情况下HornetQ的接收者一端会将消息进行缓存以提高性能。如果不这样做,那每次接收者收到一个消息, 都得通知服务器传递下一个消息,然后服务器再将下一个消息传递过来。这就增加了通信的次数。

对于每一次消息传递都有一个网络的往返通信,这样降低了性能。

为了避免这样,HornetQ将每个接收者的消息提前接收到一处缓存中。每个缓存的最大值由 consumer-window-size参数决定(单位字节)。

consumer-window-size的默认值是 1 MiB (1024 * 1024 字节)。

它的值可以是:

合理设置接收者的窗口大小可以显著提高性能。下面是两个极端的例子:

快速接收者

所谓快速接收者是指消息的接收者处理消息的速度大于等于它的接收速度。

对于快速接收者或以将consumer-window-size设为 -1,使得客户端的消息缓存的大小 无限制

请谨慎使用这一设置值: 如果接收者的消息处理速度比接收速度小,可造成客户端内存溢出。

慢接收者

所谓慢接收者是指接收者每处理一个消息就要花很多时间。这样将缓存关闭就比较合理。服务器可以将多余的 消息传递给其它的接收者。

假设一个队列有2个接收者。其中一个接收者非常慢。消息被轮流传递到两个接收者。其中的快速接收者 很快将其缓存中的消息处理完毕。同时慢接收者的缓存中还有一些消息等待处理。这样快速接收者在一段时间 内就处于空闲状态。

这时,将consumer-window-size 设为0 (没有缓存),就可以将它变成 慢接收者。这样在慢接收者一方不会缓存消息,这使得快的接收者可以处理更多的消息,而不至于处于空闲 状态。

这说明将它设置为0可以控制一个队列的消息在多个接收者之间的消息分配。

大多数情况下很难判断哪些接收者是快速的,哪些是慢速的。往往很多接收者是处于两者之间。这样对于 consumer-window-size的值就要视具体情况而定。有时需要进行一定的测试 来决定它的最佳值。通常情况下将其设为1MiB可以满足大多数的应用情况。

19.1.1.1. 使用核心接口(Core API)进行流控制

Hornet的核心接口中,ClientSessionFactory.setConsumerWindowSize()方法和一些 ClientSession.createConsumer()方法可以控制流的窗口大小。

19.1.1.2. 使用JMS的流控制

若使用JNDI来获得连接工厂,则需要通过配置hornetq-jms.xml文件来设定窗口大小:

<connection-factory name="ConnectionFactory">
   <connectors>
      <connector-ref connector-name="netty-connector"/>
   </connectors>
   <entries>
      <entry name="ConnectionFactory"/>       
   </entries>
      
   <!-- Set the consumer window size to 0 to have *no* buffer on the client side -->
   <consumer-window-size>0</consumer-window-size>
</connection-factory>
            

如果直接实例化连接工厂,则使用HornetQConnectionFactory.setConsumerWindowSize() 方法来设定窗口大小。

参见例子Section 11.1.32, “零接收缓冲”来了解如何配置HornetQ来 关闭接收者的缓存。

19.1.2. 速率流控制

我们还可以通过控制 速率的方法来控制流。这是一种像调节节流阀的形式。 这种方法保证一个接收者接收消息的速率不会超过设定的值。

速率必须是一个正整数。它代表最大接收速度,单位是消息每秒。将它设为-1就会关闭速率流控制。 默认值是-1

参见有关速率流控制的例子Section 11.1.10, “限制接收速率”以进一步了解它的工作原理。

19.1.2.1. 使用核心接口(Core API)

HornetQ的核心接口的ClientSessionFactory.setConsumerMaxRate(int consumerMaxRate)方法或 某些ClientSession.createConsumer()方法可以实现对流的速率控制。

19.1.2.2. 使用JMS

如果从JNDI中获取连接工厂,需要通过配置hornetq-jms.xml来进行速率流控制:

<connection-factory name="ConnectionFactory">
      <connectors>
         <connector-ref connector-name="netty-connector"/>
      </connectors>
      <entries>
         <entry name="ConnectionFactory"/>       
      </entries>
      <!-- We limit consumers created on this connection factory to consume messages
             at a maximum rate
      of 10 messages per sec -->
      <consumer-max-rate>10</consumer-max-rate>
 </connection-factory>

如果是直接实例化连接工厂,则通过HornetQConnectionFactory.setConsumerMaxRate(int consumerMaxRate)方法来设定最大流速率。

Note

速率流控制可以与窗口流控制结合使用。速率控制只规定了客户端每秒接收多少消息。因此如果你设定 了一个较低的速率,同时又设定了一个大的缓存窗口,那么客户端的缓存将会很快饱和。

参见接收速率流控制的例子Section 11.1.10, “限制接收速率”进一步了解速率流控制的配置和使用。

19.2. 发送者(producer)的流控制

HornetQ还可以控制客户端向服务器发送消息的速度,以避免服务器因大量数据过载。

19.2.1. 基于窗口的流控制

与接收者的相应的控制相似。在默认条件下,发送者要有足够的份额(credits)才可以向服务器的地址发送消息。 这个份额就是消息的大小。

当发送者的份额不足时,它要向服务器请求更多的份额以便发送更多的消息。

发送者一次向服务器请求的份额值被称为窗口大小

于是窗口大小就是指发送者向服务器不间断发送消息的总最大字节数。当发送完毕时需再向服务器请求份额。这样就避免了 服务器消息过载的情况。

19.2.1.1. 使用核心接口(Core API)

若使用核心接口,ClientSessionFactory.setProducerWindowSize(int producerWindowSize) 方法可以对窗口大小进行设定。

19.2.1.2. 使用JMS

如果使用JNDI来获得连接工厂,则需要配置hornetq-jms.xml文件以设定窗口大小:

               <connection-factory name="ConnectionFactory">
                  <connectors>
                     <connector-ref connector-name="netty-connector"/>
                  </connectors>
                  <entries>
                     <entry name="ConnectionFactory"/>       
                  </entries>
                  <producer-window-size>10</producer-window-size>
               </connection-factory>

如果是直接实例化连接工厂,则使用HornetQConnectionFactory.setProducerWindowSize(int producerWindowSize)方法来设定窗口大小。

19.2.1.3. 限定发送者窗口流控制

通常情况下客户端请求多少份额,HornetQ服务器就给予多少份额。然而我们还可以针对每个地址来设定一个最大 的份额值,以使服务器给出的份额都不大于该值。这样可以防止一个地址的内存溢出。

例如,如果有一个队列称为“myqueue”。将它的最大内存值设为10MiB,则服务器就会控制给出的份额以保证向该队列的地 址发送消息时不会占大于10MiB的内存空间。

当一相地址已经满了的时候,发送者将会阻塞直到该地址有了多余的空间为止,即地址中的消息被接收了一部分后使得 地址腾出了一些空间。

我们将这种控制方法称为限定发送者窗口流控制。这是一种有效的防止服务器内存溢出的手段。

它可以看成是分页转存(paging)的另一种方法。分页转存不阻塞发送者,它将消息转存到存贮介质上以节省内存的空间。

要配置一个地址的最大容量并告诉服务器在地址满了的情况下阻塞发送者,你需要为该地址定义一个 AddressSettings (Section 25.3, “通过地址设置来配置队列属性”) 并设定 max-size-bytesaddress-full-policy

这个配置对所有注册到该地址的队列有效。即所有注册队列的总内存将不超过 max-size-bytes。对于JMS topic情况则意谓着该topic的所有订阅的内存不能超过 max-size-bytes的设定值。

下面是一个例子:

               <address-settings>
                  <address-setting match="jms.queue.exampleQueue">            
                     <max-size-bytes>100000</max-size-bytes>
                     <address-full-policy>BLOCK</address-full-policy>   
                  </address-setting>
               </address-settings>

上面的例子将JMS队列"exampleQueue"的最大内存值设为 100000 字节并且阻塞发送者以防止消息量超过这个值。

注意必须设置 BLOCK的策略才能打开限定发送者窗口控制。

Note

请注意默认的配置下当一个地址中的消息量达到10MiB时,其所有的消息发送者将变为阻塞状态,也就是说 在没有接收的情况下你不能向一个地址不阻塞地一次发送超过10MiB的消息。要想增加这个限制,可以加大 max-size-bytes参数的值,或者调整地址的消息容量限制。

19.2.2. 速率流控制

HornetQ也可以控制发送者发送消息的速率。单位是每秒消息数。通过设定速率可保证发送者的发送速率不超过某个值。

速率必须是一个正整数。如果设为 -1 则关闭速率流控制。默认值是-1

请参见例子Section 11.1.36, “消息发送速度限制”进一步了解速率流控制的使用方法。

19.2.2.1. 使用核心接口(Core API)

如果使用核心接口,ClientSessionFactory.setProducerMaxRate(int consumerMaxRate)方法或 某些 ClientSession.createProducer()方法可以设置最大速率值。

19.2.2.2. 使用 JMS

如果使用JNDI,需要配置hornetq-jms.xml文件:

<connection-factory name="ConnectionFactory">
      <connectors>
         <connector-ref connector-name="netty-connector"/>
      </connectors>
      <entries>
         <entry name="ConnectionFactory"/>       
      </entries>
      <!-- We limit producers created on this connection factory to produce messages 
                at a maximum rate
      of 10 messages per sec -->
      <producer-max-rate>10</producer-max-rate>
 </connection-factory>

如果直接实例化连接工厂,则使用HornetQConnectionFactory.setProducerMaxRate(int consumerMaxRate)方法来设置。