rpc請求&響應參數規範

§0. 有話先說

0.1 幾個概念html

api-應用程序接口,最狹隘的定義,指的是咱們程序裏類或接口的方法。java

rpc-局域網內部跨應用通訊框架。常見的有dubbo、thrift、HSF、Feign。json

rpcapi-基本上能夠跟程序內部api同樣用法的api。api

0.2 someword:框架

下面說的參數,包括請求參數和返回參數。maven

  1. 方法的參數,一般不建議使用Object、Map,不易讀。
  2. 方法的參數,若是都用String,好比JSON字符串或其餘拼裝起來的字符串,不易讀是一方面,同時,潛在問題更多,你品。
  3. rpcapi,建議自行處理異常,別拋給調用方。
  4. 方法的參數,使用枚舉會提升可讀性 @see
  5. 。。。

0.3 爲何有本文?ide

書接前文,rpcapi雖然能夠像程序內部api同樣,支持java各類數據類型,能夠像調用程序內部api同樣調用rpcapi。不過,所不一樣的是,rpcapi應該處理各類可能的異常狀況,而不是拋出異常。這使得rpcapi與restapi同樣,不能只是返回數據(資源),而應該對可能出現的異常狀況進行判斷,好比參數合法性,數據是否存在,數據狀態,程序異常,等等,當沒有異常狀況時,才返回所需數據(資源)。所以,就有了本文下文說的Result<T>。this

§1. 請求參數

1.1 若是參數比較少,好比少於3個,能夠顯式定義出來。好比spa

getEnterpriseById(String enterpriseId)rest

getEnterpriseById(String enterpriseId,ProductEnum product)

 

1.2 若是參數超過3個,建議定義一個DTO。rpc傳輸對象暫定統一命名爲DTO。請求dto命名建議以ReqDTO結尾。固然,若是請求響應都使用相同的dto的話,就直接以DTO結尾也何嘗不可。好比

addEnterprise(EnterpriseDTO enterprise);

selectEnterprise(EnterpriseDTO enterprise);

§2. 響應參數

2.1 首先,響應參數統一使用Result<T>。即,返回值統一使用泛型。Result<T>主要成員有3個:

  • int code - 響應碼,成功統一是200. 對應的code枚舉定義在ResultCodeEnum.java裏
  • String message - 響應描述,尤爲是當非200的狀況下,須要指出錯誤信息。
  • T result - 響應數據。通常在code=200的狀況下會設定result。

Result<T>相關操做方法後文贅述。

2.2 其次,對於返回數據來講,同1.一、1.2所述。

2.2.1 若是比較單一,好比就返回一個交易量,能夠是Result<Integer> selectTransCount(...)。

2.2.2 若是返回數據比較複雜,能夠定義一個DTO,響應dto命名建議以RespDTO結尾。固然,若是請求響應都使用相同的dto的話,就直接以DTO結尾也何嘗不可。

2.2.2.1 若是你不肯意定義一個dto對象,也行。

能夠考慮返回JSON對象(JSON對象哦,不是JSON字符串),即Result<JSONObject>。

也能夠考慮返回Map對象,好比Result<Map<String,Object>>。瞧瞧,使用map,有些狀況下,就不可避免的涉及到Object。使用Object來傳參或做爲方法返回值是大忌,與CV大法同樣多少都會使人詬病。

2.2.3 對於返回集合的狀況,固然也很簡單,無非就是Result<List<EnterpriseDTO>>了唄。

 

§3. 強大的Result<T>

Result<T>是一個泛型類,ResultCodeEnum定義了code的枚舉項,它們定義在com.emax.zhenghe:zhenghe-rpcapistyle包裏。

maven dependency依賴:

< dependency >
     < groupId >com.emax.zhenghe</ groupId >
     < artifactId >zhenghe-rpcapistyle</ artifactId >
     < version >1.0.1-SNAPSHOT</ version >
</ dependency >

 

Result<T>重要成員方法:

Result<T>主要操做方法是設置返回結果的(這不是廢話嘛~),返回分兩種,成功的返回,錯誤的返回。所以,Result<T>定義了兩類方法,public static Result<T> success(...)和public static Result<T> err(...)。 固然,爲了方便你們使用,方法重載是免不了的。詳細見下面列表,總有一款適合你!

▄︻┻┳═一 public static <T> Result<T> success()
▄︻┻┳═一 public static <T> Result<T> success(T data)
▄︻┻┳═一 public static <T> Result<T> success(T data, String msg)
▄︻┻┳═一 public Result<T> successWithMsg(String message)
▄︻┻┳═一 public static <T> Result<T> err(String msg)
▄︻┻┳═一 public static <T> Result<T> err(ResultCodeEnum code, String msg)
▄︻┻┳═一 public static <T> Result<T> err(int code, String msg)

 

這個class的實現代碼呢, 一睹芳容吧!

public class Result<T> implements Serializable {
 
    /**
     * 返回代碼
     */
    private int code = 0;
 
    /**
     * 返回處理消息
     */
    private String message = "操做成功!";
 
