Go(1)

前言

开个go的坑,填坑时间不定。


环境搭建

系统:docker+Ubuntu18.04镜像

安装golang:

1
2
apt-get update
apt-get install golang

在Ubuntu18.04下即可安装1.10版本的golang,如果需要最新版本的可以去https://golang.org/dl/(需要翻墙),或者直接wget下载,然后解压、建立工作目录并配置环境变量:

1
2
3
4
5
6
7
8
9
10
cd /opt
wget https://dl.google.com/go/go1.12.1.linux-amd64.tar.gz
tar zxvf go1.12.1.linux-amd64.tar.gz
cd
mkdir -p golang/src golang/bin
echo "export GOROOT=/opt/go" >> .bashrc
echo "export GOPATH=/root/golang" >> .bashrc
source .bashrc
echo "export PATH=$GOPATH/bin:$GOROOT/bin:$PATH" >> .bashrc
source .bashrc

然后export一下即可看到:

1
2
3
declare -x GOPATH="/root/golang"
declare -x GOROOT="/opt/go"
declare -x PATH="/root/golang/bin:/opt/go/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"

最后运行go version,看到golang版本:

1
go version go1.12.1 linux/amd64

安装完成。


Hello World && Go Web 服务器

在GOPATH/src下新建一个目录hello_word,然后新建go源代码文件,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import (
"fmt"
"net/http"
)

func handler(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Hello World, %s!", request.URL.Path[1:])
}

func main() {
http.HandleFunc("/index", handler)
http.ListenAndServe(":80", nil)
}

很简单的逻辑,服务端监听80端口,然后把对/index的访问请求都交给handler来处理,而handler则获取我们的请求URL,然后格式化进Hello World字符串中作为响应,我们编译运行:

1
2
go install hello_world
$GOPATH/bin/hello_world

浏览器打开即可看到效果。


多路复用 && 更多的Server配置

我们还可以通过Server结构体对服务器进行更多的配置,写法如下:

1
2
3
4
server := http.Server {
Addr: "0.0.0.0:80",
}
server.ListenAndServe()

Server结构的配置选项如下:

1
2
3
4
5
6
7
8
9
10
11
type Server struct {
Addr string
Handler Handler
ReadTimeout time.Duration
WriteTimeout time.Duration
MaxHeaderBytes int
TLSConfig *tls.Config
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnStats)
ErrorLog *log.Logger
}

Handler参数的默认值为DefaultServeMux多路复用处理器,它是ServeMux结构(多路复用器)的一个实例,具备处理器的特征(ServeHTTP方法),它的作用是根据请求的URL的不同,将请求交付不同的处理器来进行处理,如果我们将它覆盖为一个普通的处理器,无论我们访问什么地址,服务器返回的响应都是相同的。利用多路复用器,我们就可以通过配置http.HandleFunc或者http.Handle来根据请求地址分发请求到不同的处理器。


处理器 && 处理器函数

处理器:

1
2
3
4
5
6
7
8
type IndexHandler struct{}
func (h *IndexHandler) ServeHTTP (writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Index")
}
type LoginHandler struct{}
func (h *LoginHandler) ServeHTTP (writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Login")
}

路由配置:

1
2
3
4
index := IndexHandler{}
login := LoginHandler{}
http.Handle("/index", &index)
http.Handle("/login", &login)

处理器函数:

1
2
3
4
5
6
func index(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Index")
}
func login(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Login")
}

路由配置:

1
2
http.HandleFunc("/index", index)
http.HandleFunc("/login", login)

处理器是一个拥有ServeHTTP方法的接口,它被用来处理HTTP请求,而处理器函数则是与处理器拥有相同行为的函数,也是用来创建处理器的一种便利的方法,当我们调用HandlerFunc函数来注册路由的时候,HandlerFunc就会将处理器函数转换成处理器,然后将它与DefaultServeMux进行绑定。

串联处理器函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package main

import (
"fmt"
"net/http"
"reflect"
"runtime"
)

func index(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Index")
}
func login(writer http.ResponseWriter, request *http.Request) {
fmt.Fprintf(writer, "Login")
}

func log(h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
name := runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
fmt.Println(name)
h(w, r)
}
}

func main() {
server := http.Server {
Addr: "0.0.0.0:80",
}
http.HandleFunc("/index", log(index))
http.HandleFunc("/login", log(login))
server.ListenAndServe()
}

runtime.FuncForPC的作用是在程序运行时获取函数信息,reflect.ValueOf的作用是获取函数的副本,Go的处理器函数调用相当于一种先hook住处理器函数index,先执行完log函数代码之后再继续执行它的感觉,跟一般的函数调用不太一样。

串联处理器跟串联处理器函数十分相似,只要修改一下类型就好了:

1
2
3
4
5
func log(h http.Handler) http.Handler {
return http.HandlerFunc (func(w http.ResponseWriter, r *http.Request) {
h.ServeHTTP(w, r)
})
}

使用第三方多路复用器

DefaultServe的缺陷在于访问/index/123之类的地址的时候,无法获取GET请求的参数。为了弥补这个缺陷,我们可以使用julienschmidt/httprouter,一个高效的轻量级第三方多路复用器,安装:

1
2
apt-get install git
go get github.com/julienschmidt/httprouter

Go会使用git从GitHub上下载源代码,然后储存到$GOPATH/src目录下,我们还需要把它从github.com文件夹中取出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package main

import (
"fmt"
"net/http"
"julienschmidt/httprouter"
)

func index(writer http.ResponseWriter, request *http.Request, p httprouter.Params) {
fmt.Fprintf(writer, "Index, %s!", p.ByName("p"))
}
func login(writer http.ResponseWriter, request *http.Request, p httprouter.Params) {
fmt.Fprintf(writer, "Login, %s!", p.ByName("id"))
}

func main() {
mux := httprouter.New()
mux.GET("/index:p", index)
mux.GET("/login/:id", login)
server := http.Server {
Addr: "0.0.0.0:80",
Handler: mux,
}
server.ListenAndServe()
}

HTTP2 && HTTPS

不整了,这个就靠反向代理。


Orz