[Switch] Go concurrent modes: Context

[Switch] Go concurrent modes: Context

  • tips: yesterday saw snow ruthless articles on Context, and have a preliminary understanding of go in Context. Today saw a go official blog introduction to the Context. Prepare yourself translation I found online translation, and I do not do a duplication of effort, reprint it, and indicate the source. Thanks for sharing translator.

In go the server, each incoming request for further processing are in their goroutine in. request handlers often start other goroutines to access back-end services such as databases and rpc. A common set of typical service request to access a specific request value goroutines, e.g. deadline as an end user, and requests authorization token. When the request is canceled or trigger a timeout, all goroutine work on that request should exit quickly, so any system resources used can be recycled.

Inside google, we developed a packet context, api can easily cross the border, passing the request range of values, and the cancel signal goroutine request deadline for all involved. The package is open is called context . This article describes how to use the package and provides a complete working example.

context

context package is the core of context type (described here is streamlined, details can be found godoc ):

// a context carries a deadline, cancelation signal, and request-scoped values
// across api boundaries. its methods are safe for simultaneous use by multiple
// goroutines.
type Context interface {
    // done returns a channel that is closed when this context is canceled
    // or times out.
    Done() <-chan struct{}

    // err indicates why this context was canceled, after the done channel
    // is closed.
    Err() error

    // deadline returns the time when this context will be canceled, if any.
    Deadline() (deadline time.time, ok bool)

    // value returns the value associated with key or nil if none.
    Value(key interface{}) interface{}
}

Done method returns a channel, for transmitting a cancellation signal (representing closed Context) to the runtime function: When the channel is closed, and the function returns subsequent procedure should be abandoned. Err method returns an error indicating why the context is canceled. Pipes and cancel article discusses idioms done channel in more detail.

Done method returns a channel, for transmitting a cancellation signal (representing closed Context) to the runtime function: When the channel is closed, and the function returns subsequent procedure should be abandoned. Err method returns an error indicating why the context is canceled. Pipes and cancel article discusses idioms done channel in more detail.

Due to Done channel receive-only, / Context / methods do not cancel: cancel the function of the received signal should not normally have the function of transmitting a signal. In particular, when the operation promoter goroutines parent operation, these sub-operation should not be able to cancel parent operation. Instead, WithCancel function (described below) provides a method to cancel the new Context value.

Context can be safely used for a plurality of simultaneously goroutines. Code may be transmitted to a single Context goroutine any number, and can transmit a signal to cancel the Context goroutine all associated.

Deadline method allows the function to determine whether it should start work; if too little time left, might not be worth it. Code may also used to manipulate the deadline set timeout I / O.

Context Value allows the transmission request data. This data must be able to simultaneously secure a plurality goroutine.

Context derivatives

context / package provides a new function from the prior Context / Context derived. The Context form a tree hierarchy: when a Context is canceled, it is derived from all the Context is also canceled.

Background Context is any root of the tree; it will never be canceled:

// Background returns an empty Context. It is never canceled, has no deadline,
// and has no values. Background is typically used in main, init, and tests,
// and as the top-level Context for incoming requests.
func Background() Context

WithCancel and WithTimeout return derived Context, derived earlier than the parent sub-Context Context can be canceled. Request context associated with the incoming request handler is typically canceled return. WithCancel also be used to cancel a request when multiple redundant copies. WithTimeout to set the back-end server request deadline is useful:

// WithCancel returns a copy of parent whose Done channel is closed as soon as
 // parent.Done is closed or cancel is called.
 func WithCancel(parent Context) (ctx Context, cancel CancelFunc)

 // A CancelFunc cancels a Context.
 type CancelFunc func()

 // WithTimeout returns a copy of parent whose Done channel is closed as soon as
 // parent.Done is closed, cancel is called, or timeout elapses. The new
 // Context's Deadline is the sooner of now+timeout and the parent's deadline, if
 // any. If the timer is still running, the cancel function releases its
 // resources.
 func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)


