以下のように、春には珍しい情報レベルのログクラウドサービスの再起動が発生します。
[INFO ] - [c.n.u.c.ShutdownEnabledTimer:59] - Exception caught (might be ok if at shutdown) [TraceInfo:-]
java.lang.IllegalStateException: Shutdown in progress
at java.lang.ApplicationShutdownHooks.remove(ApplicationShutdownHooks.java:82)
at java.lang.Runtime.removeShutdownHook(Runtime.java:239)
at com.netflix.util.concurrent.ShutdownEnabledTimer.cancel(ShutdownEnabledTimer.java:57)
at com.netflix.loadbalancer.BaseLoadBalancer.cancelPingTask(BaseLoadBalancer.java:632)
at com.netflix.loadbalancer.BaseLoadBalancer.shutdown(BaseLoadBalancer.java:883)
at com.netflix.loadbalancer.DynamicServerListLoadBalancer.shutdown(DynamicServerListLoadBalancer.java:285)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.beans.factory.support.DisposableBeanAdapter.invokeCustomDestroyMethod(DisposableBeanAdapter.java:337)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:271)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1052)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1059)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1035)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1011)
at org.springframework.context.support.AbstractApplicationContext.close(AbstractApplicationContext.java:961)
at org.springframework.cloud.context.named.NamedContextFactory.destroy(NamedContextFactory.java:92)
at org.springframework.beans.factory.support.DisposableBeanAdapter.destroy(DisposableBeanAdapter.java:256)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroyBean(DefaultSingletonBeanRegistry.java:571)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingleton(DefaultSingletonBeanRegistry.java:543)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingleton(DefaultListableBeanFactory.java:1052)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.destroySingletons(DefaultSingletonBeanRegistry.java:504)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.destroySingletons(DefaultListableBeanFactory.java:1059)
at org.springframework.context.support.AbstractApplicationContext.destroyBeans(AbstractApplicationContext.java:1035)
at org.springframework.context.support.AbstractApplicationContext.doClose(AbstractApplicationContext.java:1011)
at org.springframework.context.support.AbstractApplicationContext$1.run(AbstractApplicationContext.java:931)
ログの追跡によると、最終的な位置決めの問題は、ShutdownEnabledTimer方法をキャンセルするために、最終的な呼び出しにつながる、shutdownメソッドDynamicServerListLoadBalancerを呼び出すことです
public void cancel() {
super.cancel();
LOGGER.info("Shutdown hook removed for: {}", this.name);
try {
Runtime.getRuntime().removeShutdownHook(this.cancelThread);
} catch (IllegalStateException ise) {
LOGGER.info("Exception caught (might be ok if at shutdown)", ise);
}
}
执行完父类cancel方法后,执行removeShutdownHook这一步抛出异常, 最终会调用到 ApplicationShutdownHooks的remove方法
static synchronized boolean remove(Thread hook) {
if(hooks == null)
throw new IllegalStateException("Shutdown in progress");
if (hook == null)
throw new NullPointerException();
return hooks.remove(hook) != null;
}
最初はフックを検証しない場合に例外をスローします。
その理由を推測:サービスは、プロセスを再起動し、実行が完了した後ApplicationShutdownHooksは、runHooksメソッドを実行し、nullにセットフック。
static void runHooks() {
Collection<Thread> threads;
synchronized(ApplicationShutdownHooks.class) {
threads = hooks.keySet();
hooks = null;
}
for (Thread hook : threads) {
hook.start();
}
for (Thread hook : threads) {
try {
hook.join();
} catch (InterruptedException x) { }
}
}
フックコードを追加し、次のとおりです。
public ShutdownEnabledTimer(String name, boolean daemon) {
super(name, daemon);
this.name = name;
this.cancelThread = new Thread(new Runnable() {
public void run() {
ShutdownEnabledTimer.super.cancel();
}
});
LOGGER.info("Shutdown hook installed for: {}", this.name);
Runtime.getRuntime().addShutdownHook(this.cancelThread);
}
cancelThreadのスレッドを呼び出すShutdownEnabledTimer
検証:新しいcom.netflix.util.concurrentをエンジニアリングする場合、ログを追加し、元のShutdownEnabledTimerクラスをコピーし、cancelThread建設
public ShutdownEnabledTimer(String name, boolean daemon) {
super(name, daemon);
this.name = name;
this.cancelThread = new Thread(new Runnable() {
public void run() {
//添加日志
log.info(Thread.currentThread().getName() + " is executed");
ShutdownEnabledTimer.super.cancel();
}
});
LOGGER.info("Shutdown hook installed for: {}", this.name);
Runtime.getRuntime().addShutdownHook(this.cancelThread);
}
展開サービス、リスタート。
観測ログとは、シャットダウン方法のDynamicServerListLoadBalancerを呼び出す前に、shutdownHookが実行されていることが分かりました。
要約すると、ネットフリックスは、サービスが停止されたとき、状況はその実行前に、互換性のタイマーフックスレッドではありませんが、それは、スタックが、捕獲されたログの中、および異常のこの種のために説明しなかったために、専用のシャットダウン方法で分裂しませんショットを記録するが、サービスの終了が最適化ポイントとして使用することができます影響を与えなかったが、実際にサービスの品質には影響を与えません。
また、問題(上のコミュニティの議論はhttps://github.com/spring-cloud/spring-cloud-commons/issues/111)、今、修正への計画を終了していません。