Spring Cloud開發人員如何解決服務衝突和例項亂竄?
阿新 • • 發佈:2019-12-31
一、背景
在我們開發微服務架構系統時,雖然說每個微服務都是孤立的可以單獨開發,但實際上並非如此,要除錯和測試你的服務不僅需要您的微服務啟動和執行,還需要它的上下文服務、依賴的基礎服務等都要執行;但如果你的系統服務數和依賴比較多呢,那就是一個比較棘手的問題!有沒有辦法能提高開發效率呢?
如上圖所示,我們能不能用伺服器把所有的服務都部署起來,然後開發只在本地執行自己所負責開發的服務,因為需要依賴其他服務所以本地啟動的服務也需要註冊到公共的註冊中心裡;
例子中
業務服務B
有3臺例項註冊到註冊中心裡 分別是:伺服器的、開發A與開發B自己本機啟動的
但是這樣做又會出現新的問題:服務會衝突亂竄,意思就是開發A
業務服務B
服務的時候可能請求會跳轉到其他人的例項上(伺服器、開發B)
二、解決思路
解決這個服務亂竄問題有一個比較優雅的方式就是自定義負載均衡規則
,主要實現以下目標:
-
普通使用者訪問伺服器上的頁面時,請求的所有路由只呼叫
伺服器上的例項
-
開發A訪問時,請求的所有路由優先呼叫
開發A本機啟動的例項
,如果沒有則呼叫伺服器上的例項
-
開發B訪問時同上,請求的所有路由優先呼叫
開發B本機啟動的例項
,如果沒有則呼叫伺服器上的例項
三、具體實現
要實現上面的目標有兩個比較關鍵的問題需要解決
- 區分
不同使用者的服務例項
- 實現
自定義負載均衡規則
3.1. 區分不同使用者的服務例項
直接使用註冊中心的元資料(metadata)來區分就可以了
主流的註冊中心都帶有元資料管理 以
Nacos
為例,只需要在配置檔案下新增
spring:
cloud:
nacos:
discovery:
server-addr: localhost:8848
metadata:
version: zlt
複製程式碼
metadata下的
version
就是我新增的元資料key
為version,value
為zlt
啟動服務後元資料就會註冊上去,如下圖
經過元資料區分後,目前是下面這個情況
- 伺服器的例項
version
- 開發人員自己本地啟動的例項
version
為唯一標識(自己的名字)
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);
}
}
複製程式碼
繼承輪詢規則
RoundRobinRule
來實現,主要的邏輯為
- 根據上游輸入的版本號
version
,有值的話則取服務元資訊
中version
值一樣的例項- 上游的版本號
version
沒值或者該版本號匹配不到任何服務,則只取服務元資訊
中version
值為空的例項
並通過配置開關控制是否開啟自定義負載規則
@Configuration
@ConditionalOnProperty(value = "zlt.ribbon.isolation.enabled",havingValue = "true")
@RibbonClients(defaultConfiguration = {RuleConfigure.class})
public class LbIsolationConfig {
}
複製程式碼
四、總結
上面提到的區分服務例項和自定義負載規則為整個解決思路的核心點,基本實現了服務例項的隔離,剩下要做的就是上游的version
怎樣傳遞呢?,下面我提供兩個思路
- 開發人員自己啟動前端工程,通過配置引數,統一在前端工程傳遞
version
- 通過
postman
呼叫介面的時候在header引數中新增
參考
推薦閱讀
- 日誌排查問題困難?分散式日誌鏈路跟蹤來幫你
- zuul整合Sentinel最新的閘道器流控元件
- 阿里註冊中心Nacos生產部署方案
- Spring Boot自定義配置項在IDE裡面實現自動提示
- Spring Cloud Zuul的動態路由怎樣做?整合Nacos實現很簡單