BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

PHP开发者的BlazeDS和JMS指南,第二部分

| 作者 Nathan A. Good 关注 0 他的粉丝 ,译者 李强 关注 0 他的粉丝 发布于 2010年7月20日. 估计阅读时间: 28 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

本系列的第1部分主要描述了通过使用BlazeDS将Java服务、PHP以及Adobe Flash Builder的用户界面与JMS相集成。文章展示了只需区区几处配置改动,您就可以使用BlazeDS通过MessageBroker servlet把Flash Builder所建立的用户界面与JMS消息队列相连接。文章还涉及了整合PHP和Java及JMS的另一种方法:使用REST(Representational State Transfer)Web服务把消息发送到JMS队列。

本系列的第2部分则讨论整合PHP和Java的另外两个方法。第一种方法——桥接(bridging),让您可以在PHP中使用Java对象,从PHP中通过Java曝露的方法把消息发送到JMS消息队列。第二种集成的方法是利用STOMP(Streaming Text Orientated Messaging Protocol)协议与ActiveMQ(JMS的实现之一)进行通信。当ActiveMQ接收到消息,ActiveMQ会把消息放入JMS队列,Adobe Flex应用程序则从队列中接收消息。

通过桥接与PHP集成

本系列的第1部分说明了如何使用BlazeDS和JMS在Java与Flex之间传递消息。其他技术也能够完成PHP与Java的集成,让您间接地把消息放入JMS队列。Java/PHP桥就是这些技术的一种,它允许您使用已配置好的、运行于ActiveMQ上的同一个JMS队列。使用Java/PHP桥,您可以写PHP脚本来调用Java类中的方法。此方法会把JMS消息放入消息队列。Flex用户界面则使用BlazeDS来连接JMS队列,从队列中接收消息,正如它在REST Web服务中做的那样。

PHP/Java桥的发布包里面有一个JavaBridge.war文件。解压此WAR包,把解压后的文件放进ActiveMQ的webapps/JavaBridge目录下,并在conf/jetty.xml配置文件中添加此应用程序。此JavaBridge Web应用程序包含了从PHP脚本接收消息的Servlets,处理消息,然后运行Java类。

Java示

由于PHP脚本和Java类之间的通信可能会很慢,因此需创建一个Java类,在一个方法中来处理。这种做法就是著名的Fa?ade设计模式——在所暴露的简单方法背后隐藏了复杂的功能。

下面是Java实例的Fa?ade类,类名为MessageHelper,包名为com.example.bridge。

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 org.apache.activemq.ActiveMQConnection;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.util.IndentPrinter;

public class MessageHelper {
    
    private Destination destination;
    private String user = ActiveMQConnection.DEFAULT_USER;
    private String password = ActiveMQConnection.DEFAULT_PASSWORD;
    
    public void sendMessage(String url, String subject, String messageText) {
        
        Connection connection = null;            
  
        try {
            boolean isTopic = true;
            boolean isPersistent = false;
            
            System.out.println("Using URL:  <" + url + ">");
            System.out.println("Using Subject:  <" + subject + ">");
            System.out.println("Sending Message Text:  <" + messageText + ">");
            
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory(user, password, url);
            connection = connectionFactory.createConnection();
            connection.start();

            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);

            if (isTopic) {
                destination = session.createTopic(subject);
            } else {
                destination = session.createQueue(subject);
            }

            // Create the producer.
            MessageProducer producer = session.createProducer(destination);
            if (isPersistent) {
                producer.setDeliveryMode(DeliveryMode.PERSISTENT);
            } else {
                producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
            }

            TextMessage message = session.createTextMessage(messageText);
            
            producer.send(message);

            System.out.println("Done.");

            // Use the ActiveMQConnection interface to dump the connection
            // stats.
            ActiveMQConnection c = (ActiveMQConnection)connection;
            c.getConnectionStats().dump(new IndentPrinter());

        } catch (Exception e) {
            System.out.println("Caught: " + e);
            e.printStackTrace();
        } finally {
            try {
                connection.close();
            } catch (Throwable ignore) {
            }
        }
        
    }
}

此Java类只有一个方法——sendMessage(),该方法有三个参数:JMS的目的地URL地址、所使用的Topic名及消息内容。

把以上写好的类放入一个JAR文件中,然后把此JAR文件放入JavaBridge Web应用程序的WEB-INF/lib文件夹下面。之所以要把该JAR文件放在JavaBridge Web应用程序的类路径(lib目录)下,是因为JavaBridge Web应用程序实际负责实例化JAR包中的Java类。MessageHelper类的这一方法与Web Server是在同一个虚拟机中执行的——在本例中,就是执行Jetty实例和ActiveMQ的那个虚拟机。

PHP示

下面的PHP脚本使用了Java.inc文件,它是PHP/Java桥自带的文件。然后PHP脚本创建Java对象并调用其方法:

<?php
require_once ("java/Java.inc");

java_autoload('messageHelper.jar');

$helper = java('com.example.bridge.MessageHelper');

