小白学标准库之 mux

Posted 乱舞春秋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了小白学标准库之 mux相关的知识,希望对你有一定的参考价值。


本文介绍第三方库 gorilla/mux,相比于 Go 自带的 net/http 它能提供更为强大的路由处理功能。

mux 表示 HTTP request multiplexer (HTTP 请求多路复用器),它通过路由器(这里的路由器不是 TCP/IP 中的路由器)实现类 mux.Router 匹配用户请求,并将请求转发到系统注册的路由规则,由相应的规则对请求进行处理。

1. gorilla/mux 使用

首先使用 go get 导入 mux 包:

go get -u github.com/gorilla/mux

实现 mux server:

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

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

        r.HandleFunc("/books/list", ListHandler)
        r.HandleFunc("/books/{quality}/{price}", RecordHandler).Methods("GET")

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

func ListHandler(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "hello, books...")
}

func RecordHandler(w http.ResponseWriter, r *http.Request) {
        vars := mux.Vars(r)

        quality := vars["quality"]
        price := vars["price"]

        w.WriteHeader(http.StatusOK)
        fmt.Fprintf(w, "books quality: %s, price: %s\\n", quality, price)
}

代码实现上:

  • 调用 mux 包的 NewRouter 构造一个路由器,通过路由器的 HandleFunc 方法建立请求和路由规则的关联。
  • server 端通过 http 包的 ListenAndServe 函数实现了路由器在地址的侦听。访问该地址的 URL 将被转发到路由器中相应的路由规则,并由路由规则处理 URL 请求。
  • mux 包的 Vars 函数解析路由参数。

查看程序实现效果。
server:

[chunqiu@104 mux]$ go run muxUpdater.go

[chunqiu@104 mux]$ netstat -antp | grep 8080
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp6       0      0 :::8080                 :::*                    LISTEN      35739/muxUpdater
tcp6       0      0 ::1:36798               ::1:8080                TIME_WAIT   -

client:

[chunqiu@105 mux]$ curl http://10.159.***.***:8080/books
hello, books...

[chunqiui@105 mux]$ curl http://localhost:8080/books
hello, books...

2. gorilla/mux 实现

简单介绍 mux 包中请求多路复用路由的实现。

2.1 mux.NewRouter

路由器中包括路由切片,切片存储路由规则:

// This will send all incoming requests to the router.
type Router struct {
	// Routes to be matched, in order.
	routes []*Route

	// Routes by name for URL building.
	namedRoutes map[string]*Route
}

2.2 HandlerFunc

...
r.NewRoute().Path(path).HandlerFunc(f)

写不下去了.....

芝兰生于空谷,不以无人而不芳。

小白学标准库之 flag


Go 提供了解析命令行参数的 flag 包,本文旨在介绍 flag 的使用及内部实现等。

1. flag 包使用及实现

type PropertyOfPod struct {
	Namespace *string
	PodName   *string
	Phase     *string
}

var pod = PropertyOfPod{}

func init() {
    // String defines a string flag with specified name, default value, and usage string.
    // The return value is the address of a string variable that stores the value of the flag.

	pod.Namespace = flag.String("namespace", "default", "resource field for pod")
	pod.PodName = flag.String("name", "", "pod name")

	pod.Phase = new(string)

    // StringVar defines a string flag with specified name, default value, and usage string.
    // The argument p points to a string variable in which to store the value of the flag.

	flag.StringVar(pod.Phase, "phase", "running", "pod phase")
}

func main() {
	flag.Parse()

	fmt.Printf("pod property:\\nnamespace: %v, pod name: %v, phase: %v\\n", *pod.Namespace, *pod.PodName, *pod.Phase)
}

调用 flag 包的 String 函数定义 flag。示例中,通过 String 和 StringVar 两种方式定义 flag。

具体定义 flag 的 String 函数做了什么呢?接着往下看 String 函数:

func String(name string, value string, usage string) *string {
	return CommandLine.String(name, value, usage)
}

String 通过实例化类 CommandLine 调用 String 方法。其中 CommandLine 的类结构体定义为:

