热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

微服务|springcloud系列之ribbon使用及原理讲解

本章节将要学习springcloud的组件ribbon的使用,ribbon是一个实现了客户端负载均衡的工具,透明的实现了负载均衡策略,我们只需要在resttemplate加上loa

目录


什么是负载均衡?​

为什么使用负载均衡? 

什么是ribbon?

ribbon与resttemplate整合

ribbon负载均衡源码跟踪

ribbon的常见配置

ribbon面试必问

总结



什么是负载均衡?​

负载均衡是将用户的请求按照某种算法,动态的去选择某一个主机去处理,对于用户来说是透明的,在没有负载均衡之前我们可能是这么去请求一个数据的

有了负载均衡之后,用户请求过程可能是这样的

负载均衡分为客户端负载均衡和集中式负载均衡

       客户端负载均衡:消费者在调用请求接口之前首先获取哪些资源是可用的,然后按照均衡算法选择一个资源进行请求,这个是客户端自己进行选择的,所以称为客户端负载均衡,常见的客户端负载均衡有ribbon

     集中式负载均衡:在消费者和服务方中间使用独立的代理方式进行负载,有硬件的(F5),软件的(nginx)

为什么使用负载均衡? 

这个问题其实很好回答,一个新的东西出来必定是为了解决某一个问题,不然他出现的就毫无意义,负载均衡也是一样,负载均衡是为了达到最优化资源使用、最大化吞吐率、最小化响应时间、同时避免过载的目的,打个比方说,就相当于我们在火车站买票,如果只开了一个窗口,那么这个窗口排队的人肯定会很长,买票的效率会很低,如果我同时开通十个窗口,那么买票速度铁定会提升的,而且单个窗口的压力也会减少,响应素度也会更快的,这也就是为什么采用负载均衡的原因。

什么是ribbon?

   ribbon其实是netflix开源的一款用于客户端负载均衡的一个框架。

 在springcloud提供了ribbon用来做客户端负载均衡,通过springcloud对ribbon的封装,我们可以很轻松的通过负载均衡去调用我们开发的rest服务,不需要手动去处理因负载均衡而出现的各种棘手情况,ribbon并不需要向eureka和网关那样单独部署,他是和每一个微服务耦合在一起的,这一节我们介绍的是resttemplate与ribbon整合去实现负载均衡的调用,这里负载均衡的启用方式是需要我们手动在resttemplate实例配置上面添加loadbalence注解的,关于resttemplate我上一节已经介绍过了,在这个后一节我会介绍feign工具的使用,使用它的时候,它自动帮我们开启了负载均衡。

ribbon与resttemplate整合

关于resttemplate的整合,上一节中,我已经介绍过了,这一节主要介绍resttemplate的api使用

GET请求

(1) public T getForObject(String url, Class responseType, Object... uriVariables)

url:请求的接口地址,例“http://192.168.31.168:8081/getOrder?name={1}”

responseType:响应数据类型,例“String.class”,返回string类型数据,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:填充地址栏里面的占位符

(2) public T getForObject(String url, Class responseType, Map uriVariables)

url:url:请求的接口地址,例“http://192.168.31.168:8081/getOrder”

responseType:响应数据类型,例“String.class”,返回string类型数据,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:参数的键值对

(3) public ResponseEntity getForEntity(String url, Class responseType, Object... uriVariables)

url:请求的接口地址,例“http://192.168.31.168:8081/getOrder?name={1}”

responseType:响应数据类型,例“String.class”,返回string类型数据,与上面不同的是,这个返回结果封装了http的响应头等信息,,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:填充地址栏里面的占位符

(4) public ResponseEntity getForEntity(String url, Class responseType, Map uriVariables)

url:url:请求的接口地址,例“http://192.168.31.168:8081/getOrder”

responseType:响应数据类型,例“String.class”,返回string类型数据,与上面不同的是,这个返回结果封装了http的响应代码等数据,如果希望返回某一个实体对象也可以这样,User.class,

uriVariables:参数的键值对

POST请求

(1) public T postForObject(String url, Class responseType, Object... uriVariables)

url:请求的接口地址,例“http://192.168.31.168:8081/getOrder?name={1}”

responseType:响应数据类型,例“String.class”,返回string类型数据,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:填充地址栏里面的占位符

(2) public T postForObject(String url, Class responseType, Map uriVariables)