$url = 'tcp://localhost:61616';
$subject = '/topic/MyTopic/';
$message = 'Hello, world!!';

$helper->sendMessage($url, $subject, $message);

echo "Message sent";
?>

在PHP脚本中,java_autoload()方法加载JAR文件。java()方法使用Java对象全名来载入Java对象。 Java对象加载后,PHP脚本通过为Java/PHP桥配置的端点调用SendMessage()方法。

为Java/PHP桥配置端点需要在java目录的java.inc文件中进行配置。通过改变端点,您可以在不同的Web服务器中执行PHP脚本。如果您已经有一个配置了PHP的Web服务器,您可以把该校本放在一个Web Document目录下并执行它。要让您的PHP脚本运行正常,您还必须将发布包中的java目录放在与PHP脚本相同的目录下(查看PHP/Java Bridge以了解详情)。

在上例中,桥接只建于PHP和Java之间,使得PHP代码可以调用Java对象。而例子中不需要从Java到PHP的桥接。

实战例子:用BlazeDS发送和接收消息

要看实战的例子,先要启动Flex应用程序。在另一个浏览器中,运行连接到PHP/Java桥的PHP脚本。您会在Flex用户界面上看到“Hello, world”消息。

使用STOMP协议与PHP集成

STOMP是另一种技术,您可以用它来弥合PHP和ActiveMQ JMS之间的鸿沟。在这个例子中,ActiveMQ使用STOMP协议从PHP脚本接收消息,然后把消息路由到JMS Topic,Flex应用程序接收消息并在界面上显示消息。

STOMP概述

STOMP是一个协议,它可以使用多种不同的客户端发送消息。尽管ActiveMQ已内建了对STOMP的支持,你还是可以使用类似 StompConnect这样的STOMP类库与JMS通信。

下面的例子说明了如何在ActiveMQ中使用内建的STOMP协议支持。与STOMP一起工作的消息代理(message broker)需要在conf/activemq.xml配置文件中打开:

<transportConnectors>
    <transportConnector name="openwire" uri="tcp://0.0.0.0:61616" />
    <transportConnector name="stomp" uri="stomp://0.0.0.0:61613" />
</transportConnectors>

添加了STOMP代理的配置后,重启ActiveMQ。当ActiveMQ启动时,它会把“STOMP连接器已经启动”的消息写入日志文件:

INFO | Connector openwire Started

INFO | Listening for connections at: stomp://silverstreak.local:61613

从PHP脚本经由STOMP发送的消息被ActiveMQ接收,接收到的消息被放入JMS主题或JMS队列,由相同的消费者接收。经由STOMP发送消息还使得Flex界面无需修改代码或配置就能接收同样的消息。在这种方式下,利用STOMP将消息发送到ActiveMQ,对应用程序的其它部分来说,是相当透明的。

PHP代码

您可以使用下面的PHP脚本利用STOMP来把消息发送到与前面例子相同的MyTopic JMS主题(本文一直在使用)。代码使用的Stomp.php类来自于FuseSource。Stomp.php类提供了一个从PHP发送消息的简单方法。要使用这个类,你必须下载该发布包并将其文件复制到示例脚本相同的目录中:

try {
    // include a library
    require_once("Stomp.php");
    // make a connection
    $con = new Stomp("tcp://localhost:61613");
    // connect
    $con->connect();
    // send a message to the queue
    $con->send("/topic/MyTopic", "Hello, world");
    echo "Sent message with body 'test'\n";
    $con->disconnect();

} catch (Exception $e) {
    var_dump($e->getMessage());
}

?>

此代码连接到ActiveMQ服务器的61613端口,并发送一个很短的文本消息(“Hello, world”)。然后,它从STOMP连接器断开连接。

ActiveMQ把消息路由到相应的JMS主题。Flex应用程序能够接收到这个消息,因为Flex订阅了此主题。

实战例子

要看到实战的例子,请再次打开Flex应用程序。另外打开一个浏览器,运行您刚才创建的PHP脚本,“Hello, world”消息将会显示到Flex应用程序的界面上。

使用JNDI

为简单起见,本系列的所有例子均直接使用了队列名和主题名(比如:/topic/MyTopic)。然而,在使用JMS的生产环境中,JMS主题名或队列名一般是使用JNDI(Java Naming and Directory Interface)进行配置的,而不会采用硬编码的方式。

JNDI是一个Java标准,它允许你抽象JMS队列名、主题名、甚至是主机名的实际名称。JNDI有点像域名系统(DNS),使您能够使用主机名而不是IP地址来识别主机。使用JNDI可以让您随时调整服务器上的JMS主题或队列的位置和实现,而不必修改您的代码。

除了抽象主题名和队列名之外,JNDI还提供了另一个有用的功能:能够使用Java EE的JMS对象接口来编写代码。使用接口而不是具体的实现,可以使您的Java代码不依赖于特定的供应商。

看看下面的Java代码例子:

