Spring Cloud開發人員如何解決服務衝突和實例亂竄?(IP實現方案)
- 2019 年 10 月 3 日
- 筆記

一、背景
在我上一篇文章《Spring Cloud開發人員如何解決服務衝突和實例亂竄?》中提到使用服務的元數據來實現隔離和路由,有朋友問到能不能直接通過IP來實現?本文就和大家一起來討論一下這個問題
二、可行性分析
要實現通過IP來隔離和路由的話有一個非常關鍵的點需要解決,就是怎樣實現IP可辨識,意思就是如何區分那個IP是伺服器上的,那個IP是開發人員本機的

如上圖所示其實我們還是能找到規律可以辨識的,所以這個是可以行的!
- 開發人員本機IP - 其實就是
客戶端IP,也就是原始請求方的IP:172.16.20.2 - 伺服器IP - 可以理解為伺服器上的服務所在機器的IP(有點繞):172.16.20.1
三、路由規則邏輯
主要實現以下目標:
- 普通用戶訪問伺服器上的頁面時,請求的所有路由只調用
伺服器上的實例 - 開發A訪問時,請求的所有路由優先調用
開發A本機啟動的實例,如果沒有則調用伺服器上的實例 - 開發B訪問時同上,請求的所有路由優先調用
開發B本機啟動的實例,如果沒有則調用伺服器上的實例
在找到IP的辨識規律後,推導出下面3個路由規則來實現上面的目標
- 優先匹配
原始請求方的IP的服務實例 - 再者匹配
上游服務所在機器IP的服務實例 - 上面2個邏輯都匹配不到的話使用輪詢的方式找一個實例
具體的自定義負載均衡的對象怎麼寫我這裡就不詳細描述了,可以參考我上一篇文章《Spring Cloud開發人員如何解決服務衝突和實例亂竄?》
四、獲取原始請求方的IP
獲取原IP的程式碼片段如下,只需要在網關上增加一個過濾器獲取IP,然後添加到header裡面一直傳遞下去就可以了
/** * 獲取Ip地址 */ private String getIpAddr(HttpServletRequest request){ String ip = request.getHeader("X-Forwarded-For"); if (isEmptyIP(ip)) { ip = request.getHeader("Proxy-Client-IP"); if (isEmptyIP(ip)) { ip = request.getHeader("WL-Proxy-Client-IP"); if (isEmptyIP(ip)) { ip = request.getHeader("HTTP_CLIENT_IP"); if (isEmptyIP(ip)) { ip = request.getHeader("HTTP_X_FORWARDED_FOR"); if (isEmptyIP(ip)) { ip = request.getRemoteAddr(); if ("127.0.0.1".equals(ip) || "0:0:0:0:0:0:0:1".equals(ip)) { // 根據網卡取本機配置的IP try { ip = InetAddress.getLocalHost().getHostAddress(); } catch (UnknownHostException e) { log.error("InetAddress.getLocalHost()-error", e); } } } } } } } else if (ip.length() > 15) { String[] ips = ip.split(","); for (int index = 0; index < ips.length; index++) { String strIp = ips[index]; if (!isEmptyIP(ip)) { ip = strIp; break; } } } return ip; } private boolean isEmptyIP(String ip) { if (StrUtil.isEmpty(ip) || UNKNOWN_STR.equalsIgnoreCase(ip)) { return true; } return false; } 把原IP添加到header的HTTP_X_FORWARDED_FOR裡面傳遞給下游服務
RequestContext ctx = RequestContext.getCurrentContext(); HttpServletRequest request = ctx.getRequest(); String sourceIp = getIpAddr(request); ctx.getZuulRequestHeaders().put("HTTP_X_FORWARDED_FOR", sourceIp);
五、獲取伺服器所在機器的IP
直接使用JDK自帶的InetAddress就可以了
String localIp = InetAddress.getLocalHost().getHostAddress()
六、總結
通過IP的方案來實現開發環境服務實例隔離和策略路由後,可以實現到開發完全無感知,既不需要配置元數據,也不需要自己去傳version之類的參數了。
但是這個方案其實也是有局限性的
- 開發伺服器必須是只用一台來部署所有的服務,因為如果上游服務和下游服務不在同一個
IP上就失去了辨識能力了 - 因為網路環境比較複雜,不一定能獲取到客戶端的真實
原IP - 開發人員啟動客戶端/前端的機器與啟動後台服務必須是同一台電腦上才行;例如如果是
前端開發人員A啟動的客戶端,去調試後台開發人員B啟動的服務就不行了,因為原IP與註冊上去的服務實例IP匹配不上
最後可能大家會問原IP怎樣全鏈路傳遞下去?鏈路傳遞可以參考一下我的另外一篇文章《日誌排查問題困難?分散式日誌鏈路跟蹤來幫你》
推薦閱讀
- zuul集成Sentinel最新的網關流控組件
- 阿里註冊中心Nacos生產部署方案
- Spring Boot自定義配置項在IDE裡面實現自動提示
- Spring Cloud Zuul的動態路由怎樣做?集成Nacos實現很簡單
掃碼關注有驚喜!
