Spring Cloud開發人員如何解決服務沖突和實例亂竄?(IP實現方案)


file

一、背景

在我上一篇文章《Spring Cloud開發人員如何解決服務沖突和實例亂竄?》中提到使用服務的元數據來實現隔離和路由,有朋友問到能不能直接通過IP來實現?本文就和大家一起來討論一下這個問題

 

二、可行性分析

要實現通過IP來隔離和路由的話有一個非常關鍵的點需要解決,就是怎樣實現IP可辨識,意思就是如何區分那個IP服務器上的,那個IP開發人員本機

file

如上圖所示其實我們還是能找到規律可以辨識的,所以這個是可以行的!

  • 開發人員本機IP - 其實就是客戶端IP,也就是原始請求方的IP172.16.20.2
  • 服務器IP - 可以理解為服務器上的服務所在機器的IP(有點繞):172.16.20.1

 

三、路由規則邏輯

主要實現以下目標:

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

在找到IP的辨識規律后,推導出下面3個路由規則來實現上面的目標

  1. 優先匹配原始請求方的IP的服務實例
  2. 再者匹配上游服務所在機器IP的服務實例
  3. 上面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添加到headerHTTP_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之類的參數了。
但是這個方案其實也是有局限性

  1. 開發服務器必須是只用一台來部署所有的服務,因為如果上游服務和下游服務不在同一個IP上就失去了辨識能力了
  2. 因為網絡環境比較復雜,不一定能獲取到客戶端的真實原IP
  3. 開發人員啟動客戶端/前端的機器與啟動后台服務必須是同一台電腦上才行;例如如果是前端開發人員A啟動的客戶端,去調試后台開發人員B啟動的服務就不行了,因為原IP與注冊上去的服務實例IP匹配不上

 
最后可能大家會問原IP怎樣全鏈路傳遞下去?鏈路傳遞可以參考一下我的另外一篇文章《日志排查問題困難?分布式日志鏈路跟蹤來幫你

 

推薦閱讀

 
掃碼關注有驚喜!

file


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM