成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Dubbo 自定義異常,你是怎么處理的?

dingding199389 / 3702人閱讀

摘要:前言記錄對(duì)于自定義異常的處理方式實(shí)現(xiàn)目標(biāo)服務(wù)層異常,直接向上層拋出,層統(tǒng)一捕獲處理如果是系統(tǒng)自定義異常,則返回其中對(duì)應(yīng)為錯(cuò)誤碼,對(duì)應(yīng)為異常信息如果非系統(tǒng)自定義異常,返回未知錯(cuò)誤,同時(shí)將異常堆棧信息輸出到日志便于定位問(wèn)題項(xiàng)目架構(gòu)先來(lái)張系統(tǒng)架

前言

記錄Dubbo對(duì)于自定義異常的處理方式.

實(shí)現(xiàn)目標(biāo)

服務(wù)層異常,直接向上層拋出,web層統(tǒng)一捕獲處理

如果是系統(tǒng)自定義異常,則返回{"code":xxx,"msg":yyy} 其中code對(duì)應(yīng)為錯(cuò)誤碼,msg對(duì)應(yīng)為異常信息

如果非系統(tǒng)自定義異常,返回{"code":-1,"msg":"未知錯(cuò)誤"},同時(shí)將異常堆棧信息輸出到日志,便于定位問(wèn)題

項(xiàng)目架構(gòu)

先來(lái)張系統(tǒng)架構(gòu)圖吧,這張圖來(lái)源自網(wǎng)絡(luò),相信現(xiàn)在大部分中小企業(yè)的分布式集群架構(gòu)都是類似這樣的設(shè)計(jì):

簡(jiǎn)要說(shuō)明下分層架構(gòu):

通常情況下會(huì)有專門一臺(tái)堡壘機(jī)做統(tǒng)一的代理轉(zhuǎn)發(fā),客戶端(pc,移動(dòng)端等)訪問(wèn)由nginx統(tǒng)一暴露的入口

nginx反向代理,負(fù)載均衡到web服務(wù)器,由tomcat組成的集群,web層僅僅是作為接口請(qǐng)求的入口,沒(méi)有實(shí)際的業(yè)務(wù)邏輯

web層再用rpc遠(yuǎn)程調(diào)用注冊(cè)到zookeeperdubbo服務(wù)集群,dubbo服務(wù)與數(shù)據(jù)層交互,處理業(yè)務(wù)邏輯

前后端分離,使用json格式做數(shù)據(jù)交互,格式可以統(tǒng)一如下:

    {
        "code": 200,            //狀態(tài)碼:200成功,其他為失敗
        "msg": "success",       //消息,成功為success,其他為失敗原因
        "data": object     //具體的數(shù)據(jù)內(nèi)容,可以為任意格式
    }

映射為javabean可以統(tǒng)一定義為:

/**
 * @program: easywits
 * @description: http請(qǐng)求 返回的最外層對(duì)象
 * @author: zhangshaolin
 * @create: 2018-04-27 10:43
 **/
@Data
@JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
public class BaseResult implements Serializable{

    private static final long serialVersionUID = -6959952431964699958L;

    /**
     * 狀態(tài)碼:200成功,其他為失敗
     */
    public Integer code;

    /**
     * 成功為success,其他為失敗原因
     */
    public String msg;

    /**
     * 具體的內(nèi)容
     */
    public T data;
}

返回結(jié)果工具類封裝:

/**
 * @program: easywits
 * @description: http返回結(jié)果工具類
 * @author: zhangshaolin
 * @create: 2018-07-14 13:38
 **/
public class ResultUtil {

    /**
     * 訪問(wèn)成功時(shí)調(diào)用 包含data
     * @param object
     * @return
     */
    public static BaseResult success(Object object){
        BaseResult result = new BaseResult();
        result.setCode(200);
        result.setMsg("success");
        result.setData(object);
        return result;
    }

    /**
     * 訪問(wèn)成功時(shí)調(diào)用 不包含data
     * @return
     */
    public static BaseResult success(){
        return success(null);
    }

    /**
     * 返回異常情況 不包含data
     * @param code
     * @param msg
     * @return
     */
    public static BaseResult error(Integer code,String msg){
        BaseResult result = new BaseResult();
        result.setCode(code);
        result.setMsg(msg);
        return result;
    }
    
     /**
     * 返回異常情況 包含data
     * @param resultEnum 結(jié)果枚舉類 統(tǒng)一管理 code msg
     * @param object
     * @return
     */
    public static BaseResult error(ResultEnum resultEnum,Object object){
        BaseResult result = error(resultEnum);
        result.setData(object);
        return result;
    }

    /**
     * 全局基類自定義異常 異常處理
     * @param e
     * @return
     */
    public static BaseResult error(BaseException e){
        return error(e.getCode(),e.getMessage());
    }

