Chapter 46. 性能调优

本章讲述如何优化HornetQ的性能

46.1. 持久层的优化

  • 将消息日志放到单独的物理卷上。如果与其它数据共享,例如事务管理、数据库或其它日志等,那么就会 增加读写的负担,磁头会在多个不同文件之间频繁地移动,极大地降低性能。我们的日志系统采用的是只 添加的模式,目的就是最大程度減少磁头的移动。如果磁盘被共享,那么这一目的将不能达到。另外如果 你使用分页转存或大消息功能时,你最好分别将它们放到各自的独立卷中。

  • 尽量减少日志文件的数量。journal-min-files参数的设置应以满足平均 运行需要为准。如果你发现系统中经常有新的日志文件被创建,这说明持久的数据量很大,你需要适当增加 这个参数的值,以使HornetQ更多时候是在重用文件,而不是创建新文件。

  • 日志文件的大小。日志文件的大小最好要与磁盘的一个柱面的容量对齐。默认值是10MiB,它在绝大多数 的系统中能够满足需要。

  • 使用AIO日志。在Linux下,尽量使用AIO型的日志。AIO的可扩展性要好于Java的NIO。

  • 优化 journal-buffer-timeout。如果增加它的值,吞吐量会增加,但是 延迟也会增加。

  • 如果使用AIO,适当增加journal-max-io可能会提高性能。如果使用的是NIO, 请不要改变这个参数。

46.2. 优化JMS

如果使用JMS接口,有以下几个方面可以改进性能。

  • 关闭消息id。如果你不需要这个id,用MessageProducersetDisableMessageID()方法可以关闭它。这可以减少消息的大小并且 省去了创建唯一ID的时间。

  • 关闭消息的时间戳。如果不需要时间戳,用MessageProducersetDisableMessageTimeStamp()方法将其关闭。

  • 尽量避免使用ObjectMessageObjectMessage会带 来额外的开销。ObjectMessage使用Java的序列化将它序列化为字节流。在对小的对象 进行序列化会占用大量的空间,使传输的数据量加大。另外,Java的序列化与其它定制的技术相比要慢。只有在不得 以的情况下才使用它。比如当你在运行时不知道对象的具体类型时,可以用ObjectMessage。

  • 避免使用AUTO_ACKNOWLEDGEAUTO_ACKNOWLEDGE 使得每收到一个消息就要向服务器发送一个通知--这样增加的网络传输的负担。如果可能,尽量使用 DUPS_OK_ACKNOWLEDGE或者CLIENT_ACKNOWLEDGE。或者使用事务性会话,将通知在提交时批量完成。

  • 避免持久化消息。默认情况下JMS消息是持久的。如果你不需要持久消息,则将其设定为非持久。 持久消息都会被写到磁盘中,这给系统带来了明显的负担。

  • 将多个发送或通知放到一个事务中完成。这样HornetQ只需要一次网络的往返来发生事务的提交,而不是每次发送 或通知就需要一次网络的往返通迅。

46.3. 其它优化

在HornetQ中还有其它一些地方可以优化:

  • 使用异步发送通知。如果你在非事务条件下发送持久的消息,并且要保证在send()返回时持久消息已经到达服 务器,不要使用阻塞式发送的方式,应该使用异步发送通知的方式。参见Chapter 20, 发送与提交的保证中的说明。

  • 使用预先通知模式。预先通知就是在消息发往客户端之前进行通知。它节省了正常 的消息通知所占用的通迅时间。详细的解释请参见 Chapter 29, 预先通知模式(pre-acknowledge)

  • 关闭安全。将hornetq-configuration.xml文件中的security-enabled 参数设为false以关闭安全。这可以带来一些性能的提高。

  • 关闭持久化。如果不你不需要消息持久化,可以将hornetq-configuration.xml 文件中的persistence-enabled参数设为false来完全关闭持久功能。

  • 采用延迟方式事务同步。将hornetq-configuration.xml文件中的journal-sync-transactional参数设为false可以得到 更好的事务持久化的性能。但是这样做可能会造成在发生故障时事务的丢失。有关详细的说明参见 Chapter 20, 发送与提交的保证

  • 采用延迟方式非事务同步。将hornetq-configuration.xml文件中的journal-sync-non-transactional参数设为false可以得到 更好的非事务持久化的性能。但是这样做可能会造成在发生故障时持久消息的丢失。有关详细的说明参见 Chapter 20, 发送与提交的保证

  • 采用非阻塞方式发送消息。将文件hornetq-jms.xml中的参数 block-on-non-durable-send设为false (使用JMS和JNDI时)或者直接在上进行相应的设置,可以使 消息发送时不阻塞等待服务器的响应。参见 Chapter 20, 发送与提交的保证

  • 如果你的接收者速度很快,你可以增加consumer-window-size。这样实际上就关闭了流控制的功能。

  • 套接字NIO与旧的IO对比。默认情况下HornetQ在服务器端使用套接字NIO技术,而在客户端则使用旧的(阻塞) IO(参见传输配置一章Chapter 16, 传输层的配置)。NIO比旧的阻塞式IO有更 强的可扩展性,但是也会带来一些延时。如果你的服务器要同时有数千个连接,使用NIO效果比较好。但是如果 连接数并没有这么多,你可以配置接收器使用旧的IO还提高性能。

  • 尽量使用核心接口而不用JMS。使用JMS接口会稍微比使用核心接口性能要低些。这是因为所有JMS操作 实际上要转化为核心的操作才能为服务器所处理。在使用核心接口时,尽量使用带有 SimpleString类型参数的方法。SimpleString与 java.lang.String不同,它在写入传输层时不需要拷贝。所以你如果在调用中重用SimpleString对象可以避免不必要的拷贝。

