Spring Cloud系列(四):Ribbon

简介

Ribbon(客服端负载均衡器) 是 Netflix 发布的云中间层服务开源项目,其主要功能是提供客户侧软件负载均衡算法,将 Netflix 的中间层服务连接在一起。Eureka 是一个 RESTful 服务,用来定位运行在 AWS 域(Region)中的中间层服务。(Nginx是属于服务端的负载均衡)

RESTful 是一种软件架构风格、设计风格,而不是标准,只是提供了一组设计原则和约束条件(设计原则和约束条件 )。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。

image
上图就是Ribbon的一个架构图,它工作时第一步先选择Eureka Server,而Eureka Server优先选择在同一个Zone且负载较少的Server,第二步在根据用户指定的负载均衡策略,再从Server取到的服务注册列表中选择一个地址。

它的负载均衡策略

RoundRobinRule:轮询策略,Ribbon以轮询的方式选择服务器

WeightedResponseTimeRule:带有加权的轮询策略,对各个服务器响应时间进行加权处理,然后在采用轮询的方式来获取相应的服务器

RandomRule:随机选择,也就是说Ribbon会随机从服务器列表中选择一个进行访问。

BestAvailableRule:最大可用策略,即先过滤出故障服务器后,选择一个当前并发请求数最小的

AvailabilityFilteringRule:可用过滤策略,先过滤出故障的或并发请求大于阈值一部分服务实例,然后再以线性轮询的方式从过滤后的实例清单中选出一个

ZoneAvoidanceRule:区域感知策略(默认),先使用主过滤条件(区域负载器,选择最优区域)对所有实例过滤并返回过滤后的实例清单,依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤,判断最小过滤数(默认1)和最小过滤百分比(默认0),最后对满足条件的服务器则使用RoundRobinRule(轮询方式)选择一个服务器实例。

另外也可以通过继承ClientConfigEnabledRoundRobinRule,来实现自己负载均衡策略。

指定或更改均衡策略(代码实现)

创建一个策略类:

/**
 * 这个类不能被 @ComponentScan | @SpringBootApplication 扫描到,SpringBootApplication 会扫描当前包和所有子包
 * 
 * @author: uncle
 * @apdateTime: 2017-12-11 15:49
 */
@Configuration
public class RibbonConfig {

    @Autowired
    private IClientConfig config;

    @Bean
    public IRule ribbonRule(IClientConfig config) {
        return new RandomRule(); // 指定随机策略
    }
}

服务消费者的启动类上添加:

@RibbonClient(name = "service id", configuration = RibbonConfig.class) // service id 是服务提供者的spring.application.name的名字

不过得注意,不能被上面说明的两个注解扫描到,否则就会导致策略会被共享。把策略类放到一个单独的包中就行。

如果一定要违背这个规则并且还想之前定义的策略不被共享的话,可以这么做:

先在同包里自定义一个注解类:

public @interface MyAnnotations { // 标记注解
}

然后在启动类上添加:

/* 覆盖 SpringBootApplication 中的 ComponentScan */
@ComponentScan(excludeFilters = {@ComponentScan.Filter(value = MyAnnotations.class)}) // http://blog.csdn.net/lindiwo/article/details/53406624

再在策略类上加上自定义的注解就行了。

@Configuration
@MyAnnotations 
public class RibbonConfig {

//    @Autowired
//    private IClientConfig config;

    @Bean
    public IRule ribbonRule(IClientConfig config) {
        return new RandomRule(); // 指定随机策略
    }
}

指定或更改均衡策略(配置文件)

其实,上面这么多麻烦的操作,只需要在配置文件中一步即可,惊不惊喜?我启动了四个service-user服务,其中两个叫service-user2,配置文件代码:

service-user: // service id,前提是此ID在eureka里是被注册了的
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

这里一个是指定service id,让此ID使用随机策略。配置文件的方式就是这么简单。(我在复制工程的时候,没有把复制后的工程的target删掉,导致测试结果一直不理想,所以想告诉大家,复制了之后工程之后,一定要把复制后的工程的target删除!!)

代码实现负载均衡

Eureka里面已经整合了Ribbon,所以无需加入依赖,启动类里创建一个Bean:

@EnableDiscoveryClient
@SpringBootApplication
public class SpringcloudEurakaInstanceOneApplication {

   // 重点
   @Bean
   @LoadBalanced // 此注解就可以让 restTemplate 拥有负载均衡的功能
   public RestTemplate restTemplate() {
      return new RestTemplate();
   }

   public static void main(String[] args) {
      SpringApplication.run(SpringcloudEurakaInstanceOneApplication.class, args);
   }
}

上面生成了一个RestTemplate实例,RestTemplate是spring内置的http请求封装,也就是Spring封装好的一个http请求的客户端,详细的API说明请参考:Class RestTemplate

还有一个LoadBalancerClient实例,是cloud自带的。深入理解Ribbon之源码解析。下面是此文章内容的部分摘要。

controller:

@RestController
@RequestMapping(value = "/movie-ribbon/")
public class MovieRibbonController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping(value = "/{id}")
    public User findById(@PathVariable(value = "id") Long id) {
        // 消费另外一个微服务的API                    http://spring.application.name/api
        return restTemplate.getForObject("http://service-user/user/" + id, User.class); // 这里只需要服务的service id就行,不需要端口号和IP地址,解决了最开始的服务发现和消费的硬编码问题
    }
}

上面这些做好之后,我启动了一个Eureka,一个movie-ribbon,两个user(关于一个项目使用多个端口的方式,eclipse和IDEA是不一样的,具体可以自行百度),然后访问movie-ribbon的API,就可以实现负载均衡:
image
上图是访问之后User:8080端口的日志,下图是User:8083的日志
image

单独使用Ribbon

之前的例子都是Ribbon和Eureka连用,如果想单独使用,情况有两种:

  • 没有引用Eureka依赖
  • 有Eureka,但是需要禁用掉
    没有Eureka依赖的话,只需要在(客户端)配置文件中添加:
stores: // 请求的服务端的service id
  ribbon:
    listOfServers: example.com,google.com // url

如果有,则是:

// 先禁用
ribbon:
  eureka:
   enabled: false
   
// 再使用
stores:
  ribbon:
    listOfServers: example.com,google.com
    
// 例
service-user:
  ribbon
    listOfServers: localhost:8080 // 现在客户端请求服务端时,即使服务端的spring.application.name相同,也只会访问端口是8080的
LoadBalancerClient(RibbonAPI)

LoadBalancerClient是一个接口,总共三个方法:

public interface LoadBalancerClient extends ServiceInstanceChooser {
    // 此方法用来执行请求
    <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;

    <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
    // 此方法用来重构URI
    URI reconstructURI(ServiceInstance var1, URI var2);
}

它继承的接口中有一个方法:

public interface ServiceInstanceChooser {
    ServiceInstance choose(String serviceId); // 根据传入的serviceId得到服务的实例,得到的实例中包含了很多信息
}

ok了。

Last modification:January 11th, 2018 at 11:15 am
If you think my article is useful to you, please feel free to appreciate

Leave a Comment