Dubbo服務提供者如何優雅升級?

2023-03-31 15:00:42

文章首發於公眾號:BiggerBoy。歡迎關注。

往期文章推薦

大坑!隱式轉換導致索引失效...
高效能分散式限流:Redis+Lua真香!
MySQL索引知識點&常見問題彙總聯合索引在B+樹上的儲存結構及資料查詢方式
Redis分散式鎖實戰Mybatis第三方PageHelper外掛分頁原理MySQL索引底層原理 

一、問題交代

當我們使用dubbo作為服務間通訊的元件時,在後期的系統維護中可能會因為業務需要,服務提供者某些介面需要升級,對應的服務消費者配合作相應的修改,測試通過後一起發版上線即可。但是在這個過程中,有很多需要注意的點,不妨來梳理一下以作記錄,希望對此不清楚的開發者有所幫助。
再次申明一下我們的需求吧。假設服務A提供了一個服務IHelloService,其中有一個方法sayHello,服務B、C、D...都對此有依賴,假設服務A提供的該介面已上線且被服務B、C、D正常消費,線上執行穩定。

 

 先將服務提供者和服務消費者分別啟動,此時消費方正常消費服務。

api:

public interface IHelloService {
    Result sayHello(ModelParent param);
}

服務提供者:

@Component
public class HelloServiceImpl implements IHelloService {
    @Override
    public Result sayHello(ModelParent param) {
        MyDto dto = new MyDto();
        dto.setMsg("hello," + param.getName());
        return Result.success(dto);
    }
}

服務消費者:

@Service
public class MyService {
    @DubboReference(group = "test",version = "1.0",registry = "registry2")
    private IHelloService helloService;

    public Result test(ModelParent param) {
        return helloService.sayHello(param);
    }
}

 

 

二、介面迭代升級方案

 

過了幾個月,服務B接到產品需求,為了滿足需求,對服務A的IHelloService.sayHello提出新的要求,因為本次需求不涉及服務C、D的改動,所以服務A對該服務還需要滿足服務C、D呼叫時保持老邏輯不變,因此服務A需要格外注意服務的相容性。
注意:這裡說的服務A、B、C...是一個個應用或系統。應用或系統對外提供多種服務,例如使用者服務對外提供使用者資訊查詢服務、使用者註冊服務等。

假設服務A和服務B的開發人員確認完需求後,服務A的開發人員想到的介面升級方案如下:

  1. 服務A新提供一個sayHelloV2方法或新服務IHelloServiceV2,提供新的業務邏輯,服務B調這個新方法或新服務,原來的sayHello保留,服務C、D繼續呼叫且本次不用修改上線。
  2. 服務A修改IHelloService.sayHello,在原來的入參中新增欄位,用於區分走新邏輯還是老邏輯。在這種方式下就需要特別注意了,因為入參增加了引數,要考慮是否會影響本次需求不涉及的服務C和服務D。

    2.1 在原入參物件中新增欄位。服務C、D對於新增的欄位肯定傳的是null,所以可以用null標識走老邏輯,用其他值比如1標識走新邏輯。

    2.2 建立新的入參物件 ModelChildren 增加新欄位,且該入參物件繼承原入參物件。此時又分不同的情況。

      a.介面方法入參物件保持原入參物件不變,即,方法簽名不變,仍然是Result sayHello(ModelParent param)。僅需改動有新需求的服務B的程式碼,將入參物件改為新入參物件,由於該物件繼承了原入參物件,所以不會有問題,但服務A需要判斷傳的是父類別 ModelParent 還是子類 ModelChildren,並從子類 ModelChildren 中獲取新欄位。

      b.介面方法入參物件改為新入參物件,即,方法簽名修改,入參改為新物件Result sayHello(ModelChildren param)。此時,所有消費該服務的消費方均需要修改,如果消費方不改,消費方呼叫該方法時會拋異常org.apache.dubbo.remoting.RemotingException: Fail to decode request due to:java.lang.IllegalArgumentException:Service not found:com.biggerboy.api.IHelloService, sayHello

 三、 總結

 

 

 

方案 參數列 優點 缺點
新增方法sayHelloV2 - 1.新老邏輯分開,相容老版本2.對本次不涉及的服務消費者友好,因為可以不用改動程式碼繼續使用老方法 1.額外建立新的服務方法,對記憶體消耗略微增加(可忽略)
新增服務IHelloServiceV2 - 1.新老邏輯分開,相容老版本2.對本次不涉及的服務消費者友好,因為可以不用改動程式碼繼續使用老服務 1.額外建立新的服務,對記憶體消耗略微增加(可忽略)
修改入參ModelParent增加欄位 不變 1.相容老版本,對不涉及的服務無影響2.不用新增服務,不會額外消費記憶體 1.程式中需使用新增的欄位區分新老邏輯
新增入參子類ModelChildren增加欄位並繼承原入參ModelParent 不變 1.相容老版本,對不涉及的服務無影響2.不用新增服務,不會額外消費記憶體3.新增加了入參不會影響其他同樣使用ModelParent作為引數的服務 1.程式中需使用新增的欄位區分新老邏輯2.額外建立新的類,對記憶體消耗略微增加(可忽略)
新增入參子類ModelChildren增加欄位並繼承原入參ModelParent 改為子類 1.不用新增服務,不會額外消費記憶體2.新增加了入參不會影響其他同樣使用ModelParent作為引數的服務 1.程式中需使用新增的欄位區分新老邏輯2.額外建立新的類,對記憶體消耗略微增加(可忽略)3.需修改參數列,當方法呼叫鏈較長且使用入參物件作為參數列時,需修改的方法較多4.不相容老版本,本次不涉及的服務消費者也需修改,負責報錯

你們公司是如何做的呢?歡迎評論區一起討論~ 

End