Learn Go HTTP Framework in Hard Way

Learn Go in 30min

Learn array slice struct interface

Learn Go concurrency

package main

import (
	"fmt"
	"sync"
)

func main() {
	people := []string{"Anna", "Bob", "Cody", "Dave", "Eva"}
	match := make(chan string, 1) // 给未匹配的元素预留空间
	wg := new(sync.WaitGroup)
	for _, name := range people {
		wg.Add(1)
		go Seek(name, match, wg)
	}
	wg.Wait()
	select {
	case name := <-match:
		fmt.Printf("No one received %s’s message.\n", name)
	default:
		// 没有待处理的发送操作.
	}
}

// 寻求发送或接收匹配上名称名称的通道,并在完成后通知等待组.
func Seek(name string, match chan string, wg *sync.WaitGroup) {
	select {
	case peer := <-match:
		fmt.Printf("%s received a message from %s.\n", name, peer)
	case match <- name:
		// 等待其他人接受消息.
	}
	wg.Done()
}

go get failed

bash$ http_proxy=http://127.0.0.1:7777 go get -v <...>
bash$ https_proxy=http://127.0.0.1:7777 go get -v <...>

echo 框架分析

demo

func main() {
	e := echo.New()
	e.GET("/", func(c echo.Context) error {
		return c.String(http.StatusOK, "Hello, World!")
	})
	e.Logger.Fatal(e.Start(":1323"))
}

监听端口

先看 net.Listen

func (sl *sysListener) listenTCP(ctx context.Context, laddr *TCPAddr) (*TCPListener, error) {
	fd, err := internetSocket(ctx, sl.network, laddr, nil, syscall.SOCK_STREAM, 0, "listen", sl.ListenConfig.Control)
	if err != nil {
		return nil, err
	}
	return &TCPListener{fd}, nil
}
type TCPListener
    func ListenTCP(network string, laddr *TCPAddr) (*TCPListener, error)
    func (l *TCPListener) Accept() (Conn, error)
    func (l *TCPListener) AcceptTCP() (*TCPConn, error)
    func (l *TCPListener) Addr() Addr
    func (l *TCPListener) Close() error
    func (l *TCPListener) File() (f *os.File, err error)
    func (l *TCPListener) SetDeadline(t time.Time) error
    func (l *TCPListener) SyscallConn() (syscall.RawConn, error)

createListener.png

再看 &tcpKeepAliveListener{l.(*net.TCPListener)}, nil 代码不多

type tcpKeepAliveListener struct {
	*net.TCPListener
}

func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
	tc, err := ln.AcceptTCP()
	if err != nil {
		return
	}
	tc.SetKeepAlive(true)
	tc.SetKeepAlivePeriod(3 * time.Minute)
	return tc, nil
}

回到 StartServer 函数

Go net/http 是如何处理 http 请求的

c := srv.newConn(rw)
c.setState(c.rwc, StateNew)

readRequest

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)
}

Echo 框架处理逻辑

e.router.Find(r.Method, getPath(r), c)
h = c.Handler()
e.GET("/", func(c echo.Context) error {
	return c.String(http.StatusOK, "Hello, World!")
})
func (c *context) Blob(code int, contentType string, b []byte) (err error) {
	c.writeContentType(contentType)
	c.response.WriteHeader(code)
	_, err = c.response.Write(b)
	return
}

Echo 的路由与上下文

// GET registers a new GET route for a path with matching handler in the router
// with optional route-level middleware.
func (e *Echo) GET(path string, h HandlerFunc, m ...MiddlewareFunc) *Route {
	return e.Add(GET, path, h, m...)
}
// Add registers a new route for an HTTP method and path with matching handler
// in the router with optional route-level middleware.
func (e *Echo) Add(method, path string, handler HandlerFunc, middleware ...MiddlewareFunc) *Route {
	name := handlerName(handler)
	e.router.Add(method, path, func(c Context) error {
		h := handler
		// Chain middleware
		for i := len(middleware) - 1; i >= 0; i-- {
			h = middleware[i](h)
		}
		return h(c)
	})
	r := &Route{
		Method: method,
		Path:   path,
		Name:   name,
	}
	e.router.routes[method+path] = r
	return r
}
func handlerName(h HandlerFunc) string {
	t := reflect.ValueOf(h).Type()
	if t.Kind() == reflect.Func {
		return runtime.FuncForPC(reflect.ValueOf(h).Pointer()).Name()
	}
	return t.String()
}
	Router struct {
		tree   *node
		routes map[string]*Route
		echo   *Echo
	}
	node struct {
		kind          kind
		label         byte
		prefix        string
		parent        *node
		children      children
		ppath         string
		pnames        []string
		methodHandler *methodHandler
	}
func (n *node) addHandler(method string, h HandlerFunc) {
	switch method {
	case CONNECT:
		n.methodHandler.connect = h
	case DELETE:
		n.methodHandler.delete = h
	case GET:
		n.methodHandler.get = h
	case HEAD:
		n.methodHandler.head = h
	case OPTIONS:
		n.methodHandler.options = h
	case PATCH:
		n.methodHandler.patch = h
	case POST:
		n.methodHandler.post = h
	case PROPFIND:
		n.methodHandler.propfind = h
	case PUT:
		n.methodHandler.put = h
	case TRACE:
		n.methodHandler.trace = h
	}
}
e.router.Find(r.Method, getPath(r), c)
...
ctx.handler = cn.findHandler(method)
...
h = c.Handler()

Reference