motan rpc 接口统一异常处理

1.hello word 一个Motan扩展

大概需要下面的三点:

  1. 实现SPI扩展点接口

    package com.weibo.api.motan.filter;
    @Spi
    public interface Filter {
        Response filter(Caller<?> caller, Request request);
    }
    

业务代码实现Filter

public class PlsProviderExceptionFilter implements Filter {
        @Override
    public Response filter(Caller<?> caller, Request request) {
        //实现具体的业务逻辑
    }
}
  1. 实现类增加注解

    @Spi(scope = Scope.SINGLETON)  //扩展加载形式,单例或多例
    @SpiMeta(name = "motan")  //name表示扩展点的名称,根据name加载对应扩展
    @Activation(sequence = 100) //同类型扩展生效顺序,部分扩展点支持。非必填
    
    @SpiMeta(name = "pls-exception-filter")
    //sequence默认20,越大越先执行完
    @Activation(sequence = 100, key = {MotanConstants.NODE_TYPE_SERVICE})
    public class PlsProviderExceptionFilter implements Filter {
    	//...
    }
    

    在这里插入图片描述

  2. 增加SPI实现声明 c l a s s p a t h / M E T A − I N F / s e r v i c e s / {classpath}/META-INF/services/ classpath/METAINF/services/{SPI interface fullname}文件中添加对应SPI接口实现类全名。 可参照motan-core模块/META-INF/services/下的配置

    #扩展接口
    com.sxl.motan.PlsProviderExceptionFilter
    

官方对moten扩展机制介绍

filter机制是在client端与server端请求处理是都会经过的过滤机制,使用者可以通过filter来实现对请求的request和response进行定制化处理。 filter扩展的实现方式是通过上述的SPI扩展,使用中有如下问题需要注意。

1、如何使扩展的filter生效。

SPI扩展必须对应一个唯一的name,实际使用中通过name来加载扩展。一般扩展需要在配置中设置对应的name,然后自动加载。

filter扩展默认是不生效的,加载filter的方式分为强制加载和指定加载。如果需要强制生效,可以通过配置@Activation来实现,MotanConstants.NODE_TYPE_SERVICE表示在server端强制生效,MotanConstants.NODE_TYPE_REFERER表示在client端强制生效。 强制生效情况下,不管任何service都会进行filter过滤。 配置样例如下:

@Activation(key = { MotanConstants.NODE_TYPE_SERVICE, MotanConstants.NODE_TYPE_REFERER })

如果只需要在某些service中生效,则可以直接通过在basicService或者service中配置 filter="filter_name"即可,其中的filter_name就是扩展filter中声明的@SpiMeta(name = “***”)中name的值

推荐通过配置方式使用filter,减少不必要的filter装载

2.实现motan异常统一处理核心逻辑

2.1自定义异常类定义

在这里插入图片描述

异常枚举

