Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。简单的说,就是在配置文件中列出Load Balancer(简称LB)后面所有的机器,Ribbon会自动的帮助你基于某种规则(如简单轮询,随即连接等)去连接这些机器。我们也很容易使用Ribbon实现自定义的负载均衡算法。
说起负载均衡一般都会想到服务端的负载均衡,常用产品包括LBS硬件或云服务、Nginx等,都是耳熟能详的产品。
而Spring Cloud提供了让服务调用端具备负载均衡能力的Ribbon,通过和Eureka的紧密结合,不用在服务集群内再架设负载均衡服务,很大程度简化了服务集群内的架构。
一、pom引入包
-
<!-- Ribbon相关 Spring Cloud Ribbon是基于Netflix 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> <!--Ribbon相关end-->
二、application.yml配置
-
server: port: 8080 #从eureka中获取微服务 eureka: client: register-with-eureka: false service-url: defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
eureka服务端为集群,因此defalutZone配置3个url,如果为单个则配置1个即可。
三、实现负载均衡
3.1默认最简单负载均衡方式
在配置类中,用@LoadBalanced注解,放置在获取RestTemplate的方法上:
-
@Bean @LoadBalanced public RestTemplate getRestTemplate(){ return new RestTemplate(); }
启动类中:
-
@SpringBootApplication @EnableEurekaClient @RibbonClient(name="MICROSERVICECLOUD-DEPT") public class DeptConsumer80_App { public static void main( String[] args ) { SpringApplication.run(DeptConsumer80_App.class,args); } }
RibbonClient注解里面的name为微服务提供者的名称,本例中由8001,8002,8003组成MICROSERVICECLOUD-DEPT集群,这样子即可完成Ribbon的负载均衡,此默认策略为轮训方式,除此以外还有其余其中策略:
-
RoundRobinRule 轮询 RandomRule 随机 AvaliabilityFilteringRule 会先过滤由于多次访问故障而处于断路器跳闸的状态的服务和并发的连接数量超过阈值的服务,然后对剩余的服务列表按照轮询策略 WeightedResponseTimeRule 根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大 RetryRule 先按照RoundRobinRule策略获取服务,如果获取服务失败会在指定时间内重试 BestAvailableRule 会先过滤掉由于多次访问故障二处于断路器跳闸状态的服务,然后选择一个并发量最小的服务 ZoneAvoidanceRule 默认规则,复合判断server所在的区域的性能和server的可用性选择服务器
3.2只用其他策略:
要使用其他策略,只需要在配置类中执行创建个方法,返回IRule对象,方法的具体实现中返回相应策略的对象即可
-
@Bean public IRule myRule(){ return new RandomRule();// 随机 }
3.3自定义负载均衡策略:
有时候提供的几种负载均衡策略无法满足实际需求,需要自定义负载均衡策略
3.3.1创建个配置类,不能和启动类在同一个包(非常重要)
-
package com.myrule; import com.netflix.loadbalancer.IRule; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; /** * Created by YQ11053 on 2018/10/5 0005. */ @Configuration public class MySelfRule { @Bean public IRule myRule(){ // return new RetryRule(); //定义为RetryRule return new RoundRobinRule_ZHF();//自定义负载均衡方式 } }
自定义负载均衡类,需要继承AbstractLoadBalancerRule类
-
package com.myrule; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.AbstractLoadBalancerRule; import com.netflix.loadbalancer.ILoadBalancer; import com.netflix.loadbalancer.Server; import java.util.List; /** * Created by YQ11053 on 2018/10/5 0005. */ public class RoundRobinRule_ZHF extends AbstractLoadBalancerRule{ //自定义需求:轮训策略,每台执行5次后进行切换 // total = 0 // 当total==5以后,我们指针才能往下走, // index = 0 // 当前对外提供服务的服务器地址, // total需要重新置为零,但是已经达到过一个5次,我们的index = 1 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) { /* * No servers. End regardless of pass, because subsequent passes only get more * restrictive. */ return null; } // int index = rand.nextInt(serverCount);// java.util.Random().nextInt(3); // server = upList.get(index); // 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; } } if (server == null) { /* * The only time this should happen is if the server list were somehow trimmed. * This is a transient condition. Retry after yielding. */ 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) { // TODO Auto-generated method stub } }
本实例实现的自定义策略为每台执行5次后进行轮训,如果其他自定义策略,只需要在代码相应地方进行算法修改即可。
项目结构为:
3.3.2主启动类中
-
package com.zhanghf; import com.myrule.MySelfRule; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; /** * Hello world! * */ @SpringBootApplication @EnableEurekaClient //自定义Ribbon的轮训策略:在启动该微服务的时候就能去加载我们的自定义Ribbon配置类,从而使配置生效 //非常重要:MySelfRule这个类不能创建在和启动类同一个包,即com.zhanghf;下 @RibbonClient(name="MICROSERVICECLOUD-DEPT",configuration=MySelfRule.class) public class DeptConsumer80_App { public static void main( String[] args ) { SpringApplication.run(DeptConsumer80_App.class,args); }