我正在使用Freemarker开发春季web应用程序,并且在增加服务器上CPU核心数时遇到性能/可伸缩性问题。
在生产中,我们的服务器具有以下配置:
英特尔至强E5-2690
8个物理核心
使用Turbo Boost时的2.9GHz至3.8GHz
启用超线程
我们购买了新服务器,用以下配置替换了旧服务器:
英特尔至强金牌6254
18个物理核心
使用Turbo Boost时可将3.4GHz提升至4GHz
禁用超线程
问题是:在使用18个物理核心的新服务器中,当我们向服务器加载500个jmeter线程时,我们的webapp会比在具有16个核心(8个物理+超线程)的旧服务器中慢。
因此,我们使用Yourkit分析了我们的应用程序,并观察到当freemarker处理模板并写入输出时,大多数tomcat ajp线程被阻塞,试图获取tomcat SynchronizedQueue(或SynchronizedStack)上的锁:
问题是:为什么要放置这些锁?我们如何避免或限制它们对性能的影响?
版本:
Tomcat 9.0.14
春季4.3.22
Freemarker 2.3.28
最佳答案
我发现2种解决方案可减少Webapp上明显阻塞的线程:
我禁用了Freemarker自动刷新调用setAutoFlush(false)
(see more)
我优化了处理freemarker模板的方式。在此之前,我的processTemplate方法是:
public void processTemplate(Template template, Map<String, Object> model, Writer writer) throws TemplateException, IOException {
model.put(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, new RequestContext(request, response, servletContext, model));
template.process(model, writer);
}
每次处理模板时,都会初始化一个新的RequestContext:在调用构造函数时放置一个锁。
现在,在我的FreemarkerProcessor的构造函数中,每个请求仅将RequestContext初始化一次:
public FreeMarkerProcessor(HttpServletRequest request, HttpServletResponse response, ServletContext servletContext, freemarker.template.Configuration freemarkerConfiguration) {
this.freemarkerConfiguration = freemarkerConfiguration;
requestContext = new RequestContext(request, response, servletContext, new HashMap<>());
}
public void processTemplate(Template template, Map<String, Object> model, Writer writer) throws TemplateException, IOException {
requestContext.getModel().clear();
requestContext.getModel().putAll(model);
model.put(AbstractTemplateView.SPRING_MACRO_REQUEST_CONTEXT_ATTRIBUTE, requestContext);
template.process(model, writer);
}
关于java - 当我增加CPU内核数时,为什么使用Freemarker的spring webapp会变慢?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/59321055/