    /**
     * 返回異常情況 不包含data
     * @param resultEnum 結(jié)果枚舉類 統(tǒng)一管理 code msg
     * @return
     */
    public static BaseResult error(ResultEnum resultEnum){
        return error(resultEnum.getCode(),resultEnum.getMsg());
    }
}

因此,模擬一次前端調(diào)用請(qǐng)求的過(guò)程可以如下:

web層接口

@RestController
@RequestMapping(value = "/user")
public class UserController {
    @Autowired
    UserService mUserService;
    @Loggable(descp = "用戶個(gè)人資料", include = "")
    @GetMapping(value = "/info")
    public BaseResult userInfo() {
        return mUserService.userInfo();
    }
}

服務(wù)層接口

 @Override
public BaseResult userInfo() {
    UserInfo userInfo = ThreadLocalUtil.getInstance().getUserInfo();
    UserInfoVo userInfoVo = getUserInfoVo(userInfo.getUserId());
    return ResultUtil.success(userInfoVo);
}

自定義系統(tǒng)異常

定義一個(gè)自定義異常,用于手動(dòng)拋出異常信息,注意這里基礎(chǔ)RuntimeException未受檢異常

簡(jiǎn)單說(shuō)明,RuntimeException及其子類為未受檢異常,其他異常為受檢異常,未受檢異常是運(yùn)行時(shí)拋出的異常,而受檢異常則在編譯時(shí)則強(qiáng)則報(bào)錯(cuò)
public class BaseException extends RuntimeException{

    private Integer code;

    public BaseException() {
    }

    public BaseException(ResultEnum resultEnum) {
        super(resultEnum.getMsg());
        this.code = resultEnum.getCode();
    }
    ...省略set get方法
}

為了方便對(duì)結(jié)果統(tǒng)一管理,定義一個(gè)結(jié)果枚舉類:

public enum ResultEnum {
    UNKNOWN_ERROR(-1, "o(╥﹏╥)o~~系統(tǒng)出異常啦!,請(qǐng)聯(lián)系管理員!!!"),
    SUCCESS(200, "success");
    
    private Integer code;
    
    private String msg;

    ResultEnum(Integer code, String msg) {
        this.code = code;
        this.msg = msg;
    }
}
web層統(tǒng)一捕獲異常

定義BaseController抽象類,統(tǒng)一捕獲由服務(wù)層拋出的異常,所有新增Controller繼承該類即可。

public abstract class BaseController {
    private final static Logger LOGGER = LoggerFactory.getLogger(BaseController.class);
    
       /**
     * 統(tǒng)一異常處理
     *
     * @param e
     */
    @ExceptionHandler()
    public Object exceptionHandler(Exception e) {
        if (e instanceof BaseException) {
            //全局基類自定義異常,返回{code,msg}
            BaseException baseException = (BaseException) e;
            return ResultUtil.error(baseException);
        } else {
            LOGGER.error("系統(tǒng)異常: {}", e);
            return ResultUtil.error(ResultEnum.UNKNOWN_ERROR);
        }
    }
}
驗(yàn)證

以上web層接口UserController繼承BaseController,統(tǒng)一捕獲異常

服務(wù)層假設(shè)拋出自定義系統(tǒng)異常BaseException,代碼如下:

 @Override
 public BaseResult userInfo() {
    UserInfo userInfo = ThreadLocalUtil.getInstance().getUserInfo();
    UserInfoVo userInfoVo = getUserInfoVo(userInfo.getUserId());
      if (userInfoVo != null) {
        //這里假設(shè)拋個(gè)自定義異常,返回結(jié)果{code:10228 msg:"用戶存在!"}
        throw new BaseException(ResultEnum.USER_EXIST);
    }
    return ResultUtil.success(userInfoVo);
}

然而調(diào)用結(jié)果后,上層捕獲到的異常卻不是BaseException,而被認(rèn)為了未知錯(cuò)誤拋出了.帶著疑問(wèn)看看Dubbo對(duì)于異常的處理

Dubbo異常處理

Dubbo對(duì)于異常有統(tǒng)一的攔截處理,以下是Dubbo異常攔截器主要代碼:

 @Override
    public Result invoke(Invoker invoker, Invocation invocation) throws RpcException {
        try {
            // 服務(wù)調(diào)用
            Result result = invoker.invoke(invocation);
            // 有異常,并且非泛化調(diào)用
            if (result.hasException() && GenericService.class != invoker.getInterface()) {
                try {
                    Throwable exception = result.getException();

                    // directly throw if it"s checked exception
                    // 如果是checked異常,直接拋出
                    if (!(exception instanceof RuntimeException) && (exception instanceof Exception)) {
                        return result;
                    }
                    // directly throw if the exception appears in the signature
                    // 在方法簽名上有聲明,直接拋出
                    try {
                        Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
                        Class[] exceptionClassses = method.getExceptionTypes();
                        for (Class exceptionClass : exceptionClassses) {
                            if (exception.getClass().equals(exceptionClass)) {
                                return result;
                            }
                        }
                    } catch (NoSuchMethodException e) {
                        return result;
                    }

                    // 未在方法簽名上定義的異常,在服務(wù)器端打印 ERROR 日志
                    // for the exception not found in method"s signature, print ERROR message in server"s log.
                    logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception);

                    // 異常類和接口類在同一 jar 包里,直接拋出
                    // directly throw if exception class and interface class are in the same jar file.
                    String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
                    String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
                    if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
                        return result;
                    }
                    // 是JDK自帶的異常,直接拋出
                    // directly throw if it"s JDK exception
                    String className = exception.getClass().getName();
                    if (className.startsWith("java.") || className.startsWith("javax.")) {
                        return result;
                    }
                    // 是Dubbo本身的異常,直接拋出
                    // directly throw if it"s dubbo exception
                    if (exception instanceof RpcException) {
                        return result;
                    }

                    // 否則,包裝成RuntimeException拋給客戶端
                    // otherwise, wrap with RuntimeException and throw back to the client
                    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
                } catch (Throwable e) {
                    logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
                            + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                            + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
                    return result;
                }
            }
            // 返回
            return result;
        } catch (RuntimeException e) {
            logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
                    + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
                    + ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
            throw e;
        }
    }

簡(jiǎn)要說(shuō)明:

有異常,并且非泛化調(diào)用時(shí),如果是受檢異常,則直接拋出

有異常,并且非泛化調(diào)用時(shí),在方法簽名上有聲明,則直接拋出

有異常,并且非泛化調(diào)用時(shí),異常類和接口類在同一 jar 包里,則直接拋出

有異常,并且非泛化調(diào)用時(shí),是Dubbo本身的異常(RpcException),則直接拋出

有異常,并且非泛化調(diào)用時(shí),剩下的情況,全部都會(huì)包裝成RuntimeException拋給客戶端

到現(xiàn)在問(wèn)題很明顯了,我們自定義的BaseException未受檢異常,況且不符合Dubbo異常攔截器中直接拋出的要求,Dubbo將其包裝成了RuntimeException,所以在上層BaseController中統(tǒng)一捕獲為系統(tǒng)未知錯(cuò)誤了.

解決辦法

異常類BaseException和接口類在同一 jar 包里,但是這種方式要在每個(gè)jar中放置一個(gè)異常類,不好統(tǒng)一維護(hù)管理

在接口方法簽名上顯式聲明拋出BaseException,這種方式相對(duì)簡(jiǎn)單一些,比較好統(tǒng)一維護(hù),只是每個(gè)接口都要顯式聲明一下異常罷了,這里我選擇這種方式解決

問(wèn)題

為什么一定要拋出自定義異常來(lái)中斷程序運(yùn)行,用return ResultUtil.error(ResultEnum resultEnum) 強(qiáng)制返回{code:xxx msg:xxx}結(jié)果,不是一樣可以強(qiáng)制中斷程序運(yùn)行?

玩過(guò)Spring的肯定都知道,Spring喲聲明式事物的概念,即在接口中添加事物注解,當(dāng)發(fā)生異常時(shí),全部接口執(zhí)行事物回滾..看下方的偽代碼:

@Transactional(rollbackFor = Exception.class)
public BaseResult handleData(){
    
    //1. 操作數(shù)據(jù)庫(kù),新增數(shù)據(jù)表A一條數(shù)據(jù),返回新增數(shù)據(jù)主鍵id
    
    //2. 操作數(shù)據(jù)庫(kù),新增數(shù)據(jù)庫(kù)B一條數(shù)據(jù),以數(shù)據(jù)表A主鍵id為外鍵關(guān)聯(lián)
    
    //3. 執(zhí)行成功 返回結(jié)果
}

該接口聲明了異常事物回滾,發(fā)送異常時(shí)會(huì)全部回滾

步驟1數(shù)據(jù)入庫(kù)失敗,理論上是拿不到主鍵id的,此時(shí)應(yīng)當(dāng)拋出自定義異常,提示操作失敗

如果步驟1數(shù)據(jù)入庫(kù)成功,步驟2中數(shù)據(jù)入庫(kù)失敗,那么理論上步驟1中的數(shù)據(jù)應(yīng)當(dāng)也要回滾,如果此時(shí)強(qiáng)制返回異常結(jié)果,那么步驟1入庫(kù)數(shù)據(jù)則成為臟數(shù)據(jù),此時(shí)拋出自定義異常是最合理的

最后的思考

在實(shí)際問(wèn)題場(chǎng)景中去閱讀源碼是最合適的,帶著問(wèn)題有目的的看指定源碼會(huì)讓人有豁然開(kāi)朗的感覺(jué).

