Spring + Jetty:优雅关机
Spring + Jetty: graceful shutdown
Objective:有 Spring 带有嵌入式 Jetty 的 Web 应用程序,我想 shutdown/restart 优雅地使用该应用程序。
这是 EmbeddedServletContainerFactory
bean(如果需要我会添加省略的代码):
@Bean
public EmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();
factory.addServerCustomizers(server -> {
server.setStopAtShutdown(false);
/*
* StatisticsHandler has to be added for graceful shutdown to work (see
* https://github.com/eclipse/jetty.project/issues/1549#issuecomment-301102535)
*/
StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(server.getHandler());
server.setHandler(statisticsHandler);
});
return factory;
}
这是我们的关闭信号处理程序:
@Component
public class ShutdownSignalHandler {
@Value("${shutdown.signalType:TERM}")
private String signal;
@Autowired
private ConfigurableApplicationContext context;
@Autowired
private Server jetty;
@PostConstruct
public void init() {
Signal.handle(new Signal(signal), this::signalHandler);
}
private void signalHandler(Signal signal) {
jetty.stop();
context.close();
}
}
问题:有了上面的配置,每次我通过TERM
信号重新启动应用程序时,我在apache_access
日志中看到很多503
条目(几乎全部发生当 jetty.stop
正在执行时)。
知道 how/why 它会发生吗?解决方案是什么?
503 - 服务不可用
Apache 在将请求代理到 servlet 容器时无法获得响应,因为没有 servlet 容器侦听代理配置中指定的 host:port。
Why/how 会发生吗?这是 Jetty 的默认行为:首先,Jetty 关闭网络连接器以停止接受新连接,然后等待处理活动请求。但要注意的是,新请求可能会通过已建立的连接(那些在关闭过程开始之前连接的连接)发送到 Jetty。
解决方案:为通过打开的连接发送的新请求发送重试响应。我们定制了 StatisticsHandler
,这样在停止过程中,它不会使用 503
处理请求,而是使用 307 Connection: close, Location: <original-url>
.
处理它们
更新:我们所做的是更改 StatisticsHandler
class 的 handle
方法的实现(查找 Change this line!
评论):
@Override
public void handle(String path, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
...
try
{
Handler handler = getHandler();
if (handler!=null && _shutdown.get()==null && isStarted())
handler.handle(path, baseRequest, request, response);
else if (baseRequest.isHandled())
{
if (_wrapWarning.compareAndSet(false,true))
LOG.warn("Bad statistics configuration. Latencies will be incorrect in {}",this);
}
else
{
baseRequest.setHandled(true);
response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503); // Change this line!
}
}
finally
{
...
}
}
Objective:有 Spring 带有嵌入式 Jetty 的 Web 应用程序,我想 shutdown/restart 优雅地使用该应用程序。
这是 EmbeddedServletContainerFactory
bean(如果需要我会添加省略的代码):
@Bean
public EmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
JettyEmbeddedServletContainerFactory factory = new JettyEmbeddedServletContainerFactory();
factory.addServerCustomizers(server -> {
server.setStopAtShutdown(false);
/*
* StatisticsHandler has to be added for graceful shutdown to work (see
* https://github.com/eclipse/jetty.project/issues/1549#issuecomment-301102535)
*/
StatisticsHandler statisticsHandler = new StatisticsHandler();
statisticsHandler.setHandler(server.getHandler());
server.setHandler(statisticsHandler);
});
return factory;
}
这是我们的关闭信号处理程序:
@Component
public class ShutdownSignalHandler {
@Value("${shutdown.signalType:TERM}")
private String signal;
@Autowired
private ConfigurableApplicationContext context;
@Autowired
private Server jetty;
@PostConstruct
public void init() {
Signal.handle(new Signal(signal), this::signalHandler);
}
private void signalHandler(Signal signal) {
jetty.stop();
context.close();
}
}
问题:有了上面的配置,每次我通过TERM
信号重新启动应用程序时,我在apache_access
日志中看到很多503
条目(几乎全部发生当 jetty.stop
正在执行时)。
知道 how/why 它会发生吗?解决方案是什么?
503 - 服务不可用
Apache 在将请求代理到 servlet 容器时无法获得响应,因为没有 servlet 容器侦听代理配置中指定的 host:port。
Why/how 会发生吗?这是 Jetty 的默认行为:首先,Jetty 关闭网络连接器以停止接受新连接,然后等待处理活动请求。但要注意的是,新请求可能会通过已建立的连接(那些在关闭过程开始之前连接的连接)发送到 Jetty。
解决方案:为通过打开的连接发送的新请求发送重试响应。我们定制了 StatisticsHandler
,这样在停止过程中,它不会使用 503
处理请求,而是使用 307 Connection: close, Location: <original-url>
.
更新:我们所做的是更改 StatisticsHandler
class 的 handle
方法的实现(查找 Change this line!
评论):
@Override
public void handle(String path, Request baseRequest, HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException
{
...
try
{
Handler handler = getHandler();
if (handler!=null && _shutdown.get()==null && isStarted())
handler.handle(path, baseRequest, request, response);
else if (baseRequest.isHandled())
{
if (_wrapWarning.compareAndSet(false,true))
LOG.warn("Bad statistics configuration. Latencies will be incorrect in {}",this);
}
else
{
baseRequest.setHandled(true);
response.sendError(HttpStatus.SERVICE_UNAVAILABLE_503); // Change this line!
}
}
finally
{
...
}
}