SpringCloud系列五:Ribbon 負載均衡(Ribbon 基本使用、Ribbon 負載均衡、自定義 Ribbon 配置、禁用 Eureka 實現 Ribbon 調用)
1、概念:Ribbon 負載均衡
2、具體內容
現在所有的服務已經通過了 Eureka 進行了註冊,那麽使用 Eureka 註冊的目的是希望所有的服務都統一歸屬到 Eureka 之中進 行處理,但是現在的問題,所有的微服務匯集到了 Eureka 之中,而客戶端的調用也應該通過 Eureka 完成。而這種調用就可以利用 Ribbon 技術來實現。
Ribbon 是一個服務調用的組件,並且是一個客戶端實現負載均衡處理的組件。服務器端實現負載均衡可以使用 Nginx、 HAProxy、LVS 等。
2.1、Ribbon 基本使用
1、 【microcloud-consumer-80】修改 pom.xml 配置文件,追加 Ribbon 相關依賴支持包:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId> </dependency>
2、 【microcloud-consumer-80】修改 RestConfig 配置類,在獲取 RestTemplate 對象的時候加入 Ribbon 的配置註解@LoadBalanced。
package cn.study.microcloud.config; import java.nio.charset.Charset; import java.util.Base64; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.web.client.RestTemplate; @Configuration public class RestConfig { @Bean public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置 HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息 String auth = "studyjava:hello"; // 認證的原始信息 byte[] encodedAuth = Base64.getEncoder() .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理 // 在進行授權的頭信息內容配置的時候加密的信息一定要與“Basic”之間有一個空格 String authHeader = "Basic " + new String(encodedAuth); headers.set("Authorization", authHeader); return headers; } @Bean @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3、 【microcloud-consumer-80】修改 aplication.yml 配置文件,追加 Eureka 的服務註冊地址配置。
server:
port: 80
eureka:
client:
register-with-eureka: false #客戶端不註冊到eureka,只是進行服務的調用
service-url:
defaultZone: http://edmin:[email protected]:7001/eureka,http://edmin:[email protected]:7002/eureka,http://edmin:[email protected]:7003/eureka
4、 【microcloud-consumer-80】修改項目的啟動類,追加 Eureka 客戶端的配置註解:
package cn.study.microcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @SpringBootApplication @EnableEurekaClient public class Consumer_80_StartSpringCloudApplication { public static void main(String[] args) { SpringApplication.run(Consumer_80_StartSpringCloudApplication.class, args); } }
5、 【microcloud-consumer-80】修改控制器調用類;
· 現在在 eureka 之中註冊的所有服務的名稱都是大寫字母:MICROCLOUD-PROVIDER-DEPT;
package cn.study.microcloud.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.vo.Dept; @RestController public class ConsumerDeptController { public static final String DEPT_GET_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/get/"; public static final String DEPT_LIST_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/list/"; public static final String DEPT_ADD_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/add?dname="; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { Dept dept = this.restTemplate .exchange(DEPT_GET_URL + id, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } @SuppressWarnings("unchecked") @RequestMapping(value = "/consumer/dept/list") public Object listDept() { List<Dept> allDepts = this.restTemplate .exchange(DEPT_LIST_URL, HttpMethod.GET, new HttpEntity<Object>(this.headers), List.class) .getBody(); return allDepts; } @RequestMapping(value = "/consumer/dept/add") public Object addDept(Dept dept) throws Exception { Boolean flag = this.restTemplate.exchange(DEPT_ADD_URL, HttpMethod.POST, new HttpEntity<Object>(dept, this.headers), Boolean.class) .getBody(); return flag; } }
訪問地址:http://client.com/consumer/dept/list。這個時候有了 Ribbon 與 Eureka 整合之後用戶不再去關註具體的 Rest 服務的地 址與端口號了,所有的信息獲取都通過 Eureka 完成。
2.2、Ribbon 負載均衡
通過上面的代碼可以發現在 Ribbon 裏面有一個負載均衡的註解:@LoadBalanced,那麽就意味著現在就可以實現負載均衡的處理了。ribbon負載均衡的原理圖如下
所有的服務都註冊到eureka中心,並且都是同一個名字microcloud-provider-dept,用戶通過eureka找到服務microcloud-provider-dept,然後通過ribbon負載均衡進行調用
1、 【microcloud-provider-dept-8001】將此項目復制為兩份“microcloud-provider-dept-8002”、 “microcloud-provider-dept-8003”;
2、 【microcloud-provider-dept-*】執行各自的數據庫腳本,隨後修改各自的數據庫連接配置;
3、 【microcloud-provider-dept-*】修改各自服務的 application.yml 配置文件;
server:
port: 8001
eureka:
client: # 客戶端進行Eureka註冊的配置
service-url:
defaultZone: http://edmin:[email protected]:7001/eureka,http://edmin:[email protected]:7002/eureka,http://edmin:[email protected]:7003/eureka
instance:
lease-renewal-interval-in-seconds: 2 # 設置心跳的時間間隔(默認是30秒)
lease-expiration-duration-in-seconds: 5 # 如果現在超過了5秒的間隔(默認是90秒)
instance-id: dept-8001.com # 在信息列表時顯示主機名稱
prefer-ip-address: true # 訪問的路徑變為IP地址
info:
app.name: study-microcloud
company.name: www.study.cn
build.artifactId: $project.artifactId$
build.version: $project.verson$
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis配置文件所在路徑
type-aliases-package: cn.study.vo # 定義所有操作類的別名所在包
mapper-locations: # 所有的mapper映射文件
- classpath:mybatis/mapper/**/*.xml
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 配置當前要使用的數據源的操作類型
driver-class-name: org.gjt.mm.mysql.Driver # 配置MySQL的驅動程序類
url: jdbc:mysql://localhost:3306/study8001 # 數據庫連接地址
username: root # 數據庫用戶名
password: mysqladmin # 數據庫連接密碼
dbcp2: # 進行數據庫連接池的配置
min-idle: 5 # 數據庫連接池的最小維持連接數
initial-size: 5 # 初始化提供的連接數
max-total: 5 # 最大的連接數
max-wait-millis: 200 # 等待連接獲取的最大超時時間
application:
name: microcloud-provider-dept
千萬記住,現在所有的服務名字一定要保持一致,如果不一致則會認為是兩個服務,無法進行負載均衡。
4、 修改項目中的 hosts 配置文件,在這個配置文件之中追加新的域名:
127.0.0.1 dept-8001.com
127.0.0.1 dept-8002.com
127.0.0.1 dept-8003.com
5、 【microcloud-provider-dept-*】在保證 Eureka 已經正確啟動之後啟動所有的部門微服務信息;
6、 【microcloud-consumer】啟動消費端,消費端在 RestTemplate 配置的時候使用了負載均衡的註解。
· 訪問地址:http://client.com/consumer/dept/list;
現在發現每一次獲取數據都是通過不同的微服務獲得的,所以現在同一個消費端就可以通過 Ribbon 實現了負載均衡配置處理。
2.3、自定義 Ribbon 配置
在之前使用了一個“@LoadBalanced”註解,該註解描述的是一個負載均衡,但是對於負載均衡也是可以由用戶修改負載均衡策略的。那麽如果說現在要想去修改這樣的策略,也是可以的,可以使用自定義的 LoadBalacne 的配置類。
1、 【microcloud-consumer-80】追加一個 LoadBalance 的配置類,這個類應該放在 SpringCloud 啟動後找不到的位置上,所以應該 做一個新的包;
package cn.study.commons.config; import org.springframework.context.annotation.Bean; import com.netflix.loadbalancer.IRule; public class MyLoadBalanceConfig { @Bean public IRule ribbonRule() { // 其中IRule就是所有規則的標準 return new com.netflix.loadbalancer.RandomRule() ; } }
2、 【microcloud-consumer-80】修改程序主類,追加 Ribbon 的配置操作:
package cn.study.microcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import cn.study.commons.config.MyLoadBalanceConfig; @SpringBootApplication @EnableEurekaClient @RibbonClient(name="ribbonClient",configuration=MyLoadBalanceConfig.class) public class Consumer_80_StartSpringCloudApplication { public static void main(String[] args) { SpringApplication.run(Consumer_80_StartSpringCloudApplication.class, args); } }
那麽此時就實現了負載均衡的配置處理操作。
3、 【microcloud-consumer-80】修改控制器程序類,在這個程序類之中可以通過負載均衡的客戶端獲取服務器的相關信息。
package cn.study.microcloud.controller; import java.util.List; import javax.annotation.Resource; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.vo.Dept; @RestController public class ConsumerDeptController { public static final String DEPT_GET_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/get/"; public static final String DEPT_LIST_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/list/"; public static final String DEPT_ADD_URL = "http://MICROCLOUD-PROVIDER-DEPT/dept/add?dname="; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @Resource private LoadBalancerClient loadBalancerClient ; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { ServiceInstance serviceInstance = this.loadBalancerClient.choose("MICROCLOUD-PROVIDER-DEPT") ; System.out.println( "【*** ServiceInstance ***】host = " + serviceInstance.getHost() + "、port = " + serviceInstance.getPort() + "、serviceId = " + serviceInstance.getServiceId()); Dept dept = this.restTemplate .exchange(DEPT_GET_URL + id, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } }
那麽此時後臺通過指定的負載均衡的配置程序,就可以取得即將操作的主機的信息。
2.4、禁用 Eureka 實現 Ribbon 調用
在之前使用的技術都是通過 Eureka 訪問了 Eureka 之中的指定名稱的服務而獲得了所需要的數據,但是在 Ribbon 設計的時候 考慮到了一個脫離 Eureka 使用的環境。如果要真進行了 Eureka 的脫離,代碼將變為如下的形式。
1、 【microcloud-consumer-ribbon】修改 application.yml 配置文件:
server: port: 80 ribbon: eureka: enabled: false microcloud-provider-dept: ribbon: listOfServers: http://dept-8001.com:8001,http://dept-8002.com:8002,http://dept-8003.com:8003
2、 【microcloud-consumer-ribbon】修改 RestConfig 配置程序類,不再需要使用“@LoadBalanced”註解:
package cn.study.microcloud.config; import java.nio.charset.Charset; import java.util.Base64; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.HttpHeaders; import org.springframework.web.client.RestTemplate; @Configuration public class RestConfig { @Bean public HttpHeaders getHeaders() { // 要進行一個Http頭信息配置 HttpHeaders headers = new HttpHeaders(); // 定義一個HTTP的頭信息 String auth = "studyjava:hello"; // 認證的原始信息 byte[] encodedAuth = Base64.getEncoder() .encode(auth.getBytes(Charset.forName("US-ASCII"))); // 進行一個加密的處理 // 在進行授權的頭信息內容配置的時候加密的信息一定要與“Basic”之間有一個空格 String authHeader = "Basic " + new String(encodedAuth); headers.set("Authorization", authHeader); return headers; } @Bean // @LoadBalanced public RestTemplate getRestTemplate() { return new RestTemplate(); } }
3、 【microcloud-consumer-ribbon】修改 ConsumerDeptController控制器,直接通過獲取主機和端口調用後臺微服務:
package cn.study.microcloud.controller; import java.net.URI; import javax.annotation.Resource; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.http.HttpEntity; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpMethod; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import cn.study.commons.config.MyLoadBalanceConfig; import cn.study.vo.Dept; @RestController @RibbonClient(name = "microcloud-provider-dept", configuration = MyLoadBalanceConfig.class) public class ConsumerDeptController { public static final String DEPT_REST_TOPIC = "microcloud-provider-dept"; @Resource private RestTemplate restTemplate; @Resource private HttpHeaders headers; @Resource private LoadBalancerClient loadBalancerClient; @RequestMapping(value = "/consumer/dept/get") public Object getDept(long id) { ServiceInstance serviceInstance = this.loadBalancerClient .choose(DEPT_REST_TOPIC); System.out.println( "【*** ServiceInstance ***】host = " + serviceInstance.getHost() + "、port = " + serviceInstance.getPort() + "、serviceId = " + serviceInstance.getServiceId() + "、uri = " + serviceInstance.getUri()); URI deptUri = URI.create(String.format("http://%s:%s/dept/get/" + id, serviceInstance.getHost(), serviceInstance.getPort())); Dept dept = this.restTemplate .exchange(deptUri, HttpMethod.GET, new HttpEntity<Object>(this.headers), Dept.class) .getBody(); return dept; } }
這種模式並不標準,只能夠說是 Ribbon 自己所具備的一項功能而已,實際之中如果不是非常必要,不建議使用。
SpringCloud系列五:Ribbon 負載均衡(Ribbon 基本使用、Ribbon 負載均衡、自定義 Ribbon 配置、禁用 Eureka 實現 Ribbon 調用)