Spring Cloud開發人員如何解決服務衝突和實例亂竄?

Spring Cloud開發人員如何解決服務衝突和實例亂竄?

一、背景

在我們開發微服務架構系統時,雖然說每個微服務都是孤立的可以單獨開發,但實際上並非如此,要調試和測試你的服務不僅需要您的微服務啟動和運行,還需要它的上下文服務、依賴的基礎服務等都要運行;但如果你的系統服務數和依賴比較多呢,那就是一個比較棘手的問題!有沒有辦法能提高開發效率呢?

Spring Cloud開發人員如何解決服務衝突和實例亂竄?

如上圖所示,我們能不能用 服務器把所有的服務都部署 起來,然後開發 只在本地運行自己所負責開發的服務 ,因為需要依賴其他服務所以本地啟動的服務也需要註冊到公共的註冊中心裡;

例子中 業務服務B 有3臺實例註冊到註冊中心裡

分別是:服務上的、開發A與開發B自己本機啟動的

但是這樣做又會出現新的問題: 服務會衝突亂竄 ,意思就是 開發A 在debug自己的 業務服務B服務的時候可能請求會跳轉到其他人的實例上(服務器、開發B)

二、解決思路

解決這個服務亂竄問題有一個比較優雅的方式就是 自定義負載均衡規則 ,主要實現以下目標:

  1. 普通用戶 訪問服務器上的頁面時,請求的所有路由只調用 服務器上的實例
  2. 開發A 訪問時,請求的所有路由優先調用 開發A本機啟動的實例 ,如果沒有則調用 服務器上的實例
  3. 開發B 訪問時同上,請求的所有路由優先調用 開發B本機啟動的實例 ,如果沒有則調用 服務器上的實例

三、具體實現

要實現上面的目標有兩個比較關鍵的問題需要解決

不同用戶的服務實例
自定義負載均衡規則

3.1. 區分不同用戶的服務實例

直接使用註冊中心的元數據(metadata)來區分就可以了

主流的註冊中心都帶有元數據管理

以 Nacos 為例,只需要在配置文件下添加

spring: 

cloud:
nacos:
discovery:
server-addr: localhost:8848
metadata:
version: zlt

metadata下的 version 就是我添加的元數據 key 為version, value 為zlt

啟動服務後元數據就會註冊上去,如下圖

Spring Cloud開發人員如何解決服務衝突和實例亂竄?

經過元數據區分後,目前是下面這個情況

  • 服務器的實例 version 為空
  • 開發人員自己本地啟動的實例 version 為唯一標識(自己的名字)
Spring Cloud開發人員如何解決服務衝突和實例亂竄?


3.2. 自定義負載均衡規則

首先在 Spring Cloud 微服務框架裡實例的負載均衡是由 Ribbon 負責。

CustomIsolationRule詳細類信息可查看: CustomIsolationRule.java

public class CustomIsolationRule extends RoundRobinRule {
/**
* 優先根據版本號取實例
*/
@Override
public Server choose(ILoadBalancer lb, Object key) {
if (lb == null) {
return null;
}
String version = LbIsolationContextHolder.getVersion();
List<server> targetList = null;
List<server> upList = lb.getReachableServers();
if (StrUtil.isNotEmpty(version)) {
//取指定版本號的實例
targetList = upList.stream().filter(
server -> version.equals(
((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION)
)
).collect(Collectors.toList());
}
if (CollUtil.isEmpty(targetList)) {
//只取無版本號的實例
targetList = upList.stream().filter(
server -> {
String metadataVersion = ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION);
return StrUtil.isEmpty(metadataVersion);
}
).collect(Collectors.toList());
}
if (CollUtil.isNotEmpty(targetList)) {
return getServer(targetList);
}
return super.choose(lb, key);
}
/**
* 隨機取一個實例
*/
private Server getServer(List<server> upList) {
int nextInt = RandomUtil.randomInt(upList.size());

return upList.get(nextInt);
}
}
/<server>/<server>/<server>

集成輪詢規則 RoundRobinRule 來實現,主要的邏輯為

  1. 根據上游輸入的版本號 version ,有值的話則取 服務元信息 中 version 值一樣的實例
  2. 上游的版本號 version 沒值或者該版本號匹配不到任何服務,則只取 服務元信息 中 version值為空的實例

並通過配置開關控制是否開啟自定義負載規則

@Configuration
@ConditionalOnProperty(value = "zlt.ribbon.isolation.enabled", havingValue = "true")
@RibbonClients(defaultConfiguration = {RuleConfigure.class})
public class LbIsolationConfig {
}

四、總結

上面提到的 區分服務實例自定義負載規則 為整個解決思路的核心點,基本實現了服務實例的隔離,剩下要做的就是 上游的 version 怎樣傳遞呢? ,下面我提供兩個思路

  • 開發人員自己啟動前端工程,通過配置參數,統一在前端工程傳遞 version
  • 通過 postman 調用接口的時候在header參數中添加
Spring Cloud開發人員如何解決服務衝突和實例亂竄?


參考

https://github.com/Nepxion/Discovery


分享到:


相關文章: