Dubbo本地调用、参数回调、事件通知(八)

1、参考

本地调用:http://dubbo.apache.org/en-us/docs/user/demos/local-call.html

参数回调:http://dubbo.apache.org/en-us/docs/user/demos/callback-parameter.html

事件通知:http://dubbo.apache.org/zh-cn/docs/user/demos/events-notify.html

2、本地调用

如果Consumer与Provider部署在一台主机上,共用一个JVM,那么当Consumer调用Provider时就没有必要经过网络栈,直接调用即可,不需要通过网络,便是Dubbo的其它Filtter正常生效。

对于Provider端而言,从2.2.0开始默认自动支持本地调用,无需任务特殊配置。值得注意的是,如果只打算让Provider提供的服务支持本地调用,可以把服务的protocol设置成injvm一个协议。这样,被配置的服务将不用支持远程调用,服务在运行时也不会开端口。

对于Consumer端而言,当调用某个服务时,如果它在本地已经暴露,则默认直接调用,这个是从2.2.0开始后的默认行为。如果想关掉此行为,则如下配置:

有点鸡肋的功能,为服务本身就是要将功能打散然后分布到系统中的多个节点上,为什么会分布在一个节点上呢?另个就算不走injvm,走其它协议,开销能有多大呢?不能指着把服务部署在相同节点上节省网络流量吧。

3、参数回调

抛开Dubbo,在普通的开发中,当调用一个方法时,我们可能在参数中给方法传递一个引用,然后方法在执行的过程中调用引中的一个方法,这就是普通的回调。

Dubbo也有类似功能,Dubbo 将基于长连接生成反向代理,Consumer端临时允当Provider端,Provider端调用的接口,真正的逻辑将在Consumer端执行。

服务端接口示例,CallbackService.java:

package com.callback;
 
public interface CallbackService {
    void addListener(String key, CallbackListener listener);
}

后边一个参数是接口,下边是参数接口定义CallbackListener.java文件:

package com.callback;
 
public interface CallbackListener {
    void changed(String msg);
}

这个接口要在Consumer端实现。

服务端接口实现:

package com.callback.impl;
 
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
 
import com.callback.CallbackListener;
import com.callback.CallbackService;
 
public class CallbackServiceImpl implements CallbackService {
     
    private final Map listeners = new ConcurrentHashMap();
  
    public CallbackServiceImpl() {
        Thread t = new Thread(new Runnable() {
            public void run() {
                while(true) {
                    try {
                        for(Map.Entry entry : listeners.entrySet()){
                           try {
                               entry.getValue().changed(getChanged(entry.getKey()));
                           } catch (Throwable t) {
                               listeners.remove(entry.getKey());
                           }
                        }
                        Thread.sleep(5000); // 定时触发变更通知
                    } catch (Throwable t) { // 防御容错
                        t.printStackTrace();
                    }
                }
            }
        });
        t.setDaemon(true);
        t.start();
    }
  
    public void addListener(String key, CallbackListener listener) {
        listeners.put(key, listener);
        listener.changed(getChanged(key)); // 发送变更通知
    }
     
    private String getChanged(String key) {
        return "Changed: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
    }
}

在上边的实现中,在构造方法中启动了一个线程,里边是一个无限循环,按固定间隔调用从Consumer端注册进来的回调。

服务端配置:



    
        
        
        
    

需要明确指定那个参数是用来回调的。

Consumer端配置,一切如常:

Consumer端代码:

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("classpath:consumer.xml");
context.start();
 
CallbackService callbackService = (CallbackService) context.getBean("callbackService");
 
callbackService.addListener("foo.bar", new CallbackListener(){
    public void changed(String msg) {
        System.out.println("callback1:" + msg);
    }
});

需要为回调的参数指定一个实例。

4、事件通知

对于一次远程方法调用,有oninvokeonreturnonthrow三个事件,分别为调用之前、返回之后,抛出异常三个事件。在Consumer端,可以为三个事件指定事件处理方法。

服务端接口:

interface IDemoService {
    public Person get(int id);
}

服务端实现:

class NormalDemoService implements IDemoService {
    public Person get(int id) {
        return new Person(id, "charles`son", 4);
    }
}

服务端配置:




没有什么特别的地方。

Consumer端的处理事件通知的接口:

interface Notify {
    public void onreturn(Person msg, Integer id);
    public void onthrow(Throwable ex, Integer id);
}

方法中,第一个参数应该是方法的返回值,其它是方法的参数。

然后Consumer端实现这个接口:

class NotifyImpl implements Notify {
    public Map    ret    = new HashMap();
    public Map errors = new HashMap();
    
    public void onreturn(Person msg, Integer id) {
        System.out.println("onreturn:" + msg);
        ret.put(id, msg);
    }
    
    public void onthrow(Throwable ex, Integer id) {
        errors.put(id, ex);
    }
}

服务端配置:



      

配置中async与onreturn、onthrow是一对,组合不同,功能也不同:

  • 异步回调模式:async=true onreturn="xxx"
  • 同步回调模式:async=false onreturn="xxx"
  • 异步无回调 :async=true
  • 同步无回调 :async=false

Consumer端测试代码:

IDemoService demoService = (IDemoService) context.getBean("demoService");
NofifyImpl notify = (NofifyImpl) context.getBean("demoCallback");
int requestId = 2;
Person ret = demoService.get(requestId);
Assert.assertEquals(null, ret);
//for Test:只是用来说明callback正常被调用,业务具体实现自行决定.
for (int i = 0; i < 10; i++) {
    if (!notify.ret.containsKey(requestId)) {
        Thread.sleep(200);
    } else {
        break;
    }
}
Assert.assertEquals(requestId, notify.ret.get(requestId).getId());

当前线程正在执行for循环,事件处理器中的代码应该是在Consumer端的IOThread中执行,可能涉及到线程同步的问题。

你可能感兴趣的:(Dubbo)