1. 怎么启动Web服务?

Go语言标准库内置的net/http包,可以实现HTTP服务端。实现HTTP服务端就是能够启动Web服务,相当于搭建起了一个Web服务器。
http.ListenAndServer()函数用来启动Web服务,绑定并监听http端口。

func ListenAndServe(addr string, handler Handler)
// addr:监听地址;如 :8080 或者0.0.0.0:8080
// handler:HTTP处理器Handler

2.启动Web服务的几种方式

根据不同服务返回的handler,常见启动Web服务有以下几种方式。

2.1 http.FileServer: 静态文件服务

http.FileServer()搭建的服务器只提供静态文件的访问。因为这种web服务只支持静态文件访问,所以称之为静态文件服务。

1.使用示例

文件目录如下:

image-20201228174249803
package main
import (
"net/http"
)
func main() {
runFileServer()
}
// 启动一个文件服务器
func runFileServer() {
// 如果改路径下有index.html,则会优先显示index.html,否则会看到文件目录
http.ListenAndServe(":3000",http.FileServer(http.Dir("./public/")))
}

注意: 启动的时候,需求使用go run main.go。否则会报404

2.2 http.HandleFunc: 默认的多路由分发服务

http.HandleFunc()的作用是注册网络访问的路由。因为它采用的是默认的路由分发任务方式,所以称之为默认的多路由分发服务。

func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
// pattern:请求路径的匹配模式
// handler:函数类型,表示这个请求需要处理的事情,其实就是Handler接口中的ServeHTTP()方法。

ServeHTTP()方法有两个参数,其中第一个参数是ResponseWriter类型,包含了服务器端给客户端的响应数据。服务器端往ResponseWriter写入了什么内容,浏览器的网页源码就是什么内容。第二个参数是一个 *Request指针,包含了客户端发送给服务器端的请求信息(路径、浏览器类型等)

1.使用示例
package main
import (
"fmt"
"net/http"
)
func main() {
// 绑定路由/hello
http.HandleFunc("/hello",helloHandle)
// 绑定路由到/test
http.HandleFunc("/test",testHandle)
// 启动服务
err := http.ListenAndServe(":5000",nil)
fmt.Println(err)
}
// 处理路由hello
func helloHandle(w http.ResponseWriter, r *http.Request) {
fmt.Println("访问路由hello")
// 解析url参数
fmt.Println(r.URL.Query())
w.Write([]byte("hello word!"))
}
// 处理路由test
func testHandle(w http.ResponseWriter, r *http.Request) {
fmt.Println("访问路由test")
// 解析url参数,并输出
fmt.Println(r.URL.Query())
w.Write([]byte("test doing!"))
}
// 访问: http://127.0.0.1:5000/test?a=1001
// 访问: http://127.0.0.1:5000/hello?b=990

通过http. HandleFunc()注册网络路由时,http.ListenAndServer()的第二个参数通常为nil,这意味着服务端采用默认的http.DefaultServeMux进行分发处理。

2.3 http.NewServeMux(): 自定义多路由分发服务

http.NewServeMux()的作用是注册网络访问的多路路由。因为它采用的是自定义的多路由分发任务方式,所以称之为自定义多路由分发服务。

注册网络路由时,如果http.ListenAndServer()的第二个参数为nil,那么表示服务端采用默认的http.DefaultServeMux进行分发处理。也可以自定义ServeMuxServeMux结构体如下

// ServeMux结构体源码
type ServeMux struct {
mu sync.RWMutex // 锁,由于请求涉及到并发处理,因此这里有个锁机制
m map[string]muxEntry // 存放具体的路由信息
es []muxEntry // 按照路由长度从大到小的存放处理函数
hosts bool // 标记路由中是否带有主机名
}
// muxEntry是路由的具体条目
type muxEntry struct {
h Handler // 处理函数
pattern string // 路由路径
}

3. 自定义多路由实践

package main
import (
"fmt"
"net/http"
)
// 定义一个接口体,用来实现http.Handler
type MyRoute struct {
}
// 实现http.Handler接口中的ServeHTTP方法
func (m *MyRoute)ServeHTTP(w http.ResponseWriter,r *http.Request) {
path := r.URL.Path
fmt.Println(path)
switch path {
case "/":
w.Write([]byte("首页"))
case "/hello":
w.Write([]byte("say hello"))
case "/test":
w.Write([]byte("test doing"))
default:
http.NotFound(w,r)
}
return
}
func main() {
myRoute := &MyRoute{}
http.ListenAndServe(":10000",myRoute)
}

3.1 代码执行流程

使用http.ListenAndServe(":10000",myRoute)启动服务之后,会发生以下操作

1.实例化http.Server,并调用ListenAndServe()
func ListenAndServe(addr string, handler Handler) error {
// 实例化 Server
server := &Server{Addr: addr, Handler: handler}
// 调用 ListenAndServe()
return server.ListenAndServe()
}
2.监听端口
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
// 监听端口
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
// 启动服务
return srv.Serve(ln)
}
3.启动for无限循环,在循环体中Accept请求,并开启 goroutine为这个请求服务
func (srv *Server) Serve(l net.Listener) error {
//...省略N行代码
for {
rw, err := l.Accept()
if err != nil {
//...省略N行代码
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew)
// 开启goroutine为这个请求服务
go c.serve(connCtx)
}
}
4.读取每个请求内容,并调用ServeHTTP
func (c *conn) serve(ctx context.Context) {
//...省略N行代码
for {
// 读取每个请求内容
w, err := c.readRequest(ctx)
//...省略N行代码

// 调用ServeHTTP
serverHandler{c.server}.ServeHTTP(w, w.req)
//...省略N行代码
}
5. 判断handler是否为空,如果为空则把handler设置成DefaultServeMux
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
// 如果为空则把handler设置成DefaultServeMux。
handler = DefaultServeMux
}
if req.RequestURI == "*" && req.Method == "OPTIONS" {
handler = globalOptionsHandler{}
}
// 上述示例中,传的是&MyRoute,所以会调用MyRoute.ServeHTTP
handler.ServeHTTP(rw, req)
}