Chapter 16. 传输层的配置

HornetQ的传输层是“可插拔的”。通过灵活的配置和一套服务提供接口(SPI),HornetQ可以很容易地更换其传输层。

在本章中我们将对HornetQ的传输相关的概念作出解释,并说明它的配置方法。

16.1. 接收器(Acceptor)

接收器(acceptor)是 HornetQ 的传输层中最为重要的概念之一。首先 介绍一下在文件hornetq-configuration.xml中是怎样定义一个接收器的:

<acceptors>                
    <acceptor name="netty">
        <factory-class>
org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory
        </factory-class>
        <param key="port" value="5446"/>
    </acceptor>
</acceptors>            
        

所有接收器都在 acceptors单元(element)内定义。在acceptors 内可以有零个或多个接收器的定义。每个服务器所拥有的接收器的数量是没有限制的。

每个接收器都要定义其与HornetQ服务器连接的方式。

以上的例子中我们定义了一个Netty接收器。它在端口5446监听连接请求。

acceptor单元内有一个子单元factory-class。这个单元是用来 定义创建连接器的工厂类。一个连接器工厂类必须要实现AcceptorFactory接口。上例中我们定义 的连接器工厂是类NettyAcceptorFactory使用Netty来建立连接。有个这个类定义,HornetQ就知道了用什么传输来建立连接了。

acceptor中还可以配置零或多个参数param。在每个param 中定义的是键-值对(key-value)。这些参数用来配置某个传输实现。不同传输有不同的配置参数。

像IP地址、端口号等都是传输配置参数的例子。

16.2. 连接器(Connectors)

接收器定义的是如何在服务器端接收连接,而连接器则是定义客户端如何连接到服务器。

以下是hornetq-configuration.xml文件中一个连接器配置的例子。

<connectors>
    <connector name="netty">
        <factory-class>
            org.hornetq.core.remoting.impl.netty.NettyConnectorFactory
        </factory-class>
        <param key="port" value="5446"/>
    </connector>
</connectors>            
        

连接器的配置在connectors单元中。可以定义一个或多个连接器。每个服务器配置的连接器 数量是没有限制的。

你可能注意到了,既然连接器是定义客户端如何连接服务器的,那么为什么要定义在 服务器端呢?原因如下:

  • 服务器有时也需要做为客户端去连接其它的服务器,比如当一个服务器通过桥连接到另一个服务器,或者是集群 中服务器之间的互相通迅。在这种情况下服务器就要知道如何与另一台服务器建立连接。因此需要在 connectors下定义连接器。

  • 如果你使用JMS服务,需要创建连接工厂的实例并绑定到JNDI。在HornetQ创建 HornetQConnectionFactory时需要连接器的必要信息,以便这个连接工厂 能知道它如何与HornetQ服务器相连接。

    这一信息被定义在配置文件hornetq-jms.xml中的connector-ref单元下。下面这段配置 就是从该配置文件中提取的相关部分,它展示了JMS的连接工厂是如何引用定义在配置文件hornetq-configuration.xml中的连接器的:

    <connection-factory name="ConnectionFactory">
        <connectors>
           <connector-ref connector-name="netty"/>
        </connectors>
        <entries>
            <entry name="ConnectionFactory"/>
            <entry name="XAConnectionFactory"/>
        </entries>
    </connection-factory>                
                

16.3. 在客户端直接配置传输层

怎样配置一个内核ClientSessionFactory以让它知道如何连接服务器的信息呢?

在直接配置内核ClientSessionFactory的时候,可以间接地使用连接器。当然在这种情况 下在服务器端定义连接器是没有意义的。我们通过将必要参数传给ClientSessionFactory的 方法来告诉使用什么样的连接器工厂。

在下面的例子中,我们创建了一个ClientSessionFactory,它可以直接连接到我们先前定 义的接收器上。它使用的是标准的Netty TCP传输层,连接主机是localhost(默认),端口5446:

Map<String, Object> connectionParams = new HashMap<String, Object>();
    
connectionParams.put(org.hornetq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, 
                    5446);

TransportConfiguration transportConfiguration = 
    new TransportConfiguration(
    "org.hornetq.core.remoting.impl.netty.NettyConnectorFactory", 
    connectionParams);

ClientSessionFactory sessionFactory = HornetQClient.createClientSessionFactory(transportConfiguration);

ClientSession session = sessionFactory.createSession(...);

etc                       
        

如果在客户端直接使用JMS的连接工厂的话,也可以用类似的方法而不需要在服务器端定义连接器或在 hornetq-jms.xml配置文件中创建连接工厂:

Map<String, Object> connectionParams = new HashMap<String, Object>();

connectionParams.put(org.hornetq.core.remoting.impl.netty.TransportConstants.PORT_PROP_NAME, 5446);

