|
本文参考孙卫琴,杜聚宾所创作的<<Spring Cloud Alibaba微服务开发宝典>>一书,即将出版在Dubbo框架中,消费者可以远程调用提供者的服务方法。此外,提供者也可以调用消费者的方法,这种调用过程称为回调。
在图1中,有两个消费者访问提供者。消费者远程调用提供者的HelloServiceImpl对象的sayHello()方法,该方法在提供者方执行;此外,提供者会回调消费者的MyCallbackListener对象的report()方法,该方法在消费者方执行。
图1 提供者回调消费者的方法
提供者微服务回调消费者微服务的创建步骤如下。
(1)在一个公共模块中创建CallbackListener回调接口,参见例程1。
/* 例程1 CallbackListener.java */
public interface CallbackListener {
//这是提供者回调消费者的方法
public void report(String msg);
} |
(2)在公共模块的HelloService接口中加入两个方法:
//提供者的服务方法
public String sayHello(String username,String key);
//消费者通过此方法向提供者注册CallbackListener
public void addListener(String key,
CallbackListener listener); |
为了便于提供者管理和访问特定CallbackListener对象,每个CallbackListener对象都有唯一的key。
(3)在消费者模块中创建MyCallbackListener类,它实现了CallbackListener接口,参见例程2。
/* 例程2 MyCallbackListener.java */
public class MyCallbackListener implements CallbackListener{
@Override
public void report(String msg) {
System.out.println(msg);
}
} |
(4)在消费者模块的HelloConsumerController类中,增加init()和testCallback()方法:
@Value("${server.port}")
private String servicePort;
//该注解表示Spring框架创建了控制器对象后就会调用init()方法
@PostConstruct
public void init(){
//向提供者注册MyCallbackListener对象,以servicePort作为key
helloService.addListener(servicePort,
new MyCallbackListener());
}
@GetMapping(value = "/callback/{username}")
public String testCallback(@PathVariable String username){
//调用提供者的sayHello()服务方法
return helloService.sayHello(username,servicePort);
} |
以上代码把当前微服务实例监听的servicePort作为MyCallbackListener对象的key。在实际应用中,也可以用其他具有业务含义的属性作为key,只要保证每个MyCallbackListener对象的key具有唯一性即可。
(5)修改提供者模块的HelloServiceImpl类,实现HelloService接口的addListener()方法和sayHello(String username,String key)方法,参见例程3。
/* 例程3 HelloServiceImpl.java */
@DubboService(
//指明addListener()方法的listener参数是回调类型的参数
methods={
@Method(name="addListener",
arguments={@Argument(index=1,callback=true)})}
)
public class HelloServiceImpl implements HelloService {
//存放消费者所注册的CallbackListener对象
private final Map<String, CallbackListener> listeners =
new ConcurrentHashMap<String, CallbackListener>();
public HelloServiceImpl() {
//创建定时向消费者推送当前时间信息的线程
Thread t = new Thread(new Runnable() {
public void run() {
while(true){
try{
for(Map.Entry<String, CallbackListener> entry :
listeners.entrySet()){
try{
//回调消费者的CallbackListener的report()方法
entry.getValue().report("当前时间:"
+new Date());
}catch (Throwable t) {
listeners.remove(entry.getKey());
}
}
Thread.sleep(5000);//通过睡眠定时向消费者推送时间信息
}catch (Throwable ex){
ex.printStackTrace();
}
}
}
});
t.setDaemon(true); //作为后台线程运行
t.start();
}
@Override
public void addListener(String key,
CallbackListener listener) {
//加入消费者注册的CallbackListener对象
listeners.put(key, listener);
}
@Override
public String sayHello(String username,String key){
CallbackListener listener=listeners.get(key);
//回调消费者的CallbackListener对象的report()方法
listener.report(username+"打过招呼。");
return "Hello," + username;
}
……
} |
HelloServiceImpl类的@DubboService注解嵌套了@Method注解,@Method注解又嵌套了@Argument(index=1,callback = true)注解。@Argument注解的作用是指明addListener()方法的索引为1的参数(即listener参数)是回调类型的参数。
(6)先后启动提供者模块和消费者模块。当提供者模块的HelloConsumerController控制器在初始化的过程中,会调用helloService.addListener(servicePort, new MyCallbackListener())方法,向提供者注册MyCallbackListener对象。
在提供者方,HelloServiceImpl对象在创建时启动了一个后台线程,该线程定时回调消费者注册的所有CallbackListener对象的report()方法,推送当前的时间信息。
通过浏览器访问http://localhost:8082/callback/Tom,提供者方的HelloServiceImpl对象的sayHello(String username,String key)方法会回调当前消费者注册的CallbackListener对象的report()方法,然后返回响应结果。在IDEA中,在消费者模块的控制台,会打印如下信息:
当前时间:Fri May 27 13:25:38 CST 2022
当前时间:Fri May 27 13:25:43 CST 2022
当前时间:Fri May 27 13:25:49 CST 2022
Tom打过招呼。
当前时间:Fri May 27 13:25:54 CST 2022
当前时间:Fri May 27 13:25:59 CST 2022 |
|
|