BlazeDS是来自Adobe公司
个开源项目
它可以让您
Flex应用
和数据服务进行连接
JMS(Java消息服务)是用Java编写
和服务相互通信
种思路方法
本文有助于您体会使用JMS
优点
以及在Flex应用
中如何使用BlazeDS通过JMS和Java服务进行通信
JMS概述
JMS是
个API(应用编程接口)标准
它允许您使用Java EE技术发送和接收消息
在Java社区很多软件Software商都提供了JMS
商业和开源实现
您可以根据自己
需求自由选择软件Software商
使用JMS
优点
使用JMS有几个优点
包括抽象、可靠
传递、异步消息
以及故障转移和高可用性
其中
抽象是非常重要
优点
JMS
消费者和生产者不必彼此相互依赖
无论是消费者端还是生产者端
代码都可以改变
只要JMS消息保持
致
两者间
连接就不会中断
JMS通常支持两种形式
消息——持久化
消息和非持久化
消息
非持久化
消息是在内存中进行处理
速度很快
但可能会因系统故障而丢失消息
持久化
消息则会把消息写入磁盘
即便系统发生故障也可找回消息
但相应
处理速度较慢
不管是否使用事务处理
JMS消息提供者都会确保所有持久化消息只被可靠传递
次
异步消息传递则提供了发送消息而无需等待响应
能力
其在Web应用中非常有用
当发送请求后应用
无需阻塞或挂起等待响应完成
这种消息传递类型对于需要长时间处理
应用(如酒店预订或构建动态文档等应用)来说是
个理想选择
只要配置正确
很多JMS
实现都提供了故障转移和高可用性能力
因此
如果某个正在运行JMS
服务器突然失效了
相同JMS实现
另
台服务器能够处理其负载
对于需要提供业务连续性
应用
来说
故障转移是
个理想选择
使用JMS和BlazeDS
优点
在使用BlazeDS连接JMS时
您
Flex应用
可以获得使用JMS
所有好处
而过去这些好处往往只有完全在J2EE平台上实现
Web应用才能享受
使用BlazeDS和JMS向服务发送消息
可以让您
Flex应用
开发更加独立于服务
服务
JMS实现甚至可以在更换软件Software供应商
而您
Flex应用
不用做任何修改
使用BlazeDS和JMS
另
个主要优点是
如果您在使用JMS的前正在使用超文本传输协议(HTTP)
那么您
Flex应用
代码基本不需要做什么改变
BlazeDS会为您处理
所有细节
理解JMS消息
JMS提供了两种类型
消息传递模型:点对点(P2P)和发布/订阅(pub/sub)模型
简单地说
发布/订阅是
对多
方式广播消息
而点对点则是
对
方式传递消息
在JMS中
消息客户端被称为JMS客户端;而消息系统则被称为面向消息
中间件(MOM——Messaging Orientated Middleware)
它也是JMS提供者
另外
产生消息
JMS客户端被称为是生产者
而接收消息
JMS客户端被称为消费者
P2P模型
P2P消息模型允许JMS客户端通过名为队列(Queue)
虚拟信道发送和接收消息
JMS队列可视为消息
容器
就像
个邮箱
JMS发送者推送
条消息到JMS消息队列就如同寄件人把
封信件放到了邮箱
而后
接收者从邮箱中获得消息并对的采取某种动作
就像邮箱中
信件
使用JMS队列从发件人发送
消息由单个接收者读取
尽管JMS对于P2P也支持类似于发布/订阅模型所使用
“推”
方式
传统
P2P模型却是
个基于“拉”或基于“轮询”
模型
是从队列中请求消息
而不是把消息推送到客户端
可能用到JMS消息和Flex客户端
个例子是:从Flex客户端
化
个进程
该进程需要服务层花费
些时间才能完成
比如创建
个按需定制
PDF文件
发布/订阅模式
在发布/订阅模式中
生产者可以通过名为主题(Topic)
虚拟信道发送
个信息给多个消费者
主题和印刷杂志类似:订阅者向出版商注册订阅
接收指定
杂志
出版商定期向区别
订阅者发送同
杂志
副本
发布/订阅模型总
来说是基于推
模型
消息被广播到消费者
而不是通过请求或拉
方式
使用JMS主题
您可以从服务层
次发送信息到多个Flex客户端
在Flex客户端使用JMS主题
个例子是用户上线
在这个例子中
当用户登录到该系统
服务层会经由JMS主题发送
个消息
告诉其他所有
Flex客户端该用户已上线
JMS消息
JMS提供
种方式来构建信息
使得主题和队列使用起来更加容易
当编写Java代码时
JMS API提供
思路方法能以
致
方式来正确构建消息
而使用BlazeDS
个好处是
您不必担心怎样创建正确
信息
BlazeDS会辅助实现这
点
JMS
实现
JMS
实现有若干
既有开源项目也有商业项目
本文使用
JMS实现是ActiveMQ
它是Apache软件Software基金会
个开放源码
项目
以 Apache许可证发布
ActiveMQ除了提供
个JMS
实现外
还提供使用其它编程语言发送消息
功能
包括PHP
其它
JMS实现还包括 IBM
WebSphere MQ
JBoss
HornetQ(注:前身是JBoss Messaging
开放
消息队列)
以及OpenJMS
配置BlazeDS使用JMS
若要BlazeDS使用JMS
其配置包括:建立正确
端点(endpo
)并在终点(destination)配置中设置JMS属性
本文
举例用到了
个简单
Flex界面、BlazeDS库
以及ActiveMQ
JMSAdapter
当消息到达MessageBroker Servlet后
BlazeDS使用JMSAdapter来完成Flex应用
和JMS
实现的间
消息转换
JMSAdapter在messages-config.xml文件中配置:
<adapters>
<adapter-definition id="actionscript" ="flex.messaging.services.messaging.adapters.ActionScriptAdapter" default="true" />
<adapter-definition id="jms" ="flex.messaging.services.messaging.adapters.JMSAdapter" />
</adapters>
端点
区别
定要记住
BlazeDS
Java库和MessageBroker servlet需要进行配置并运行于Web容器
由于MessageBroker必须处于运行状态
使用JMS适配器
终点(destination)仍要使用AMF(Adobe Action Message Format)通道
在本例中使用
端点是:
<?xml version="1.0" encoding="UTF-8"?>
<services-config>
<services>
<service- file-path="messaging-config.xml" />
</services>
<security/>
<channels>
<channel-definition id="my-amf" ="mx.messaging.channels.AMFChannel">
<endpo url="http://{server.name}:8161/blazeds/messagebroker/amf" ="flex.messaging.endpos.AMFEndpo"/>
</channel-definition>
<channel-definition id="my-streaming-amf" ="mx.messaging.channels.StreamingAMFChannel">
<endpo url="http://{server.name}:8161/blazeds/messagebroker/streamingamf" ="flex.messaging.endpos.StreamingAMFEndpo"/>
</channel-definition>
<channel-definition id="my-polling-amf" ="mx.messaging.channels.AMFChannel">
<endpo url="http://{server.name}:8161/blazeds/messagebroker/amfpolling" ="flex.messaging.endpos.AMFEndpo"/>
<properties>
<polling-enabled>true</polling-enabled>
<polling-erval-seconds>1</polling-erval-seconds>
</properties>
</channel-definition>
</channels>
</services-config>
JMS属性
JMS需要
些自己
配置
比如连接工厂用于建立JMS连接
JNDI(Java命名和目录接口)
队列名或主题名也是使用JNDI进行解析
JNDI是
个Java API
允许把资源
物理位置抽象成名字
JNDI
常见使用方法是数据库命名
应用
使用此JNDI名获得数据库连接
除了连接工厂、主题或队列
JNDI名
JMS变量还允许您定义用户证书、消息类型、终点类型
等等
下面展示
是在本例中使用
JMS属性:
<destination id="my-jms-destination">
<properties>
<jms>
<destination-type>Topic</destination-type>
<message-type>javax.jms.TextMessage</message-type>
<connection-factory>ConnectionFactory</connection-factory>
<destination-jndi-name>dynamicTopics/MyTopic</destination-jndi-name>
<delivery-mode>NON_PERSISTENT</delivery-mode>
<message-priority>DEFAULT_PRIORITY</message-priority>
<acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
<initial-context-environment>
<property>
<name>Context.INITIAL_CONTEXT_FACTORY</name>
<value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
</property>
<property>
<name>Context.PROVIDER_URL</name>
<value>tcp://localhost:61616</value>
</property>
</initial-context-environment>
</jms>
</properties>
<channels>
<channel ref="my-amf" />
</channels>
<adapter ref="jms" />
</destination>
您可以查看BlazeDS文档以获得进
步
属性解释
从BlazeDS发送消息
要尝试本文
例子
请在创建您
测试Flex项目的前
按照下面
步骤行事
下载ActiveMQ 2进制发布包并解压
您可以启动ActiveMQ并完成ActiveMQ文档中
验证步骤
但在继续下
步的前请先关闭ActiveMQ
下载BlazeDS 2进制发布包
首先要下载BlazeDS 2进制发布包:
将BlazeDS发布包blazeds.war解压到ActiveMQ
webapps目录下
这么做可保证您运行
例子中只使用了ActiveMQ
在Jetty服务器中建立Web应用上下文
Jetty是
个Web容器
ActiveMQ 2进制发布包自带了Jetty
如要增加上下文
需修改jetty.xml文件
它位于ActiveMQ目录下
conf目录:
<webAppContext contextPath="/blazeds" resourceBase="${activemq.base}/webapps/blazeds" logUrlOnStart="true"/>
启动ActiveMQ
在浏览器地址栏输入http://localhost:8161/blazeds
应该可以看到BlazeDS
目录列表
这
步是验证你已经把BlazeDS WAR放在正确
位置
并配置正确
创建
个Flex项目
它包含两个配置文件
配置文件位于src/WEB-INF/flex目录
分别为:messaging-config.xml和services-config.xml
使用文章前述
例子来设置JMS
终点
并添加JMSAdapter
把Flex应用项目中
services-config.xml和messaging-config.xml文件复制到ActiveMQ
webapps/blazeds/WEB-INF/flex目录
发送消息(举例)
为测试从Flex发送
消息
本文使用了ActiveMQ自带
命令行例子
为从Flex应用
中发送
条消息到命令行客户端
您可以在Flex代码中建立
个生产者(producer)
然后用区别
参数运行命令行例子
并查看结果
您需要在Flex应用
中创建
个生产者来发送消息
作为
个终点
这个新生产者将使用配置在messaging-config.xml文件中
my-jms-destination
下面这个例子是
个非常简单
带有消息生产者(producer)
Flex表单:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" initialize="consumer.subscribe">
<mx:Script>
<![CDATA[
import mx.messaging.messages.IMessage;
import mx.messaging.messages.AsyncMessage;
import mx.messaging.events.MessageEvent;
private function sendMessage(value : String) : void
{
var message : IMessage = AsyncMessage;
message.body = value;
producer.send(message);
}
]]>
</mx:Script>
<mx:Producer id="producer"
channelConnect="trace('producer connected')"
destination="my-jms-destination"
fault="trace(event.faultDetail)"
/>
<mx:VBox>
<mx:Form>
<mx:FormItem label="Message:">
<mx:TextInput id="textInput" />
</mx:FormItem>
</mx:Form>
<mx:Button id="sendButton" label="Send Message" click="sendMessage(textInput.text)" />
</mx:VBox>
</mx:Application>
界面上有
个按钮sendButton
点击此按钮会
sendMessage
sendMessage
先创建
个 AsyncMessage
其是IMessage接口
个实现
然后使用producer
send
思路方法发送此消息
赋值给fault属性
trace
思路方法会把给定
信息打印到控制台
从而帮助您调试任何可能发生
问题
验证消息:运行Flex应用
;切换到命令行
运行ActiveMQ自带
消费者(consumer)例子
脚本如下:
$ ant consumer -Dtopic=true -Dsubject=MyTopic -Dmax=2
如果你输入框中输入消息
然后点击发送消息按钮
该消息会出现在消费者端
命令行上
在收到两条消息后
消费者端
命令行例子会自动退出
接收消息(举例)
要接收JMS消息
您需要构建
个使用JMS终点
消费者(consumer)
在例子中为简单起见
消费者重用了生产者用来发送消息
终点
创建消费者
代码大致如下:
<mx:Consumer id="consumer" channelConnect="trace('consumer connected')"
channelFault="trace(event.faultDetail)"
fault="trace(event.faultDetail)"
destination="my-jms-destination"
message="messageHandler(event)" />
...
<mx:TextArea id="results" width="100%" />
在<mx:Script>代码块中处理消息
大致如下:
private function messageHandler(event : MessageEvent) : void {
results.text event.message.body + "\n";
}
添加了代码后
启动Flex应用
当Flex应用启动后
在命令行运行生产者
例子
脚本如下:
ant producer -Dtopic=true -Dsubject=MyTopic -Dmax=5
消息将出现在Flex
文本框中
通过REST服务和PHP集成
Web服务是集成PHP Web应用和Java
思路方法的
REST Web服务在Java中作为Servlets以及在PHP中作为脚本实现都比较容易
REST概述
REST Web服务比SOAP Web服务更为简单
REST Web服务没有
个标准
消息格式(例如
单个
网页可以是
个REST Web服务)
URL地址就是服务端点(endpo
)
而返回
Web页面则是该服务
响应
编写REST Web服务
个优点是可被多种类型
客户端使用
REST Web服务举例如下
它作为
个Java Servlet
能被其它语言如PHP
而无需修改服务
Java Servlet举例
本例使用了
个简单
Java Servlet来作为REST Web服务
此Servlet处理PUT请求
从PUT请求
内容和各种参数中得到消息
例如:得到目标对象(JMS终点
名字)
此消息是否是
个主题
JMS连接使用
URL等
package com.example.servlets;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URLDecoder;
import javax.jms.Connection;
import javax.jms.DeliveryMode;
import javax.jms.Destination;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.TextMessage;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.util.IndentPrer;
public MessageService extends HttpServlet {
private final long serialVersionUID = -8129137144911681674L;
private Destination destination;
private String user = ActiveMQConnection.DEFAULT_USER;
private String password = ActiveMQConnection.DEFAULT_PASSWORD;
@Override
protected void doPut(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
Connection connection = null;
try {
boolean isTopic = "true".equals(request.getParameter("topic"));
boolean isPersistent = "true".equals(request.getParameter("persistent"));
String subject = request.getParameter("subject");
String url = URLDecoder.decode(request.getParameter("url"), "UTF-8");
String messageText = "";
BufferedReader buffer = BufferedReader( InputStreamReader(request.getInputStream));
messageText = buffer.readLine;
.out.prln("Using URL: <" + url + ">");
.out.prln("Using Subject: <" + subject + ">");
.out.prln("Sending Message Text: <" + messageText + ">");
ActiveMQConnectionFactory connectionFactory = ActiveMQConnectionFactory(user, password, url);
connection = connectionFactory.createConnection;
connection.start;
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
(isTopic) {
destination = session.createTopic(subject);
} {
destination = session.createQueue(subject);
}
// Create the producer.
MessageProducer producer = session.createProducer(destination);
(isPersistent) {
producer.DeliveryMode(DeliveryMode.PERSISTENT);
} {
producer.DeliveryMode(DeliveryMode.NON_PERSISTENT);
}
TextMessage message = session.createTextMessage(messageText);
producer.send(message);
.out.prln("Done.");
// Use the ActiveMQConnection erface to dump the connection
// stats.
ActiveMQConnection c = (ActiveMQConnection)connection;
c.getConnectionStats.dump( IndentPrer);
} catch (Exception e) {
.out.prln("Caught: " + e);
e.prStackTrace;
} finally {
try {
connection.close;
} catch (Throwable ignore) {
}
}
}
}
除了用ActiveMQ特定类
你还可以使用JNDI来写代码
编译后
Servlet类文件(比如前面
MessageService类)必须放在ActiveMQ
webapps目录下
要添加新目录
则需要在conf目录
jetty.xml配置文件中添加入口(和前面
步骤4相似)
PHP举例
PHP脚本使用CURL库(译者注:原文
CURL库
链接有错)
REST服务
将数据发送到REST服务
URL(由Java Servlet处理)
如果您
PHP版本没有包含CURL库
您可以使用Ajax来
REST Web服务
在Web应用
实际产品中
Ajax可能会提供更好
用户体验
PHP脚本包含
个表单来提交自身
当提交表单时
该脚本从文本框取得消息
并把消息作为数据发送到REST服务
URL
请看下面
例子:
<?php
(is($_POST['submit'])) {
$url = 'http://localhost:8161/messageservice/?url=tcp://localhost:61616&subject=MyTopic&topic=true';
function doPut($ch, $a_data) {
curl_opt($ch, CURLOPT_CUSTOMREQUEST, 'PUT');
curl_opt($ch, CURLOPT_HTTPHEADER, .gif' />('Content-Length: '.strlen($a_data)));
curl_opt($ch, CURLOPT_POSTFIELDS, $a_data);
curl_exec($ch);
}
$data = $_POST['message'];
$ch = curl_init;
curl_opt($ch, CURLOPT_URL, $url);
$response = doPut($ch, $data);
echo 'Message sent';
} {
?>
<html><head><title>Post a message</title></head>
<body>
<form action="<?php echo $_SERVER['PHP_SELF'];?>" method="post">
<p>
<label for="message">Message:
<input type="text" id="message" name="message" />
</label>
</p>
<input type="submit" value="submit" name="submit" id="submit" />
</form>
</body>
</html>
<?php
}
?>
在URL上传递
JMS服务器
topic、subject和URL信息都赋值给了$url
用BlazeDS发送/接收
举例
要想看BlazeDS发送/接收
例子
启动Flex应用
当应用
打开
在区别
浏览器上打开PHP脚本
给消息框添加
个值
然后单击提交
消息框
值将会出现在Flex应用
文本框中
小结
JMS是
个消息传递服务
支持主题和队列两种模式以及其它
很多特征
是可靠消息传递
不错选择
BlazeDS可以让您只需做少量工作即可完成从Flex客户端向JMS发送消息
PHP应用
可以以多种方式连接到JMS
使用REST Web服务是把PHP和Java应用
整合在
起
种方式——REST Web服务提供了
种相对简单
接口供客户端使用
使用Java实现REST服务
您可以利用现有
Java代码来发送JMS消息
本系列
第2部分主要讲通过JMS整合PHP和Flex应用
其它思路方法:PHP / Java桥和STOMP
译者注:STOMP
Streaming Text Orientated Message Protocol
是流文本定向消息协议
是
种为MOM(Message Oriented Middleware
面向消息
中间件)设计
简单文本协议