javax.naming.Context ctx = new javax.naming.InitialContext();
javax.jms.ConnectionFactory factory = (javax.jms.ConnectionFactory)ctx.lookup("ConnectionFactory");
javax.jms.Connection conn = factory.createConnection();
javax.jms.Destination destination = (javax.jms.Destination)jndiContext.lookup("MyTopic");
javax.jms.Session session = conn.createSession(false, Session.AUTO_ACKNOWLEDGE);
// send the message...
javax.jms.MessageProducer producer = session.createProducer(destination);
javax.jms.TextMessage message = session.createTextMessage();
message.setText("Hello, world!");
producer.send(message);
conn.close();

以上Java代码没有使用ActiveMQConnectionFactory的具体实现,而是使用了InitialContext对象来“查找(lookup)”指定的连接器工厂实现。然后,代码使用InitialContext对象上相同的查询方法(lookup)来获取JMS主题的引用。这样,本例的代码中就不会包含对ActiveMQ的直接引用。

使用JNDI的缺点——除了要学习如何在Java代码中使用JNDI之外,还需要提供一些额外的配置,以获取初始的JDNI上下文。对于前者,ActiveMQ提供了一个简单的JNDI InitialContextFactory以方便使用。(请参阅ActiveMQ支持页面以获取更多信息)。至于需要额外的配置,无需修改代码即可在不同厂商提供的应用服务器上部署和执行这一优点可以抵消掉这一瑕疵。如果您的环境有可能切换到不同的供应商,您可以考虑使用JNDI来帮助抽象细节。

网络考虑因素

此集成解决方案提供了PHP和Flex客户端通过Java/JMS技术在网络上进行通信的功能。因为您可以把不同的内容部署到不同的Web服务器,在评估集成技术时还需考虑网络访问的一些因素。

如果在PHP、Flex和Java/JMS服务之间存在防火墙,防火墙开放的端口可能会影响您选择用于集成的方法。由于BlazeDS是通过MessageBroker Servlet将Flex界面连接到JMS的,因此服务器只需标准的80端口(或Web应用服务器绑定的任何端口)能够访问即可。

ActiveMQ使用非标准的端口来监听消息,但如果ActiveMQ与BlazeDS作为Web应用服务器运行在同一台机器上时,端口(“openwire”连接器的61616端口)问题只受限于本地接口,而不是公开暴露出来。

您的Web应用服务器可以在80端口暴露REST Web服务,使得跨网络和服务器进行集成相对简单。基于这个原因,许多人会选择Web Services。

PHP/Java桥使用了部署在JavaBridge Web应用程序中的一个Servlet,通过XML流机制暴露Java类给PHP。因为这个桥接是作为一个Servlet安装的,所以也可以从Web服务器的标准端口访问。

但是,使用STOMP协议需要在PHP脚本和(ActiveMQ的)STOMP消息代理之间开放一个非标准的端口。上面例子中开放的端口是61613端口。如果限制了从网络访问这个端口,那么STOMP或许并不是集成PHP脚本、Flex界面及Java/JMS服务一个好的解决方案。

小结

JMS是一个消息传递服务,支持主题和队列,其很多特性使其成为稳定消息传递的一个很好选择。BlazeDS使得您无需付出很多工作量即可让Flex应用程序从Flex客户端发送消息到JMS。

本系列的第1部分介绍了JMS的消息传递,并示范了如何配置BlazeDS与JMS队列进行通信。文章还介绍了使用Java实现的REST Web服务来集成PHP应用程序和JMS的方法,让您的Flex用户界面可以从PHP脚本收到消息。

本文则介绍了从PHP发送消息到JMS主题或队列的另外两种方法。PHP/Java Bridge是一个开源项目,提供了一个从PHP脚本调用Java类的框架。通过编写单个的Java类——即所谓的Fa?ade模式,您的PHP脚本就能够将消息发送到JMS,并且从Flex界面接收到此消息。STOMP是一个协议,它有多种语言的支持库。利用STOMP,您可以在Flex应用中与JMS消息代理通信并接收消息。

总之,这些不同的技术提供了整合PHP、Flex或Java 应用的不同选项。整合现有的应用程序而不是重新开发它们,可以节省您的时间和精力,而且您也能自由选择适合您需要的最佳实现。

关于作者

Nathan A. Good居住在Minnesota的Twin城地区。其专长是软件开发、软件架构和系统管理。平时不编写软件时,他喜欢组装PC和服务器、阅读和使用新技术,并鼓励他的朋友转用开源软件。他是许多书籍和文章的作者或合著者,其中包括《Professional Red Hat Enterprise Linux 3》、《Regular Expression Recipes: A Problem-Solution Approach》以及《Foundations of PEAR: Rapid PHP Development》。

查看英文原文:BlazeDS and JMS for PHP Developers, Part 2


译者简介:李强,计算机硕士,毕业于电子科技大学,目前在四川长虹电器股份有限公司技术中心从事研发工作,主要研究领域是SOA、ESB、Web服务以及分布式应用等。

感谢宋玮对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

有可运行的小例子么- - by W g

有可运行的小例子么- - 这个没看懂 配置了老运行老报错

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

1 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT