HornetQ可以在有限的内存下支持包含百万消息的超大规模的队列。
当有限的内存无法装得下如此多的消息时,HornetQ将它们分页转存到磁盘中,在内存 有空闲时再将消息分页装载到内存。通过这样的处理,不需要服务器有很大的内存就可以支持大容量的队列。
通过配置可以给一个地址设置一个最大消息值。当这个地址消息数在内存中超过了这个值时,HornetQ开始将多余的消息 转存到磁盘中。
默认情况下HornetQ不转存任何消息。这一功能必须显式地通过配置来激活。
消息按照所属的地址分别保存在不同的文件中。每一个地址有一个单独的文件夹,每个文件夹内消息被保存在 数个文件中(分页文件)。每个文件保存固定数量的消息(由参数page-size-bytes 设定)。当从分页文件中读取消息时,一个文件的所有消息被读到内存并被路由。当所有消息处理后,该文件就 被删除。
你可以配置分页转存文件夹的位置。
在主配置文件hornetq-configuration.xml)中 可以定义全局的分页转发参数。
<configuration xmlns="urn:hornetq" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:hornetq /schema/hornetq-configuration.xsd"> ... <paging-directory>/somewhere/paging-directory</paging-directory> ...
一个地址只要消息的数量超过定义的值,它就转到分页转存的模式。
分页转存是针对每个地址设置的。如果你为一个地址配置了一个max-size-bytes,那么每个匹配的地址 都有一个最大值的限制。但是这并不表示所有匹配的地址的大小总和受这个参数的限制。
有关分页转存的配置在主要配置文件(hornetq-configuration.xml) 的地址设置的部分内。
<address-settings> <address-setting match="jms.someaddress"> <max-size-bytes>104857600</max-size-bytes> <page-size-bytes>10485760</page-size-bytes> <address-full-policy>PAGE</address-full-policy> </address-setting> </address-settings>
下面列出了可用的参数:
Table 24.2. 分页转存参数设置
参数名称 | 说明 | 默认值 |
---|---|---|
max-size-bytes | 地址的最大内存值。当消息占用内存超过此值时,进入分页转存模式。 | -1 (关闭分页转存功能) |
page-size-bytes | 每个分页文件的大小。 | 10MiB (10 * 1024 * 1024 字节) |
address-full-policy | 要使用分页转存,这个参数必须设为PAGE。PAGE表示多余的消息会被保存到磁盘。 如果设为DROP,那么多余的消息将会被丢弃。如果设为BLOCK,当消息占满设定的最大 内存时,在客户端消息的发送者将被阻塞,不能向服务器发送更多的消息。 | PAGE |
一个地址除了可以分页转存多余的消息外,还可以通过配置使得消息的发送者在消息达到最大值时阻塞消息 的发送,以防止服务器由于消息过多而耗尽内存。
随着服务器的内存被释放,发送者自动解除阻塞,继续发送消息。
这种方式需要将address-full-policy设为BLOCK。
在默认的配置中,所有的地址在消息的量达到10MiB后将阻塞发送者。
当一个消息被路由到一个绑定了多个队列(queue)的地址时(比如JMS的订阅),在内存中仍然只有一分消息的拷贝。每个 队列所持有的不过是它的一个引用。因此,只有所有队列中的消息都成功地传递出去后,这个消息才会从内存中清除。也就是说 只要有一个队列没有传递这个消息,那么就会造成这个消息处于未被传递的状态。
例如:
一个地址绑定了10个队列(queue)。
其中一个队列没有传递它的消息(也许因为接收者很慢)。
消息不断增加,触发了分页转存模式。
而其它9个队列尽管发送了消息,但由于地址将多余的消息转存到磁盘,所以它们都是空的。
在这个例子中,必须要等到最后一个队列传递了一些消息后,那些转存的消息被装载回内存,其它队列才有机会得到更多的消息。
请注意消息选择器只对内存的消息进行操作。如果大量的消息被转存在磁盘中,而其中有些消息与选择器是相匹配的, 那么只有内存的消息被传递,这些消息被重新装载入内存后才有可能被传递出去。 HornetQ不会扫描在磁盘中的消息来找出与选择器匹配的消息。这样做的话需要实现并管理一种索引机制才能使扫描有效地进行,另外 需要其它额外的工作。所有这些如果去完成的话,相当于实现一个关系型数据库!这并不是消息系统的主要任务。如果你要完成的任务是 从海量的消息中选择少数消息,那么你很可能需要使用的是一个关系型数据库,不是消息系统。因为这相当于表的查询。
请注意浏览器只对内存中的消息进行操作,它不对转存到磁盘中的消息进行操作。 消息是在被路由到任何队列之前进行转存的,所以在转存时刻,它们还没有进入到任何队列中, 自然也就不可能出现在对某个队列浏览的結果中。
请注意如果消息没有被通知,它会一直留在服务器的内存中,占用着内存资源。只要消息在被接收者收到并通知后,它才 会在服务器端被清除,空出内存空间以便转存在磁盘上的消息被装载到内存进行传递。如果没有通知,消息不会被清除, 也就不会空出内存空间,转存到磁盘上的消息也就无法装载到内存进行传递。于是在接收端就会呈现出死机的现象。 如果消息的通知是依靠ack-batch-size的设定进行的批量通知,那么一定要注意不要将 分页转存的消息临界值设得小于ack-batch-size,否则你的系统可能会发生死机现象!
Section 11.1.34, “分页(paging)”是一个说明如何使用HornetQ的分页转发功能的例子。