46.4. 传输层的优化

  • TCP缓存大小。如果你的网络速度很快,并且你的主机也很快,你可以通过增加TCP的发送和接收缓存 来提高性能。参见Chapter 16, 传输层的配置中的详细说明。

    Note

    注意某些操作系统,如最近的Linux版本中,包括了TCP自动优化功能。如果再手工设置TCP缓存 会导致自动优化失效,最終使性能下降!

  • 增加服务器中文件句柄数量限制。如果你的服务器将要处理很多并行的连接,或者客户端在快速不停地 打开和关闭连接,你要确保在服务器端有足够的文件句柄以供使用。

    这个限制在不同平台有不同的方法。在Linux系统中,你可以编辑文件/etc/security/limits.conf,增加以下内容:

    serveruser     soft    nofile  20000
    serveruser     hard    nofile  20000                   
                    

    它设置了用户serveruser可以最多打开20000个文件句柄。

  • 利用参数batch-delay并将参数direct-deliver 设为false来提高小消息的处理效率。HornetQ在其hornetq-configuration.xml 中预先配置了一个连接器/接受器对(netty-throughput),并且在 hornetq-jms.xml中配置了一个JMS连接工厂( ThroughputConnectionFactory)。它们可以用在小消息的处理应用中以提 供最佳呑吐量。参见Chapter 16, 传输层的配置

46.5. 优化虚拟机

我们强烈建议你使用最新的Java 6虚拟机。它在很多方面对以前Java 5的虚拟机进行了改进,特别是在网络功能方面。 这是根据我们内部使用Sun的实现测试的結果,可能不适用于其它的Java实现(例如IBM或JRockit)。

  • 拉圾回收。为了使服务器的运行比较平滑,我们建议使用并行拉圾回收的算法。例如在Sun的JDK使用 JVM选项-XX:+UseParallelGC.

  • 内存设置。尽量为服务器分配更多的内存。HornetQ利用其分页转存技术可以在很少的内存下运行(在 Chapter 24, 分页转存中有说明)。但是如果所有队列都在内存运行,性能将会很好。具体需要 多少内存要由你的队列的大小和数量以及消息的大小和数量决定。使用JVM参数-Xms-Xmx来为你的服务器分配内存。我们建议两个参数的设为相同的值。

  • 主动选项(Aggressive options)。不同JVM有不同的JVM优化参数。对于Sun的Hotspot JVM,在这里有一个完整的参数列表。我们建议至少要使用 -XX:+AggressiveOpts -XX:+UseFastAccessorMethods选项。根据不同的平台,可能还有其它一些参数供你使用, 以提高JVM的性能。

46.6. 避免违背设计模式

  • 重用连接/会话/接收者/发送者。最常见的错误恐怕就是每发送/接收一个消息都要创建一个新的连接 /会话/发送者或接收者。这样非常浪费资源。这些对象的创建要占用时间和网络带宽。它们应该进行重用。

    Note

    有些常用的框架如Spring JMS Template在使用JMS时违背了设计模式。如果你在使用了它后性能 受到了影响。这不是HornetQ的原因!Spring的JMS模板只有与能缓存JMS会话的应用服务器一起使用 才是安全的,并且只能是用于发送消息。使用它在应用服务器中同步接收消息是不安全的。

  • 避免使用繁锁的消息格式。如XML,它会使数据量变大进而降低性能。所以应该尽量避免在消息体中使用XML。

  • 不要为每个请求都创建新的临时队列。临时队列通常用于请求-响应模式的消息应用。在这个模式中消息被发往 一个目的,它带有一个reply-to的头属性指向一个本地的临时队列的地址。当消息被收到后,接收方将响应做为消息发 往那个reply-to指定的临时的地址。如果每发一个消息都创建一个临时队列,那么性能将会受很大影响。正确的 作法是在发送消息时重用临时队列。

  • 尽量不要使用MDB。使用MDB,消息的接收过程要比直接接收复杂得多,要执行很多应用服务器内部的代码。 在设计应用时要问一下是否真的需要MDB?可不可以直接使用消息接收者完成同样的任务?