// WithDeadline returns a copy of the parent context with the deadline adjusted
// to be no later than d. If the parent's deadline is already earlier than d,
// WithDeadline(parent, d) is semantically equivalent to parent. The returned
// context's Done channel is closed when the deadline expires, when the returned
// cancel function is called, or when the parent context's Done channel is
// closed, whichever happens first.
//
// Canceling this context releases resources associated with it, so code should
// call cancel as soon as the operations running in this Context complete.
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)

WithValue is provided a method for the Context value associated with the requested range:

// WithValue returns a copy of parent whose Value method returns val for key.
func WithValue(parent Context, key interface{}, val interface{}) Context

NOTE: use the context of the Value associated method should be used only metadata associated with the request and delivery procedures in the interface, do not use it to pass some optional parameters;

The best way to grasp how to use the context of the package is a true and complete by example.

Context simple example of use

Simple example, easier to understand Context derivative function of each applicable scenarios, and edit this document using Org-mode, in the process of editing, can be executed people (for org-mode interested, you can contact in the comments I). Here is the code, derived from the context of godoc.

WithCancel

WithCancel example, demonstrates how to use context to cancel goroutine to prevent leakage. The end of the example of a function, activated by the gen goroutine returns without leakage transmission.

package main

import (
  "context"
  "fmt"
)

func main() {
  // gen generates integers in a separate goroutine and
  // sends them to the returned channel.
  // The callers of gen need to cancel the context once
  // they are done consuming generated integers not to leak
  // the internal goroutine started by gen.
  gen := func(ctx context.Context) <-chan int {
    dst := make(chan int)
    n := 1
    go func() {
      for {
    select {
    case <-ctx.Done():
      return // returning not to leak the goroutine
    case dst <- n:
      n++
    }
      }
    }()
    return dst
  }

  ctx, cancel := context.WithCancel(context.Background())
  defer cancel() // cancel when we are finished consuming integers

  for n := range gen(ctx) {
    fmt.Println(n)
    if n == 5 {
      break
    }
  }
}

WithDeadline

WithDeadline example, to tell a function of blocking a deadline by Context, once he got to the final deadline, to give up its work

package main

import (
  "context"
  "fmt"
  "time"
)

func main() {
  d := time.Now().Add(50 * time.Millisecond)
  ctx, cancel := context.WithDeadline(context.Background(), d)

  // Even though ctx will be expired, it is good practice to call its
  // cancelation function in any case. Failure to do so may keep the
  // context and its parent alive longer than necessary.
  defer cancel()

  select {
  case <-time.After(1 * time.Second):
    fmt.Println("overslept")
  case <-ctx.Done():
    fmt.Println(ctx.Err())
  }

}

Withtimeount

WithTimeount example, transmission timeout Context has to inform the blocking function, it will drop its work after the timeout.

package main

import (
  "context"
  "fmt"
  "time"
)

func main() {
  // Pass a context with a timeout to tell a blocking function that it
  // should abandon its work after the timeout elapses.
  ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
  defer cancel()

  select {
  case <-time.After(1 * time.Second):
    fmt.Println("overslept")
  case <-ctx.Done():
    fmt.Println(ctx.Err()) // prints "context deadline exceeded"
  }

}

WithValue

WithValue simple example code:

package main

import (
  "context"
  "fmt"
)

func main() {
  type favContextKey string

  f := func(ctx context.Context, k favContextKey) {
    if v := ctx.Value(k); v != nil {
      fmt.Println("found value:", v)
      return
    }
    fmt.Println("key not found:", k)
  }

  k := favContextKey("language")
  ctx := context.WithValue(context.Background(), k, "Go")

  f(ctx, k)
  f(ctx, favContextKey("color"))

}

