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开始后的默认行为。如果想关掉此行为,则如下配置:
<dubbo:reference ... scope="remote" />
有点鸡肋的功能,为服务本身就是要将功能打散然后分布到系统中的多个节点上,为什么会分布在一个节点上呢?另个就算不走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<String, CallbackListener> listeners = new ConcurrentHashMap<String, CallbackListener>();
public CallbackServiceImpl() {
Thread t = new Thread(new Runnable() {
public void run() {
while(true) {
try {
for(Map.Entry<String, CallbackListener> 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端注册进来的回调。
服务端配置:
<bean id="callbackService" class="com.callback.impl.CallbackServiceImpl" />
<dubbo:service interface="com.callback.CallbackService" ref="callbackService" connections="1" callbacks="1000">
<dubbo:method name="addListener">
<dubbo:argument index="1" callback="true" />
<!--也可以通过指定类型的方式-->
<!--<dubbo:argument type="com.demo.CallbackListener" callback="true" />-->
</dubbo:method>
</dubbo:service>
需要明确指定那个参数是用来回调的。
Consumer端配置,一切如常:
<dubbo:reference id="callbackService" interface="com.callback.CallbackService" />
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、事件通知
对于一次远程方法调用,有oninvoke
、onreturn
、onthrow三个事件,分别为调用之前、返回之后,抛出异常三个事件。在Consumer端,可以为三个事件指定事件处理方法。
服务端接口:
interface IDemoService {
public Person get(int id);
}
服务端实现:
class NormalDemoService implements IDemoService {
public Person get(int id) {
return new Person(id, "charles`son", 4);
}
}
服务端配置:
<dubbo:application name="rpc-callback-demo" />
<dubbo:registry address="zookeeper://127.0.0.1:2181"/>
<bean id="demoService" class="org.apache.dubbo.callback.implicit.NormalDemoService" />
<dubbo:service interface="org.apache.dubbo.callback.implicit.IDemoService" ref="demoService" version="1.0.0" group="cn"/>
没有什么特别的地方。
Consumer端的处理事件通知的接口:
interface Notify {
public void onreturn(Person msg, Integer id);
public void onthrow(Throwable ex, Integer id);
}
方法中,第一个参数应该是方法的返回值,其它是方法的参数。
然后Consumer端实现这个接口:
class NotifyImpl implements Notify {
public Map<Integer, Person> ret = new HashMap<Integer, Person>();
public Map<Integer, Throwable> errors = new HashMap<Integer, Throwable>();
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);
}
}
服务端配置:
<bean id ="demoCallback" class = "org.apache.dubbo.callback.implicit.NofifyImpl" />
<dubbo:reference id="demoService" interface="org.apache.dubbo.callback.implicit.IDemoService" version="1.0.0" group="cn" >
<dubbo:method name="get" async="true" onreturn = "demoCallback.onreturn" onthrow="demoCallback.onthrow" />
</dubbo:reference>
配置中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中执行,可能涉及到线程同步的问题。