记录 RPC 服务有损正常运行时间的分析过程

小夏 科技 更新 2024-01-31

应用程序开始提供 JSF 服务后,短时间内会出现大量空指针异常。

分析日志后发现,服务所依赖的藏经阁的配置数据没有加载。 所谓的有损上线或者是的直接出版当应用程序启动时,服务在加载之前就开始向外界提供服务,导致调用失败

密钥 ** 如下。

数据的初始加载是通过实现 commandlinerunner 接口完成的。

@componentpublic class loadsystemargslistener implements commandlinerunner }
cjgconfigcacheloader.refresh() 方法在内部将数据加载到内存中。

** Scripture Pavilion 配置数据 key: tenant value: 配置数据 * public static map cjgruleconfigmap = new hashmap<>(
如果数据尚未加载,请调用 cjgruleconfigmapget("301").getxx(),则会报空指针异常。

总结根本原因:jsf 提供程序在服务依赖关系之前发布初始数据加载,从而导致调用失败

在解决这个问题之前,我们需要回顾并熟悉一下Spring Boot的启动过程和JSF服务的发布过程。

运行方法,主要关注点refreshcontext(context) 中。

public configurableapplicationcontext run(string...args) ,context);准备上下文:主要将environment、applicationarguments等关键属性设置到applicationcontext中,并加载applicationlistener、applicationrunner、commandlinerunner等。  preparecontext(context, environment, listeners, applicationarguments, printedbanner);Refresh Context:这是启动 Spring IOC 容器的关键,包括 bean 创建、依赖注入、初始化、发布事件等。 afterrefresh(context, applicationarguments); stopwatch.stop();打印启动信息:如果弹簧main.log-startup-info 为 true,则打印 (this.) 的启动信息LogStartUpInfo):通知所有 SpringApplicationRunListeners 应用程序 Listeners 已启动started(context);调用 Runner:调用所有 ApplicationRunner 和 CommandLineRunner callrunners(context, applicationarguments); catch (throwable ex) try catch (throwable ex) return context;}
refreshcontext(context) 中。refresh() 方法,这是该方法的主要关注点FinishBeanFactoryInitialization(BeanFactory) 在 finishRefresh() 之前实例化 BEAN。

public void refresh() throws beansexception, illegalstateexception .
要实例化 Bean,您需要熟悉 Bean 生命周期(重要)。

com 类jd.jsf.gd.config.spring.providerbean 调用方法 comjd.jsf.gd.config.providerconfig 导出。

JSF源码地址:

public class providerbean extends providerconfig implements initializingbean, disposablebean, applicationcontextaware, applicationlistener, beannameaware after spring context refreshed.", this.beanname); if (this.delay < 1) catch (throwable var2) providerbean.this.export();thread.setdaemon(true); thread.setname("delayexportthread"); thread.start();else }private boolean isdelay() public void afterpropertiesset() throws exception after properties set.", this.beanname); this.export();
public synchronized void export() throws initerrorexception catch (throwable var2) providerconfig.this.doexport();thread.setdaemon(true); thread.setname("delayexportthread"); thread.start();else }
可以看出,提供者发布的地方有两个。

Bean 初始化过程 (delay>=0)。

实现 initializingbean 接口并重写 afterpropertiesset 方法。 如果它大于或等于 0,它将在此处发布。 具体来说,在导出方法中,如果延迟>0,则发布会延迟,例如,如果配置了 5000,则发布会延迟 5 秒如果 delay=0,则立即发布。

侦听要触发的 contextRefreshedEvent 事件 (delay<0)。

实现 ApplicationListener API 并重写 OnApplicationEvent 方法。 延迟时属于事件 contextRefreshedEvent

从上面的介绍中,我了解到执行顺序1.Bean 初始化> 2ContextRefreshedEvent 事件触发器 > 3调用 applicationrunner 或 commandlinerunner;

上面已经知道了提供程序发布正在进行中,请避免使用方法 3 初始化数据。

先决条件:delay 的默认值为 -1,可以保留为未配置或负数。 jsf 提供程序版本处于进程 2 中,该进程由侦听 contextRefreshedEvent 事件触发

@componentpublic class dataloader ") public void loaddata()
注意:如果 Bean 依赖于其他 Bean,请确保实例化依赖 Bean,否则会报告空指针异常。

contextRefreshedEvent 事件的发布方式。

调用过程 AbstractApplicationContext FinishRefresh ->AbstractApplicationContext PublishEvent-> ApplicationEventMulticaster MulticastEvent

public void multicastevent(final applicationevent event, @nullable resolvabletype eventtype) else }
ApplicationEventMulticaster 的 MulticastEvent 方法调用 InvokeListener() 来发布事件,GetTaskExecutor() 默认为 null(Executor 对象的自定义设置除外),并且该类的所有 ApplicationListener 实现都串行执行 OnApplicationEvent 方法。

