duanbin198788 2016-10-05 01:05
浏览 48
已采纳

http.Handle包装模式->堆栈会膨胀吗?

I am doing a first production webservice in go, so I am quite new to the language and some concepts/patterns.

My question is related to handlers and essentially how to pull out duplicated code without degrading performance.

I have come accross the pattern to wrap either http.Handle or http.HandlerFunc to clean up code. For example this blog post here using an adapter pattern https://medium.com/@matryer/writing-middleware-in-golang-and-how-go-makes-it-so-much-fun-4375c1246e81#.hvsc236iv

It might end up with something like this (copied from the blob post):

  1. http.Handle("/", Adapt(indexHandler, AddHeader("Server", "Mine"),
  2. CheckAuth(providers),
  3. CopyMgoSession(db),
  4. Notify(logger),
  5. )

which is basically a deeply nested function call.

The question I have is what happens in the stack and the performance of the service? With this pattern, every single user request will add at least 5 stack frames to the stack. Is that acceptable or will it have a negative effect on performance when traffic is high?

  • 写回答

1条回答 默认 最新

  • douti0467 2016-10-05 01:45
    关注

    Chaining middlewares is basically just making the handlers of the chain call the next one, often based on a condition whether everything went well. Or in another approach some external mechanism may call handlers one-by-one.

    However, all things come down to that the handlers will be called. The Handler.ServeHTTP() method looks like this:

    type Handler interface {
        ServeHTTP(ResponseWriter, *Request)
    }
    

    A simple method with 2 parameters and no return values. The parameters are of type http.ResponseWriter (an interface type) and *http.Request (a pointer type).

    So a call to a handler's ServeHTTP() involves 2 things: making a copy of its arguments - which is fast since they are small, and actually making the call (taking care of stack update like create a new stack frame, record return address, save used registers, and execute the called function) - which is also very fast (see quote at the end of the answer).

    So should you worry about calling functions? No. Will this be less performant compared to a handler which contains everything? Yes. Is the difference significant? No. Serving an HTTP request could take hundreds of milliseconds (network latency included). Calling 10 functions in your handler will not make it noticeably slower.

    If you'd worry about the performance loss due to function calls, then your app would consist of one single main() function. Obviously nobody wants that. You create functions to break down your initially large problem to smaller ones (recursively until it is "small enough" to be on its own) which you can oversee and reuse and test independently from others, and you assemble your large problem from the smaller ones. It's not really a question of performance but maintainability and reusability. Would you really want to copy that 100-line code which checks the user's identity to all your 10 different handlers?

    One last thing. Should you be concerned about "consuming" the stack (resulting in a stack overflow error)? The answer is no. A goroutine starts with a small 4096 byte stack which grows and shrinks as needed without the risk of ever running out. Read more about it at Why is a Goroutine’s stack infinite? Also detailed at FAQ: Why goroutines instead of threads?

    To make the stacks small, Go's run-time uses resizable, bounded stacks. A newly minted goroutine is given a few kilobytes, which is almost always enough. When it isn't, the run-time grows (and shrinks) the memory for storing the stack automatically, allowing many goroutines to live in a modest amount of memory. The CPU overhead averages about three cheap instructions per function call.

    展开全部

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
编辑
预览

报告相同问题?

手机看
程序员都在用的中文IT技术交流社区

程序员都在用的中文IT技术交流社区

专业的中文 IT 技术社区,与千万技术人共成长

专业的中文 IT 技术社区,与千万技术人共成长

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

关注【CSDN】视频号,行业资讯、技术分享精彩不断,直播好礼送不停!

客服 返回
顶部