ActiveMQ in Action-4.3 Connecting to ActiveMQ over the network- 高飞网

4.3 Connecting to ActiveMQ over the network

2016-05-13 19:27:33.0

4.3 经由网络连接到ActiveMQ

    运行ActiveMQ最常见的场景就是作为一个单独的java应用了。这意味着客户端(生产和消费应用)将使用一种网络协议去访问服务端目标。这部分内容中,将会介绍一些你用来获取client-to-broker通讯的可用网络协议。

    我们先从tcp连接器开始,tcp是使用最广泛的协议,它提供了最理想的性能。然后会剖析NIO连接器,当然低层使用的TCP协议,但是额外地提供了一些比tcp连接器更优的扩展性,因为它使用提java的NIO API。UDP网络协议也是因特网应用使用的,因此下一个讲的是UDP连接器。UDP协议之于TCP,牺牲了可用性,引入了性能上的优势。它也会应用到ActiveMQ连接器上,所以UDP连接器能提供一些之于TCP连接器的性能优势,但它仍然不常用,因为它的可用性低。SSL连接器能被用于建立到服务商的安全连接,最后会介绍如何使用HTTP协议进行通讯。当希望,但个部分都会讨论每个协议的优缺点。因此,你可能想去研读你感兴趣的部分然后继续下面的章节。表格4.1 包含了连接器的小节并附带一些描述。

table4.1 用以client-broker通讯的网络协议小结

协议
描述
TCP
使用最广的默认网络协议
NIO
如果你需要为由生产者和消费者到服务端的连接提供更好的可扩展性你要考虑NIO协议
UDP
如果你解决客户端和服务端的防火墙问题,考虑使用UDP协议
SSL
当你想使客户端与服务端间的通讯安全时考虑SSL。
HTTP(S)
当你你解决客户端和服务端的防火墙问题,考虑使用HTTP(S)协议
VM
即使不是一个网络协议,当你的服务端与客户端通讯使用的是嵌入在同一个jvm虚拟机中时,可以使用VM协议。