getApplicationListeners(event, type) 获取所有实现类,并继续查看内部调用 annotationawareordercomparatorsort(alllisteners) 对所有 applicationlisteners 进行排序,alllisteners 是要排序的对象列表。 此方法根据对象上的排序批注或接口确定排序顺序,并返回按指定顺序排序的对象列表。 具体来说,排序规则如下:

1.首先,根据对象上@order注释的值进行排序。 @order 注解值越小,排序优先级越高

2.如果对象上没有@order注释,或者多个对象具有相同的@order注释值,则顺序基于对象是否实现有序接口。 实现有序接口的对象可以通过 getorder() 方法返回排序值。

3.如果对象既没有@order注释,也没有有序接口,则使用默认排序值“最低优先级(整数)”max_value)。特殊:如果 beana 和 beanb 排序值都是默认值,则保持原始顺序,即 bean 的加载顺序

摘要:默认该类的所有 ApplicationListener 实现都按顺序执行 OnApplicationEvent 方法,顺序取决于 AnnotationAwareOrderComparatorsort(alllisteners),@order注解的值越小,排序优先级越高

@component@order(1)public class dataloader implements applicationlistener }
也可以在具有 @springbootapplication 的启动类中实现它(默认情况下,在 Spring Boot 中,使用基于注解的配置和管理 bean,因此在 XML 定义的 bean 之前加载注解定义的 bean)。

@springbootapplicationpublic class demoapplication implements applicationlistener @override public void onapplicationevent(contextrefreshedevent event) }
应用程序启动后,初始化应用程序,然后手动发布提供程序。 这可以通过实现接口 applicationrunner 或接口 commandlinerunner 来执行初始化来完成。

@componentpublic class dataloader implements applicationrunner }
提供程序的 dynamic 属性设置为 false

RPC 服务(如 JSF 和 DUBBO)通常以两种方式启动: 1. 延迟发布 2.手动启动

如果您的服务需要一些初始化操作才能提供外部服务,例如初始化缓存(不限于采集经文、DUCC、MySQL,甚至调用其他 JSF 服务)、Redis 连接池等相关资源到位,可以参考本文介绍的方法。

本文是作者通过阅读源码+本地验证得出的结论,如果有任何错误或遗漏或更好的解决方案,请指出共同的进展!

相似文章

    如何在鲨鱼簿记中一次删除所有记录

    如何在鲨鱼簿记中一次删除所有记录 本文将介绍如何一次性删除 Shark Ledger 中的所有记录的话题,目标受众是所有使用 Shark Ledger 并希望删除所有记录的用户。Shark Bookkeeping App 是一款簿记软件,可帮助用户记录和管理他们的个人财务。在 Shark Ledge...

    记录一个水库野钓鲫鱼,整个水库捕获的鱼最多,策略与思考

    今天来记录一个野生垂钓水库鲫鱼的全过程,虽然标题上说整个水库钓到的鲫鱼最多,但实际上只有十几条鲫鱼,但一定是真的 一共有多个垂钓者 可以看到 只有一个人钓到了两条鲫鱼,另外最好的是两条虾虎鱼。因为最近上班,需要带两个孩子,所以钓鱼的时间不多,安排在周日通过 一大早,到了钓鱼点,天亮就能看到浮标。本来...

    泰国著名的奇特服务是每40分钟一次,游客的身体被掏空

    泰国著名的奇特服务是每分钟一次,游客的身体被掏空 在泰国很有名 美妙 分钟的 按摩 让乘客们大喊 全身都抽干了 泰式推拿的起源和特点。泰式推拿是古代泰国医学的一个分支,起源于古印度,由印度皇家医生创立。经过多年的发展,泰式按摩已成为泰国人引以为豪的传统保健方式。早在泰国,泰式按摩就被认为是一种很好的...

    爱侬家政北辰分店做好每一项服务,守护万千家庭幸福

    为了表彰在 年家庭看护技能大赛 中展现新时代管家风采,产生积极影响的个人和团队,艾侬打造了 闪亮的管家力量 系列,用镜头记录艾侬人的闪耀与成长。今天,我们走近了一等奖获得者张亚杰和他的北辰分公司,听听他们的家政故事。张亚杰,河南省新乡市人,在爱侬北辰分院从事养老居家养老工作年。荣获爱农 年家庭看护技...

    美国圈套下的乌克兰,一次次做出无奈的选择

    美国以忘利而臭名昭著,甚至在与盟友打交道时也经常 背后捅刀子 受其害的国家包括英国 法国 德国 澳大利亚和印度。然而,令人尴尬的是,一些国家一再落入美国设下的圈套,比如乌克兰。它在美国的怂恿下一再挑衅俄罗斯,但几乎造成了毁灭性的灾难。关键时刻,美国溜走了,站在一旁冷眼旁观。幸运的是,普京表现出了相对...