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中執行,可能涉及到線程同步的問題。