<p>Hi!</p>
<p>This package helps you manage shutdown code centrally, and provides functionality to execute code when a controlled shutdown occurs.</p>
<p>This will enable you to save data, notify other services that your application is shutting down.</p>
<ul>
<li><a href="https://github.com/klauspost/shutdown">Project page</a></li>
<li><a href="https://godoc.org/github.com/klauspost/shutdown">Godoc</a></li>
</ul>
<p>Managing shutdowns can be very tricky, often leading to races, crashes and strange behavior. This package will help you manage the shutdown process and will attempt to fix some of the common problems when dealing with shutting down.</p>
<p>The shutdown package allow you to block shutdown while certain parts of your code is running. This is helpful to ensure that operations are not interrupted.</p>
<p>The second part of the shutdown process is notifying goroutines waiting in a select loop and calling functions in your code that handles various shutdown procedures, like closing databases, notifying other servers, deleting temporary files, etc.</p>
<p>See more information and examples on the project page. As always, feedback and comments are appreciated.</p>
<p>/Klaus</p>
<hr/>**评论:**<br/><br/>[deleted]: <pre><p>Hi Klaus,</p>
<p>I like the look of the API so far so I took the liberty of forking this and will look some more into it.
<del>Just in passing though, are you sure panicking is really necessary for SetTimeoutN ?</del>
edit: nevermind, it's fine here I think.</p></pre>klauspost: <pre><p>You raise a good point, since I considered a lot of options for such a "simple" operation.</p>
<p>1) Current implementation (panic): You get an instant error as a user. You <em>should</em> set timeouts on startup, so any error is instantly seen. But it depends on using the library correct, which I don't like.</p>
<p>2) Return an error. Seems overly complicated to use such a simple function.</p>
<p>3) Use an "type Stage int": This will sort of work, but will make calls a bit more complex shutdown.SetTimeoutN(timeout.FirstStage, time.Second). It makes the call overly complex, and doesn't really protect against users casting in a for loop anyway.</p>
<p>4) Add Functions for each Timeout stage, eg. SetTimeoutFirst(), SetTimeoutSecond(). It is safe, but makes it impossible to do a for loop.</p>
<p>5) Ignore invalid values. A big no-no for me, since it will hide usage errors.</p>
<p>I am still undecided if option 1 was the correct one. The only real alternative I see is option 4.</p></pre>jerf: <pre><p>If you're going to commit to having a certain predefined set of stages, then consider one of these two alternatives. In real code the const will have to come first, but the harshest one:</p>
<pre><code>type stage byte
const (
Preshutdown = stage(0)
Stage1 = stage(1)
Stage2 = stage(2)
Stage3 = stage(3)
)
</code></pre>
<p>Now the only way to have a legal stage value is to use one of those. The downside is that you can't store a "stage" anywhere, because the type is not exported. You can use <code>:=</code> to assign it in a function, but that's it. This is sometimes suitable for flag-like things, and your case is at least borderline.</p>
<p>The other option:</p>
<pre><code>type Stage struct {
stage byte
}
var Preshutdown = Stage{0}
var Stage1 = Stage{1}
var Stage2 = Stage{2}
var Stage3 = Stage{3}
</code></pre>
<p>Again, the only way for an external package to obtain a legal Stage value is by using the four values you've provided. This also has the advantage that external consumers can now store things with the exported "Stage" type. But you can't stop anyone from doing "Stage1 = Stage2" in their code. Though the Go solution to that problem is for the consumers not to do that.</p>
<p>With either of these choices, it becomes impossible for an external user to call the function with an illegal value. (Internally you can still screw up, but, Don't Do That.)</p>
<p>At this point, if you are feeling particularly saucy...</p>
<pre><code>func (s Stage) SetTimeout(d time.Duration) {
...
}
</code></pre>
<p>It looks like it would clean up your code to have</p>
<pre><code>func (s Stage) Notifier() Notifier { ... }
func (s Stage) SetFunc(f ShutdownFn, v interface{})
</code></pre>
<p>too... very much a code smell to have First, Second, and Third appearing like that. "There are only three numbers in computer programming... 0, 1, and <em>n</em>." It is likely that you'll find a lot of other places where you've got a <code>[3]</code> really ought to be properties on the stage, rather than global arrays. </p>
<pre><code>type Stage struct {
shutdownQueue []Notify
shutdownFnQueue []Notify
timeout time.Duration
// ....
}
</code></pre>
<p>If the Stage type starts storing all those values, you can then start handing out <code>*Stage</code>s instead. And then once you do that you may discover that your code <em>further</em> collapses and you can almost more easily offer users arbitrary numbers of "stages" that get tied together via a <code>next *Stage</code> or something, rather than a hard-coded 3. Behold OO.</p>
<p>(One more thing pre-emptively, not that you've said anything but I've encountered it before and find it a bit aggravating... with all due respect, to a first approximation you currently have zero external users. Wait until you have some to worry about "backwards compatibility".)</p></pre>klauspost: <pre><p>Thanks a lot for taking the time. I think you made a persuasive argument for the var types. I have changed the library to reflect that. </p>
<p>It was great getting rid of the panic.</p>
<p>The choice to go for a maximum of three stages is long-considered and on limiting on purpose.</p>
<p>The aim was to get rid of what I call the "z-order" issue. This refers to CSS, where you can specify a "z-order" property on html element, that is used to determine the layering order of HTML element.</p>
<p>The ability to specify any number in time leads to pure chaos on a page. Some people go for 0, 100, 200 so they can insert stuff later, and then use '99999' because "it must <em>always</em> be on top". But somehow, there is always a need for a '1000000' because this one element needs to be just one higher.... and so on.</p>
<p>By limiting the design to "only" three stages enable you to clearly make design choices, and force you to run as many things as possible in parallel. With this you can write simple design docs (webserver example):</p>
<ul>
<li>Preshutdown: Finish accepted requests, refuse new ones.</li>
<li>Stage 1: Notify clients, flush data to database, notify upstream servers we are offline.</li>
<li>Stage 2: Flush database bulk writers, messages, close databases. (no database writes)</li>
<li>Stage 3: Flush/close log/metrics writer. (no log writes)</li>
</ul>
<p>With arbitrary stage ordering you will quickly loose the overview of the ordering. If the database package needs the package logger to shut down after itself - how should it indicate that in a simple concise way?</p>
<p>Don't think of the 3-stages as something that must do all stages of your shutdown. A single function call can of course (and is intended to) contain several "substages". Shutting down the database can easily be several stages, but you only register a single stage in the shutdown manager. The important part is that nothing else in <em>the same stage</em> can use the database.</p>
<p>My intention is that this makes the shutdown process easier to manage, and encourage more concurrency, because you don't create a long daisy-chain of events, and doesn't force you to look through all your code to insert a single event correctly.</p></pre>jerf: <pre><blockquote>
<p>The choice to go for a maximum of three stages is long-considered and on limiting on purpose.</p>
</blockquote>
<p>No problem. Go's pretty opinionated about this sort of thing in general so it fits right in. I just wanted to make sure it was on purpose and not due to incidental implementations issues. Happy Gophering!</p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传