douhu8851 2015-02-12 19:39
浏览 130
已采纳

传入请求的上下文

From time to time I am faced with the "Context" concept which, as a rule is created for all incoming requests. Recently I've read the Go blog article that describes using the golang.org/x/net/context package. However, after playing with the code and trying to reproduce the logic of the article, I still hardly understand how to use it for every incoming request and even why it is useful for this.

How should I organize my code to create context (and what should it contain, generally) for every incoming request using the golang.org/x/net/context package? Could anybody give a little example and explain what is so useful and why so frequently used?

  • 写回答

2条回答 默认 最新

  • douchiwan1503 2015-02-13 18:38
    关注

    One of the most common needs for context passing is correlating outgoing requests to incoming requests. I have used this for a variety of purposes, for example:

    • I want error logs for my database component to include the full url from the http request it is a result of.
    • Incoming http requests contain a set of headers that I need to preserve and pass on to other http services I call downstream (maybe for tracking reasons).
    • I want to examine the incoming http request in some other component to do access control or user authentication or whatever. This could be at the http handler layer, or some other part of my application.

    Many languages and platforms have convenient/magical ways to get the current Http request. C# has HttpRequest.Current which is globally available (via thread local storage) to anyone who wants to know the context of the current http request. You can set arbitrary data on it to communicate various context data. Other platforms have similar facilities.

    Since go has no facilities for goroutine local storage, there is no way to store a global variable in the context of the current http request. Instead, it is idiomatic to initialize the context at the boundary of your system (an incoming request), and pass it as an argument to any downstream components that need access to that information.

    One super simple way to do this would be to make a context object with the current http request and pass that around:

    func someHandler(w http.ResponseWriter, r * http.Request){
       ctx := context.WithValue(context.Background(),"request",r)
       myDatabase.doSomething(ctx,....)
    }
    

    You can of course limit it to a more targeted set of data you need to pass around rather than the entire request.

    The other thing that the context package helps with (and I think that blog does an ok job of pointing out), is a common framework for timeouts or deadlines.

    Note that the context package does not enforce timeouts for you. It is up to the components receiving a context object to watch the Done channel and self-cancel their own http request or database call or calculation or whatever.

    edit - on timeouts

    It is extremely useful to be able to manage timeouts from the outside of a component. If I have a database module, I don't need to hardcode timeout values, just be able to handle a timeout triggered from the outside.

    One way I have done this is in a service that makes multiple db / service calls per incoming request. If the total time exceeds 1 second, I want to abort all outbound operations and return a partial or error result. Initializing the context with a timeout at the top level and passing it to all dependencies is a really easy way to manage this.

    It is not always pretty for the dependency to listen to the Done channel and abort it's work, but as the blog shows, it is not terribly painful either.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥50 silvaco GaN HEMT有栅极场板的击穿电压仿真问题
  • ¥15 谁会P4语言啊,我想请教一下
  • ¥20 win11无法启动 持续蓝屏且系统还原失败,无法开启系统保护
  • ¥15 哪个tomcat中startup一直一闪而过 找不出问题
  • ¥15 这个怎么改成直流激励源给加热电阻提供5a电流呀
  • ¥50 求解vmware的网络模式问题 别拿AI回答
  • ¥24 EFS加密后,在同一台电脑解密出错,证书界面找不到对应指纹的证书,未备份证书,求在原电脑解密的方法,可行即采纳
  • ¥15 springboot 3.0 实现Security 6.x版本集成
  • ¥15 PHP-8.1 镜像无法用dockerfile里的CMD命令启动 只能进入容器启动,如何解决?(操作系统-ubuntu)
  • ¥30 请帮我解决一下下面六个代码