一:自定義LoadBalance介面
具體代碼如下:
package com.springcloudtest.order.config;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
public interface LoadBalance {
ServiceInstance instances(List<ServiceInstance> serviceInstanceList);
}
該介面有兩個作用:
1.對外暴露,當需要使用負載均衡功能時,可以呼叫該介面的 instances方法,傳入微服務實體List,
回傳一個經過負載均衡演算法選擇后的具體實體
2.進行不同負載均衡演算法的實作,當需要實作其他型別的負載均衡演算法時,可以實作該介面,然后再具體
寫出負載均衡演算法的實作
二:采用負載均衡輪詢演算法實作該介面
1.實作上面的LoadBalance介面
package com.springcloudtest.order.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class MyLoadBalance implements LoadBalance {
public ServiceInstance instances(List<ServiceInstance> serviceInstanceList) {
return null;
}
}
注意:
注意@Component,需要把該類注入到IOC容器中,以便后續的@Resource獲取
2.采用 CAS+自旋鎖方式實作輪詢演算法
package com.springcloudtest.order.config;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
@Component
public class MyLoadBalance implements LoadBalance {
//設定原子Integer,用于保證服務在呼叫時的執行緒安全問題
private AtomicInteger atomicInteger = new AtomicInteger(0);
/**
* 通過CAS +自旋鎖的方式 回傳一個呼叫該微服務的次數
* @return
*/
public final int getServiceCallSubscript(){
//當前值
int current;
//修改值
int next;
//自旋鎖
do{
//獲取當前值
current = this.atomicInteger.get();
next = current >=Integer.MAX_VALUE ? 0 :current+1;
//CAS 比較并替換 ,如果滿足該條件則結束回圈,不滿足該條件繼續自旋
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("next:--------"+next);
return next;
}
public ServiceInstance instances(List<ServiceInstance> serviceInstanceList) {
return null;
}
}
采用CAS+自旋鎖的原因:由于負載均衡使用的場景是高并發,而輪詢演算法的核心是得到一個整數型的下標,在高并發的場景下,需要保證該下標的資料一致性,CAS可以保證該下標的資料一致性,自旋鎖可以使該次請求不斷的訪問重試直到成功為止,CAS+自旋鎖的原因是可以在高效率和安全性的情況下保證微服務的高可用,
——————————————————————————————————————
//設定原子Integer,用于保證服務在呼叫時的執行緒安全問題
private AtomicInteger atomicInteger = new AtomicInteger(0);
采用AtomicInteger 的原因:
由于該下標是共享變數,用于保證對該下標進行操作時的原子性和可見性,讓每一個執行緒都可以訪問到該
下標的值,是使用CAS的前提
——————————————————————————————————————
this.atomicInteger.compareAndSet(current,next)
CAS操作
comapreAndSet底層采用了comapreAndSwap,比較并替換是一個在硬體方面上實作的原子性操作把,
比較并替換變成了一步操作(可以理解成相當于加了一個鎖),這樣就可以保證在多執行緒的情況下,該下標只能
被一個執行緒比較并替換,這樣就保證了共享變數的安全性
——————————————————————————————————————
do{
//獲取當前值
current = this.atomicInteger.get();
next = current >=Integer.MAX_VALUE ? 0 :current+1;
//CAS 比較并替換 ,如果滿足該條件則結束回圈,不滿足該條件繼續自旋
}while (!this.atomicInteger.compareAndSet(current,next));
System.out.println("next:--------"+next);
return next;
}
自旋鎖:
自旋鎖的實作方式有很多種,這里采用了do....while回圈,這里自旋鎖的作用是,當某一個執行緒不滿足CAS
操作時,就會繼續進行比較并替換的操作直到成功為止,自旋鎖不是重量級鎖,他可以提高多執行緒情況下效率
current = this.atomicInteger.get();
current是獲取當前下標的值,用于CAS的比較
next = current >=Integer.MAX_VALUE ? 0 :current+1;
next的賦值才用了三目運算子,如果該服務呼叫的次數超過了整型的最大值就重置為0,否則就加一,其目的是修改當前下標的值
2.獲取該輪詢演算法產生的值,引數微服務實體List下標
public ServiceInstance instances(List<ServiceInstance> serviceInstanceList) {
//采用取余的方式獲取下標
int index = getServiceCallSubscript()%serviceInstanceList.size();
// 獲取 serviceInstanceList中該下標對應的ServiceInstance物件
return serviceInstanceList.get(index);
}
采用取余法獲取需要呼叫的微服務實體下標:
公式: index = current % 微服務實體個數
index 是 List<ServiceInstance> 中的下標
current 是通過輪詢演算法得到的結果
微服務實體個數 是 List<ServiceInstance>的長度
最后再回傳通過該下標獲取到的實體
三:呼叫該負載均衡輪詢方法實作負載均衡
package com.springcloudtest.order.controller;
import com.springcloudtest.order.config.LoadBalance;
import com.springcloudtest.order.config.MyLoadBalance;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.stereotype.Controller;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestTemplate;
import javax.annotation.Resource;
import java.net.URI;
import java.util.List;
@Controller
public class TestController {
@Value("${server.port}")
private String serverPort;
@Resource
private LoadBalance myLoadBalance;
@Resource
private DiscoveryClient discoveryClient;
@Resource
private RestTemplate restTemplate;
@RequestMapping("/order/myLoadBalance")
@ResponseBody
public String myLoadBalance(){
//獲取 名稱為payment-springcloud的微服務實體List
List<ServiceInstance> serviceInstanceList = discoveryClient.getInstances("payment-springcloud");
//如果該微服務List為空,說明該微服務沒有可用的實體,回傳null
if(CollectionUtils.isEmpty(serviceInstanceList)){
return null;
}
//把該List傳入自定義負載均衡類中,獲取出呼叫的微服務實體
ServiceInstance serviceInstance = myLoadBalance.instances(serviceInstanceList);
//獲取該實體的訪問地址
URI uri = serviceInstance.getUri();
return restTemplate.postForObject(uri+"/payment/consul",null,String.class);
}
}
具體步驟:
1.獲取payment-springcloud該微服務名稱的微服務List
2.呼叫上面自定義的負載均衡輪詢類中的instances方法,獲取出呼叫的微服務實體
3.獲取該實體中的URI地址,使用restTemplate進行服務呼叫,即可實作負載均衡
注意:
1.想要獲取discoveryClient這個物件,必須要在application啟動類中加入@EnableDiscoveryClient注解
2.想要獲取restTemplate這個物件,必須要先注入restTemplate該物件
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/258144.html
標籤:其他
上一篇:轉型時代,我心目中的數字化產品!
