国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
如何做一個(gè)簡(jiǎn)單的開放接口(2)-核心引擎(上)

1、要實(shí)現(xiàn)的功能

書接上回,本回書我們要完成開放接口平臺(tái)核心引擎的多Handler支持機(jī)制。

如圖1所示。


圖1 開放接口服務(wù)器端架構(gòu)

2、Filter還是裝飾模式

裝飾者模式貌似是一個(gè)實(shí)現(xiàn)的候選,類似Java的I/O實(shí)現(xiàn)。
多“裝飾”一層,就獲得了新的功能,原來的功能還在。

對(duì)我現(xiàn)在的應(yīng)用場(chǎng)景來說,這種實(shí)現(xiàn)方式過于復(fù)雜了。
相對(duì)而言,F(xiàn)ilter更簡(jiǎn)潔。

當(dāng)前的應(yīng)用場(chǎng)景對(duì)性能是有極高要求的,不適合使用哪怕稍微復(fù)雜的模式。

3、Handler接口定義

我的Handler接口定義如下。

public interface Handler {    public void inWay(HttpServletRequest request,HttpServletResponse response);     public void outWay(HttpServletRequest request,HttpServletResponse response);}
  • 1
  • 2
  • 3
  • 4

更有節(jié)操的童鞋會(huì)自己定義Request、Response0,甚至Context對(duì)象。以便脫離Web容器的限制,進(jìn)一步實(shí)現(xiàn)自己的底層通信協(xié)議。

我這里先偷個(gè)懶,等有時(shí)間了慢慢來。

Handler接口中,inWay方法對(duì)應(yīng)圖1左側(cè)的向下箭頭,outWay對(duì)應(yīng)右側(cè)的向上箭頭。

這樣,在同一個(gè)Handler定義進(jìn)、的邏輯。
對(duì)于實(shí)現(xiàn)序列化功能的Handler,inWay中實(shí)現(xiàn)反序列化,outWay中實(shí)現(xiàn)序列化。
對(duì)于實(shí)現(xiàn)加密功能的Handler,inWay中實(shí)現(xiàn)解密,outWay中實(shí)現(xiàn)加密。
對(duì)于實(shí)現(xiàn)壓縮功能的Handler,inWay中實(shí)現(xiàn)解壓縮的邏輯,outWay中實(shí)現(xiàn)壓縮的邏輯。

這樣,當(dāng)不需要某個(gè)Handler的時(shí)候,直接去掉就好了。

當(dāng)然,outWay中可以do nothing。

另外,非常重要的,Handler實(shí)現(xiàn)類不可以有自己的屬性。Handler實(shí)例不能有“狀態(tài)”。
我們需要Handler是線程安全的。

3、可配置

多個(gè)Handler是可配置的,每個(gè)Handler鏈可以服務(wù)于一個(gè)或多個(gè)接口。

在Handler無狀態(tài)、線程安全的基礎(chǔ)上,我們可以采用在每個(gè)JVM中Handler單例的方式,避免頻繁創(chuàng)建、回收Handler對(duì)象的損失。

配置信息可以保存在properties文件中,可以保存在xml中,可以保存在數(shù)據(jù)中,范例如下:

openapi.handler.keys=surface,encrypt,authopenapi.handler.surface=cn.hailong.common.openapi.handler.SurfaceHandleropenapi.handler.encrypt=cn.hailong.common.openapi.handler.EncryptHandleropenapi.handler.auth=cn.hailong.common.openapi.handler.AuthHandler
  • 1
  • 2
  • 3
  • 4

配置系統(tǒng)中可能用到的所有Handler,并在系統(tǒng)啟動(dòng)時(shí)加載。

在上面的配置中,配置了每個(gè)Handler的類名,加載的時(shí)候,可以根據(jù)類名創(chuàng)建類的實(shí)例。
給每個(gè)Handler起了一個(gè)短名稱,便于在配置Handler鏈的時(shí)候引用。

對(duì)應(yīng)的加載代碼為:

public class HandlerManager {       /**     * 保存系統(tǒng)中的所有Handler,key為handler短名,value為Handler實(shí)例。     */    private static Map<String, Handler> handlersMap = new ConcurrentHashMap<String, Handler>();    static{        reloadHandlers();    }    public static synchronized void reloadHandlers(){        handlersMap.clear();        logger.info("Open Api Handlers load start ... ");        long begin = System.currentTimeMillis();        Properties props = ConfigManager.getProperties("openapi");        String handlerKeys = props.getProperty("openapi.handler.keys");        logger.debug("loading handlers : "+handlerKeys);        Handler handler = null;        if(!StringUtils.isEmpty(handlerKeys)){            String[] handlerKeyArray = handlerKeys.split(",");            if(handlerKeyArray!=null && handlerKeyArray.length>0){                for (String handlerKey : handlerKeyArray) {                    String propertiesKey = "openapi.handler."+handlerKey;                    String handlerClassName = props.getProperty(propertiesKey);                    if(StringUtils.isEmpty(handlerClassName)){                        continue;                    }                    handlerClassName = handlerClassName.trim();                    Class<?> clz = Class.forName(className);                    handler = BeanUtil.newInstance(Handler.class,clz);                    if(handler!=null){                        handlersMap.put(handlerKey,handler);                        logger.debug(String.format("handler[%s] loaded : %s ", handlerKey,handler));                    }                }            }        }        logger.info("Open Api Handlers load end , time costs "+(System.currentTimeMillis()-begin));    }    /**     *     */    public static Hanlder get(String shortName){        return handlerMap.get(shortName);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55

修改了Handler的配置,需要重新加載的時(shí)候,也無需重啟服務(wù)器(在生產(chǎn)環(huán)境,這非常重要),再次調(diào)用HandlerManager.reloadHandlers()即可。

接下來是Handler鏈的配置,配置范例如下:

openapi.handler.chain.keys=full,idleopenapi.handler.chain.full=surface,encrypt,auth,traffic,config,validateopenapi.handler.chain.idle=idle,idle,idle,idle,idle
  • 1
  • 2
  • 3

加載邏輯與HandlerManager中代碼邏輯類似。

4、Handler執(zhí)行過程

Handler的執(zhí)行過程如圖1所示。

public class HandlerChain {    protected List<Handler> handlersList = null;    protected List<Handler> handlersReversedList = null;    protected Iterator<Handler> inIterator = null;    protected Iterator<Handler> outIterator = null;    public HandlerChain(List<Handler> handlers) {        setHandlers(handlers);        reset();    }    protected void setHandlers(List<Handler> handlers) {        if (handlers == null) {            return;        }        // 正向        this.handlersList = handlers;        // 反向        if (handlersReversedList == null) {            handlersReversedList = new ArrayList<Handler>();        } else {            handlersReversedList.clear();        }        for (int idx = handlers.size() - 1; idx > -1; --idx) {            handlersReversedList.add(handlers.get(idx));        }    }    public void reset() {        if (handlersList != null) {            inIterator = handlersList.iterator();        }        if (handlersReversedList != null) {            outIterator = handlersReversedList.iterator();        }    }    public void inWay(HttpServletRequest request, HttpServletResponse response) {        Handler nextHandler = null;        if (inIterator != null && inIterator.hasNext()) {            nextHandler = inIterator.next();        }        if (nextHandler != null) {            nextHandler.inWay(request, response);            this.inWay(request, response);//遞歸調(diào)用        } else {            logger.debug(String.format("In End Time:%s.",(System.currentTimeMillis() - this.time)));        }    }    public void outWay(HttpServletRequest request, HttpServletResponse response) {        Handler nextHandler = null;        if (outIterator != null && outIterator.hasNext()) {            nextHandler = outIterator.next();        }         if (nextHandler != null) {            nextHandler.outWay(request, response);            this.outWay(request, response);        } else {            logger.debug(String.format("Out End Time:%s.",(System.currentTimeMillis() - this.time)));        }    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66

HandlerChain 是有狀態(tài)的,對(duì)每個(gè)請(qǐng)求創(chuàng)建一個(gè)實(shí)例。
調(diào)用 handlerChainInstance.inWay(req,resp)則執(zhí)行了相應(yīng)Handler鏈的所有inWay方法。
調(diào)用 handlerChainInstance.outWay(req,resp)則執(zhí)行了相應(yīng)Handler鏈的所有outWay方法。

5、消息格式參考實(shí)現(xiàn)

對(duì)接口的調(diào)用類似對(duì)方法的調(diào)用,傳入?yún)?shù)包括:接口名稱、參數(shù)值,傳出參數(shù)可能是返回值,可能是異常消息。

定義如下接口。

interface RpcMessage{    Object getMeta();    /**     * @return 調(diào)用接口的名稱。     */    String getMethod();    /**     * @return 傳入的參數(shù)值。     */    List<Object> getParams();    /**     * @return 返回值。     */    Object getResult();    /**     * @return 異常信息。     */    Object getError();}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

Meta中包含可能的調(diào)用者信息,授權(quán)信息等。

對(duì)于這樣的數(shù)據(jù)結(jié)構(gòu),json-rpc(http://json-rpc.org/wiki/specification)是一個(gè)簡(jiǎn)單易用的序列化方案。
雖然解析效率不高,但json足夠簡(jiǎn)單,作為參考實(shí)現(xiàn)是個(gè)好選擇。在生產(chǎn)環(huán)境中,需要有更成熟的考慮。

采用json-rpc作為持久化方案的情況下。

請(qǐng)求信息可能如下所示:

{meta:{token:'765959559266'},method:'getUserInfo',params:['32899688']}
  • 1

返回信息可能如下所示:

{result:{name:'劉海龍',addr:'人民大學(xué)北路'}}
  • 1

{error:{code:'AUTH_ERR',msg:'Token過期'}}
  • 1

對(duì)于每次請(qǐng)求應(yīng)該創(chuàng)建一個(gè) RpcMessage 的實(shí)例,可以保存在 Request 中,或者 ThreadLocal 中。

6、異常處理

現(xiàn)在需要考慮一個(gè)稍復(fù)雜的問題。
觀察圖1,考慮各個(gè)環(huán)節(jié)拋出異常的時(shí)候應(yīng)該如何應(yīng)對(duì)。考慮授權(quán)失敗,或超出流量控制時(shí)應(yīng)該如何應(yīng)對(duì)。

首先明確一個(gè)問題,客戶端是按照outWay的配置定制的。
outWay中如果有序列化和加密,客戶端就會(huì)解密和反序列化。
生產(chǎn)環(huán)境采用的序列化方案一般是二進(jìn)制的,開發(fā)環(huán)境可能采用JSON或XML。
加解密一般會(huì)針對(duì)不同用戶采用不同的Key。

返回的消息如果沒有經(jīng)過特定格式的序列化或者加密,客戶端將無法讀取消息。

所以,我們得到的第一個(gè)結(jié)論是:異常消息也要報(bào)所有的Handler的outWay方法走一遍。

接下來,對(duì)圖1流程中的每個(gè)環(huán)節(jié)逐個(gè)考慮。

6.1、inWay過程中的異常

通過如下代碼可以捕捉到inWay過程中的異常,并保存到 rpcMessage 實(shí)例中。

try{    handlerChainInstance.inWay(req,resp)}catch(Throwable e){    rpcMessage.addError("PRE_INVOKE_ERR",e.getMessage());}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

接下來,如果inWay中出現(xiàn)了異常,則跳過調(diào)用 業(yè)務(wù)邏輯 對(duì)象的代碼。直接執(zhí)行 outWay ,在outWay執(zhí)行過程中,根據(jù)配置,該怎么序列化怎么序列化,該怎么加密怎么加密。

有一種情況是這樣的,假如加密需要明確客戶身份,才知道用哪個(gè)Key,但請(qǐng)求中未包含用戶身份信息或者用戶身份信息無效,這可怎么辦?
為了解決這個(gè)問題,按如下兩點(diǎn)做:
第一,用戶身份標(biāo)識(shí)不要在消息體中傳遞。否則識(shí)別不了身份,用什么Key解密都不知道,讀不出來。
第二,身份無效的錯(cuò)誤消息,定義特殊代碼,明文傳??蛻舳嗽趫?zhí)行所有Handler之前,先判斷是不是這個(gè)錯(cuò)誤。

6.2、調(diào)用業(yè)務(wù)邏輯過程中發(fā)生的異常

這個(gè)最簡(jiǎn)單,捕捉住,放到 rpcMessage 就可以了。
然后該怎么走outWay就怎么走。

6.3、outWay過程中的異常

這個(gè)最難處理。

應(yīng)在開發(fā)過程中極力排除,并避免。

萬一發(fā)生了,比如在加密、壓縮過程中異常了,這時(shí)候也只能返回預(yù)定的消息,告訴調(diào)用方:“服務(wù)器出錯(cuò)了,客官請(qǐng)聯(lián)系店小二”。

預(yù)定義的消息,根據(jù) Handler鏈的配置自動(dòng)生成,代碼如下所示。

private static byte[] outWayErrorMessage = null;outWayErrorMessage = buildResponseErrorMessage(handerList,"POST_ERR","服務(wù)器出錯(cuò)了,客官請(qǐng)聯(lián)系店小二");
  • 1
  • 2
  • 3
本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
開放平臺(tái)文檔中心
C#調(diào)用用友U8OpenAPI體驗(yàn)
TeaDSL:支持任意 OpenAPI 網(wǎng)關(guān)的多語言 SDK 方案
OpenAPI EAI API
Hashtable和HashMap的區(qū)別
Android中定時(shí)器Timer和TimerTask的啟動(dòng),停止,暫停,繼續(xù)等操作實(shí)例
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服