{"id":10020408,"date":"2024-11-22T02:53:56","date_gmt":"2024-11-21T18:53:56","guid":{"rendered":"https:\/\/itmax.cn\/?p=10020408"},"modified":"2024-11-22T02:53:56","modified_gmt":"2024-11-21T18:53:56","slug":"go-%e7%a8%8b%e5%ba%8f%e5%a6%82%e4%bd%95%e5%ae%9e%e7%8e%b0%e4%bc%98%e9%9b%85%e9%80%80%e5%87%ba%ef%bc%9f%e6%9d%a5%e7%9c%8b%e7%9c%8b-k8s-%e6%98%af%e6%80%8e%e4%b9%88%e5%81%9a%e7%9a%84","status":"publish","type":"post","link":"https:\/\/itmax.cn\/?p=10020408","title":{"rendered":"Go \u7a0b\u5e8f\u5982\u4f55\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\uff1f\u6765\u770b\u770b K8s \u662f\u600e\u4e48\u505a\u7684\u2014\u2014\u4e0a\u7bc7"},"content":{"rendered":"<blockquote><p>\u672c\u6587\u5e26\u5927\u5bb6\u4e00\u8d77\u6765\u8be6\u7ec6\u5b66\u4e60\u4e0b Go \u4e2d\u7684\u4f18\u96c5\u9000\u51fa\uff0c\u7531\u4e8e\u6587\u7ae0\u8fc7\u957f\uff0c\u62c6\u5206\u6210\u4e0a\u4e0b\u4e24\u7bc7\uff0c\u672c\u6587\u4e3a\u4e0a\u7bc7\u3002<\/p><\/blockquote>\n<p>\u5728\u5199 Go \u7a0b\u5e8f\u65f6\uff0c\u4f18\u96c5\u9000\u51fa\u662f\u4e00\u4e2a\u8001\u751f\u5e38\u8c08\u7684\u95ee\u9898\uff0c\u4e5f\u662f\u6211\u4eec\u5728\u5fae\u670d\u52a1\u5f00\u53d1\u8fc7\u7a0b\u4e2d\u7684\u6807\u914d\uff0c\u672c\u6587\u5c31\u6765\u4ecb\u7ecd\u4e0b\u5de5\u4f5c\u4e2d\u5e38\u89c1\u7684\u51e0\u79cd\u4f18\u96c5\u9000\u51fa\u573a\u666f\uff0c\u4ee5\u53ca\u5e26\u5927\u5bb6\u4e00\u8d77\u6765\u770b\u4e00\u4e0b K8s \u4e2d\u7684\u4f18\u96c5\u9000\u51fa\u662f\u600e\u4e48\u5b9e\u73b0\u7684\u3002<\/p>\n<h3>\u4f18\u96c5\u9000\u51fa<\/h3>\n<p>\u6211\u4eec\u4e00\u822c\u53ef\u4ee5\u901a\u8fc7\u5982\u4e0b\u65b9\u5f0f\u6267\u884c\u4e00\u4e2a Go \u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go\n$ .\/main<\/code><\/pre>\n<p>\u5982\u679c\u8981\u505c\u6b62\u6b63\u5728\u8fd0\u884c\u7684\u7a0b\u5e8f\uff0c\u901a\u5e38\u53ef\u4ee5\u8fd9\u6837\u505a\uff1a<\/p>\n<ul>\n<li>\u5728\u6b63\u5728\u8fd0\u884c\u7a0b\u5e8f\u7684\u7ec8\u7aef\u6267\u884c <code>Ctrl + C<\/code>\u3002<\/li>\n<li>\u5728\u6b63\u5728\u8fd0\u884c\u7a0b\u5e8f\u7684\u7ec8\u7aef\u6267\u884c <code>Ctrl + \/<\/code>\u3002<\/li>\n<li>\u5728\u7ec8\u7aef\u6267\u884c <code>kill<\/code> \u547d\u4ee4\uff0c\u5982 <code>kill pid<\/code> \u6216 <code>kill -9 pid<\/code>\u3002<\/li>\n<\/ul>\n<p>\u4ee5\u4e0a\u662f\u51e0\u79cd\u6bd4\u8f83\u5e38\u89c1\u7684\u7ec8\u6b62\u7a0b\u5e8f\u7684\u65b9\u5f0f\u3002<\/p>\n<p>\u8fd9\u51e0\u79cd\u64cd\u4f5c\u672c\u8eab\u6ca1\u4ec0\u4e48\u95ee\u9898\uff0c\u4e0d\u8fc7\u5b83\u4eec\u7684\u9ed8\u8ba4\u884c\u4e3a\u90fd\u6bd4\u8f83\u201c\u66b4\u529b\u201d\u3002\u5b83\u4eec\u4f1a\u76f4\u63a5\u5f3a\u5236\u5173\u95ed\u8fdb\u7a0b\uff0c\u8fd9\u5c31\u6709\u53ef\u80fd\u5bfc\u81f4\u51fa\u73b0\u6570\u636e\u4e0d\u4e00\u81f4\u7684\u95ee\u9898\u3002<\/p>\n<p>\u6bd4\u5982\uff0c\u4e00\u4e2a HTTP Server \u7a0b\u5e8f\u6b63\u5728\u5904\u7406\u7528\u6237\u4e0b\u5355\u8bf7\u6c42\uff0c\u7528\u6237\u4ed8\u6b3e\u64cd\u4f5c\u5df2\u7ecf\u5b8c\u6210\uff0c\u4f46\u8ba2\u5355\u72b6\u6001\u8fd8\u6ca1\u6765\u5f97\u53ca\u4ece\u300c\u5f85\u652f\u4ed8\u300d\u53d8\u66f4\u4e3a\u300c\u5df2\u652f\u4ed8\u300d\uff0c\u8fdb\u7a0b\u5c31\u88ab\u6740\u6b7b\u9000\u51fa\u4e86\u3002<\/p>\n<p>\u8fd9\u79cd\u60c5\u51b5\u80af\u5b9a\u662f\u8981\u907f\u514d\u7684\uff0c\u4e8e\u662f\u5c31\u6709\u4e86\u4f18\u96c5\u9000\u51fa\u7684\u6982\u5ff5\u3002<\/p>\n<p>\u6240\u8c13\u7684\u4f18\u96c5\u9000\u51fa\uff0c\u5176\u5b9e\u5c31\u662f\u5728\u5173\u95ed\u8fdb\u7a0b\u7684\u65f6\u5019\uff0c\u4e0d\u80fd\u201c\u66b4\u529b\u201d\u5173\u95ed\uff0c\u800c\u662f\u8981\u7b49\u5f85\u8fdb\u7a0b\u4e2d\u7684\u903b\u8f91\uff08\u6bd4\u5982\u4e00\u6b21\u5b8c\u6574\u7684 HTTP \u8bf7\u6c42\uff09\u5904\u7406\u5b8c\u6210\u540e\uff0c\u624d\u5173\u95ed\u8fdb\u7a0b\u3002<\/p>\n<h3>os\/singal \u4fe1\u53f7\u673a\u5236<\/h3>\n<p>\u5176\u5b9e\u4e0a\u9762\u4ecb\u7ecd\u7684\u51e0\u79cd\u7ec8\u6b62\u7a0b\u5e8f\u7684\u65b9\u5f0f\uff0c\u90fd\u662f\u901a\u8fc7\u5411\u6b63\u5728\u6267\u884c\u7684\u8fdb\u7a0b\u53d1\u9001\u4fe1\u53f7\u6765\u5b9e\u73b0\u7684\u3002<\/p>\n<ul>\n<li>\u5728\u7ec8\u7aef\u6267\u884c <code>Ctrl + C<\/code> \u53d1\u9001\u7684\u662f <code>SIGINT<\/code> \u4fe1\u53f7\uff0c\u8fd9\u4e2a\u4fe1\u53f7\u8868\u793a\u4e2d\u65ad\uff0c\u9ed8\u8ba4\u884c\u4e3a\u5c31\u662f\u7ec8\u6b62\u7a0b\u5e8f\u3002<\/li>\n<li>\u5728\u7ec8\u7aef\u6267\u884c <code>Ctrl + \/<\/code> \u53d1\u9001\u7684\u662f <code>SIGQUIT<\/code> \u4fe1\u53f7\uff0c\u8fd9\u4e2a\u4fe1\u53f7\u5176\u5b9e\u8ddf <code>SIGINT<\/code> \u4fe1\u53f7\u5dee\u4e0d\u591a\uff0c\u4e0d\u8fc7\u5b83\u4f1a\u751f\u6210 core \u6587\u4ef6\uff0c\u5e76\u5728\u7ec8\u7aef\u4f1a\u6253\u5370\u5f88\u591a\u65e5\u5fd7\u5185\u5bb9\uff0c\u4e0d\u5982 <code>Ctrl + C<\/code> \u5e38\u7528\u3002<\/li>\n<li><code>kill<\/code> \u547d\u4ee4\u4e0e\u4e0a\u9762\u4e24\u4e2a\u5feb\u6377\u952e\u76f8\u6bd4\uff0c\u66f4\u5e38\u7528\u4e8e<strong>\u7ed3\u675f\u4ee5\u540e\u53f0\u6a21\u5f0f\u542f\u52a8\u7684\u8fdb\u7a0b<\/strong>\uff0c<code>kill pid<\/code> \u53d1\u9001\u7684\u662f <code>SIGTERM<\/code> \u4fe1\u53f7\uff0c\u800c <code>kill -9 pid<\/code> \u5219\u53d1\u9001 <code>SIGKILL<\/code> \u4fe1\u53f7\u3002<\/li>\n<\/ul>\n<p>\u4ee5\u4e0a\u51e0\u79cd\u65b9\u5f0f\u4e2d\u6211\u4eec\u89c1\u5230\u4e86 4 \u79cd\u7ec8\u6b62\u8fdb\u7a0b\u7684\u4fe1\u53f7\uff1a<code>SIGINT<\/code>\u3001<code>SIGQUIT<\/code>\u3001<code>SIGTERM<\/code> \u548c <code>SIGKILL<\/code>\u3002<\/p>\n<p><strong>\u8fd9\u5176\u4e2d\uff0c\u524d 3 \u79cd\u4fe1\u53f7\u662f\u53ef\u4ee5\u88ab Go \u8fdb\u7a0b\u5185\u90e8\u6355\u83b7\u5e76\u5904\u7406\u7684\uff0c\u800c <code>SIGKILL<\/code> \u4fe1\u53f7\u5219\u65e0\u6cd5\u6355\u83b7\uff0c\u5b83\u4f1a\u5f3a\u5236\u6740\u6b7b\u8fdb\u7a0b\uff0c\u6ca1\u6709\u56de\u65cb\u4f59\u5730<\/strong>\u3002<\/p>\n<p>\u5728\u5199 Go \u4ee3\u7801\u65f6\uff0c\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u6211\u4eec\u6ca1\u6709\u5173\u6ce8\u4efb\u4f55\u4fe1\u53f7\uff0cGo \u7a0b\u5e8f\u4f1a\u81ea\u884c\u5904\u7406\u63a5\u6536\u5230\u7684\u4fe1\u53f7\u3002\u5bf9\u4e8e <code>SIGINT<\/code>\u3001<code>SIGTERM<\/code>\u3001<code>SIGQUIT<\/code> \u8fd9\u51e0\u4e2a\u4fe1\u53f7\uff0cGo \u7684\u5904\u7406\u65b9\u5f0f\u662f\u76f4\u63a5\u5f3a\u5236\u7ec8\u6b62\u8fdb\u7a0b\u3002<\/p>\n<p>\u8fd9\u5728 <code>os\/signal<\/code> \u5305\u7684 <a href=\"https:\/\/link.segmentfault.com\/?enc=oGMw%2FMmaJRzNJH1LRlMnHQ%3D%3D.CCQVhlVmGiNKOHIu0KHyq8yT2Lo1PDINbahCrlHjH0MbxM7FyveYvFdj%2FFSw5KaubaWBqLo%2FxpqPJsgevrj4EVLVUzpLx706HbE%2FjzpXxp635PgYTBMIcP97o%2FgnSmGj\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">\u5b98\u65b9\u6587\u6863<\/a> \u4e2d\u6709\u63d0\u53ca\uff1a<\/p>\n<blockquote>\n<p>The signals SIGKILL and SIGSTOP may not be caught by a program, and therefore cannot be affected by this package.<\/p>\n<p>By default, a synchronous signal is converted into a run-time panic. A SIGHUP, SIGINT, or SIGTERM signal causes the program to exit. A SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGSTKFLT, SIGEMT, or SIGSYS signal causes the program to exit with a stack dump. A SIGTSTP, SIGTTIN, or SIGTTOU signal gets the system default behavior (these signals are used by the shell for job control). The SIGPROF signal is handled directly by the Go runtime to implement runtime.CPUProfile. Other signals will be caught but no action will be taken.<\/p>\n<\/blockquote>\n<p>\u8bd1\u6587\u5982\u4e0b\uff1a<\/p>\n<blockquote>\n<p>SIGKILL \u548c SIGSTOP \u4e24\u4e2a\u4fe1\u53f7\u53ef\u80fd\u4e0d\u4f1a\u88ab\u7a0b\u5e8f\u6355\u83b7\uff0c\u56e0\u6b64\u4e0d\u4f1a\u53d7\u5230\u6b64\u5305\u7684\u5f71\u54cd\u3002<\/p>\n<p>\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4e00\u4e2a\u540c\u6b65\u4fe1\u53f7\u4f1a\u88ab\u8f6c\u6362\u4e3a\u8fd0\u884c\u65f6\u6050\u614c\uff08panic\uff09\u3002\u5728\u6536\u5230 SIGHUP\u3001SIGINT \u6216 SIGTERM \u4fe1\u53f7\u65f6\uff0c\u7a0b\u5e8f\u5c06\u9000\u51fa\u3002\u6536\u5230 SIGQUIT\u3001SIGILL\u3001SIGTRAP\u3001SIGABRT\u3001SIGSTKFLT\u3001SIGEMT \u6216 SIGSYS \u4fe1\u53f7\u65f6\uff0c\u7a0b\u5e8f\u4f1a\u5728\u9000\u51fa\u65f6\u751f\u6210\u5806\u6808\u8f6c\u50a8\uff08stack dump\uff09\u3002SIGTSTP\u3001SIGTTIN \u6216 SIGTTOU \u4fe1\u53f7\u5c06\u6309\u7167\u7cfb\u7edf\u7684\u9ed8\u8ba4\u884c\u4e3a\u5904\u7406\uff08\u8fd9\u4e9b\u4fe1\u53f7\u901a\u5e38\u7531 shell \u7528\u4e8e\u4f5c\u4e1a\u63a7\u5236\uff09\u3002SIGPROF \u4fe1\u53f7\u7531 Go \u8fd0\u884c\u65f6\u76f4\u63a5\u5904\u7406\uff0c\u7528\u4e8e\u5b9e\u73b0 runtime.CPUProfile\u3002\u5176\u4ed6\u4fe1\u53f7\u5c06\u88ab\u6355\u83b7\u4f46\u4e0d\u4f1a\u91c7\u53d6\u4efb\u4f55\u884c\u52a8\u3002<\/p>\n<\/blockquote>\n<p>\u4ece\u8fd9\u6bb5\u63cf\u8ff0\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u53d1\u73b0\uff0c<strong>Go \u7a0b\u5e8f\u5728\u6536\u5230 <code>SIGINT<\/code> \u548c <code>SIGTERM<\/code> \u4e24\u79cd\u4fe1\u53f7\u65f6\uff0c\u7a0b\u5e8f\u4f1a\u76f4\u63a5\u9000\u51fa\uff0c\u5728\u6536\u5230 <code>SIGQUIT<\/code> \u4fe1\u53f7\u65f6\uff0c\u7a0b\u5e8f\u9000\u51fa\u5e76\u751f\u6210 <code>stack dump<\/code>\uff0c\u5373\u9000\u51fa\u540e\u63a7\u5236\u53f0\u6253\u5370\u7684\u90a3\u4e9b\u65e5\u5fd7\u3002<\/strong><\/p>\n<p>\u6211\u4eec\u53ef\u4ee5\u5199\u4e00\u4e2a\u7b80\u5355\u7684\u5c0f\u7a0b\u5e8f\uff0c\u6765\u5b9e\u9a8c\u4e00\u4e0b <code>Ctrl + C<\/code> \u7ec8\u6b62 Go \u7a0b\u5e8f\u7684\u6548\u679c\u3002<\/p>\n<p>\u793a\u4f8b\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;fmt&quot;\n    &quot;time&quot;\n)\n\nfunc main() {\n    fmt.Println(&quot;main enter&quot;)\n\n    time.Sleep(time.Second)\n\n    fmt.Println(&quot;main exit&quot;)\n}<\/code><\/pre>\n<p>\u6309\u7167\u5982\u4e0b\u65b9\u5f0f\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\nmain enter\n^C\n$ echo $?\n130<\/code><\/pre>\n<p>\u8fd9\u91cc\u5148\u542f\u52a8 Go \u7a0b\u5e8f\uff0c\u7136\u540e\u5728 Go \u7a0b\u5e8f\u6267\u884c\u5230 <code>time.Sleep<\/code> \u7684\u65f6\uff0c\u6309\u4e0b <code>Ctrl + C<\/code>\uff0c\u7a0b\u5e8f\u4f1a\u7acb\u5373\u7ec8\u6b62\u3002<\/p>\n<p>\u5e76\u4e14\u901a\u8fc7 <code>echo $?<\/code> \u547d\u4ee4\u53ef\u4ee5\u770b\u5230\u7a0b\u5e8f\u9000\u51fa\u7801\u4e3a <code>130<\/code>\uff0c\u8868\u793a\u5f02\u5e38\u9000\u51fa\uff0c\u7a0b\u5e8f\u6b63\u5e38\u9000\u51fa\u7801\u901a\u5e38\u4e3a <code>0<\/code>\u3002<\/p>\n<blockquote><p>NOTE: <br \/>\u8fd9\u91cc\u4e4b\u6240\u4ee5\u4f7f\u7528 <code>go build<\/code> \u547d\u4ee4\u5148\u5c06 Go \u7a0b\u5e8f\u7f16\u8bd1\u6210\u4e8c\u8fdb\u5236\u6587\u4ef6\u7136\u540e\u518d\u6267\u884c\uff0c\u800c\u4e0d\u662f\u76f4\u63a5\u4f7f\u7528 <code>go run<\/code> \u547d\u4ee4\u6267\u884c\u7a0b\u5e8f\u3002\u662f\u56e0\u4e3a\u4e0d\u7ba1\u7a0b\u5e8f\u6267\u884c\u7ed3\u679c\u5982\u4f55\uff0c<code>go run<\/code> \u547d\u4ee4\u8fd4\u56de\u7684\u7a0b\u5e8f\u9000\u51fa\u72b6\u6001\u7801\u59cb\u7ec8\u4e3a <code>1<\/code>\u3002\u53ea\u6709\u5148\u5c06 Go \u7a0b\u5e8f\u7f16\u8bd1\u6210\u4e8c\u8fdb\u5236\u6587\u4ef6\u4ee5\u540e\uff0c\u518d\u6267\u884c\u4e8c\u8fdb\u5236\u6587\u4ef6\u624d\u80fd\u83b7\u5f97\uff08\u53ef\u4ee5\u4f7f\u7528 <code>echo $?<\/code> \u547d\u4ee4\uff09\u6b63\u5e38\u7684\u8fdb\u7a0b\u9000\u51fa\u7801\u3002<\/p><\/blockquote>\n<p><strong>\u5982\u679c\u6211\u4eec\u5728 Go \u4ee3\u7801\u4e2d\u81ea\u884c\u5904\u7406\u6536\u5230\u7684 <code>Ctrl + C<\/code> \u4f20\u6765\u7684\u4fe1\u53f7 <code>SIGINT<\/code>\uff0c\u6211\u4eec\u5c31\u80fd\u591f\u63a7\u5236\u7a0b\u5e8f\u7684\u9000\u51fa\u884c\u4e3a\uff0c\u8fd9\u4e5f\u662f\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u7684\u673a\u4f1a\u6240\u5728\u3002<\/strong><\/p>\n<p>\u73b0\u5728\u6211\u4eec\u5c31\u6765\u4e00\u8d77\u5b66\u4e60\u4e0b Go \u4e3a\u6211\u4eec\u63d0\u4f9b\u7684\u4fe1\u53f7\u5904\u7406\u5305 <code>os\/singal<\/code>\u3002<\/p>\n<p>Go \u4e3a\u6211\u4eec\u63d0\u4f9b\u4e86 <code>os\/singal<\/code> \u5185\u7f6e\u5305\u7528\u6765\u5904\u7406\u4fe1\u53f7\uff0c<code>os\/singal<\/code> \u5305\u63d0\u4f9b\u4e86\u5982\u4e0b <a href=\"https:\/\/link.segmentfault.com\/?enc=WvHAhyJwZ%2F8OihKlhkNz1g%3D%3D.t6GglcQo4%2FfqOe8EbYFR8OMVkThvikXNk%2BrInqkSxgkzTLfwbZMo2K%2F1cpxdpY3a\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">6 \u4e2a\u51fd\u6570<\/a> \u4f9b\u6211\u4eec\u4f7f\u7528\uff1a<\/p>\n<pre><code class=\"go\">\/\/ \u5ffd\u7565\u4e00\u4e2a\u6216\u591a\u4e2a\u6307\u5b9a\u7684\u4fe1\u53f7\nfunc Ignore(sig ...os.Signal)\n\/\/ \u5224\u65ad\u6307\u5b9a\u7684\u4fe1\u53f7\u662f\u5426\u88ab\u5ffd\u7565\u4e86\nfunc Ignored(sig os.Signal) bool\n\/\/ \u6ce8\u518c\u9700\u8981\u5173\u6ce8\u7684\u67d0\u4e9b\u4fe1\u53f7\uff0c\u4fe1\u53f7\u4f1a\u88ab\u4f20\u9012\u7ed9\u51fd\u6570\u7684\u7b2c\u4e00\u4e2a\u53c2\u6570\uff08channel \u7c7b\u578b\u7684\u53c2\u6570 c\uff09\nfunc Notify(c chan&lt;- os.Signal, sig ...os.Signal)\n\/\/ \u5e26\u6709 Context \u7248\u672c\u7684 Notify\nfunc NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc)\n\/\/ \u53d6\u6d88\u5173\u6ce8\u6307\u5b9a\u7684\u4fe1\u53f7\uff08\u4e4b\u524d\u901a\u8fc7\u8c03\u7528 Notify \u6240\u5173\u6ce8\u7684\u4fe1\u53f7\uff09\nfunc Reset(sig ...os.Signal)\n\/\/ \u505c\u6b62\u5411 channel \u53d1\u9001\u6240\u6709\u5173\u6ce8\u7684\u4fe1\u53f7\nfunc Stop(c chan&lt;- os.Signal)<\/code><\/pre>\n<p><strong>\u6b64\u5305\u4e2d\u7684\u51fd\u6570\u5141\u8bb8\u7a0b\u5e8f\u66f4\u6539 Go \u7a0b\u5e8f\u5904\u7406\u4fe1\u53f7\u7684\u9ed8\u8ba4\u65b9\u5f0f\u3002<\/strong><\/p>\n<p>\u8fd9\u91cc\u6211\u4eec\u6700\u9700\u8981\u5173\u6ce8\u7684\u5c31\u662f <code>Notify<\/code> \u51fd\u6570\uff0c\u5b83\u53ef\u4ee5\u7528\u6765\u6ce8\u518c\u6211\u4eec\u9700\u8981\u5173\u6ce8\u7684\u67d0\u4e9b\u4fe1\u53f7\uff0c\u8fd9\u4f1a\u7981\u7528\u7ed9\u5b9a\u4fe1\u53f7\u7684\u9ed8\u8ba4\u884c\u4e3a\uff0c\u8f6c\u800c\u901a\u8fc7\u4e00\u4e2a\u6216\u591a\u4e2a\u5df2\u6ce8\u518c\u7684\u901a\u9053\uff08<code>channel<\/code>\uff09\u4f20\u9001\u5b83\u4eec\u3002<\/p>\n<p>\u6211\u4eec\u5199\u4e00\u4e2a\u4ee3\u7801\u793a\u4f8b\u7a0b\u5e8f\u6765\u770b\u4e00\u4e0b <code>os\/singal<\/code> \u5982\u4f55\u4f7f\u7528\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;fmt&quot;\n    &quot;os&quot;\n    &quot;os\/signal&quot;\n    &quot;syscall&quot;\n)\n\nfunc main() {\n    fmt.Println(&quot;main enter&quot;)\n\n    quit := make(chan os.Signal, 1)\n    \/\/ \u6ce8\u518c\u9700\u8981\u5173\u6ce8\u7684\u4fe1\u53f7\uff1aSIGINT\u3001SIGTERM\u3001SIGQUIT\n    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)\n    \/\/ \u963b\u585e\u5f53\u524d goroutine \u7b49\u5f85\u4fe1\u53f7\n    sig := &lt;-quit\n    fmt.Printf(&quot;received signal: %d-%s\/n&quot;, sig, sig)\n\n    fmt.Println(&quot;main exit&quot;)\n}<\/code><\/pre>\n<p>\u5982\u4f60\u6240\u89c1\uff0c<code>os\/singal<\/code> \u5305\u4f7f\u7528\u8d77\u6765\u975e\u5e38\u7b80\u5355\u3002<\/p>\n<p>\u9996\u5148\u6211\u4eec\u5b9a\u4e49\u4e86\u4e00\u4e2a\u540d\u4e3a <code>quit<\/code> \u7684 <code>channel<\/code>\uff0c\u7c7b\u578b\u4e3a <code>os.Signal<\/code>\uff0c\u957f\u5ea6\u4e3a <code>1<\/code>\uff0c\u7528\u6765\u63a5\u6536\u5173\u6ce8\u7684\u4fe1\u53f7\u3002<\/p>\n<p>\u8c03\u7528 <code>signal.Notify<\/code> \u51fd\u6570\u5bf9\u6211\u4eec\u8981\u5173\u6ce8\u7684\u4fe1\u53f7\u8fdb\u884c\u6ce8\u518c\u3002<\/p>\n<p>\u7136\u540e\u8c03\u7528 <code>sig := &lt;-quit<\/code> \u5c06\u4f1a\u963b\u585e\u5f53\u524d <code>main<\/code> \u51fd\u6570\u6240\u5728\u7684\u4e3b goroutine\uff0c\u76f4\u5230\u8fdb\u7a0b\u63a5\u6536\u5230 <code>SIGINT<\/code>\u3001<code>SIGTERM<\/code>\u3001<code>SIGQUIT<\/code> \u4e2d\u7684\u4efb\u610f\u4e00\u4e2a\u4fe1\u53f7\u3002<\/p>\n<p>\u5f53\u6211\u4eec\u5173\u6ce8\u4e86\u8fd9\u51e0\u4e2a\u4fe1\u53f7\u4ee5\u540e\uff0cGo \u4e0d\u4f1a\u81ea\u884c\u5904\u7406\u8fd9\u51e0\u4e2a\u4fe1\u53f7\uff0c\u9700\u8981\u6211\u4eec\u81ea\u5df1\u6765\u5904\u7406\u3002<\/p>\n<p>\u7a0b\u5e8f\u4e2d\u6253\u5370\u4e86\u51e0\u6761\u65e5\u5fd7\u7528\u6765\u89c2\u5bdf\u6548\u679c\u3002<\/p>\n<p>\u8fd9\u91cc\u9700\u8981\u7279\u522b\u6ce8\u610f\u7684\u662f\uff1a<strong>\u6211\u4eec\u901a\u8fc7 <code>signal.Notify(c chan&lt;- os.Signal, sig ...os.Signal)<\/code> \u51fd\u6570\u6ce8\u518c\u6240\u5173\u6ce8\u7684\u4fe1\u53f7\uff0c<code>signal<\/code> \u5305\u5728\u6536\u5230\u5bf9\u5e94\u4fe1\u53f7\u65f6\uff0c\u4f1a\u5411 <code>c<\/code> \u8fd9\u4e2a <code>channel<\/code> \u53d1\u9001\u4fe1\u53f7\uff0c\u4f46\u662f\u53d1\u9001\u4fe1\u53f7\u65f6\u4e0d\u4f1a\u963b\u585e\u3002\u4e5f\u5c31\u662f\u8bf4\uff0c\u5982\u679c <code>signal<\/code> \u5305\u53d1\u9001\u4fe1\u53f7\u5230 <code>c<\/code> \u65f6\uff0c\u7531\u4e8e <code>c<\/code> \u6ee1\u4e86\u800c\u5bfc\u81f4\u963b\u585e\uff0c<code>signal<\/code> \u5305\u4f1a\u76f4\u63a5\u4e22\u5f03\u4fe1\u53f7\u3002<\/strong><\/p>\n<p>\u5728 <code>signal.Notify<\/code> \u51fd\u6570\u7b7e\u540d\u4e0a\u65b9\u7684\u6ce8\u91ca\u4e2d\u6709\u8be6\u7ec6\u8bf4\u660e\uff1a<\/p>\n<blockquote><p><a href=\"https:\/\/link.segmentfault.com\/?enc=S58fqEAfK2KE0J3as6gr%2Bg%3D%3D.PpFY6bU%2FiNsk1mLP5N3oX5xhC%2BBhfcPMsb1xDvkskqMjPYNCdvKI20NYgnD6NQ26tYlaZM7bExP3UnQgiMObNh3nCNYg%2FLgqPZ6ro1VL%2B3DXNFjZ3D6kzzddV1IYRs6g\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/os\/signal\/signal.go#L121<\/a><\/p><\/blockquote>\n<pre><code class=\"go\">\/\/ Notify causes package signal to relay incoming signals to c.\n\/\/ If no signals are provided, all incoming signals will be relayed to c.\n\/\/ Otherwise, just the provided signals will.\n\/\/\n\/\/ Package signal will not block sending to c: the caller must ensure\n\/\/ that c has sufficient buffer space to keep up with the expected\n\/\/ signal rate. For a channel used for notification of just one signal value,\n\/\/ a buffer of size 1 is sufficient.\n\/\/\n\/\/ It is allowed to call Notify multiple times with the same channel:\n\/\/ each call expands the set of signals sent to that channel.\n\/\/ The only way to remove signals from the set is to call Stop.\n\/\/\n\/\/ It is allowed to call Notify multiple times with different channels\n\/\/ and the same signals: each channel receives copies of incoming\n\/\/ signals independently.\nfunc Notify(c chan&lt;- os.Signal, sig ...os.Signal) {\n    ...\n}<\/code><\/pre>\n<p>\u8bd1\u6587\u5982\u4e0b\uff1a<\/p>\n<blockquote><p>Notify \u4f1a\u8ba9 signal \u5305\u5c06\u6536\u5230\u7684\u4fe1\u53f7\u8f6c\u53d1\u5230\u901a\u9053 c \u4e2d\u3002\u5982\u679c\u6ca1\u6709\u63d0\u4f9b\u5177\u4f53\u7684\u4fe1\u53f7\u7c7b\u578b\uff0c\u5219\u6240\u6709\u6536\u5230\u7684\u4fe1\u53f7\u90fd\u4f1a\u88ab\u8f6c\u53d1\u5230 c\u3002\u5426\u5219\uff0c\u53ea\u4f1a\u5c06\u6307\u5b9a\u7684\u4fe1\u53f7\u8f6c\u53d1\u5230 c\u3002<br \/>signal \u5305\u5411 c \u53d1\u9001\u4fe1\u53f7\u65f6\u4e0d\u4f1a\u963b\u585e\uff1a\u8c03\u7528\u8005\u5fc5\u987b\u786e\u4fdd c \u5177\u6709\u8db3\u591f\u7684\u7f13\u51b2\u533a\u7a7a\u95f4\u4ee5\u5e94\u5bf9\u9884\u671f\u7684\u4fe1\u53f7\u901f\u7387\u3002\u5982\u679c\u4e00\u4e2a\u901a\u9053\u4ec5\u7528\u4e8e\u901a\u77e5\u5355\u4e2a\u4fe1\u53f7\u503c\uff0c\u90a3\u4e48\u4e00\u4e2a\u5927\u5c0f\u4e3a 1 \u7684\u7f13\u51b2\u533a\u5c31\u8db3\u591f\u4e86\u3002<br \/>\u5141\u8bb8\u4f7f\u7528\u540c\u4e00\u4e2a\u901a\u9053\u591a\u6b21\u8c03\u7528 Notify\uff1a\u6bcf\u6b21\u8c03\u7528\u90fd\u4f1a\u6269\u5c55\u53d1\u9001\u5230\u8be5\u901a\u9053\u7684\u4fe1\u53f7\u96c6\u3002\u8981\u79fb\u9664\u4fe1\u53f7\u96c6\u4e2d\u7684\u4fe1\u53f7\uff0c\u552f\u4e00\u7684\u65b9\u6cd5\u662f\u8c03\u7528 Stop\u3002<br \/>\u5141\u8bb8\u4f7f\u7528\u4e0d\u540c\u7684\u901a\u9053\u548c\u76f8\u540c\u7684\u4fe1\u53f7\u591a\u6b21\u8c03\u7528 Notify\uff1a\u6bcf\u4e2a\u901a\u9053\u90fd\u4f1a\u72ec\u7acb\u63a5\u6536\u4f20\u5165\u4fe1\u53f7\u7684\u526f\u672c\u3002<\/p><\/blockquote>\n<p>\u5728 <code>signal.Notify<\/code> \u51fd\u6570\u5185\u90e8\u901a\u8fc7 <code>go watchSignalLoop()<\/code> \u65b9\u5f0f\u542f\u52a8\u4e86\u4e00\u4e2a\u65b0\u7684 goroutine\uff0c\u7528\u6765\u76d1\u63a7\u4fe1\u53f7\uff1a<\/p>\n<pre><code class=\"go\">var (\n    \/\/ watchSignalLoopOnce guards calling the conditionally\n    \/\/ initialized watchSignalLoop. If watchSignalLoop is non-nil,\n    \/\/ it will be run in a goroutine lazily once Notify is invoked.\n    \/\/ See Issue 21576.\n    watchSignalLoopOnce sync.Once\n    watchSignalLoop     func()\n)\n\n...\n\nfunc Notify(c chan&lt;- os.Signal, sig ...os.Signal) {\n    ...\n    add := func(n int) {\n        if n &lt; 0 {\n            return\n        }\n        if !h.want(n) {\n            h.set(n)\n            if handlers.ref[n] == 0 {\n                enableSignal(n)\n\n                \/\/ The runtime requires that we enable a\n                \/\/ signal before starting the watcher.\n                watchSignalLoopOnce.Do(func() {\n                    if watchSignalLoop != nil {\n                        \/\/ \u76d1\u63a7\u4fe1\u53f7\u5faa\u73af\n                        go watchSignalLoop()\n                    }\n                })\n            }\n            handlers.ref[n]++\n        }\n    }\n...\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u53d1\u73b0 <code>watchSignalLoop<\/code> \u51fd\u6570\u53ea\u4f1a\u6267\u884c\u4e00\u6b21\uff0c\u5e76\u4e14\u91c7\u7528 goroutine \u7684\u65b9\u5f0f\u6267\u884c\u3002<\/p>\n<p><code>watchSignalLoop<\/code> \u51fd\u6570\u7684\u5b9a\u4e49\u53ef\u4ee5\u5728 <code>os\/signal\/signal_unix.go<\/code> \u4e2d\u627e\u5230\uff1a<\/p>\n<blockquote><p><a href=\"https:\/\/link.segmentfault.com\/?enc=u6sRU2jCy57mcOXqo3FaDg%3D%3D.3jHaxskYoWz%2BoG%2BexUvgYs2yS7hC%2BKx3VSg8hdGktzqlFSnWFz2pr6f1T4hdtQl%2FnJJu%2Fm7kPEMCiLL2eLMKVqvV%2FMnnCI3z7YGwtxesDSfurMh%2B9Rw3TF6nbhRdBD%2Fw\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/os\/signal\/signal_unix.go<\/a><\/p><\/blockquote>\n<pre><code class=\"go\">func loop() {\n    for {\n        process(syscall.Signal(signal_recv()))\n    }\n}\n\nfunc init() {\n    watchSignalLoop = loop\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u770b\u5230\uff0c\u8fd9\u91cc\u5f00\u542f\u4e86\u4e00\u4e2a\u65e0\u9650\u5faa\u73af\uff0c\u6765\u6267\u884c <code>process<\/code> \u51fd\u6570\uff1a<\/p>\n<blockquote><p><a href=\"https:\/\/link.segmentfault.com\/?enc=COM7uRz%2B8Jw5gST6L3IFlw%3D%3D.E7tQYhO8Mnm728gTsgih6nnkW7PYHgQRuAzn47vaJAw%2FMTZTPOw3OFjzlo9vdbHkd6nWFwP5wlmJFkRo3ADt%2FhJEZpHrYFm7CnYKv74xCp7Fkdc%2F7R5QMMMn0PZmDR0O\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/os\/signal\/signal.go#L232<\/a><\/p><\/blockquote>\n<pre><code class=\"go\">func process(sig os.Signal) {\n    n := signum(sig)\n    if n &lt; 0 {\n        return\n    }\n\n    handlers.Lock()\n    defer handlers.Unlock()\n\n    for c, h := range handlers.m {\n        if h.want(n) {\n            \/\/ send but do not block for it\n            select {\n            case c &lt;- sig:\n            default: \/\/ \u5f53\u5411 c \u53d1\u9001\u4fe1\u53f7\u9047\u5230\u963b\u585e\u65f6\uff0cdefault \u903b\u8f91\u76f4\u63a5\u4e22\u5f03\u4e86 sig \u4fe1\u53f7\uff0c\u6ca1\u505a\u4efb\u4f55\u5904\u7406\n            }\n        }\n    }\n\n    \/\/ Avoid the race mentioned in Stop.\n    for _, d := range handlers.stopping {\n        if d.h.want(n) {\n            select {\n            case d.c &lt;- sig:\n            default:\n            }\n        }\n    }\n}<\/code><\/pre>\n<p>\u8fd9\u4e2a <code>process<\/code> \u51fd\u6570\u5c31\u662f <code>os\/signal<\/code> \u5305\u5411\u6211\u4eec\u6ce8\u518c\u7684 <code>channel<\/code> \u53d1\u9001\u4fe1\u53f7\u7684\u6838\u5fc3\u903b\u8f91\u3002<\/p>\n<p><code>os\/signal<\/code> \u5305\u5728\u6536\u5230\u6211\u4eec\u4f7f\u7528 <code>signal.Notify<\/code> \u6ce8\u518c\u7684\u4fe1\u53f7\u65f6\uff0c\u4f1a\u901a\u8fc7 <code>c &lt;- sig<\/code> \u5411\u901a\u9053 <code>c<\/code> \u53d1\u9001\u4fe1\u53f7\u3002\u5982\u679c\u5411 <code>c<\/code> \u53d1\u9001\u4fe1\u53f7\u9047\u5230\u963b\u585e\uff0c<code>default<\/code> \u903b\u8f91\u4f1a\u76f4\u63a5\u4e22\u5f03 <code>sig<\/code> \u4fe1\u53f7\uff0c\u4e0d\u505a\u4efb\u4f55\u5904\u7406\u3002\u8fd9\u4e5f\u5c31\u662f\u4e3a\u4ec0\u4e48\u6211\u4eec\u5728\u521b\u5efa <code>quit := make(chan os.Signal, 1)<\/code> \u65f6\u4e00\u5b9a\u8981\u7ed9 <code>channel<\/code> \u5206\u914d\u81f3\u5c11 <code>1<\/code> \u4e2a\u7f13\u51b2\u533a\u3002<\/p>\n<p>\u6211\u4eec\u53ef\u4ee5\u5c1d\u8bd5\u6267\u884c\u8fd9\u4e2a\u793a\u4f8b\u7a0b\u5e8f\uff0c\u5f97\u5230\u5982\u4e0b\u8f93\u51fa\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\nmain enter\n^Creceived signal: 2-interrupt\nmain exit\n$ echo $?\n0<\/code><\/pre>\n<p>\u9996\u5148\u4f7f\u7528 <code>go build -o main main.go &amp;&amp; .\/main<\/code> \u547d\u4ee4\u7f16\u8bd1\u5e76\u6267\u884c\u7a0b\u5e8f\u3002<\/p>\n<p>\u7136\u540e\u7a0b\u5e8f\u4f1a\u6253\u5370 <code>main enter<\/code> \u65e5\u5fd7\u5e76\u963b\u585e\u5728\u90a3\u91cc\u3002<\/p>\n<p>\u6b64\u65f6\u6211\u4eec\u6309\u4e0b <code>Ctrl + C<\/code>\uff0c\u63a7\u5236\u53f0\u4f1a\u6253\u5370\u65e5\u5fd7 <code>^Creceived signal: 2-interrupt<\/code>\uff0c\u7136\u540e\u8f93\u51fa <code>main exit<\/code> \u5e76\u9000\u51fa\u3002<\/p>\n<p>\u8fd9\u91cc\u7b2c\u4e8c\u884c\u65e5\u5fd7\u5f00\u5934\u7684 <code>^C<\/code> \u5c31\u8868\u793a\u6211\u4eec\u6309\u4e0b\u4e86 <code>Ctrl + C<\/code>\uff0c\u6536\u5230\u7684\u4fe1\u53f7\u503c\u4e3a\u4e3a <code>2<\/code>\uff0c\u5b57\u7b26\u4e32\u8868\u793a\u5f62\u5f0f\u4e3a <code>interrupt<\/code>\u3002<\/p>\n<p>\u7a0b\u5e8f\u9000\u51fa\u7801\u4e3a <code>0<\/code>\uff0c\u56e0\u4e3a\u4fe1\u53f7\u88ab\u6211\u4eec\u6355\u83b7\u5e76\u5904\u7406\uff0c\u7136\u540e\u7a0b\u5e8f\u6b63\u5e38\u9000\u51fa\uff0c\u6211\u4eec\u6539\u53d8\u4e86 Go \u7a0b\u5e8f\u5bf9 <code>SIGINT<\/code> \u4fe1\u53f7\u5904\u7406\u7684\u9ed8\u8ba4\u884c\u4e3a\u3002<\/p>\n<p>\u5176\u5b9e\u6bcf\u4e00\u4e2a\u4fe1\u53f7\u5728 <code>os\/signal<\/code> \u5305\u4e2d\u90fd\u88ab\u5b9a\u4e49\u4e3a\u4e00\u4e2a\u5e38\u91cf\uff1a<\/p>\n<pre><code class=\"go\">\/\/ A Signal is a number describing a process signal.\n\/\/ It implements the os.Signal interface.\ntype Signal int\n\n\/\/ Signals\nconst (\n    SIGINT    = Signal(0x2)\n    SIGQUIT   = Signal(0x3)\n    SIGTERM   = Signal(0xf)\n    ...\n)<\/code><\/pre>\n<p>\u5bf9\u5e94\u7684\u5b57\u7b26\u4e32\u8868\u793a\u5f62\u5f0f\u4e3a\uff1a<\/p>\n<pre><code class=\"go\">\n\/\/ Signal table\nvar signals = [...]string{\n    2:  &quot;interrupt&quot;,\n    3:  &quot;quit&quot;,\n    15: &quot;terminated&quot;,\n    ...\n}<\/code><\/pre>\n<blockquote><p>NOTE:<br \/>\u793a\u4f8b\u7a0b\u5e8f\u4e2d\uff0c\u6211\u4eec\u662f\u5728 <code>main<\/code> \u51fd\u6570\u4e2d\u8c03\u7528 <code>signal.Notify<\/code> \u6ce8\u518c\u5173\u6ce8\u7684\u4fe1\u53f7\uff0c\u5373\u5728\u4e3b\u7684 goroutine \u4e2d\u8c03\u7528\u3002\u5176\u5b9e\u5c06\u5176\u653e\u5728\u5b50 goroutine \u4e2d\u8c03\u7528\u4e5f\u662f\u53ef\u4ee5\u7684\uff0c\u5e76\u4e0d\u4f1a\u5f71\u54cd\u7a0b\u5e8f\u6548\u679c\uff0c\u4f60\u53ef\u4ee5\u81ea\u884c\u5c1d\u8bd5\u3002<\/p><\/blockquote>\n<p>\u73b0\u5728\u6211\u4eec\u5df2\u7ecf\u77e5\u9053\u4e86\u5728 Go \u4e2d\u5982\u4f55\u4f7f\u7528 <code>os\/signal<\/code> \u6765\u63a5\u6536\u5e76\u5904\u7406\u8fdb\u7a0b\u9000\u51fa\u4fe1\u53f7\uff0c\u90a3\u4e48\u63a5\u4e0b\u6765\u8981\u5173\u6ce8\u7684\u5c31\u662f\u5728\u8fdb\u7a0b\u9000\u51fa\u524d\uff0c\u5982\u4f55\u4fdd\u8bc1\u4e3b\u903b\u8f91\u64cd\u4f5c\u5b8c\u6210\uff0c\u4ee5\u5b9e\u73b0 Go \u7a0b\u5e8f\u7684\u4f18\u96c5\u9000\u51fa\u3002<\/p>\n<h3>net\/http \u7684\u4f18\u96c5\u9000\u51fa<\/h3>\n<p>\u8bb2\u89e3\u5b8c\u4e86\u524d\u7f6e\u77e5\u8bc6\uff0c\u7ec8\u4e8e\u53ef\u4ee5\u8fdb\u5165\u8bb2\u89e3\u4f18\u96c5\u9000\u51fa\u7684\u73af\u8282\u4e86\u3002<\/p>\n<p>\u9996\u5148\u6211\u4eec\u5c31\u4ee5\u4e00\u4e2a HTTP Server \u4e3a\u4f8b\uff0c\u8bb2\u89e3\u4e0b\u5728 Go \u7a0b\u5e8f\u4e2d\u5982\u4f55\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u3002<\/p>\n<h4>HTTP Server \u793a\u4f8b\u7a0b\u5e8f<\/h4>\n<p>\u8fd9\u662f\u4e00\u4e2a\u7b80\u5355\u7684 HTTP Server \u793a\u4f8b\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;log&quot;\n    &quot;net\/http&quot;\n    &quot;time&quot;\n)\n\nfunc main() {\n    srv := &amp;http.Server{\n        Addr: &quot;:8000&quot;,\n    }\n\n    http.HandleFunc(&quot;\/sleep&quot;, func(w http.ResponseWriter, r *http.Request) {\n        duration, err := time.ParseDuration(r.FormValue(&quot;duration&quot;))\n        if err != nil {\n            http.Error(w, err.Error(), 400)\n            return\n        }\n        time.Sleep(duration)\n        _, _ = w.Write([]byte(&quot;Hello World!&quot;))\n    })\n\n    if err := srv.ListenAndServe(); err != nil {\n        \/\/ Error starting or closing listener:\n        log.Fatalf(&quot;HTTP server ListenAndServe: %v&quot;, err)\n    }\n    log.Println(&quot;Stopped serving new connections&quot;)\n}<\/code><\/pre>\n<blockquote><p>NOTE:<br \/>\u6ce8\u610f\u793a\u4f8b\u7a0b\u5e8f\u4e2d\u5728\u5904\u7406 <code>srv.ListenAndServe()<\/code> \u8fd4\u56de\u7684\u9519\u8bef\u65f6\uff0c\u4f7f\u7528\u4e86 <code>log.Fatalf<\/code> \u6765\u6253\u5370\u65e5\u5fd7\u5e76\u9000\u51fa\u7a0b\u5e8f\uff0c\u8fd9\u4f1a\u8c03\u7528 <code>os.Exit(1)<\/code>\uff0c\u5982\u679c\u51fd\u6570\u4e2d\u6709 <code>defer<\/code> \u8bed\u53e5\u5219\u4e0d\u4f1a\u88ab\u6267\u884c\uff0c\u6240\u4ee5 <code>log.Fatalf<\/code> \u4ec5\u5efa\u8bae\u5728\u6d4b\u8bd5\u7a0b\u5e8f\u4e2d\u4f7f\u7528\uff0c\u751f\u4ea7\u73af\u5883\u4e2d\u8c28\u614e\u4f7f\u7528\u3002<\/p><\/blockquote>\n<p>\u793a\u4f8b\u4e2d\u7684 HTTP Server \u76d1\u542c\u4e86 <code>8000<\/code> \u7aef\u53e3\uff0c\u5e76\u63d0\u4f9b\u4e00\u4e2a <code>\/sleep<\/code> \u63a5\u53e3\uff0c\u6839\u636e\u7528\u6237\u4f20\u5165\u7684 <code>duration<\/code> \u53c2\u6570 sleep \u5bf9\u5e94\u7684\u65f6\u95f4\uff0c\u7136\u540e\u8fd4\u56de\u54cd\u5e94\u3002<\/p>\n<p>\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main<\/code><\/pre>\n<p>\u7136\u540e\u65b0\u6253\u5f00\u53e6\u5916\u4e00\u4e2a\u7ec8\u7aef\u8bbf\u95ee\u8fd9\u4e2a HTTP Server\uff1a<\/p>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=0s&quot;\nHello World!<\/code><\/pre>\n<p>\u4f20\u9012 <code>duration=0s<\/code> \u53c2\u6570\u8868\u793a\u4e0d\u8fdb\u884c sleep\uff0c\u6211\u4eec\u7acb\u5373\u5f97\u5230\u4e86\u6b63\u786e\u7684\u54cd\u5e94\u3002<\/p>\n<p>\u73b0\u5728\u6211\u4eec\u589e\u52a0\u4e00\u70b9 sleep \u65f6\u95f4\u8fdb\u884c\u8bf7\u6c42\uff1a<\/p>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;<\/code><\/pre>\n<p>\u5728 <code>5s<\/code> \u4ee5\u5185\u56de\u5230\u8fd0\u884c HTTP Server \u7684\u7ec8\u7aef\uff0c\u5e76\u7528 <code>Ctrl + C<\/code> \u7ec8\u6b62\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C<\/code><\/pre>\n<p>\u8fd9\u6b21\u6211\u4eec\u7684\u5ba2\u6237\u7aef\u8bf7\u6c42\u6ca1\u6709\u5f97\u5230\u6b63\u786e\u7684\u54cd\u5e94\uff1a<\/p>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;\ncurl: (52) Empty reply from server<\/code><\/pre>\n<p>\u8fd9\u5c31\u662f\u6ca1\u6709\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u6240\u5e26\u6765\u7684\u540e\u679c\u3002<\/p>\n<p>\u5f53\u4e00\u4e2a\u5ba2\u6237\u7aef\u8bf7\u6c42\u6b63\u5728\u8fdb\u884c\u4e2d\uff0c\u6b64\u65f6\u7ec8\u6b62 HTTP Server \u8fdb\u7a0b\uff0c\u8bf7\u6c42\u8fd8\u6ca1\u6709\u6765\u5f97\u53ca\u5b8c\u6210\uff0c\u8fde\u63a5\u5c31\u88ab\u65ad\u5f00\u4e86\uff0c\u5ba2\u6237\u7aef\u65e0\u6cd5\u5f97\u5230\u6b63\u786e\u7684\u54cd\u5e94\u7ed3\u679c\u3002<\/p>\n<p>\u4e3a\u4e86\u6539\u53d8\u8fd9\u4e00\u5c40\u9762\uff0c\u5c31\u9700\u8981\u8fdb\u884c\u4f18\u96c5\u9000\u51fa\u64cd\u4f5c\u3002<\/p>\n<h4>HTTP Server \u4f18\u96c5\u9000\u51fa<\/h4>\n<p>\u6211\u4eec\u6765\u5206\u6790\u4e0b\u4e00\u4e2a HTTP Server \u8981\u8fdb\u884c\u4f18\u96c5\u9000\u51fa\uff0c\u9700\u8981\u505a\u54ea\u4e9b\u4e8b\u60c5\uff1a<\/p>\n<ol>\n<li>\u9996\u5148\uff0c\u6211\u4eec\u8981\u5173\u95ed HTTP Server \u76d1\u542c\u7684\u7aef\u53e3\uff0c\u5373\u901a\u8fc7 <code>net.Listen<\/code> \u6240\u5f00\u542f\u7684 <code>Listener<\/code>\uff0c\u4ee5\u514d\u65b0\u7684\u8bf7\u6c42\u8fdb\u6765\u3002<\/li>\n<li>\u63a5\u7740\uff0c\u6211\u4eec\u8981\u5173\u95ed\u6240\u6709\u7a7a\u95f2\u7684 HTTP \u8fde\u63a5\u3002<\/li>\n<li>\u7136\u540e\uff0c\u6211\u4eec\u8fd8\u9700\u8981\u7b49\u5f85\u6240\u6709\u6b63\u5728\u5904\u7406\u8bf7\u6c42\u7684 HTTP \u8fde\u63a5\u53d8\u4e3a\u7a7a\u95f2\u72b6\u6001\u4e4b\u540e\u5173\u95ed\u5b83\u4eec\u3002\u8fd9\u91cc\u5e94\u8be5\u53ef\u4ee5\u8fdb\u884c\u65e0\u9650\u671f\u7b49\u5f85\uff0c\u4e5f\u53ef\u4ee5\u8bbe\u7f6e\u4e00\u4e2a\u8d85\u65f6\u65f6\u95f4\uff0c\u8d85\u8fc7\u4e00\u5b9a\u65f6\u95f4\u540e\u5f3a\u5236\u65ad\u5f00\u8fde\u63a5\uff0c\u4ee5\u514d\u7a0b\u5e8f\u6c38\u8fdc\u65e0\u6cd5\u9000\u51fa\u3002<\/li>\n<li>\u6700\u540e\uff0c\u6b63\u5e38\u9000\u51fa\u8fdb\u7a0b\u3002<\/li>\n<\/ol>\n<p>\u5e78\u8fd0\u7684\u662f\u9488\u5bf9\u4ee5\u4e0a HTTP Server \u7684\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\uff0c<code>net\/http<\/code> \u5305\u5df2\u7ecf\u5e2e\u6211\u4eec\u5b9e\u73b0\u597d\u4e86\u3002<\/p>\n<p>\u5728 Go 1.8 \u7248\u672c\u4e4b\u524d\uff0c\u6211\u4eec\u9700\u8981\u81ea\u5df1\u5b9e\u73b0\u4ee5\u4e0a\u6d41\u7a0b\uff0c\u6216\u8005\u6709\u4e00\u4e9b\u6d41\u884c\u7684\u7b2c\u4e09\u65b9\u5305\u4e5f\u80fd\u5e2e\u6211\u4eec\u505a\u5230\u3002\u800c\u4ece Go 1.8 \u7248\u672c\u5f00\u59cb\uff0c<code>net\/http<\/code> \u5305\u81ea\u8eab\u4e3a\u6211\u4eec\u63d0\u4f9b\u4e86 <code>http.Server.Shutdown<\/code> \u65b9\u6cd5\u53ef\u4ee5\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u7684\u5b8c\u6574\u6d41\u7a0b\u3002<\/p>\n<p>\u53ef\u4ee5\u5728 Go \u4ed3\u5e93 <a href=\"https:\/\/link.segmentfault.com\/?enc=SgmREN03pmIxj9U3cnv8nQ%3D%3D.5%2B3tjJvvd6GrmQJxSAG2K1J724Flvcps34sY8gJ9TU%2BlFK%2Fgs8xIKbdpsZwPvWB8\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">issues\/4674<\/a> \u4e2d\u770b\u5230\u5bf9 <code>net\/http<\/code> \u5305\u52a0\u5165\u4f18\u96c5\u9000\u51fa\u529f\u80fd\u7684\u8ba8\u8bba\uff0c\u8fd9\u4e2a\u95ee\u9898\u6700\u65e9\u5728 2013 \u5e74\u5c31\u88ab\u63d0\u51fa\u4e86\uff0c\u4e0d\u8fc7\u5374\u4ece Go 1.1 \u7248\u672c\u62d6\u5230\u4e86 Go 1.8 \u7248\u672c\u624d\u5f97\u4ee5\u652f\u6301\u3002<\/p>\n<p>\u6211\u4eec\u53ef\u4ee5\u5728 <a href=\"https:\/\/link.segmentfault.com\/?enc=CHX53H60FCmAGizKKzCTmw%3D%3D.BofOBneC5tGp0aZscP6kvhea6hr8zMC6THcQui5nvx276%2BKeniNrvqoy%2B0%2F4kDvrBxusfuPbIl96ZZbBEc8B1Q%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">net\/http \u6587\u6863<\/a> \u4e2d\u627e\u5230\u5173\u4e8e <code>http.Server.Shutdown<\/code> \u65b9\u6cd5\u7684\u8bf4\u660e\uff1a<\/p>\n<blockquote><p>Shutdown gracefully shuts down the server without interrupting any active connections. Shutdown works by first closing all open listeners, then closing all idle connections, and then waiting indefinitely for connections to return to idle and then shut down. If the provided context expires before the shutdown is complete, Shutdown returns the context&#8217;s error, otherwise it returns any error returned from closing the Server&#8217;s underlying Listener(s).<\/p><\/blockquote>\n<p>\u8bd1\u6587\u5982\u4e0b\uff1a<\/p>\n<blockquote><p>Shutdown \u4f1a\u4f18\u96c5\u5730\u5173\u95ed\u670d\u52a1\u5668\uff0c\u800c\u4e0d\u4f1a\u4e2d\u65ad\u4efb\u4f55\u6d3b\u52a8\u7684\u8fde\u63a5\u3002\u5b83\u7684\u5de5\u4f5c\u539f\u7406\u662f\u5148\u5173\u95ed\u6240\u6709\u5df2\u6253\u5f00\u7684\u76d1\u542c\u5668\uff08listeners\uff09\uff0c\u7136\u540e\u5173\u95ed\u6240\u6709\u7a7a\u95f2\u7684\u8fde\u63a5\uff0c\u5e76\u65e0\u9650\u671f\u5730\u7b49\u5f85\u6240\u6709\u8fde\u63a5\u53d8\u4e3a\u7a7a\u95f2\u72b6\u6001\u540e\u518d\u5173\u95ed\u670d\u52a1\u5668\u3002\u5982\u679c\u5728\u5173\u95ed\u5b8c\u6210\u4e4b\u524d\uff0c\u4f20\u5165\u7684\u4e0a\u4e0b\u6587\uff08context\uff09\u8fc7\u671f\uff0cShutdown \u4f1a\u8fd4\u56de\u4e0a\u4e0b\u6587\u7684\u9519\u8bef\uff0c\u5426\u5219\u5b83\u5c06\u8fd4\u56de\u5173\u95ed\u670d\u52a1\u5668\u5e95\u5c42\u76d1\u542c\u5668\u65f6\u6240\u4ea7\u751f\u7684\u4efb\u4f55\u9519\u8bef\u3002<\/p><\/blockquote>\n<p>\u73b0\u5728\uff0c\u7ed3\u5408\u524d\u6587\u4ecb\u7ecd\u7684 <code>os\/signal<\/code> \u5305\u4ee5\u53ca <code>net\/http<\/code> \u5305\u63d0\u4f9b\u7684 <code>http.Server.Shutdown<\/code> \u65b9\u6cd5\uff0c\u6211\u4eec\u53ef\u4ee5\u5199\u51fa\u5982\u4e0b\u4f18\u96c5\u9000\u51fa HTTP Server \u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;context&quot;\n    &quot;errors&quot;\n    &quot;log&quot;\n    &quot;net\/http&quot;\n    &quot;os&quot;\n    &quot;os\/signal&quot;\n    &quot;syscall&quot;\n    &quot;time&quot;\n)\n\nfunc main() {\n    srv := &amp;http.Server{\n        Addr: &quot;:8000&quot;,\n    }\n\n    http.HandleFunc(&quot;\/sleep&quot;, func(w http.ResponseWriter, r *http.Request) {\n        duration, err := time.ParseDuration(r.FormValue(&quot;duration&quot;))\n        if err != nil {\n            http.Error(w, err.Error(), 400)\n            return\n        }\n        time.Sleep(duration)\n        _, _ = w.Write([]byte(&quot;Hello World!&quot;))\n    })\n\n    go func() {\n        quit := make(chan os.Signal, 1)\n        signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)\n        &lt;-quit\n        log.Println(&quot;Shutdown Server...&quot;)\n\n        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n        defer cancel()\n\n        \/\/ We received an SIGINT\/SIGTERM\/SIGQUIT signal, shut down.\n        if err := srv.Shutdown(ctx); err != nil {\n            \/\/ Error from closing listeners, or context timeout:\n            log.Printf(&quot;HTTP server Shutdown: %v&quot;, err)\n        }\n        log.Println(&quot;HTTP server graceful shutdown completed&quot;)\n    }()\n\n    if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {\n        \/\/ Error starting or closing listener:\n        log.Fatalf(&quot;HTTP server ListenAndServe: %v&quot;, err)\n    }\n    log.Println(&quot;Stopped serving new connections&quot;)\n}<\/code><\/pre>\n<p>\u793a\u4f8b\u4e2d\uff0c\u4e3a\u4e86\u8ba9\u4e3b goroutine \u4e0d\u88ab\u963b\u585e\uff0c\u6211\u4eec\u5f00\u542f\u4e86\u4e00\u4e2a\u65b0\u7684 goroutine \u6765\u652f\u6301\u4f18\u96c5\u9000\u51fa\u3002<\/p>\n<p><code>quit<\/code> \u7528\u6765\u63a5\u6536\u5173\u6ce8\u7684\u4fe1\u53f7\uff0c\u6211\u4eec\u5173\u6ce8\u4e86 <code>SIGINT<\/code>\u3001<code>SIGTERM<\/code>\u3001<code>SIGQUIT<\/code> \u8fd9 3 \u4e2a\u7a0b\u5e8f\u9000\u51fa\u4fe1\u53f7\u3002<\/p>\n<p><code>&lt;-quit<\/code> \u6536\u5230\u9000\u51fa\u4fe1\u53f7\u4ee5\u540e\uff0c\u7a0b\u5e8f\u5c06\u8fdb\u5165\u4f18\u96c5\u9000\u51fa\u73af\u8282\u3002<\/p>\n<p>\u8c03\u7528 <code>srv.Shutdown(ctx)<\/code> \u8fdb\u884c\u4f18\u96c5\u9000\u51fa\u65f6\uff0c\u4f20\u9012\u4e86\u4e00\u4e2a <code>10s<\/code> \u8d85\u65f6\u7684 <code>Context<\/code>\uff0c\u8fd9\u662f\u4e3a\u4e86\u8d85\u8fc7\u4e00\u5b9a\u65f6\u95f4\u540e\u5f3a\u5236\u9000\u51fa\uff0c\u4ee5\u514d\u7a0b\u5e8f\u65e0\u9650\u671f\u7b49\u5f85\u4e0b\u53bb\uff0c\u6c38\u8fdc\u65e0\u6cd5\u9000\u51fa\u3002<\/p>\n<p>\u5e76\u4e14\u6211\u4eec\u4fee\u6539\u4e86\u8c03\u7528 <code>srv.ListenAndServe()<\/code> \u65f6\u7684\u9519\u8bef\u5904\u7406\u5224\u65ad\u4ee3\u7801\uff0c\u56e0\u4e3a\u8c03\u7528 <code>srv.Shutdown(ctx)<\/code> \u540e\uff0c<code>srv.ListenAndServe()<\/code> <strong>\u7acb\u5373\u8fd4\u56de <code>http.ErrServerClosed<\/code> \u9519\u8bef<\/strong>\uff0c\u8fd9\u662f\u7b26\u5408\u9884\u671f\u7684\u9519\u8bef\uff0c\u6240\u4ee5\u6211\u4eec\u5c06\u5176\u6392\u9664\u5728\u9519\u8bef\u5904\u7406\u6d41\u7a0b\u4e4b\u5916\u3002<\/p>\n<p>\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main<\/code><\/pre>\n<p>\u6253\u5f00\u65b0\u7684\u7ec8\u7aef\uff0c\u8bbf\u95ee HTTP Server\uff1a<\/p>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;<\/code><\/pre>\n<p><code>5s<\/code> \u4ee5\u5185\u56de\u5230 HTTP Server \u542f\u52a8\u7ec8\u7aef\uff0c\u6309\u4e0b <code>Ctrl + C<\/code>\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C2024\/08\/22 09:15:20 Shutdown Server...\n2024\/08\/22 09:15:20 Stopped serving new connections<\/code><\/pre>\n<p>\u53ef\u4ee5\u53d1\u73b0\u7a0b\u5e8f\u8fdb\u5165\u4e86\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b <code>Shutdown Server...<\/code>\uff0c\u5e76\u6700\u7ec8\u6253\u5370 <code>Stopped serving new connections<\/code> \u65e5\u5fd7\u540e\u9000\u51fa\u3002<\/p>\n<p>\u9057\u61be\u7684\u662f\uff0c\u5ba2\u6237\u7aef\u8bf7\u6c42\u5e76\u6ca1\u6709\u63a5\u6536\u5230\u6210\u529f\u7684\u54cd\u5e94\u4fe1\u606f\uff1a<\/p>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;\ncurl: (52) Empty reply from server<\/code><\/pre>\n<p>\u770b\u6765\uff0c\u6211\u4eec\u7684\u4f18\u96c5\u9000\u51fa\u5b9e\u73b0\u5e76\u6ca1\u6709\u751f\u6548\u3002<\/p>\n<p>\u5176\u5b9e\u4ed4\u7ec6\u89c2\u5bdf\u4f60\u4f1a\u53d1\u73b0\uff0c\u6211\u4eec\u7684\u7a0b\u5e8f\u5c11\u6253\u5370\u4e86\u4e00\u884c <code>HTTP server graceful shutdown completed<\/code> \u65e5\u5fd7\uff0c\u8bf4\u660e\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u7684 goroutine \u5e76\u6ca1\u6709\u6267\u884c\u5b8c\u6210\u3002<\/p>\n<p>\u6839\u636e <a href=\"https:\/\/link.segmentfault.com\/?enc=tZ8A%2FYQhdL4ETWj7s%2B54Rg%3D%3D.dbCoOXLJZp%2Bl%2FwGpGnDLqOS44zYe0UuxxBrA36r9BqSumVu4r6Grkn4c5jKK1aNFWAqVNp7q4Uwd2QTJYiMbRw%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">net\/http \u6587\u6863<\/a> \u7684\u63cf\u8ff0\uff1a<\/p>\n<blockquote><p>When Shutdown is called, Serve, ListenAndServe, and ListenAndServeTLS immediately return ErrServerClosed. Make sure the program doesn&#8217;t exit and waits instead for Shutdown to return.<\/p><\/blockquote>\n<p>\u5373\u5f53\u8c03\u7528 <code>srv.Shutdown(ctx)<\/code> \u8fdb\u5165\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u540e\uff0c<code>Serve<\/code>\u3001<code>ListenAndServe<\/code> \u548c <code>ListenAndServeTLS<\/code> \u8fd9\u4e09\u4e2a\u65b9\u6cd5\u4f1a<strong>\u7acb\u5373\u8fd4\u56de <code>ErrServerClosed<\/code> \u9519\u8bef\uff0c\u6211\u4eec\u8981\u786e\u4fdd\u7a0b\u5e8f\u6ca1\u6709\u9000\u51fa\uff0c\u800c\u662f\u7b49\u5f85 <code>Shutdown<\/code> \u65b9\u6cd5\u6267\u884c\u5b8c\u6210\u5e76\u8fd4\u56de<\/strong>\u3002<\/p>\n<p>\u56e0\u4e3a\u8fdb\u5165\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u540e\uff0c<code>srv.ListenAndServe()<\/code> \u4f1a\u7acb\u5373\u8fd4\u56de\uff0c\u4e3b goroutine \u4f1a\u9a6c\u4e0a\u6267\u884c\u6700\u540e\u4e00\u884c\u4ee3\u7801 <code>log.Println(&quot;Stopped serving new connections&quot;)<\/code> \u5e76\u9000\u51fa\u3002<\/p>\n<p>\u6b64\u65f6\u5b50 goroutine \u4e2d\u8fd0\u884c\u7684\u4f18\u96c5\u9000\u51fa\u903b\u8f91 <code>srv.Shutdown(ctx)<\/code> \u8fd8\u6ca1\u6765\u5f97\u53ca\u5904\u7406\u5b8c\u6210\uff0c\u5c31\u8ddf\u968f\u4e3b goroutine \u4e00\u540c\u9000\u51fa\u4e86\u3002<\/p>\n<p>\u8fd9\u662f\u4e00\u4e2a\u6709\u5751\u7684\u5b9e\u73b0\u3002<\/p>\n<p><strong>\u6211\u4eec\u5fc5\u987b\u4fdd\u8bc1\u7a0b\u5e8f\u4e3b goroutine \u7b49\u5f85 <code>Shutdown<\/code> \u65b9\u6cd5\u6267\u884c\u5b8c\u6210\u5e76\u8fd4\u56de\u540e\u624d\u9000\u51fa\u3002<\/strong><\/p>\n<p>\u4fee\u6539\u540e\u7684\u4ee3\u7801\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;context&quot;\n    &quot;errors&quot;\n    &quot;log&quot;\n    &quot;net\/http&quot;\n    &quot;os&quot;\n    &quot;os\/signal&quot;\n    &quot;syscall&quot;\n    &quot;time&quot;\n)\n\nfunc main() {\n    srv := &amp;http.Server{\n        Addr: &quot;:8000&quot;,\n    }\n\n    http.HandleFunc(&quot;\/sleep&quot;, func(w http.ResponseWriter, r *http.Request) {\n        duration, err := time.ParseDuration(r.FormValue(&quot;duration&quot;))\n        if err != nil {\n            http.Error(w, err.Error(), 400)\n            return\n        }\n\n        time.Sleep(duration)\n        _, _ = w.Write([]byte(&quot;Welcome HTTP Server&quot;))\n    })\n\n    go func() {\n        if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {\n            \/\/ Error starting or closing listener:\n            log.Fatalf(&quot;HTTP server ListenAndServe: %v&quot;, err)\n        }\n        log.Println(&quot;Stopped serving new connections&quot;)\n    }()\n\n    \/\/ \u53ef\u4ee5\u6ce8\u518c\u4e00\u4e9b hook \u51fd\u6570\uff0c\u6bd4\u5982\u4ece\u6ce8\u518c\u4e2d\u5fc3\u4e0b\u7ebf\u903b\u8f91\n    srv.RegisterOnShutdown(func() {\n        log.Println(&quot;Register Shutdown 1&quot;)\n    })\n    srv.RegisterOnShutdown(func() {\n        log.Println(&quot;Register Shutdown 2&quot;)\n    })\n\n    quit := make(chan os.Signal, 1)\n    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)\n    &lt;-quit\n    log.Println(&quot;Shutdown Server...&quot;)\n\n    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n    defer cancel()\n\n    \/\/ We received an SIGINT\/SIGTERM\/SIGQUIT signal, shut down.\n    if err := srv.Shutdown(ctx); err != nil {\n        \/\/ Error from closing listeners, or context timeout:\n        log.Printf(&quot;HTTP server Shutdown: %v&quot;, err)\n    }\n    log.Println(&quot;HTTP server graceful shutdown completed&quot;)\n}<\/code><\/pre>\n<p>\u8fd9\u4e00\u6b21\u6211\u4eec\u5c06\u4f18\u96c5\u9000\u51fa\u903b\u8f91 <code>srv.Shutdown(ctx)<\/code> \u548c\u670d\u52a1\u76d1\u542c\u903b\u8f91 <code>srv.ListenAndServe()<\/code> \u4ee3\u7801\u4f4d\u7f6e\u8fdb\u884c\u4e86\u4e92\u6362\u3002<\/p>\n<p>\u5c06 <code>srv.Shutdown(ctx)<\/code> \u653e\u5728\u4e3b goroutine \u4e2d\uff0c\u800c <code>srv.ListenAndServe()<\/code> \u653e\u5728\u4e86\u5b50 goroutine \u4e2d\u3002\u8fd9\u6837\u7684\u76ee\u7684\u663e\u800c\u6613\u89c1\uff0c\u4e3b goroutine \u53ea\u6709\u7b49\u5f85 <code>srv.Shutdown(ctx)<\/code> \u6267\u884c\u5b8c\u6210\u624d\u4f1a\u9000\u51fa\uff0c\u6240\u4ee5\u4e5f\u5c31\u4fdd\u8bc1\u4e86\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u80fd\u8fc7\u6267\u884c\u5b8c\u6210\u3002<\/p>\n<p>\u6b64\u5916\uff0c\u6211\u8fd8\u987a\u4fbf\u4f7f\u7528 <code>srv.RegisterOnShutdown()<\/code> \u6ce8\u518c\u4e86\u4e24\u4e2a\u51fd\u6570\u5230\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u4e2d\uff0c<code>srv.Shutdown(ctx)<\/code> \u5185\u90e8\u4f1a\u6267\u884c\u8fd9\u91cc\u6ce8\u518c\u7684\u51fd\u6570\u3002\u6240\u4ee5\u8fd9\u91cc\u53ef\u4ee5\u6ce8\u518c\u4e00\u4e9b\u5e26\u6709\u6e05\u7406\u529f\u80fd\u7684\u51fd\u6570\uff0c\u6bd4\u5982\u4ece\u6ce8\u518c\u4e2d\u5fc3\u4e0b\u7ebf\u903b\u8f91\u7b49\u3002<\/p>\n<p>\u73b0\u5728\u518d\u6b21\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C2024\/08\/22 09:16:21 Shutdown Server...\n2024\/08\/22 09:16:21 Stopped serving new connections\n2024\/08\/22 09:16:21 Register Shutdown 1\n2024\/08\/22 09:16:21 Register Shutdown 2\n2024\/08\/22 09:16:24 HTTP server graceful shutdown completed<\/code><\/pre>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;\nWelcome HTTP Server<\/code><\/pre>\n<p>\u6839\u636e\u8fd9\u4e24\u6bb5\u65e5\u5fd7\u6765\u770b\uff0c\u8fd9\u4e00\u6b21\u7684\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u4e00\u5207\u6b63\u5e38\u3002<\/p>\n<p>\u5e76\u4e14\u53ef\u4ee5\u89c2\u5bdf\u5230\uff0c\u6211\u4eec\u4f7f\u7528 <code>srv.RegisterOnShutdown<\/code> \u6ce8\u518c\u7684 2 \u4e2a hook \u51fd\u6570\u662f\u6309\u7167\u6ce8\u518c\u987a\u5e8f\u4f9d\u6b21\u6267\u884c\u7684\u3002<\/p>\n<p>\u6211\u4eec\u8fd8\u53ef\u4ee5\u6d4b\u8bd5\u4e0b\u8d85\u65f6\u9000\u51fa\u7684\u903b\u8f91\uff0c\u5148\u542f\u52a8 HTTP Server\uff0c\u7136\u540e\u4f7f\u7528 <code>curl<\/code> \u547d\u4ee4\u8bf7\u6c42\u670d\u52a1\u65f6\u8bbe\u7f6e\u4e00\u4e2a <code>20s<\/code> \u8d85\u65f6\uff0c\u8fd9\u6837\u5728\u901a\u8fc7 <code>Ctrl + C<\/code> \u8fdb\u884c\u4f18\u96c5\u9000\u51fa\u64cd\u4f5c\u65f6\uff0c<code>srv.Shutdown(ctx)<\/code> \u5c31\u4f1a\u56e0\u4e3a\u7b49\u5f85\u8d85\u8fc7 <code>10s<\/code> \u6ca1\u6709\u5904\u7406\u5b8c\u8bf7\u6c42\u800c\u5f3a\u5236\u9000\u51fa\u3002<\/p>\n<p>\u6267\u884c\u65e5\u5fd7\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C2024\/08\/22 09:17:09 Shutdown Server...\n2024\/08\/22 09:17:09 Stopped serving new connections\n2024\/08\/22 09:17:09 Register Shutdown 1\n2024\/08\/22 09:17:09 Register Shutdown 2\n2024\/08\/22 09:17:19 HTTP server Shutdown: context deadline exceeded\n2024\/08\/22 09:17:19 HTTP server graceful shutdown completed<\/code><\/pre>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=20s&quot;\ncurl: (52) Empty reply from server<\/code><\/pre>\n<p>\u65e5\u5fd7\u8f93\u51fa\u7ed3\u679c\u7b26\u5408\u9884\u671f\u3002<\/p>\n<blockquote><p>NOTE:<br \/>\u6211\u4eec\u5f53\u524d\u793a\u4f8b\u7684\u5b9e\u73b0\u65b9\u5f0f\u6709\u4e9b\u60c5\u51b5\u4e0b\u5b58\u5728\u4e00\u4e2a\u5c0f\u95ee\u9898\uff0c\u5f53 <code>srv.ListenAndServe()<\/code> \u8fd4\u56de\u540e\uff0c\u5982\u679c\u5b50 goroutine \u51fa\u73b0\u4e86 <code>panic<\/code>\uff0c\u7531\u4e8e\u6211\u4eec\u6ca1\u6709\u4f7f\u7528 <code>recover<\/code> \u8bed\u53e5\u6355\u83b7 <code>panic<\/code>\uff0c\u5219 <code>main<\/code> \u51fd\u6570\u4e2d\u7684 <code>defer<\/code> \u8bed\u53e5\u4e0d\u4f1a\u6267\u884c\uff0c\u8fd9\u4e00\u70b9\u5728\u751f\u4ea7\u73af\u5883\u4e0b\u4f60\u8981\u5c0f\u5fc3\u3002<br \/>\u5176\u5b9e <code>net\/http<\/code> \u5305\u63d0\u4f9b\u4e86\u53e6\u4e00\u79cd\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u7684 <a href=\"https:\/\/link.segmentfault.com\/?enc=PR6nIOOUa%2BC4rAH0dNuMNQ%3D%3D.%2BAxAPOeeiJLXmYmB47m1583d6X7OuVKQ%2FTVre%2BTfnUkZbcD7ssLnfN2%2FO%2F21VXjGKhiu9wT1uoD4MlzE1mw%2BOA%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">\u793a\u4f8b\u4ee3\u7801<\/a>\uff0c\u793a\u4f8b\u4e2d\u8fd8\u662f\u5c06 <code>srv.ListenAndServe()<\/code> \u653e\u5728\u4e3b goroutine \u4e2d\uff0c<code>srv.Shutdown(ctx)<\/code> \u653e\u5728\u5b50 goroutine \u4e2d\uff0c\u5229\u7528\u4e00\u4e2a\u65b0\u7684 <code>channel<\/code> \u963b\u585e\u4e3b goroutine \u7684\u65b9\u5f0f\u6765\u5b9e\u73b0\u3002\u611f\u5174\u8da3\u7684\u8bfb\u8005\u53ef\u4ee5\u70b9\u51fb\u8fdb\u53bb\u5b66\u4e60\u3002<\/p><\/blockquote>\n<h4>HTTP Handler \u4e2d\u6709 goroutine \u7684\u60c5\u51b5<\/h4>\n<p>\u6211\u4eec\u5728 HTTP Handler \u51fd\u6570\u4e2d\u52a0\u4e0a\u4e00\u6bb5\u5f02\u6b65\u4ee3\u7801\uff0c\u4fee\u6539\u540e\u7684\u7a0b\u5e8f\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">http.HandleFunc(&quot;\/sleep&quot;, func(w http.ResponseWriter, r *http.Request) {\n    duration, err := time.ParseDuration(r.FormValue(&quot;duration&quot;))\n    if err != nil {\n        http.Error(w, err.Error(), 400)\n        return\n    }\n\n    time.Sleep(duration)\n\n    \/\/ \u6a21\u62df\u9700\u8981\u5f02\u6b65\u6267\u884c\u7684\u4ee3\u7801\uff0c\u6bd4\u5982\u6ce8\u518c\u63a5\u53e3\u5f02\u6b65\u53d1\u9001\u90ae\u4ef6\u3001\u53d1\u9001 Kafka \u6d88\u606f\u7b49\n    go func() {\n        log.Println(&quot;Goroutine enter&quot;)\n        time.Sleep(time.Second * 5)\n        log.Println(&quot;Goroutine exit&quot;)\n    }()\n\n    _, _ = w.Write([]byte(&quot;Welcome HTTP Server&quot;))\n})<\/code><\/pre>\n<p>\u8fd9\u91cc\u65b0\u542f\u52a8\u4e86\u4e00\u4e2a goroutine \u6a21\u62df\u9700\u8981\u5f02\u6b65\u6267\u884c\u7684\u4ee3\u7801\uff0c\u6bd4\u5982\u6ce8\u518c\u63a5\u53e3\u5f02\u6b65\u53d1\u9001\u90ae\u4ef6\u3001\u53d1\u9001 Kafka \u6d88\u606f\u7b49\u3002\u8fd9\u5728\u5b9e\u9645\u5de5\u4f5c\u4e2d\u975e\u5e38\u5e38\u89c1\u3002<\/p>\n<p>\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff0c\u518d\u6b21\u6d4b\u8bd5\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C2024\/08\/22 09:18:53 Shutdown Server...\n2024\/08\/22 09:18:53 Stopped serving new connections\n2024\/08\/22 09:18:53 Register Shutdown 1\n2024\/08\/22 09:18:53 Register Shutdown 2\n2024\/08\/22 09:18:56 Goroutine enter\n2024\/08\/22 09:18:56 HTTP server graceful shutdown completed<\/code><\/pre>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot; \nWelcome HTTP Server<\/code><\/pre>\n<p>\u5ba2\u6237\u7aef\u8bf7\u6c42\u88ab\u6b63\u786e\u5904\u7406\u4e86\uff0c\u4f46\u662f\u6839\u636e\u65e5\u5fd7\u8f93\u51fa\u53ef\u4ee5\u53d1\u73b0\uff0c\u5904\u7406\u51fd\u6570 Handler \u4e2d\u7684\u5b50 goroutine \u5e76\u6ca1\u6709\u6b63\u5e38\u6267\u884c\u5b8c\u6210\u5c31\u9000\u51fa\u4e86\uff0c<code>Goroutine enter<\/code> \u65e5\u5fd7\u6709\u88ab\u6253\u5370\uff0c<code>Goroutine exit<\/code> \u65e5\u5fd7\u5e76\u6ca1\u6709\u88ab\u6253\u5370\u3002<\/p>\n<p>\u51fa\u73b0\u8fd9\u79cd\u60c5\u51b5\u7684\u539f\u56e0\uff0c\u540c\u6837\u662f\u56e0\u4e3a\u4e3b goroutine \u5df2\u7ecf\u9000\u51fa\uff0c\u5b50 goroutine \u8fd8\u6ca1\u6765\u5f97\u53ca\u5904\u7406\u5b8c\u6210\uff0c\u5c31\u8ddf\u968f\u4e3b goroutine \u4e00\u540c\u9000\u51fa\u4e86\u3002<\/p>\n<p>\u8fd9\u4f1a\u5bfc\u81f4\u6570\u636e\u4e0d\u4e00\u81f4\u95ee\u9898\u3002<\/p>\n<p>\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e00\u95ee\u9898\uff0c\u6211\u4eec\u5bf9\u793a\u4f8b\u7a0b\u5e8f\u505a\u5982\u4e0b\u4fee\u6539\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;context&quot;\n    &quot;errors&quot;\n    &quot;log&quot;\n    &quot;net\/http&quot;\n    &quot;os&quot;\n    &quot;os\/signal&quot;\n    &quot;sync&quot;\n    &quot;syscall&quot;\n    &quot;time&quot;\n)\n\ntype Service struct {\n    wg sync.WaitGroup\n}\n\nfunc (s *Service) FakeSendEmail() {\n    s.wg.Add(1)\n\n    go func() {\n        defer s.wg.Done()\n        defer func() {\n            if err := recover(); err != nil {\n                log.Printf(&quot;Recovered panic: %v\/n&quot;, err)\n            }\n        }()\n\n        log.Println(&quot;Goroutine enter&quot;)\n        time.Sleep(time.Second * 5)\n        log.Println(&quot;Goroutine exit&quot;)\n    }()\n}\n\nfunc (s *Service) GracefulStop(ctx context.Context) {\n    log.Println(&quot;Waiting for service to finish&quot;)\n    quit := make(chan struct{})\n    go func() {\n        s.wg.Wait()\n        close(quit)\n    }()\n    select {\n    case &lt;-ctx.Done():\n        log.Println(&quot;context was marked as done earlier, than user service has stopped&quot;)\n    case &lt;-quit:\n        log.Println(&quot;Service finished&quot;)\n    }\n}\n\nfunc (s *Service) Handler(w http.ResponseWriter, r *http.Request) {\n    duration, err := time.ParseDuration(r.FormValue(&quot;duration&quot;))\n    if err != nil {\n        http.Error(w, err.Error(), 400)\n        return\n    }\n\n    time.Sleep(duration)\n\n    \/\/ \u6a21\u62df\u9700\u8981\u5f02\u6b65\u6267\u884c\u7684\u4ee3\u7801\uff0c\u6bd4\u5982\u6ce8\u518c\u63a5\u53e3\u5f02\u6b65\u53d1\u9001\u90ae\u4ef6\u3001\u53d1\u9001 Kafka \u6d88\u606f\u7b49\n    s.FakeSendEmail()\n\n    _, _ = w.Write([]byte(&quot;Welcome HTTP Server&quot;))\n}\n\nfunc main() {\n    srv := &amp;http.Server{\n        Addr: &quot;:8000&quot;,\n    }\n\n    svc := &amp;Service{}\n    http.HandleFunc(&quot;\/sleep&quot;, svc.Handler)\n\n    go func() {\n        if err := srv.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {\n            \/\/ Error starting or closing listener:\n            log.Fatalf(&quot;HTTP server ListenAndServe: %v&quot;, err)\n        }\n        log.Println(&quot;Stopped serving new connections&quot;)\n    }()\n\n    \/\/ \u9519\u8bef\u5199\u6cd5\n    \/\/ srv.RegisterOnShutdown(func() {\n    \/\/     svc.GracefulStop(ctx)\n    \/\/ })\n\n    quit := make(chan os.Signal, 1)\n    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)\n    &lt;-quit\n    log.Println(&quot;Shutdown Server...&quot;)\n\n    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n    defer cancel()\n\n    \/\/ We received an SIGINT\/SIGTERM\/SIGQUIT signal, shut down.\n    if err := srv.Shutdown(ctx); err != nil {\n        \/\/ Error from closing listeners, or context timeout:\n        log.Printf(&quot;HTTP server Shutdown: %v&quot;, err)\n    }\n\n    \/\/ \u4f18\u96c5\u9000\u51fa service\n    svc.GracefulStop(ctx)\n    log.Println(&quot;HTTP server graceful shutdown completed&quot;)\n}<\/code><\/pre>\n<p>\u8fd9\u91cc\u5bf9\u793a\u4f8b\u7a0b\u5e8f\u8fdb\u884c\u4e86\u91cd\u6784\uff0c\u5b9a\u4e49\u4e00\u4e2a <code>Service<\/code> \u7ed3\u6784\u4f53\u7528\u6765\u627f\u8f7d\u4e1a\u52a1\u903b\u8f91\uff0c\u5b83\u5305\u542b\u4e00\u4e2a <code>sync.WaitGroup<\/code> \u5bf9\u8c61\u7528\u6765\u63a7\u5236\u5f02\u6b65\u7a0b\u5e8f\u6267\u884c\u3002<\/p>\n<p>Handler \u4e2d\u7684\u5f02\u6b65\u4ee3\u7801\u88ab\u79fb\u5230\u4e86 <code>s.FakeSendEmail()<\/code> \u65b9\u6cd5\u4e2d\uff0c<code>s.FakeSendEmail()<\/code> \u65b9\u6cd5\u5185\u90e8\u4f1a\u542f\u52a8\u4e00\u4e2a\u65b0\u7684 goroutine \u6a21\u62df\u5f02\u6b65\u53d1\u9001\u90ae\u4ef6\u3002<\/p>\n<p>\u5e76\u4e14\u6211\u4eec\u8fd8\u4e3a <code>Service<\/code> \u63d0\u4f9b\u4e86\u4e00\u4e2a\u4f18\u96c5\u9000\u51fa\u65b9\u6cd5 <code>GracefulStop<\/code>\uff0c<code>GracefulStop<\/code> \u4f7f\u7528 <code>s.wg.Wait()<\/code> \u65b9\u5f0f\uff0c\u6765\u7b49\u5f85\u5b83\u5173\u8054\u7684\u6240\u6709\u5df2\u5f00\u542f\u7684 goroutine \u6267\u884c\u5b8c\u6210\u518d\u9000\u51fa\u3002<\/p>\n<p>\u5728 <code>main<\/code> \u51fd\u6570\u4e2d\uff0c\u6267\u884c <code>srv.Shutdown(ctx)<\/code> \u5b8c\u6210\u540e\uff0c\u518d\u8c03\u7528 <code>svc.GracefulStop(ctx)<\/code> \u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u3002<\/p>\n<p>\u73b0\u5728\uff0c\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\uff0c\u518d\u6b21\u6d4b\u8bd5\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n^C2024\/08\/22 09:20:03 Shutdown Server...\n2024\/08\/22 09:20:03 Stopped serving new connections\n2024\/08\/22 09:20:06 Goroutine enter\n2024\/08\/22 09:20:06 Waiting for service to finish\n2024\/08\/22 09:20:11 Goroutine exit\n2024\/08\/22 09:20:11 Service finished\n2024\/08\/22 09:20:11 HTTP server graceful shutdown completed<\/code><\/pre>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;\nWelcome HTTP Server<\/code><\/pre>\n<p>\u8fd9\u4e00\u6b21 <code>Goroutine exit<\/code> \u65e5\u5fd7\u88ab\u6b63\u786e\u6253\u5370\u51fa\u6765\uff0c\u8bf4\u660e\u4f18\u96c5\u9000\u51fa\u751f\u6548\u4e86\u3002<\/p>\n<p>\u8fd9\u4e5f\u63d0\u9192\u6211\u4eec\uff0c\u5728\u5f00\u53d1\u8fc7\u7a0b\u4e2d\uff0c\u4e0d\u8981\u968f\u610f\u521b\u5efa\u4e00\u4e2a\u4e0d\u77e5\u9053\u4f55\u65f6\u9000\u51fa\u7684 goroutine\uff0c\u6211\u4eec\u8981\u4e3b\u52a8\u5173\u6ce8 goroutine \u7684\u751f\u547d\u5468\u671f\uff0c\u4ee5\u514d\u7a0b\u5e8f\u5931\u63a7\u3002<\/p>\n<p>\u7ec6\u5fc3\u7684\u8bfb\u8005\u5e94\u8be5\u5df2\u7ecf\u53d1\u73b0\uff0c\u6211\u5728\u793a\u4f8b\u7a0b\u5e8f\u4e2d\u6ce8\u91ca\u4e86\u4e00\u6bb5\u9519\u8bef\u5199\u6cd5\u7684\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"go\">\/\/ \u9519\u8bef\u5199\u6cd5\n\/\/ srv.RegisterOnShutdown(func() {\n\/\/     svc.GracefulStop(ctx)\n\/\/ })<\/code><\/pre>\n<p>\u5bf9\u4e8e <code>Service<\/code> \u4f18\u96c5\u9000\u51fa\u5b50 goroutine \u7684\u573a\u666f\u7684\u786e\u4e0d\u9002\u7528\u4e8e\u5c06\u5176\u6ce8\u518c\u5230 <code>srv.RegisterOnShutdown<\/code> \u4e2d\u3002<\/p>\n<p>\u8fd9\u662f\u56e0\u4e3a <code>svc.Handler<\/code> \u4e2d\u7684\u4ee3\u7801\u6267\u884c\u5230 <code>time.Sleep(duration)<\/code> \u65f6\uff0c\u7a0b\u5e8f\u8fd8\u6ca1\u5f00\u59cb\u6267\u884c <code>svc.FakeSendEmail()<\/code>\uff0c\u8fd9\u65f6\u5982\u679c\u6211\u4eec\u6309 <code>Ctrl + C<\/code> \u9000\u51fa\u7a0b\u5e8f\uff0c<code>srv.Shutdown(ctx)<\/code> \u5185\u90e8\u4f1a\u5148\u6267\u884c <code>srv.RegisterOnShutdown<\/code> \u6ce8\u518c\u7684\u51fd\u6570\uff0c<code>svc.GracefulStop<\/code> \u4f1a\u7acb\u5373\u6267\u884c\u5b8c\u6210\u5e76\u9000\u51fa\uff0c\u4e4b\u540e\u7b49\u5f85\u51e0\u79d2\uff0c<code>svc.Handler<\/code> \u4e2d\u7684\u903b\u8f91\u624d\u4f1a\u8d70\u5230 <code>svc.FakeSendEmail()<\/code>\uff0c\u6b64\u65f6\u5c31\u5df2\u7ecf\u65e0\u6cd5\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa goroutine \u4e86\u3002<\/p>\n<p>\u81f3\u6b64\uff0cHTTP Server \u4e2d\u57fa\u672c\u7684\u5e38\u89c1\u4f18\u96c5\u9000\u51fa\u573a\u666f\u53ca\u65b9\u6848\u6211\u4eec\u5c31\u4ecb\u7ecd\u5b8c\u4e86\uff0c\u63a5\u4e0b\u6765\u6211\u518d\u5e26\u4f60\u4e00\u8d77\u6df1\u5165\u4e86\u89e3\u4e00\u4e0b <code>Shutdown<\/code> \u7684\u6e90\u7801\u662f\u5982\u4f55\u5b9e\u73b0\u7684\u3002<\/p>\n<h4>Shutdown \u6e90\u7801<\/h4>\n<p><code>Shutdown<\/code> \u65b9\u6cd5\u6e90\u7801\u5982\u4e0b\uff1a<\/p>\n<blockquote><p><a href=\"https:\/\/link.segmentfault.com\/?enc=x%2Fjt1abHLqyZIsqlJWBDaw%3D%3D.p8wuOnhSfvfdSg6Od9b1Mkn2T%2FIdaAOLBnGGJfY8CLL23y6A0OUbGGz3f1ZZ5nwKGc7QhOW9ERENGDMEuVwJXDJ1tZYMk1V8r5cRNzBDu0LuffdVtKbAxcYbfuoWe1wT\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/net\/http\/server.go#L2990<\/a><\/p><\/blockquote>\n<pre><code class=\"go\">\/\/ Shutdown gracefully shuts down the server without interrupting any\n\/\/ active connections. Shutdown works by first closing all open\n\/\/ listeners, then closing all idle connections, and then waiting\n\/\/ indefinitely for connections to return to idle and then shut down.\n\/\/ If the provided context expires before the shutdown is complete,\n\/\/ Shutdown returns the context's error, otherwise it returns any\n\/\/ error returned from closing the [Server]'s underlying Listener(s).\n\/\/\n\/\/ When Shutdown is called, [Serve], [ListenAndServe], and\n\/\/ [ListenAndServeTLS] immediately return [ErrServerClosed]. Make sure the\n\/\/ program doesn't exit and waits instead for Shutdown to return.\n\/\/\n\/\/ Shutdown does not attempt to close nor wait for hijacked\n\/\/ connections such as WebSockets. The caller of Shutdown should\n\/\/ separately notify such long-lived connections of shutdown and wait\n\/\/ for them to close, if desired. See [Server.RegisterOnShutdown] for a way to\n\/\/ register shutdown notification functions.\n\/\/\n\/\/ Once Shutdown has been called on a server, it may not be reused;\n\/\/ future calls to methods such as Serve will return ErrServerClosed.\nfunc (srv *Server) Shutdown(ctx context.Context) error {\n    srv.inShutdown.Store(true)\n\n    srv.mu.Lock()\n    lnerr := srv.closeListenersLocked()\n    for _, f := range srv.onShutdown {\n        go f()\n    }\n    srv.mu.Unlock()\n    srv.listenerGroup.Wait()\n\n    pollIntervalBase := time.Millisecond\n    nextPollInterval := func() time.Duration {\n        \/\/ Add 10% jitter.\n        interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase\/10)))\n        \/\/ Double and clamp for next time.\n        pollIntervalBase *= 2\n        if pollIntervalBase &gt; shutdownPollIntervalMax {\n            pollIntervalBase = shutdownPollIntervalMax\n        }\n        return interval\n    }\n\n    timer := time.NewTimer(nextPollInterval())\n    defer timer.Stop()\n    for {\n        if srv.closeIdleConns() {\n            return lnerr\n        }\n        select {\n        case &lt;-ctx.Done():\n            return ctx.Err()\n        case &lt;-timer.C:\n            timer.Reset(nextPollInterval())\n        }\n    }\n}<\/code><\/pre>\n<p>\u9996\u5148 <code>Shutdown<\/code> \u65b9\u6cd5\u6ce8\u91ca\u5199\u7684\u975e\u5e38\u6e05\u6670\uff1a<\/p>\n<blockquote>\n<p>Shutdown \u4f1a\u4f18\u96c5\u5730\u5173\u95ed\u670d\u52a1\u5668\uff0c\u800c\u4e0d\u4f1a\u4e2d\u65ad\u4efb\u4f55\u6d3b\u52a8\u7684\u8fde\u63a5\u3002\u5b83\u7684\u5de5\u4f5c\u539f\u7406\u662f\u5148\u5173\u95ed\u6240\u6709\u5df2\u6253\u5f00\u7684\u76d1\u542c\u5668\uff08listeners\uff09\uff0c\u7136\u540e\u5173\u95ed\u6240\u6709\u7a7a\u95f2\u7684\u8fde\u63a5\uff0c\u5e76\u65e0\u9650\u671f\u5730\u7b49\u5f85\u6240\u6709\u8fde\u63a5\u53d8\u4e3a\u7a7a\u95f2\u72b6\u6001\u540e\u518d\u5173\u95ed\u670d\u52a1\u5668\u3002\u5982\u679c\u5728\u5173\u95ed\u5b8c\u6210\u4e4b\u524d\uff0c\u4f20\u5165\u7684\u4e0a\u4e0b\u6587\uff08context\uff09\u8fc7\u671f\uff0cShutdown \u4f1a\u8fd4\u56de\u4e0a\u4e0b\u6587\u7684\u9519\u8bef\uff0c\u5426\u5219\u5b83\u5c06\u8fd4\u56de\u5173\u95ed\u670d\u52a1\u5668\u5e95\u5c42\u76d1\u542c\u5668\u65f6\u6240\u4ea7\u751f\u7684\u4efb\u4f55\u9519\u8bef\u3002<\/p>\n<p>\u5f53\u8c03\u7528 Shutdown \u65f6\uff0c[Serve]\u3001[ListenAndServe] \u548c [ListenAndServeTLS] \u4f1a\u7acb\u5373\u8fd4\u56de [ErrServerClosed] \u9519\u8bef\u3002\u8bf7\u786e\u4fdd\u7a0b\u5e8f\u4e0d\u4f1a\u76f4\u63a5\u9000\u51fa\uff0c\u800c\u662f\u7b49\u5f85 Shutdown \u8fd4\u56de\u540e\u518d\u9000\u51fa\u3002<\/p>\n<p>Shutdown \u4e0d\u4f1a\u5c1d\u8bd5\u5173\u95ed\u6216\u7b49\u5f85\u88ab\u52ab\u6301\u7684\u8fde\u63a5\uff08\u4f8b\u5982 WebSocket\uff09\u3002Shutdown \u7684\u8c03\u7528\u8005\u5e94\u5355\u72ec\u901a\u77e5\u8fd9\u4e9b\u957f\u65f6\u95f4\u5b58\u5728\u7684\u8fde\u63a5\u5173\u4e8e\u5173\u95ed\u7684\u4fe1\u606f\uff0c\u5e76\u6839\u636e\u9700\u8981\u7b49\u5f85\u5b83\u4eec\u5173\u95ed\u3002\u53ef\u4ee5\u53c2\u8003 [Server.RegisterOnShutdown] \u6765\u6ce8\u518c\u5173\u95ed\u901a\u77e5\u51fd\u6570\u3002<\/p>\n<p>\u4e00\u65e6\u5728\u670d\u52a1\u5668\u4e0a\u8c03\u7528\u4e86 Shutdown\uff0c\u5b83\u5c06\u65e0\u6cd5\u518d\u6b21\u4f7f\u7528\uff1b\u4e4b\u540e\u5bf9 Serve \u7b49\u65b9\u6cd5\u7684\u8c03\u7528\u5c06\u8fd4\u56de ErrServerClosed \u9519\u8bef\u3002<\/p>\n<\/blockquote>\n<p>\u901a\u8fc7\u8fd9\u6bb5\u6ce8\u91ca\uff0c\u6211\u4eec\u5c31\u80fd\u5bf9 <code>Shutdown<\/code> \u65b9\u6cd5\u6267\u884c\u6d41\u7a0b\u6709\u4e2a\u5927\u6982\u7406\u89e3\u3002<\/p>\n<p>\u63a5\u7740\u6211\u4eec\u6765\u4ece\u4e0a\u5230\u4e0b\u4f9d\u6b21\u5206\u6790\u4e0b <code>Shutdown<\/code> \u6e90\u7801\u3002<\/p>\n<p>\u7b2c\u4e00\u884c\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">srv.inShutdown.Store(true)<\/code><\/pre>\n<p><code>Shutdown<\/code> \u9996\u5148\u5c06 <code>inShutdown<\/code> \u6807\u8bb0\u4e3a <code>true<\/code>\uff0c<code>inShutdown<\/code> \u662f <code>atomic.Bool<\/code> \u7c7b\u578b\uff0c\u5b83\u7528\u6765\u6807\u8bb0\u670d\u52a1\u5668\u662f\u5426\u6b63\u5728\u5173\u95ed\u3002<\/p>\n<p>\u8fd9\u91cc\u4f7f\u7528\u4e86 <code>atomic<\/code> \u6765\u4fdd\u8bc1\u64cd\u4f5c\u7684\u539f\u5b50\u6027\uff0c\u4ee5\u514d\u5176\u4ed6\u65b9\u6cd5\u8bfb\u53d6\u5230\u9519\u8bef\u7684 <code>inShutdown<\/code> \u6807\u5fd7\u4f4d\uff0c\u53d1\u751f\u9519\u8bef\u3002\u907f\u514d HTTP Server \u8fdb\u7a0b\u5df2\u7ecf\u5f00\u59cb\u5904\u7406\u7ed3\u675f\u903b\u8f91\uff0c\u8fd8\u4f1a\u6709\u65b0\u7684\u8bf7\u6c42\u8fdb\u5165\u5230 <code>srv.Serve<\/code> \u65b9\u6cd5\u3002<\/p>\n<p>\u63a5\u7740 <code>Shutdown<\/code> \u4f1a\u5173\u95ed\u76d1\u542c\u7684\u7aef\u53e3\uff1a<\/p>\n<pre><code class=\"go\">srv.mu.Lock()\nlnerr := srv.closeListenersLocked()\nfor _, f := range srv.onShutdown {\n    go f()\n}\nsrv.mu.Unlock()<\/code><\/pre>\n<p>\u4ee3\u7801\u4e2d\u7684 <code>srv.closeListenersLocked()<\/code> \u5c31\u662f\u5728\u5173\u95ed\u6240\u6709\u7684\u76d1\u542c\u5668\uff08<code>listeners<\/code>\uff09\u3002<\/p>\n<p>\u65b9\u6cd5\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">func (s *Server) closeListenersLocked() error {\n    var err error\n    for ln := range s.listeners {\n        if cerr := (*ln).Close(); cerr != nil &amp;&amp; err == nil {\n            err = cerr\n        }\n    }\n    return err\n}<\/code><\/pre>\n<p>\u8fd9\u4e00\u64cd\u4f5c\uff0c\u5c31\u5bf9\u5e94\u4e86\u5728\u524d\u6587\u4e2d\u8bb2\u89e3\u7684 HTTP Server \u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u4e2d\u7684\u7b2c 1 \u6b65\uff0c\u5173\u95ed\u6240\u6709\u5f00\u542f\u7684 <code>net.Listener<\/code> \u5bf9\u8c61\u3002<\/p>\n<p>\u63a5\u4e0b\u6765\u5faa\u73af\u904d\u5386 <code>srv.onShutdown<\/code> \u4e2d\u7684\u51fd\u6570\uff0c\u5e76\u4f9d\u6b21\u542f\u52a8\u65b0\u7684 goroutine \u5bf9\u5176\u8fdb\u884c\u8c03\u7528\u3002<\/p>\n<p><code>onShutdown<\/code> \u662f <code>[]func()<\/code> \u7c7b\u578b\uff0c\u5176\u5207\u7247\u5185\u5bb9\u6b63\u662f\u5728\u6211\u4eec\u8c03\u7528 <code>srv.RegisterOnShutdown<\/code> \u7684\u65f6\u5019\u6ce8\u518c\u8fdb\u6765\u7684\u3002<\/p>\n<p><code>srv.RegisterOnShutdown<\/code> \u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">\/\/ RegisterOnShutdown registers a function to call on [Server.Shutdown].\n\/\/ This can be used to gracefully shutdown connections that have\n\/\/ undergone ALPN protocol upgrade or that have been hijacked.\n\/\/ This function should start protocol-specific graceful shutdown,\n\/\/ but should not wait for shutdown to complete.\nfunc (srv *Server) RegisterOnShutdown(f func()) {\n    srv.mu.Lock()\n    srv.onShutdown = append(srv.onShutdown, f)\n    srv.mu.Unlock()\n}<\/code><\/pre>\n<p>\u8fd9\u662f\u6211\u4eec\u5728\u524d\u6587\u4e2d\u7684\u4f7f\u7528\u793a\u4f8b\uff1a<\/p>\n<pre><code class=\"go\">\/\/ \u53ef\u4ee5\u6ce8\u518c\u4e00\u4e9b hook \u51fd\u6570\uff0c\u6bd4\u5982\u4ece\u6ce8\u518c\u4e2d\u5fc3\u4e0b\u7ebf\u903b\u8f91\nsrv.RegisterOnShutdown(func() {\n    log.Println(&quot;Register Shutdown 1&quot;)\n})\nsrv.RegisterOnShutdown(func() {\n    log.Println(&quot;Register Shutdown 2&quot;)\n})<\/code><\/pre>\n<p>\u63a5\u7740\u4ee3\u7801\u6267\u884c\u5230\u8fd9\u4e00\u6b65\uff1a<\/p>\n<pre><code class=\"go\">srv.listenerGroup.Wait()<\/code><\/pre>\n<p>\u6839\u636e\u8fd9\u4e2a\u64cd\u4f5c\u7684\u5c5e\u6027\u540d\u548c\u65b9\u6cd5\u540d\u53ef\u4ee5\u731c\u5230\uff0c<code>listenerGroup<\/code> \u660e\u663e\u662f <code>sync.WaitGroup<\/code> \u7c7b\u578b\u3002<\/p>\n<p>\u65e2\u7136\u6709 <code>Wait()<\/code>\uff0c\u90a3\u5c31\u5e94\u8be5\u4f1a\u6709 <code>Add(1)<\/code> \u64cd\u4f5c\u3002\u5728\u6e90\u7801\u4e2d\u641c\u7d22 <code>listenerGroup.Add(1)<\/code> \u5173\u952e\u5b57\uff0c\u53ef\u4ee5\u641c\u5230\u5982\u4e0b\u65b9\u6cd5\uff1a<\/p>\n<pre><code class=\"go\">\/\/ trackListener adds or removes a net.Listener to the set of tracked\n\/\/ listeners.\n\/\/\n\/\/ We store a pointer to interface in the map set, in case the\n\/\/ net.Listener is not comparable. This is safe because we only call\n\/\/ trackListener via Serve and can track+defer untrack the same\n\/\/ pointer to local variable there. We never need to compare a\n\/\/ Listener from another caller.\n\/\/\n\/\/ It reports whether the server is still up (not Shutdown or Closed).\nfunc (s *Server) trackListener(ln *net.Listener, add bool) bool {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    if s.listeners == nil {\n        s.listeners = make(map[*net.Listener]struct{})\n    }\n    if add {\n        if s.shuttingDown() {\n            return false\n        }\n        s.listeners[ln] = struct{}{}\n        s.listenerGroup.Add(1)\n    } else {\n        delete(s.listeners, ln)\n        s.listenerGroup.Done()\n    }\n    return true\n}<\/code><\/pre>\n<p><code>trackListener<\/code> \u7528\u4e8e\u6dfb\u52a0\u6216\u79fb\u9664\u4e00\u4e2a <code>net.Listener<\/code> \u5230\u5df2\u8ddf\u8e2a\u7684\u76d1\u542c\u5668\u96c6\u5408\u4e2d\u3002<\/p>\n<p>\u8fd9\u4e2a\u65b9\u6cd5\u4f1a\u88ab <code>Serve<\/code> \u65b9\u6cd5\u8c03\u7528\uff0c\u800c\u5b9e\u9645\u4e0a\u6211\u4eec\u6267\u884c <code>srv.ListenAndServe()<\/code> \u7684\u65b9\u6cd5\u5185\u90e8\uff0c\u4e5f\u662f\u5728\u8c03\u7528 <code>Serve<\/code> \u65b9\u6cd5\u3002<\/p>\n<p><code>Serve<\/code> \u65b9\u6cd5\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">\/\/ Serve accepts incoming connections on the Listener l, creating a\n\/\/ new service goroutine for each. The service goroutines read requests and\n\/\/ then call srv.Handler to reply to them.\n\/\/\n\/\/ HTTP\/2 support is only enabled if the Listener returns [*tls.Conn]\n\/\/ connections and they were configured with &quot;h2&quot; in the TLS\n\/\/ Config.NextProtos.\n\/\/\n\/\/ Serve always returns a non-nil error and closes l.\n\/\/ After [Server.Shutdown] or [Server.Close], the returned error is [ErrServerClosed].\nfunc (srv *Server) Serve(l net.Listener) error {\n    if fn := testHookServerServe; fn != nil {\n        fn(srv, l) \/\/ call hook with unwrapped listener\n    }\n\n    origListener := l\n    l = &amp;onceCloseListener{Listener: l}\n    defer l.Close()\n\n    if err := srv.setupHTTP2_Serve(); err != nil {\n        return err\n    }\n\n    \/\/ \u5c06 `net.Listener` \u6dfb\u52a0\u5230\u5df2\u8ddf\u8e2a\u7684\u76d1\u542c\u5668\u96c6\u5408\u4e2d\n    \/\/ \u5185\u90e8\u4f1a\u901a\u8fc7\u8c03\u7528 s.shuttingDown() \u5224\u65ad\u662f\u5426\u6b63\u5728\u8fdb\u884c\u9000\u51fa\u64cd\u4f5c\uff0c\u5982\u679c\u662f\uff0c\u5219\u8fd4\u56de ErrServerClosed\n    if !srv.trackListener(&amp;l, true) {\n        return ErrServerClosed\n    }\n    \/\/ Serve \u51fd\u6570\u9000\u51fa\u65f6\uff0c\u5c06 `net.Listener` \u4ece\u5df2\u8ddf\u8e2a\u7684\u76d1\u542c\u5668\u96c6\u5408\u4e2d\u79fb\u9664\n    defer srv.trackListener(&amp;l, false)\n\n    baseCtx := context.Background()\n    if srv.BaseContext != nil {\n        baseCtx = srv.BaseContext(origListener)\n        if baseCtx == nil {\n            panic(&quot;BaseContext returned a nil context&quot;)\n        }\n    }\n\n    var tempDelay time.Duration \/\/ how long to sleep on accept failure\n\n    ctx := context.WithValue(baseCtx, ServerContextKey, srv)\n    for {\n        rw, err := l.Accept()\n        if err != nil {\n            \/\/ \u6bcf\u6b21\u65b0\u7684\u8bf7\u6c42\u8fdb\u6765\uff0c\u5148\u5224\u65ad\u5f53\u524d\u670d\u52a1\u662f\u5426\u5df2\u7ecf\u88ab\u6807\u8bb0\u4e3a\u6b63\u5728\u5173\u95ed\uff0c\u5982\u679c\u662f\uff0c\u5219\u76f4\u63a5\u8fd4\u56de ErrServerClosed\n            if srv.shuttingDown() {\n                return ErrServerClosed\n            }\n            if ne, ok := err.(net.Error); ok &amp;&amp; ne.Temporary() {\n                if tempDelay == 0 {\n                    tempDelay = 5 * time.Millisecond\n                } else {\n                    tempDelay *= 2\n                }\n                if max := 1 * time.Second; tempDelay &gt; max {\n                    tempDelay = max\n                }\n                srv.logf(&quot;http: Accept error: %v; retrying in %v&quot;, err, tempDelay)\n                time.Sleep(tempDelay)\n                continue\n            }\n            return err\n        }\n        connCtx := ctx\n        if cc := srv.ConnContext; cc != nil {\n            connCtx = cc(connCtx, rw)\n            if connCtx == nil {\n                panic(&quot;ConnContext returned nil&quot;)\n            }\n        }\n        tempDelay = 0\n        c := srv.newConn(rw)\n        c.setState(c.rwc, StateNew, runHooks) \/\/ before Serve can return\n        go c.serve(connCtx)\n    }\n}<\/code><\/pre>\n<p>\u5728 <code>Serve<\/code> \u65b9\u6cd5\u5185\u90e8\uff0c\u6211\u4eec\u5148\u91cd\u70b9\u5173\u6ce8\u5982\u4e0b\u4ee3\u7801\u6bb5\uff1a<\/p>\n<pre><code class=\"go\">if !srv.trackListener(&amp;l, true) {\n    return ErrServerClosed\n}\ndefer srv.trackListener(&amp;l, false)<\/code><\/pre>\n<p>\u8fd9\u8bf4\u660e <code>Serve<\/code> \u5728\u542f\u52a8\u7684\u65f6\u5019\u4f1a\u5c06\u4e00\u4e2a\u65b0\u7684\u76d1\u542c\u5668\uff08<code>net.Listener<\/code>\uff09\u52a0\u5165\u5230 <code>listeners<\/code> \u96c6\u5408\u4e2d\u3002<\/p>\n<p><code>Serve<\/code> \u51fd\u6570\u9000\u51fa\u65f6\uff0c\u4f1a\u5bf9\u5176\u8fdb\u884c\u79fb\u9664\u3002<\/p>\n<p>\u5e76\u4e14\uff0c<code>srv.trackListener<\/code> \u5185\u90e8\u53c8\u8c03\u7528\u4e86 <code>s.shuttingDown()<\/code> \u5224\u65ad\u5f53\u524d\u670d\u52a1\u662f\u5426\u6b63\u5728\u8fdb\u884c\u9000\u51fa\u64cd\u4f5c\uff0c\u5982\u679c\u662f\uff0c\u5219\u8fd4\u56de <code>ErrServerClosed<\/code>\u3002<\/p>\n<pre><code class=\"go\">\/\/ \u6807\u8bb0\u4e3a\u5173\u95ed\u72b6\u6001\uff0c\u5c31\u4e0d\u4f1a\u6709\u8bf7\u6c42\u8fdb\u6765\uff0c\u76f4\u63a5\u8fd4\u56de\u9519\u8bef\nif srv.shuttingDown() {\n    return ErrServerClosed\n}<\/code><\/pre>\n<p><code>shuttingDown<\/code> \u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">func (s *Server) shuttingDown() bool {\n    return s.inShutdown.Load()\n}<\/code><\/pre>\n<p>\u540c\u7406\uff0c\u5728 <code>for<\/code> \u5faa\u73af\u4e2d\uff0c\u6bcf\u6b21 <code>rw, err := l.Accept()<\/code> \u6536\u5230\u65b0\u7684\u8bf7\u6c42\uff0c\u90fd\u4f1a\u5148\u5224\u65ad\u5f53\u524d\u670d\u52a1\u662f\u5426\u5df2\u7ecf\u88ab\u6807\u8bb0\u4e3a\u6b63\u5728\u5173\u95ed\uff0c\u5982\u679c\u662f\uff0c\u5219\u76f4\u63a5\u8fd4\u56de <code>ErrServerClosed<\/code>\u3002<\/p>\n<p>\u8fd9\u91cc\u5176\u5b9e\u5c31\u662f\u5728\u8ddf <code>Shutdown<\/code> \u65b9\u6cd5\u4e2d\u7684 <code>srv.inShutdown.Store(true)<\/code> \u8fdb\u884c\u914d\u5408\u64cd\u4f5c\u3002<\/p>\n<p><code>Shutdown<\/code> \u6536\u5230\u4f18\u96c5\u9000\u51fa\u8bf7\u6c42\uff0c\u5c31\u5c06 <code>inShutdown<\/code> \u6807\u8bb0\u4e3a <code>true<\/code>\u3002\u6b64\u65f6 <code>Serve<\/code> \u65b9\u6cd5\u5185\u90e8\u4e3a\u4e86\u4e0d\u518d\u63a5\u6536\u65b0\u7684\u8bf7\u6c42\u8fdb\u6765\uff0c\u6bcf\u6b21\u90fd\u4f1a\u8c03\u7528 <code>s.shuttingDown()<\/code> \u8fdb\u884c\u5224\u65ad\u3002\u4fdd\u8bc1\u4e0d\u4f1a\u518d\u6709\u65b0\u7684\u8bf7\u6c42\u8fdb\u6765\uff0c\u5bfc\u81f4 <code>Shutdown<\/code> \u65e0\u6cd5\u9000\u51fa\u3002<\/p>\n<p>\u8fd9\u8ddf\u524d\u6587\u8bb2\u89e3\u5b8c\u5168\u543b\u5408\uff0c\u5728 <code>Shutdown<\/code> \u65b9\u6cd5\u8fd8\u6ca1\u6267\u884c\u5b8c\u6210\u7684\u65f6\u5019\uff0c<code>Serve<\/code> \u65b9\u6cd5\u5176\u5b9e\u5df2\u7ecf\u9000\u51fa\u4e86\u3002\u4e5f\u662f\u6211\u4eec\u4e3a\u4ec0\u4e48\u5c06 <code>srv.ListenAndServe()<\/code> \u4ee3\u7801\u653e\u5230\u5b50 goroutine \u4e2d\u7684\u539f\u56e0\u3002<\/p>\n<p><code>Shutdown<\/code> \u63a5\u7740\u5f80\u4e0b\u6267\u884c\uff1a<\/p>\n<pre><code class=\"go\">pollIntervalBase := time.Millisecond\nnextPollInterval := func() time.Duration {\n    \/\/ Add 10% jitter.\n    interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase\/10)))\n    \/\/ Double and clamp for next time.\n    pollIntervalBase *= 2\n    if pollIntervalBase &gt; shutdownPollIntervalMax {\n        pollIntervalBase = shutdownPollIntervalMax\n    }\n    return interval\n}\n\ntimer := time.NewTimer(nextPollInterval())\ndefer timer.Stop()\nfor {\n    if srv.closeIdleConns() {\n        return lnerr\n    }\n    select {\n    case &lt;-ctx.Done():\n        return ctx.Err()\n    case &lt;-timer.C:\n        timer.Reset(nextPollInterval())\n    }\n}<\/code><\/pre>\n<p>\u8fd9\u4e00\u6bb5\u903b\u8f91\u6bd4\u8f83\u591a\uff0c\u5e76\u4e14 <code>nextPollInterval<\/code> \u51fd\u6570\u770b\u8d77\u6765\u6bd4\u8f83\u8ff7\u60d1\uff0c\u4e0d\u8fc7\u6ca1\u5173\u7cfb\uff0c\u6211\u4eec\u4e00\u70b9\u70b9\u6765\u5206\u6790\u3002<\/p>\n<p>\u6211\u4eec\u628a\u8fd9\u6bb5\u4ee3\u7801\u4e2d\u7684 <code>nextPollInterval<\/code> \u51fd\u6570\u5355\u72ec\u62ff\u51fa\u6765\u8dd1\u4e00\u4e0b\uff0c\u5c31\u80fd\u5927\u6982\u77e5\u9053\u5b83\u7684\u610f\u56fe\u4e86\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;fmt&quot;\n    &quot;math\/rand&quot;\n    &quot;time&quot;\n)\n\nfunc main() {\n    const shutdownPollIntervalMax = 500 * time.Millisecond\n    pollIntervalBase := time.Millisecond\n    nextPollInterval := func() time.Duration {\n        \/\/ Add 10% jitter.\n        interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase\/10)))\n        \/\/ Double and clamp for next time.\n        pollIntervalBase *= 2\n        if pollIntervalBase &gt; shutdownPollIntervalMax {\n            pollIntervalBase = shutdownPollIntervalMax\n        }\n        return interval\n    }\n\n    for i := 0; i &lt; 20; i++ {\n        fmt.Println(nextPollInterval())\n    }\n}<\/code><\/pre>\n<p>\u6267\u884c\u8fd9\u6bb5\u7a0b\u5e8f\uff0c\u8f93\u5165\u7ed3\u679c\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"bash\">$ go run main.go\n1.078014ms\n2.007835ms\n4.151327ms\n8.474296ms\n17.487625ms\n34.403371ms\n64.613106ms\n136.696655ms\n273.873977ms\n516.290814ms\n502.815326ms\n516.160214ms\n523.34143ms\n537.808701ms\n518.913897ms\n526.711692ms\n518.421559ms\n527.229427ms\n526.904891ms\n502.738764ms<\/code><\/pre>\n<p>\u6211\u4eec\u53ef\u4ee5\u628a\u968f\u673a\u6570\u518d\u53bb\u6389\u3002<\/p>\n<p>\u628a\u8fd9\u884c\u4ee3\u7801\uff1a<\/p>\n<pre><code class=\"go\">interval := pollIntervalBase + time.Duration(rand.Intn(int(pollIntervalBase\/10)))<\/code><\/pre>\n<p>\u6539\u6210\u8fd9\u6837\uff1a<\/p>\n<pre><code class=\"go\">interval := pollIntervalBase + time.Duration(pollIntervalBase\/10)<\/code><\/pre>\n<p>\u91cd\u65b0\u6267\u884c\u8fd9\u6bb5\u7a0b\u5e8f\uff0c\u8f93\u5165\u7ed3\u679c\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"bash\">$ go run main.go\n1.1ms\n2.2ms\n4.4ms\n8.8ms\n17.6ms\n35.2ms\n70.4ms\n140.8ms\n281.6ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms\n550ms<\/code><\/pre>\n<p>\u6839\u636e\u8f93\u51fa\u7ed3\u679c\uff0c\u6211\u4eec\u53ef\u4ee5\u6e05\u6670\u7684\u770b\u51fa\uff0c<code>nextPollInterval<\/code> \u51fd\u6570\u6267\u884c\u8fd4\u56de\u503c\uff0c\u5f00\u59cb\u65f6\u6309\u7167 2 \u500d\u65b9\u5f0f\u589e\u957f\uff0c\u6700\u7ec8\u56fa\u5b9a\u5728 <code>550ms<\/code>\u3002<\/p>\n<p><code>pollIntervalBase<\/code> \u6700\u7ec8\u503c\u7b49\u4e8e <code>shutdownPollIntervalMax<\/code>\u3002<\/p>\n<p>\u6839\u636e\u516c\u5f0f\u8ba1\u7b97 <code>interval := pollIntervalBase + time.Duration(pollIntervalBase\/10)<\/code>\uff0c\u5373 <code>interval = 500 + 500 \/ 10 = 550ms<\/code>\uff0c\u8ba1\u7b97\u7ed3\u679c\u4e0e\u8f93\u51fa\u7ed3\u679c\u76f8\u543b\u5408\u3002<\/p>\n<p>\u8bf4\u767d\u4e86\uff0c\u8fd9\u6bb5\u4ee3\u7801\u5199\u8fd9\u4e48\u590d\u6742\uff0c\u5176\u6838\u5fc3\u76ee\u7684\u5c31\u662f\u4e3a <code>Shutdown<\/code> \u65b9\u6cd5\u4e2d\u7b49\u5f85\u7a7a\u95f2\u8fde\u63a5\u5173\u95ed\u7684\u8f6e\u8be2\u64cd\u4f5c\u8bbe\u8ba1\u4e00\u4e2a\u52a8\u6001\u7684\u3001\u5e26\u6709\u6296\u52a8\uff08jitter\uff09\u7684\u65f6\u95f4\u95f4\u9694\u3002\u8fd9\u79cd\u8bbe\u8ba1\u786e\u4fdd\u670d\u52a1\u5668\u5728\u6267\u884c\u4f18\u96c5\u9000\u51fa\u65f6\uff0c\u80fd\u591f\u6709\u6548\u5730\u5904\u7406\u5269\u4f59\u7684\u7a7a\u95f2\u8fde\u63a5\uff0c\u540c\u65f6\u907f\u514d\u4e0d\u5fc5\u8981\u7684\u8d44\u6e90\u6d6a\u8d39\u3002<\/p>\n<p>\u73b0\u5728\u6765\u770b <code>for<\/code> \u5faa\u73af\u8fd9\u6bb5\u4ee3\u7801\uff0c\u5c31\u975e\u5e38\u597d\u7406\u89e3\u4e86\uff1a<\/p>\n<pre><code class=\"go\">timer := time.NewTimer(nextPollInterval())\ndefer timer.Stop()\nfor {\n    if srv.closeIdleConns() {\n        return lnerr\n    }\n    select {\n    case &lt;-ctx.Done():\n        return ctx.Err()\n    case &lt;-timer.C:\n        timer.Reset(nextPollInterval())\n    }\n}<\/code><\/pre>\n<p>\u8fd9\u5c31\u662f Go \u5e38\u7528\u7684\u5b9a\u65f6\u5668\u60ef\u7528\u6cd5\u3002<\/p>\n<p>\u6839\u636e <code>nextPollInterval()<\/code> \u8fd4\u56de\u503c\u5927\u5c0f\uff0c\u6bcf\u6b21\u5b9a\u65f6\u5faa\u73af\u8c03\u7528 <code>srv.closeIdleConns()<\/code> \u65b9\u6cd5\u3002<\/p>\n<p>\u5e76\u4e14\u8fd9\u91cc\u6709\u4e00\u4e2a case \u6267\u884c\u4e86 <code>case &lt;-ctx.Done()<\/code>\uff0c\u8fd9\u6b63\u662f\u6211\u4eec\u8c03\u7528 <code>srv.Shutdown(ctx)<\/code> \u65f6\uff0c\u7528\u6765\u63a7\u5236\u8d85\u65f6\u65f6\u95f4\u4f20\u9012\u8fdb\u6765\u7684 <code>Context<\/code>\u3002<\/p>\n<p>\u53e6\u5916\uff0c\u503c\u5f97\u4e00\u63d0\u7684\u662f\uff0c\u5728 <a href=\"https:\/\/link.segmentfault.com\/?enc=ETdPGIfZhDqJNtLOCnXZ3Q%3D%3D.S86nyyG0ZF2xIYZrHB8qocVgzyJuSErx1Tb8SAA9wKqD%2FW5b3f6B6GpJIKDr3GTe%2FjZXhMH%2FwYvGVNKwvBdsAcvxlDZFtteScMNp0HY5gG2Wu8pmXxn7G6J1kvt2WdDL\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Go 1.15<\/a> \u53ca\u4ee5\u524d\u7684\u7248\u672c\u7684 <code>Shutdown<\/code> \u4ee3\u7801\u4e2d\u8fd9\u6bb5\u5b9a\u65f6\u5668\u4ee3\u7801\u5e76\u4e0d\u662f\u8fd9\u6837\u5b9e\u73b0\u7684\u3002<\/p>\n<p>\u65e7\u7248\u672c\u4ee3\u7801\u5b9e\u73b0\u5982\u4e0b\uff1a<\/p>\n<blockquote><p><a href=\"https:\/\/link.segmentfault.com\/?enc=3YK5Us41gEHCIcWUiPGvqA%3D%3D.y319IH87D8URT1QTjFy6RZ9K%2FQmpslWCftMwvTaJmInUK6CdaMoW6LO1V36ITJTcNsKETAHZwx%2BJs%2BsVkvk0O9bTNnv8D2h3kGj%2FP3ALNNo%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.15\/src\/net\/http\/server.go<\/a><\/p><\/blockquote>\n<pre><code class=\"go\">var shutdownPollInterval = 500 * time.Millisecond\n...\n\nticker := time.NewTicker(shutdownPollInterval)\ndefer ticker.Stop()\nfor {\n    if srv.closeIdleConns() &amp;&amp; srv.numListeners() == 0 {\n        return lnerr\n    }\n    select {\n    case &lt;-ctx.Done():\n        return ctx.Err()\n    case &lt;-ticker.C:\n    }\n}<\/code><\/pre>\n<p>\u65e7\u7248\u672c\u4ee3\u7801\u5b9e\u73b0\u66f4\u52a0\u7b80\u5355\uff0c\u5e76\u6ca1\u6709\u4f7f\u7528 <code>time.NewTimer<\/code>\uff0c\u800c\u662f\u4f7f\u7528\u4e86 <code>time.NewTicker<\/code>\u3002\u8fd9\u6837\u5b9e\u73b0\u7684\u597d\u5904\u662f\u4ee3\u7801\u7b80\u5355\uff0c\u903b\u8f91\u6e05\u6670\uff0c\u6ca1\u82b1\u54e8\u7684\u529f\u80fd\u3002<\/p>\n<p>\u5176\u5b9e\u6211\u4eec\u5728\u5de5\u4f5c\u4e2d\u5199\u4ee3\u7801\u4e5f\u662f\u4e00\u6837\u7684\u9053\u7406\uff0c\u5148\u8ba9\u4ee3\u7801 <code>run<\/code> \u8d77\u6765\uff0c\u540e\u671f\u518d\u8003\u8651\u4f18\u5316\u7684\u95ee\u9898\u3002<code>Shutdown<\/code> \u65b9\u6cd5\u5728 Go 1.8 \u7248\u672c\u88ab\u52a0\u5165\uff0c\u76f4\u5230 Go 1.16 \u7248\u672c\u8fd9\u6bb5\u4ee3\u7801\u624d\u53d1\u751f\u6539\u53d8\u3002<\/p>\n<p>\u65e7\u7248\u672c\u4ee3\u7801\u4f7f\u7528 <code>time.NewTicker<\/code> \u662f\u56e0\u4e3a\u6bcf\u6b21\u5b9a\u65f6\u5faa\u73af\u7684\u5468\u671f\u90fd\u662f\u56fa\u5b9a\u503c\uff0c\u4e0d\u9700\u8981\u6539\u53d8\u3002<\/p>\n<p>\u65b0\u7248\u672c\u4ee3\u7801\u4f7f\u7528 <code>time.NewTimer<\/code> \u662f\u4e3a\u4e86\u5728\u6bcf\u6b21\u5faa\u73af\u5468\u671f\u4e2d\u8c03\u7528 <code>timer.Reset<\/code> \u91cd\u7f6e\u95f4\u9694\u65f6\u95f4\u3002<\/p>\n<p>\u8fd9\u4e5f\u662f\u4e00\u4e2a\u503c\u5f97\u5b66\u4e60\u7684\u5c0f\u6280\u5de7\u3002\u6211\u4eec\u5728\u5de5\u4f5c\u4e2d\u7ecf\u5e38\u4f1a\u9047\u5230\u7c7b\u4f3c\u7684\u9700\u6c42\uff1a\u6bcf\u9694\u4e00\u6bb5\u65f6\u95f4\uff0c\u6267\u884c\u4e00\u6b21\u64cd\u4f5c\u3002\u6700\u7b80\u5355\u7684\u65b9\u5f0f\u5c31\u662f\u4f7f\u7528 <code>time.Sleep<\/code> \u6765\u505a\u95f4\u9694\u65f6\u957f\uff0c\u7136\u540e\u5c31\u662f <code>time.NewTicker<\/code> \u548c <code>time.NewTimer<\/code> \u8fd9\u4e24\u79cd\u65b9\u5f0f\u3002\u8fd9 3 \u79cd\u65b9\u5f0f\u5176\u5b9e\u90fd\u80fd\u5b9e\u73b0\u6bcf\u9694\u4e00\u6bb5\u65f6\u95f4\u6267\u884c\u4e00\u6b21\u64cd\u4f5c\uff0c\u4f46\u5b83\u4eec\u9002\u7528\u573a\u666f\u53c8\u6709\u6240\u4e0d\u540c\u3002<\/p>\n<p><code>time.Sleep<\/code> \u662f\u7528\u963b\u585e\u5f53\u524d goroutine \u7684\u65b9\u5f0f\u6765\u5b9e\u73b0\u7684\uff0c\u5b83\u9700\u8981\u8c03\u5ea6\u5668\u5148\u5524\u9192\u5f53\u524d goroutine\uff0c\u7136\u540e\u624d\u80fd\u6267\u884c\u540e\u7eed\u4ee3\u7801\u903b\u8f91\u3002<\/p>\n<p><code>time.Ticker<\/code> \u521b\u5efa\u4e86\u4e00\u4e2a\u5e95\u5c42\u6570\u636e\u7ed3\u6784\u5b9a\u65f6\u5668 <code>runtimeTimer<\/code>\uff0c\u5e76\u4e14\u76d1\u542c <code>runtimeTimer<\/code> \u8ba1\u65f6\u7ed3\u675f\u540e\u4ea7\u751f\u7684\u4fe1\u53f7\u3002\u56e0\u4e3a Go \u4e3a\u5176\u8fdb\u884c\u4e86\u4f18\u5316\uff0c\u6240\u4ee5\u5b83\u7684 CPU \u6d88\u8017\u6bd4 <code>time.Sleep<\/code> \u5c0f\u5f88\u591a\u3002<\/p>\n<p><code>time.Timer<\/code> \u5e95\u5c42\u4e5f\u662f\u5b9a\u65f6\u5668 <code>runtimeTimer<\/code>\uff0c\u53ea\u4e0d\u8fc7\u6211\u4eec\u53ef\u4ee5\u65b9\u4fbf\u7684\u4f7f\u7528 <code>timer.Reset<\/code> \u91cd\u7f6e\u95f4\u9694\u65f6\u95f4\u3002<\/p>\n<p>\u6240\u4ee5\u8fd9 3 \u8005\u90fd\u6709\u5404\u81ea\u9002\u7528\u7684\u573a\u666f\u3002<\/p>\n<p>\u73b0\u5728\u6211\u4eec\u9700\u8981\u7ee7\u7eed\u8ddf\u8e2a\u7684\u4ee3\u7801\u5c31\u5269\u4e0b <code>srv.closeIdleConns()<\/code> \u4e86\uff0c\u6839\u636e\u65b9\u6cd5\u547d\u540d\u6211\u4eec\u4e5f\u80fd\u5927\u6982\u731c\u6d4b\u5230\u5b83\u7684\u7528\u9014\u5c31\u662f\u4e3a\u4e86\u5173\u95ed\u7a7a\u95f2\u8fde\u63a5\u3002<\/p>\n<p><code>closeIdleConns<\/code> \u65b9\u6cd5\u5b9a\u4e49\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">\/\/ closeIdleConns closes all idle connections and reports whether the\n\/\/ server is quiescent.\nfunc (s *Server) closeIdleConns() bool {\n    s.mu.Lock()\n    defer s.mu.Unlock()\n    quiescent := true\n    for c := range s.activeConn {\n        st, unixSec := c.getState()\n        \/\/ Issue 22682: treat StateNew connections as if\n        \/\/ they're idle if we haven't read the first request's\n        \/\/ header in over 5 seconds.\n        \/\/  \u8fd9\u91cc\u9884\u7559 5s\uff0c\u9632\u6b62\u5728\u7b2c\u4e00\u6b21\u8bfb\u53d6\u8fde\u63a5\u5934\u90e8\u4fe1\u606f\u65f6\u8d85\u8fc7 5s\n        if st == StateNew &amp;&amp; unixSec &lt; time.Now().Unix()-5 {\n            st = StateIdle\n        }\n        if st != StateIdle || unixSec == 0 {\n            \/\/ Assume unixSec == 0 means it's a very new\n            \/\/ connection, without state set yet.\n            \/\/ \/\/ unixSec == 0 \u4ee3\u8868\u8fd9\u4e2a\u8fde\u63a5\u662f\u975e\u5e38\u65b0\u7684\u8fde\u63a5\uff0c\u5219\u6807\u5fd7\u4f4d\u88ab\u7f6e\u4e3a false\n            quiescent = false\n            continue\n        }\n        c.rwc.Close()\n        delete(s.activeConn, c)\n    }\n    return quiescent\n}<\/code><\/pre>\n<p>\u8fd9\u4e2a\u65b9\u6cd5\u6bd4\u8f83\u6838\u5fc3\uff0c\u6240\u4ee5\u6574\u4e2a\u64cd\u4f5c\u505a\u4e86\u52a0\u9501\u5904\u7406\u3002<\/p>\n<p>\u4f7f\u7528 <code>for<\/code> \u5faa\u73af\u904d\u5386\u6240\u6709\u8fde\u63a5\uff0c<code>activeConn<\/code> \u662f\u4e00\u4e2a\u96c6\u5408\uff0c\u7c7b\u578b\u4e3a <code>map[*conn]struct{}<\/code>\uff0c\u91cc\u9762\u8bb0\u5f55\u4e86\u6240\u6709\u5b58\u6d3b\u7684\u8fde\u63a5\u3002<\/p>\n<p><code>c.getState()<\/code> \u80fd\u591f\u83b7\u53d6\u8fde\u63a5\u7684\u5f53\u524d\u72b6\u6001\uff0c\u5bf9\u5e94\u7684\u8fd8\u6709\u4e00\u4e2a <code>setState<\/code> \u65b9\u6cd5\u80fd\u591f\u8bbe\u7f6e\u72b6\u6001\uff0c<code>setState<\/code> \u65b9\u6cd5\u4f1a\u5728 <code>Serve<\/code> \u65b9\u6cd5\u4e2d\u88ab\u8c03\u7528\u3002\u8fd9\u5176\u5b9e\u5c31\u5f62\u6210\u95ed\u73af\u4e86\uff0c\u6bcf\u6b21\u6709\u65b0\u7684\u8bf7\u6c42\u8fdb\u6765\uff0c\u90fd\u4f1a\u8bbe\u7f6e\u8fde\u63a5\u72b6\u6001\uff08<code>Serve<\/code> \u4f1a\u6839\u636e\u5f53\u524d\u5904\u7406\u8bf7\u6c42\u7684\u8fdb\u5ea6\uff0c\u5c06\u8fde\u63a5\u72b6\u6001\u8bbe\u7f6e\u6210 <code>StateNew<\/code>\u3001<code>StateActive<\/code>\u3001<code>StateIdle<\/code>\u3001<code>StateClosed<\/code> \u7b49\uff09\uff0c\u800c\u5728 <code>Shutdown<\/code> \u65b9\u6cd5\u4e2d\u83b7\u53d6\u8fde\u63a5\u72b6\u6001\u3002<\/p>\n<p>\u63a5\u7740\uff0c\u4ee3\u7801\u4e2d\u4f1a\u5224\u65ad\u8fde\u63a5\u4e2d\u7684\u8bf7\u6c42\u662f\u5426\u5df2\u7ecf\u5b8c\u6210\u64cd\u4f5c\uff08\u5373\uff1a\u662f\u5426\u5904\u4e8e\u7a7a\u95f2\u72b6\u6001 <code>StateIdle<\/code>\uff09\uff0c\u5982\u679c\u662f\uff0c\u5c31\u76f4\u63a5\u5c06\u8fde\u63a5\u5173\u95ed\uff0c\u5e76\u4ece\u8fde\u63a5\u96c6\u5408\u4e2d\u79fb\u9664\uff0c\u5426\u5219\uff0c\u8df3\u8fc7\u6b64\u6b21\u5faa\u73af\uff0c\u7b49\u5f85\u4e0b\u6b21\u5faa\u73af\u5468\u671f\u3002<\/p>\n<p>\u8fd9\u91cc\u8c03\u7528 <code>c.rwc.Close()<\/code> \u5173\u95ed\u8fde\u63a5\uff0c\u8c03\u7528 <code>delete(s.activeConn, c)<\/code> \u5c06\u5f53\u524d\u8fde\u63a5\u4ece\u96c6\u5408\u4e2d\u79fb\u9664\uff0c\u76f4\u5230\u96c6\u5408\u4e3a\u7a7a\uff0c\u8868\u793a\u5168\u90e8\u8fde\u63a5\u5df2\u7ecf\u88ab\u5173\u95ed\u91ca\u653e\uff0c\u5faa\u73af\u9000\u51fa\u3002<\/p>\n<p><code>closeIdleConns<\/code> \u65b9\u6cd5\u6700\u7ec8\u8fd4\u56de\u7684 <code>quiescent<\/code> \u6807\u5fd7\u4f4d\uff0c\u662f\u7528\u6765\u6807\u8bb0\u662f\u5426\u6240\u6709\u7684\u8fde\u63a5\u90fd\u5df2\u7ecf\u5173\u95ed\u3002\u5982\u679c\u662f\uff0c\u8fd4\u56de <code>true<\/code>\uff0c\u5426\u5219\uff0c\u8fd4\u56de <code>false<\/code>\u3002<\/p>\n<p>\u8fd9\u4e2a\u65b9\u6cd5\u7684\u903b\u8f91\uff0c\u5176\u5b9e\u5c31\u5bf9\u5e94\u4e86\u524d\u6587\u8bb2\u89e3\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u4e2d\u7684\u7b2c 2\u30013 \u4e24\u6b65\u3002<\/p>\n<p>\u81f3\u6b64\uff0c<code>Shutdown<\/code> \u7684\u6e90\u7801\u5c31\u5206\u6790\u5b8c\u6210\u4e86\u3002<\/p>\n<p><code>Shutdown<\/code> \u65b9\u6cd5\u7684\u6574\u4e2a\u6d41\u7a0b\u4e5f\u5b8c\u5168\u662f\u6309\u7167\u6211\u4eec\u524d\u6587\u4e2d\u8bb2\u89e3\u7684\u4f18\u96c5\u9000\u51fa\u6d41\u7a0b\u6765\u7684\u3002<\/p>\n<blockquote><p>NOTE:<br \/>\u9664\u4e86\u4f7f\u7528 <code>Shutdown<\/code> \u8fdb\u884c\u4f18\u96c5\u9000\u51fa\uff0c<code>net\/http<\/code> \u5305\u8fd8\u4e3a\u6211\u4eec\u63d0\u4f9b\u4e86 <code>Close<\/code> \u65b9\u6cd5\u7528\u6765\u5f3a\u5236\u9000\u51fa\uff0c\u4f60\u53ef\u4ee5\u81ea\u884c\u5c1d\u8bd5\u3002<\/p><\/blockquote>\n<h3>Gin \u7684\u4f18\u96c5\u9000\u51fa<\/h3>\n<p>\u5728\u5de5\u4f5c\u4e2d\u6211\u4eec\u5f00\u53d1 Go Web \u7a0b\u5e8f\u65f6\uff0c\u5f80\u5f80\u4e0d\u4f1a\u76f4\u63a5\u4f7f\u7528 <code>net\/http<\/code> \u5305\uff0c\u800c\u662f\u5f15\u5165\u4e00\u4e2a\u7b2c\u4e09\u65b9\u5e93\u6216\u6846\u67b6\u3002\u5176\u4e2d Gin \u4f5c\u4e3a Go \u751f\u6001\u4e2d\u6700\u4e3a\u6d41\u884c\u7684 Web \u5e93\uff0c\u6211\u89c9\u5f97\u6709\u5fc5\u8981\u8bb2\u89e3\u4e00\u4e0b\u5728 Gin \u4e2d\u5982\u4f55\u8fdb\u884c\u4f18\u96c5\u9000\u51fa\u3002<\/p>\n<p>\u4e0d\u8fc7\uff0c\u5b66\u4e60\u4e86 <code>net\/http<\/code> \u7684\u4f18\u96c5\u9000\u51fa\uff0c\u5b9e\u9645\u4e0a Gin \u6846\u67b6\u7684\u4f18\u96c5\u9000\u51fa\u662f\u4e00\u6837\u7684\uff0c\u56e0\u4e3a Gin \u53ea\u662f\u4e00\u4e2a\u8def\u7531\u5e93\uff0c\u63d0\u4f9b HTTP Server \u80fd\u529b\u7684\u8fd8\u662f <code>net\/http<\/code>\u3002<\/p>\n<p>Gin \u6846\u67b6\u4e2d\u7684\u4f18\u96c5\u9000\u51fa\u793a\u4f8b\u4ee3\u7801\u5982\u4e0b\uff1a<\/p>\n<pre><code class=\"go\">package main\n\nimport (\n    &quot;context&quot;\n    &quot;errors&quot;\n    &quot;log&quot;\n    &quot;net\/http&quot;\n    &quot;os&quot;\n    &quot;os\/signal&quot;\n    &quot;syscall&quot;\n    &quot;time&quot;\n\n    &quot;github.com\/gin-gonic\/gin&quot;\n)\n\nfunc main() {\n    router := gin.Default()\n\n    router.GET(&quot;\/sleep&quot;, func(c *gin.Context) {\n        duration, err := time.ParseDuration(c.Query(&quot;duration&quot;))\n        if err != nil {\n            c.String(http.StatusBadRequest, err.Error())\n            return\n        }\n\n        time.Sleep(duration)\n        c.String(http.StatusOK, &quot;Welcome Gin Server&quot;)\n    })\n\n    srv := &amp;http.Server{\n        Addr:    &quot;:8000&quot;,\n        Handler: router,\n    }\n\n    go func() {\n        \/\/ \u670d\u52a1\u8fde\u63a5\n        if err := srv.ListenAndServe(); err != nil &amp;&amp; !errors.Is(err, http.ErrServerClosed) {\n            \/\/ Error starting or closing listener:\n            log.Fatalf(&quot;HTTP server ListenAndServe: %v&quot;, err)\n        }\n        log.Println(&quot;Stopped serving new connections&quot;)\n    }()\n\n    \/\/ \u7b49\u5f85\u4e2d\u65ad\u4fe1\u53f7\u4ee5\u4f18\u96c5\u5730\u5173\u95ed\u670d\u52a1\u5668\uff08\u8bbe\u7f6e 5 \u79d2\u7684\u8d85\u65f6\u65f6\u95f4\uff09\n    quit := make(chan os.Signal, 1)\n    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM, syscall.SIGQUIT)\n    &lt;-quit\n    log.Println(&quot;Shutdown Server...&quot;)\n\n    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n    defer cancel()\n\n    \/\/ We received an SIGINT\/SIGTERM signal, shut down.\n    if err := srv.Shutdown(ctx); err != nil {\n        \/\/ Error from closing listeners, or context timeout:\n        log.Printf(&quot;HTTP server Shutdown: %v&quot;, err)\n    }\n    log.Println(&quot;HTTP server graceful shutdown completed&quot;)\n}<\/code><\/pre>\n<p>\u53ef\u4ee5\u53d1\u73b0\uff0c\u5728 Gin \u6846\u67b6\u4e2d\u5b9e\u73b0\u4f18\u96c5\u9000\u51fa\u4ee3\u7801\u4e0e\u6211\u4eec\u5728 <code>net\/http<\/code> \u5305\u4e2d\u7684\u5b9e\u73b0\u6ca1\u4ec0\u4e48\u4e0d\u540c\u3002<\/p>\n<p>\u53ea\u4e0d\u8fc7\u6211\u4eec\u5728\u5b9e\u4f8b\u5316 <code>http.Server<\/code> \u5bf9\u8c61\u65f6\uff0c\u5c06 <code>*gin.Engine<\/code> \u4f5c\u4e3a\u4e86 Handler \u590d\u5236\u7ed9 <code>Handler<\/code> \u5c5e\u6027\uff1a<\/p>\n<pre><code class=\"go\">srv := &amp;http.Server{\n    Addr:    &quot;:8000&quot;,\n    Handler: router,\n}<\/code><\/pre>\n<p>\u6267\u884c\u793a\u4f8b\u7a0b\u5e8f\u6d4b\u8bd5\u4f18\u96c5\u9000\u51fa\uff0c\u5f97\u5230\u5982\u4e0b\u8f93\u51fa\uff1a<\/p>\n<pre><code class=\"bash\">$ go build -o main main.go &amp;&amp; .\/main\n[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached.\n\n[GIN-debug] [WARNING] Running in &quot;debug&quot; mode. Switch to &quot;release&quot; mode in production.\n - using env:   export GIN_MODE=release\n - using code:  gin.SetMode(gin.ReleaseMode)\n\n[GIN-debug] GET    \/sleep                    --&gt; main.main.func1 (3 handlers)\n^C2024\/08\/22 09:23:36 Shutdown Server...\n2024\/08\/22 09:23:36 Stopped serving new connections\n[GIN] 2024\/08\/22 - 09:23:39 | 200 |  5.001282167s |       127.0.0.1 | GET      &quot;\/sleep?duration=5s&quot;\n2024\/08\/22 09:23:39 HTTP server graceful shutdown completed<\/code><\/pre>\n<pre><code class=\"bash\">$ curl &quot;http:\/\/localhost:8000\/sleep?duration=5s&quot;\nWelcome Gin Server<\/code><\/pre>\n<p>\u8fd9\u91cc\u540c\u6837\u5728\u5904\u7406\u8bf7\u6c42\u7684\u8fc7\u7a0b\u4e2d\uff0c\u6309\u4e0b <code>Ctrl + C<\/code>\uff0c\u6839\u636e\u65e5\u5fd7\u53ef\u4ee5\u53d1\u73b0\uff0cGin \u793a\u4f8b\u4ee3\u7801\u4e2d\u7684\u4f18\u96c5\u9000\u51fa\u6ca1\u6709\u95ee\u9898\u3002<\/p>\n<p>Gin <a href=\"https:\/\/link.segmentfault.com\/?enc=pUFVvDfTwugA01oWFy4Qkg%3D%3D.Qz3rc30f4QbhpsQfNRutewoJvHxtTs1XejPz0koQHMQPLMib1%2FeC03QgWN2XtPHuUyqiFqUJfaap8pb%2F%2B5j6yEkx7gHa3VPtKXwcWf%2BbV5k%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">\u6846\u67b6\u6587\u6863<\/a> \u4e5f\u63d0\u5230\u4e86\u5728 Go 1.8 \u7248\u672c\u4e4b\u524d\u53ef\u4ee5\u4f7f\u7528\u5982\u4e0b\u51e0\u4e2a\u7b2c\u4e09\u65b9\u5e93\u5b9e\u73b0\u7684\u4f18\u96c5\u9000\u51fa\u66ff\u4ee3\u65b9\u6848\uff1a<\/p>\n<ul>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=Ay7OGn5Xk6MSbgc14gvcmg%3D%3D.8Ee%2Fgbu8XDdaRJ5ZR5VB7vHJ5LOBkN06EBJk8fVWmyy1JMk6NCuYVmtzwMhGk6g4\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">manners<\/a>\uff1a\u53ef\u4ee5\u4f18\u96c5\u5173\u673a\u7684 Go Http \u670d\u52a1\u5668\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=aV1t2NbDEXgahy4%2FmvQ09Q%3D%3D.INHUudUGYORMFGWG8Mf%2F%2FWWihufXXqMnZkb53RE4GE8ljS%2FrfseY%2FIpDlx%2Baz4Du\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">graceful<\/a>\uff1aGraceful \u662f\u4e00\u4e2a Go \u6269\u5c55\u5305\uff0c\u53ef\u4ee5\u4f18\u96c5\u5730\u5173\u95ed http.Handler \u670d\u52a1\u5668\u3002<\/li>\n<li><a href=\"https:\/\/link.segmentfault.com\/?enc=35XsuzXRCsE4tJueLqIg8A%3D%3D.GfAmjNTPKlvmpAm49oSxi0lV21FiBHxCirCOOslumdYXvDMeqSYeJr4g1ey8wrNZ\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">grace<\/a>\uff1aGo \u670d\u52a1\u5668\u5e73\u6ed1\u91cd\u542f\u548c\u96f6\u505c\u673a\u65f6\u95f4\u90e8\u7f72\u3002<\/li>\n<\/ul>\n<p>\u5f53\u7136\u6211\u4eec\u4f7f\u7528\u7684\u662f Go 1.8 \u4ee5\u4e0a\u7248\u672c\uff0c\u53ef\u4ee5\u4e0d\u9700\u8981\u8fd9\u4e9b\u5e93\u3002\u56e0\u4e3a <code>net\/http<\/code> \u5df2\u7ecf\u63d0\u4f9b\u4e86\u539f\u751f\u7684\u4f18\u96c5\u9000\u51fa\u65b9\u6848\uff0c\u6240\u4ee5\u51e0\u4e4e\u7528\u4e0d\u5230\u5b83\u4eec\uff0c\u611f\u5174\u8da3\u7684\u53ef\u4ee5\u81ea\u884c\u7814\u7a76\u4e0b\u3002<\/p>\n<blockquote><p>NOTE:<br \/>\u53ef\u4ee5\u53c2\u9605 Gin \u5b8c\u6574\u7684 <a href=\"https:\/\/link.segmentfault.com\/?enc=Srs0anKvvRaLb3Z1wCzr%2FQ%3D%3D.u9I0gOMsC0eGhZcIDQwo8eOm8P6W7yO%2FtMrf4KWcnBiCoU1gRY4VFTuWo7nywuAoykmfLFD%2B%2BqW%2BfA3OVwlqqS6VpcxOGQkZvTUA1Ylhcis%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">graceful-shutdown<\/a> \u793a\u4f8b\u3002<\/p><\/blockquote>\n<p>\u672a\u5b8c\u5f85\u7eed\u3002<\/p>\n<p><strong>\u5ef6\u4f38\u9605\u8bfb<\/strong><\/p>\n<ul>\n<li>os\/signal Documentation\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=oPPNdCnWbn9mlmXEkQzC2w%3D%3D.DdUYl8uNfbD1j%2FdXPxIBfjesOGJHkk5JwwogPEVj5%2FTQPw101wGQTL5uTtgg58HN\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/pkg.go.dev\/os\/signal@go1.22.0<\/a><\/li>\n<li>os\/signal \u6e90\u7801\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=oF7h10zL8yUc603ZEGKaHA%3D%3D.%2FnMNXOduuAAAz%2FUDy0EWv7B01B9abbazDgizJFI4r5WXDfPKRTqnIKTzczjQ5D2cXAfTmWDBs9wsfpWe75KJGI9VUKft4z7MLKj6V4SxkC8%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/os\/signal\/signal.go<\/a><\/li>\n<li>net\/http Documentation\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=TZnmIdW1HVBfKzT76JiWKg%3D%3D.jinVsc1hwXUbq0pEgEmwa2px%2Bu0C6llscb9WmEEwB5N0KWcWZFUgevwyg1Wxhms9\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/pkg.go.dev\/net\/http@go1.22.0<\/a><\/li>\n<li>net\/http Server Shutdown \u6e90\u7801\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=3TinrjTuMA5OA%2BAw4%2Bu%2F3w%3D%3D.N7wubUPtjzY%2FEw%2FCWoJtQNVOWZ54ToRTK%2FsajxCUk4C7LfZ970YfsnUG2r6n68%2Bg7TvA7mncim%2BO4xnH8a4qTO0YaHdsc1i3Brs%2BNgIghmc%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/golang\/go\/blob\/release-branch.go1.22\/src\/net\/http\/server.go<\/a><\/li>\n<li>Gin Web Framework \u6587\u6863\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=WqILKA57jaMy4OC9vDoCzQ%3D%3D.uyqt%2FL9vPhw50dLink4afpEiraWy%2Bw2y9R69XnnFv4xMuZxlrTkLKDKnJBtuA83w0HgAbPjNkz494nVlEvEfokatZ17JAv%2FZvSb4gkghkHg%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/gin-gonic.com\/zh-cn\/docs\/examples\/graceful-restart-or&#8230;<\/a><\/li>\n<li>Gin graceful-shutdown examples\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=gqe6TvORWxDXN01plb9jYg%3D%3D.HmmbaNlThMmjqkPYGFa0tZ6Zd7Jw8R%2Fhed2jBbStpXUAH8a4qOt7p8iBKzqmcPiGj%2B21uBNpPd%2BAgPSEt34%2F1Nq2RZedFREBYi9zCWd6RE4%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/gin-gonic\/examples\/tree\/master\/graceful-shutdown<\/a><\/li>\n<li>gRPC Quick start\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=t4CkHkVxNotQ591IkwALrQ%3D%3D.O8AQsj3vdc18T48XUmH4CSOAF1lmMbRC0%2BV8RlSQVoNJZ8RCnmT%2Bc76XP4HEeJcL\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/grpc.io\/docs\/languages\/go\/quickstart\/<\/a><\/li>\n<li>gRPC-Go Examples\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=h%2Ftx6ejXyuJ3UNlKwvgp7Q%3D%3D.S1X8ohBrpEoaM1itqICml2y2d3AF3ixMV8Bxy1ZzAEp6F1hQM3hWAwNbROMUTeOdmCUYuLkmWMytEYwTVvqOmw%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/grpc\/grpc-go\/tree\/master\/examples<\/a><\/li>\n<li>gRPC Server GracefulStop \u6e90\u7801\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=JmdFsBjaOt2N3CFZMDaGXw%3D%3D.av9Uy5B2yT5fZJueHXXbOjBNkk2K78uBVXNxrPUd5DAorPFi93USdWeK0ncfd9b94z3YmjT4BzDBpI%2FiRqUfxg%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/grpc\/grpc-go\/blob\/v1.65.0\/server.go<\/a><\/li>\n<li>Kubernetes \u4f18\u96c5\u9000\u51fa\u4fe1\u53f7\u5904\u7406\u6e90\u7801\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=5RvPAMowcCmmRsJspY6qYA%3D%3D.mOOSv7UGO3k475zhjXRfs3dWox%2B4SyW7%2BAzxh9Sd8yo9cSR8ut15DtupdhOcqZN6ET74%2Fpy2XTyL%2BssnMFDk6TOuDXZjndpgFIBr2kQ9Xyo%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/kubernetes\/apiserver\/blob\/release-1.31\/pkg\/server\/signal.go<\/a><\/li>\n<li>Proper HTTP shutdown in Go\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=BylAdnGry2TQItpusdWOGQ%3D%3D.RyBidqB9f%2FJc%2BocwoiL6tMCM9E%2BL1jnrQplnd895%2FDi7%2BlY5JlbHENFXL67isBZ2%2BQoNBkGjCkHeJo5osb6G9w%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/dev.to\/mokiat\/proper-http-shutdown-in-go-3fji<\/a><\/li>\n<li>Golang: graceful shutdown\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=HR%2Br7JTIMQ9TmcNOMR8Zxw%3D%3D.toGgGfCRhN2oHUXvM6%2Ff9xLbSDG4dEpVc6a7FMtOXtIgaTeCKFlR6jYyGmsomX%2F2XU9HVPrtOjah9PgrCIW19A%3D%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/dev.to\/antonkuklin\/golang-graceful-shutdown-3n6d<\/a><\/li>\n<li>How to stop http.ListenAndServe()\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=soR2a04vy8DEjAwX6TWXng%3D%3D.b9XMGtmOIrUJaqe4WywtCJ0I8nMvj3edzxP3JLhMjvwWpk43y2P%2BZwjn2%2FmOk4vJuOTrBYNGLzZnuXw5OvLPgK1669Z974tX6XrFoGTF2Ms%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/stackoverflow.com\/questions\/39320025\/how-to-stop-http-listenandserve<\/a><\/li>\n<li>\u672c\u6587 GitHub \u793a\u4f8b\u4ee3\u7801\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=srfJxeNhfbrrznCGMVgICw%3D%3D.Zhpz%2BLdc%2BYvDEqAKkjeb5%2FxDQ9l%2BllO5vGR2CF0UodrtSxPfPHQNsoAyW70tTqqWdUt%2F4n3NUeiC8Hl3UefbNvVIpxqmSQNBW1XKRMnZj4g%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/github.com\/jianghushinian\/blog-go-example\/tree\/main\/gracefulstop<\/a><\/li>\n<\/ul>\n<p><strong>\u8054\u7cfb\u6211<\/strong><\/p>\n<ul>\n<li>\u516c\u4f17\u53f7\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=S%2BVL5BzIyAFcp98efl9G0w%3D%3D.0wBeDSlClfEvJuVHbYTSQfHftrrLS1iPzMf11BkyrGc%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">Go\u7f16\u7a0b\u4e16\u754c<\/a><\/li>\n<li>\u5fae\u4fe1\uff1ajianghushinian<\/li>\n<li>\u90ae\u7bb1\uff1a<a href=\"https:\/\/segmentfault.com\/a\/mailto:jianghushinian007@outlook.com\" target=\"_blank\" rel=\"noopener noreferrer\">jianghushinian007@outlook.com<\/a><\/li>\n<li>\u535a\u5ba2\uff1a<a href=\"https:\/\/link.segmentfault.com\/?enc=p4mP7qeUiO%2ButLCFlaeDOA%3D%3D.X9aGBtJhjTOtE%2F7JNBrjEZy0Kp2w19gE0W1vCrdTij8%3D\" rel=\"nofollow noopener noreferrer\" target=\"_blank\">https:\/\/jianghushinian.cn<\/a><\/li>\n<\/ul>\n<p><a href=\"https:\/\/segmentfault.com\/a\/1190000045217706\">https:\/\/segmentfault.com\/a\/1190000045217706<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u672c\u6587\u5e26\u5927\u5bb6\u4e00\u8d77\u6765\u8be6\u7ec6\u5b66\u4e60\u4e0b Go \u4e2d\u7684\u4f18\u96c5\u9000\u51fa\uff0c\u7531\u4e8e\u6587\u7ae0\u8fc7\u957f\uff0c\u62c6\u5206\u6210\u4e0a\u4e0b\u4e24\u7bc7\uff0c\u672c\u6587\u4e3a\u4e0a\u7bc7\u3002 \u5728\u5199 Go \u7a0b\u5e8f\u65f6 [&hellip;]<\/p>\n","protected":false},"author":7,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[4],"tags":[],"class_list":["post-10020408","post","type-post","status-publish","format-standard","hentry","category-share"],"jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10020408","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/users\/7"}],"replies":[{"embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=10020408"}],"version-history":[{"count":0,"href":"https:\/\/itmax.cn\/index.php?rest_route=\/wp\/v2\/posts\/10020408\/revisions"}],"wp:attachment":[{"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=10020408"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=10020408"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/itmax.cn\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=10020408"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}