Example is a HTTP server, by forwarding the query "golang" to Google Web Search API and render query results, to deal with "/ search? Q = golang & timeout = 1s" URL like. timeout parameter tells the server to cancel the request after this time has elapsed.

The sample code is split into three packages:

It provides a main function server and "/ search" handler.
userip provides functions to extract the ip address associated with the user and a request from the Context.
google Google's Search function provides the search field sent.

server

Server to process the request "search? Q = golang" such as by providing the first few Google search results golang. It registers / handleSearch to deal with "search". Creating a handler / Context ctx is called, and when the handler returns, and a cancel. If the request URL contains a timeout parameter, the context will be automatically canceled when the timeout:

func handleSearch(w http.ResponseWriter, req *http.Request) {
  // ctx is the Context for this handler. Calling cancel closes the
  // ctx.Done channel, which is the cancellation signal for requests
  // started by this handler.
  var (
    ctx    context.Context
    cancel context.CancelFunc
  )
  timeout, err := time.ParseDuration(req.FormValue("timeout"))
  if err == nil {
    // The request has a timeout, so create a context that is
    // canceled automatically when the timeout expires.
    ctx, cancel = context.WithTimeout(context.Background(), timeout)
  } else {
    ctx, cancel = context.WithCancel(context.Background())
  }
  defer cancel() // Cancel ctx as soon as handleSearch returns.
}

Processing program to extract keywords from the query request in, and to extract the IP address of the client by calling userip package. Rear request requires the client's IP address, so handleSearch attach it to the ctx:

// Check the search query.
query := req.FormValue("q")
if query == "" {
  http.Error(w, "no query", http.StatusBadRequest)
  return
}

// Store the user IP in ctx for use by code in other packages.
userIP, err := userip.FromRequest(req)
if err != nil {
  http.Error(w, err.Error(), http.StatusBadRequest)
  return
}
ctx = userip.NewContext(ctx, userIP)

Ctx handler uses keywords and queries call google.Search:

// Run the Google search and print the results.
start := time.Now()
results, err := google.Search(ctx, query)
elapsed := time.Since(start)
if err != nil {
  http.Error(w, err.Error(), http.StatusInternalServerError)
  return
}

If the search is successful, the handler will render returns the result:

if err := resultsTemplate.Execute(w, struct {
  Results          google.Results
  Timeout, Elapsed time.Duration
}{
  Results: results,
  Timeout: timeout,
  Elapsed: elapsed,
}); err != nil {
  log.Print(err)
  return
}

userip

userip package provides the user an IP address extracted from the request and the function associated with the Context. Providing Context map key-value mapping, wherein both the key and value type interface {}. key type must support equality, value must be a multiple goroutine safe. userip such a package will hide the details of the map and provides strongly typed access to specific Context value.

In order to avoid keyword conflicts, userip defines the type of a key is not exported, and use the value of this type as a keyword in Context:

// The key type is unexported to prevent collisions with context keys defined in
// other packages.
type key int

// userIPkey is the context key for the user IP address.  Its value of zero is
// arbitrary.  If this package defined other context keys, they would have
// different integer values.
const userIPKey key = 0

Extracting a value from FromRequest userIP http.Request of:

func FromRequest(req *http.Request) (net.IP, error) {
  ip, _, err := net.SplitHostPort(req.RemoteAddr)
  if err != nil {
    return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
  }

  userIP := net.ParseIP(ip)
  if userIP == nil {
    return nil, fmt.Errorf("userip: %q is not IP:port", req.RemoteAddr)
  }
  return userIP, nil
}

NewContext returns with a new Context of userIP:

func NewContext(ctx context.Context, userIP net.IP) context.Context {
    return context.WithValue(ctx, userIPKey, userIP)
}

FromContext userIP extracted from the Context:

func FromContext(ctx context.Context) (net.IP, bool) {
    // ctx.Value returns nil if ctx has no value for the key;
    // the net.IP type assertion returns ok=false for nil.
    userIP, ok := ctx.Value(userIPKey).(net.IP)
    return userIP, ok
}

