虽然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. 安卓camera拍照时序

    转自:http://blog.csdn.net/tankai19880619/article/details/17147125 一.看看调用时序图 1.拍照命令时序图 2.拍照数据回调时序图 二.看看 ...

  2. js实现回放拖拽轨迹-------Day48

    今天有点小高兴,csdn博客浏览量过万了,在过去还从来没有过这么高的浏览量呢.不得不说.太多时候还是有些矫情.可看到这些鼓舞还是忍不住高兴啊,至少,这样让我有一种行内人员的感觉,吾道不孤啊. 闲话不多 ...

  3. MQTT---HiveMQ源代码具体解释(七)Netty-SSL/NoSSL

    源博客地址:http://blog.csdn.net/pipinet123 MQTT交流群:221405150 实现功能 依据用户配置的不同的Listener(TcpListener.TlsTcpLi ...

  4. 织梦dedecms修改include和plus重命名提高安全性防漏洞注入挂马

    织梦dedecms是新手站长使用得比较多的一个建站开源程序,正因如此,也是被被入侵挂马比较多的程序.下面就来跟大家说一下怎么重新命名dedecms的include文件夹以及plus文件夹来提高网站的安 ...

  5. thinkphp 3.2多语言设置

    1.将CheckLangBehavior.class.php(没有的话去下载完整版)文件放到此目录下:\ThinkPHP\Extend\Behavior 2.修改目录下文件Application\Ho ...

  6. 跨浏览器的CORS

    function createCORSRequest(method, url){ var xhr = new XMLHttpRequest(); if("withCredentials&qu ...

  7. Java 继承、多态与类的复用

    摘要: 本文结合Java的类的复用对面向对象两大特征继承和多态进行了全面的介绍. 首先,我们介绍了继承的实质和意义,并探讨了继承,组合和代理在类的复用方面的异同.紧接着,我们依据继承引入了多态.介绍了 ...

  8. fs 小计

    如果是export 就可以放到b-leg上 如果是set就可以对于a-leg

  9. linux下/etc/hosts 和hostname文件的区别

    很过人一提到更改hostname首先就想到修改/etc/hosts文件,认为hostname的配置文件就是/etc/hosts.其实不是的. hosts文件的作用相当如DNS,提供IP地址到hostn ...

  10. Atitit.木马病毒的操作注册表原理 系统服务管理器 atiSysService

    Atitit.木马病毒的操作注册表原理 系统服务管理器 atiSysService 1. atiSysService1 2. atiSysService  原理1 3. Java code1 4. 参 ...