流程概括
首先来大体上抽象一下Go服务器的基本运行流程。
- 创建 Listen Socket ,并将其绑定到对应的监听端口上。
- 等待并接受客户端发送的请求,从而获取 Client Socket ,程序将通过它获取http的请求头以及body内容(POST)。
- 根据请求的Path以及内部逻辑选择对应的的 请求处理函数 来处理相应的请求。
- 将处理结果发送回Client Socket ,并通过它写回客户端。
源码追踪
关联结构
http.ListenAndServestringServeMux
HandlerServeHTTP
自定义路由
先来看一下自定义路由选择器,它的实现逻辑相对比较简单:
type MyMux struct {}
//实现Handler接口
func (m *MyMux)ServeHTTP(w http.ResponseWriter, r *http.Request) {
...(自定义路由选择逻辑)
}
func main() {
mux := &MyMux{}
err := http.ListenAndServe(":9090", mux)
}
(这部分代码在最底下有完整版作为直观感受)
默认路由
之后就来看一下当使用默认路由时所涉及的内部结构吧:
func sayhelloName(w http.ResponseWriter, r *http.Request) {
...(自定义请求处理函数)
}
func main() {
http.HandleFunc("/", sayhelloName) //设置访问的路由
err := http.ListenAndServe(":9090", nil) //设置监听的端口
}
- 首先来看一下ServeMux的结构。
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry // slice of entries sorted from longest to shortest.
hosts bool // whether any patterns contain hostnames
}
mumHandler
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
Handler
- 路由规则存储匹配字符串以及对应的请求处理接口。
type muxEntry struct {
h Handler
pattern string
}
ServeHttp
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
HandlerFuncHandler
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
运行流程
现在来正式过一遍Go http的执行经过:
这里将源码中参数设置,错误检查以及一些非主要流程的相关代码给省去了,标记为:(…)
那么就从main开始吧:
func main() {
http.HandleFunc("/", sayhelloName)
err := http.ListenAndServe(":9090", nil)
}
http.ListenAndServe
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
Server.ListenAndServe
func (srv *Server) ListenAndServe() error {
(...)
ln, err := net.Listen("tcp", addr)
(...)
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
Server.Servenet.Listener.AcceptServer.newConn
func (srv *Server) Serve(l net.Listener) error {
(...)
for {
rw, e := l.Accept()
(...)
c := srv.newConn(rw)
c.setState(c.rwc, StateNew) // before Serve can return
go c.serve(ctx)
}
}
conn.serveServeHttp
func (c *conn) serve(ctx context.Context) {
for {
w, err := c.readRequest(ctx)
(...)
// Expect 100 Continue support
req := w.req
(...)
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
(...)
}
}
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
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)
}
还有就是一些细节上的工作,包括验证,有效时间等等。但在程序提供服务时的主要逻辑就是以上的途径了。
附加
ServeMux
(不需要弄明白每一步的逻辑,毕竟只是截取的一部分,但应该很容易看明白它在干些什么)
type MyMux struct {}
func (m *MyMux)ServeHTTP(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/static") {
http.ServeFile(w, r, "./web/"+r.URL.Path)
} else {
switch r.URL.Path {
case "/":
sayHello(w, r)
return
case "/login":
service.Login2(w, r)
return
case "/upload":
service.Upload(w, r)
return
case "/view":
service.View(w, r)
return
default:
http.NotFound(w, r)
return
}
}
}
func sayHello(w http.ResponseWriter, r *http.Request) {
var name string
cookie, err := r.Cookie("username")
if err != nil || cookie.Value == "" {
name = "tourist"
}else {
name = cookie.Value
}
_, _ = fmt.Fprintf(w, "Hello, %s", name)
}
func main() {
mux := &MyMux{}
err := http.ListenAndServe(":9090", mux)
if err != nil {
log.Fatal("ListenAndServe:", err)
}
}
ServeMuxHandlerFuncHandlerServeHTTP