234 lines
17 KiB
XML
234 lines
17 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
||
<!-- ============================================================================= -->
|
||
<!-- Copyright © 2009 Red Hat, Inc. and others. -->
|
||
<!-- -->
|
||
<!-- The text of and illustrations in this document are licensed by Red Hat under -->
|
||
<!-- a Creative Commons Attribution–Share Alike 3.0 Unported license ("CC-BY-SA"). -->
|
||
<!-- -->
|
||
<!-- An explanation of CC-BY-SA is available at -->
|
||
<!-- -->
|
||
<!-- http://creativecommons.org/licenses/by-sa/3.0/. -->
|
||
<!-- -->
|
||
<!-- In accordance with CC-BY-SA, if you distribute this document or an adaptation -->
|
||
<!-- of it, you must provide the URL for the original version. -->
|
||
<!-- -->
|
||
<!-- Red Hat, as the licensor of this document, waives the right to enforce, -->
|
||
<!-- and agrees not to assert, Section 4d of CC-BY-SA to the fullest extent -->
|
||
<!-- permitted by applicable law. -->
|
||
<!-- ============================================================================= -->
|
||
<chapter id="perf-tuning">
|
||
<title>性能调优</title>
|
||
<para>本章讲述如何优化ActiveMQ的性能</para>
|
||
<section>
|
||
<title>持久层的优化</title>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>将消息日志放到单独的物理卷上。如果与其它数据共享,例如事务管理、数据库或其它日志等,那么就会
|
||
增加读写的负担,磁头会在多个不同文件之间频繁地移动,极大地降低性能。我们的日志系统采用的是只
|
||
添加的模式,目的就是最大程度減少磁头的移动。如果磁盘被共享,那么这一目的将不能达到。另外如果
|
||
你使用分页转存或大消息功能时,你最好分别将它们放到各自的独立卷中。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>尽量减少日志文件的数量。<literal>journal-min-files</literal>参数的设置应以满足平均
|
||
运行需要为准。如果你发现系统中经常有新的日志文件被创建,这说明持久的数据量很大,你需要适当增加
|
||
这个参数的值,以使ActiveMQ更多时候是在重用文件,而不是创建新文件。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>日志文件的大小。日志文件的大小最好要与磁盘的一个柱面的容量对齐。默认值是10MiB,它在绝大多数
|
||
的系统中能够满足需要。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>使用AIO日志。在Linux下,尽量使用AIO型的日志。AIO的可扩展性要好于Java的NIO。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>优化 <literal>journal-buffer-timeout</literal>。如果增加它的值,吞吐量会增加,但是
|
||
延迟也会增加。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>如果使用AIO,适当增加<literal>journal-max-io</literal>可能会提高性能。如果使用的是NIO,
|
||
请不要改变这个参数。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>优化JMS</title>
|
||
<para>如果使用JMS接口,有以下几个方面可以改进性能。</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>关闭消息id。如果你不需要这个id,用<literal>MessageProducer</literal>的
|
||
<literal>setDisableMessageID()</literal>方法可以关闭它。这可以减少消息的大小并且
|
||
省去了创建唯一ID的时间。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>关闭消息的时间戳。如果不需要时间戳,用<literal
|
||
>MessageProducer</literal>的<literal
|
||
>setDisableMessageTimeStamp()</literal>方法将其关闭。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>尽量避免使用<literal>ObjectMessage</literal>。<literal>ObjectMessage</literal>会带
|
||
来额外的开销。<literal>ObjectMessage</literal>使用Java的序列化将它序列化为字节流。在对小的对象
|
||
进行序列化会占用大量的空间,使传输的数据量加大。另外,Java的序列化与其它定制的技术相比要慢。只有在不得
|
||
以的情况下才使用它。比如当你在运行时不知道对象的具体类型时,可以用ObjectMessage。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>避免使用<literal>AUTO_ACKNOWLEDGE</literal>。 <literal>AUTO_ACKNOWLEDGE</literal>
|
||
使得每收到一个消息就要向服务器发送一个通知--这样增加的网络传输的负担。如果可能,尽量使用
|
||
<literal>DUPS_OK_ACKNOWLEDGE</literal>或者<literal
|
||
>CLIENT_ACKNOWLEDGE</literal>。或者使用事务性会话,将通知在提交时批量完成。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>避免持久化消息。默认情况下JMS消息是持久的。如果你不需要持久消息,则将其设定为非持久。
|
||
持久消息都会被写到磁盘中,这给系统带来了明显的负担。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>将多个发送或通知放到一个事务中完成。这样ActiveMQ只需要一次网络的往返来发生事务的提交,而不是每次发送
|
||
或通知就需要一次网络的往返通迅。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>其它优化</title>
|
||
<para>在ActiveMQ中还有其它一些地方可以优化:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>使用异步发送通知。如果你在非事务条件下发送持久的消息,并且要保证在send()返回时持久消息已经到达服
|
||
务器,不要使用阻塞式发送的方式,应该使用异步发送通知的方式。参见<xref
|
||
linkend="send-guarantees"/>中的说明。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>使用预先通知模式。预先通知就是在消息发往客户端<literal>之前</literal>进行通知。它节省了正常
|
||
的消息通知所占用的通迅时间。详细的解释请参见
|
||
<xref linkend="pre-acknowledge"/>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>关闭安全。将<literal>activemq-configuration.xml</literal>文件中的<literal>security-enabled</literal>
|
||
参数设为<literal>false</literal>以关闭安全。这可以带来一些性能的提高。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>关闭持久化。如果不你不需要消息持久化,可以将<literal>activemq-configuration.xml</literal>
|
||
文件中的<literal>persistence-enabled</literal>参数设为false来完全关闭持久功能。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>采用延迟方式事务同步。将<literal>activemq-configuration.xml</literal>文件中的<literal
|
||
>journal-sync-transactional</literal>参数设为<literal>false</literal>可以得到
|
||
更好的事务持久化的性能。但是这样做可能会造成在发生故障时事务的丢失。有关详细的说明参见
|
||
<xref linkend="send-guarantees"/>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>采用延迟方式非事务同步。将<literal>activemq-configuration.xml</literal>文件中的<literal
|
||
>journal-sync-non-transactional</literal>参数设为<literal>false</literal>可以得到
|
||
更好的非事务持久化的性能。但是这样做可能会造成在发生故障时持久消息的丢失。有关详细的说明参见
|
||
<xref linkend="send-guarantees"/>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>采用非阻塞方式发送消息。将文件<literal>hornetq-jms.xml</literal>中的参数
|
||
<literal>block-on-non-durable-send</literal>设为<literal>false</literal>
|
||
(使用JMS和JNDI时)或者直接在上进行相应的设置,可以使
|
||
消息发送时不阻塞等待服务器的响应。参见 <xref linkend="send-guarantees"/>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>如果你的接收者速度很快,你可以增加consumer-window-size。这样实际上就关闭了流控制的功能。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>套接字NIO与旧的IO对比。默认情况下ActiveMQ在服务器端使用套接字NIO技术,而在客户端则使用旧的(阻塞)
|
||
IO(参见传输配置一章<xref linkend="configuring-transports"/>)。NIO比旧的阻塞式IO有更
|
||
强的可扩展性,但是也会带来一些延时。如果你的服务器要同时有数千个连接,使用NIO效果比较好。但是如果
|
||
连接数并没有这么多,你可以配置接收器使用旧的IO还提高性能。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>尽量使用核心接口而不用JMS。使用JMS接口会稍微比使用核心接口性能要低些。这是因为所有JMS操作
|
||
实际上要转化为核心的操作才能为服务器所处理。在使用核心接口时,尽量使用带有
|
||
<literal>SimpleString</literal>类型参数的方法。<literal>SimpleString</literal>与
|
||
java.lang.String不同,它在写入传输层时不需要拷贝。所以你如果在调用中重用<literal
|
||
>SimpleString</literal>对象可以避免不必要的拷贝。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>传输层的优化</title>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>TCP缓存大小。如果你的网络速度很快,并且你的主机也很快,你可以通过增加TCP的发送和接收缓存
|
||
来提高性能。参见<xref linkend="configuring-transports"/>中的详细说明。</para>
|
||
<note>
|
||
<para>注意某些操作系统,如最近的Linux版本中,包括了TCP自动优化功能。如果再手工设置TCP缓存
|
||
会导致自动优化失效,最終使性能下降!
|
||
</para>
|
||
</note>
|
||
</listitem>
|
||
<listitem>
|
||
<para>增加服务器中文件句柄数量限制。如果你的服务器将要处理很多并行的连接,或者客户端在快速不停地
|
||
打开和关闭连接,你要确保在服务器端有足够的文件句柄以供使用。</para>
|
||
<para>这个限制在不同平台有不同的方法。在Linux系统中,你可以编辑文件<literal
|
||
>/etc/security/limits.conf</literal>,增加以下内容:
|
||
<programlisting>
|
||
serveruser soft nofile 20000
|
||
serveruser hard nofile 20000
|
||
</programlisting>
|
||
它设置了用户<literal>serveruser</literal>可以最多打开20000个文件句柄。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>利用参数<literal>batch-delay</literal>并将参数<literal>direct-deliver</literal>
|
||
设为false来提高小消息的处理效率。ActiveMQ在其<literal>activemq-configuration.xml</literal>
|
||
中预先配置了一个连接器/接受器对(<literal>netty-throughput</literal>),并且在
|
||
<literal>hornetq-jms.xml</literal>中配置了一个JMS连接工厂(
|
||
<literal>ThroughputConnectionFactory</literal>)。它们可以用在小消息的处理应用中以提
|
||
供最佳呑吐量。参见<xref
|
||
linkend="configuring-transports"/>。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>优化虚拟机</title>
|
||
<para>我们强烈建议你使用最新的Java 6虚拟机。它在很多方面对以前Java 5的虚拟机进行了改进,特别是在网络功能方面。
|
||
这是根据我们内部使用Sun的实现测试的結果,可能不适用于其它的Java实现(例如IBM或JRockit)。</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>拉圾回收。为了使服务器的运行比较平滑,我们建议使用并行拉圾回收的算法。例如在Sun的JDK使用
|
||
JVM选项<literal>-XX:+UseParallelOldGC</literal>.</para>
|
||
</listitem>
|
||
<listitem id="perf-tuning.memory">
|
||
<para>内存设置。尽量为服务器分配更多的内存。ActiveMQ利用其分页转存技术可以在很少的内存下运行(在
|
||
<xref linkend="paging"/>中有说明)。但是如果所有队列都在内存运行,性能将会很好。具体需要
|
||
多少内存要由你的队列的大小和数量以及消息的大小和数量决定。使用JVM参数<literal>-Xms</literal>
|
||
和<literal>-Xmx</literal>来为你的服务器分配内存。我们建议两个参数的设为相同的值。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>主动选项(Aggressive options)。不同JVM有不同的JVM优化参数。对于Sun的Hotspot JVM,在<ulink
|
||
url="http://java.sun.com/javase/technologies/hotspot/vmoptions.jsp"
|
||
>这里</ulink>有一个完整的参数列表。我们建议至少要使用 <literal
|
||
>-XX:+AggressiveOpts</literal> 和<literal>
|
||
-XX:+UseFastAccessorMethods</literal>选项。根据不同的平台,可能还有其它一些参数供你使用,
|
||
以提高JVM的性能。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>避免违背设计模式</title>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>重用连接/会话/接收者/发送者。最常见的错误恐怕就是每发送/接收一个消息都要创建一个新的连接
|
||
/会话/发送者或接收者。这样非常浪费资源。这些对象的创建要占用时间和网络带宽。它们应该进行重用。</para>
|
||
<note>
|
||
<para>有些常用的框架如Spring JMS Template在使用JMS时违背了设计模式。如果你在使用了它后性能
|
||
受到了影响。这不是ActiveMQ的原因!Spring的JMS模板只有与能缓存JMS会话的应用服务器一起使用
|
||
才是安全的,并且只能是用于发送消息。使用它在应用服务器中同步接收消息是不安全的。</para>
|
||
</note>
|
||
</listitem>
|
||
<listitem>
|
||
<para>避免使用繁锁的消息格式。如XML,它会使数据量变大进而降低性能。所以应该尽量避免在消息体中使用XML。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>不要为每个请求都创建新的临时队列。临时队列通常用于请求-响应模式的消息应用。在这个模式中消息被发往
|
||
一个目的,它带有一个reply-to的头属性指向一个本地的临时队列的地址。当消息被收到后,接收方将响应做为消息发
|
||
往那个reply-to指定的临时的地址。如果每发一个消息都创建一个临时队列,那么性能将会受很大影响。正确的
|
||
作法是在发送消息时重用临时队列。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>尽量不要使用MDB。使用MDB,消息的接收过程要比直接接收复杂得多,要执行很多应用服务器内部的代码。
|
||
在设计应用时要问一下是否真的需要MDB?可不可以直接使用消息接收者完成同样的任务?</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
</chapter>
|