github.com/graybobo/golang.org-package-offline-cache@v0.0.0-20200626051047-6608995c132f/x/blog/content/context.article (about)

     1  Go Concurrency Patterns: Context
     2  29 Jul 2014
     3  Tags: concurrency, cancelation, cancellation, context
     4  
     5  Sameer Ajmani
     6  
     7  * Introduction
     8  
     9  In Go servers, each incoming request is handled in its own goroutine.
    10  Request handlers often start additional goroutines to access backends such as
    11  databases and RPC services.
    12  The set of goroutines working on a request typically needs access to
    13  request-specific values such as the identity of the end user, authorization
    14  tokens, and the request's deadline.
    15  When a request is canceled or times out, all the goroutines working on that
    16  request should exit quickly so the system can reclaim any resources they are
    17  using.
    18  
    19  At Google, we developed a `context` package that makes it easy to pass
    20  request-scoped values, cancelation signals, and deadlines across API boundaries
    21  to all the goroutines involved in handling a request.
    22  The package is publicly available as
    23  [[http://godoc.org/golang.org/x/net/context][golang.org/x/net/context]].
    24  This article describes how to use the package and provides a complete working
    25  example.
    26  
    27  * Context
    28  
    29  The core of the `context` package is the `Context` type:
    30  
    31  .code context/interface.go /A Context/,/^}/
    32  
    33  (This description is condensed; the
    34  [[http://godoc.org/golang.org/x/net/context][godoc]] is authoritative.)
    35  
    36  The `Done` method returns a channel that acts as a cancelation signal to
    37  functions running on behalf of the `Context`: when the channel is closed, the
    38  functions should abandon their work and return.
    39  The `Err` method returns an error indicating why the `Context` was canceled.
    40  The [[/pipelines][Pipelines and Cancelation]] article discusses the `Done`
    41  channel idiom in more detail.
    42  
    43  A `Context` does _not_ have a `Cancel` method for the same reason the `Done`
    44  channel is receive-only: the function receiving a cancelation signal is usually
    45  not the one that sends the signal.
    46  In particular, when a parent operation starts goroutines for sub-operations,
    47  those sub-operations should not be able to cancel the parent.
    48  Instead, the `WithCancel` function (described below) provides a way to cancel a
    49  new `Context` value.
    50  
    51  A `Context` is safe for simultaneous use by multiple goroutines.
    52  Code can pass a single `Context` to any number of goroutines and cancel that
    53  `Context` to signal all of them.
    54  
    55  The `Deadline` method allows functions to determine whether they should start
    56  work at all; if too little time is left, it may not be worthwhile.
    57  Code may also use a deadline to set timeouts for I/O operations.
    58  
    59  `Value` allows a `Context` to carry request-scoped data.
    60  That data must be safe for simultaneous use by multiple goroutines.
    61  
    62  ** Derived contexts
    63  
    64  The `context` package provides functions to _derive_ new `Context` values from
    65  existing ones.
    66  These values form a tree: when a `Context` is canceled, all `Contexts` derived
    67  from it are also canceled.
    68  
    69  `Background` is the root of any `Context` tree; it is never canceled:
    70  
    71  .code context/interface.go /Background returns/,/func Background/
    72  
    73  `WithCancel` and `WithTimeout` return derived `Context` values that can be
    74  canceled sooner than the parent `Context`.
    75  The `Context` associated with an incoming request is typically canceled when the
    76  request handler returns.
    77  `WithCancel` is also useful for canceling redundant requests when using multiple
    78  replicas.
    79  `WithTimeout` is useful for setting a deadline on requests to backend servers:
    80  
    81  .code context/interface.go /WithCancel/,/func WithTimeout/
    82  
    83  `WithValue` provides a way to associate request-scoped values with a `Context`:
    84  
    85  .code context/interface.go /WithValue/,/func WithValue/
    86  
    87  The best way to see how to use the `context` package is through a worked
    88  example.
    89  
    90  * Example: Google Web Search
    91  
    92  Our example is an HTTP server that handles URLs like
    93  `/search?q=golang&timeout=1s` by forwarding the query "golang" to the
    94  [[https://developers.google.com/web-search/docs/][Google Web Search API]] and
    95  rendering the results.
    96  The `timeout` parameter tells the server to cancel the request after that
    97  duration elapses.
    98  
    99  The code is split across three packages:
   100  
   101  - [[context/server/server.go][server]] provides the `main` function and the handler for `/search`.
   102  - [[context/userip/userip.go][userip]] provides functions for extracting a user IP address from a request and associating it with a `Context`.
   103  - [[context/google/google.go][google]] provides the `Search` function for sending a query to Google.
   104  
   105  ** The server program
   106  
   107  The [[context/server/server.go][server]] program handles requests like
   108  `/search?q=golang` by serving the first few Google search results for `golang`.
   109  It registers `handleSearch` to handle the `/search` endpoint.
   110  The handler creates an initial `Context` called `ctx` and arranges for it to be
   111  canceled when the handler returns.
   112  If the request includes the `timeout` URL parameter, the `Context` is canceled
   113  automatically when the timeout elapses:
   114  
   115  .code context/server/server.go /func handleSearch/,/defer cancel/
   116  
   117  The handler extracts the query from the request and extracts the client's IP
   118  address by calling on the `userip` package.
   119  The client's IP address is needed for backend requests, so `handleSearch`
   120  attaches it to `ctx`:
   121  
   122  .code context/server/server.go /Check the search query/,/userip.NewContext/
   123  
   124  The handler calls `google.Search` with `ctx` and the `query`:
   125  
   126  .code context/server/server.go /Run the Google search/,/elapsed/
   127  
   128  If the search succeeds, the handler renders the results:
   129  
   130  .code context/server/server.go /resultsTemplate/,/}$/
   131  
   132  ** Package userip
   133  
   134  The [[context/userip/userip.go][userip]] package provides functions for
   135  extracting a user IP address from a request and associating it with a `Context`.
   136  A `Context` provides a key-value mapping, where the keys and values are both of
   137  type `interface{}`.
   138  Key types must support equality, and values must be safe for simultaneous use by
   139  multiple goroutines.
   140  Packages like `userip` hide the details of this mapping and provide
   141  strongly-typed access to a specific `Context` value.
   142  
   143  To avoid key collisions, `userip` defines an unexported type `key` and uses
   144  a value of this type as the context key:
   145  
   146  .code context/userip/userip.go /The key type/,/const userIPKey/
   147  
   148  `FromRequest` extracts a `userIP` value from an `http.Request`:
   149  
   150  .code context/userip/userip.go /func FromRequest/,/}/
   151  
   152  `NewContext` returns a new `Context` that carries a provided `userIP` value:
   153  
   154  .code context/userip/userip.go /func NewContext/,/}/
   155  
   156  `FromContext` extracts a `userIP` from a `Context`:
   157  
   158  .code context/userip/userip.go /func FromContext/,/}/
   159  
   160  ** Package google
   161  
   162  The [[context/google/google.go][google.Search]] function makes an HTTP request
   163  to the [[https://developers.google.com/web-search/docs/][Google Web Search API]]
   164  and parses the JSON-encoded result.
   165  It accepts a `Context` parameter `ctx` and returns immediately if `ctx.Done` is
   166  closed while the request is in flight.
   167  
   168  The Google Web Search API request includes the search query and the user IP as
   169  query parameters:
   170  
   171  .code context/google/google.go /func Search/,/q.Encode/
   172  
   173  `Search` uses a helper function, `httpDo`, to issue the HTTP request and cancel
   174  it if `ctx.Done` is closed while the request or response is being processed.
   175  `Search` passes a closure to `httpDo` handle the HTTP response:
   176  
   177  .code context/google/google.go /var results/,/return results/
   178  
   179  The `httpDo` function runs the HTTP request and processes its response in a new
   180  goroutine.
   181  It cancels the request if `ctx.Done` is closed before the goroutine exits:
   182  
   183  .code context/google/google.go /func httpDo/,/^}/
   184  
   185  * Adapting code for Contexts
   186  
   187  Many server frameworks provide packages and types for carrying request-scoped
   188  values.
   189  We can define new implementations of the `Context` interface to bridge between
   190  code using existing frameworks and code that expects a `Context` parameter.
   191  
   192  For example, Gorilla's
   193  [[http://www.gorillatoolkit.org/pkg/context][github.com/gorilla/context]]
   194  package allows handlers to associate data with incoming requests by providing a
   195  mapping from HTTP requests to key-value pairs.
   196  In [[context/gorilla/gorilla.go][gorilla.go]], we provide a `Context`
   197  implementation whose `Value` method returns the values associated with a
   198  specific HTTP request in the Gorilla package.
   199  
   200  Other packages have provided cancelation support similar to `Context`.
   201  For example, [[http://godoc.org/gopkg.in/tomb.v2][Tomb]] provides a `Kill`
   202  method that signals cancelation by closing a `Dying` channel.
   203  `Tomb` also provides methods to wait for those goroutines to exit, similar to
   204  `sync.WaitGroup`.
   205  In [[context/tomb/tomb.go][tomb.go]], we provide a `Context` implementation that
   206  is canceled when either its parent `Context` is canceled or a provided `Tomb` is
   207  killed.
   208  
   209  * Conclusion
   210  
   211  At Google, we require that Go programmers pass a `Context` parameter as the
   212  first argument to every function on the call path between incoming and outgoing
   213  requests.
   214  This allows Go code developed by many different teams to interoperate well.
   215  It provides simple control over timeouts and cancelation and ensures that
   216  critical values like security credentials transit Go programs properly.
   217  
   218  Server frameworks that want to build on `Context` should provide implementations
   219  of `Context` to bridge between their packages and those that expect a `Context`
   220  parameter.
   221  Their client libraries would then accept a `Context` from the calling code.
   222  By establishing a common interface for request-scoped data and cancelation,
   223  `Context` makes it easier for package developers to share code for creating
   224  scalable services.