url:url:请求的接口地址,例“http://192.168.31.168:8081/getOrder”

responseType:响应数据类型,例“String.class”,返回string类型数据,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:参数的键值对

(3) public ResponseEntity postForEntity(String url, Class responseType, Object... uriVariables)

url:请求的接口地址,例“http://192.168.31.168:8081/getOrder?name={1}”

responseType:响应数据类型,例“String.class”,返回string类型数据,与上面不同的是,这个返回结果封装了http的响应头等信息,,如果希望返回某一个实体对象也可以这样,User.class

uriVariables:填充地址栏里面的占位符

(4) public ResponseEntity postForEntity(String url, Class responseType, Map uriVariables)

url:url:请求的接口地址,例“http://192.168.31.168:8081/getOrder”

responseType:响应数据类型,例“String.class”,返回string类型数据,与上面不同的是,这个返回结果封装了http的响应代码等数据,如果希望返回某一个实体对象也可以这样,User.class,

uriVariables:参数的键值对

ribbon负载均衡源码跟踪

设想:ribbon是如何实现负载均衡的呢?根据我们前面说ribbon实现的的客户端负载均衡,所以他自己肯定有一个可用的服务列表,服务列表里面存储的是可用服务的地址,这是第一个条件;第二个是我们在resttemplate上面添加了注解后,他就自动实现了负载均衡,这个我们肯定会想到他拦截了我们的请求,所以这里肯定会有一个拦截器在帮助我们实现此功能,第三个是为什么我们加上了注解后,也可以使用服务名直接去调用了呢,肯定也有组件帮我们实现了替换的,让我们带着者三个疑问一起去追踪源码吧。

再看源码之前,我们都会从一个入口出发,然后进行调试。研究springmvc源码时,我们都知道肯定都是从dispatcherservlet出发,然后往下走,这里也是一样,通过我们上面的分析,发现实现了负载均衡时和这个注解有关的,所以我们先看看这个注解是如何定义的

