热门标签 | 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更加快速高效的调用远程服务。

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





乐哉码农




推荐阅读
  • SpringMVC RestTemplate的几种请求调用(转)
    SpringMVCRestTemplate的几种请求调用(转),Go语言社区,Golang程序员人脉社 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 本文介绍如何使用 Android 的 Canvas 和 View 组件创建一个简单的绘图板应用程序,支持触摸绘画和保存图片功能。 ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 优化局域网SSH连接延迟问题的解决方案
    本文介绍了解决局域网内SSH连接到服务器时出现长时间等待问题的方法。通过调整配置和优化网络设置,可以显著缩短SSH连接的时间。 ... [详细]
  • 本文详细介绍了如何在 Windows 环境下使用 node-gyp 工具进行 Node.js 本地扩展的编译和配置,涵盖从环境搭建到代码实现的全过程。 ... [详细]
  • 本文探讨了在 ASP.NET MVC 5 中实现松耦合组件的方法。通过分离关注点,应用程序的各个组件可以更加独立且易于维护和测试。文中详细介绍了依赖项注入(DI)及其在实现松耦合中的作用。 ... [详细]
  • 本文深入探讨了HTTP请求和响应对象的使用,详细介绍了如何通过响应对象向客户端发送数据、处理中文乱码问题以及常见的HTTP状态码。此外,还涵盖了文件下载、请求重定向、请求转发等高级功能。 ... [详细]
  • PHP 过滤器详解
    本文深入探讨了 PHP 中的过滤器机制,包括常见的 $_SERVER 变量、filter_has_var() 函数、filter_id() 函数、filter_input() 函数及其数组形式、filter_list() 函数以及 filter_var() 和其数组形式。同时,详细介绍了各种过滤器的用途和用法。 ... [详细]
  • 本文详细介绍了 org.apache.commons.io.IOCase 类中的 checkCompareTo() 方法,通过多个代码示例展示其在不同场景下的使用方法。 ... [详细]
  • 采用IKE方式建立IPsec安全隧道
    一、【组网和实验环境】按如上的接口ip先作配置,再作ipsec的相关配置,配置文本见文章最后本文实验采用的交换机是H3C模拟器,下载地址如 ... [详细]
  • 深入解析Java虚拟机(JVM)架构与原理
    本文旨在为读者提供对Java虚拟机(JVM)的全面理解,涵盖其主要组成部分、工作原理及其在不同平台上的实现。通过详细探讨JVM的结构和内部机制,帮助开发者更好地掌握Java编程的核心技术。 ... [详细]
  • yikesnews第11期:微软Office两个0day和一个提权0day
    点击阅读原文可点击链接根据法国大选被黑客干扰,发送了带漏洞的文档Trumps_Attack_on_Syria_English.docx而此漏洞与ESET&FireEy ... [详细]
  • 本文详细介绍了如何在云服务器上配置Nginx、Tomcat、JDK和MySQL。涵盖从下载、安装到配置的完整步骤,帮助读者快速搭建Java Web开发环境。 ... [详细]
  • 本文探讨了如何利用HTML5和JavaScript在浏览器中进行本地文件的读取和写入操作,并介绍了获取本地文件路径的方法。HTML5提供了一系列API,使得这些操作变得更加简便和安全。 ... [详细]
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社区 版权所有