SpringCloud中提供了一个组件Ribbon,Spring Cloud Ribbon是一个基于Http和TCP的客服端负载均衡工具,它是基于Netflix Ribbon实现的。
对于SpringCloud来说为什么要实现负载均衡?负载均衡对于集群或者是分布式来说都是一种常见的手段用于减轻服务器压力,并不单单针对于微服务,而在另外的一套分布式架构Zookeeper,Dubbo,常用的软负载均衡是nginx。在SpringCloud中,服务提供者可能会有多个,而用户同过消费者去访问提供者,自然而然要需要负载均衡去实现各服务器平均分担压力。下面我们在构建负载均衡的微服务之前,先来搭建Eureka和Ribbon的整合,达到真正的通过rest访问微服务名称来调用微服务。
对于消费者来说,它是访问的入口,所以它要加上Ribbon的依赖,而Ribbon要需要依赖于Eureka,所以也要把Eureka的依赖加上:
<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-config</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-test</artifactId><scope>test</scope></dependency></dependencies>
在application.yml中加上:
server:port: 80eureka:client:register-with-eureka: falseservice-url:defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
端口号为80端口,配置与Eureka服务注册中心连接,这里配上连接主要是消费者会先从Eureka中获取提供者的信息,包括服务应用名称,IP地址,端口号等等。
启动类:
@SpringBootApplication@EnableEurekaClientpublic class EurekaServerConsumerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerConsumerApplication.class, args);}}
然后关键的一点来了!在我们之前搭建的项目中,消费者通过RestTempete的具体url去访问提供者,而对于真正的微服务来说,这是不合规范的,而实现负载均衡也不能这样写,应该是通过微服务应用名称去访问提供者。
首先在RestTemplate上加上@LoadBanlanced注解,加上这个注解后RestTemplate才能识别出微服务应用名称去访问提供者,否则不能访问。
@Configurationpublic class ConfigBean {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}}
通过微服务应用名称访问,http://微服务应用名称/url,这里我设置的微服务名称为EUREKA-SERVER-PROVIDER
@RequestMapping("/consumer/test3")public String test3(){return restTemplate.getForObject("http://EUREKA-SERVER-PROVIDER/provider/test",String.class);}
这样Eureka与Ribbon整合实现通过微服务应用名称去访问提供者就成功了。
上面只是实现了通过微服务应用名称去访问提供者,但是并没有实现负载均衡,因为负载均衡就要基于通过微服务应用名称去访问提供者实现的,所以我们上一步的搭建是为了实现负载均衡打得铺垫。下面我们来实现负载均衡。
我们创建并启动三个服务提供者,分别为8001,8002和8003,并且设置它们的微服务应用名称且一致。
8001配置文件,8002和8003的配置文件都与8001的类似,只是设置的端口号不同:
server:port: 8001eureka:client:service-url:defaultZone: :7001/eureka/,:7002/eureka/,:7003/eureka/ #Eureka Server urlinstance-id: eureka-server-provider-test #修改主机名称prefer-ip-address: true #设置主机ip映射spring:application:name: eureka-server-provider #该微服务应用名称
然后我们回过头来看看消费者的配置文件,我们上面在消费者的application.yml中设置了
eureka:client:register-with-eureka: falseservice-url:defaultZone: http://localhost:7001/eureka/,http://localhost:7002/eureka/,http://localhost:7003/eureka/
这个设置非常重要,我们可以尝试一旦不设置,用户通过消费者访问就会发生下面的错误:
我们可以看到有这么一句No instances available for EUREKA-SERVER-PROVIDER,意思就是找不到名称为EUREKA-SERVER-PROVIDER的微服务,那么我们去注册中心中看看这个微服务到底存不存在
可以发现,这个微服务是存在的,那么可以证明消费者在访问前是要通过查询注册中心中提供者的信息,比如微服务应用名称,ip地址,端口号等等这些信息, 然后通过获取到的微服务应用名称才能去访问到提供者,而怎样去注册中心中获取到这些信息的就是要配置上面的与注册中心的连接了。
言归正传,在8001,8002和8003服务提供者中加入下面的RequestMapping进行测试。
8001:
@RequestMapping("/provider/test")public String test(){return "这是provider01";}
8002:
@RequestMapping("/provider/test")public String test(){return "这是provider02";}
8003:
@RequestMapping("/provider/test")public String test(){return "这是provider03";}
然后我们把Eureka集群701,7002和7003,服务提供者8001,8002和8003,以及服务消费者80启动,通过消费者访问http://localhost/consumer/test
第一次访问:
第二次访问:
第三次访问:
然后下面每一次访问都是02,01,03(轮询算法,Ribbon默认),所以我们的负载均衡实现成功了。
总结:我们上面启动了7个微服务进程,其中3个Eureka注册中心组成一个集群,3个服务提供者(微服务应用名称都想同的是一组服务提供者),一个服务消费者,服务消费者启动后会连接Eureka注册中心获取到注册成功的服务提供者的微服务应用名称(还有其他信息),然后保存到本地,下次用户通过消费者去访问提供者的时候,根据之前保存的多个提供者的信息(微服务应用名称)去访问提供者,根据@LoadBanlanced注解实现负载均衡的目的,而如果提供者宕机后,注册中心会通知到消费者及时更新之前保存的提供者的信息,这样,服务消费者,注册中心,服务提供者就能协同起来一起工作了。
下面上一张图说明上面的总结:
更换Ribbon默认的负载均衡策略
在Ribbon中默认的负载均衡策略是通过轮询的方式进行的,而轮询算法策略是通过实现的IRule接口
在Ribbon中还有其他Ribbon已经帮我们实现的算法策略,分别是:
RoundRobinRule 轮询RandomRule 随机AvailabilityFilteringRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,还有并发的连接数超过阈值的服务,然后对剩余的服务列表进行轮询WeightedResponseTimeRule 权重 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越高。刚启动时,如果统计信息不足,则使用轮询策略,等信息足够,切换到 WeightedResponseTimeRule
RetryRule 重试 先按照轮询策略获取服务,如果获取失败则在指定时间内重试,获取可用服务BestAvailableRule 选过滤掉多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务ZoneAvoidanceRule 符合判断server所在区域的性能和server的可用性选择服务
所以如果我们想要更换算法策略,就要告诉Ribbon我们需要的策略。
@Configurationpublic class ConfigBean {@Bean@LoadBalancedpublic RestTemplate restTemplate(){return new RestTemplate();}@Beanpublic IRule iRule(){return new RandomRule();}}
这样如果我们没有告诉Ribbon,那么Ribbon就使用默认的轮询算法策略,如果我们在配置类中声明了,那么就是用我们声明的算法策略。
自定义算法策略
除了上面Ribbon自带的算法策略,我们还可以自己实现算法策略
首先新建一个配置类,注意,这个配置类不能是@ComponentScans包以及子包下,而我们的@SpringbootApplication注解便是继承了@ComponentScans注解,所以我们的配合类不能与启动类同包或者在其子包下。
自定义轮询算法,每台服务器访问5次再到下一台服务器访问5次。以此类推
private int total = 0; // 总共被调用的次数,目前要求每台被调用5次private int currentIndex = 0;// 当前提供服务的机器号if(total < 5){server = upList.get(currentIndex);total++;}else {total = 0;currentIndex++;if(currentIndex >= upList.size()){currentIndex = 0;}}
最后在启动类中加上@RibbonClient注解,属性name是声明作用与哪一组服务提供者,configuration是我们刚才定义的配置类
@SpringBootApplication@EnableEurekaClient@RibbonClient(name = "MICROSERVICECLOUD-DEPT",configuration = MyRule.class)public class EurekaServerConsumerApplication {public static void main(String[] args) {SpringApplication.run(EurekaServerConsumerApplication.class, args);}}
这样就完成了我们自定义算法策略的实现了。
如果觉得《Idea搭建SpringCloud(三)------Ribbon实现负载均衡及其自定义算法策略》对你有帮助,请点赞、收藏,并留下你的观点哦!