虽然activemq+jencks的jms轻量级解决方案已经很好地在psa中work了,尤其spring的JmsTemplate使得代码更简单,但是还是存在问题。

问题来自暑期做psa的时候,linke突然提出要求,需要MDP返回些处理信息,比如处理结果、异常,以便后台监控和前台显示,但是,MDP没有返回值也没法返回异常,当时我只能无奈。

 
Lingo解决了这个问题,它扩展了JMS,允许异步的函数调用。
在下载了lingo-1.0-M1后(虽然1.2.1发布了,但是还没有文档支持,所以暂且用1.0),参考其自带的example,了解了它异步函数调用的代码思路。
 
客户端拥有服务端的方法接口,客户端将callback和相关参数代入接口,进行异步调用,而服务端的接口实现中利用callback来返回必要的信息。
callback实现了EventListener,提供了返回值和异常的接口,另外涉及到两个方面,首先,callback本身需要轮询,其次,callback可以由实例池管理。
 
第一个方面主要参考了lingo的example,使用semaphore来进行轮询。
第二个方面并没有利用实例池,而是利用ThreadPoolExecutor来newFixedThreadPool,管理不同的异步调用线程,来完成对callback的调度。
配置部分:
<?xml version="1.0"
encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD
BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
 <bean id="broker"
class="org.activemq.spring.BrokerFactoryBean">
  <property name="config"
value="classpath:activemq.xml"
/>
 </bean>
 <bean id="jmsFactory"
  class="org.activemq.ActiveMQConnectionFactory">

<property
name="brokerURL">
   <value>tcp://localhost:61616</value>

</property>
 </bean>

 <bean id="destination"
class="org.activemq.message.ActiveMQQueue">
  <constructor-arg
index="0">
   <value>lingo.demo</value>

</constructor-arg>
 </bean>

 <bean
id="invocationFactory"
  class="org.logicblaze.lingo.LingoRemoteInvocationFactory">

<constructor-arg>
   <bean
class="org.logicblaze.lingo.SimpleMetadataStrategy">

<!-- 允许单向异步调用
-->
    <constructor-arg
value="true" />
   </bean>
  </constructor-arg>
 </bean>

 <!-- 客户端配置 -->
 <bean id="client"
  class="org.logicblaze.lingo.jms.JmsProxyFactoryBean">

<property name="serviceInterface"
   value="org.openepo.jms.lingo.MailService"
/>
  <property name="connectionFactory"
ref="jmsFactory" />
  <property name="destination"
ref="destination" />

  <!-- 允许客户端单向异步调用
-->
  <property
name="remoteInvocationFactory"
   ref="invocationFactory" />
 </bean>
 <!-- 服务端配置 -->
 <bean id="server"
  class="org.logicblaze.lingo.jms.JmsServiceExporter">

<property name="service"
ref="serverImpl" />
  <property name="serviceInterface"
   value="org.openepo.jms.lingo.MailService"
/>
  <property name="connectionFactory"
ref="jmsFactory" />
  <property name="destination"
ref="destination" />
  <property name="invocationFactory"
ref="invocationFactory" />
 </bean>

 <!-- 服务端代码实现 -->
 <bean id="serverImpl"
class="org.openepo.jms.lingo.MailServiceImpl"
/>
 <!-- 管理callback池,处理回调结果
-->
 <bean id="asyncManager"
class="org.openepo.jms.lingo.AsyncManager"
singleton="false">
  <property name="mailClient" ref="client"
/>
  <property name="threadSize" value="5"
/>
 </bean>
</beans>
ResultListener和ResultListenerImpl:callback接口及实现。
 
ResultListener.java:
package org.openepo.jms.lingo;
import java.util.EventListener;
public interface ResultListener extends EventListener {
    public void onResult(Object result);
    // lifecycle end methods
    public void stop();
    public void onException(Exception e);
}
 
ResultListenerImpl.java:
package org.openepo.jms.lingo;
import java.util.ArrayList;
import java.util.List;
public class ResultListenerImpl implements ResultListener
{
    private List results = new ArrayList();
    private Object semaphore = new Object();
    private boolean stopped;
    private Exception onException;
    private long waitTime = 1000;
 
    public synchronized void onResult(Object result)
{
       
results.add(result);
        synchronized (semaphore) {
           
semaphore.notifyAll();
       
}
   
}
 
