一、簡介
目前主流的負載均衡的方案分爲兩種:一種是集中式的負載均衡,在消費者和服務提供方中間使用獨立的大力方式進行負載,另一種則是客戶端自己做負載均衡,根據自己的請求情況做負載。Ribbon就是客戶端自己做負載。
二、Ribbon模塊
ribbon-loadbalancer:負載均衡模塊,可獨立使用,也可以和別的模塊一起使用。Ribbon的內置負載均衡算法都實現在其中
ribbon-eureka:基於Eureka封裝的模塊,能夠快速的方便的集成Eureka。
ribbon-transport:就要Netty 實現多協議的支持,比如Http、TCP、Udp等
ribbon-httpclient:基於Apache HttpClient 封裝的Rest的客戶端,集成了負載均衡模塊,可以一直接在項目中使用來調用接口。
ribbon-example:Ribbon使用代碼示例
ribbon-core:一些比較和音的具有通用性的代碼,客戶端API的一些配置和其他API的定義。
三、Ribbon的使用
準備工作:需要啓動兩個服務,下面的示例中這兩個服務的端口分別爲8081和8082他們都有/user/hello的接口
1. 引入依賴
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon</artifactId>
<version> 2.3.0</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-loadbalancer</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>com.netflix.ribbon</groupId>
<artifactId>ribbon-core</artifactId>
<version>2.3.0</version>
</dependency>
<dependency>
<groupId>io.reactivex</groupId>
<artifactId>rxjava</artifactId>
<version>1.3.8</version>
</dependency>
2. 編寫客戶端調用接口
public String testRibbon(){
List<Server> serverList = new ArrayList<>();
serverList.add(new Server("localhost",8081));
serverList.add(new Server("localhost",8082));
ILoadBalancer loadBalancer = LoadBalancerBuilder.newBuilder().buildFixedServerListLoadBalancer(serverList);
for (int i=0;i<5;i++){
String result = LoadBalancerCommand.<String>builder().withLoadBalancer(loadBalancer)
.build().submit(new ServerOperation<String>() {
@Override
public Observable<String> call(Server server) {
try{
String addr = "http://"+server.getHost()+":"+server.getPort()+"/user/hello";
System.out.println("調用地址:"+addr);
URL url = new URL(addr);
HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
httpURLConnection.setRequestMethod("GET");
httpURLConnection.connect();
InputStream inputStream = httpURLConnection.getInputStream();
byte[] data = new byte[inputStream.available()];
inputStream.read();
return Observable.just(new String(data));
}catch (Exception e){
return Observable.error(e);
}
}
}).toBlocking().first();
System.out.println("調用結果:"+result);
}
return "finish";
}
運行後的結果如下:
調用地址:http://localhost:8082/user/hello
調用結果:hello everyone
調用地址:http://localhost:8081/user/hello
調用結果:hello everyone
調用地址:http://localhost:8082/user/hello
調用結果:hello everyone
調用地址:http://localhost:8081/user/hello
調用結果:hello everyone
調用地址:http://localhost:8082/user/hello
調用結果:hello everyone
根據上面的輸入可以看出負載起了作用。
四、RestTemplate 結合Ribbon的使用
1. 引入依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
<version> 2.2.2.RELEASE</version>
</dependency>
2. 在RestTemplate的配置上加LoadBalance註解
@Configuration
public class BeanConfiguration {
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
}
3. 接口的調用
@RestController
public class ArticleController {
private final RestTemplate restTemplate;
@Autowired
public ArticleController(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
@GetMapping("/article/callhello2")
public String callHello2(){
return restTemplate.getForObject("http://eureka-client-user-service/user/hello",String.class);
}
}
@LoadBalanced作用:給RestTemplate增加攔截器,在請求之前對請求的地址進行替換,或者根據具體的負載均衡的策略選擇服務地址,然後再去調用。
五、負載均衡策略
1. BestAvailabl:選擇一個最小的併發請求的 Server ,逐個考察 Server ,如果Server標記爲錯誤責跳過,然後再選擇ActiveRequestCount中最小的 Server 。
2. AvailabilityFilteringRule:過濾掉那些一直連接失敗並且被標記爲 circuit tripped的後端 Server ,並過濾掉那些高併發的後端 Server 或者使用一個 AvailabilityPredicate 來包含過濾 Availability 的窺覬,其實就是檢查 Status 裏記錄的各個 Server 的運行狀態。
3. ZoneAvoiddanceRule:使用 ZoneAvoiddancePredicate 和AvailabilityPredicate 來判斷是否選擇某個Server ,前一個判斷判定一個Zone的運行性能是否可用,刪除不可用的Zone的所有Server,後一個用於過濾連接數過多的Server。
4. RadomRule:隨機選擇一個Server。
5. RoundRobinRule:輪詢選擇,輪詢index,選擇index對應位置的Server
6.RetryRule:對選定的負載均衡策略機上重試機制,也就是說當選定了某個策略進行請求負載時在一個配置時間段內若選擇Server不成功,責一直嘗試使用 subRule的方式選擇一個可用的Server.
7. ResponseTimeWeightedRule:作用同 WeightedResponseTimeRule ,ResponseTimeWeightedRule後來改名爲WeightedResponseTimeRule。
8. WeightedResponseTimeRule:根絕相應時間分配一個Weight,相應時間越長 Weight越小,被選中的可能性越低。
六、自定義負載均衡策略
public class MyRule implements IRule {
private ILoadBalancer loadBalancer;
@Override
public Server choose(Object o) {
//主要選擇的邏輯都在這個方法裏面
List<Server> allServers = loadBalancer.getAllServers();
for (Server server: allServers) {
System.out.println(server.getHost());
}
return allServers.get(0);
}
@Override
public void setLoadBalancer(ILoadBalancer iLoadBalancer) {
this.loadBalancer = loadBalancer;
}
@Override
public ILoadBalancer getLoadBalancer() {
return loadBalancer;
}
}