用于规范异常,各个模块维护自己的异常编码。对应的异常编码可以同步 [帮助系统并给出解决方案。方便***用户查找解决***

package com.sxl.exception.errorCode;

/**
 * 系统各平台业务异常枚举
 * @ClassName: com.sxl.exception.PlsBizErrorCode.java
 * @author: songxulin
 * @date :  2021-01-04 15:37
 * @version V1.0
 */
public enum PlsBizErrorCode {
    /************************** 公共异常 *****************************/
    E_000000("000000","fail","失败"),E_000001("000001","success","成功"),E_000002("000002","parameterError","请求参数为空"),
    E_000003("000003","parameterInvalid","请求参数非法"),E_000004("000004","jsonError","JSON转换失败"),E_000005("000005","dbError","数据库异常"),
    E_000006("000006","networkError","网络异常"),E_000007("000007","unkownError","未知异常"),E_000008("000008","handleDataException","数据处理异常"),
    E_000009("000009","existed","数据已存在"), E_000010("000010","numberFormatException","数值转换异常"),E_000011("000011","NullPointerException","数据空指针异常"),
    /************************** 通用模块异常  ******************************/
    P_C00001("P_C00001","skuParseError","sku解析异常"),
    ;
    private String code;
    private String name;
    private String desc;
    PlsBizErrorCode(String code, String name, String desc) {
        this.code = code;
        this.name = name;
        this.desc = desc;
    }
    @Override
    public String getCode() {
        return code;
    }
    @Override
    public String getName() {
        return name;
    }
    @Override
    public String getDesc() {
        return desc;
    }
}
异常抽象类

异常统一继承此类

package com.sxl.exception;

import com.sxl.exception.errorCode.ErrorCode;

/**
 * 刊登系统异常抽象类
 * @ClassName: com.sxl.exception.AbstractException.java
 * @author: songxulin
 * @date :  2021-01-04 17:27
 * @version V1.0
 */
public abstract class AbstractPLSException extends RuntimeException{
    private static final long serialVersionUID = -4470524790791804455L;
    private String state;
    private String name;
    private void init(String code, String name){
        this.state = code;
        this.name = name;
    }
    private void init(ErrorCode errorCode){
        init(errorCode.getCode(), errorCode.getName());
    }
    private AbstractPLSException(String message) {
        super(message);
    }
    private AbstractPLSException(String message, Throwable cause){
        super(message,cause);
    }
    public AbstractPLSException(String code, String name, String desc){
        this(desc);
        init(code, name);
    }
    public AbstractPLSException(String code, String name, String desc, Throwable cause{
        this(desc, cause);
        init(code, name);
    }
    public AbstractPLSException(ErrorCode errorCode){
        this(errorCode.getDesc());
        init(errorCode);
    }
    public AbstractPLSException(ErrorCode errorCode, String desc){
        this(desc);
        init(errorCode);
    }
    public AbstractPLSException(ErrorCode errorCode, Throwable cause){
        this(errorCode.getDesc(), cause);
        init(errorCode);
    }
    public String getState() {
        return state;
    }
    public String getName() {
        return name;
    }
    public String getStateMsg(){
        return state+":"+getMessage();
    }
}
自定义异常
package com.sxl.exception;

import com.sxl.exception.errorCode.ErrorCode;

/**
 * 刊登系统业务异常
 * @ClassName: com.sxl.exception.PlsBizException.java
 * @author: songxulin
 * @date :  2021-01-04 15:36
 * @version V1.0
 */
public class PlsBizException extends AbstractPLSException {
    private static final long serialVersionUID = 5600133107550376666L;

    public PlsBizException(String code, String name, String desc) {
        super(code, name, desc);
    }
    public PlsBizException(ErrorCode errorCode, String desc) {
        super(errorCode,desc);
    }
    public PlsBizException(ErrorCode errorCode) {
        super(errorCode);
    }
    public PlsBizException(ErrorCode errorCode, Throwable cause) {
        super(errorCode, cause);
    }
}
异常枚举
package com.sxl.exception;

import com.alibaba.fastjson.JSONException;
import com.sxl.exception.errorCode.ErrorCode;
import com.sxl.exception.errorCode.PlsBizErrorCode;
import java.util.HashMap;
import java.util.Map;
/**
 * 异常枚举
 * @ClassName: com.sxl.exception.ExceptionEnum.java
 * @author: songxulin
 * @date :  2021-01-04 19:38
 * @version V1.0
 */
public enum ExceptionEnum {
    JSONException("JSONException", JSONException.class, PlsBizErrorCode.E_000004),
    NumberFormatException("NumberFormatException",NumberFormatException.class, PlsBizErrorCode.E_000010),
    NullPointerException("NullPointerException",NullPointerException.class, PlsBizErrorCode.E_000010),
    IllegalArgumentException("IllegalArgumentException",IllegalArgumentException.class,PlsBizErrorCode.E_000008),
    ;
    private String name;
    private Class clazz;
    private ErrorCode errorCode;
    private static Map<Class,ExceptionEnum> enumMap =new HashMap<>(16);
    static {
        for (ExceptionEnum value : ExceptionEnum.values()) {
            enumMap.put(value.clazz,value);
        }
    }
    ExceptionEnum(String name, Class clazz, ErrorCode errorCode) {
        this.name = name;
        this.clazz = clazz;
        this.errorCode = errorCode;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Class getClazz() {
        return clazz;
    }
    public void setClazz(Class clazz) {
        this.clazz = clazz;
    }
    public ErrorCode getErrorCode() {
        return errorCode;
    }
    public void setErrorCode(ErrorCode errorCode) {
        this.errorCode = errorCode;
    }
    /**
     * 根据异常类获取对应异常
     * @param e Class 异常class
     * @return ExceptionEnum
     */
    public static ExceptionEnum getByClazz(Class e){
        return enumMap.get(e);
    }
}

2.2 filter编写

package com.sxl.motan;

import com.alibaba.fastjson.JSON;
import com.weibo.api.motan.common.MotanConstants;
import com.weibo.api.motan.core.extension.Activation;
import com.weibo.api.motan.core.extension.SpiMeta;
import com.weibo.api.motan.exception.MotanAbstractException;
import com.weibo.api.motan.filter.Filter;
import com.weibo.api.motan.rpc.Caller;
import com.weibo.api.motan.rpc.DefaultResponse;
import com.weibo.api.motan.rpc.Request;
import com.weibo.api.motan.rpc.Response;
import com.sxl.exception.AbstractPLSException;
import com.sxl.exception.ExceptionEnum;
import com.sxl.exception.PLSServiceException;
import com.sxl.vo.ResultVO;
import com.sxl.vo.VoHelper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * 
 * @ClassName: com.sxl.motan.PlsProviderExceptionFilter.java
 * @author: songxulin
 * @date :  2021-01-04 20:09
 * @version V1.0
 */
@SpiMeta(name = "pls-exception-filter")
//sequence默认20,越大越先执行完
@Activation(sequence = 100, key = {MotanConstants.NODE_TYPE_SERVICE})
public class PlsProviderExceptionFilter implements Filter {
    private final static Logger log = LoggerFactory.getLogger(PlsProviderExceptionFilter.class);

    @Override
    public Response filter(Caller<?> caller, Request request) {
        Response response;
        try {
            response = caller.call(request);
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            return buildExceptionResponse(e);
        }
        if (response.getException() != null) {
            log.error(String.format("%s,%s 调用异常",request.getAttachments().get("host"),request.getMethodName()),response.getException().getCause());
            return buildExceptionResponse(response);
        }
        return response;
    }
    /**
     * 构建异常Response
     * @param e 异常
     * @return Response
     */
    private Response buildExceptionResponse(Exception e) {
        DefaultResponse response = new DefaultResponse();
        AbstractPLSException ex;
        //自定义异常
        if (e instanceof MotanAbstractException) {
            ex = getException((MotanAbstractException) e);
        } else {//系统未知异常
            ex = new PLSServiceException("", "", "系统未知异常");
        }
        response.setValue(JSON.toJSONString(formatError(ex)));
        return response;
    }
    /**
     * 获取异常类型
     * @param e 异常
     * @return AbstractPLSException
     */
    private AbstractPLSException getException(MotanAbstractException e) {
        Throwable cause = e.getCause();
        if (cause instanceof AbstractPLSException) {
            return (AbstractPLSException) cause;
        } else {
            ExceptionEnum exceptionEnum = ExceptionEnum.getByClazz(cause.getClass());
            if(exceptionEnum!=null){
                return new PLSServiceException(exceptionEnum.getErrorCode());
            }
            return new PLSServiceException("", "", "系统未知异常");
        }
    }
    /**
     * 格式化异常
     * @param e AbstractPLSException
     * @return ResultVO 自定义返回体
     */
    private ResultVO formatError(AbstractPLSException e) {
        return VoHelper.getErrorResult(e.getStateMsg());
    }
    private Response buildExceptionResponse(Response response) {
        return buildExceptionResponse(response.getException());
    }
}

完成上面的代码后,motan接口统一处理就完成了。

3.使用

1. 抛出异常
throw new PlsBizException(PlsBizErrorCode.P_YA0001);
2. motan门面类

异常不进行抓取 ,需要处理异常的地方进行抛出 throws Exception

没有统一处理前

@Override
public String addListing(String jsonParam) {
    LOGGER.info("新增listing接口被请求,请求参数jsonParam:{}", jsonParam);
    ResultVO resultVO = new ResultVO();
    try {
       //...
        resultVO = iPlsAliexpressListingService.addListing(data, operator);

    } catch (ValidatorParameterException vpe) {
        resultVO.setMsg(vpe.getMessage());
        resultVO.setState(ResponseCode.FAIL.getCode());
    } catch (WarningException e) {
        resultVO.setMsg(e.getMessage());
        resultVO.setState(ResponseCode.WARNING.getCode());
        LOGGER.error("AliExpress addListing 新增Listing  侵权检测警告,{} ", e.getMessage(), e);
    } catch (Exception e) {
        resultVO.setMsg(e.getMessage());
        resultVO.setState(ResponseCode.FAIL.getCode());
        LOGGER.error("新增listing接口异常", e);
    }
    return JSONUtils.toJSON(resultVO);
}

统一处理后

 @Override
    public String addListing(String jsonParam)  throws Exception{
         LOGGER.info("新增listing接口被请求,请求参数jsonParam:{}", jsonParam);
    	//..
        ResultVO resultVO = iPlsAliexpressListingService.addListing(data, operator);
    }

可以看到代码变得简洁了很多,而且异常的格式也得到规范。

} catch (Exception e) {
    resultVO.setMsg(e.getMessage());
    resultVO.setState(ResponseCode.FAIL.getCode());
    LOGGER.error("新增listing接口异常", e);
}
return JSONUtils.toJSON(resultVO);

}


统一处理后

```java
 @Override
    public String addListing(String jsonParam)  throws Exception{
         LOGGER.info("新增listing接口被请求,请求参数jsonParam:{}", jsonParam);
    	//..
        ResultVO resultVO = iPlsAliexpressListingService.addListing(data, operator);
    }

可以看到代码变得简洁了很多,而且异常的格式也得到规范。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
HTTP接口RPC接口是两种常见的网络通信协议。 HTTP(Hypertext Transfer Protocol)是一种基于请求-响应模式的协议,通常用于在客户端和服务器之间传输超文本数据。HTTP接口使用HTTP协议进行通信,通过URL和HTTP方法(如GET、POST、PUT、DELETE)来定义请求的资源和操作。 RPC(Remote Procedure Call)是一种远程过程调用协议,用于在不同的计算机之间进行通信。RPC接口通过定义接口和方法来进行通信,客户端可以像调用本地方法一样调用远程服务器上的方法,而不需要关心底层的网络通信细节。 区别: 1. 语义:HTTP接口主要用于传输超文本数据,适用于Web应用程序的开发;而RPC接口更侧重于远程方法调用,适用于分布式系统的开发。 2. 通信方式:HTTP接口使用RESTful风格,通过URL和HTTP方法进行通信;而RPC接口使用自定义的协议进行通信,可以支持各种传输协议,如TCP、UDP等。 3. 序列化方式:HTTP接口通常使用JSON或XML等文本格式进行数据序列化;而RPC接口可以使用更高效的二进制序列化方式,如Protocol Buffers、Thrift等。 4. 技术栈:HTTP接口可以使用各种Web框架进行开发,如Spring MVC、Django等;而RPC接口通常需要使用专门的RPC框架,如gRPC、Dubbo等。 需要根据具体的应用场景和需求选择合适的接口类型,HTTP接口更适合Web应用的前后端通信,RPC接口更适合分布式系统的服务间通信。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值