为什么当我增加 CPU 核心数量时,我使用 Freemarker 的 spring webapp 会变慢?
Why does my spring webapp using Freemarker get slower when I increase CPU core number?
我正在使用 Freemarker 开发 spring 网络应用程序,当我增加服务器上的 CPU 内核数量时,我遇到了性能/可扩展性问题。
在生产环境中,我们的服务器配置如下:
- 英特尔至强 E5-2690
- 8 个物理内核
- 2.9GHz 至 3.8GHz,带 Turbo Boost
- 启用超线程
我们购买了新服务器以替换具有以下配置的旧服务器:
- 英特尔至强金牌 6254
- 18 个物理核心
- 3.4GHz 最高 4GHz,带 Turbo Boost
- 禁用超线程
问题是:在具有 18 个物理内核的新服务器中,当我们使用 500 个 jmeter 线程加载服务器时,我们的 webapp 比具有 16 个内核(8 个物理 + 超线程)的旧服务器慢。
因此,我们使用 Yourkit 来分析我们的应用程序,并观察到当 freemarker 正在处理模板时,大多数 tomcat ajp 线程在尝试获取 tomcat SynchronizedQueue(或 SynchronizedStack)上的锁时被阻止,并且在输出中写入:
问题是:为什么要放置这些锁,我们如何避免或限制它们对性能的影响?
版本:
- Tomcat 9.0.14
- Spring 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:在调用构造函数时放置一个锁。
现在,RequestContext 在我的 FreemarkerProcessor 的构造函数上每个请求只初始化一次:
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);
}
我正在使用 Freemarker 开发 spring 网络应用程序,当我增加服务器上的 CPU 内核数量时,我遇到了性能/可扩展性问题。
在生产环境中,我们的服务器配置如下:
- 英特尔至强 E5-2690
- 8 个物理内核
- 2.9GHz 至 3.8GHz,带 Turbo Boost
- 启用超线程
我们购买了新服务器以替换具有以下配置的旧服务器:
- 英特尔至强金牌 6254
- 18 个物理核心
- 3.4GHz 最高 4GHz,带 Turbo Boost
- 禁用超线程
问题是:在具有 18 个物理内核的新服务器中,当我们使用 500 个 jmeter 线程加载服务器时,我们的 webapp 比具有 16 个内核(8 个物理 + 超线程)的旧服务器慢。
因此,我们使用 Yourkit 来分析我们的应用程序,并观察到当 freemarker 正在处理模板时,大多数 tomcat ajp 线程在尝试获取 tomcat SynchronizedQueue(或 SynchronizedStack)上的锁时被阻止,并且在输出中写入:
问题是:为什么要放置这些锁,我们如何避免或限制它们对性能的影响?
版本:
- Tomcat 9.0.14
- Spring 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:在调用构造函数时放置一个锁。
现在,RequestContext 在我的 FreemarkerProcessor 的构造函数上每个请求只初始化一次:
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);
}