更多原創(chuàng)文章會(huì)第一時(shí)間推送公眾號(hào)【張少林同學(xué)】,歡迎關(guān)注!

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/72830.html

相關(guān)文章

  • 前后端分離應(yīng)用——用戶信息傳遞

    摘要:前言記錄前后端分離的系統(tǒng)應(yīng)用下應(yīng)用場(chǎng)景用戶信息傳遞需求緣起照例先看看系統(tǒng)的一張經(jīng)典架構(gòu)圖,這張圖參考自網(wǎng)絡(luò)在自定義異常,你是怎么處理的中已經(jīng)對(duì)該架構(gòu)做了簡(jiǎn)單說(shuō)明,這里不再描述。 showImg(https://segmentfault.com/img/remote/1460000017839927?w=1024&h=768); 前言 記錄前后端分離的系統(tǒng)應(yīng)用下應(yīng)用場(chǎng)景————用戶信息傳...

    PAMPANG 評(píng)論0 收藏0
  • 瀏覽器如何解析html、css、js

    摘要:全局變量局部變量全局函數(shù)一段也是一塊域。此時(shí)打印的自然是,要記住相當(dāng)于,所以這時(shí)候改變的是局部變量,并沒(méi)有影響到全局變量,所以第二次打印的依然是。 在熟悉了瀏覽器的工作原理之后,今天我們來(lái)講講瀏覽器在從服務(wù)器獲取到網(wǎng)頁(yè)文件之后是如何解析的。了解了這個(gè)基礎(chǔ)知識(shí),對(duì)敲出來(lái)的代碼,質(zhì)量會(huì)有不小的提升。 一、瀏覽器如何解析html html文件在沒(méi)有寫(xiě)入html標(biāo)簽之前和txt文本是一個(gè)性質(zhì)的...

    Awbeci 評(píng)論0 收藏0
  • 瀏覽器如何解析html、css、js

    摘要:全局變量局部變量全局函數(shù)一段也是一塊域。此時(shí)打印的自然是,要記住相當(dāng)于,所以這時(shí)候改變的是局部變量,并沒(méi)有影響到全局變量,所以第二次打印的依然是。 在熟悉了瀏覽器的工作原理之后,今天我們來(lái)講講瀏覽器在從服務(wù)器獲取到網(wǎng)頁(yè)文件之后是如何解析的。了解了這個(gè)基礎(chǔ)知識(shí),對(duì)敲出來(lái)的代碼,質(zhì)量會(huì)有不小的提升。 一、瀏覽器如何解析html html文件在沒(méi)有寫(xiě)入html標(biāo)簽之前和txt文本是一個(gè)性質(zhì)的...

    Panda 評(píng)論0 收藏0
  • 瀏覽器如何解析html、css、js

    摘要:全局變量局部變量全局函數(shù)一段也是一塊域。此時(shí)打印的自然是,要記住相當(dāng)于,所以這時(shí)候改變的是局部變量,并沒(méi)有影響到全局變量,所以第二次打印的依然是。 在熟悉了瀏覽器的工作原理之后,今天我們來(lái)講講瀏覽器在從服務(wù)器獲取到網(wǎng)頁(yè)文件之后是如何解析的。了解了這個(gè)基礎(chǔ)知識(shí),對(duì)敲出來(lái)的代碼,質(zhì)量會(huì)有不小的提升。 一、瀏覽器如何解析html html文件在沒(méi)有寫(xiě)入html標(biāo)簽之前和txt文本是一個(gè)性質(zhì)的...

    Salamander 評(píng)論0 收藏0
  • 深度學(xué)習(xí)研究綜述

    摘要:此原因在一定程度上阻礙了深度學(xué)習(xí)的發(fā)展,并將大多數(shù)機(jī)器學(xué)習(xí)和信號(hào)處理研究,從神經(jīng)網(wǎng)絡(luò)轉(zhuǎn)移到相對(duì)較容易訓(xùn)練的淺層學(xué)習(xí)結(jié)構(gòu)。深度學(xué)習(xí)算法可以看成核機(jī)器學(xué)習(xí)中一個(gè)優(yōu)越的特征表示方法。 摘要:深度學(xué)習(xí)是一類新興的多層神經(jīng)網(wǎng)絡(luò)學(xué)習(xí)算法。因其緩解了傳統(tǒng)訓(xùn)練算法的局部最小性, 引起機(jī)器學(xué)習(xí)領(lǐng)域的廣泛關(guān)注。首先論述了深度學(xué)習(xí)興起淵源, 分析了算法的優(yōu)越性, 并介紹了主流學(xué)習(xí)算法及應(yīng)用現(xiàn)狀,最后總結(jié)當(dāng)前存在的...

    jokester 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

dingding199389

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<