Ribbon与Eureka整合分析(四)、客户端配置延迟加载和启用饥饿加载
客户端配置延迟加载和启用饥饿加载
该博文为Ribbon与Eureka整合分析系列文章中的第四篇,主要介绍客户端所需配置,默认情况下,如何在创建客户端时,才加载配置,以及如何在启动时,加载客户端配置(即饥饿加载)。
文章目录
- 客户端配置延迟加载和启用饥饿加载
- 一、客户端配置加载
- 二、客户端饥饿加载
- 总结
一、客户端配置加载
最近在调试和研究Ribbon源码的时候,发现,客户端所需配置,默认情况下,并不是随着容器启动而加载,而是在使用时(请求经过LoadBalancerInterceptor拦截器处理),才进行加载。之所以能达到这个效果,是借助于Spring的AnnotationConfigApplicationContext类。该类的注释信息如下:
Standalone application context, accepting annotated classes as input - in particular {@link Configuration @Configuration}-annotated classes, but also plain {@link org.springframework.stereotype.Component @Component} types and JSR-330 compliant classes using {@code javax.inject} annotations. Allows for registering classes one by one using {@link #register(Class…)} as well as for classpath scanning using {@link #scan(String…)}.
In case of multiple {@code @Configuration} classes, @{@link Bean} methods defined in later classes will override those defined in earlier classes. This can be leveraged to deliberately override certain bean definitions via an extra {@code @Configuration} class.
即通过AnnotationConfigApplicationContext,达到Bean注册效果。另外,通过其构造方法上注释,需要先调用register方法,然后调用refresh方法。
/*** Create a new AnnotationConfigApplicationContext that needs to be populated* through {@link #register} calls and then manually {@linkplain #refresh refreshed}.*/public AnnotationConfigApplicationContext() {this.reader = new AnnotatedBeanDefinitionReader(this);this.scanner = new ClassPathBeanDefinitionScanner(this);}
如下代码,是Ribbon根据客户端serviceId,创建AnnotationConfigApplicationContext的方法。其中register,都是从configurations属性获取需要注册的内容,且分为两部分,key为客户端serviceId的注册以及以default开头的注册。
protected AnnotationConfigApplicationContext createContext(String name) {AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();if (this.configurations.containsKey(name)) {for (Class<?> configuration : this.configurations.get(name).getConfiguration()) {context.register(configuration);}}for (Map.Entry<String, C> entry : this.configurations.entrySet()) {if (entry.getKey().startsWith("default.")) {for (Class<?> configuration : entry.getValue().getConfiguration()) {context.register(configuration);}}}context.register(PropertyPlaceholderAutoConfiguration.class,this.defaultConfigType);context.getEnvironment().getPropertySources().addFirst(new MapPropertySource(this.propertySourceName,Collections.<String, Object> singletonMap(this.propertyName, name)));if (this.parent != null) {// Uses Environment from parent as well as beanscontext.setParent(this.parent);}context.setDisplayName(generateDisplayName(name));context.refresh();return context;}
那么,configurations这个属性值,是如何生成的呢?这里涉及到ImportBeanDefinitionRegistrar接口,该接口注释信息如下
Interface to be implemented by types that register additional bean definitions when processing @{@link Configuration} classes. Useful when operating at the bean definition level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
Along with {@code @Configuration} and {@link ImportSelector}, classes of this type may be provided to the @{@link Import} annotation (or may also be returned from an {@code ImportSelector}).
An {@link ImportBeanDefinitionRegistrar} may implement any of the following {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective methods will be called prior to {@link #registerBeanDefinitions}:
RIbbon针对于该接口,提供RibbonClientConfigurationRegistrar实现类,用于处理RibbonClients以及RibbonClient注解。然后将收集到的数据信息,注册为RibbonClientSpecification bean对象。
@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata,BeanDefinitionRegistry registry) {Map<String, Object> attrs = metadata.getAnnotationAttributes(RibbonClients.class.getName(), true);if (attrs != null && attrs.containsKey("value")) {AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");for (AnnotationAttributes client : clients) {registerClientConfiguration(registry, getClientName(client),client.get("configuration"));}}if (attrs != null && attrs.containsKey("defaultConfiguration")) {String name;if (metadata.hasEnclosingClass()) {name = "default." + metadata.getEnclosingClassName();} else {name = "default." + metadata.getClassName();}registerClientConfiguration(registry, name,attrs.get("defaultConfiguration"));}Map<String, Object> client = metadata.getAnnotationAttributes(RibbonClient.class.getName(), true);String name = getClientName(client);if (name != null) {registerClientConfiguration(registry, name, client.get("configuration"));}}private void registerClientConfiguration(BeanDefinitionRegistry registry,Object name, Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(RibbonClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + ".RibbonClientSpecification",builder.getBeanDefinition());}
在解析这里,基于RibbonClients注解的defaultConfiguration属性,增加default前缀进行标识,其余的通过value(或name属性,对应于客户端serviceId),作为RibbonClientSpecification的name属性。
经过上述分析,默认情况下,创建客户端配置时,会通过RibbonAutoConfiguration和RibbonEurekaAutoConfiguration两个配置类,完成客户端注册操作。
在上述getInstance处,在AnnotationConfigApplicationContext创建完成之后,能根据指定类型,从客户端上下文环境中,获取指定的配置bean。
二、客户端饥饿加载
默认情况下,客户端所需配置,是在第一次请求到来之后,才进行创建。这种情况下,会增加第一次请求处理时长,从而增加请求处理超时的风险。为此,Ribbon增加RibbonEagerLoadProperties配置类,用于指定是否需要在容器启动时加载指定客户端所需配置信息。默认是关闭的。
@ConfigurationProperties(prefix = "ribbon.eager-load")
public class RibbonEagerLoadProperties {private boolean enabled = false;private List<String> clients;public boolean isEnabled() {return enabled;}public void setEnabled(boolean enabled) {this.enabled = enabled;}public List<String> getClients() {return clients;}public void setClients(List<String> clients) {this.clients = clients;}
}
该配置类,在RibbonAutoConfiguration配置类中使用。当ribbon.eager-load.enabled设置为true,将开启如下配置。而RibbonApplicationContextInitializery为Spring事件监听处理类,用于监听ApplicationReadyEvent事件。
@Bean@ConditionalOnProperty(value = "ribbon.eager-load.enabled")public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {return new RibbonApplicationContextInitializer(springClientFactory(),ribbonEagerLoadProperties.getClients());}
在接收到ApplicationReadyEvent事件后,调用init方法,先创建客户端所需上下文环境。
protected void initialize() {if (clientNames != null) {for (String clientName : clientNames) {this.springClientFactory.getContext(clientName);}}}
到此,Ribbon的懒加载配置项如下:
ribbon.eager-load.enabled=true
ribbon.eager-load.clients=service_id1,service_id2
总结
客户端配置加载
通过RibbonClientConfigurationRegistrar收集RibbonClients和RibbonClient注解信息,并将其配置为RibbonClientSpecification Bean。然后基于AnnotationConfigApplicationContext,分别针对于客户端,构建其上下文环境信息,然后在获取IRule等配置。
客户端饥饿加载
默认情况下,客户端所需配置,是在接收到请求之后,才会进行创建。这种模式,会增加第一个请求处理时长,从而增加第一个请求处理超时的风险,Ribbon增加饥饿加载配置,通过ribbon.eager-load.enabled=true,开启饥饿加载,同时通过ribbon.eager-load.clients配置项指定,那些客户端需要饥饿加载。
Ribbon与Eureka整合分析(四)、客户端配置延迟加载和启用饥饿加载相关推荐
- Spring Cloud Alibaba - 09 Ribbon 饥饿加载及其他配置参数解读
文章目录 解决Ribbon 第一次调用耗时高的配置 超时时间相关参数 并发参数 重试 源码 解决Ribbon 第一次调用耗时高的配置 开启饥饿加载 # ribbon 饥饿加载 解决第一次耗时多的问题 ...
- Spring Cloud实战小贴士:Ribbon的饥饿加载(eager-load)模式
我们在使用Spring Cloud的Ribbon或Feign来实现服务调用的时候,如果我们的机器或网络环境等原因不是很好的话,有时候会发现这样一个问题:我们服务消费方调用服务提供方接口的时候,第一次请 ...
- Ribbon负载均衡策略、懒加载及饥饿加载
目录 一.负载均衡概述 二.负载均衡策略 三.懒加载及饥饿加载 一.负载均衡概述 在业务初期,我们一般会先使用单台服务器对外提供服务.随着业务流量越来越大,单台服务器无论如何优化,无论采用多好的硬件, ...
- Ribbon负载均衡 饥饿加载
需要两份或多份相同的性质的服务的模块,地址与端口不同,服务模块名称相同,访问者通过名称进行访问 让访问者进行负载均衡的选择 在Eureka中发送这个路径我使用的是名字,而不是ip,这里面的负载均衡就是 ...
- nginx 配置静态文件目录_nginx缓存静态资源,只需几个配置提升10倍页面加载速度...
nginx缓存静态资源,只需几个配置提升10倍页面加载速度 首先我们看图说话 这是在没有缓存的情况下,这个页面发送了很多静态资源的请求: 可以看到,静态资源占用了整个页面加载用时的90%以上,而且这个 ...
- Ribbon开启饥饿加载
Ribbon开启饥饿加载 Ribbon默认开启的是懒加载,这时第一次访问的时候相比之后会非常慢,以我的结果为例,懒汉模式下第一次请求耗时500ms,第二次却只有20ms,这样就可以提高体验感. app ...
- 【Android NDK 开发】Android.mk 配置动态库 ( Android Studio 配置动态库 | 动态库加载版本限制 | 本章仅做参考推荐使用 CMake 配置动态库 )
文章目录 I . Android Studio 中使用 Android.mk 配置动态库 总结 II . 第三方动态库来源 III . 配置 Android.mk 构建脚本路径 IV . 预编译 第三 ...
- 项目遇到的问题总结(四):单页面首屏加载慢解决方案
项目遇到的问题总结(四):单页面首屏加载慢解决方案 参考文章: (1)项目遇到的问题总结(四):单页面首屏加载慢解决方案 (2)https://www.cnblogs.com/myfirstboke/ ...
- GIS开发之二维地下管线综合管理系统(Arcgis)第四节 使用arcgis api for js 加载天地图
GIS开发之二维地下管线综合管理系统(Arcgis)第四节 使用arcgis api for js 加载天地图 核心js文件 调用方式 调用结果 核心js文件 #通过定义加载天地图js文件,引用并组织 ...
最新文章
- 马斯克Neuralink联合创始人宣布离职,此前尚未推出上市产品
- python简单编程例子-中文方便就用中文编程!Python图形界面开发实例
- [转帖]IP地址、子网掩码、网络号、主机号、网络地址、主机地址以及ip段/数字-如192.168.0.1/24是什么意思?...
- Java程序员必须知道的Java10特性
- c .net ajax,Asp.net mvc 2中使用Ajax的三种方式
- 口算练习题(洛谷P1957题题解,Java语言描述)
- iptv管理系统php制作,双子星IPTV管理系统搭建教程
- Java中sleep,wait的区别
- hadoop组件---数据仓库---hive简介
- 第 2 届河北省大学生程序设计竞赛(河北省赛)-Problem C. icebound 的账单-题解
- Python和R之间转换的基本指南:使用Python或R知识来有效学习另一种语言的简单方法。
- 七代处理器装win7_Intel7代处理器 win10重装win7后无法安装显卡声卡驱动的解决方案 | A小可私人狗窝...
- CSS/HTML制作电影网站中的电影卡片
- Kal设置启动时输出日志,不显示logo
- 解决Android v4、v7包导入标红问题import android.support.v4.app.ActivityCompat;import android.support.v7.app
- 快上车,老司机带你实现后台录像功能
- J9数字论:什么是DAO模式?DAO发展过程的阻碍
- RSS的基本使用 - rsslibj
- 序列3:序列方法实操上
- html5 photo gallery,jquery图片查看器 jquery-photo-gallery
热门文章
- VsCode+OpenOCD 开发stm32系列
- 不等距双杆模型_电磁感应之双杆模型ppt课件
- 麦肯锡三部曲_《学会提问:麦肯锡工作法》—读书笔记导图分享
- 当case when then else end 语句遇上sum或count等统计函数
- rgb sw 线主板接口在哪_自带RGB风扇,支持神光同步的九州风神小堡垒120I水冷散热器...
- [nlp] sentiment analysis(情感分析)
- vue-i18n的使用,前端实现中英文切换
- PDF生成插件--TcPDF
- ERROR: node with name rabbit already running的解决方法
- MySQL删除数据后,释放磁盘空间