SpringCloud详解(二):客户端负载均衡Ribbon

1、客户端负载均衡

我们通常所说的负载均衡都指的是服务端负载均衡,其中分为硬件负载均衡和软件负载均衡

硬件负载均衡主要通过在服务器节点之间安装专门用于负载均衡的设备,比如F5等;软件负载均衡则是通过在服务器上安装一些具有均衡负载功能或模块的软件来完成请求分发工作,比如Nginx等
在这里插入图片描述
硬件负载均衡的设备或是软件负载均衡的软件模块都会维护一个下挂可用的服务端清单,通过心跳检测来剔除故障的服务端节点以保证清单中都是可以正常访问的服务端节点。当客户端发送请求到负载均衡设备的时候,该设备按某种算法(比如线性轮询、按权重负载、按流量负载等)从维护的可用服务端清单中取出一台服务端的地址,然后进行转发

而客户端负载均衡和服务端负载均衡最大的不同点在于服务清单所存储的位置。在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端的清单来自于服务注册中心。在客户端负载均衡中也需要心跳去维护服务端清单的健康性,这个步骤需要与服务注册中心配合完成

通过SpringCloud Ribbon的封装,我们在微服务架构中使用客户端负载均衡调用只需要如下两步:

  • 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心
  • 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用

2、RestTemplate详解

在上一篇博客中,已经通过引入Ribbon实现了服务消费者的客户端负载均衡功能,其中使用了RestTemplate对象。这个对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced还能够开启客户端负载均衡。下面将介绍一下RestTemplate针对几种不同请求类型和参数类型的服务调用实现

1)、GET请求

在这里插入图片描述
getForEntity方法返回的是ResponseEntity,该对象是Spring对HTTP请求响应的封装,其中主要存储了HTTP的几个重要元素,比如HTTP请求状态码的枚举对象HttpStatus、在它的父类HttpEntity中还存储着HTTP请求的头信息对象HttpHeaders以及泛型类型的请求体对象

getForEntity函数提供了以下三种不同的重载实现

  • getForEntity(String url, Class<T> responseType, Object... uriVariables):该方法提供了三个参数,其中url为请求的地址,responseType为请求响应体body的包装类型,uriVariables为url中的参数绑定

访问HELLO-SERVICE服务的/hello请求,同时最后一个参数tom会替换url中的{1}占位符,而返回的ResponseEntity对象中的body内容类型会根据第二个参数转换为String类型

        ResponseEntity<String> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello?name={1}", String.class, "tom");
        String body = responseEntity.getBody();

如果希望返回的body是一个User对象类型

        ResponseEntity<User> responseEntity = restTemplate.getForEntity("http://HELLO-SERVICE/hello?name={1}", User.class, "tom");
        User user = responseEntity.getBody();
  • getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables):使用该方法进行参数绑定时需要在占位符中指定Map中参数的key值
        Map<String, String> params = new HashMap<>();
        params.put("name", "tom");
        String body = restTemplate.getForEntity("http://HELLO-SERVICE/hello?name={name}", String.class, params).getBody();
  • getForEntity(URI url, Class<T> responseType):该方法使用URI对象替代之前的url和uriVariables参数来指定访问地址和参数绑定。java.net.URI表示一个统一资源标识符引用
        UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/hello?name={name}").build().expand("tom").encode();
        URI uri = uriComponents.toUri();
        String body = restTemplate.getForEntity(uri, String.class).getBody();

getForObject方法是对getForEntity的进一步封装,通过HttpMessageConverterExtractor对HTTP的请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容。比如:

        UriComponents uriComponents = UriComponentsBuilder.fromUriString("http://HELLO-SERVICE/hello?name={name}").build().expand("tom").encode();
        URI uri = uriComponents.toUri();
        String body = restTemplate.getForObject(uri, String.class);

getForObject的三种重载方法的参数与getForEntity的相同

2)、POST请求

在这里插入图片描述
postForEntity方法同GET请求中的getForEntity类似。如下使用postForEntity提交POST请求到HELLO-SERVICE服务的/hello接口,提交的body内容为user对象,请求响应返回的body类型为String

        User user = new User(1, "tom", 22);
        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/hello", user, String.class);
        String body = responseEntity.getBody();

或者采用HttpEntity的方式:

        HttpEntity<User> request = new HttpEntity<>(new User(1, "tom", 22));
        ResponseEntity<String> responseEntity = restTemplate.postForEntity("http://HELLO-SERVICE/hello", request, String.class);
        String body = responseEntity.getBody();

postForObject函数也跟getForObject类似

        User user = new User(1, "tom", 22);
        String body = restTemplate.postForObject("http://HELLO-SERVICE/hello", user, String.class);

postForLocation函数实现了以POST请求提交资源,并返回新资源的URI

        User user = new User(1, "tom", 22);
        URI uri = restTemplate.postForLocation("http://HELLO-SERVICE/hello", user);

由于postForLocation函数会返回新资源的URI,该URI就相当于指定了返回类型,所以此方法实现的POST请求不需要像postForEntity和postForObject那样指定responseType

3)、PUT请求

PUT请求和DELETE请求基本使用与GET请求一致
在这里插入图片描述

4)、DELETE请求

在这里插入图片描述

