1. 怎么启动Web服务?
Go
语言标准库内置的net/http
包,可以实现HTTP
服务端。实现HTTP
服务端就是能够启动Web
服务,相当于搭建起了一个Web
服务器。
http.ListenAndServer()
函数用来启动Web
服务,绑定并监听http
端口。
func ListenAndServe(addr string, handler Handler)
|
2.启动Web服务的几种方式
根据不同服务返回的handler
,常见启动Web
服务有以下几种方式。
2.1 http.FileServer: 静态文件服务
http.FileServer()
搭建的服务器只提供静态文件的访问。因为这种web
服务只支持静态文件访问,所以称之为静态文件服务。
1.使用示例
文件目录如下:
package main import ( "net/http" ) func main() { runFileServer() }
func runFileServer() { 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) }
|
ServeHTTP()
方法有两个参数,其中第一个参数是ResponseWriter
类型,包含了服务器端给客户端的响应数据。服务器端往ResponseWriter
写入了什么内容,浏览器的网页源码就是什么内容。第二个参数是一个 *Request
指针,包含了客户端发送给服务器端的请求信息(路径、浏览器类型等)
1.使用示例
package main import ( "fmt" "net/http" ) func main() { http.HandleFunc("/hello",helloHandle) http.HandleFunc("/test",testHandle) err := http.ListenAndServe(":5000",nil) fmt.Println(err) }
func helloHandle(w http.ResponseWriter, r *http.Request) { fmt.Println("访问路由hello") fmt.Println(r.URL.Query()) w.Write([]byte("hello word!")) }
func testHandle(w http.ResponseWriter, r *http.Request) { fmt.Println("访问路由test") fmt.Println(r.URL.Query()) w.Write([]byte("test doing!")) }
|
通过http. HandleFunc()
注册网络路由时,http.ListenAndServer()
的第二个参数通常为nil
,这意味着服务端采用默认的http.DefaultServeMux
进行分发处理。
2.3 http.NewServeMux(): 自定义多路由分发服务
http.NewServeMux()
的作用是注册网络访问的多路路由。因为它采用的是自定义的多路由分发任务方式,所以称之为自定义多路由分发服务。
注册网络路由时,如果http.ListenAndServer()
的第二个参数为nil
,那么表示服务端采用默认的http.DefaultServeMux
进行分发处理。也可以自定义ServeMux
。ServeMux
结构体如下
type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry hosts bool }
type muxEntry struct { h Handler pattern string }
|
3. 自定义多路由实践
package main import ( "fmt" "net/http" )
type MyRoute struct { }
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{Addr: addr, Handler: handler} 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 { for { rw, err := l.Accept() if err != nil { } 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) go c.serve(connCtx) } }
|
4.读取每个请求内容,并调用ServeHTTP
func (c *conn) serve(ctx context.Context) { for { w, err := c.readRequest(ctx) serverHandler{c.server}.ServeHTTP(w, w.req) }
|
5. 判断handler
是否为空,如果为空则把handler
设置成DefaultServeMux
。
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } handler.ServeHTTP(rw, req) }
|