Ribbon是一個為客戶端提供負載均衡功能的服務,它內部提供了一個叫做ILoadBalance的接口代表負載均衡器的操作,比如有添加服務器操作、選擇服務器操作、獲取所有的服務器列表、獲取可用的服務器列表等等。
需要解決的問題:
① 如何在配置Eureka Client注冊中心時不去硬編碼Eureka Server的地址?
② 在微服務不同模塊間進行通信時,如何不去硬編碼服務提供者的地址?
③ 當部署多個相同微服務時,如何實現請求時的負載均衡?
Ribbon是什么?
Ribbon是Netflix發布的雲中間層服務開源項目,其主要功能是提供客戶端實現負載均衡算法。Ribbon客戶端組件提供一系列完善的配置項如連接超時,重試等。簡單的說,Ribbon是一個客戶端負載均衡器,我們可以在配置文件中Load Balancer后面的所有機器,Ribbon會自動的幫助你基於某種規則(如簡單輪詢,隨機連接等)去連接這些機器,我們也很容易使用Ribbon實現自定義的負載均衡算法。
下圖展示了Eureka使用Ribbon時的大致架構:
SpringCloud之Ribbon入門案例
① 首先引入Ribbon依賴,Ribbon的使用依賴Eureka:
<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>
② 如何使用Ribbon
使用RestTemplate進行Eureka Client(包括服務提供者以及服務消費者,在這里其實是服務消費者使用RestTemplate)之間的通信,為RestTemplate配置類添加@LoadBalanced注解即可,如下所示:
@Bean @LoadBalanced public RestTemplate restTemplate() { return new RestTemplate(); }
主程序:
@SpringBootApplication @EnableEurekaClient //在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效 @RibbonClient(name="MICROSERVICECLOUD-DEPT") public class DeptConsumer80_App{ public static void main(String[] args){ SpringApplication.run(DeptConsumer80_App.class, args); } }
③ 如何解決硬編碼
使用添加@LoadBalanced注解后的RestTemplate調用服務提供者的接口時,可以使用虛擬IP替代真實IP地址。所謂的虛擬IP就是服務提供者在application.properties或yml文件中配置的spring.application.name屬性的值。示例如下:
以前:
@RestController public class DeptController_Consumer { private static final String REST_URL_PREFIX = "http://localhost:8001"; //需要ip+端口 @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/consumer/dept/add") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class); } }
使用Ribbon后:
@RestController public class DeptController_Consumer { private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; //微服務的虛擬id @Autowired private RestTemplate restTemplate; @RequestMapping(value = "/consumer/dept/add") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class); } }
小總結:Ribbon和Eureka整合后Consumer可以直接調用服務而不用再關心ip地址和端口號
微服務(服務提供者)集群搭建:
機器1 server: port: 8001 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/cloudDB01 username: root password: 123456 機器2 server: port: 8002 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/cloudDB02 username: root password: 123456 機器3 server: port: 8003 spring: application: name: microservicecloud-dept datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/cloudDB03 username: root password: 123456
其中{Spring.application.name}都是一樣的,不可以變。
Ribbon組件IRule
默認的是RoundBobinRule(輪詢)
RetryRule
- 1、先按照RoundRobinRule(輪詢)的策略獲取服務,如果獲取的服務失敗側在指定的時間會進行重試,進行獲取可用的服務
- 2、如多次獲取某個服務失敗,這不會再再次獲取該服務如(高德地圖上某條道路堵車,司機不會走那條道路)=
使用:
@Configuration public class ConfigBean //boot -->spring applicationContext.xml --- @Configuration配置 ConfigBean = applicationContext.xml { @Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Bean public IRule myRule(){ //return new RoundRobinRule(); //return new RandomRule();//達到的目的,用我們重新選擇的隨機算法替代默認的輪詢。 return new RetryRule(); //在這里選擇負載均衡算法 } }
自定義負載均衡算法:
所謂的自定義Ribbon Client的主要作用就是使用自定義配置替代Ribbon默認的負載均衡策略,注意:自定義的Ribbon Client是有針對性的,一般一個自定義的Ribbon Client是對一個服務提供者(包括服務名相同的一系列副本)而言的。自定義了一個Ribbon Client 它所設定的負載均衡策略只對某一特定服務名的服務提供者有效,但不能影響服務消費者與別的服務提供者通信所使用的策略。根據官方文檔的意思,推薦在 springboot 主程序掃描的包范圍之外進行自定義配置類。其實純代碼自定義RibbonClient的話有兩種方式:
方式一:在springboot主程序掃描的包外定義配置類,然后為springboot主程序添加 @RibbonClient 注解引入配置類。
配置類不應該在SpringBoot的包路徑下,通過@RibbonClient 注解加載:
@Configuration public class MySelfRule { @Bean public IRule myRule() { return new RandomRule_ZY(); // 我自定義為每台機器5次,5次之后在輪詢到下一個 } }
springboot主程序:
@SpringBootApplication @EnableEurekaClient //在啟動該微服務的時候就能去加載我們的自定義Ribbon配置類,從而使配置生效 @RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class) public class DeptConsumer80_App { public static void main(String[] args) { SpringApplication.run(DeptConsumer80_App.class, args); } }
自定義LoadBalance:
public class RandomRule_ZY extends AbstractLoadBalancerRule{ // total = 0 // 當total==5以后,我們指針才能往下走, // index = 0 // 當前對外提供服務的服務器地址, // total需要重新置為零,但是已經達到過一個5次,我們的index = 1 // 分析:我們5次,但是微服務只有8001 8002 8003 三台,OK? private int total = 0; // 總共被調用的次數,目前要求每台被調用5次 private int currentIndex = 0; // 當前提供服務的機器號 public Server choose(ILoadBalancer lb, Object key){ if (lb == null) { return null; } Server server = null; while (server == null) { if (Thread.interrupted()) { return null; } List<Server> upList = lb.getReachableServers(); //激活可用的服務 List<Server> allList = lb.getAllServers(); //所有的服務 int serverCount = allList.size(); if (serverCount == 0) { return null; } if(total < 5){ server = upList.get(currentIndex); total++; }else { total = 0; currentIndex++; if(currentIndex >= upList.size()){ currentIndex = 0; } } if (server == null) { Thread.yield(); continue; } if (server.isAlive()) { return (server); } // Shouldn't actually happen.. but must be transient or a bug. server = null; Thread.yield(); } return server; } @Override public Server choose(Object key){ return choose(getLoadBalancer(), key); } @Override public void initWithNiwsConfig(IClientConfig clientConfig){ } }