Go实战--Gorilla web toolkit使用之gorilla/context

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wangshubo1989/article/details/78910935

感慨:
小说《人间失格》
保温杯,枸杞
中兴程序员跳楼
朴树演到“情千缕,酒一杯,声声离笛催”时的哽咽
《芳华》,芳华已逝,面目全非
……
哎,生活不易。

生命不止,继续 go go go ~~~

接下来打算跟大家分享一系列Gorilla web toolkit。
这里写图片描述

gorilla是用golang写的web工具箱,里面提供了一系列的工具。
在用golang开发web中,搭配gorilla可以加快整个开发的进程。

官网:
http://www.gorillatoolkit.org/

代码:
https://github.com/gorilla/

首先,介绍的是gorilla/context
在Go1.7之前,Go标准库还没有内置Context。但是现在有了,所以通过标准库对比进行学习。

Package context

Package context defines the Context type, which carries deadlines, cancelation signals, and other request-scoped values across API boundaries and between processes.

Context 结构体

type Context interface {
        Deadline() (deadline time.Time, ok bool)

        Done() <-chan struct{}

        Err() error

        Value(key interface{}) interface{}
}

Deadline(),返回截止时间和ok。
Done(),返回一个channel。当times out或者调用cancel方法时,将会close掉。
Err(),返回一个错误。该context为什么被取消掉。
Value(),返回值。

所有方法

func Background() Context
func TODO() Context

func WithCancel(parent Context) (ctx Context, cancel CancelFunc)
func WithDeadline(parent Context, deadline time.Time) (Context, CancelFunc)
func WithTimeout(parent Context, timeout time.Duration) (Context, CancelFunc)
func WithValue(parent Context, key, val interface{}) Context

context.TODO should be used context.TODO when it’s unclear which Context to use.

context.Background is typically used by the main function, initialization, and tests, and as the top-level Context for incoming requests.
Both are never canceled, have no values, and has no deadline.

WithCancel 对应的是 cancelCtx ,其中,返回一个 cancelCtx ,同时返回一个 CancelFunc,CancelFunc 是 context 包中定义的一个函数类型:type CancelFunc func()。调用这个 CancelFunc 时,关闭对应的c.done,也就是让他的后代goroutine退出

WithDeadline 和 WithTimeout 对应的是 timerCtx ,WithDeadline 和 WithTimeout 是相似的,WithDeadline 是设置具体的 deadline 时间,到达 deadline 的时候,后代 goroutine 退出,而 WithTimeout 简单粗暴,直接 return WithDeadline(parent, time.Now().Add(timeout))

WithValue 对应 valueCtx ,WithValue 是在 Context 中设置一个 map,拿到这个 Context 以及它的后代的 goroutine 都可以拿到 map 里的值

WithCancel使用示例

package main

import (
    "context"
    "fmt"
)

func main() {

    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()

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

输出:
1
2
3
4
5

WithDeadline使用示例

package main

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

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

    defer cancel()

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

}

输出:
context deadline exceeded

WithTimeout使用示例

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"
    }

}

输出:
context deadline exceeded

WithValue使用示例

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"))

}

输出:
found value: Go
key not found: color

HTTP请求中使用context

func Do(ctx context.Context, client *http.Client, req *http.Request)
func Get(ctx context.Context, client *http.Client, url string)
func Head(ctx context.Context, client *http.Client, url string)
func PostForm(ctx context.Context, client *http.Client, url string, data url.Values)

说白了就是context作为函数的第一个参数:

package main

import "fmt"
import "context"

// A message processes parameter and returns the result on responseChan.
// ctx is places in a struct, but this is ok to do.
type message struct {
    responseChan chan<- int
    parameter    string
    ctx          context.Context
}

func ProcessMessages(work <-chan message) {
    for job := range work {
        select {
        // If the context is finished, don't bother processing the
        // message.
        case <-job.ctx.Done():
            continue
        default:
        }
        // Assume this takes a long time to calculate
        hardToCalculate := len(job.parameter)
        select {
        case <-job.ctx.Done():
        case job.responseChan <- hardToCalculate:
        }
    }
}

func newRequest(ctx context.Context, input string, q chan<- message) {
    r := make(chan int)
    select {
    // If the context finishes before we can send msg onto q,
    // exit early
    case <-ctx.Done():
        fmt.Println("Context ended before q could see message")
        return
    case q <- message{
        responseChan: r,
        parameter:    input,
        // We are placing a context in a struct.  This is ok since it
        // is only stored as a passed message and we want q to know
        // when it can discard this message
        ctx: ctx,
    }:
    }

    select {
    case out := <-r:
        fmt.Printf("The len of %s is %d\n", input, out)
    // If the context finishes before we could get the result, exit early
    case <-ctx.Done():
        fmt.Println("Context ended before q could process message")
    }
}

func main() {
    q := make(chan message)
    go ProcessMessages(q)
    ctx := context.Background()
    newRequest(ctx, "hi", q)
    newRequest(ctx, "hello", q)
    close(q)
}