3、Ribbon组件和原理

  • IRule:负载均衡策略

  • IPing:探测服务实例是否存活的策略

  • ServerList:负载均衡使用的服务器列表。这个列表会缓存在负载均衡器中,并定期更新。当Ribbon与Eureka 结合使用时,ServerList的实现类就是DiscoveryEnabledNIWSServerList,它会保存Eureka Server中注册的服务实例表

  • ServerListFilter:服务器列表过滤器。主要用于对服务消费者获取到的服务器列表进行预过滤

  • ILoadBalancer:负载均衡器。上层代码通过调用其API进行服务调用的负载均衡选择,一般ILoadBalancer的实现类中会引用一个IRule

  • RestClient:服务调用器,Ribbon向服务提供者发起REST请求的工具

Ribbon的主要工作:

  • 优先选择在同一个Zone且负载较少的Eureka Server
  • 定期从Eureka更新并过滤服务实例列表
  • 根据用户指定的策略,再从Server取到的服务注册列表中选择一个实例的地址
  • 通过RestClient进行服务调用

4、负载均衡策略

在这里插入图片描述

public interface IRule{

    //选择哪个服务处理请求
    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

AbstractLoadBalancerRule是负载均衡策略的抽象类,在该抽象类中定义了负载均衡器ILoadBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并以此设计一些算法来实现针对特定场景的高效策略

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {

    private ILoadBalancer lb;
        
    @Override
    public void setLoadBalancer(ILoadBalancer lb){
        this.lb = lb;
    }
    
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }      
}
  • RandomRule:随机

  • RoundRobinRule:轮询

  • RetryRule:先按照轮询的策略获取服务,如果获取服务失败则在指定的时间内会进行重试,获取可用的服务

  • WeightedResponseTimeRule:根据平均响应时间计算所有服务的权重,响应时间越快服务权重越大被选中的概率越大。刚启动时如果同统计信息不足,则使用轮询的策略,等统计信息足够会切换到自身规则

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

  • AvailabilityFilteringRule:先过滤掉由于多次访问故障处于断路器跳闸状态的服务,还有并发的连接数量超过阈值的服务,然后对于剩余的服务列表按照轮询的策略进行访问

  • ZoneAvoidanceRule:以ZoneAvoidancePredicate(服务所在的Zone是否可用)为主过滤条件,AvailabilityPredicate(服务连接数量是否超过阈值)为次过滤条件的组合过滤条件CompositePredicate进行过滤,然后对于剩余的服务列表按照轮询的策略进行访问

在配置类中指定负载均衡策略

    @Bean
    public IRule rule() {
        //随机策略
        return new RandomRule();
    }

5、参数配置

Ribbon的参数配置有两种方式:全局配置以及指定客户端配置

1)、全局配置

格式:ribbon.<key>=<value>

<key>代表Ribbon客户端配置的参数名,<value>代表了对应参数的值

全局配置Ribbon创建连接的超时时间

ribbon.ConnectTimeout=3000
2)、指定客户端配置

格式:<client>.ribbon.<key>=<value>

<key><value>的含义同全局配置相同,<client>代表了客户端的名称

为客户端指定具体的实例清单

hello-service.ribbon.listOfServers=localhost:8000,localhost:8001

6、与Eureka相结合

当在SpringCloud的应用中同时引入SpringCloud Ribbon和SpringCloud Eureka依赖时,会触发Eureka中实现的对Ribbon的自动化配置。ServerList的维护机制将被com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList的实例所覆盖,该实现会将服务清单列表交给Eureka的服务治理机制来维护;IPing的实现将被com.netflix.niws.loadbalancer.NIWSDiscoveryPing的实例所覆盖,该实现也将实例检查的任务交给了服务治理框架来进行维护

由于SpringCloud Ribbon默认实现了区域亲和策略,可以通过Eureka实例的元数据配置来实现区域化的实例配置方案。比如,可以将处于不同机房的实例配置成不同的区域值,以作为跨区域的容错机制实现,只需在服务实例的元数据中增加zone参数来指定自己所在的区域,比如:

eureka.instance.metadata-map.zone=shanghai

在SpringCloud Ribbon和SpringCloud Eureka结合的工程中,可以通过如下的参数配置来禁用Eureka对Ribbon服务实例的维护实现。这时对于服务实例的维护又回归到使用<client>.ribbon.listOfServers参数配置的方式来实现

ribbon.eureka.enabled=false

7、重试机制

spring.cloud.loadbalancer.retry.enabled=true
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=10000
hello-service.ribbon.ConnectTimeout=250
hello-service.ribbon.ReadTimeout=1000
hello-service.ribbon.OkToRetryOnAllOperations=true
hello-service.ribbon.MaxAutoRetriesNextServer=2
hello.ribbon.MaxAutoRetries=1
  • spring.cloud.loadbalancer.retry.enabled:该参数用来开启重试机制
  • hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds:断路器的超时时间需要大于Ribbon的超时时间,不然不会触发重试
  • hello-service.ribbon.ConnectTimeout:请求连接的超时时间
  • hello-service.ribbon.ReadTimeout:请求处理的超时时间
  • hello-service.ribbon.OkToRetryOnAllOperations:对所有操作请求都进行重试
  • hello-service.ribbon.MaxAutoRetriesNextServer:切换实例的重试次数
  • hello.ribbon.MaxAutoRetries:对当前实例的重试次数

根据如上配置,当访问到故障请求的时候,它会再尝试访问一次当前实例(次数由MaxAutoRetries配置),如果不行,就换一个实例进行访问,如果还是不行,再换一次实例访问(更换次数由MaxAutoRetriesNextServer配置),如果依然不行,返回失败信息

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

邋遢的流浪剑客

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值