missian(4)使用异步客户端

来源:互联网 发布:什么是数据共享 编辑:程序博客网 时间:2024/06/15 06:25

重要:Missian刚刚更新到0.31,新增了Future风格的回调方式。

 

Missian没有绑定spring,但是强烈推荐配合spring一起使用。异步客户端由于需要调用BeanLocator去寻找回调的 Bean,如果配合Spring使用,可以直接使用SpringLocator(BeanLocator的唯一实现),否则需要自己实现。

 

使用异步客户端需要注意一点:由于是异步调用,所以一个远程方法的返回值永远是null(如果不是void的话)或者是原生数据类型的默认值。一段时间后(比如100毫秒)后客户端收到这个返回值,会去找到相应的回调对象进行调用。

 

异步的优势是:在调用的期间我们不需要像同步调用一样有一个线程一直在等着它的返回值,而是调用完即可返回释放线程,当客户端接受到返回值后会进行回调,业务流程可以继续往下执行。不要小看这个等待的时间,假如A服务调用了一个跨机房的服务或者一个重型的服务B,那么B的响应时间可能是100毫秒甚至更多,那么可以想象在高并发的情况下,可能A服务的全部线程都耗死在无穷的等待上了。

 

我们还是先看看如何配合Spring来使用Missian异步客户端。

 

步骤一:给Hello.hello(String, int)创建一个回调类

注意和0.2x相比,这里有比较大的不同:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class HelloCallback {  
  2.     public void hello(String returnValue) {  
  3.         System.out.println(returnValue);  
  4.     }  
  5. }  

这个类的方法要和Hello接口的方法一一对应,Hello中所有方法(除了返回值为void的方法)都应该有一个回调方法,回调方法名和Hello接口中对应的方法名一样,而且只接受一个参数,参数类型和对应方法的返回值一致。

 

例如,Hello有一个hello(String, int)方法的返回值是String类型,那么要求HelloCallback必须有一个hello(String)的方法。

 

 

步骤二:修改Hello接口,用注解的方法声明回调Bean

这里和0.21前的版本也有所不同,以前这个注解是用在方法上的,现在直接用在接口上,所以一个接口只需要注解一次了。

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @CallbackTarget("helloCallback")  
  2. public interface Hello {  
  3.     public String hello(String name, int age);  
  4. }  

 

步骤三:在Spring配置文件中配置这个回调Bean

[xml] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="UTF-8"?>  
  2. <beans xmlns="http://www.springframework.org/schema/beans"  
  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
  4.     xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">  
  5.     <!-- your callback bean, missian client will invoke its execute() method when received the returned object -->  
  6.     <bean id="helloCallback" class="com.missian.example.bean.HelloCallback">  
  7.     </bean>  
  8. </beans>  

 

步骤四:在Spring中创建AsyncMissianProxyFactory

[xml] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <bean id="asyncMissianProxyFactory" class="com.missian.client.async.AsyncMissianProxyFactory" init-method="init" destroy-method="destroy">  
  2.     <constructor-arg >  
  3.         <bean class="com.missian.common.beanlocate.SpringLocator"/>  
  4.     </constructor-arg>  
  5. </bean>  

这里我们使用的是AsyncMissianProxyFactory的最简单的构造函数,只接受一个BeanLocator。这时候默认创建一个4 个线程的线程池用来处理回调逻辑,1个线程用来处理IO,需要指定线程数,或者将一个已经存在的线程池传入,可以参考其它几个构造函数:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, ExecutorService threadPool,  int callbackIoProcesses, boolean logBeforeCodec, boolean logAfterCodec, NetworkConfig networkConfig) {}  
  2. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, ExecutorService threadPool,  int callbackIoProcesses, boolean logBeforeCodec, boolean logAfterCodec){}  
  3. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, ExecutorService threadPool) {}  
  4. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, int threadPoolSize, int callbackIoProcesses, boolean logBeforeCodec, boolean logAfterCodec) {}  
  5. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, ExecutorService threadPool, NetworkConfig networkConfig) {}  
  6. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, int threadPoolSize, int callbackIoProcesses, boolean logBeforeCodec, boolean logAfterCodec, NetworkConfig networkConfig) {}  
  7. public AsyncMissianProxyFactory(BeanLocator callbackLoacator, int threadPoolSize){}  

假如在服务器里使用Missian客户端,可以考虑将服务器主线程池传入给AsyncMissianProxyFactory,共享线程池。 

 

步骤五:实现异步调用

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public static void main(String[] args) throws IOException {  
  2.     ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("com/missian/example/client/async/withspring/applicationContext-*.xml");  
  3.     //actually you can inject AsyncMissianProxyFactory into any other beans to use it.  
  4.     //we just show how AsyncMissianProxyFactory works here.  
  5.     AsyncMissianProxyFactory asyncMissianProxyFactory = (AsyncMissianProxyFactory)context.getBean("asyncMissianProxyFactory");  
  6.     Hello hello = (Hello)asyncMissianProxyFactory.create(Hello.class"tcp://localhost:1235/hello");  
  7.     long time = System.currentTimeMillis();  
  8.     for(int i=0; i<10000; i++) {  
  9.         hello.hello("gg"25);    
  10.     }  
  11.     System.out.println(System.currentTimeMillis()-time);  
  12. }  

 你可以清楚地看到,所有的请求都发送出去之后,返回值陆续返回并回掉了HelloCallback。

