-
Notifications
You must be signed in to change notification settings - Fork 208
/
using-jms.xml
273 lines (269 loc) · 17.9 KB
/
using-jms.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
<?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="using-jms">
<title>使用JMS</title>
<para>很多用户喜欢使JMS,因此HornetQ提供了JMS服务。</para>
<para>JMS是一个普遍使用API标准,绝大多数的消息系统都提供JMS接口。如果你对JMS还不熟悉,建议你先参考一下
Sun的<ulink url="http://java.sun.com/products/jms/tutorial/1_3_1-fcs/doc/jms_tutorialTOC.html">
JMS 教程</ulink>。</para>
<para>HornetQ还提供了许多的JMS的示例程序(examples)。比如简单的JMS Queue和Topic的示例,就很适合初学者做为了
解HornetQ JMS的起点。<xref linkend="examples"/>对这些示例作了详细的说明。</para>
<para>下面我们将带领读者一步步地配置HornetQ的JMS服务,并创建一个简单的JMS程序。我们还将展示如何在没有JNDI的情况下
来使用HornetQ中的JMS。</para>
<section>
<title>一个简单的订购系统</title>
<para>本章我们将用一个简单的订购系统做为一个例子。尽管它十分简单,但是它能够很好地向大家展示JMS的设置和使用。</para>
<para>本例中有一个名为 <literal>OrderQueue</literal>JMS队列,还将有一个 <literal>MessageProducer</literal>
用来向队列发送订购消息。发送到队列的消息由一个 <literal>MessageConsumer</literal> 来接收。</para>
<para>我们所用的队列是<literal>持久(durable)</literal>的队列,也就是说这个队列不受服务器故障的影响。当服务器
发生故障重新启动后,这个队列仍然存在。我们需要把这个队列事先部署好。办法就是将队列写到JMS的配置文件中。当服务启动
时将配置文件中的队列自动部署好。</para>
</section>
<section id="using-jms.server.configuration">
<title>JMS服务的配置</title>
<para><literal>hornetq-jms.xml</literal>文件包含了需要创建与部署的JMS Queue,Topic和ConnectionFactory
的实例。该文件必须要指定在classpath中。从这个文件中部署好的对象都可以用JNDI来找到。</para>
<para>JMS客户端可以利用JMS ConnectionFactory对象来创建与服务器的连接。ConnectionFactory中有关于服务器地址的
信息以及各种参数。通常使用这些参数的默认值即可。</para>
<para>这里我们将要在服务器端部署一个JMS队列和一个JMS ConnectionFactory (连接工厂)。当然完全可以部署多个JMS对象。
下面给出了具体的配置内容:</para>
<programlisting>
<configuration xmlns="urn:hornetq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hornetq ../schemas/hornetq-jms.xsd ">
<connection-factory name="ConnectionFactory">
<connectors>
<connector-ref connector-name="netty"/>
</connectors>
<entries>
<entry name="ConnectionFactory"/>
</entries>
</connection-factory>
<queue name="OrderQueue">
<entry name="queues/OrderQueue"/>
</queue>
</configuration>
</programlisting>
<para>在本文件中我们部署了一个名为 <literal>ConnectionFactory</literal> 的一个连接工厂,并且将其绑定到
JNDI中。如果需要可以将一个连接工厂绑定为多个名称。只需要将绑定的名字加入到 <literal>entry</literal>
中即可。</para>
<note>
<para>在JMS ConnectionFactory的配置中引用了一个名为 <literal>netty</literal>的<literal>connector</literal>。
它实际上指向的是HornetQ核心中部署的一个连接器(connector)。它的配置在HornetQ的核心配置文件
<literal>hornetq-configuration.xml</literal> 中。它定义了采用何种传输与服务器连接。</para>
</note>
</section>
<section id="using-jms.configure.factory.types">
<title>连接工厂的类型</title>
<para>在JMS API文档中有几种不同类型的连接工厂供用户使用。HornetQ为用户提供了配置连接工厂类型的参数。用户可以通过
配置连接工厂的”signature"属性和"xa"参数来得到想要的类型。“singature"属性是字符串类型,它有三个可选值:
<emphasis>generic</emphasis>、<emphasis>queue</emphasis>和<emphasis>topic</emphasis>;
<literal>xa</literal>是一个布尔型参数。下表给出了不同类型连接工厂对应的配置值:</para>
<table frame="topbot" id="using-jms.table.configure.factory.types">
<title>连接工厂类型的配置</title>
<tgroup cols="3">
<colspec colname="signature" colnum="1"/>
<colspec colname="xa" colnum="2"/>
<colspec colname="cftype" colnum="3"/>
<thead>
<row>
<entry>signature</entry>
<entry>xa</entry>
<entry>连接工厂的类型</entry>
</row>
</thead>
<tbody>
<row>
<entry>generic (默认)</entry>
<entry>false (默认)</entry>
<entry>javax.jms.ConnectionFactory</entry>
</row>
<row>
<entry>generic</entry>
<entry>true</entry>
<entry>javax.jms.XAConnectionFactory</entry>
</row>
<row>
<entry>queue</entry>
<entry>false</entry>
<entry>javax.jms.QueueConnectionFactory</entry>
</row>
<row>
<entry>queue</entry>
<entry>true</entry>
<entry>javax.jms.XAQueueConnectionFactory</entry>
</row>
<row>
<entry>topic</entry>
<entry>false</entry>
<entry>javax.jms.TopicConnectionFactory</entry>
</row>
<row>
<entry>topic</entry>
<entry>true</entry>
<entry>javax.jms.XATopicConnectionFactory</entry>
</row>
</tbody>
</tgroup>
</table>
<para>下面的例子配置了一个XAQueueConnectionFactory:</para>
<programlisting>
<configuration xmlns="urn:hornetq"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="urn:hornetq ../schemas/hornetq-jms.xsd ">
<connection-factory name="ConnectionFactory" signature="queue">
<xa>true</xa>
<connectors>
<connector-ref connector-name="netty"/>
</connectors>
<entries>
<entry name="ConnectionFactory"/>
</entries>
</connection-factory>
</configuration>
</programlisting>
</section>
<section>
<title>JNDI的配置</title>
<para>当客户端使用JNDI时需要定义一些JNDI的参数。这些参数主要用来确定JNDI服务的地址。这些参数通常保存在
一个名为 <literal>jndi.properties</literal> 的文件中。这个文件需要在客户端的classpath中。或者你
可以在创建JNDI的InitialContext时将这些参数传进去。想了解全面的JNDI知识,可以参见 <ulink
url="http://java.sun.com/products/jndi/tutorial/TOC.html">Sun JNDI 教程</ulink>
。</para>
<para>要与JBoss的JNDI Server进行通迅,需要指定以下的JNDI参数:</para>
<programlisting>
java.naming.factory.initial=org.jnp.interfaces.NamingContextFactory
java.naming.provider.url=jnp://myhost:1099
java.naming.factory.url.pkgs=org.jboss.naming:org.jnp.interfaces
</programlisting>
<para>其中的 <literal>myhost</literal> 是 JNDI server的主机名或IP地址。 1099是端口号,根据不同的配置,
端口号也可能不同。</para>
<para>在默认的单独方式(standalone)配置中,JNDI服务端口等参数定义在<literal>hornetq-beans.xml</literal>
文件中的 <literal>JNDIServer</literal> bean下,如:</para>
<programlisting>
<bean name="JNDIServer" class="org.jnp.server.Main">
<property name="namingInfo">
<inject bean="Naming"/>
</property>
<property name="port">1099</property>
<property name="bindAddress">localhost</property>
<property name="rmiPort">1098</property>
<property name="rmiBindAddress">localhost</property>
</bean>
</programlisting>
<note>
<para>如果你的JNDI服务器与客户端不在同一台机器上,一定不要忘记将bindAddress改成相应的地址,
千万不能用<literal>localhost</literal>!</para>
</note>
<note>
<para><emphasis>只有当HornetQ作为独立服务器运行时</emphasis>
才可以配置JNDIServer bean。当HornetQ运行于JBoss应用服务器中时,由于JBOSS服务器已经提供了
JNDI服务,所以就不需要再进行配置了。</para>
</note>
</section>
<section>
<title>程序代码</title>
<para>下面给出的例子中的代码:</para>
<para>首先我们创建一个JNDI的Initial Context:</para>
<programlisting>InitialContext ic = new InitialContext();</programlisting>
<para>下面我们查找 connection factory:</para>
<programlisting>ConnectionFactory cf = (ConnectionFactory)ic.lookup("/ConnectionFactory");</programlisting>
<para>然后查找 Queue:</para>
<programlisting>Queue orderQueue = (Queue)ic.lookup("/queues/OrderQueue");</programlisting>
<para>接下来用拿到的ConnectionFactory建立JMS连接:</para>
<programlisting>Connection connection = cf.createConnection();</programlisting>
<para>再创建一个非事务的、AUTO_ACKNOWLEDGE方式的JMS Session:</para>
<programlisting>Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);</programlisting>
<para>创建一个 MessageProducer 以向队列发送订单消息:</para>
<programlisting>MessageProducer producer = session.createProducer(orderQueue);</programlisting>
<para>创建一个 MessageConsumer 以从队列中接收订单消息:</para>
<programlisting>MessageConsumer consumer = session.createConsumer(orderQueue);</programlisting>
<para>要启动连接,以使消息能传递给接收者:</para>
<programlisting>connection.start();</programlisting>
<para>发送一个简单的TextMessage:</para>
<programlisting>TextMessage message = session.createTextMessage("This is an order");
producer.send(message);</programlisting>
<para>之后接收这个消息:</para>
<programlisting>TextMessage receivedMessage = (TextMessage)consumer.receive();
System.out.println("Got order: " + receivedMessage.getText());
</programlisting>
<para>看起来就是这么简单。 在HornetQ有发布包中有很多各种各样的JMS例子供用户参考。</para>
<warning>
<para>请注意,JMS的连接(connection)、会话(session)、生产者(producer)和消费者(consumer)
对象是可以<emphasis>重用</emphasis>的。</para>
<para>如果每发送或接收一个消息都要重新创建这些JMS对象,是不符合设计模式的要求的。这样做会造成应用程序
的性能很差。这方面的内容在<xref linkend="perf-tuning"/>中将会进一步的讨论。</para>
</warning>
</section>
<section>
<title>不使用JNDI而直接创建JMS的对象</title>
<para>尽管采用JNDI对 JMS 的各种<emphasis>管理对象(Administered
Objects)</emphasis> (即JMS Queue, Topic and ConnectionFactory)是很常用的方法,但在有些
情况时JNDI不可用,或者你不需要用JNDI时,如何还能正常使用JMS呢?</para>
<para>HornetQ允许你不通过JNDI也能使用JMS。HornetQ支持直接创建JMS的各种对象而无需JNDI的存在。</para>
<para>在<xref linkend="examples"/>中包括有这样的例子供读者参考。</para>
<para>下面我们就将上述那个简单的例子重写,以抛开对JNDI的依赖:</para>
<para>我们通过HornetQJMSClient类来方便地创建JMS的ConnectionFactory。注意这里要提供各种连接参数和定义
所用的传输方式。有关连接器(connector)的信息参见<xref linkend="configuring-transports"
/>。</para>
<programlisting>
TransportConfiguration transportConfiguration =
new TransportConfiguration(NettyConnectorFactory.class.getName());
ConnectionFactory cf = HornetQJMSClient.createConnectionFactory(transportConfiguration);
</programlisting>
<para>同样利用HornetQJMSClient类创建JMS Queue对象:</para>
<programlisting>Queue orderQueue = HornetQJMSClient.createQueue("OrderQueue");</programlisting>
<para>然后用连接工厂创建 JMS 连接:</para>
<programlisting>Connection connection = cf.createConnection();</programlisting>
<para>还有非事务的\AUTO_ACKNOWLEDGE方式的 JMS 会话(session):</para>
<programlisting>Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);</programlisting>
<para>以及用于发送消息的MessageProducer:</para>
<programlisting>MessageProducer producer = session.createProducer(orderQueue);</programlisting>
<para>和接收消息的 MessageConsumer:</para>
<programlisting>MessageConsumer consumer = session.createConsumer(orderQueue);</programlisting>
<para>启动连接:</para>
<programlisting>connection.start();</programlisting>
<para>创建一个简单的 TextMessage 并将其发送到队列:</para>
<programlisting>TextMessage message = session.createTextMessage("This is an order");
producer.send(message);</programlisting>
<para>接收消息:</para>
<programlisting>TextMessage receivedMessage = (TextMessage)consumer.receive();
System.out.println("Got order: " + receivedMessage.getText());
</programlisting>
</section>
<section id="using-jms.clientid">
<title>Client ID的设置</title>
<para>在建立持久的订阅(subscription)时,JMS客户需要有一个客户ID (client id)。我们可以通过配置
connection factory来定义它。(其中的 <literal>client-id</literal>项)。这样所有通过这个
connection factory来创建的连接都具有这个客户ID。</para>
</section>
<section id="using-jms.dupsokbatchsize">
<title>设置DUPS_OK的Batch Size </title>
<para>如果JMS的通知模式为<literal>DUPS_OK</literal>,我们可以配置接收者(consumer)以使得它以批为单位
发送通知,而不是一个一个地发通知。这样做可以节省很多带宽,效率高。配置的方法是设置connection factory下
的<literal>dups-ok-batch-size</literal>项。单位是字节(byte)。默认值是1024 * 1024 bytes = 1 MiB。</para>
</section>
<section id="using-jms.txbatchsize">
<title>设置事务(Transaction)的Batch Size</title>
<para>当在一个事务内接收消息时,可能通过配置使接收者采用批量的方式发送通知,而不是一个一个的发送。这样也可以节省带宽。
配置方法是设置connection factory下的<literal
>transaction-batch-size</literal>项。 单位是字节(byte)。默认值是1024 *
1024。</para>
</section>
</chapter>