    // lifecycle end methods
    public void stop() {
        stopped = true;
    }
    public void onException(Exception e)
{
        onException = e;
    }
    public Exception getOnException()
{
        return
onException;
    }
    public List getResults()
{
        return results;
    }
    public boolean isStopped()
{
        return stopped;
    }
    public void waitForAsyncResponses(int
messageCount) {
       
System.out.println("Waiting for: " + messageCount +
" responses to arrive");
        long start =
System.currentTimeMillis();
        for (int i = 0; i
< 10; i++) {
           
try {
               
if (hasReceivedResponses(messageCount))
{
                   
break;
               
}
               
synchronized (semaphore) {
                   
semaphore.wait(waitTime);
               
}
           
}
           
catch (InterruptedException e) {
               
System.out.println("Caught: " + e);
           
}
        }
        long end =
System.currentTimeMillis() - start;
       
System.out.println("End of wait for " + end + " millis");
    }
    protected boolean hasReceivedResponse()
{
        return
results.isEmpty();
    }
    protected synchronized boolean
hasReceivedResponses(int messageCount) {
        return results.size()
>= messageCount;
    }
    public long getWaitTime()
{
        return waitTime;
    }
    public void setWaitTime(long waitTime)
{
        this.waitTime =
waitTime;
    }
}
MailService和MailServiceImpl:服务代码。
 
MailService.java:
package org.openepo.jms.lingo;
import java.util.List;
public interface MailService {
 public void
asyncSendMail(List<Mail> mails,
ResultListener listener);
}
 
MailServiceImpl.java:
package org.openepo.jms.lingo;
import java.util.List;
public class MailServiceImpl implements MailService
{
    public void
asyncSendMail(List<Mail> mails,
ResultListener listener) {
  
      try {
           
for (Mail mail : mails) {
           
 sendMail(mail);
               
Thread.sleep(2000);// 服务端时耗
               
listener.onResult(mail.getContent() +
" Sended Successfully.");
           
}
           
listener.stop();
        } catch
(Exception e) {
       
 listener.onException(e);
        }
    }
    public void sendMail(Mail mail) throws
Exception {
      // 可以取消下面的注释来查看服务端将异常传给客户端
      //throw new Exception("Error occurs
on server side.");
    }
}
在服务端方法中,可以利用callback将处理结果,是否结束和异常信息返回客户端.
 
Mail.java:
package org.openepo.jms.lingo;
import java.io.Serializable;
public class Mail implements Serializable
{
 
 private static final long serialVersionUID = 1L;
 
 private String content;
 
 public String getContent() {
  return content;
 }
 public void setContent(String content)
{
  this.content = content;
 }
 public Mail(String content) {
  this.content = content;
 }
}
AsyncManager:各类异步调用的方法可以集中在这个类中,利用线程池来统一控制callback实例。
 
AsyncManager.java:
package org.openepo.jms.lingo;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
public class AsyncManager {
 static private int threadSize =
10;   //callback池大小
 static private ThreadPoolExecutor executor =
(ThreadPoolExecutor) Executors
   .newFixedThreadPool(threadSize); //callback池
 public void setThreadSize(int threadSize)
{
  AsyncManager.threadSize = threadSize;
 }
 private MailService mailClient;
 public void setMailClient(MailService mailClient)
{
  this.mailClient = mailClient;
 }
 public AsyncManager() {
 }
 public void sendMails(final
List<Mail> mails)
{
  // callback对象
  final ResultListenerImpl callBack = new
ResultListenerImpl();
  callBack.setWaitTime(2000);
  
  // 异步调用
  mailClient.asyncSendMail(mails, callBack);
 
  // 调用线程池中的callback
  executor.execute(new Runnable()
{
   public void run() {
    // callBack 阻塞等待n个消息
    callBack.waitForAsyncResponses(mails.size());
    if (callBack.getOnException() != null) {
     // 服务端异常
     System.out.println("Server
Exception: "
       +
callBack.getOnException().getMessage());
    } else
{
     // 得到服务端处理结果,打印结果
     for (Object result :
callBack.getResults())
{
      System.out.println("Result:
" + result);
     }
    }
   }
  });
 }
}
上面匿名类的run方法中,在callback的waitForAsyncResponses方法结束后,可以检查callback中的信息,进行异常处理等。
 
下面是测试用例:
@Test
public void test() {
 List<Mail> mails = new
ArrayList<Mail>();
 mails.add(new Mail("mail1"));
 mails.add(new Mail("mail2"));
 // 计算时间
 long startTime = System.currentTimeMillis();
  
 try {
  // 异步调用
  asyncManager.sendMails(mails);
  // 没有阻塞
  System.out.println("Cost time "
     + (System.currentTimeMillis() -
startTime) + "ms");
   
  //为查看结果,sleep主线程
  Thread.sleep(10000);
 } catch (InterruptedException e)
{
  e.printStackTrace();
 }
}
使用Lingo对JMS增强后,通过callback,使得异步调用的确比较OO了,但是更重要的是服务端的信息可以通过callback返回给客户端,客户端可以相应地进行处理。

多出了许多代码,自然复杂度有所增加,但是lingo-1.2.1后,增加了annotation,减少了callback的代码量。

使用Lingo增强JMS的更多相关文章

  1. Spring 4.3.11.RELEASE文档阅读(一):overview

