{"id":919,"date":"2023-03-11T17:12:11","date_gmt":"2023-03-11T09:12:11","guid":{"rendered":"https:\/\/www.appblog.cn\/?p=919"},"modified":"2023-04-29T15:48:43","modified_gmt":"2023-04-29T07:48:43","slug":"elegant-shutdown-of-spring-boot-2","status":"publish","type":"post","link":"https:\/\/www.appblog.cn\/index.php\/2023\/03\/11\/elegant-shutdown-of-spring-boot-2\/","title":{"rendered":"Spring Boot 2.0 \u4e4b\u4f18\u96c5\u505c\u673a"},"content":{"rendered":"<p>Spring Boot\u201c\u7ea6\u5b9a\u5927\u4e8e\u914d\u7f6e\u201d\u7684\u7279\u6027\uff0c\u4f53\u73b0\u4e86\u4f18\u96c5\u6d41\u7545\u7684\u5f00\u53d1\u8fc7\u7a0b\uff0c\u5176\u90e8\u7f72\u542f\u52a8\u65b9\u5f0f<code>java -jar xxx.jar<\/code>\u4e5f\u5f88\u4f18\u96c5\u3002\u4f46\u662f\u901a\u5e38\u4f7f\u7528\u7684\u505c\u6b62\u5e94\u7528\u65b9\u5f0f\u662f<code>kill -9 \u8fdb\u7a0b\u53f7<\/code>\uff0c\u5373\u4f7f\u5199\u4e86\u811a\u672c\uff0c\u8fd8\u662f\u663e\u5f97\u6709\u4e9b\u7c97\u9c81\u3002\u8fd9\u6837\u7684\u5e94\u7528\u505c\u6b62\u65b9\u5f0f\uff0c\u5728\u505c\u6b62\u7684\u90a3\u4e00\u5239\u90a3\uff0c\u5e94\u7528\u4e2d\u6b63\u5728\u5904\u7406\u7684\u4e1a\u52a1\u903b\u8f91\u4f1a\u88ab\u4e2d\u65ad\uff0c\u5bfc\u81f4\u4ea7\u751f\u4e1a\u52a1\u5f02\u5e38\u60c5\u5f62\u3002\u8fd9\u79cd\u60c5\u51b5\u5982\u4f55\u907f\u514d\uff0c\u672c\u6587\u4ecb\u7ecd\u7684\u4f18\u96c5\u505c\u673a\uff0c\u5c06\u5b8c\u7f8e\u89e3\u51b3\u8be5\u95ee\u9898\u3002<\/p>\n<h2>\u524d\u8a00<\/h2>\n<p><!-- more --><\/p>\n<p>\u4ec0\u4e48\u53eb\u4f18\u96c5\u505c\u673a\uff1f\u7b80\u5355\u8bf4\u5c31\u662f\uff0c\u5728\u5bf9\u5e94\u7528\u8fdb\u7a0b\u53d1\u9001\u505c\u6b62\u6307\u4ee4\u4e4b\u540e\uff0c\u80fd\u4fdd\u8bc1\u6b63\u5728\u6267\u884c\u7684\u4e1a\u52a1\u64cd\u4f5c\u4e0d\u53d7\u5f71\u54cd\u3002\u5e94\u7528\u63a5\u6536\u5230\u505c\u6b62\u6307\u4ee4\u4e4b\u540e\u7684\u6b65\u9aa4\u5e94\u8be5\u662f\uff0c\u505c\u6b62\u63a5\u6536\u8bbf\u95ee\u8bf7\u6c42\uff0c\u7b49\u5f85\u5df2\u7ecf\u63a5\u6536\u5230\u7684\u8bf7\u6c42\u5904\u7406\u5b8c\u6210\uff0c\u5e76\u80fd\u6210\u529f\u8fd4\u56de\uff0c\u8fd9\u65f6\u624d\u771f\u6b63\u505c\u6b62\u5e94\u7528\u3002<\/p>\n<p>\u8fd9\u79cd\u5b8c\u7f8e\u7684\u5e94\u7528\u505c\u6b62\u65b9\u5f0f\u5982\u4f55\u5b9e\u73b0\u5462\uff1f\u5c31Java\u8bed\u8a00\u751f\u6001\u6765\u8bf4\uff0c\u5e95\u5c42\u7684\u6280\u672f\u662f\u652f\u6301\u7684\uff0c\u6240\u4ee5\u6211\u4eec\u624d\u80fd\u5b9e\u73b0\u5728Java\u8bed\u8a00\u4e4b\u4e0a\u7684\u5404\u4e2aweb\u5bb9\u5668\u7684\u4f18\u96c5\u505c\u673a\u3002<\/p>\n<p>\u5728\u666e\u901a\u5916\u7f6e\u7684Tomcat\u4e2d\uff0c\u6709shutdown\u811a\u672c\u63d0\u4f9b\u4f18\u96c5\u7684\u505c\u673a\u673a\u5236\uff0c\u4f46\u662f\u6211\u4eec\u5728\u4f7f\u7528Spring Boot\u7684\u8fc7\u7a0b\u4e2d\u53d1\u73b0web\u5bb9\u5668\u90fd\u662f\u5185\u7f6e\uff08\u5f53\u7136\u4e5f\u53ef\u4f7f\u7528\u5916\u7f6e\uff0c\u4f46\u662f\u4e0d\u63a8\u8350\uff09\uff0c\u8fd9\u79cd\u65b9\u5f0f\u63d0\u4f9b\u7b80\u5355\u7684\u5e94\u7528\u542f\u52a8\u65b9\u5f0f\uff0c\u65b9\u4fbf\u7684\u7ba1\u7406\u673a\u5236\uff0c\u975e\u5e38\u9002\u7528\u4e8e\u5fae\u670d\u52a1\u5e94\u7528\u4e2d\uff0c\u4f46\u662f\u9ed8\u8ba4\u6ca1\u6709\u63d0\u4f9b\u4f18\u96c5\u505c\u673a\u7684\u65b9\u5f0f\u3002\u8fd9\u4e5f\u662f\u672c\u6587\u63a2\u7d22\u8fd9\u4e2a\u95ee\u9898\u7684\u6839\u672c\u539f\u56e0\u3002<\/p>\n<p>\u5e94\u7528\u662f\u5426\u662f\u5b9e\u73b0\u4e86\u4f18\u96c5\u505c\u673a\uff0c\u5982\u4f55\u624d\u80fd\u9a8c\u8bc1\u5462\uff1f\u8fd9\u9700\u8981\u4e00\u4e2a\u5904\u7406\u65f6\u95f4\u8f83\u957f\u7684\u4e1a\u52a1\u903b\u8f91\uff0c\u6a21\u62df\u8fd9\u6837\u7684\u903b\u8f91\u5e94\u8be5\u5f88\u7b80\u5355\uff0c\u4f7f\u7528\u7ebf\u7a0bsleep\u6216\u8005\u957f\u65f6\u95f4\u5faa\u73af\u3002\u672c\u6587\u6a21\u62df\u4e1a\u52a1\u903b\u8f91\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">@Slf4j\n@RestController\npublic class HomeController {\n\n    @GetMapping(value = &quot;\/test&quot;, produces = &quot;application\/json&quot;)\n    public Result&lt;Long&gt; sleepOne(String systemNo) {\n        log.info(&quot;\u6a21\u62df\u4e1a\u52a1\u5904\u74061\u5206\u949f\uff0c\u8bf7\u6c42\u53c2\u6570\uff1a{}&quot;, systemNo);\n        Long serverTime = System.currentTimeMillis();\n\/\/        try {\n\/\/            Thread.sleep(60*1000L);\n\/\/        } catch (InterruptedException e) {\n\/\/            e.printStackTrace();\n\/\/        }\n        while (System.currentTimeMillis() &lt; serverTime + (60 * 1000)) {\n            try {\n                Thread.sleep(1000);\n            } catch (InterruptedException e) {\n                e.printStackTrace();\n            }\n            log.info(&quot;\u6b63\u5728\u5904\u7406\u4e1a\u52a1\uff0c\u5f53\u524d\u65f6\u95f4\uff1a{}\uff0c\u5f00\u59cb\u65f6\u95f4\uff1a{}&quot;, System.currentTimeMillis(), serverTime);\n        }\n        Result&lt;Long&gt; result = new Result&lt;&gt;(serverTime);\n        log.info(&quot;\u6a21\u62df\u4e1a\u52a1\u5904\u74061\u5206\u949f\uff0c\u54cd\u5e94\u53c2\u6570\uff1a{}&quot;, result);\n        return result;\n    }\n\n}<\/code><\/pre>\n<pre><code class=\"language-xml\">&lt;dependencies&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\n    &lt;\/dependency&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-cloud-starter-netflix-eureka-client&lt;\/artifactId&gt;\n        &lt;version&gt;2.1.4.RELEASE&lt;\/version&gt;\n    &lt;\/dependency&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.springframework.cloud&lt;\/groupId&gt;\n        &lt;artifactId&gt;spring-cloud-dependencies&lt;\/artifactId&gt;\n        &lt;version&gt;Greenwich.SR4&lt;\/version&gt;\n        &lt;type&gt;pom&lt;\/type&gt;\n        &lt;scope&gt;runtime&lt;\/scope&gt;\n    &lt;\/dependency&gt;\n    &lt;dependency&gt;\n        &lt;groupId&gt;org.projectlombok&lt;\/groupId&gt;\n        &lt;artifactId&gt;lombok&lt;\/artifactId&gt;\n        &lt;optional&gt;true&lt;\/optional&gt;\n    &lt;\/dependency&gt;\n&lt;\/dependencies&gt;<\/code><\/pre>\n<p>\u9a8c\u8bc1\u65b9\u5f0f\u5c31\u662f\uff0c\u5728\u89e6\u53d1\u8fd9\u4e2a\u63a5\u53e3\u7684\u4e1a\u52a1\u5904\u7406\u4e4b\u540e\uff0c\u4e1a\u52a1\u903b\u8f91\u5904\u7406\u65f6\u95f4\u957f\u8fbe1\u5206\u949f\uff0c\u9700\u8981\u5728\u5904\u7406\u7ed3\u675f\u524d\uff0c\u53d1\u8d77\u505c\u6b62\u6307\u4ee4\uff0c\u9a8c\u8bc1\u662f\u5426\u80fd\u591f\u6b63\u5e38\u8fd4\u56de\u3002\u9a8c\u8bc1\u65f6\u6240\u4f7f\u7528\u7684kill\u6307\u4ee4\uff1a<code>kill -2(Ctrl + C)<\/code>\u3001<code>kill -15<\/code>\u3001<code>kill -9<\/code>\u3002<\/p>\n<h2>Java \u8bed\u8a00\u7684\u4f18\u96c5\u505c\u673a<\/h2>\n<p>\u4ece\u4e0a\u9762\u7684\u4ecb\u7ecd\u4e2d\u6211\u4eec\u53d1\u73b0\uff0cJava\u8bed\u8a00\u672c\u8eab\u662f\u652f\u6301\u4f18\u96c5\u505c\u673a\u7684\uff0c\u8fd9\u91cc\u5c31\u5148\u4ecb\u7ecd\u4e00\u4e0b\u666e\u901a\u7684Java\u5e94\u7528\u662f\u5982\u4f55\u5b9e\u73b0\u4f18\u96c5\u505c\u6b62\u7684\u3002<\/p>\n<p>\u5f53\u6211\u4eec\u4f7f\u7528<code>kill PID<\/code>\u7684\u65b9\u5f0f\u7ed3\u675f\u4e00\u4e2aJava\u5e94\u7528\u7684\u65f6\u5019\uff0cJVM\u4f1a\u6536\u5230\u4e00\u4e2a\u505c\u6b62\u4fe1\u53f7\uff0c\u7136\u540e\u6267\u884c<code>shutdownHook<\/code>\u7684\u7ebf\u7a0b\u3002\u4e00\u4e2a\u5b9e\u73b0\u793a\u4f8b\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-java\">public class ShutdownHook extends Thread {\n    private Thread mainThread;\n    private boolean shutDownSignalReceived;\n\n    @Override\n    public void run() {\n        System.out.println(&quot;Shut down signal received.&quot;);\n        this.shutDownSignalReceived = true;\n        mainThread.interrupt();\n        try {\n            mainThread.join(); \/\/\u5f53\u6536\u5230\u505c\u6b62\u4fe1\u53f7\u65f6\uff0c\u7b49\u5f85mainThread\u7684\u6267\u884c\u5b8c\u6210\n        } catch (InterruptedException e) {\n        }\n        System.out.println(&quot;Shut down complete.&quot;);\n    }\n\n    public ShutdownHook(Thread mainThread) {\n        super();\n        this.mainThread = mainThread;\n        this.shutDownSignalReceived = false;\n        Runtime.getRuntime().addShutdownHook(this);\n    }\n\n    public boolean shouldShutDown(){\n        return shutDownSignalReceived;\n    }\n}<\/code><\/pre>\n<p>\u5176\u4e2d\u5173\u952e\u8bed\u53e5<code>Runtime.getRuntime().addShutdownHook(this);<\/code>\uff0c\u6ce8\u518c\u4e00\u4e2aJVM\u5173\u95ed\u7684\u94a9\u5b50\uff0c\u8fd9\u4e2a\u94a9\u5b50\u53ef\u4ee5\u5728\u4ee5\u4e0b\u51e0\u79cd\u573a\u666f\u88ab\u8c03\u7528\uff1a<\/p>\n<ul>\n<li>\u7a0b\u5e8f\u6b63\u5e38\u9000\u51fa<\/li>\n<li>\u4f7f\u7528<code>System.exit()<\/code><\/li>\n<li>\u7ec8\u7aef\u4f7f\u7528<code>Ctrl+C<\/code>\u89e6\u53d1\u7684\u4e2d\u65ad<\/li>\n<li>\u7cfb\u7edf\u5173\u95ed<\/li>\n<li>\u4f7f\u7528<code>Kill pid<\/code>\u547d\u4ee4\u5e72\u6389\u8fdb\u7a0b<\/li>\n<\/ul>\n<p>\u6d4b\u8bd5<code>shutdownHook<\/code>\u7684\u529f\u80fd\uff0c\u4ee3\u7801\u793a\u4f8b\uff1a<\/p>\n<pre><code class=\"language-java\">public class TestMain {\n    private ShutdownHook shutdownHook;\n\n    public static void main( String[] args ) {\n        TestMain app = new TestMain();\n        System.out.println( &quot;Begin of main()&quot; );\n        app.execute();\n        System.out.println( &quot;End of main()&quot; );\n    }\n\n    public TestMain() {\n        this.shutdownHook = new ShutdownHook(Thread.currentThread());\n    }\n\n    public void execute() {\n        while (!shutdownHook.shouldShutDown()) {\n            System.out.println(&quot;I am sleep&quot;);\n            try {\n                Thread.sleep(1*1000);\n            } catch (InterruptedException e) {\n                System.out.println(&quot;execute() interrupted&quot;);\n            }\n            System.out.println(&quot;I am not sleep&quot;);\n        }\n        System.out.println(&quot;end of execute()&quot;);\n    }\n}<\/code><\/pre>\n<p>\u542f\u52a8\u6d4b\u8bd5\u4ee3\u7801\uff0c\u4e4b\u540e\u518d\u53d1\u9001\u4e00\u4e2a\u4e2d\u65ad\u4fe1\u53f7\uff0c\u63a7\u5236\u53f0\u8f93\u51fa\uff1a<\/p>\n<pre><code>I am sleep\nI am not sleep\nI am sleep\nI am not sleep\nI am sleep\nI am not sleep\nI am sleep\nShut down signal received.\nexecute() interrupted\nI am not sleep\nend of execute()\nEnd of main()\nShut down complete.\n\nProcess finished with exit code 130 (interrupted by signal 2: SIGINT)<\/code><\/pre>\n<p>\u53ef\u4ee5\u770b\u51fa\uff0c\u5728\u63a5\u6536\u5230\u4e2d\u65ad\u4fe1\u53f7\u4e4b\u540e\uff0c\u6574\u4e2amain\u51fd\u6570\u662f\u6267\u884c\u5b8c\u6210\u7684\u3002<\/p>\n<h2>actuator\/shutdown of Spring Boot<\/h2>\n<p>\u6211\u4eec\u77e5\u9053\u4e86Java\u672c\u8eab\u5728\u652f\u6301\u4f18\u96c5\u505c\u673a\u4e0a\u7684\u80fd\u529b\uff0c\u7136\u540e\u5728Spring Boot\u4e2d\u53c8\u53d1\u73b0\u4e86<code>actuator\/shutdown<\/code>\u7684\u7ba1\u7406\u7aef\u70b9\u3002\u4e8e\u662f\u6211\u628a\u4f18\u96c5\u505c\u673a\u7684\u529f\u80fd\u5bc4\u5e0c\u671b\u4e8e\u6b64\uff0c\u5f00\u59cb\u914d\u7f6e\u6d4b\u8bd5\uff0c\u5f00\u542f\u914d\u7f6e\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"language-yml\">management:\n  server:\n    port: 8888\n    servlet:\n      context-path: \/\n    ssl:\n      enabled: false\n  endpoints:\n    web:\n      exposure:\n        include: &quot;*&quot;\n  endpoint:\n    health:\n      show-details: always\n    shutdown:\n      enabled: true #\u542f\u7528shutdown\u7aef\u70b9<\/code><\/pre>\n<p>\u6d4b\u8bd5\u7ed3\u679c\u5f88\u5931\u671b\uff0c\u5e76\u6ca1\u6709\u5b9e\u73b0\u4f18\u96c5\u505c\u673a\u7684\u529f\u80fd\uff0c\u5c31\u662f\u5c06\u666e\u901a\u7684kill\u547d\u4ee4\uff0c\u505a\u6210\u4e86HTTP\u7aef\u70b9\u3002\u4e8e\u662f\u5f00\u59cb\u67e5\u770bSpring Boot\u7684\u5b98\u65b9\u6587\u6863\u548c\u6e90\u4ee3\u7801\uff0c\u8bd5\u56fe\u627e\u5230\u5b83\u7684\u539f\u56e0\u3002<\/p>\n<p>\u5728\u5b98\u65b9\u6587\u6863\u4e0a\u5bf9shutdown\u7aef\u70b9\u7684\u4ecb\u7ecd\uff1a<\/p>\n<pre><code>shutdown    Lets the application be gracefully shutdown.<\/code><\/pre>\n<p>\u4ece\u6b64\u4ecb\u7ecd\u53ef\u4ee5\u770b\u51fa\uff0c\u8bbe\u8ba1\u4e0a\u5e94\u8be5\u662f\u652f\u6301\u4f18\u96c5\u505c\u673a\u7684\u3002\u4f46\u662f\u4e3a\u4ec0\u4e48\u73b0\u5728\u8fd8\u4e0d\u591f\u4f18\u96c5\uff0c\u5728Github\u4e0a\u6258\u7ba1\u7684Spring Boot\u9879\u76ee\u4e2d\u53d1\u73b0\uff0c\u6709\u4e00\u4e2a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/github.com\/spring-projects\/spring-boot\/issues\/4657\" title=\"issue\">issue<\/a>\u4e00\u76f4\u5904\u4e8e\u6253\u5f00\u72b6\u6001\uff0c\u5df2\u7ecf\u4e24\u5e74\u591a\u4e86\uff0c\u91cc\u9762\u5f88\u591a\u8ba8\u8bba\uff0c\u770b\u5b8c\u4e4b\u540e\u53d1\u73b0\u5728Spring Boot\u4e2d\u5b8c\u7f8e\u7684\u652f\u6301\u4f18\u96c5\u505c\u673a\u4e0d\u662f\u4e00\u4ef6\u5bb9\u6613\u7684\u4e8b\uff0c\u9996\u5148Spring Boot\u652f\u6301web\u5bb9\u5668\u5f88\u591a\uff0c\u5176\u6b21\u5bf9\u4ec0\u4e48\u6837\u7684\u5b9e\u73b0\u624d\u662f\u771f\u6b63\u7684\u4f18\u96c5\u505c\u673a\uff0c\u8ba8\u8bba\u4e86\u5f88\u591a\u3002\u60f3\u4e86\u89e3\u66f4\u591a\u7684\u540c\u5b66\uff0c\u628a\u8fd9\u4e2aissue\u597d\u597d\u9605\u8bfb\u4e00\u4e0b\u3002<\/p>\n<p>\u8fd9\u4e2aissue\u4e2d\u8fd8\u6709\u4e00\u4e2a\u91cd\u8981\u4fe1\u606f\uff0c\u5c31\u662f\u8fd9\u4e2aissue\u66fe\u7ecf\u88ab\u52a0\u5165\u52302.0.0\u7684milestone\u4e2d\uff0c\u540e\u6765\u7531\u4e8e\u6ca1\u6709\u5b8c\u6210\u53c8\u79fb\u9664\u4e86\uff0c\u73b0\u5728\u72b6\u6001\u662f\u88ab\u6dfb\u52a0\u57282.1.0\u7684milestone\u4e2d\u3002\u6211\u6d4b\u8bd5\u7684\u7248\u672c\u662f2.2.1\uff0c\u671f\u5f85\u5b98\u65b9\u7ed9\u51fa\u5b8c\u7f8e\u7684\u4f18\u96c5\u505c\u673a\u65b9\u6848\u3002<\/p>\n<h2>Spring boot \u4f18\u96c5\u505c\u673a<\/h2>\n<p>\u867d\u7136\u5b98\u65b9\u6682\u65f6\u8fd8\u6ca1\u6709\u63d0\u4f9b\u4f18\u96c5\u505c\u673a\u7684\u652f\u6301\uff0c\u4f46\u662f\u6211\u4eec\u4e3a\u4e86\u51cf\u5c11\u8fdb\u7a0b\u505c\u6b62\u5bf9\u4e1a\u52a1\u7684\u5f71\u54cd\uff0c\u8fd8\u662f\u8981\u7ed9\u51fa\u80fd\u6ee1\u8db3\u57fa\u672c\u9700\u6c42\u7684\u65b9\u6848\u3002<\/p>\n<h3>\u9488\u5bf9tomcat\u7684\u89e3\u51b3\u65b9\u6848<\/h3>\n<pre><code class=\"language-java\">import lombok.extern.slf4j.Slf4j;\nimport org.apache.catalina.connector.Connector;\nimport org.springframework.boot.web.embedded.tomcat.TomcatConnectorCustomizer;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.stereotype.Component;\n\nimport java.util.concurrent.Executor;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\n\n\/**\n * \u4f18\u96c5\u5173\u95ed Spring Boot tomcat\n *\/\n@Slf4j\n@Component\npublic class GracefulShutdownTomcat implements TomcatConnectorCustomizer, ApplicationListener&lt;ContextClosedEvent&gt; {\n    private volatile Connector connector;\n    private final int waitTime = 30;\n\n    @Override\n    public void customize(Connector connector) {\n        this.connector = connector;\n    }\n\n    @Override\n    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {\n        this.connector.pause();\n        Executor executor = this.connector.getProtocolHandler().getExecutor();\n        if (executor instanceof ThreadPoolExecutor) {\n            try {\n                ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executor;\n                threadPoolExecutor.shutdown();\n                if (!threadPoolExecutor.awaitTermination(waitTime, TimeUnit.SECONDS)) {\n                    log.warn(&quot;Tomcat thread pool did not shut down gracefully within &quot; + waitTime + &quot; seconds. Proceeding with forceful shutdown&quot;);\n                }\n            } catch (InterruptedException ex) {\n                Thread.currentThread().interrupt();\n            }\n        }\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">@EnableDiscoveryClient\n@SpringBootApplication\npublic class ShutdownApplication {\n\n    public static void main(String[] args) {\n        SpringApplication.run(ShutdownApplication.class, args);\n    }\n\n    @Autowired\n    private GracefulShutdownTomcat gracefulShutdownTomcat;\n\n    @Bean\n    public ServletWebServerFactory servletContainer() {\n        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory();\n        tomcat.addConnectorCustomizers(gracefulShutdownTomcat);\n        return tomcat;\n    }\n}<\/code><\/pre>\n<p>\u8be5\u65b9\u6848\u7684\u4ee3\u7801\u6765\u81ea\u5b98\u65b9issue\u4e2d\u7684\u8ba8\u8bba\uff0c\u6dfb\u52a0\u8fd9\u4e9b\u4ee3\u7801\u5230\u4f60\u7684Spring Boot\u9879\u76ee\u4e2d\uff0c\u7136\u540e\u518d\u91cd\u65b0\u542f\u52a8\u4e4b\u540e\uff0c\u53d1\u8d77\u6d4b\u8bd5\u8bf7\u6c42\uff0c\u7136\u540e\u53d1\u9001kill\u505c\u6b62\u6307\u4ee4<code>kill -2(Ctrl + C)<\/code>\u3001<code>kill -15<\/code>\u3002\u6d4b\u8bd5\u7ed3\u679c\uff1a<\/p>\n<ul>\n<li>Spring Boot\u7684\u5065\u5eb7\u68c0\u67e5\uff0c\u4e3aUP<\/li>\n<li>\u6b63\u5728\u6267\u884c\u64cd\u4f5c\u4e0d\u4f1a\u7ec8\u6b62\uff0c\u76f4\u5230\u6267\u884c\u5b8c\u6210<\/li>\n<li>\u4e0d\u518d\u63a5\u6536\u65b0\u7684\u8bf7\u6c42\uff0c\u5ba2\u6237\u7aef\u62a5\u9519\u4fe1\u606f\u4e3a\uff1a<code>Connection reset by peer<\/code><\/li>\n<li>\u6700\u540e\u6b63\u5e38\u7ec8\u6b62\u8fdb\u7a0b\uff08\u4e1a\u52a1\u6267\u884c\u5b8c\u6210\u540e\uff0c\u7acb\u5373\u8fdb\u7a0b\u505c\u6b62\uff09<\/li>\n<\/ul>\n<p>\u4ece\u6d4b\u8bd5\u7ed3\u679c\u6765\u770b\uff0c\u662f\u6ee1\u8db3\u6211\u4eec\u7684\u9700\u6c42\u7684\u3002\u5f53\u7136\u5982\u679c\u53d1\u9001\u6307\u4ee4<code>kill -9<\/code>\uff0c\u8fdb\u7a0b\u4f1a\u7acb\u5373\u505c\u6b62\u3002<\/p>\n<h3>\u9488\u5bf9undertow\u7684\u89e3\u51b3\u65b9\u6848<\/h3>\n<pre><code class=\"language-java\">import io.undertow.Undertow;\nimport io.undertow.server.ConnectorStatistics;\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springframework.boot.web.embedded.undertow.UndertowServletWebServer;\nimport org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext;\nimport org.springframework.context.ApplicationListener;\nimport org.springframework.context.event.ContextClosedEvent;\nimport org.springframework.stereotype.Component;\n\nimport java.lang.reflect.Field;\nimport java.util.List;\n\n\/**\n * \u4f18\u96c5\u5173\u95ed Spring Boot undertow\n *\/\n@Component\npublic class GracefulShutdownUndertow implements ApplicationListener&lt;ContextClosedEvent&gt; {\n\n    @Autowired\n    private GracefulShutdownUndertowWrapper gracefulShutdownUndertowWrapper;\n\n    @Autowired\n    private ServletWebServerApplicationContext context;\n\n    @Override\n    public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {\n        gracefulShutdownUndertowWrapper.getGracefulShutdownHandler().shutdown();\n        try {\n            UndertowServletWebServer webServer = (UndertowServletWebServer)context.getWebServer();\n            Field field = webServer.getClass().getDeclaredField(&quot;undertow&quot;);\n            field.setAccessible(true);\n            Undertow undertow = (Undertow) field.get(webServer);\n            List&lt;Undertow.ListenerInfo&gt; listenerInfo = undertow.getListenerInfo();\n            Undertow.ListenerInfo listener = listenerInfo.get(0);\n            ConnectorStatistics connectorStatistics = listener.getConnectorStatistics();\n            while (connectorStatistics.getActiveConnections() &gt; 0){}\n        } catch (Exception e) {\n            \/\/ Application Shutdown\n        }\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">import io.undertow.server.HandlerWrapper;\nimport io.undertow.server.HttpHandler;\nimport io.undertow.server.handlers.GracefulShutdownHandler;\nimport org.springframework.stereotype.Component;\n\n@Component\npublic class GracefulShutdownUndertowWrapper implements HandlerWrapper {\n    private GracefulShutdownHandler gracefulShutdownHandler;\n    @Override\n    public HttpHandler wrap(HttpHandler handler) {\n        if(gracefulShutdownHandler == null) {\n            this.gracefulShutdownHandler = new GracefulShutdownHandler(handler);\n        }\n        return gracefulShutdownHandler;\n    }\n    public GracefulShutdownHandler getGracefulShutdownHandler() {\n        return gracefulShutdownHandler;\n    }\n}<\/code><\/pre>\n<pre><code class=\"language-java\">public class UnipayProviderApplication {\n    public static void main(String[] args) {\n        SpringApplication.run(UnipayProviderApplication.class);\n    }\n    @Autowired\n    private GracefulShutdownUndertowWrapper gracefulShutdownUndertowWrapper;\n    @Bean\n    public UndertowServletWebServerFactory servletWebServerFactory() {\n        UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();\n        factory.addDeploymentInfoCustomizers(deploymentInfo -&gt; deploymentInfo.addOuterHandlerChainWrapper(gracefulShutdownUndertowWrapper));\n        factory.addBuilderCustomizers(builder -&gt; builder.setServerOption(UndertowOptions.ENABLE_STATISTICS, true));\n        return factory;\n    }\n}<\/code><\/pre>\n<p>\u91c7\u7528\u4e0eTomcat\u540c\u6837\u7684\u6d4b\u8bd5\u65b9\u6848\uff0c\u6d4b\u8bd5\u7ed3\u679c\uff1a<\/p>\n<ul>\n<li>Spring boot\u7684\u5065\u5eb7\u68c0\u67e5\uff0c\u4e3aUP<\/li>\n<li>\u6b63\u5728\u6267\u884c\u64cd\u4f5c\u4e0d\u4f1a\u7ec8\u6b62\uff0c\u76f4\u5230\u6267\u884c\u5b8c\u6210<\/li>\n<li>\u4e0d\u518d\u63a5\u6536\u65b0\u7684\u8bf7\u6c42\uff0c\u5ba2\u6237\u7aef\u62a5\u9519\u4fe1\u606f\u4e3a\uff1a<code>503 Service Unavailable<\/code><\/li>\n<li>\u6700\u540e\u6b63\u5e38\u7ec8\u6b62\u8fdb\u7a0b\uff08\u5728\u4e1a\u52a1\u6267\u884c\u5b8c\u6210\u540e\u7684\u4e00\u5206\u949f\u8fdb\u7a0b\u505c\u6b62\uff09<\/li>\n<\/ul>\n<h2>\u7ed3\u675f<\/h2>\n<p>\u5230\u6b64\u4e3a\u6b62\uff0c\u5bf9Java\u548cSpring boot\u5e94\u7528\u7684\u4f18\u96c5\u505c\u673a\u673a\u5236\u6709\u4e86\u57fa\u672c\u7684\u8ba4\u8bc6\u3002\u867d\u7136\u5b9e\u73b0\u4e86\u9700\u6c42\uff0c\u4f46\u662f\u8fd9\u5176\u4e2d\u8fd8\u6709\u5f88\u591a\u77e5\u8bc6\u70b9\u9700\u8981\u63a2\u7d22\uff0c\u6bd4\u5982Spring\u4e0a\u4e0b\u6587\u76d1\u542c\u5668\uff0c\u4e0a\u4e0b\u6587\u5173\u95ed\u4e8b\u4ef6\u7b49\uff0c\u8fd8\u6709undertow\u63d0\u4f9b\u7684<code>GracefulShutdownHandler<\/code>\u7684\u539f\u7406\u662f\u4ec0\u4e48\uff0c\u4e3a\u4ec0\u4e48\u662f1\u5206\u949f\u4e4b\u540e\u8fdb\u7a0b\u518d\u505c\u6b62\u3002<\/p>\n<p>\u53c2\u8003\u6587\u7ae0\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"http:\/\/www.spring4all.com\/article\/1022\" title=\"Spring Boot \u5185\u5d4c\u5bb9\u5668 Tomcat \/ Undertow \/ Jetty \u4f18\u96c5\u505c\u673a\u5b9e\u73b0\">Spring Boot \u5185\u5d4c\u5bb9\u5668 Tomcat \/ Undertow \/ Jetty \u4f18\u96c5\u505c\u673a\u5b9e\u73b0<\/a><br \/>\n\u53c2\u8003\u6587\u7ae0\uff1a<a target=\"_blank\" rel=\"noopener\" href=\"https:\/\/www.jianshu.com\/p\/0c49eb23c627\" title=\"Spring boot 2.0 \u4e4b\u4f18\u96c5\u505c\u673a\">Spring boot 2.0 \u4e4b\u4f18\u96c5\u505c\u673a<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Spring Boot\u201c\u7ea6\u5b9a\u5927\u4e8e\u914d\u7f6e\u201d\u7684\u7279\u6027\uff0c\u4f53\u73b0\u4e86\u4f18\u96c5\u6d41\u7545\u7684\u5f00\u53d1\u8fc7\u7a0b\uff0c\u5176\u90e8\u7f72\u542f\u52a8\u65b9\u5f0fjava -jar xx [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[41],"tags":[],"class_list":["post-919","post","type-post","status-publish","format-standard","hentry","category-spring-boot"],"_links":{"self":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/919","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/comments?post=919"}],"version-history":[{"count":0,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/posts\/919\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/media?parent=919"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/categories?post=919"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.appblog.cn\/index.php\/wp-json\/wp\/v2\/tags?post=919"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}