3.2 Use case one: the stock portfolio example
3.2 用例一: stock portfolio实例
As mentioned earlier in the chapter, the first use case revolves around a stock portfolio
use case for demonstrating publish/subscribe messaging. This example is simple
and utilizes a Publisher class for sending stock price messages to a topic, as well as a
Consumer class for registering a Listener class to consume messages from topics in
an asynchronous manner. These three classes embody the functionality of generating
ever-changing stock prices that are published to topics on which the consumer is
subscribed.
如本章前文所示,第一个用例以股票交易为例,阐述发布/订阅消息模型.这个简单的例子利用发布者类
发送股票价格消息给一个主题,同时消费者类通过注册一个监听类使用异步方式从主题接收消息.
这三个类实现了如下功能:通过订阅主题,消息消费者可获取发送到该主题的不断变化的股票价格.
In this example, stock prices are published to an arbitrary number of topics. The
number of topics is based on the number of arguments sent to the Publisher and the
Consumer on the command line. Each class will dynamically send and receive to/from
the topics (an example is provided next). Take a look at figures 3.2 and 3.3 to see at a
high level what the examples seek to achieve.
本例中,股票价格被发布到任意数量的主题中.主题的数量取决于命令行中传递给消息发布者和消息
消费者的主题数目参数.每个类都可以动态的发送消息到主题或者从主题接收消息(接下来将提供一个例子).
图3.2和图3.3 概述这两个例子是如何设计实现的.
For the sake of this demonstration, two topics will be used. The Publisher class
uses a single JMS MessageProducer to send 1,000 fictitious stock price messages in
blocks of 10, randomly distributed across the topics named in the command-line argument.
After it sends 1,000 messages, it shuts down. The Consumer class creates one JMS
MessageConsumer per topic and registers a JMS MessageListener for each topic.
Because this example demonstrates publish/subscribe, the Consumers must be online
to consume messages being sent by the Publisher, because durable consumers aren’t
used in the basic stock portfolio example. The next step is to actually run the example
so that you can see them in action.
本例中,我们使用了两个主题.消息发布者类使用单个JMS消息生产者发送1000个虚构的股票价格消息.
这些股票价格消息以10个为一组,随机分发到不同的主题,这些主题的名字是通过命令行参数传递进来的.
发送完1000个消息后,消息发布者被关闭.消息消费者类为每一个主题创建一个消息消费者(MessageConsumer)
并为每一个主题创建一个消息监听器(MessageListener).因为本例是阐述消息发布/订阅的,
并且stock portfolio实例中没有使用持久的消息消费者,所以消息消费者必须联机在线,以便消费发布者发送的消息.
下一步我们将真正运行这个实例,你将看到这个实例中的类是如何工作的.
3.2.1 Running the stock portfolio example
3.2.1 运行stock portfolio实例
There are three basic steps to running this example:
1 Start up ActiveMQ
2 Run the Consumer class
3 Run the Publisher class
可以通过以下三个基本步骤运行本实例:
1 启动ActiveMQ
2 运行Consumer类
3 运行Publisher类
These steps appear to be simple, and they are. The only item of note is that the Consumer
should be started before the Publisher, in order to receive all messages that are
published. This is because this example demonstrates pub/sub messaging and topics
won’t hold messages unless the consumer makes a durable subscription, and we’re
not using durable subscriptions here. So let’s get started with the stock portfolio
example.
这些步骤正如他们看起来的那样--简单.唯一需要注意的一点就是Consumer类需要在Publisher
类之前运行,以便接收所有已发布的消息.这是因为本实例阐述的是消息的发布/订阅,而主题不会
持久化消息,除非消息消费者进行了持久化订阅.本例中消息消费者没有使用持久化订阅.
下面让我开始运行stock portfolio这个例子.
The first task is to open a terminal or command line and execute ActiveMQ. This
only requires a single command as demonstrated in the following listing.
第一步需要打开一个终端或控制台窗口,然后启动ActiveMQ.这一步仅仅需要一个命令,如下所示:
./bin/activemq console (注:windows下,到ActiveMQ的bin目录中 执行activemq即可)
Listing 3.2 Start up ActiveMQ
[apache-activemq-5.4.1] $ ./bin/activemq console
INFO: Using default configuration
(you can configure options in one of these file:
/etc/default/activemq /Users/bsnyder/.activemqrc)
INFO: Invoke the following command to create a configuration file
./bin/activemq setup [/etc/default/activemq | /Users/bsnyder/.activemqrc]
INFO: Using java
'/System/Library/Frameworks/JavaVM.framework/Home/bin/java'
INFO: Starting in foreground, this is just for debugging purposes
(stop process by pressing CTRL+C)
Java Runtime: Apple Inc. 1.6.0_20
/System/Library/Frameworks/JavaVM.framework/Versions/1.6.0/Home
Heap sizes: current=258880k free=253105k max=258880k
JVM args: -Xms256M -Xmx256M
-Dorg.apache.activemq.UseDedicatedTaskRunner=true
-Djava.util.logging.config.file=logging.properties
-Dcom.sun.management.jmxremote
-Dactivemq.classpath=/Users/bsnyder/amq/apache-activemq-5.4.1/conf;
-Dactivemq.home=/Users/bsnyder/amq/apache-activemq-5.4.1
-Dactivemq.base=/Users/bsnyder/amq/apache-activemq-5.4.1
ACTIVEMQ_HOME: /Users/bsnyder/amq/apache-activemq-5.4.1
ACTIVEMQ_BASE: /Users/bsnyder/amq/apache-activemq-5.4.1
Loading message broker from: xbean:activemq.xml
...
INFO | Started [email protected]:8161
The next task is to open a second terminal or command line to execute the Consumer
class. The Consumer is executed using the maven-exec-plugin (http://mng.bz/bf7g)
by passing it some system properties as arguments using the exec.args property. An
example of running the Consumer is shown next.
下一步,打开第二个终端窗口或控制台窗口执行Consumer类.Consumer类使用Maven的maven-exec-plugin
插件,需要使用exec.args传递几个系统属性作为参数.下面是允许Consumer的命令例子:
Listing 3.3 Run the stock portfolio consumer
[amq-in-action-example-src] $ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Consumer -Dexec.args="CSCO ORCL"
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'exec'.
[INFO] org.apache.maven.plugins: checking for updates from central
[INFO] org.codehaus.mojo: checking for updates from central
[INFO] artifact org.codehaus.mojo:exec-maven-plugin: checking for updates from central
[INFO] snapshot org.codehaus.mojo:exec-maven-plugin:1.1.2-SNAPSHOT: checking for updates from public-snapshots
[INFO] snapshot org.codehaus.mojo:exec-maven-plugin:1.1.2-SNAPSHOT: checking for updates from central
Downloading: http://localhost:8081/nexus/content/groups/public/org/codehaus/mojo/exec-maven-plugin/1.1.2-SNAPSHOT/exec-maven-plugin-1.1.2-20091120.114446-3.pom
4K downloaded (exec-maven-plugin-1.1.2-20091120.114446-3.pom)
Downloading:
http://localhost:8081/nexus/content/groups/public/org/codehaus/mojo/mojo-parent/22/mojo-parent-22.pom
18K downloaded (mojo-parent-22.pom)
Downloading:
http://localhost:8081/nexus/content/groups/public-snapshots/org/codehaus/mojo/exec-maven-plugin/1.1.2-SNAPSHOT/exec-maven-plugin-1.1.2-20091120.114446-3.jar
36K downloaded (exec-maven-plugin-1.1.2-20091120.114446-3.jar)
[INFO] ------------------------------------------------------------------------
[INFO] Building ActiveMQ in Action Examples
[INFO] task-segment: [exec:java]
[INFO] ------------------------------------------------------------------------
[INFO] Preparing exec:java
[INFO] No goals needed for project - skipping
[WARNING] POM for 'woodstox:wstx-asl:pom:3.2.7:compile' is invalid.
Its dependencies (if any) will NOT be available to the current build.
Downloading:
http://localhost:8081/nexus/content/groups/public/org/apache/commons/
commons-exec/1.0.1/commons-exec-1.0.1.pom
7K downloaded (commons-exec-1.0.1.pom)
Downloading:
http://localhost:8081/nexus/content/groups/public/org/apache/commons/
commons-exec/1.0.1/commons-exec-1.0.1.jar
48K downloaded (commons-exec-1.0.1.jar)
[INFO] [exec:java {execution: default-cli}]
You can see in listing 3.3 that Maven downloads the necessary artifacts it needs to run
the examples. Once this has completed, the Publisher can start up and begin publishing
stock prices to the two topics named on the command line, CSCO and ORCL.
These two topic names were picked at random and can be replaced with any Strings
you desire. The important part is that the same arguments be used for both the
Consumer and the Publisher (the Publisher is shown next) via the system property
exec.args.
在清单3.3中,你会看到Maven下载必要的依赖包以便运行这个例子.一旦消费者类运行完成,
即可启动Publisher类开始发布股票价格消息到以命令行传递的参数命名的主题,传递的参数为:
CSCO 和 ORCL.这两个主题名称是随便选取的,你可以用其他任意字符串代替.重要的一点是
运行Consumer类 和 Publisher类都要使用的通过系统属性exec.args传递的变量
(运行Publisher的命令将在下文给出).
BUILD ERRORS WHEN RUNNING THE CONSUMER
运行CONSUMER时提示编译错误
If you receive a BUILD ERROR while attempting to run the consumer class,
you’ll need to compile the source code before running it.
To compile all the source, run the following command:
$ mvn clean install
This command will compile and package the source so that it’s ready to be
run. After this command completes, you can go back and run the command
consumer using the command shown earlier.
如果在运行consumer时提示BUILD ERROR(编译错误),你需要重新编译源代码.
使用下面的命令重新编译源代码:$ mvn clean install
这个命令将编译打包源代码以便执行.执行完这个命令后,可以使用前文所述
的运行consumer类的命令.
Note that the output just seems to stop as the Consumer hangs there. This behavior is
correct because it’s waiting for messages to arrive in the topics to be consumed. When
the Publisher begins sending messages, the Consumer will begin to consume them.
注意,运行consumer类的命令后的输出看起来像是Consumer类挂起了.这是正常的,因为Consumer在
等待主题中的消息.一旦消息发布者发送消息,Consumer就将开始处理消息.
Why are all the artifacts being downloaded from the localhost in the output shown?
为什么前面命令输出中显示Maven依赖库都从本地(localhost)下载?
As long as Maven was set up correctly in section 3.1, then Maven will download all
the necessary artifacts it needs to run the examples. You can see it downloading artifacts
in the first portion of the output. Note that all the artifacts are being downloaded
from the localhost instead of from a remote Maven repository. This is because the
example is being run with Maven, which is configured to use a Maven repository manager
named Nexus on the local computer. Nexus provides many benefits, one of
which is a proxy to remote Maven repositories with a local cache of all downloaded
artifacts. After Maven downloads artifacts the first time via Nexus, they’re held in a
local cache. During successive builds, Nexus provides the artifacts from the local
cache instead of checking a remote repository, and this speeds up the build time
quite dramatically. For more information about Nexus and to discover more about its
features, see: http://nexus.sonatype.org/.
如果3.1节中Maven设置正确,Maven在运行示例代码时会下载所有的依赖包.在输出中也可看到Maven
下载依赖包的相关信息.注意到所有的依赖包都是从本地下载而不是从远程的maven依赖包仓库下载.
这是因为实例是使用Maven运行的,Maven使用了本机中一个名称为Nexus的依赖库管理器.使用Nexus
有很多好处,其中之一是:Nexus使用所有已下载的依赖库的本地缓存作为到远程Maven依赖库的代理.
在成功构建后,Nexus本地依赖库缓存取代检查远程代理库,这样很可观的加快了构建速度.
更多关于Nexus的信息,请访问:http://nexus.sonatype.org/.
The next task is to open a third terminal or command line to execute the Publisher
class. Note that the same arguments are used in exec.args that were used for executing
the Consumer class earlier, because the maven-exec-plugin is used to execute the
Publisher class as well. An example of running Publisher is shown here.
接下来需要打开第三个终端或命令行窗口运行Publisher类.注意和前面运行Consumer类一样,
需要使用exec.args参数,因为maven也是使用 maven-exec-plugin插件运行Publisher类的.
下面是运行Publisher类的命令实例:
[amq-in-action-example-src] $ mvn exec:java -Dexec.mainClass=org.apache.activemq.book.ch3.portfolio.Publisher -Dexec.args="CSCO ORCL"
[INFO] Scanning for projects...
[INFO] Searching repository for plugin with prefix: 'exec'.
[INFO] -------------------------------------------------------------------
-----
[INFO] Building ActiveMQ in Action Examples
[INFO] task-segment: [exec:java]
[INFO] -------------------------------------------------------------------
-----
[INFO] Preparing exec:java
[INFO] No goals needed for project - skipping
[WARNING] POM for 'woodstox:wstx-asl:pom:3.2.7:compile' is invalid.
Its dependencies (if any) will NOT be available to the current build.
[INFO] [exec:java {execution: default-cli}]
Sending: {offer=62.6861410176471, price=62.62351750014696, up=true,
stock=ORCL} on destination: topic://STOCKS.ORCL
Sending: {offer=55.508573596887715, price=55.45312047641131, up=true,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=62.527946513790205, price=62.46548103275745, up=false,
stock=ORCL} on destination: topic://STOCKS.ORCL
Sending: {offer=55.78778713074073, price=55.73205507566507, up=true,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=55.593918646251986, price=55.53838026598601, up=false,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=55.83360390719586, price=55.777826081114746, up=true,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=55.99233608275527, price=55.93639968307221, up=true,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=62.006501598331475, price=61.94455704129019, up=false,
stock=ORCL} on destination: topic://STOCKS.ORCL
Sending: {offer=55.53698948617822, price=55.48150797820003, up=false,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=61.43866500377897, price=61.377287716062916, up=false,
stock=ORCL} on destination: topic://STOCKS.ORCL
Published '10' of '10' price messages
Sending: {offer=55.466945358331216, price=55.41153382450671, up=false,
stock=CSCO} on destination: topic://STOCKS.CSCO
Sending: {offer=61.27694222131968, price=61.215726494824864, up=false,
stock=ORCL} on destination: topic://STOCKS.ORCL
...
Published '10' of '30' price messages
When executing the Publisher class, Maven already has all the necessary dependencies
from the earlier execution of the Consumer class, so nothing should be downloaded.
The lower portion of the output shows the stock price messages being sent to
the two topics in blocks of 10. The example output is truncated for space, so just know
that the Publisher will run until it sends a total of 1,000 messages.
After running the Publisher, if you switch back to the second terminal where the
Consumer was started, you should see that it’s now consuming messages from the topics:
...
因为之前运行Consumer类时,Maven已经下载了所有的依赖包,所以执行Publisher时就不需要再下载了.
下面的输出显示了以10个为一组发送到两个主题的股票价格消息.因为篇幅关系,这个例子的输出被截断了,
这里仅需要了解的是的Publisher会一直类运行到发送了1000个消息为止.
执行完Publisher类后,如果你回头看看第二个终端或命令行窗口,你会看到Consumer类启动了,
你应该会看到类型下面的Consumer类处理主题中消息的输出信息:
[INFO] [exec:java {execution: default-cli}]
ORCL 62.62 62.69 up
CSCO 55.45 55.51 up
ORCL 62.47 62.53 down
CSCO 55.73 55.79 up
CSCO 55.94 55.99 up
CSCO 55.41 55.47 down
ORCL 61.22 61.28 down
ORCL 61.42 61.48 up
...
The preceding output comes from the Listener class that’s registered by the
Consumer on the two topics named ORCL and CSCO. This output shows the consumption
of the stock price messages from the same two topics to which the Publisher is
sending messages. Once the Publisher reaches 1,000 messages sent, it’ll shut down.
But the Consumer will continue to run and just hang there waiting for more messages
to arrive on those two topics. You can press CTRL-C in the second terminal to shut
down the Consumer at this point.
Now that you’ve seen how ActiveMQ works well in a pub/sub messaging scenario,
the following section will explore how it works in point-to-point messaging.
前面提到的输出来自Listener类,是Consumer类在主题名称为ORCL和CSCO上注册的监听器.
这些输出显示了消息消费者处理股票价格消息时的输出信息.一旦Publisher发布了1000个消息,
Publisher将自动关闭.而Consumer类将一直运行并挂起以等待更多的消息发布到那个那个主题.
在第二个终端或者命令行窗口按CTRL+C键可停止Consumer类.
现在你看到ActiveMQ可在消息发布/订阅这一场景中正常运行,
下一节将介绍在ActiveMQ中如何使用点对点消息.