[置頂]移植jQuery deferred到j(luò)ava,基于java的promise編程模型
移植jQuery deferred到java,基于java的promise編程模型
很多語言都支持promise編程模型,像是scala中promise類和jquery(javascript)中的deferred對象等,在java中好像缺少相關(guān)實(shí)現(xiàn)。筆者不得以,只能自己動(dòng)手弄了一個(gè)。最后選擇將jquery中的deferred對象移植到java中來的方案。目前已經(jīng)應(yīng)用在企業(yè)級項(xiàng)目的高性能服務(wù)器和android客戶端等項(xiàng)目中。
Promise編程模型的概念這里也不再贅述,大家自己上網(wǎng)查找即可。這種編程模型主要解決的問題就是“同步調(diào)用變異步的問題”,通常解決異步調(diào)用的方式是使用“回調(diào)”。但普通回調(diào)的使用在代碼書寫,返回值傳遞和“異步方法編排上”非常的不方便。所以才會(huì)有Promise模型的誕生。
這次會(huì)介紹java版的deferred對象的使用方法,以及用jquery版之間的變化和改進(jìn)。目前開放的版本是基于線程池的版本,正在開發(fā)基于akka的版本。在jquery的實(shí)現(xiàn)中,因?yàn)?/span>javascript是單線程的,所以不用考慮線程同步的問題。在java線程池的版的deferred里,基于多線程環(huán)境做了很多測試,保證了線程安全及可靠性。
一. 基本調(diào)用形式
final Deferred def = new Deferred (App. executor);
執(zhí)行某個(gè)異步調(diào)用,比如某個(gè)基于網(wǎng)絡(luò)的異步服務(wù)
callService(new Response(){
public void onMessage(Object message){
def.resolve(message);
}
Public void onFail(Exception e){
def.reject(e);
}
});
你可以在構(gòu)造Deferred 對象后的任意時(shí)候,使用def的then方法。比如
def.then(new Reply(){
public Object done(Object d) {
System.out.println("response:"+d);
return d;
}
public void fail(Object f) {
System.out.println("error:"+f);
}
});
一個(gè)經(jīng)常遇到的場景是callService后將def作為參數(shù)傳遞到其他方法,在其他方法內(nèi)部再?zèng)Q定def要綁定什么樣的后續(xù)動(dòng)作,也就是綁定什么樣的then。
注意then方法的定義public Object done(Object d),在實(shí)際使用中done通常是以“處理鏈”的方式來使用的,即你會(huì)看到def.then().then().then()…這樣的方式,每一個(gè)then的done方法接收的參數(shù)都是其上一個(gè)then的done方法的返回值。通常作為參數(shù)傳遞給某個(gè)方法的Deferred上面已經(jīng)綁定了一些默認(rèn)的then對象,來處理一些必要的步驟。比如對接收報(bào)文的初步解碼。
注意同在Reply接口中fail方法是沒有返回值的,一旦異步處理鏈上的某個(gè)Deferred被reject,其本身及后面所有的Deferred綁定的then都會(huì)被觸發(fā)fail方法。這保證了整個(gè)業(yè)務(wù)編排上或是你精心設(shè)計(jì)的算法編排上任意一個(gè)環(huán)節(jié),無論如何都會(huì)得到響應(yīng),這也是Promise模型關(guān)于異常的最重要的處理方式。
Promise編程模型本身是強(qiáng)健的,但異步服務(wù)卻不是總能得到響應(yīng)。在實(shí)際應(yīng)用中,每一個(gè)作為計(jì)算或業(yè)務(wù)環(huán)節(jié)的Deferred都應(yīng)該被定時(shí)輪詢,以保證在異步服務(wù)徹底得不到響應(yīng)的時(shí)候(比如你執(zhí)行了一個(gè)數(shù)據(jù)庫查詢,但過了很長很長時(shí)間仍沒有得到回應(yīng)),可以給Deferred對象reject一個(gè)超時(shí)錯(cuò)誤。
響應(yīng)處理對象then中方法done和fail都是不允許拋出任何異常的,特別是done方法,如果你的算法依賴異常,請?jiān)?/span>done中加上try…catch,并將異常傳換成下一個(gè)then可以理解的信息,以便這個(gè)Deferred處理鏈中可以正常執(zhí)行下去。
二. pipe到另外一個(gè)異步處理流程上去
假如你有如下的業(yè)務(wù)場景,你需要順序調(diào)用三個(gè)異步的webservice服務(wù)來得到最終的返回結(jié)果,其中沒個(gè)webservice的入?yún)⒍己蜕弦粋€(gè)的異步返回結(jié)果相關(guān)。(注意,異步的webservice是調(diào)用之后,服務(wù)端立刻返回,服務(wù)端處理完成后再主動(dòng)訪問剛才的請求方返回結(jié)果的方式)如果將這種webservice調(diào)用封裝成同步方法無疑在編程上是非常方便的,可以使用我們平常寫程序時(shí)順序的書寫方式,比如
reval1 = callwebservice1(param0)
reval2 = callwebservice2(reval1)
reval3 = callwebservice3(reval2)
方便的同時(shí)卻犧牲了性能。調(diào)用線程要在callwebservice方法內(nèi)阻塞,以等待異步返回。這樣的編程方法無法滿足高性能及高并發(fā)的需要。那么有沒有既能類似于平常寫程序時(shí)順序的書寫方式又能滿足異步無阻塞的需要呢,這就是Promise編程模型本身要解決的最大問題。
通常解決這種問題的方式是使用pipe,pipe這個(gè)方法名稱的由來應(yīng)該是來自于linux shell的管道符,即“|”
使用Deferred對象的解決方案類似于如下:
Deferred.resolvedDeferred(App.executor,param0).pipe(new AsyncRequest2(){
public void apply(Object param0,final Deferred newDefered) throws Exception{
asyncCallwebservice1(param0).onResponse(new Response(){
public void onMessage(String message){
newDefered.resolve(message);
}
});
}
}).pipe(new AsyncRequest2(){
public void apply(Object reval1,final Deferred newDefered) throws Exception{
asyncCallwebservice2(reval1).onResponse(new Response(){
public void onMessage(String message){
newDefered.resolve(message);
}
});
}
}).pipe(new AsyncRequest2(){
public void apply(Object reval2,final Deferred newDefered) throws Exception{
asyncCallwebservice3(reval3).onResponse(new Response(){
public void onMessage(String message){
newDefered.resolve(message);
}
});
}
}).then(new new Reply(){
public Object done(Object d) {
//在這里消費(fèi)最終結(jié)果
return d;
}
public void fail(Object f) {
}
});
使用Deferred對象提供的方案好處就是,所有的調(diào)用都是異步的,上面這一連串代碼立刻就會(huì)返回。所有的業(yè)務(wù)編排會(huì)按照書寫順序在線程池中的線程里被調(diào)用,你也不必?fù)?dān)心返回值結(jié)果和參數(shù)傳遞過程中的線程安全問題,框架在關(guān)鍵位置都做了同步,也做了相當(dāng)多的測試用于驗(yàn)證。
可以看出,對于異步方法調(diào)用而言,比較難以解決的問題是異步算法的編排問題。Deferred對象為異步算法提供了很好的解決方案。
相較于AsyncRequest2類還有一個(gè)AsyncRequest1類,接口如下:
public interface AsyncRequest1<R> {
public Deferred apply(R result) throws Exception;
}
這個(gè)類要求在在apply方法中要自己創(chuàng)建Deferred對象。
三. 一些小改進(jìn)
相較于傳統(tǒng)promise編程模型,在java多線程環(huán)境下做了一些小升級。這里主要介紹synchronize方法
Synchronize方法簽名如下:
Deferred synchronize(ExecutorService executor,Deferred... deferreds)
實(shí)際上,synchronize方法將眾多的Deferred對象的完成狀態(tài)同歸集到一個(gè)唯一的Deferred對象上去,即如果所有的Deferred對象參數(shù)都resolved了,作為最終結(jié)果的Deferred也resolve,如果眾多的Deferred對象參數(shù)有一個(gè)reject了,最終的那個(gè)Deferred也會(huì)立即reject(其他參數(shù)的狀態(tài)都舍棄)。
這個(gè)方法一般用于多個(gè)并行流程最終狀態(tài)的“歸并”中。
除了synchronize,框架還提供一些傳統(tǒng)promise編程模型沒有的改進(jìn),比如pipe4fail和source等。
四.在android項(xiàng)目中的應(yīng)用
(略)
https://github.com/jonenine/javaDeferred
posted @ 2017-07-23 11:59 溪石 閱讀(843) | 評論 (0) | 編輯 收藏