google

google.Search function makes an HTTP request to the Google Web Search API, and parsing JSON encoded result. It accepts parameters Context ctx, and returns immediately upon ctx.Done closed.

Google Web Search API requests, including search queries and user IP as a query parameter:

func Search(ctx context.Context, query string) (Results, error) {
    // Prepare the Google Search API request.
    req, err := http.NewRequest("GET", "https://ajax.googleapis.com/ajax/services/search/web?v=1.0", nil)
    if err != nil {
    return nil, err
    }
    q := req.URL.Query()
    q.Set("q", query)

    // If ctx is carrying the user IP address, forward it to the server.
    // Google APIs use the user IP to distinguish server-initiated requests
    // from end-user requests.
    if userIP, ok := userip.FromContext(ctx); ok {
    q.Set("userip", userIP.String())
    }
    req.URL.RawQuery = q.Encode()

    // Issue the HTTP request and handle the response.

}

Search httpDo using a helper function to issue an HTTP request, if the closed ctx.Done while processing a request or response, cancellation httpDo. Search will pass close to handle HTTP response packet to httpDo:

var results Results
err = httpDo(ctx, req, func(resp *http.Response, err error) error {
  if err != nil {
    return err
  }
  defer resp.Body.Close()

  // Parse the JSON search result.
  // https://developers.google.com/web-search/docs/#fonje
  var data struct {
    ResponseData struct {
      Results []struct {
    TitleNoFormatting string
    URL               string
      }
    }
  }
  if err := json.NewDecoder(resp.Body).Decode(&data); err != nil {
    return err
  }
  for _, res := range data.ResponseData.Results {
    results = append(results, Result{Title: res.TitleNoFormatting, URL: res.URL})
  }
  return nil
})
// httpDo waits for the closure we provided to return, so it's safe to
// read results here.
return results, err

httpDo function initiates an HTTP request and response process at the new goroutine. If you turn off ctx.Done before goroutine exit, it will cancel the request:

func httpDo(ctx context.Context, req *http.Request, f func(*http.Response, error) error) error {
    // Run the HTTP request in a goroutine and pass the response to f.
    tr := &http.Transport{}
    client := &http.Client{Transport: tr}
    c := make(chan error, 1)
    go func() { c <- f(client.Do(req)) }()
    select {
    case <-ctx.Done():
    tr.CancelRequest(req)
    <-c // Wait for f to return.
    return ctx.Err()
    case err := <-c:
    return err
    }
}

Context adaptation to existing code

Many types of server framework for bearer request packet and range of values. Context can be defined a new implementation of the interface, so that the existing code and the desired frame is adapted Context parameter.

For example, Gorilla github.com/gorilla/context package allows the processing program from the HTTP request by providing data to be associated with the incoming request to the key mapping. In gorilla.go, there is provided a Context realize that method returns Value values ​​associated with a particular packet Gorilla HTTP requests.

Other packages provide similar support to cancel the Context. For example, Tomb provides a method of killing, to cancel the signal emitted by closing the death channel. Tomb also provides a method goroutine waiting to exit, similar to sync.WaitGroup. In tomb.go, the realization of a Context, Context was canceled when his father offered Tomb or be killed, the Context is canceled.

to sum up

At Google, we ask programmers Go through the Context incoming and outgoing parameters as the first parameter of each function on the call path between the request. This allows many different Go codes developed by the team were good interoperability. It provides simple control of overtime and canceled, and to ensure the safety certificate and other key value is correct transfer Go program.

Built on the desired Context Context server framework should provide an implementation of the package so that between them and the desired adaptation parameter between a packet Context. The client library will accept Context from the calling code. By a request to establish a range of data and cancel common interface, Context enables developers to more readily scalable shared code is used to create services.

Guess you like

Origin www.cnblogs.com/ishenghuo/p/11119905.html