4.3.1 传输控制协议(Transcation Control Protocol(TCP))

    TCP是在今天对人类的重要性就好比电力一样。做为最基础的网络协议,在几乎我们所有的在线通讯中都使用到它。它作为一个底层的网络协议,应用于广泛的网络服务如电邮和网站。

    希望你已经熟悉了基本的TCP协议,但让我们通过引用RFC793规则说明书(http://mng.bz/Bns2)来开始关于TCP的讨论:

TCP协议,在主机之间通过网络交换意在作为一个高可用的主机到主机的数据包,连通网络系统。

    由于服务端到客户端应用是网络上的主机尝试以可靠性的方式进行通讯,很容易发现为什么TCP对于JMS实现来说是一个理想的网络协议。所以一点也不惊讶TCP传输连接器是ActiveMQ连接器中最为广泛使用的。

    在跨越网络进行数据交换之前,我们需要将数据序列化为另一种格式。消息必须被(反)序列化为字节序列在线路上通过称为wire protocol线协议发送出去。该协议的说明在ActiveMQ网站上可以找到(http://mng.bz/u23T). OpenWire协议不像TCP一样,它不可以被用于其他网络协议。它的主要目的是网络上的快速有效的数据交换。而且正如OpenWire一个标准且开放的协议允许原生的ActiveMQ客户端在不同语言环境中开发。该话题和ActiveMQ可用的其他线路层协议的描述在第9章讲到。

    正如面前看到的,一个默认默认的配置下,启动ActiveMQ时使用了TCP协议,在61616端口监听客户端,TCP连接器URI使用下面的语法:

tcp://hostname:port?key=value&key=value

    请注意上面黑色字体的内容是必须的。任何跟在?后面的一个kv对是可选的并以&号连接。

    我们在本节和以后的章节都会不介绍任何特定协议的可选项。这些可选项由在线说明书中介绍会比较好。TCP连接器的最新手册在ActiveMQ的网站可以以看到(http://mng.bz/ngU2)。

    下面的配置片段示例在如何在ActiveMQ的配置文件中配置TCP连接器:

<transportConnectors>
<transportConnector name="tcp"
uri="tcp://localhost:61616?trace=true"/>
</transportConnectors>

    注意加在TCP协议uri后面的可选项trace,该可选项表示服务端会记录在这个连接器上的所有的命令,其将有利于调试之目的。在这里我们把他作为连接器可选项的一个示例。使用trace可选项的更多信息查看第14章。

    要点:在修改了配置文件之后,ActiveMQ必须重启才能生效。

    前面的部分概述了客户端应用连接到服务端使用的协议。仅仅作为参考,下面的示例展示了如何使用TCP连接器运行消费者(consumer)。

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Consumer \
-Dexec.args="tcp://localhost:61616 CSCO ORCL"
...
ORCL 65.71 65.78 up
ORCL 66.07 66.14 up
ORCL 65.93 65.99 down
CSCO 23.30 23.33 up
...

    下文列出了TCP连接器的优势:

  •     · 高效:由于该连接器使用了OpenWire协议将消息转化为字节流(或者相反),因此它在网络应用和性能方面都表示出色。
  •     · 可用性:TCP是使用最广泛的网站协议之一,从java最初就已经支持,所以几乎肯定支持你的平台。
  •     · 可靠性:TCP协议能确定消息不会在网络上丢失(例如一些小故障)

    现在让我们介绍一些TCP连接器的替代品吧。

4.3.2 New I/O API (NIO)

    New I/O(NIO)是在java SE1.4时引入的,为了增强直到当前还一直使用的I/O API(标准IO)。权管其名称前面有个New,NIO并不是传统Java IO Api的替代品。它的目的是为了提供一个二选一的方式去进行网络编程和访问一些现代操作系统的低层IO操作。NIO最显著的特点就是选择器(selectors)和非阻塞IO(nonbloching),允许开发者用相同的资源处理更多的网络客户端和在服务上通常更重的负载。

    从客户端的角度来看,NIO传输连接器和标准的TCP连接器没什么区别,依据是它低层仍然使用的CTP协议和OpenWire作为消息序列化协议。仅有的不同是协议显示的表面之下,使用的是NIO的API。这使得NIO连接器更适合以下场景:

    你想要更多的客户端连接到服务端:通常连接到服务端的客户端数量是受操作系统支持线程数限制的。由于NIO连接器的实现会比TCP连接器启动更少的连接器,你应在TCP不能满足你要求的这种情况下使用NIO连接器。

    繁忙的网络通信:通常NIO连接器要比TCP连接器性能更优越(因此它在服务端消耗更少的资源),所以你发现TCP连接器不能满足你要求时使用。

    注意到这点是重要的,就是在调整ActiveMQ性能时不仅仅是选择正确的连接器,许多其他方式也可以调节,包括服务端网络拓扑结构和生产者与消费者设置的可选项。

    URI连接器语法与TCP连接器语法类似,仅有的不同是nio scheme代替了tcp。如下:

nio://hostname:port?key=value

    看下下面的配置文件片段,粗体表示nio部分:

<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true" />
<transportConnector
name="nio"
uri="nio:localhost:61618?trace=true" />
</transportConnectors>

    现在运行股票项目示例,但这次你将使用不同的连接器连接生产者和消费者。如图4.3所示,生产者会使用NIO连接器发送消息,而消费者会使用TCP连接器接收消息。

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Publisher \
-Dexec.args="nio://localhost:61618 CSCO ORCL"
...
Sending: {price=65.713356601409, stock=JAVA, offer=65.779069958011, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=66.071605671946, stock=JAVA, offer=66.137677277617, up=true}


    注意,在连接器uri中nio scheme用以指定使用的是NIO连接器。

    消费者应该使用tcp连接器如下:

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Consumer \
-Dexec.args="tcp://localhost:61616 CSCO ORCL"
...
ORCL 65.71 65.78 up
ORCL 66.07 66.14 up
ORCL 65.93 65.99 down
CSCO 23.30 23.33 up
...
    

    在生产者和消息费者都启动之后,你会注册到应该之间的消息正如预期的进行交换。实际上,他们使用了不同的连接器交流数据,而服务端没有扮演任何角色。


4.3.3 用户数据报协议(User Datagram Protocol (UDP))

    用户数据报协议与tcp都是网络协议的核心。两种协议的目的都是相同的,都是在网络上发送和接收数据。但他们之间有一些区别:

    TCP是一个面向流的协议:意味着数据包的序列是有保障的。不可能会出现消息重复或者次序颠倒问题。然而UDP不同,接收者可能会接收到重复数据或者次序颠倒。

    TCP协议保证传输包的可靠性:意味着tcp协议传输数据时,数据不会丢失。这在发送者和接收者之间的活动连接是受保证的。UDP连接是一个无连接的协议,所以它不能保证这个。

    由于以上的不同,TCP协议用在那些要求可用性的场景,如email,而UDP经常在要求快速传输数据并且可以处理数据包丢失时有用武之地,如VoIP或在线游戏。

    你可以通过UDP连接器,从而使用UDP协议连接到ActiveMQ,UDP连接器的语法与TCP的非常相似。仅有的不同就是把scheme从tcp换成udp。如下片段:

udp://hostname:port?key=value

完整的UDP协议说明可以查看ActiveMQ官网:http://mng.bz/1i4g

    

TCP和UDP传输的比较

    当考虑TCP和UDP协议时,首选考虑的问题就是比较这两个协议。什么时候你应该使用UDP而不是TCP协议呢?有两个基本的情况UDP协议具有优势:

    · 服务端在防火墙之后,你不能控制它,你只能通过UDP端口访问。

    · 你使用的是时间敏感的消息,并且你想尽可能的消除网络传输带来的延迟。

    当然UDP连接器也有缺憾:

    · 由于UDP是不可靠的,你的程序可能会因丢失数据而退出,所以应用该知道如何处理这个情况。

    · 客户端和服务端的网络数据包传输可能不仅仅是消息,也可能包含所谓的控制命令。如果这些控制命令由于UDP的不可靠而丢失,JMS连接将很危险。

    现在让我们在不同的端口上配置TCP和UDP连接器,下面是配置片段,UDP粗体的即是。

<transportConnectors>
<transportConnector
name="tcp"
uri="tcp://localhost:61616?trace=true"/>
<transportConnector
name="udp"
uri="udp://localhost:61618?trace=true" />
</transportConnectors>


以UDP协议运行之前的股票示例的生产者:

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Publisher \
-Dexec.args="udp://localhost:61618 CSCO ORCL"
...
Sending: {price=65.713356601409, stock=JAVA, offer=65.779069958011, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=66.071605671946, stock=JAVA, offer=66.137677277617, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=65.929035001620, stock=JAVA, offer=65.994964036622, up=false}
on destination: topic://STOCKS.JAVA
...

    消息消费者仍然使用TCP协议如下:

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Consumer \
-Dexec.args="tcp://localhost:61616 CSCO ORCL"
...
ORCL 65.71 65.78 up
ORCL 66.07 66.14 up
ORCL 65.93 65.99 down
CSCO 23.30 23.33 up
...

       不出所料,系统从整体上与只使用TCP协议表现一样。这主要是由于程序运行在本地网络,非常可靠且没有数据包丢失。


4.3.4 安全套接字层协议(Secure Sockets Layer Protocol,SSL)

    想像一下把你置身于这样的环境中:把服务端完全暴露在一个不安全的网络中,并且需要保护数据的隐秘。相同的问题也出现在网站超越了学术领域并进入企业用途时。通过TCP发送原生数据变得不再安全,难以找到一个解决方案。安全数据传输的方案就是安全套接字层(Secure Sockets Layer,SSL),设计这种协议使得数据在TCP之上加密传输。它使用一对密钥(公钥和私钥)去确保一个安全的通讯通道。ActiveMQ提供了这种SSL协议,使得客户端与服务端之间的通讯可以加密传输数据。由于在配置SSL的过程中涉及到SSL、密钥和证书,所以这部分内容会更长一些,让我们研究下配置细节。

URI的语法如下:

ssl://hostname:port?key=value

    由于ssl是基于TCP协议的,因此可选项也一样。更多关于使用SSL连接器的信息可以查看ActiveMQ官网(http://mng.bz/s8I2)。

    ActiveMQ使用Java Secure Socket Extension(JSSE)实现的SSL功能,更详细的介绍超出了本书的范畴,在阅读后面的部分之前可以查看在线的关于JSSE的资料(http://mng.bz/7TYe)。

    配置ActiveMQ服务端使用SSL,第一步是配置SSL连接器。

    修改配置文件${ACTIVEMQ_HOME}/conf/activemq.xml文件的<transportConnectors>节点:

<transportConnectors>
<transportConnector name="ssl" uri="ssl://localhost:61617?trace=true" />
</transportConnectors>

    但是SSL传输还需要其他几项配置才能使之正常工作。这些项目包括为了能成功使用SSL协议通讯的SSL证书。基本上,JSSE定义了两种类型的文件用以保存密钥和证书。第一个被称为keystores,保存了你自己的私钥证书和私钥密钥。应用的受信任的证书保存在trustores。要想真正地使用SSL传输正常地工作,额外的这些项目在下面两部分中详细讲解。

关于ActiveMQ中的默认keystores和trustores的注意事项

    在ActiveMQ的${ACTIVEMQ_HOME}/conf/目录下发布的默认的keystores和trustores。在该目录下,你会发现一个包含了默认服务端证书的keystore,即broker.ks,同时还有一个服务端用来持有可信任客户端证书的trustores,即broker.ts。默认情况下,ActiveMQ使用broker.ks和broker.ts进行SSL连接器。请注意默认的keystores和trustores仅仅用以示例的目的,不应将之用于生产环境当中。如果是生产环境,强烈建议你创建自己的keystore和trustore.

    更多关于SSL配置的内容查看第6章。

    现在你已经理解了能成功使用SSL通讯的配置要素,是时候真正操练一下了。首选,你将使用默认的证书连接到服务端。第二步你通过生成自己的证书去运行服务端和客户端。


使用SSL

    首选体验一下,在你未提供任何其他的SSL相关的选项参数时使用SSL连接到服务端会发生什么事情。将股票投资项目的消费者通过SSL协议,但不创建适当的密钥存储时连接就会报错。这里有个简单修改连接器的示例:

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Consumer \
-Dexec.args="ssl://localhost:61617 CSCO ORCL"

    不创建不指示适当的keystore和trustore,会出现以下的异步信息:

WARNING: Async exception with no exception listener:
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException: PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target
javax.net.ssl.SSLHandshakeException:
sun.security.validator.ValidatorException:
PKIX path building failed:
sun.security.provider.certpath.SunCertPathBuilderException:
unable to find valid certification path to requested target


注:译者的ActiveMQ5.13.2报的如下异常:

Exception in thread "main" javax.jms.JMSException: Could not connect to broker URL: ssl://localhost:61617. Reason: javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target


服务端的异常如下:

ERROR | Could not accept connection from tcp://127.0.0.1:52340: javax.net.ssl.SSLHandshakeException: Received fatal alert: certificate_unknown

这些异常意味着ssl连接没有建立起来。

这是一个所有的客户端连接到未受信任的服务端时出现的错误(未提供适当的keystore和strustore)。

使用JSSE,你必须通过系统属性提供一些ssl参数。为了能成功经由SSL连接到服务端,我们必须提供keystore,keystore的密码,和使用的trustore。这些由下面的系统属性来完成:

· javax.net.ssl.keyStore——定义了客户端使用的keystore

· javax.net.ssl.keyStorePassword——定义了keyStore的密码

· javax.net.ssl.trustore——定义了客户端使用的trustore

现在,看一下下面的股票投资项目的生产者运行示例,使用默认的客户端证书库。

$ mvn \
-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/client.ks \
-Djavax.net.ssl.keyStorePassword=password \
-Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/client.ts \
exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Publisher \
-Dexec.args="ssl://localhost:61617 CSCO ORCL"
...
Sending: {price=65.713356601409, stock=JAVA, offer=65.779069958011, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=66.071605671946, stock=JAVA, offer=66.137677277617, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=65.929035001620, stock=JAVA, offer=65.994964036622, up=false}
on destination: topic://STOCKS.JAVA
...

    下面黑色字体即为使用JSSE。这些属性提供了必要的keystore、keystore password和trustore。提供了这些参数之后,生产者将能成功连接到服务端而不会再报错。当然,如果客户端与服务端没有在一台机器上,你要拷贝上面的文件和修改相应的路径。

    类似地,消费者也可以这样运行。

$ mvn \
-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/client.ks \
-Djavax.net.ssl.keyStorePassword=password \
-Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/client.ts \
exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Consumer \
-Dexec.args="ssl://localhost:61617 CSCO ORCL"

...
ORCL 65.71 65.78 up
ORCL 66.07 66.14 up
73 Connecting to ActiveMQ over the network
ORCL 65.93 65.99 down
CSCO 23.30 23.33 up
...

    译者注:

如果是在eclipse中运行,可以通过下面的方式设置系统变量:

System.setProperty("javax.net.ssl.keyStore", "${ACTIVEMQ_HOME}/conf/client.ks");
System.setProperty("javax.net.ssl.keyStorePassword", "password");
System.setProperty("javax.net.ssl.trustStore", "${ACTIVEMQ_HOME}/conf/client.ts");

    再次注意粗体部分使用的JSSE系统属性。现在两个客户端都可以经由SSL连接器通过加密的网络通道进行通讯的。

    使用默认的证书、keystore和trustore,在开发环境是没有问题的,但是生产环境,还是强烈建议创建自己的证书。甚至可以把你不使用的证书禁用密码,你需要从受信任的证书机构购买合适的SSL证书。

http://activemq.apache.org/how-do-i-use-ssl.html

    遗憾的是,译者使用默认的keystore,服务端报错:

Could not accept connection from tcp://127.0.0.1:56659: javax.net.ssl.SSLHandshakeException: no cipher suites in common

客户端报错:

Exception in thread "main" javax.jms.JMSException: Could not connect to broker URL: ssl://localhost:61617. 
Reason: javax.net.ssl.SSLHandshakeException: Received fatal alert: handshake_failure

    开始怀疑该密钥库的密码不是password,用下面的命令,使用password作为密码,正常打印出了信息。

keytool -list -v -keystore client.ks


解决办法:http://troyjsd.blogspot.jp/2013/06/activemq-https.html

简单地说,就是在启动脚本中(ACTIVEMQ_HOME/bin/activemq.bat)也加入上面的系统参数:

在if "%ACTIVEMQ_OPTS%" == "" set ACTIVEMQ_OPTS=-Xms1G -Xmx1G -Djava.util.logging.config.file=logging.properties -Djava.security.auth.login.config=%ACTIVEMQ_CONF%\login.config 的后面,加上:

-Djavax.net.ssl.keyStore=%ACTIVEMQ_CONF%\broker.ks -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=%ACTIVEMQ_CONF%\broker.ts

或者更简单的:

bin\activemq.bat start -Djavax.net.ssl.keyStore=d:/software/apache-activemq-5.13.2/conf\broker.ks -Djavax.net.ssl.keyStorePassword=password -Djavax.net.ssl.trustStore=d:/software/apache-activemq-5.13.2/conf\broker.ts


生成你自己的SSL资源

    为了开发的需要,你可能会想创建一个自签名的证书。接下来的部分,你带领你亲自实战一下创建和分享自签名证书。因此需要使用keytool这个工具,这是一个随java一起发布用以管理密钥库的命令行工具。

    首选,你要为服务端创建一个密钥库和证书。下面是一个示例:

$ keytool -genkey -alias broker -keyalg RSA -keystore mybroker.ks
Enter keystore password: test123
What is your first and last name?
[Unknown]: Dejan Bosanac
What is the name of your organizational unit?
[Unknown]: Chapter 4
What is the name of your organization?
[Unknown]: ActiveMQ in Action
What is the name of your City or Locality?
[Unknown]: Belgrade
What is the name of your State or Province?
[Unknown]:
What is the two-letter country code for this unit?
[Unknown]: RS
Is CN=Dejan Bosanac, OU=Chapter 3, O=ActiveMQ in Action,
L=Belgrade, ST=Unknown, C=RS correct?
[no]: yes
Enter key password for <broker>
(RETURN if same as keystore password):
    keytool工具提示你输入一些证书数据并创建一个密钥库,证书就放在这里。这个例子中我们创建了一个名为myborker.ks的密钥库,密码是test123.

    下一步是从密钥库中导出证书。所以它可以与客户端的客户端共享。用以下的命令导出:

$ keytool -export -alias broker -keystore mybroker.ks -file mybroker_cert
Enter keystore password: test123
Certificate stored in file <mybroker_cert>
    这一步创建了一个名为mybroker_cert的证书。

    现在你要创建了一个带有相应证书的客户端密钥库,用的命令与前面创建服务端密钥库的类似。

$ keytool -genkey -alias client -keyalg RSA -keystore myclient.ks
What is your first and last name?
[Unknown]: Dejan Bosanac
What is the name of your organizational unit?
[Unknown]: Chapter 4
What is the name of your organization?
[Unknown]: ActiveMQ in Action
What is the name of your City or Locality?
[Unknown]: Belgrade
What is the name of your State or Province?
[Unknown]:
What is the two-letter country code for this unit?
[Unknown]: RS
Is CN=Dejan Bosanac, OU=Chapter 3, O=ActiveMQ in Action,
L=Belgrade, ST=Unknown, C=RS correct?
[no]: yes
Enter key password for <client>
(RETURN if same as keystore password):
    命令的结果是用于客户端的带有相应证书的myclient.ks文件。最后,客户端信任库必须创建,服务端证书要导入进去。keytool用下面的命令去实现:
$ keytool -import -alias broker -keystore myclient.ts -file mybroker_cert
Enter keystore password: test123
Owner: CN=Dejan Bosanac, OU=Chapter 3, O=ActiveMQ in Action,
L=Belgrade, ST=Unknown, C=RS
Issuer: CN=Dejan Bosanac, OU=Chapter 3, O=ActiveMQ in Action,
L=Belgrade, ST=Unknown, C=RS
Serial number: 484fdc8a
Valid from: Wed Jun 11 16:09:14 CEST 2008 until: Tue Sep 09 16:09:14 CEST 2008
Certificate fingerprints:
MD5: 04:66:F2:AA:71:3A:9E:0A:3C:1B:83:C0:23:DC:EC:6F
SHA1: FB:FA:BB:45:DC:05:9D:AE:C3:BE:5D:86:86:0F:76:84:43:C7:36:D3
Trust this certificate? [no]: yes
Certificate was added to keystore

    

    用上面的步骤,所有必需的密钥库都已经创建了,服务端证书也已经导入进去了。现在股票投资项目示例就可以用上了。

    记得在启动broker昌要使用新创建的证书。一种方式用新生成的文件替换conf/目录下的相应文件。例如,如果你不想改示例中的代码,而使用新创建的证书。用下面的命令:

$ cp src/main/resources/org/apache/activemq/book/ch4/mybroker.ks \
${ACTIVEMQ_HOME}/conf/broker.ks
$ cp src/main/resources/org/apache/activemq/book/ch4/myclient.ks \
${ACTIVEMQ_HOME}/conf/client.ks
$ cp src/main/resources/org/apache/activemq/book/ch4/myclient.ts \
${ACTIVEMQ_HOME}/conf/client.ts


    另外一种方式是带着系统参数启动服务。我们首先将生成的证书文件带着它们原始的名字拷贝到conf/目录下面:

$ cp src/main/resources/org/apache/activemq/book/ch4/mybroker.ks \
${ACTIVEMQ_HOME}/conf/
$ cp src/main/resources/org/apache/activemq/book/ch4/myclient.ks \
${ACTIVEMQ_HOME}/conf/
$ cp src/main/resources/org/apache/activemq/book/ch4/myclient.ts \
${ACTIVEMQ_HOME}/conf/
    现在就可以带着系统参数用另外的密钥库而不是默认的了。

${ACTIVEMQ_HOME}/bin/activemq console \
-Djavax.net.ssl.keyStorePassword=test123 \
-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/mybroker.ks

    最后,可以用<sslContext/>元素配置以达到相同的效果。如下:

<broker xmlns="http://activemq.apache.org/schema/core" brokerName="localhost"
dataDirectory="${activemq.base}/data">
<sslContext>
<sslContext keyStore="file:${activemq.base}/conf/mybroker.ks" keyStorePassword="test123"/>
</sslContext>
<transportConnectors>
<transportConnector name="ssl" uri="ssl://localhost:61617" />
</transportConnectors>
</broker>

    用一般的方式启动服务端:

${ACTIVEMQ_HOME}/bin/activemq console
xbean:src/main/resources/org/apache/activemq/book/ch4/activemq-ssl.xml
...
Loading message broker from:
xbean:src/main/resources/org/apache/activemq/book/ch4/activemq-ssl.xml
INFO | Using Persistence Adapter:
AMQPersistenceAdapter(/workspace/apache-activemq-5.3.0/data/localhost)
INFO | AMQStore starting using directory:
/workspace/apache-activemq-5.3.0/data/localhost
INFO | Kaha Store using data directory
/workspace/apache-activemq-5.3.0/data/localhost/kr-store/state
INFO | Active data files: []
INFO | ActiveMQ 5.3.0 JMS Message Broker (localhost) is starting
INFO | For help or more information please see: http://activemq.apache.org/
INFO | Kaha Store using data directory
/workspace/apache-activemq-5.3.0/data/localhost/kr-store/data
INFO | JMX consoles can connect to
service:jmx:rmi:///jndi/rmi://localhost:1099/jmxrmi
INFO | Listening for connections at: ssl://localhost:61617
INFO | Connector ssl Started
INFO | ActiveMQ JMS Message Broker
(localhost, ID:dejan-bosanacs-macbook-pro.local-52935-1265550444721-0:0)
started
...

    现在看一下如何将相同的变更应用于客户端。如果你尝试用旧的证书文件运行客户端应用,你会得到一个错误: unknown_certificate。就像当你不用任何证书去连服务端一样。所以你不得不更新命令:

$ mvn \
-Djavax.net.ssl.keyStore=${ACTIVEMQ_HOME}/conf/myclient.ks \
-Djavax.net.ssl.keyStorePassword=test123 \
-Djavax.net.ssl.trustStore=${ACTIVEMQ_HOME}/conf/myclient.ts \
exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Publisher \
-Dexec.args="ssl://localhost:61617 CSCO ORCL"
...
Sending: {price=65.713356601409, stock=JAVA, offer=65.779069958011, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=66.071605671946, stock=JAVA, offer=66.137677277617, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=65.929035001620, stock=JAVA, offer=65.994964036622, up=false}
on destination: topic://STOCKS.JAVA
...

    上面的指令使得发布者使用了最新生成的客户端秘钥库。在这些修改之后,股票投资应用又可以工作了。

    启用和禁用SSL加密算法Cipher

    ActiveMQ的SSL传输的密码套件由JVM提供。关于密码套件的专业信息可以查看SUN JSSE提供的文档(http://mng.bz/7TYe)。SUN JSSE提供了很多密码套件,并且以其默认的优先级顺序被使用。在有些情况下,可能需要禁用某个密码套件,例如这种情况,在一个流行的加密算法中发现一个漏洞,或者需求要求只支持某一个加密算法。为了方便启动或禁用某一个加密算法,从ActiveMQ5.4.0开始,添加了一个新的选项,名为 transport.enabledCipherSuites 。如下示例:

<transportConnectors>
<transportConnector
name="ssl"
uri="ssl://localhost:61617?
transport.enabledCipherSuites=SSL_RSA_WITH_RC4_128_SHA" />
</transportConnectors>

    请注意,这里为了可读性好,把uri属性,分为两行了。这仅仅是为了可读性好,如果以这种方式配置会导致出错,如果你使用这个例子,一定要把这两行合成一行,用“号引起来。

    前面的例子中,加密算法 SSL_RSA_WITH_RC4_128_SHA被设置为仅被启用的。其他的加密算法也可以被启动,只要用,隔开追加在后面。增加这个或选项的目的,是为了添加安全性,因为它只允许某种加密算法使用。如支付卡行业中,考虑到某些加密算法太弱难以启动的场景。

    注意:为了测试一个加密算法是否可启用,可以参考一个Perl脚本,名为ssl-cipher-check.pl。可以在http://mng.bz/Ko7k 看到。该脚本受支付卡行业数据安全标准(Payment Card Industry Data Security Standard ( PCI DSS ))启发,为了防止信用卡欺诈(http://mng.bz/8cYo).该算法简单易用,并且可以非常方便的检查加密算法是否是疲弱的。

    并非每个人都需要禁用SSL加密算法,但如果你需要,这个新选项可以使之更方便。


4.3.5 超文本传输协议(Hypertext Transfer Protocol (HTTP/HTTPS))

    在很多环境中,防火墙配置得只允许一些基本的服务,如web服务或email访问。所以ActiveMQ如何在这种情况下使用呢?这时就到了http协议上场了。

    HTTP协议最初是设计用以在web上传输超文本(html)页面用的。底层网络协议使用tcp,为了在浏览器和服务器之间更好的通讯,添加了一些额外的逻辑。在第一次互联网繁荣之后,web基础架构和http协议,都被看作支持web服务的一个新的特别的角色,这些日子中,通常被用在应用之间交换数据。主要不同的是,这些web服务中,交换的是xml格式的数据,而不是html格式的。

    ActiveMQ实现了http协议连接器,这让服务端使用http协议时提供了交换xml格式消息的能力。这允许ActiveMQ绕开严格的防火墙规则。可以这么说,通过使用http协议运行标准端口(80),ActiveMQ实际上使用防火墙的一个漏洞。

    这种协议的uri语法为:

http://hostname:port?key=value

    安全https也支持:

https://hostname:port?key=value

    注意,上面两个示例中,scheme上的轻微的差别,是基于是否使用ssl。让我们亲自体验一下配置过程,如何把示例运行在http协议上。该协议连接器的xml配置与之前的协议连接器的非常相似,只是改成了http的scheme。

<transportConnectors>
	<transportConnector name="tcp" uri="tcp://localhost:61616?trace=true"/>
	<transportConnector name="http" uri="http://localhost:8080?trace=true" />
</transportConnectors>

    注意:上面配置了两个协议,TCP和HTTP。其他中HTTP协议监听在8080端口上。

为了使用HTTP传输协议运行客户端,必须类路径加上一个依赖。HTTP传输属于ActiveMQ的一个可选模块,所以你不得不将其添加到应用的类路径里面。如果使用的是Maven的话,在pom.xml中添加下面的依赖即可:

<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-optional</artifactId>
	<version>5.4.1</version>
</dependency>

    注:译者使用的是ActiveMQ5.13.2,因此依赖稍有不同,如下:

<dependency>
	<groupId>org.apache.activemq</groupId>
	<artifactId>activemq-optional</artifactId>
	<version>5.7.0</version>
</dependency>
<dependency>
	<groupId>com.thoughtworks.xstream</groupId>
	<artifactId>xstream</artifactId>
	<version>1.4.8</version>
</dependency>

    这将会将activemq可选模块包含进来,并且所有的依赖库也都会包含到类路径中。如果你没有使用maven,确认下你包含下面的jar到你的类库目录下即可:

$ACTIVEMQ_HOME/lib/optional/activemq-optional-<version>.jar
$ACTIVEMQ_HOME/lib/optional/commons-httpclient-<version>.jar
$ACTIVEMQ_HOME/lib/optional/xstream-<version>.jar
$ACTIVEMQ_HOME/lib/optional/xmlpull-<version>.jar

    最后,就可以运行股票投资项目,使用HTTP协议:

$ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch4.Publisher \
-Dexec.args="http://localhost:8080 CSCO ORCL"
...
Sending: {price=65.713356601409, stock=JAVA, offer=65.779069958011, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=66.071605671946, stock=JAVA, offer=66.137677277617, up=true}
on destination: topic://STOCKS.JAVA
Sending: {price=65.929035001620, stock=JAVA, offer=65.994964036622, up=false}
on destination: topic://STOCKS.JAVA
...
    


    正如上面显示的,当使用HTTP协议时,所以的服务端到客户端的通讯,都变为XML消息格式。这种通讯类型相对于使用TCP协议,对整个系统都有性能上的影响。因此,如果特别考虑性能因素的话,你最好还是使用TCP传输数据,并找到一个其他的工作环境以解决防火墙问题。

    到目前为止,该章已经包含了在操作系统中,用网络方式连接服务端和客户端的所用的协议。作为一个另一个替代选择,ActiveMQ设置时可以嵌入到java应用当中。这允许客户端到服务端的数据通讯可以放置在虚拟机当中进行,而不是走网络。为了支持这种虚拟机内部的通讯,ActiveMQ提供了一种特别的协议,叫作VM协议(VM protocol)。