var CommandLine = NewFlagSet(os.Args[0], ExitOnError)

type FlagSet struct {
	Usage func()

	name          string                                // 每个 FlagSet 有唯一的 name
	parsed        bool                                  // parsed 标志记录 FlagSet 是否解析命令行参数
	actual        map[string]*Flag                      // 最终记录的实际可用标志
	formal        map[string]*Flag                      // 标志信息,未“过滤”
	args          []string // arguments after flags     // 命令行传入参数 os.Args[1:] 写入到 args 中
	errorHandling ErrorHandling
	output        io.Writer // nil means stderr; use Output() accessor
}

FlagSet 的 String 方法又做了什么呢?接着往下看:

func (f *FlagSet) String(name string, value string, usage string) *string {
	p := new(string)
	f.StringVar(p, name, value, usage)
	return p
}

func (f *FlagSet) StringVar(p *string, name string, value string, usage string) {
	f.Var(newStringValue(value, p), name, usage)
}

在 String 方法内创建了临时指针变量 p, p 的值被传入到 StringVar 函数。这和示例直接调用 StringVar 是一样的,在 newStringValue 函数中,p 将指向 value 的地址:

// -- string Value
type stringValue string

func newStringValue(val string, p *string) *stringValue {
	*p = val
	return (*stringValue)(p)
}

最后调用 Var 方法实现 flag 的定义,Var 方法的第一个参数值得一说,它接受的是接口类型 Value 的值,为什么接受接口类型是因为这里需要多态实现不仅定义 String flag,也能定义 Int,Bool 等类型的 flag。

Var 方法将外部传入参数定义到 Flag 结构体中,并作为值赋给 formal:

func (f *FlagSet) Var(value Value, name string, usage string) {
	flag := &Flag{name, usage, value, value.String()}
	_, alreadythere := f.formal[name]
	if alreadythere {
		var msg string
		if f.name == "" {
			msg = fmt.Sprintf("flag redefined: %s", name)
		} else {
			msg = fmt.Sprintf("%s flag redefined: %s", f.name, name)
		}
		fmt.Fprintln(f.Output(), msg)
		panic(msg) // Happens only if flags are declared with identical names
	}
	if f.formal == nil {
		f.formal = make(map[string]*Flag)
	}
	f.formal[name] = flag
}

定义了 flag 之后,还需要 parse 对传入的 flag 进行解析,实例中调用 flag 的 Parse 函数实现解析:

func Parse() {
	// Ignore errors; CommandLine is set for ExitOnError.
	CommandLine.Parse(os.Args[1:])
}

func (f *FlagSet) Parse(arguments []string) error {
	f.parsed = true                 // parse 时将 parsed 标志记为 true
	f.args = arguments
	for {
		seen, err := f.parseOne()
		if seen {
			continue
		}
		if err == nil {
			break
		}
		...
	}
	return nil
}

其中,parseOne 方法解析 args 参数中的 flag,并将解析的 flag 赋给 map actual。

2. flag 方法

2.1 Visit

// Visit visits the command-line flags in lexicographical order, calling fn
// for each. It visits only those flags that have been set.

flag.Visit(func(f *flag.Flag) {
		key := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
		Flag_visit[key] = string(f.Value.String())
	})
	fmt.Printf("Flag_visit: %v\\n", Flag_visit)

2.2 VisitAll

// VisitAll visits the command-line flags in lexicographical order, calling
// fn for each. It visits all flags, even those not set.

flag.VisitAll(func(f *flag.Flag) {
		key := strings.ToUpper(strings.ReplaceAll(f.Name, "-", "_"))
		Flag_visit[key] = string(f.Value.String())
	})
	fmt.Printf("Flag_visit: %v\\n", Flag_visit)
芝兰生于空谷,不以无人而不芳。

以上是关于小白学标准库之 mux的主要内容,如果未能解决你的问题,请参考以下文章

小白学标准库之 log

python标准库 —— os模块

Python常用标准库之fileinput

Python常用标准库之fileinput

python标准库之sys模块 学习

是否可以在 Google appengine 标准环境中使用 Gorilla mux 路由器?