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

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





乐哉码农




推荐阅读
  • http:my.oschina.netleejun2005blog136820刚看到群里又有同学在说HTTP协议下的Get请求参数长度是有大小限制的,最大不能超过XX ... [详细]
  • 本文介绍了Web学习历程记录中关于Tomcat的基本概念和配置。首先解释了Web静态Web资源和动态Web资源的概念,以及C/S架构和B/S架构的区别。然后介绍了常见的Web服务器,包括Weblogic、WebSphere和Tomcat。接着详细讲解了Tomcat的虚拟主机、web应用和虚拟路径映射的概念和配置过程。最后简要介绍了http协议的作用。本文内容详实,适合初学者了解Tomcat的基础知识。 ... [详细]
  • 自动轮播,反转播放的ViewPagerAdapter的使用方法和效果展示
    本文介绍了如何使用自动轮播、反转播放的ViewPagerAdapter,并展示了其效果。该ViewPagerAdapter支持无限循环、触摸暂停、切换缩放等功能。同时提供了使用GIF.gif的示例和github地址。通过LoopFragmentPagerAdapter类的getActualCount、getActualItem和getActualPagerTitle方法可以实现自定义的循环效果和标题展示。 ... [详细]
  • 解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法
    本文介绍了解决nginx启动报错epoll_wait() reported that client prematurely closed connection的方法,包括检查location配置是否正确、pass_proxy是否需要加“/”等。同时,还介绍了修改nginx的error.log日志级别为debug,以便查看详细日志信息。 ... [详细]
  • 本文介绍了一个适用于PHP应用快速接入TRX和TRC20数字资产的开发包,该开发包支持使用自有Tron区块链节点的应用场景,也支持基于Tron官方公共API服务的轻量级部署场景。提供的功能包括生成地址、验证地址、查询余额、交易转账、查询最新区块和查询交易信息等。详细信息可参考tron-php的Github地址:https://github.com/Fenguoz/tron-php。 ... [详细]
  • SpringBoot整合SpringSecurity+JWT实现单点登录
    SpringBoot整合SpringSecurity+JWT实现单点登录,Go语言社区,Golang程序员人脉社 ... [详细]
  • LVS实现负载均衡的原理LVS负载均衡负载均衡集群是LoadBalance集群。是一种将网络上的访问流量分布于各个节点,以降低服务器压力,更好的向客户端 ... [详细]
  • 目录浏览漏洞与目录遍历漏洞的危害及修复方法
    本文讨论了目录浏览漏洞与目录遍历漏洞的危害,包括网站结构暴露、隐秘文件访问等。同时介绍了检测方法,如使用漏洞扫描器和搜索关键词。最后提供了针对常见中间件的修复方式,包括关闭目录浏览功能。对于保护网站安全具有一定的参考价值。 ... [详细]
  • 安装mysqlclient失败解决办法
    本文介绍了在MAC系统中,使用django使用mysql数据库报错的解决办法。通过源码安装mysqlclient或将mysql_config添加到系统环境变量中,可以解决安装mysqlclient失败的问题。同时,还介绍了查看mysql安装路径和使配置文件生效的方法。 ... [详细]
  • 本文介绍了在rhel5.5操作系统下搭建网关+LAMP+postfix+dhcp的步骤和配置方法。通过配置dhcp自动分配ip、实现外网访问公司网站、内网收发邮件、内网上网以及SNAT转换等功能。详细介绍了安装dhcp和配置相关文件的步骤,并提供了相关的命令和配置示例。 ... [详细]
  • 计算机存储系统的层次结构及其优势
    本文介绍了计算机存储系统的层次结构,包括高速缓存、主存储器和辅助存储器三个层次。通过分层存储数据可以提高程序的执行效率。计算机存储系统的层次结构将各种不同存储容量、存取速度和价格的存储器有机组合成整体,形成可寻址存储空间比主存储器空间大得多的存储整体。由于辅助存储器容量大、价格低,使得整体存储系统的平均价格降低。同时,高速缓存的存取速度可以和CPU的工作速度相匹配,进一步提高程序执行效率。 ... [详细]
  • 在重复造轮子的情况下用ProxyServlet反向代理来减少工作量
    像不少公司内部不同团队都会自己研发自己工具产品,当各个产品逐渐成熟,到达了一定的发展瓶颈,同时每个产品都有着自己的入口,用户 ... [详细]
  • 个人学习使用:谨慎参考1Client类importcom.thoughtworks.gauge.Step;importcom.thoughtworks.gauge.T ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • 31.项目部署
    目录1一些概念1.1项目部署1.2WSGI1.3uWSGI1.4Nginx2安装环境与迁移项目2.1项目内容2.2项目配置2.2.1DEBUG2.2.2STAT ... [详细]
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社区 版权所有