duanbin198788 2016-10-05 09: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):

http.Handle("/", Adapt(indexHandler, AddHeader("Server", "Mine"),
                                 CheckAuth(providers),
                                 CopyMgoSession(db),
                                 Notify(logger), 
                               )

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 09: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.

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

报告相同问题?

悬赏问题

  • ¥15 想问一下树莓派接上显示屏后出现如图所示画面,是什么问题导致的
  • ¥100 嵌入式系统基于PIC16F882和热敏电阻的数字温度计
  • ¥15 cmd cl 0x000007b
  • ¥20 BAPI_PR_CHANGE how to add account assignment information for service line
  • ¥500 火焰左右视图、视差(基于双目相机)
  • ¥100 set_link_state
  • ¥15 虚幻5 UE美术毛发渲染
  • ¥15 CVRP 图论 物流运输优化
  • ¥15 Tableau online 嵌入ppt失败
  • ¥100 支付宝网页转账系统不识别账号