    /**
     * 返回數據對象 data
     */
    private T result;
     
     
    // 服務提供方的操做方法-----開始
    public static <T> Result<T> success(T data){
        return success(data, "成功");
    }
    public static <T> Result<T> success(T data, String msg) {
        Result<T> r = new Result<>();
        r.setSuccess(true);
        r.setCode(CommonConstant.SC_OK_200);
        r.setResult(data);
        return r;
    }
    public static <T> Result<T> success() {
        Result<T> r = new Result<>();
        r.setSuccess(true);
        r.setCode(CommonConstant.SC_OK_200);
        r.setMessage("成功");
        return r;
    }
    public Result<T> successWithMsg(String message){
        this.message = message;
        this.code = CommonConstant.SC_OK_200;
        this.success = true;
        return this;
    }  
 
    public static <T> Result<T> err(String msg) {
        return err(ResultCodeEnum.INTERNAL_SERVER_ERROR, msg);
    }
 
    public static <T> Result<T> err(ResultCodeEnum code, String msg) {
        return err(code.getCode(), StringUtils.isBlank(msg) ? code.getMsg() : msg);
    }
 
    public static <T> Result<T> err(int code, String msg) {
        Result<T> r = new Result<>();
        r.setCode(code);
        r.setMessage(msg);
        r.setSuccess(false);
        return r;
    }
    // 服務提供方的操做方法-----結束
     
     
    // 服務提供方的操做方法-----開始
    // 客戶端接收到響應後,能夠使用isSuccess來判斷是否成功,成功後,能夠獲取返回數據進行後續處理;不成功,則能夠 getCode()和 getMessage()來記錄code和msg。
    public boolean isSuccess() {
        return code == ResultCodeEnum.SUCCESS.getCode();
    }
    public int getCode() {
        return code;
    }
    public String getMessage() {
        return message;
    }
    public T getResult() {
        return this.result;
    }
    // 服務提供方的操做方法-----結束
}

 

對擴展開放

注意到了嗎?Result的code是int類型,不是上文提到的ResultCodeEnum枚舉。

why?code定義成ResultCodeEnum固然是再好不過了,程序使用枚舉要比數字易讀多了。爲何不這麼作呢?——爲了考慮擴展。試想,若是現有ResultCodeEnum的枚舉項不知足你的項目須要,你是否是要增長ResultCodeEnum枚舉項?是的,那麼,這時,就要去修改zhenghe-rpcapistyle的源碼,而zhenghe-rpcapistyle在另外一個項目裏。隨着依賴zhenghe-rpcapistyle的項目的逐漸增多,ResultCodeEnum也許將變得尤爲難於使用。

所以,若是zhenghe-rpcapistyle裏ResultCodeEnum不知足項目須要,你們能夠在你的項目裏自行定義一個ResultCodeEnum.java,或者能夠在Constant裏定義code(推薦前者)。

§4. How to use?

以下testcase在zhenghe-rpcapistyle包裏,能夠幫助你快速瞭解並掌控Result<T>。

package  com.emax.zhenghe.common.api.vo;
 
import  com.alibaba.fastjson.JSON;
import  lombok.extern.slf4j.Slf4j;
 
import  java.util.Arrays;
import  java.util.List;
 
@Slf4j
public  class  ResultTest {
     public  static  void  main(String[] args) {
         //Result裏保存Long型數值
         Result<Long> longResult = Result.success(5L,  "" );
         System.out.println( "longResult="  + longResult);
         System.out.println(longResult.getCode() +  "----" );
         longResult.setResult(1L);
         System.out.println(longResult.getCode());
 
         //Result裏存儲數據集合
         Result<List<Integer>> listResult = Result.success(Arrays.asList( 1 2 4 ));
         System.out.println(JSON.toJSONString(listResult.getResult()));
 
         //以下Result至關於Result<Object>或Result<?>
         Result objectResult = Result.success();
         System.out.println( "objectResult.getCode()="  + objectResult.getCode());
 
         //在某些error狀況下,Result要設置響應數據。
         Result<Integer> integerResult = Result.err( "sdfsda" );
         integerResult.setResult( 1 );
         System.out.println( "integerResult="  + integerResult);
 
         //構造器初始化的Result對象,code的默認值是200(成功)
         Result<Integer> newResult =  new  Result<>();
         newResult.setCode( 404 );
         newResult.setResult(Integer.MIN_VALUE);
         System.out.println(newResult.getResult() +  "---"  + newResult.getCode());
     }
 
}

 

 

下面,以一個簡單的示例來介紹rpc服務的實現類裏如何使用Result<T>

@Override
public  Result<Long> separateFeeQuery() {
     Long count=0L; //todo:調用service讀庫       
     return  Result.success(count);
}

又例如:

@Override
public  Result addEnterprise(EnterpriseDTO enterprise) {
     ....
     boolean  ok = enterpriseService.save(po);
     if  (ok){
         return  Result.success();
     else  {
         return  Result.err( "數據保存失敗" );
     }
}

 

§5. 命名規範

方法命名可能還真很差統一,也很差立規範。

一樣rpcapi接口類名也是如此,好比能夠統一以-Api結尾,也能夠統一以-Service結尾,不過最好與工程裏的Service區分開來,這樣便於程序理解。

就像上面§1提到的數據傳輸對象用DTO命名同樣,有的人說用VO比較好,仁者見仁智者見智吧。

相關文章
相關標籤/搜索