負載均衡(Spring Cloud Ribbon)之快速入門
背景介紹:
目前主要流行的負載均衡分為兩種:一種是集中式負載均衡,在消費者和提供者中間使用獨立的代理方式進行負載,有硬體也有軟體,另外一種就是客戶端自己進行負載均衡,ribbon就是屬於客戶端負載均衡。
ribbon是一個基於http和tcp的客戶端負載均衡工具,基於Netfix Ribbon實現,通過Cloud的封裝,可以讓我們輕鬆的實現,他不像註冊中心,配置中心,API閘道器那樣需要單獨部署,他存在於所有需要負載均衡的服務中。
快速入門:
首先新建一個springboot專案,命名為:ribbon-service
pom.xml檔案:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.ribbon</groupId> <artifactId>ribbon</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>ribbon</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Dalston.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
配置檔案:
spring.application.name=ribbon-service
server.port=9000
eureka.client.serviceUrl.defaultZone=http://127.0.0.1:8000/eureka/
eureka.instance.preferIpAddress=true
package com.ribbon; import com.ribbon.MyInterceptor.MyLoadBalanced; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.context.annotation.Bean; import org.springframework.web.client.RestTemplate; 啟動類: 啟動類里加一個RestTemplate的bean,這裡的RestTemplate添加了@LoadBalanced註解就有了負載均衡的功能了,後面再細說 @SpringBootApplication @EnableDiscoveryClient public class RibbonApplication { @Bean // @LoadBalanced //客戶端負載均衡 @MyLoadBalanced RestTemplate restTemplate(){ return new RestTemplate(); } public static void main(String[] args) { SpringApplication.run(RibbonApplication.class, args); } }
然後寫一個controller,
@RestController public class ConsumerController { @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/ribbon",method = RequestMethod.GET) public String helloConsumer(){ return restTemplate.getForEntity( "http://hello-service/hello",String.class).getBody(); } }
訪問這個介面,就可以通過註冊中心訪問到hello-service的介面,我們啟動兩個hello-service,埠分別為8800,8801,多訪問幾次就會看到兩個hello-service都有訪問到了。
RestTemplate簡介:
RestTemplate是spring自帶的一個訪問介面的類,並不是ribbon的
get請求:第一種getForEntity函式,返回的是ResponseEntity物件,第二種getForObject函式,這個是進一步封裝,直接得到返回體
post請求:第一種postForEntity函式,返回的是整個ResponseEntity<T>物件,第二種postForObject函式,也是直接返回返回體,第三種getForLocation函式,該方法實現了post請求提交資源,返回新的資源的url。
RestTemplate只是請求介面的一種方法,由spring提供。還有put,delete等方法,我們現在用的最多的就是post,get。
@LoadBalanced註解:
這個註解就是開啟負載均衡的一個關鍵,主要的邏輯就是給RestTemplate增加攔截器,在請求之前對請求的地址進行替換或者根據具體的負載策略選擇服務地址,然後再去呼叫,預設策略是輪詢。下面自己實現一個簡單的@LoadBalanced來理解下原理。
首先新建一個攔截器:
package com.ribbon.MyInterceptor;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
import java.io.IOException;
import java.net.URI;
/**
* 自定義一個負載均衡的攔截器
* Created by qhe on 2018/8/16.
*/
public class MyLoadBalancerInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] bytes, ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
final URI originalUri = httpRequest.getURI();
String servicename = originalUri.getHost();
System.out.println("進入自定義的請求攔截器中:"+servicename);
return clientHttpRequestExecution.execute(httpRequest,bytes);
}
}
然後實現一個註解,這裡我們複製 @LoadBalanced的原始碼就行,修改下名字
package com.ribbon.MyInterceptor;
import org.springframework.beans.factory.annotation.Qualifier;
import java.lang.annotation.*;
/**
* 自定義一個負載均衡
* Created by qhe on 2018/8/16.
*/
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface MyLoadBalanced {
}
再寫一個配置類:
package com.ribbon.MyInterceptor;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Created by qhe on 2018/8/16.
*/
@Configuration
public class MyLoadBalancedAutoConfiguration {
@MyLoadBalanced //這是我們自己定義的註解
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList(); //這是restTemplates集合
@Bean //把攔截器注入進來
public MyLoadBalancerInterceptor myLoadBalancerInterceptor(){
return new MyLoadBalancerInterceptor();
}
//維護一個restTemplates列表,在SmartInitializingSingleton 裡對restTemplate進行攔截設定。
@Bean
public SmartInitializingSingleton myLoadBalancedRestTemplataInitializer(){
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
for (RestTemplate restTemplate:MyLoadBalancedAutoConfiguration.this.restTemplates){
List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());
list.add(myLoadBalancerInterceptor());
restTemplate.setInterceptors(list);
}
}
};
}
}
然後在啟動類裡改成我們自定義的註解:
@Bean
// @LoadBalanced //客戶端負載均衡
@MyLoadBalanced//自定義的負載均衡
RestTemplate restTemplate(){
return new RestTemplate();
}
啟動服務,訪問介面,我們就可以看到控制檯上有自定義的輸出了
原始碼在spring-cloud-commons.jar中的org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration類中。
負載均衡策略:
ribbon作為一款客戶端的負載均衡框架,預設的是輪詢策略,同時提供了其他很多策略。在IRule中。
BestAvailabl:選擇一個最小的併發請求的Server,逐個考察server,如果servcer標記錯了則跳過,然後在選擇最小的server.
AvailabilityFilteringRule:過濾掉那些一直連線失敗且被標記為circuit tripped的server,並過濾那些高併發的server,或者使用一個AvailabilityPredicate來包含過濾邏輯。
ZoneAvoidanceRule:判斷一個zone的執行效能是否可用,剔除不可用的zone,或者過濾掉連線數過多的server。
RandomRule:隨機選擇一個server。
RoundRobinRule:輪詢選擇
RetryRule:對選定的負載均衡策略上重試機制,就是說如果根據策略選擇的server請求不成功,就重試。
WeightedResponseTimeRule:根據相應時間分配一個權重,相應時間越長,權重越小,被選中的可能性就越低。
當然還可以自定義均衡策略。實現IRule介面就行。
學習資料:《springcloud微服務實戰》《springcloud 微服務全棧技術與案例解析》