TransportConfiguration transportConfiguration = 
    new TransportConfiguration(
    "org.hornetq.core.remoting.impl.netty.NettyConnectorFactory", 
    connectionParams);

ConnectionFactory connectionFactory = HornetQJMSClient.createConnectionFactory(transportConfiguration);

Connection jmsConnection = connectionFactory.createConnection();

etc                       
        

16.4. 配置 Netty 传输层

HornetQ当前使用Netty作为其默认的连接层。Netty是一个高性能的底层网络库.

Netty传输的配置有几种不同的方法。它可以使用传统的Java IO(阻塞方式)、NIO(非阻塞)或直接使用 TCP socket及SSL。或者使用HTTP或HTTPS协议。同时还可能使用servlet进行传输。

采用Netty应该能满足绝大部分的传输要求。

16.4.1. 配置 Netty TCP

Netty TCP 是简单的非加密的基于TCP socket的传输。它可以使用阻塞式的Java IO或非阻塞式的Java NIO。 我们建议在服务器端采用非阻塞式的NIO以获得良好的并发处理能力。当并发能力并不是很重要时,可以使用阻塞式 的方式以增加响应的速度。

如果你的应用是运行在不信任的网络上,你应该选择使用SSL或HTTPS。

Netty TCP的所有连接都是从客户端发起的。服务器端不向客户端发起任何连接。在有防火墙的环境中,这种方式 是比较适合的。因为防火墙只允许单方向的连接。

org.hornetq.core.remoting.impl.netty.TransportConstants类中定义了所 有的配置参数的名称(key)。它们当中绝大多娄既用于配置接收器也用于配置连接器,有一些只适用于接收器。 下面列出的参数用以配置一个简单的Netty TCP:

  • use-nio。如果设为true则使用非阻塞的Java NIO。如果false则使用传统的阻塞方式的Java IO。

    我们建议使用Java NIO处理并行连接。因为Java NIO不是为每一个连接分配一个线程,所以它要比传统的阻塞式 Java IO具有更强的并发连接的处理能力。如果你不需要处理并发连接,那么使用旧的阻塞式的IO性能会好一些。这个参 数的默认值在服务器端是false,在客户端是false

  • host。主机名或IP地址。对于接收器来说,它是服务器接收连接的地址。 对于连接器端,它是客户端连接的目标地址。默认值是localhost。 在配置接收器时可以指定多个主机名或IP地址,中间用逗号隔开。如果指定的主机是0.0.0.0, 则接收器将从主机上所有的网络接口中接受连接请求。连接器不允许指定多个主机地址,它只能与一个 地址建立连接。

    Note

    一定不要忘记指定一个主机名或IP地址!一个服务器要想接受来自其它节点的连接就必需有一个 主机名或IP地址来绑定及监听外部的连接请求。默认的主机名localhost是不能接受外部的 连接请求的!

  • port。连接的端口。用于配置连接器或接收器。连接器用此端口来建立 连接。接收器在些端口上监听连接请求。默认值是5445

  • tcp-no-delay。将它设为true就会使用 Nagle 算法.默认值是true

  • tcp-send-buffer-size。这个参数指定了TCP的发送缓冲大小,单位是字节。 默认值是32768字节(32KiB)。

    这个参数要根据你的网络的带宽与时延的情况而调整。 这个链接对此有很好的论述。

    简言之,TCP的发送/接收缓冲的大小可以用下面公式来计算:

                            缓冲大小 = 带宽 * RTT
                        

    其中带宽的单位是 每秒字节数,RTT(网络往返程时间)的单位是秒。 使用ping工具可以方便地测量出RTT。

    对于快速网络可以适当加大缓冲的大小。

  • tcp-receive-buffer-size。这个参数指定了TCP接收缓冲的大小,单位是字节。 默认值是32768字节(32KiB)。

  • batch-delay。HornetQ可以通过配置该参数,在数据包写入传输层之前有一个 最大延时(毫秒),达到批量写入的目的。这样可以提高小消息的发送效率。但这样做会增加单个消息的平均发送 延迟。默认值为0毫秒。

  • direct-deliver。消息到达服务器后,默认是由一个不同的线程来将消息传递 到接收者。这样可以使服务的呑吐量和可扩展性达到最佳,特别是在多核的系统上效果更为明显。但是线程切换 会带来一些传递的延迟。如果你希望延迟最小,并不在意呑吐量的话,可以将参数direct-deliver设为true。默认值是true。如果你更希望有 较大的呑吐量的话,将它设为false

  • nio-remoting-threads。如果使用NIO,默认情况下HornetQ会使用系统中处理 器内核(或超线程)数量三倍的线程来处理接收的数据包。内核的数量是通过调用Runtime.getRuntime().availableProcessors()来得到的。如果你想改变这个数量, 你可以设定本参数。默认的值是-1,表示线程数为Runtime.getRuntime().availableProcessors() * 3。