输出:
The len of hi is 2
The len of hello is 5

遵循规则
遵循以下规则,以保持包之间的接口一致,并启用静态分析工具以检查上下文传播。
不要将 Contexts 放入结构体,相反context应该作为第一个参数传入,命名为ctx。 func DoSomething(ctx context.Context,arg Arg)error { // … use ctx … }
即使函数允许,也不要传入nil的 Context。如果不知道用哪种 Context,可以使用context.TODO()。
使用context的Value相关方法只应该用于在程序和接口中传递的和请求相关的元数据,不要用它来传递一些可选的参数
相同的 Context 可以传递给在不同的goroutine;Context 是并发安全的

gorilla/context

获取:

go get github.com/gorilla/context

API

++func Clear(r *http.Request)++

Clear removes all values stored for a given request.

This is usually called by a handler wrapper to clean up request variables at the end of a request lifetime. See ClearHandler().

++func ClearHandler(h http.Handler) http.Handler++

ClearHandler wraps an http.Handler and clears request values at the end of a request lifetime.

++func Delete(r *http.Request, key interface{})++

Delete removes a value stored for a given key in a given request.

++func Get(r *http.Request, key interface{}) interface{}++

Get returns a value stored for a given key in a given request.

++func GetAll(r *http.Request) map[interface{}]interface{}++

GetAll returns all stored values for the request as a map. Nil is returned for invalid requests.

++func GetAllOk(r *http.Request) (map[interface{}]interface{}, bool)++

GetAllOk returns all stored values for the request as a map and a boolean value that indicates if the request was registered.

++func GetOk(r *http.Request, key interface{}) (interface{}, bool)++

GetOk returns stored value and presence state like multi-value return of map access.

++func Purge(maxAge int) int++

Purge removes request data stored for longer than maxAge, in seconds. It returns the amount of requests removed.

If maxAge <= 0, all request data is removed.

This is only used for sanity check: in case context cleaning was not properly set some request data can be kept forever, consuming an increasing amount of memory. In case this is detected, Purge() must be called periodically until the problem is fixed.

++func Set(r *http.Request, key, val interface{})++

Set stores a value for a given key in a given request.

示例:

type contextString string

const ConfigKey contextString = "context"

func SetConfigHandler(w http.ResponseWriter, r *http.Request) {
    var config Config
    //...Getting the config data from request...
   context.Set(r, ConfigKey, config)
 }

func GetClicksHandler(w http.ResponseWriter, r *http.Request) {
    config := context.Get(r, ConfigKey).(Config)
    if config.TrackClicks{
        WriteNumberClicks(w)
    }
}

存取值

package main

import (
    "net/http"
    "strconv"

    "github.com/gorilla/context"
)

func main() {

    http.Handle("/", http.HandlerFunc(myHander))
    http.ListenAndServe(":8080", nil)
}

func myHander(rw http.ResponseWriter, r *http.Request) {
    context.Set(r, "user", "SuperWang")
    context.Set(r, "age", 66)
    doHander(rw, r)
}
func doHander(rw http.ResponseWriter, r *http.Request) {
    user := context.Get(r, "user").(string)
    age := context.Get(r, "age").(int)
    rw.WriteHeader(http.StatusOK)
    rw.Write([]byte("the user is " + user + ", age is " + strconv.Itoa(age)))
}

gorilla/mux与gorilla/context结合使用

package main

 import (
         "fmt"
         "github.com/gorilla/context"
         "github.com/gorilla/mux"
         "net/http"
 )

 type Key string

 const GlobalRequestVariable Key = ""

 func SetGlobalHandler(w http.ResponseWriter, r *http.Request) {
         context.Set(r, GlobalRequestVariable, "test")

         get, ok := context.GetOk(r, GlobalRequestVariable)
         w.Write([]byte(fmt.Sprintf("GetOK : [%v] and get what :[%v] ", ok, get)))

         InternalGetGlobalHandler(w, r)

 }

 func InternalGetGlobalHandler(w http.ResponseWriter, r *http.Request) {

         get, ok := context.GetOk(r, GlobalRequestVariable)
         w.Write([]byte(fmt.Sprintf("\nInternal GetOK : [%v] and get what :[%v] ", ok, get)))

 }

 func GetGlobalHandler(w http.ResponseWriter, r *http.Request) {

         get, ok := context.GetOk(r, GlobalRequestVariable)
         w.Write([]byte(fmt.Sprintf("GetOK : [%v] and get what :[%v] ", ok, get)))

 }

 func main() {
         mx := mux.NewRouter()

         mx.HandleFunc("/", SetGlobalHandler)
         mx.HandleFunc("/get", GetGlobalHandler)

         http.ListenAndServe(":8080", mx)
 }

猜你喜欢

转载自blog.csdn.net/wangshubo1989/article/details/78910935
今日推荐