springboot+Ribbon实现负载均衡 Ribbon官网地址https://github.com/Netflix/ribbon/wiki/Getting-Started Ribbon虽然现在也进入到了维护阶段,但是由于有很多的项目还在使用它所以还是很有潜力的一个工具。 Ribbon是一个属于进程内的负载均衡工具,这个和ngnix是不一样的,ngnix是一个集中式的负载均衡器。 这里举个例子:ngnix就是我们进入学校的一个大门,Ribbon就相当于我们进入学校后要去到的班级。 Ribbon默认采用的是轮询的算法,负载均衡+RestTemplate调用,Ribbon主要就是和RestTemplate进行配合使用。如果想要实现负载均衡只需要在客户端的RestTemplate类中配置注解 @LoadBalanced即可实现轮询的算法

@Configuration
public class ApplicationContextConfig {
    @Bean
    //使用轮询的负载均衡 @LoadBalanced
    @LoadBalanced
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}

总结:Ribbon其实就是一个软负载均衡的客户端组件,他可以和其他所需请求的客户端结合使用,和eureka结合只是其中的一个实例。 在新的spring-cloud-starter-netflix-eureka-client中默认是有Ribbon的所以不用再添加pom

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

在spring-cloud-starter-consul-discovery也是默认存在Ribbon的

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-consul-discovery</artifactId>
        </dependency>

在 spring-cloud-starter-zookeeper-discovery也是默认存在Ribbon的

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-zookeeper-discovery</artifactId>
        </dependency>

后续如果还有别的默认Ribbon的将再更新 Ribbon出了默认的轮询还有哪些负载均衡算法

com.netflix.loadbalancer.RoundRobinRule 轮询

com.netflix.loadbalancer.RandomRule 随机

com.netflix.loadbalancer.RetryRule 先按照RoundRobinRule的策略获取服务,如果获取服务失败则在指定时间内会进行重试

WeightedResponseTimeRule 对RoundRobinRule的扩展,响应速度越快的实例选择权重越大,越容易被选择

BestAvailableRule 会先过滤掉由于多次访问故障而处于断路器跳闸状态的服务,然后选择一个并发量最小的服务

AvailabilityFilteringRule 先过滤掉故障实例,再选择并发较小的实例

ZoneAvoidanceRule默认规则,复合判断server所在区域的性能和server的可用性选择服务器

这些算法都可以通过替换轮询来自定义实现Ribbon的负载均衡

下面就是如何来替换默认的轮询算法 这里需要特别注意的是不要将其他算法的实现配置类放到能被@ComponentScan扫描到的包下,如果放到能被扫描到的包下那么我们自定义的这个Ribbon的配置类就会被Ribbon的客户端共享,这样就起不到自定义的效果了。 比如@ComponentScan能扫描到的包为com.cn.ribbon,那么我们在自定义配置类时最少也得到com.cn.ribbon1路径的上一次或者干脆就写个不一样的路径。 生产者就不再重复新建了需要的请看

消费者需要修改 这里我就将修改的部分贴出需要其他内容的看上面的连接 新建一个不被@ComponentScan扫描到的路径然后在该路径下新建一个配置类MySelfRule

@Configuration
public class MySelfRule {
    @Bean
    public IRule myRule(){
        
        return new RandomRule();//定义为随机
         }
         }

然后在启动类上添加注解@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class) 这样就表示使用自定义的负载均衡算法代替默认的轮询算法

@SpringBootApplication
@EnableEurekaClient
@RibbonClient(name = "CLOUD-PAYMENT-SERVICE",configuration = MySelfRule.class)
public class OrderMain {
    public static void main(String[] args) {
        SpringApplication.run(OrderMain.class,args);
    }
}

启动项目运行接口就可以看到随机访问。

手写一个简单的负载均衡算法

服务器轮询负载均衡原理

Java负载均衡依赖 springboot负载均衡_算法

Ribbon各种算法的结构树

Java负载均衡依赖 springboot负载均衡_算法_02

手写一个简单的轮询算法

在生产者的controller中添加一个获取当前端口号的方法

@GetMapping(value = "/payment/lb")public String getPaymentLB(){    return serverPort;}

消费者端先将ApplicationContextBean去掉@LoadBalanced 写一个LoadBalancer接口

public interface LoadBalancer {     
//收集服务器总共有多少台能够提供服务的机器,并放到list里面    
ServiceInstance instances(List<ServiceInstance> serviceInstances);}

实现LoadBalancer接口

@Component
public class MyLB implements LoadBalancer {   
 private AtomicInteger atomicInteger = new AtomicInteger(0);    //坐标   
  private final int getAndIncrement(){      
    int current; 
           int next;   
                do { 
                           current = this.atomicInteger.get();    
                                   next = current >= 2147483647 ? 0 : current + 1;   
                                        }while (!this.atomicInteger.compareAndSet(current,next));  //第一个参数是期望值,第二个参数是修改值是     
                                           System.out.println("*******第几次访问,次数next: "+next);        return next;    }   
                                            @Override 
                                               public ServiceInstance instances(List<ServiceInstance> serviceInstances) { 
                                                //得到机器的列表     
                                                  int index = getAndIncrement() %serviceInstances.size(); 
                                                  //得到服务器的下标位置     
                                                     return serviceInstances.get(index);    }}

controller类

@GetMapping(value = "/consumer/payment/lb")   
  public String getPaymentLB(){     
     List<ServiceInstance> instances = discoveryClient.getInstances("CLOUD-PAYMENT-SERVICE");      
       if (instances == null || instances.size() <= 0){     
              return null;     
                 }        
                 ServiceInstance serviceInstance = loadBalancer.instances(instances);      
                   URI uri = serviceInstance.getUri();    
              return restTemplate.getForObject(uri+"/payment/lb",String.class);    }