16.4.2. 配置Netty SSL

Netty SSL的配置与Netty TCP相似。它采用了安全套接字层(SSL)来提供加密的TCP连接。

我们提供了一个Netty SSL的例子来演示其配置和应用。

Netty SSL拥有Netty TCP一样的参数,另外还有下列的附加参数:

  • ssl-enabled。必须设为true以使用SSL。

  • key-store-path。存放SSL密钥的路径(key store)。这是存放客户端证书的地方。

  • key-store-password。用于访问key store的密码。

  • trust-store-path。服务器端存放可信任客户证书的路径。

  • trust-store-password。用于访问可信任客户证书(trust store)的密码。

16.4.3. 配置Netty HTTP

Netty HTTP 通过HTTP通道传送数据包。在有些用户环境中防火墙只允许有HTTP通信,这时采用Netty HTTP作为HornetQ 的传输层就能解决问题。

我们提供了一个Netty HTTP的例子来演示其配置和应用。

Netty HTTP具有和Netty TCP同样的配置参数,另外它还有以下参数:

  • http-enabled。如果要使用HTTP,这个参数必须设为true

  • http-client-idle-time。客户端空闲时间。如果客户端的空闲时间超过 这个值,Netty就会发送一个空的HTTP请求以保持连接不被关闭。

  • http-client-idle-scan-period。扫描空闲客户端的间隔时间。单位是毫秒。

  • http-response-time。服务器端向客户端发送空的http响应前的最大等待时间。

  • http-server-scan-period。服务器扫描需要响应的客户端的时间间隔。单位是毫秒。

  • http-requires-session-id。如果设为true,客户端在第一次请求后将等待 接收一个会话ID。http 连接器用它来连接servlet接收器(不建议这样使用)。

16.4.4. 配置Netty Servlet

HornetQ可以使用Netty servlet来传输消息。使用servlet可以将HornetQ的数据通过HTTP传送到一个 运行的servlet,再由servlet转发给HornetQ服务器。

servlet与HTTP的不同之处在于,当用HTTP传输时,HornetQ如同一个web服务器,它监听在某个端口上的HTTP 请求并返回响应。比如80端口或8080端口。而当使用servlet时,HornetQ的传输数据是通过运行在某一servlet容器 中的一个特定的servlet来转发的。而这个sevlet容器中同时还可能运行其他的应用,如web服务。当一个公司有多个应用 但只允许一个http端口可以访问时,servlet传输可以很好的解决HornetQ的传输问题。

请参见HornetQ所提供的servlet例子来了解详细的配置方法。

要在HornetQ中使用Netty servlet传输方式,需要以下步骤:

  • 部署servlet。下面是一个web.xml例子:

    <?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>HornetQServlet</servlet-name>
          <servlet-class>org.jboss.netty.channel.socket.http.HttpTunnelingServlet</servlet-class>
          <init-param>
                 <param-name>endpoint</param-name>
                 <param-value>local:org.hornetq</param-value>
               </init-param>
               <load-on-startup>1</load-on-startup>
       </servlet>
    
       <servlet-mapping>
          <servlet-name>HornetQServlet</servlet-name>
          <url-pattern>/HornetQServlet</url-pattern>
       </servlet-mapping>
    </web-app>
    
    
  • 我们还需要在服务器端加上一个特殊的Netty invm 接收器。

    下面是从hornetq-configuration.xml配置文件中摘取的定义接收器的配置部分:

                        
    <acceptors>
    
          <acceptor name="netty-invm">
             <factory-class>
                org.hornetq.core.remoting.impl.netty.NettyAcceptorFactory
             </factory-class>
             <param key="use-invm" value="true"/>
             <param key="host" value="org.hornetq"/>
          </acceptor>
    
    </acceptors>                                         
                    
  • 最后我们需要在客户端配置连接器,也是在hornetq-configuration.xml文件中来做。如下所示:

    <connectors>
    
          <connector name="netty-servlet">
             <factory-class>
                org.hornetq.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/HornetQServlet"/>
          </connector>
    
     </connectors>

下面列出了初始化参数以及它们的用途:

  • endpoint - Netty接收器的名字。servlet将向它转发数据包。它与host参数的值是对应的。

web.xml中定义的servlet的URL形式与在连接器配置文件中定义的 servlet-path值应该相匹配。

servlet可以与SSL一起使用。只需要在连接器配置中加上下面的配置即可:

    <connector name="netty-servlet">
         <factory-class>org.hornetq.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/HornetQServlet"/>
         <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>

另外你还需要为服务器指定一个KeyStore。打开server/default/deploy/jbossweb.sar 下的server.xml文件,按照下面的内容编辑其中的SSL/TLS连接器配置:

<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" />

SSL需要keystore和访问密码。参见servlet ssl例子以了解更多的有关信息。