发现了Spring的bug,领导带我出去一起爬山玩水了
- 完全同步的AbstractRefreshableApplicationContext.getBeanFactory,可能导致大量线程阻塞:
在运行具有大量CPU 核的Tomcat的大型服务器上,由于同步块,在getBeanFactory()中看到大量线程阻塞。这是因为我们使用XmlWebApplicationContext,并且在Web请求期间每次查找bean时都需要bean工厂。
在Web应用程序之外,我们还使用GenericXmlApplicationContext拥有另一个上下文,而这个上下文没有此问题。
哪个代码路径通常会碰到那里的障碍? WebApplicationContext引用上某种形式的getBean查找,每次都在内部委派给BeanFactory吗?我们的内部查找通常会保留内部BeanFactory并直接对其进行操作,这也可能是定制检索代码的一种出路。
就是说,可以使用一些更细粒度的锁定来对这个(相当古老的,2008 年spring编写的锁)beanFactoryMonitor锁进行重做,以进行读取访问,甚至有可能用作访问的volatile字段以及(重新)初始化和关闭的专用锁。
案例
这是第一个有趣的纯Spring版本(TenantIgnoreXmlWebApplicationContext是我们的类,但没重写containsBean()或任何方式)。似乎每个通过org.springframework.web.multipart.support.MultipartFilter的请求始终在进行bean查找,并且总是在访问getBeanFactory()。
...
TenantIgnoreXmlWebApplicationContext(AbstractRefreshableApplicationContext).getBeanFactory() line: 175
TenantIgnoreXmlWebApplicationContext(AbstractApplicationContext).containsBean(String) line: 1146
MultipartFilter.lookupMultipartResolver() line: 157
MultipartFilter.lookupMultipartResolver(HttpServletRequest) line: 143
MultipartFilter.doFilterInternal(HttpServletRequest, HttpServletResponse, FilterChain) line: 108
MultipartFilter(OncePerRequestFilter).doFilter(ServletRequest, ServletResponse, FilterChain) line: 119
ApplicationFilterChain.internalDoFilter(ServletRequest, ServletResponse) line: 193
ApplicationFilterChain.doFilter(ServletRequest, ServletResponse) line: 166
FilterChainProxy$VirtualFilterChain.doFilter(ServletRequest, ServletResponse) line: 320
FilterSecurityInterceptor.invoke(FilterInvocation) line: 127
FilterSecurityInterceptor.doFilter(ServletRequest, ServletResponse, FilterChain) line: 91
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
确实,spring的某些过滤器实现以及某些Web集成的委托(比如JSF)在WebApplicationContext级别调用getBean方法,因此需要加锁。
尽管我们可以修改这些位置以保留嵌套的BeanFactory,但将AbstractRefreshableApplicationContext切换到volatile beanFactory字段似乎更具吸引力。由于AbstractApplicationContext已经应用了startupShutdownMonitor,因此我们应该能够完全摆脱beanFactoryMonitor,而始终访问volatile字段。
我不确定在哪里有嵌套的BeanFactory,因为在我们的堆栈,总是只有这两个方法位于我们自己对getBean(String)的调用之上:
- org.springframework.context.support.AbstractApplicationContext.getBean(String,Class)调用
- org.springframework.context.support.AbstractRefreshableApplicationContext.getBeanFactory()
当我们以下面的方式查找当前的Web应用程序上下文时,可能是在调用之前缺少了某些东西。 这是我们遗留代码库所需要的一种静态实用程序方法,以防万一。
private static ApplicationContext getWebApplicationContextIfExists()
{
ServletContext servletContext = null; final RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
if (requestAttributes instanceof ServletRequestAttributes)
{ servletContext = ((ServletRequestAttributes) requestAttributes).getRequest().getServletContext();
}
...
if (servletContext != null)
{ final ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext); if (context == null) { if (log.isDebugEnabled()) { log.debug("No web spring configuration for webapp " + servletContext.getServletContextName() + " found, using only core configuration."); } } else {
… return context; }
}
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
最终修改代码的提交
文章来源: javaedge.blog.csdn.net,作者:JavaEdge.,版权归原作者所有,如需转载,请联系作者。
原文链接:javaedge.blog.csdn.net/article/details/108191962
- 点赞
- 收藏
- 关注作者
评论(0)