377 lines
25 KiB
XML
377 lines
25 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="configuring-transports">
|
||
<title>传输层的配置</title>
|
||
<para>ActiveMQ的传输层是“可插拔的”。通过灵活的配置和一套服务提供接口(SPI),ActiveMQ可以很容易地更换其传输层。</para>
|
||
<para>在本章中我们将对ActiveMQ的传输相关的概念作出解释,并说明它的配置方法。</para>
|
||
<section id="configuring-transports.acceptors">
|
||
<title>接收器(Acceptor)</title>
|
||
<para>接收器(<emphasis>acceptor</emphasis>)是 ActiveMQ 的传输层中最为重要的概念之一。首先
|
||
介绍一下在文件<literal>activemq-configuration.xml</literal>中是怎样定义一个接收器的:</para>
|
||
<programlisting>
|
||
<acceptors>
|
||
<acceptor name="netty">
|
||
<factory-class>
|
||
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
|
||
</factory-class>
|
||
<param key="port" value="5446"/>
|
||
</acceptor>
|
||
</acceptors>
|
||
</programlisting>
|
||
<para>所有接收器都在 <literal>acceptors</literal>单元(element)内定义。在<literal>acceptors</literal>
|
||
内可以有零个或多个接收器的定义。每个服务器所拥有的接收器的数量是没有限制的。</para>
|
||
<para>每个接收器都要定义其与ActiveMQ服务器连接的方式。</para>
|
||
<para>以上的例子中我们定义了一个<ulink
|
||
url="http://jboss.org/netty">Netty</ulink>接收器。它在端口<literal>5446</literal>监听连接请求。</para>
|
||
<para>在<literal>acceptor</literal>单元内有一个子单元<literal>factory-class</literal>。这个单元是用来
|
||
定义创建连接器的工厂类。一个连接器工厂类必须要实现<literal>AcceptorFactory</literal>接口。上例中我们定义
|
||
的连接器工厂是类NettyAcceptorFactory使用Netty来建立连接。有个这个类定义,ActiveMQ就知道了用什么传输来建立连接了。</para>
|
||
<para>在<literal>acceptor</literal>中还可以配置零或多个参数<literal>param</literal>。在每个<literal>param</literal>
|
||
中定义的是键-值对(key-value)。这些参数用来配置某个传输实现。不同传输有不同的配置参数。</para>
|
||
<para>像IP地址、端口号等都是传输配置参数的例子。</para>
|
||
</section>
|
||
<section id="configuring-transports.connectors">
|
||
<title>连接器(Connectors)</title>
|
||
<para>接收器定义的是如何在服务器端接收连接,而连接器则是定义客户端如何连接到服务器。</para>
|
||
<para>以下是<literal>activemq-configuration.xml</literal>文件中一个连接器配置的例子。</para>
|
||
<programlisting>
|
||
<connectors>
|
||
<connector name="netty">
|
||
<factory-class>
|
||
org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory
|
||
</factory-class>
|
||
<param key="port" value="5446"/>
|
||
</connector>
|
||
</connectors>
|
||
</programlisting>
|
||
<para>连接器的配置在<literal>connectors</literal>单元中。可以定义一个或多个连接器。每个服务器配置的连接器
|
||
数量是没有限制的。</para>
|
||
<para>你可能注意到了,既然连接器是定义<emphasis>客户端</emphasis>如何连接服务器的,那么为什么要定义在
|
||
<emphasis>服务器</emphasis>端呢?原因如下:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>服务器有时也需要做为客户端去连接其它的服务器,比如当一个服务器通过桥连接到另一个服务器,或者是集群
|
||
中服务器之间的互相通迅。在这种情况下服务器就要知道如何与另一台服务器建立连接。因此需要在
|
||
<emphasis>connectors</emphasis>下定义连接器。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para>如果你使用JMS服务,需要创建连接工厂的实例并绑定到JNDI。在ActiveMQ创建
|
||
<literal>ActiveMQConnectionFactory</literal>时需要连接器的必要信息,以便这个连接工厂
|
||
能知道它如何与ActiveMQ服务器相连接。</para>
|
||
<para>这一信息被定义在配置文件<literal
|
||
>activemq-jms.xml</literal>中的<literal>connector-ref</literal>单元下。下面这段配置
|
||
就是从该配置文件中提取的相关部分,它展示了JMS的连接工厂是如何引用定义在配置文件<literal
|
||
>activemq-configuration.xml</literal>中的连接器的:</para>
|
||
<programlisting>
|
||
<connection-factory name="ConnectionFactory">
|
||
<connectors>
|
||
<connector-ref connector-name="netty"/>
|
||
</connectors>
|
||
<entries>
|
||
<entry name="ConnectionFactory"/>
|
||
<entry name="XAConnectionFactory"/>
|
||
</entries>
|
||
</connection-factory>
|
||
</programlisting>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section id="configuring-transports.client.side">
|
||
<title>在客户端直接配置传输层</title>
|
||
<para>怎样配置一个内核<literal>ClientSessionFactory</literal>以让它知道如何连接服务器的信息呢?</para>
|
||
<para>在直接配置内核<literal>ClientSessionFactory</literal>的时候,可以间接地使用连接器。当然在这种情况
|
||
下在服务器端定义连接器是没有意义的。我们通过将必要参数传给<literal>ClientSessionFactory</literal>的
|
||
方法来告诉使用什么样的连接器工厂。</para>
|
||
<para>在下面的例子中,我们创建了一个<literal>ClientSessionFactory</literal>,它可以直接连接到我们先前定
|
||
义的接收器上。它使用的是标准的Netty TCP传输层,连接主机是localhost(默认),端口5446:</para>
|
||
<programlisting>
|
||
Map<String, Object> connectionParams = new HashMap<String, Object>();
|
||
|
||
connectionParams.put(org.apache.activemq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME,
|
||
5446);
|
||
|
||
TransportConfiguration transportConfiguration =
|
||
new TransportConfiguration(
|
||
"org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory",
|
||
connectionParams);
|
||
|
||
ClientSessionFactory sessionFactory = ActiveMQClient.createClientSessionFactory(transportConfiguration);
|
||
|
||
ClientSession session = sessionFactory.createSession(...);
|
||
|
||
etc
|
||
</programlisting>
|
||
<para>如果在客户端直接使用JMS的连接工厂的话,也可以用类似的方法而不需要在服务器端定义连接器或在
|
||
<literal>activemq-jms.xml</literal>配置文件中创建连接工厂:</para>
|
||
<programlisting>
|
||
Map<String, Object> connectionParams = new HashMap<String, Object>();
|
||
|
||
connectionParams.put(org.apache.activemq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, 5446);
|
||
|
||
TransportConfiguration transportConfiguration =
|
||
new TransportConfiguration(
|
||
"org.apache.activemq.core.remoting.impl.netty.NettyConnectorFactory",
|
||
connectionParams);
|
||
|
||
ConnectionFactory connectionFactory = ActiveMQJMSClient.createConnectionFactory(transportConfiguration);
|
||
|
||
Connection jmsConnection = connectionFactory.createConnection();
|
||
|
||
etc
|
||
</programlisting>
|
||
</section>
|
||
<section>
|
||
<title>配置 Netty 传输层</title>
|
||
<para>ActiveMQ当前使用<ulink url="http://www.jboss.org/netty/"
|
||
>Netty</ulink>作为其默认的连接层。Netty是一个高性能的底层网络库.</para>
|
||
<para>Netty传输的配置有几种不同的方法。它可以使用传统的Java IO(阻塞方式)、NIO(非阻塞)或直接使用
|
||
TCP socket及SSL。或者使用HTTP或HTTPS协议。同时还可能使用servlet进行传输。</para>
|
||
<para>采用Netty应该能满足绝大部分的传输要求。</para>
|
||
<section>
|
||
<title>配置 Netty TCP</title>
|
||
<para>Netty TCP 是简单的非加密的基于TCP socket的传输。它可以使用阻塞式的Java IO或非阻塞式的Java NIO。
|
||
我们建议在服务器端采用非阻塞式的NIO以获得良好的并发处理能力。当并发能力并不是很重要时,可以使用阻塞式
|
||
的方式以增加响应的速度。</para>
|
||
<para>如果你的应用是运行在不信任的网络上,你应该选择使用SSL或HTTPS。</para>
|
||
<para>Netty TCP的所有连接都是从客户端发起的。服务器端不向客户端发起任何连接。在有防火墙的环境中,这种方式
|
||
是比较适合的。因为防火墙只允许单方向的连接。</para>
|
||
<para>在<literal>org.apache.activemq.core.remoting.impl.netty.TransportConstants</literal>类中定义了所
|
||
有的配置参数的名称(key)。它们当中绝大多娄既用于配置接收器也用于配置连接器,有一些只适用于接收器。
|
||
下面列出的参数用以配置一个简单的Netty TCP:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><literal>use-nio</literal>。如果设为<literal>true</literal>则使用非阻塞的Java
|
||
NIO。如果<literal>false</literal>则使用传统的阻塞方式的Java IO。</para>
|
||
<para>我们建议使用Java NIO处理并行连接。因为Java NIO不是为每一个连接分配一个线程,所以它要比传统的阻塞式
|
||
Java IO具有更强的并发连接的处理能力。如果你不需要处理并发连接,那么使用旧的阻塞式的IO性能会好一些。这个参
|
||
数的默认值在服务器端是<literal>false</literal>,在客户端是<literal>false</literal>。
|
||
</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>host</literal>。主机名或IP地址。对于接收器来说,它是服务器接收连接的地址。
|
||
对于连接器端,它是客户端连接的目标地址。默认值是<literal>localhost</literal>。
|
||
在配置接收器时可以指定多个主机名或IP地址,中间用逗号隔开。如果指定的主机是<code>0.0.0.0</code>,
|
||
则接收器将从主机上所有的网络接口中接受连接请求。连接器不允许指定多个主机地址,它只能与一个
|
||
地址建立连接。</para>
|
||
<note>
|
||
<para>一定不要忘记指定一个主机名或IP地址!一个服务器要想接受来自其它节点的连接就必需有一个
|
||
主机名或IP地址来绑定及监听外部的连接请求。默认的主机名localhost是不能接受外部的
|
||
连接请求的!</para>
|
||
</note>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>port</literal>。连接的端口。用于配置连接器或接收器。连接器用此端口来建立
|
||
连接。接收器在些端口上监听连接请求。默认值是<literal>5445</literal>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>tcp-no-delay</literal>。将它设为<literal>true</literal>就会使用
|
||
<ulink url="http://en.wikipedia.org/wiki/Nagle's_algorithm">Nagle
|
||
算法</ulink>.默认值是<literal>true</literal>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>tcp-send-buffer-size</literal>。这个参数指定了TCP的发送缓冲大小,单位是字节。
|
||
默认值是<literal>32768</literal>字节(32KiB)。</para>
|
||
<para>这个参数要根据你的网络的带宽与时延的情况而调整。<ulink url="http://www-didc.lbl.gov/TCP-tuning/">
|
||
这个链接</ulink>对此有很好的论述。</para>
|
||
<para>简言之,TCP的发送/接收缓冲的大小可以用下面公式来计算:</para>
|
||
<programlisting>
|
||
缓冲大小 = 带宽 * RTT
|
||
</programlisting>
|
||
<para>其中带宽的单位是 <emphasis>每秒字节数</emphasis>,RTT(网络往返程时间)的单位是秒。
|
||
使用<literal>ping</literal>工具可以方便地测量出RTT。</para>
|
||
<para>对于快速网络可以适当加大缓冲的大小。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>tcp-receive-buffer-size</literal>。这个参数指定了TCP接收缓冲的大小,单位是字节。
|
||
默认值是<literal>32768</literal>字节(32KiB)。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>batch-delay</literal>。ActiveMQ可以通过配置该参数,在数据包写入传输层之前有一个
|
||
最大延时(毫秒),达到批量写入的目的。这样可以提高小消息的发送效率。但这样做会增加单个消息的平均发送
|
||
延迟。默认值为<literal>0</literal>毫秒。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>direct-deliver</literal>。消息到达服务器后,默认是由一个不同的线程来将消息传递
|
||
到接收者。这样可以使服务的呑吐量和可扩展性达到最佳,特别是在多核的系统上效果更为明显。但是线程切换
|
||
会带来一些传递的延迟。如果你希望延迟最小,并不在意呑吐量的话,可以将参数<literal
|
||
>direct-deliver</literal>设为true。默认值是<literal>true</literal>。如果你更希望有
|
||
较大的呑吐量的话,将它设为<literal>false</literal>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>nio-remoting-threads</literal>。如果使用NIO,默认情况下ActiveMQ会使用系统中处理
|
||
器内核(或超线程)数量三倍的线程来处理接收的数据包。内核的数量是通过调用<literal
|
||
>Runtime.getRuntime().availableProcessors()</literal>来得到的。如果你想改变这个数量,
|
||
你可以设定本参数。默认的值是<literal>-1</literal>,表示线程数为<literal
|
||
>Runtime.getRuntime().availableProcessors()</literal> * 3。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>配置Netty SSL</title>
|
||
<para>Netty SSL的配置与Netty TCP相似。它采用了安全套接字层(SSL)来提供加密的TCP连接。</para>
|
||
<para>我们提供了一个Netty SSL的例子来演示其配置和应用。</para>
|
||
<para>Netty SSL拥有Netty TCP一样的参数,另外还有下列的附加参数:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><literal>ssl-enabled</literal>。必须设为<literal>true</literal>以使用SSL。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>key-store-path</literal>。存放SSL密钥的路径(key store)。这是存放客户端证书的地方。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>key-store-password</literal>。用于访问key store的密码。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>trust-store-path</literal>。服务器端存放可信任客户证书的路径。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>trust-store-password</literal>。用于访问可信任客户证书(trust store)的密码。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>配置Netty HTTP</title>
|
||
<para>Netty HTTP 通过HTTP通道传送数据包。在有些用户环境中防火墙只允许有HTTP通信,这时采用Netty HTTP作为ActiveMQ
|
||
的传输层就能解决问题。</para>
|
||
<para>我们提供了一个Netty HTTP的例子来演示其配置和应用。</para>
|
||
<para>Netty HTTP具有和Netty TCP同样的配置参数,另外它还有以下参数:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para><literal>http-enabled</literal>。如果要使用HTTP,这个参数必须设为<literal>true</literal>。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>http-client-idle-time</literal>。客户端空闲时间。如果客户端的空闲时间超过
|
||
这个值,Netty就会发送一个空的HTTP请求以保持连接不被关闭。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>http-client-idle-scan-period</literal>。扫描空闲客户端的间隔时间。单位是毫秒。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>http-response-time</literal>。服务器端向客户端发送空的http响应前的最大等待时间。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>http-server-scan-period</literal>。服务器扫描需要响应的客户端的时间间隔。单位是毫秒。</para>
|
||
</listitem>
|
||
<listitem>
|
||
<para><literal>http-requires-session-id</literal>。如果设为true,客户端在第一次请求后将等待
|
||
接收一个会话ID。http 连接器用它来连接servlet接收器(不建议这样使用)。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
</section>
|
||
<section>
|
||
<title>配置Netty Servlet</title>
|
||
<para>ActiveMQ可以使用Netty servlet来传输消息。使用servlet可以将ActiveMQ的数据通过HTTP传送到一个
|
||
运行的servlet,再由servlet转发给ActiveMQ服务器。</para>
|
||
<para>servlet与HTTP的不同之处在于,当用HTTP传输时,ActiveMQ如同一个web服务器,它监听在某个端口上的HTTP
|
||
请求并返回响应。比如80端口或8080端口。而当使用servlet时,ActiveMQ的传输数据是通过运行在某一servlet容器
|
||
中的一个特定的servlet来转发的。而这个sevlet容器中同时还可能运行其他的应用,如web服务。当一个公司有多个应用
|
||
但只允许一个http端口可以访问时,servlet传输可以很好的解决ActiveMQ的传输问题。</para>
|
||
<para>请参见ActiveMQ所提供的servlet例子来了解详细的配置方法。</para>
|
||
<para>要在ActiveMQ中使用Netty servlet传输方式,需要以下步骤:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>部署servlet。下面是一个web.xml例子:</para>
|
||
<programlisting><?xml version="1.0" encoding="UTF-8"?>
|
||
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
|
||
version="2.4">
|
||
<servlet>
|
||
<servlet-name>ActiveMQServlet</servlet-name>
|
||
<servlet-class>org.jboss.netty.channel.socket.http.HttpTunnelingServlet</servlet-class>
|
||
<init-param>
|
||
<param-name>endpoint</param-name>
|
||
<param-value>local:org.apache.activemq</param-value>
|
||
</init-param>
|
||
<load-on-startup>1</load-on-startup>
|
||
</servlet>
|
||
|
||
<servlet-mapping>
|
||
<servlet-name>ActiveMQServlet</servlet-name>
|
||
<url-pattern>/ActiveMQServlet</url-pattern>
|
||
</servlet-mapping>
|
||
</web-app>
|
||
|
||
</programlisting>
|
||
</listitem>
|
||
<listitem>
|
||
<para>我们还需要在服务器端加上一个特殊的Netty invm 接收器。</para>
|
||
<para>下面是从<literal>activemq-configuration.xml</literal>配置文件中摘取的定义接收器的配置部分:</para>
|
||
<programlisting>
|
||
<acceptors>
|
||
|
||
<acceptor name="netty-invm">
|
||
<factory-class>
|
||
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
|
||
</factory-class>
|
||
<param key="use-invm" value="true"/>
|
||
<param key="host" value="org.apache.activemq"/>
|
||
</acceptor>
|
||
|
||
</acceptors>
|
||
</programlisting>
|
||
</listitem>
|
||
<listitem>
|
||
<para>最后我们需要在客户端配置连接器,也是在<literal>activemq-configuration.xml</literal>文件中来做。如下所示:</para>
|
||
<programlisting><connectors>
|
||
|
||
<connector name="netty-servlet">
|
||
<factory-class>
|
||
org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory
|
||
</factory-class>
|
||
<param key="host" value="localhost"/>
|
||
<param key="port" value="8080"/>
|
||
<param key="use-servlet" value="true"/>
|
||
<param key="servlet-path" value="/messaging/ActiveMQServlet"/>
|
||
</connector>
|
||
|
||
</connectors></programlisting>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>下面列出了初始化参数以及它们的用途:</para>
|
||
<itemizedlist>
|
||
<listitem>
|
||
<para>endpoint - Netty接收器的名字。servlet将向它转发数据包。它与<literal
|
||
>host</literal>参数的值是对应的。</para>
|
||
</listitem>
|
||
</itemizedlist>
|
||
<para>在<literal>web.xml</literal>中定义的servlet的URL形式与在连接器配置文件中定义的
|
||
<literal>servlet-path</literal>值应该相匹配。</para>
|
||
<para>servlet可以与SSL一起使用。只需要在连接器配置中加上下面的配置即可:<programlisting> <connector name="netty-servlet">
|
||
<factory-class>org.apache.activemq.core.remoting.impl.netty.NettyAcceptorFactory</factory-class>
|
||
<param key="host" value="localhost"/>
|
||
<param key="port" value="8443"/>
|
||
<param key="use-servlet" value="true"/>
|
||
<param key="servlet-path" value="/messaging/ActiveMQServlet"/>
|
||
<param key="ssl-enabled" value="true"/>
|
||
<param key="key-store-path" value="path to a keystoree"/>
|
||
<param key="key-store-password" value="keystore password"/>
|
||
</connector>
|
||
</programlisting></para>
|
||
<para>另外你还需要为服务器指定一个KeyStore。打开<literal>server/default/deploy/jbossweb.sar</literal>
|
||
下的<literal>server.xml</literal>文件,按照下面的内容编辑其中的SSL/TLS连接器配置:<programlisting><Connector protocol="HTTP/1.1" SSLEnabled="true"
|
||
port="8443" address="${jboss.bind.address}"
|
||
scheme="https" secure="true" clientAuth="false"
|
||
keystoreFile="path to a keystore"
|
||
keystorePass="keystore password" sslProtocol = "TLS" />
|
||
|
||
</programlisting>SSL需要keystore和访问密码。参见servlet ssl例子以了解更多的有关信息。</para>
|
||
</section>
|
||
</section>
|
||
</chapter>
|