SpringCloud之Ribbon负载均衡
Ribbon是什么?
Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。
简单的说,Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随机连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
LB(负载均衡)
LB,即负载均衡(Load Balance),在微服务或分布式集群中经常用的一种应用。
负载均衡简单的说就是将用户的请求平摊的分配到多个服务上,从而达到系统的HA(高可用)。
常见的负载均衡有软件Nginx,LVS,硬件 F5等。
相应的在中间件,例如:dubbo和SpringCloud中均给我们提供了负载均衡,SpringCloud的负载均衡算法可以自定义。
集中式LB
即在服务的消费方和提供方之间使用独立的LB设施(可以是硬件,如F5, 也可以是软件,如nginx), 由该设施负责把访问请求通过某种策略转发至服务的提供方;
进程内LB
将LB逻辑集成到消费方,消费方从服务注册中心获知有哪些地址可用,然后自己再从这些地址中选择出一个合适的服务器。
Ribbon就属于进程内LB,它只是一个类库,集成于消费方进程,消费方通过它来获取到服务提供方的地址。
官网资料
https://github.com/Netflix/ribbon/wiki/Getting-Started
案例实践
要实现负载均衡,首先要有多个订单服务提供者,目前我们就一个 microservice-order-provider8001,端口号 8001,我们可以仿照这个服务,再创建两个子模块,也是订单服务提供者,取名为 microservice-order-provider8002,端口号 8002 和 microservice-order-provider8003,端口号 8003。
现在三个订单服务有了,再加上 Ribbon 和 Eureka 集群后,系统的架构如下:
创建一个名为microservicecloud-consumer-dept-80的springboot工程,并在pom中添加依赖
<!-- Ribbon相关 --> <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.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
配置yml文件
server: port: 80 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
由于springcloud采用的是Rest通信,所以
创建一个配置类,将RestTemplate添加到容器里,并添加@LoadBalanced注解
package com.atguigu.springcloud.cfgbeans; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; /** * 配置类 * @ClassName: ConfigBean * @Description:TODO * @author: jp * @date: 2019年6月3日 下午4:42:09 * * @Copyright: 2019 www.tydic.com Inc. All rights reserved. * */ @Configuration public class ConfigBean { @Bean @LoadBalanced//Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。 public RestTemplate getRestTemplate() { return new RestTemplate(); } }
controller控制器
package com.atguigu.springcloud.controller; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import com.atguigu.springcloud.entities.Dept; /** * 消费方控制器 * @ClassName: DeptController_Consumer * @Description:TODO * @author: jp * @date: 2019年6月3日 下午4:48:40 * * @Copyright: 2019 www.tydic.com Inc. All rights reserved. * */ @RestController public class DeptController_Consumer { /** * 使用 使用restTemplate访问restful接口非常的简单粗暴无脑。 (url, requestMap, * ResponseBean.class)这三个参数分别代表 REST请求地址、请求参数、HTTP响应转换被转换成的对象类型。 */ @Autowired RestTemplate restTemplate; //private static final String REST_URL_PREFIX = "http://localhost:8001"; //之前是直接通过对目的的服务提供者获取 private static final String REST_URL_PREFIX = "http://MICROSERVICECLOUD-DEPT"; //现在通过euraka获取服务 @RequestMapping(value = "/consumer/dept/add") public boolean add(Dept dept) { return restTemplate.postForObject(REST_URL_PREFIX + "/dept/add", dept, Boolean.class); } @RequestMapping(value = "/consumer/dept/get/{id}") public Dept get(@PathVariable("id") Long id) { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/get/" + id, Dept.class); } @SuppressWarnings("unchecked") @RequestMapping(value = "/consumer/dept/list") public List<Dept> list() { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/list", List.class); } // 测试@EnableDiscoveryClient,消费端可以调用服务发现 @RequestMapping(value = "/consumer/dept/discovery") public Object discovery() { return restTemplate.getForObject(REST_URL_PREFIX + "/dept/discovery", Object.class); } }
启动类
package com.atguigu.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication @EnableEurekaClient public class DeptConsumer80_App { public static void main(String[] args) { // TODO Auto-generated method stub SpringApplication.run(DeptConsumer80_App.class, args); } }
依次将 注册中心eureka-7001、eureka-7002、eureka-7003
服务提供者provider-dept-8001、provider-dept-8002、provider-dept-8003
服务消费者consumer-dept-80启动。
然后浏览器输入:
http://localhost/consumer/dept/get/1,可以得到三种不同效果
Ribbon核心组件IRule
Ribbon的源码地址:https://github.com/Netflix/ribbon
IRule:根据特定的算法中从服务器列表中选取一个要访问的服务,Ribbon默认的算法是轮询算法。
Ribbon中的7种负载均衡算法:
(1)RoundRobinRule:轮询;
(2)RandomRule:随机;
(3)AvailabilityFilteringRule:会先过滤掉由于多次访问故障而处于断路器状态的服务,还有并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略进行访问;
(4)WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快的服务权重越大被选中的概率越大。刚启动时如果统计信息不足,则使用RoundRobinRule(轮询)策略,等统计信息足够,会切换到WeightedResponseTimeRule;
(5)RetryRule:先按照RoundRobinRule(轮询)策略获取服务,如果获取服务失败则在指定时间内进行重试,获取可用的服务;
(6)BestAvailableRule:会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务;
(7)ZoneAvoidanceRule:复合判断Server所在区域的性能和Server的可用性选择服务器;
配置指定的负载均衡算法
打开consumer-dept-80 消费者工程,增加如下配置:
package com.atguigu.springcloud.cfgbeans; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RetryRule; @Configuration public class ConfigBean //boot -->spring applicationContext.xml --- @Configuration配置 ConfigBean = applicationContext.xml { @Bean @LoadBalanced//Spring Cloud Ribbon是基于Netflix Ribbon实现的一套客户端 负载均衡的工具。 public RestTemplate getRestTemplate() { return new RestTemplate(); } //配置负载均衡的策略为随机,默认算法为轮询算法 @Bean public IRule myRule() { //这里可以修改算法 //return new RoundRobinRule(); return new RandomRule();//达到的目的,用我们重新选择的随机算法替代默认的轮询。 //return new RetryRule(); } }
然后重启这个消费者服务,
访问:http://localhost/consumer/dept/get/1
查看到随机访问到3个生产者服务