|  | 本文参考孙卫琴所创作的<<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
 | 
 
 
 [这个贴子最后由 sunweiqin 在 2024-07-11 13:46:27 重新编辑]
 |  |