    一.宏观概述中的体会和发现 Spring是组件式的框架,它允许我们只使用其一小部分.Spring所做的工作,就是不断的简化我们的操作.比如它的IOC容器,当我们自己应用设计模式,比如说:建造者.工厂. ...

  2. 详细介绍Spring 5的那些新特性与增强

    Spring5 是一个重要的版本,距离SpringFramework4差不多四年.在此期间,大多数增强都是在 SpringBoot 项目中完成的.在本文中,我们将很快了解到Spring5发行版中的一些 ...

  3. J2EE相关概念,EJB/JNDI/JMS/RMI等

    J2EE 四层模型 J2EE的核心API.组件.相关概念 JDBC(Java Database Connectivity) JNDI(Java Name and Directory Interface ...

  4. JMS规范与Kafka

    一.为什么需要消息队列 消息队列的核心作用就是三点:解耦一个系统中各个子模块的互相绑定与依赖,异步执行后台耗时逻辑,并行处理一个请求中涉及的多个操作. 以我们常见的下订单场景来说明,我们熟悉的淘宝,后 ...

  5. JPA、JTA与JMS

    三者都属于Java企业级规范 JPA(java persistence API) JPA 通过JDK5.0的注解或XML来描述 对象-关系表的映射关系,并将运行期的实体对象持久化存储到数据库中. JT ...

  6. Asynchronous calls and remote callbacks using Lingo Spring Remoting

    http://www.jroller.com/sjivan/entry/asynchronous_calls_and_callbacks_using Asynchronous calls and re ...

  7. Lingo (Spring Remoting) : Passing client credentials to the server

    http://www.jroller.com/sjivan/entry/lingo_spring_remoting_passing_client Lingo (Spring Remoting) : P ...

  8. java高级特性增强

    第4天 java高级特性增强 今天内容安排: 1.掌握多线程 2.掌握并发包下的队列 3.了解JMS 4.掌握JVM技术 5.掌握反射和动态代理 java多线程增强 .1. java多线程基本知识 . ...

  9. 什么是JMS规范?

    一.简介 JMS是什么:JMS是Java提供的一套技术规范和关于消息中间件的协议 JMS干什么用:通过生产者Producer,消息服务器,以及消费者通力合作,使异构系统能进行集成通信,缓解系统瓶颈,提 ...

随机推荐

  1. Public Private Protect Inheritance and access specifiers

    In the previous lessons on inheritance, we've been making all of our data members public in order to ...

  2. FZU1920 Left Mouse Button(dfs)

     Problem 1920 Left Mouse Button Accept: 385    Submit: 719 Time Limit: 1000 mSec    Memory Limit : 3 ...

  3. C语言中函数和指针的參数传递

    近期写二叉树的数据结构实验.想用一个没有返回值的函数来创建一个树,发现这个树就是建立不起来,那么我就用这个样例讨论一下c语言中指针作为形參的函数中传递中隐藏的东西. 大家知道C++中有引用的概念,两个 ...

  4. 【Linux】配置JAVA_HOME环境变量

    1. 永久修改,对所有用户有效 # vi /etc/profile //按键盘[Shift + g], 在profile文件最后添加下面的内容: export JAVA_HOME = /home/my ...

  5. HttpClient4.3教程 第二章 连接管理

    2.1.持久连接 两个主机建立连接的过程是很复杂的一个过程,涉及到多个数据包的交换,并且也很耗时间.Http连接需要的三次握手开销很大,这一开销对于比较小的http消息来说更大.但是如果我们直接使用已 ...

  6. Spring bean注入方式

    版权声明:本文为博主原创文章,如需转载请标注转载地址. 博客地址:http://www.cnblogs.com/caoyc/p/5619525.html  Spring bean提供了3中注入方式:属 ...

  7. RPM卸载

    安全地卸载 rpm卸载软件包,并不是简单地将原来安装的文件逐个删除,那样做的话,可能会出现这样或那样的问题.如,a软件包依靠b软件包做某些工作,若b软件包卸载了,则a软件包就不能正常运行了.rpm为用 ...

  8. resin设置jvm参数

    http://www.quiee.com.cn/archives/592/ resin resin3.0 及前期版本内存设置, 如下: 启动时设置虚拟内存: unix> bin/httpd.sh ...

  9. 【转】ESFramework成熟的C#网络通信框架(跨平台)

    原文地址:http://www.cnblogs.com/zhuweisky/archive/2010/08/12/1798211.html ESFramework网络通信框架是一套性能卓越.稳定可靠. ...

  10. jQuery Autocomplete 用户快速找到并从预设值列表中选择

    jQuery Autocomplete 插件根据用户输入值进行搜索和过滤,让用户快速找到并从预设值列表中选择.通过给 Autocomplete 字段焦点或者在其中输入字符,插件开始搜索匹配的条目并显示 ...