ccl125

Results 6 comments of ccl125

感谢作者的解答,我最近又看了些资料,还是有些问题要请教下。 **1、** 只使用一级的缓存是否能解决没有代理对象的循环依赖? 没有代理对象的情况下,在bean实例化后就放入singletonObjects中,提前暴露。这样做可以解决循环依赖带来的闭环调用问题。但是这样能否解决没有代理对象的循环依赖问题,我感觉不可以解决,因为如果这样做的话,相当于没有半成品与成品对象的区分了,singletonObjects这一个缓存中可能既存在一个bean对象的完整态,也存在bean的半成品状态(未设置属性),那么如果恰好在某个bean处于半成品的时机,其他地方调用了getBean()方法想获取到完整的bean对象,获取到的bean对象却是一个未设置属性的bean,这就有问题了吧。这是我的理解。 **2、** 2个缓存,是否能解决循环依赖? 结合网上的资料。 第一种观点是说两级缓存不能解决循环依赖。 文章会首先说两级缓存可以解决普通的循环依赖,但是有AOP的情况,就无法解决,因为最终返回的对象与代理对象不一致(也就是本项目循环依赖两节内容所讲的问题)接着引入三级缓存,提前暴露singletonFactory解决有代理对象的循环依赖。 这个观点没有问题,Spring的源码就是这样,如果不使用三级缓存,只使用二级缓存就像本项目第一节循环依赖那样,显然是没办法解决的。 第二种观点是说两级缓存可以解决循环依赖(也是作者大大回复我的观点)。解决的办法就是提前将生成的代理对象放入缓存中,因为Spring解决循环依赖。三级缓存的设计实际上就是在1、实例化,2、设置属性、3初始化。第三步3初始化后通过BeanPostProcessor生成代理的。那么如果只用二级缓存的话,就是在实例化后不管有没有AOP都生成代理对象放入缓存中,对应代码其实就是在bean实例化后,直接调用getEarlyBeanReference()方法,生成代理对象后放入二级缓存,而不是采用三级缓存的方式再最后完成整个bean创建过程后再通过提前暴露的ObjectFactory去获取代理对象。采用这种方式,就是在步骤1,实例化后就生成代理对象,这样就可以用两级缓存解决循环依赖。接着再说为什么Spring采用三级缓存而不采用二级缓存的方式,实际上是Spring的一个设计理念所决定的,也就是说Spring用三级缓存这种方式去处理更加的“优雅”。 下面是我摘抄的比较有说服性的回答: 并不是说二级缓存如果存在aop的话就无法将代理对象注入的问题,本质应该说是初始spring是没有解决循环引用问题的,设计原则是 bean 实例化、属性设置、初始化之后 再 生成aop对象,但是为了解决循环依赖但又尽量不打破这个设计原则的情况下,使用了存储了函数式接口的第三级缓存; 如果使用二级缓存的话,可以将aop的代理工作提前到 提前暴露实例的阶段执行; 也就是说所有的bean在创建过程中就先生成代理对象再初始化和其他工作; 但是这样的话,就和spring的aop的设计原则相驳,aop的实现需要与bean的正常生命周期的创建分离; 这样只有使用第三级缓存封装一个函数式接口对象到缓存中, 发生循环依赖时,触发代理类的生成 其实总结就是Spring开发者不希望循环依赖的处理影响了原本创建bean的流程。就是初始化完后再通过后置处理去生成代理对象,而不是为了循环依赖把整个bean生命周期流程更改掉。 上面这两个观点一个说二级缓存不能解决,一个说可以解决,其实是不矛盾的,因为是从两个角度去看问题的。 我的疑问点其实在观点二这里。这种方式去解决循环依赖,假设我在实例化后,不管有没有AOP都先通过getEarlyBeanReference()方法提前去放入二级缓存中代理对象。那如果有不需要代理对象的情况怎么办?怎么去判断我要获取的对象到底是代理的对象还是实际的原对象呢。还是说我理解错了,没有这种即要代理又有其他地方需要真实对象的情况?因为我感觉如果就算提前放入二级缓存中代理对象,那如果不需要代理的地方是不是就没办法获取到真实对象了 总结: 其实就是两个问题: 1、不考虑AOP的情况下,一级缓存能否解决循环依赖? 2、二级缓存的设计(实例化后就将代理放入缓存中)到底能否解决循环依赖?如果可以,怎么区分缓存中的真实对象还是代理对象,getBean的时候从缓存中获取,可能有的地方是要代理对象,有的地方是要真实对象,怎么保证获取的就是我想要的呢?

谢谢作者的解答,我已经理解了。 1、第一个问题我在纠结没有AOP的情况下只用一级缓存会造成没办法区分半成品和成品的bean。实际上框架加载是单线程的,也就是框架加载完成后bean就也创建好了,所以不会有半成品和成品混到一块的情况。 2、第二个问题是用二级缓存去解决循环依赖会存在代理对象和原始对象无法区分的情况。实际上这种情况也是没有的,因为说到底,代理就是对对象方法的增强,对应到框架内就是比如AOP的应用,所以不可能出现又要一个bean对象的代理对象又要这个bean的原始对象,这两张情况是不会同时发生的,而使用二级缓存去解决就是getEarlyBeanReference()这个方法,需要代理对象的时候返回代理对象,需要原始对象的时候就返回原始的对象。 再次感谢作者开源的本项目,学习到了很多知识,进步很多。从一开始的阅读源码不知道从哪里入手到现在已经能根据整个bean的生命周期独立的阅读spring的源码,当然学无止境,后续还要不断的学习消化

> 不同任务创建不同线程池,既然是隔离,相互之间就不应该有任何共享、借用关系(框架层面也支持不了),通过线程池自己的弹性策略,没任务时缩容、有任务时扩容,你这类业务场景完全可以满足了 > dynamic-tp的作用是给予线程池更多扩展功能,并且能够监控告警和动态调参等。针对于你提出的场景,可以根据自己定义的任务类型划分,如果没有提交新任务,配置了更大的maximumPoolSize也不会占用太多资源,需要考虑的所有业务都繁忙的时候机器是否顶得住,使用dynamic-tp配置好监控告警即可,流量大的时候动态调参。 我们系统其实目前是可以做到通过配置中心的修改,应用动态监听每个不同任务类型的线程池参数,动态调整的。但是没办法做到将整个服务器机器的资源做到动态的共享。隔离是不同任务线程池相互隔离,共享和借用关系指的是对整个服务器资源的共享。 其实主要想实现的是通过任务的提交情况动态的对每个不同类型任务的线程池去做分配,现状是如果某个任务类型的任务多了,就手动的调大线程池参数,同时要确保整体的所有线程池参数不能超过机器资源的配置

是的,就是看下有没有类似的实现,比如各个下游和自身的资源等等都是一种扩缩容的策略,实现对应的策略,可能是各种HPA指标,当没有达到配置的阈值的时候就动态的扩容,直到达到系统预期的利用率。当流量高的时候超过策略配置的缩容阈值就缩容。

I noticed that when I set withDictionaryEncoding(false), the writer switches from using FallbackValuesWriter to PlainValuesWriter. These two have significantly different memory usage. It seems that using PlainValuesWriter might address my...

> In general, dictionary encoding consumes a lot of memory due to buffering all entries. So yes, `withDictionaryEncoding(false)` is the right approach to reduce the memory footprint in your case....