和同步的客户端一样,可以使用http协议发送数据:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Hello hello = (Hello)asyncMissianProxyFactory.create(Hello.class"http://localhost:1235/hello");  

但目前比较遗憾的是,还不能够支持异步调用Hessian服务。  

另外需要说明的是,这个直接从Context里面取出AsyncMissianProxyFactory只是用来演示异步调用的用法;正常的做法应该是将AsyncMissianProxyFactory注入到我们需要使用它的Bean。


===============0.31.新增功能分割线===================

 

如何为重载方法都实现回调?

比如以下两个方法都需要回调:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public interface Hello {  
  2.     String hello(String name, int age, String country);  
  3.     String hello(String name, int age);  
  4. }  

 按照上面所说的,他们的回调方法都映射到:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. void hello(String);  

 这样会造成回调错误,因此需要使用一个注解来说明回调方法名:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. @CallbackTarget("helloCallback")  
  2. public interface Hello {  
  3.     @CallbackTargetMethod("hello0")  
  4.     public String hello(String name, int age, String country);  
  5.   
  6.     @CallbackTargetMethod("hello1")  
  7.     public String hello(String name, int age);  
  8. }  

 对应的,回调类的实现:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class HelloCallback {  
  2.     public void hello0(String returnValue) {  
  3.         System.out.println(returnValue);  
  4.     }  
  5.     public void hello1(String returnValue) {  
  6.         System.out.println("hello1:"+returnValue);  
  7.     }  
  8.       
  9. }  

 注意如果不使用注解,系统寻找默认的方法。注解同样也可以用于非重载的方法。

 

另外一种回调的实现

如果不希望使用注解,那么还有另外一种方式可供选择:

如果服务器端的方法是:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. String hello(String name, int age);  

 那么客户端的接口可以写成(注意,Missian不要求服务器端和客户端使用同一个接口类,甚至接口名都可以不同,而只要求方法名及参数必须匹配):

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public interface Hello {  
  2.     public String hello(String name, int age, Callback cb);  
  3. }  

 调用时:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Hello hello = (Hello)factory.create(Hello.class"http://localhost:1235/hello");  
  2. Callback cb = ......  
  3. hello.hello("name"80, cb);  

 即可以异步调用成功。

 

Future风格的异步实现

我个人非常喜欢Future这种方法,在Mina中就有大量的使用。同样Missian也提供了这样一个能力。提供了一个AysncFuture,即可以通过get()变成同步,也可以通过addListner()来监听,一旦返回值到达,就会出发监听器。

 

如果服务器端的方法是:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. String hello(String name, int age);  

 那么客户端的接口可以写成(注意,Missian不要求服务器端和客户端使用同一个接口类,甚至接口名都可以不同,而只要求方法名及参数必须匹配):

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public interface Hello {  
  2.     public AysncFuture<String> hello(String name, int age, Class<String> returnType);  
  3. }  

 调用时:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. Hello hello = (Hello)factory.create(Hello.class"http://localhost:1235/hello");  
  2. Async<String> future = hello.hello("name"80, String.class);  

 如果想阻塞直到数据返回,那么:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. String value = future.get();  
  2. System.out.println(value);  

 如果想通过监听器实现事件驱动:

[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. AsyncListener listener = ....  
  2. future.addListener(listener);  
 

 

Author: gh_aiyz

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 梦幻忘记账号了怎么办 三国杀相同ip怎么办 ps图片导出太大怎么办 xp系统不能全屏怎么办 xp系统不全屏怎么办 word空白页无法删除怎么办 word文档用不了怎么办 wps文档不能编辑怎么办 电脑e盘不见了怎么办 电脑d盘权限怎么办 共享打印机无法打印怎么办 网络权限被禁止怎么办 硬盘显示不出来怎么办 硬盘插上不显示怎么办 mac不能读取移动硬盘怎么办 mac系统坏了怎么办 ip地址网站打不开怎么办? 电脑页面偏了怎么办 wifi被禁止联网怎么办 oppo浏览器屏蔽网站怎么办 被网站禁言怎么办 网站被模仿了怎么办 公积金不允许提取还房贷怎么办 电脑超出频率限制怎么办 海带宝转运丢失怎么办 xp电脑证书过期怎么办 电脑qq重复登录怎么办 海带宝少东西怎么办 苹果手机照片打不开怎么办 旧电脑没有密码了怎么办 快手忘记登录账号怎么办 b站永久封禁怎么办 微博自动关注怎么办 微博会自动点赞怎么办 游戏名字被占用怎么办 闲鱼头像违规怎么办 请求状态码错误怎么办 微博错误20521怎么办 忘记申请的邮箱怎么办 填写简历忘记邮箱怎么办 雅虎邮箱忘记用户名怎么办