<p>I've been implementing a few handlers in golang, but I don't like the fact that I have to wrap all the handlers one by one like below</p>
<pre><code> mid1(mid2(mid3(app)))
</code></pre>
<p>Is not that bad, but if I want to remove or add one middleware, it's getting pretty bad..
I've heard about negroni that seems to do the work and is full net/http compatible, but I wanna hear what people are using here;)</p>
<hr/>**评论:**<br/><br/>TheMerovius: <pre><p>I don't get it.</p>
<pre><code>h := app
h = mid3(h)
h = mid2(h)
h = mid1(h)
http.ListenAndServe("/", h)
</code></pre>
<p>Now, adding or removing a middleware is as simple as it can get, delete a line, add a line…</p>
<p>I don't see, how using a slice helps. Or how a package could possibly help…</p></pre>daveddev: <pre><p>The benefit is in reusing chains on multiple endpoints and enabling flexible composition of chains.</p>
<p><a href="https://github.com/codemodus/chain" rel="nofollow">https://github.com/codemodus/chain</a></p>
<p>Edit to add:
Please note the TODO - <a href="https://play.golang.org/p/CLsIHRp9Ft" rel="nofollow">https://play.golang.org/p/CLsIHRp9Ft</a></p>
<p>Using a library like "chain" keeps nested handler composition clean and free while remaining simple and low-cost (sub-microsecond).</p>
<p>Edit again to add:
The same code as above using chain - <a href="https://play.golang.org/p/qdigQNJqyn" rel="nofollow">https://play.golang.org/p/qdigQNJqyn</a></p></pre>TheMerovius: <pre><p>Okay, cool, I can <em>kind of</em> see, why people would see a benefit [edit: But to be clear: That's not <em>really</em> what OP asked for]. Though it probably wouldn't be worth an external dependency for the same value as</p>
<pre><code>func Chain(end http.Handler, middle ...func(http.Handler) http.Handler) http.Handler {
for _, m := range middle{
end = m(end)
}
return end
}
</code></pre>
<blockquote>
<p>low-cost (sub-microsecond)</p>
</blockquote>
<p>Routing performance does not matter. Estimation time:</p>
<p>From the benchmarks <a href="https://github.com/julienschmidt/go-http-routing-benchmark" rel="nofollow">here</a> it seems a pretty safe bet, that tens of μs is a reasonable baseline for somewhat naive routing - even on relatively sizable APIs.</p>
<p><a href="http://searchengineland.com/google-now-handles-2-999-trillion-searches-per-year-250247" rel="nofollow">Google handles on the order of trillions of searches</a> each year. If we assume something like each search actually making 10 HTTP-queries or something, we are talking 30 trillion queries a year, that's, conveniently, roughly a million QPS. Each query spends 10μs in routing, so that's 10 CPU seconds per second on routing. so Google search dedicates roughly 10 cores total to routing HTTP queries, if they use something relatively naive. 10 cores on <a href="https://cloud.google.com/compute/pricing" rel="nofollow">GCP</a> cost roughly $250 a month (the actual cost will be <em>much lower</em>, because CPUs become much cheaper at that scale), which at an average salary of $80K/y for a SWE in the US (and given that the <em>cost</em> of a SWE is probably at least twice or three times their base salary) comes down to something like three engineering hours or so.</p>
<p>So yes. If you are running at the scale of Google, it might be worth optimizing your HTTP-router. For one engineer, spending <em>maybe</em> one or two days a year doing it.</p>
<p>So stop worrying about a microsecond.</p>
<p>(that being said: I don't think there is an actual performance penalty of the naive, obvious approach over that "chain" package… But <em>who cares, because routing performance does not matter</em>)</p></pre>daveddev: <pre><p>My post clarified the benefits of using a slice for managing nested http handlers and also noted that the penalty of doing so was low. No particular state of concern compelled which details were shared. Further, while tools like chain are often used with routers, I'm unsure about what it has to do with multiplexing.</p>
<p>The external dependency is certainly worth avoiding; As mentioned in the chain project's readme:</p>
<blockquote>
<p>Nesting functions is a simple concept. If your nested handler order does not need to be composable, please do not use this or any similar package and avoid adding a dependency to your project.</p>
</blockquote></pre>TheMerovius: <pre><blockquote>
<p>Further, while tools like chain are often used with routers, I'm unsure about what it has to do with multiplexing.</p>
</blockquote>
<p>You just mentioned the cost of "nested handler composition" (really, I subsumed that into "routing"), and you specifically mentioned it being sub μs. Like it mattered.</p>
<p>The go community needs to realize that it doesn't (whether it's about "multiplexing" or "handler composition"), so that the proliferation of "high performance whatever frameworks" in the http-space stops. Getting your HTTP-request from <code>net/http</code> to your actual handler is going to be negligible. Stop talking about the cost of it like it is a thing. It's just not.</p></pre>dlsniper: <pre><p>Put them in a slice and run thru the slice as an alternative :)</p></pre>gogroob: <pre><p>Here's my suggestion. Create your own helper to do this. </p>
<p>I just got finished doing the same for a few shared projects I have:</p>
<p><a href="https://github.com/micromdm/go4/blob/e55c5245fedcdd8462e931c1cb2447f092b4456d/httputil/middleware.go#L12-L27" rel="nofollow">https://github.com/micromdm/go4/blob/e55c5245fedcdd8462e931c1cb2447f092b4456d/httputil/middleware.go#L12-L27</a></p>
<p>First, I defined a Middleware function type like so:</p>
<pre><code>// Middleware is a chainable decorator for HTTP Handlers.
type Middleware func(http.Handler) http.Handler
</code></pre>
<p>Next, I created the helper function which lets me do <code>mid1(mid2(mid3(app)))</code></p>
<pre><code>// Chain is a helper function for composing middlewares. Requests will
// traverse them in the order they're declared. That is, the first middleware
// is treated as the outermost middleware.
//
// Chain is identical to the go-kit helper for Endpoint Middleware.
func Chain(outer Middleware, others ...Middleware) Middleware {
return func(next http.Handler) http.Handler {
for i := len(others) - 1; i >= 0; i-- { // reverse
next = others[i](next)
}
return outer(next)
}
}
</code></pre>
<p>Now I can chain a set of handlers. See example here: <a href="https://github.com/micromdm/go4/blob/e55c5245fedcdd8462e931c1cb2447f092b4456d/httputil/middleware_example_test.go#L11-L29" rel="nofollow">https://github.com/micromdm/go4/blob/e55c5245fedcdd8462e931c1cb2447f092b4456d/httputil/middleware_example_test.go#L11-L29</a></p></pre>zemirco: <pre><p><a href="https://github.com/justinas/alice" rel="nofollow">https://github.com/justinas/alice</a> is pretty nice.</p></pre>jimijiim: <pre><p>In my opinion middleware and routing shouldn't be orthogonal. A router should support middlewares ( or filters which is a better name for it ).</p></pre>zdebra1: <pre><p>Use Adapter (Decorator) pattern.</p>
<p>Great article on that:
<a href="https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81" rel="nofollow">https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81</a></p></pre>
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889
- 请尽量让自己的回复能够对别人有帮助
- 支持 Markdown 格式, **粗体**、~~删除线~~、
`单行代码`
- 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
- 图片支持拖拽、截图粘贴等方式上传