/**
* Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

进入了LoadBalanced 注解后,我们看到他是这么定义的,并且注意到了上面的一行注释,通过注解标记resttemplate使用LoadBalancerClient去代理使用,这个我们先不关注,我们都知道,在springboot中,有一个申明式的注解,必定在其同名的包下面会有一个这样的(xxxAutoConfiguration)配置类,去配置这个注解,我们定位到该包下面,看到

 同名的包下面确实有这样一个配置类LoadBalancerAutoConfiguration

我们看下这个类的注解信息

@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)

这里会涉及到spring的以注解使用,我挑几个常用的简单说下

@Configuration:注解可以用java代码的形式实现spring中xml配置文件配置的效果。

@ConditionalOnClass:其用途是判断当前classpath下是否存在指定类,若是则将当前的配置装载入spring容器

@ConditionalOnBean:当给定的在bean存在时,则实例化当前Bean

@EnableConfigurationProperties:如果该类只使用了@ConfigurationProperties注解,然后该类没有在扫描路径下或者没有使用@Component等注解,导致无法被扫描为bean,那么就必须在配置类上使用@EnableConfigurationProperties注解去指定这个类,这个时候就会让该类上的@ConfigurationProperties生效,然后作为bean添加进spring容器中

@ConditionalOnMissingBean:容器中不存在指定bean

介绍完几个基本注解之后,我们继续分析,看到

@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();

这里面会注入只有被loadbalence注解的所有的resttemplate实例,我们再点击resttemplate进去,看到此类继承了Intecepting,看来真的有拦截器在起作用,我们可以看到InterceptingHttpAccessor类中有一个setInterceptors方法,这里标记下

然后我们看看他是在哪里调用这份方法的,我们再回到LoadBalancerAutoConfiguration配置类中

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}

看到这里再往resttemplate中添加拦截器LoadBalancerInterceptor,我们点击这个拦截器进去,主要看这段代码,我们在这打个断点,

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}

这里就是做拦截作用了,实现真正的逻辑实在execute方法中,我们点进去看看

public T execute(String serviceId, LoadBalancerRequest request) throws IOException {
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
Server server = this.getServer(loadBalancer);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
} else {
RibbonLoadBalancerClient.RibbonServer ribbOnServer= new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
return this.execute(serviceId, ribbonServer, request);
}
}

第一行通过serviceID找到对应的服务实例均衡器,serviceId其实就是每个微服务的应用名称("")

loadBalancer 这里面保存了所有的服务实例,可用的,宕机的都在里面

然后通过调用getServer方法,使用均衡算法拿到可用的服务实例(从几个中选择一个,达到均衡的作用),返回server,然后拿到这个server就可以继续执行目标请求,我们在这里也打一个断点,到目前位置,我们的第一步已经完成,我们现在要做的就是模拟一个请求,跟踪请求来调试源码,

源码调试

我们还是使用上一节的项目来调试。如果需要获取项目,请关注我的公众号“乐哉码农”,回复“eureka”获取资料

启动项目,在启动过程中,我们来验证下,ribbon是否拿到所有被loadbalence注解的resttemplate

我们可以看到resttemplates里面有两个resttemplate,我们在restteplate的定义中是否有两个被loadbalence注解

确实有两个,最上面一个没有被loadbalenced注解,这里写两个是为了让我们理解的更加清晰点,没有实际作用,

我们放掉断点,看到他到了这里

在这里,他为我们的template添加了拦截器,我们跳过,让项目启动完成

访问接口http://localhost:8082/getOrderForLoadBalence,进入断点

往下走,看到进入了拦截器(LoadBalancerInterceptor)里面的断点

在这里面获取到了我们的servicename,然后通过servicename去均衡器里面获取服务地址,继续往下走,

我们根据serviceid获取到了一个均衡器,我们看看这里面存的什么

可以看到upserverlist里面保存了两个地址,这个地址我们应该很熟悉,就是我们启动的两个服务实例,所以说明

getLoadBalancer方法根据serviceid拿到了可用的服务实例,将我们的服务名转换成了具体的实例地址,因为我们最终肯定只调用一个服务地址,所以我们继续往下面看,

 往下走一步发现得到了一个server,这个server绑定的地址是8085端口,所以可以看出这里使用了均衡算法,返回一个服务实例给我们,后面肯定是拿着这个地址去替换我们现有的请求地址去发送请求,后的的不是我们这边关注的,所以整个源码分析也就到这里结束了。

ribbon的常见配置

1.禁用eureka

当我们在resttemplate上面添加loadbalence注解后,就可以使用服务名去调用,如果我们向关闭这个功能,可以使用ribbon.eureka.enable=false

2.配置接口地址列表

如果我们关闭了eureka之后,我们还想用服务名去调用,就需要手动配置服务配置列表

服务名.ribbon.listOfServers=IP:PORT1,IP:PORT2

3.配置负载均衡策略

服务名.ribbon.NFLoadBalencerRuleClassName=策略class全类名

4.超时时间

ribbon中有两种和超时时间相关的配置

ribbon.COnnectTimeout=2000   请求连接的超时时间

ribbon.ReadTimeout=5000 请求处理的超时时间

可以在前面加上具体的服务名,为指定的服务配置

5.并发参数

ribbon.MaxTotalCOnnections=500  最大连接数

ribbon,MaxCOnnectionsPerHost=500  每个host最大连接数

5.重试机制1

在集群环境中,用多个节点来提供相同的服务,当一个节点出现故障时,nginx会继续请求另外一个节点,而在eureka中,牺牲了数据一致性,保证了AP原则,有可能在某一个节点出现故障时,他短时间内还不会将这个节点去移除,他会在一段时间内等待这个节点重启,所以在这个时间段内,eurekaui一直将这个注册表保存在可用的服务列表中,导致我们去请求时,请求失败,这是我们客户端发现请求失败后就必须进行重试操作,在ribbon可以通过服务名.ribbon.NFLoadBalencerRuleClassName=xxx来指定重试机制

6.重试机制2

除了使用ribbon自带的重试机制,还可以集成spring-retry,

ribbon.maxAutoRetries=1  当前实例重试次数

ribbon.maxAutoRetriesNextServer=3 切换节点的重试次数

ribbon.okToRetryOnAllOperations=true,对所有请求进行重试

ribbon.retryableStatusCodes=500,402  对指定错误代码进行重试

ribbon面试必问

1.ribbon是什么?

答: Ribbon是Netflix发布的开源项目,主要功能是提供客户端的软件负载均衡算法,将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等。

2.ribbon是什么类型的负载均衡?

答:ribbon实现的是客户端负载均衡

3.常见的实现了负载均衡有哪些?

nginx(集中式),ribbon(客户端),F5(集中式)

4.负载均衡的算法都有哪些?

IPhash,轮询,权重

总结

通过本章的学习,对ribbon和负载均衡有了一个整体的学习,通过源码跟踪,了解了其负载均衡实现的原理,最后通过讲解一些ribbon的配置,对ribbon的常见自定义配置有了大概的理解,后面一章将会介绍feign的使用,使用feign更加快速高效的调用远程服务。

欢迎大家关注我的公众号,回复“资料领取”,获取人工智能、大数据等更多学习视频资料,如有侵权,请联系作者立即删除!





乐哉码农




推荐阅读
  • 深入解析Spring Cloud Ribbon负载均衡机制
    本文详细介绍了Spring Cloud中的Ribbon组件如何实现服务调用的负载均衡。通过分析其工作原理、源码结构及配置方式,帮助读者理解Ribbon在分布式系统中的重要作用。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • Explore a common issue encountered when implementing an OAuth 1.0a API, specifically the inability to encode null objects and how to resolve it. ... [详细]
  • PHP 5.2.5 安装与配置指南
    本文详细介绍了 PHP 5.2.5 的安装和配置步骤,帮助开发者解决常见的环境配置问题,特别是上传图片时遇到的错误。通过本教程,您可以顺利搭建并优化 PHP 运行环境。 ... [详细]
  • 使用 Azure Service Principal 和 Microsoft Graph API 获取 AAD 用户列表
    本文介绍了一段通用代码示例,该代码不仅能够操作 Azure Active Directory (AAD),还可以通过 Azure Service Principal 的授权访问和管理 Azure 订阅资源。Azure 的架构可以分为两个层级:AAD 和 Subscription。 ... [详细]
  • 将Web服务部署到Tomcat
    本文介绍了如何在JDeveloper 12c中创建一个Java项目,并将其打包为Web服务,然后部署到Tomcat服务器。内容涵盖从项目创建、编写Web服务代码、配置相关XML文件到最终的本地部署和验证。 ... [详细]
  • 本文介绍如何在Spring Boot项目中集成Redis,并通过具体案例展示其配置和使用方法。包括添加依赖、配置连接信息、自定义序列化方式以及实现仓储接口。 ... [详细]
  • Nginx 反向代理与负载均衡实验
    本实验旨在通过配置 Nginx 实现反向代理和负载均衡,确保从北京本地代理服务器访问上海的 Web 服务器时,能够依次显示红、黄、绿三种颜色页面以验证负载均衡效果。 ... [详细]
  • 本文详细介绍了在 Windows 7 系统中配置 Nginx 1.10.3 和 PHP 7.1.1 NTS 的步骤,包括修改 PHP 配置文件、处理依赖项以及创建批处理脚本启动和停止服务。重点解释了如何解决常见的运行时错误。 ... [详细]
  • 深入理解Docker网络管理
    本文介绍了Docker网络管理的基本概念,包括为什么需要Docker网络管理以及Docker提供的多种网络驱动模式。同时,文章还详细解释了Docker网络相关的命令操作,帮助读者更好地理解和使用Docker网络功能。 ... [详细]
  • 本文介绍了Java并发库中的阻塞队列(BlockingQueue)及其典型应用场景。通过具体实例,展示了如何利用LinkedBlockingQueue实现线程间高效、安全的数据传递,并结合线程池和原子类优化性能。 ... [详细]
  • 深入解析Nginx中的Location指令及其属性
    本文将详细探讨Nginx配置文件中关键的location指令,包括其三种匹配方式(精准匹配、普通匹配和正则匹配),以及如何在实际应用中灵活运用这些匹配规则。此外,还将介绍location下的重要子元素如root、alias和proxy_pass,并解释相关参数的使用方法。 ... [详细]
  • 阿里云ecs怎么配置php环境,阿里云ecs配置选择 ... [详细]
  • 全面解析运维监控:白盒与黑盒监控及四大黄金指标
    本文深入探讨了白盒和黑盒监控的概念,以及它们在系统监控中的应用。通过详细分析基础监控和业务监控的不同采集方法,结合四个黄金指标的解读,帮助读者更好地理解和实施有效的监控策略。 ... [详细]
  • 使用Nginx反向代理实现多域名端口映射
    本文介绍如何通过配置本地hosts文件和Nginx反向代理,实现多个虚拟域名的端口映射,使用户可以通过标准HTTP端口80访问不同后端服务。 ... [详细]
author-avatar
黄小翻_618
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有