net/http
初始化
注册路由
type ServeMux struct {
mu sync.RWMutex
m map[string]muxEntry
es []muxEntry
hosts bool
}
type muxEntry struct {
h Handler
pattern string
}
func main() {
http.HandleFunc("/", HandleHttp)
}
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
func (mux *ServeMux) Handle(pattern string, handler Handler) {
mux.mu.Lock()
defer mux.mu.Unlock()
......
if mux.m == nil {
mux.m = make(map[string]muxEntry)
}
e := muxEntry{h: handler, pattern: pattern}
mux.m[pattern] = e
if pattern[len(pattern)-1] == '/' {
mux.es = appendSorted(mux.es, e)
}
if pattern[0] != '/' {
mux.hosts = true
}
}
开启服务
func main() {
err := http.ListenAndServe("127.0.0.1:8080", nil)
}
type Server struct {
Addr string
Handler Handler
}
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
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(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
func (srv *Server) Serve(l net.Listener) error {
...
baseCtx := context.Background()
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
rw, e := l.Accept()
...
c := srv.newConn(rw)
c.setState(c.rwc, StateNew)
go c.serve(ctx)
}
}
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc,
}
if debugServerConnections {
c.rwc = newLoggingConn("server", c.rwc)
}
return c
}
- 当一个连接建立之后,该连接中所有的请求都将在这个协程中进行处理,直到连接被关闭。
func (c *conn) serve(ctx context.Context) {
...
for {
w, err := c.readRequest(ctx)
if c.r.remain != c.server.initialReadLimitSize() {
c.setState(c.rwc, StateActive)
}
...
serverHandler{c.server}.ServeHTTP(w, w.req)
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
c.closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle)
c.curReq.Store((*response)(nil))
...
}
type serverHandler struct {
srv *Server
}
}
- sh.srv.Handler其实就是我们最初在http.ListenAndServe()中传入的第二个参数,通常为nil
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匹配url和实际的handler,先精确匹配,后模糊匹配
- 对于类似/path1/path2/path3这样的路由,如果不能找到精确匹配的路由规则,那么则会去匹配和当前路由最接近的已注册的父节点路由,所以如果路由/path1/path2/已注册,那么该路由会被匹配,否则继续匹配下一个父节点路由,直到根路由/。
- 由于[]muxEntry中的muxEntry按照路由表达式从长到短排序,所以进行近似匹配时匹配到的节点路由一定是已注册父节点路由中最相近的
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)
}
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {
mux.mu.RLock()
defer mux.mu.RUnlock()
if mux.hosts {
h, pattern = mux.match(host + path)
}
if h == nil {
h, pattern = mux.match(path)
}
if h == nil {
h, pattern = NotFoundHandler(), ""
}
return
}
func (mux *ServeMux) match(path string) (h Handler, pattern string) {
v, ok := mux.m[path]
if ok {
return v.h, v.pattern
}
for _, e := range mux.es {
if strings.HasPrefix(path, e.pattern) {
return e.h, e.pattern
}
}
return nil, ""
}
gin
封装net/http,实现路由,支持中间件
相对net/http的优化点
- 路由快速:基于基数树的httprouter
- 支持中间件:方便、灵活,支持自定义
- 支持崩溃恢复:可以捕捉panic引发的程序崩溃,使Web服务可以一直运行。自带中间件实现。
- JSON验证:可以验证请求中JSON数据格式。
- 路由分组:支持路由分组(RouteGroup),可以更方便组织路由。
- 多种数据渲染方式:支持HTML、JSON、YAML、XML等数据格式的响应。
- 开源框架,开发者活跃
路由数据结构
基数树
- 使用基于基数树(Radix Tree)的httprouter
- 压缩版前缀树,:对于基数树的每个节点,如果该节点是唯一的子树的话,就和父节点合并
- 路由树节点
// tree.go
type node struct {
// 节点路径,比如上面的s,earch,和upport
path string
// 和children字段对应, 保存的是分裂的分支的第一个字符
// 例如search和support, 那么s节点的indices对应的"eu"
// 代表有两个分支, 分支的首字母分别是e和u
indices string
// 儿子节点
children []*node
// 处理函数链条(切片)
handlers HandlersChain
......
}
// gin.go
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
// yangxiangrui.site...
// 获取请求方法对应的树
root := engine.trees.get(method)
if root == nil {
// 如果没有就创建一个
root = new(node)
root.fullPath = "/"
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
root.addRoute(path, handlers)
}
路由组
type RouterGroup struct {
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
type Engine struct {
RouterGroup
trees methodTrees
......
}
type methodTree struct {
method string
root *node
}
初始化
注册路由
func main(){
group.GET(relativePath, handler)
}
func (group *RouterGroup) GETEX(relativePath string, handler gin.HandlerFunc, handlerName string) gin.IRoutes {
internal.SetHandlerName(handler, handlerName)
return
}
func (group *RouterGroup) GET(relativePath string, handlers ...HandlerFunc) IRoutes {
return group.handle(http.MethodGet, relativePath, handlers)
}
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
absolutePath := group.calculateAbsolutePath(relativePath)
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
func (group *RouterGroup) combineHandlers(handlers HandlersChain) HandlersChain {
finalSize := len(group.Handlers) + len(handlers)
if finalSize >= int(abortIndex) {
panic("too many handlers")
}
mergedHandlers := make(HandlersChain, finalSize)
copy(mergedHandlers, group.Handlers)
copy(mergedHandlers[len(group.Handlers):], handlers)
return mergedHandlers
}
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
......
root.addRoute(path, handlers)
......
}
func (n *node) addRoute(path string, handlers HandlersChain) {
fullPath := path
n.priority++
numParams := countParams(path)
if len(n.path) == 0 && len(n.children) == 0 {
n.insertChild(numParams, path, fullPath, handlers)
n.nType = root
return
}
parentFullPathIndex := 0
walk:
for {
......
i := longestCommonPrefix(path, n.path)
if i < len(n.path) {
child := node{
path: n.path[i:],
wildChild: n.wildChild,
indices: n.indices,
children: n.children,
handlers: n.handlers,
priority: n.priority - 1,
fullPath: n.fullPath,
}
n.children = []*node{&child}
n.indices = bytesconv.BytesToString([]byte{n.path[i]})
n.path = path[:i]
n.handlers = nil
n.wildChild = false
n.fullPath = fullPath[:parentFullPathIndex+i]
...... }
if i < len(path) {
......
n.insertChild(numParams, path, fullPath, handlers)
return
}
if n.handlers != nil {
panic("handlers are already registered for path '" + fullPath + "'")
}
......
return
}
}
注册中间件
- 注册中间件其实就是将中间件函数追加到RouterGroup.Handlers中
- 中间件+业务逻辑构成请求处理链
func Default() *Engine {
r := New()
mwConfig := DefaultMiddlewareConfig()
r.Use(mwConfig.MiddlewareList()...)
return r
}
func (engine *Engine) Use(middleware ...HandlerFunc) IRoutes {
engine.RouterGroup.Use(middleware...)
engine.rebuild404Handlers()
engine.rebuild405Handlers()
return engine
}
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
开启服务
func (engine *Engine) Run(addr ...string) (err error) {
......
if listener, hook, err := createListener(); nil != err {
logs.Warnf("create listen fail, err is %s", err)
return err
} else {
errCh := make(chan error, 1)
go func() {
logs.Info("Run in %s mode", appConfig.Mode)
server := &http.Server{Handler: engine}
if err := doHttpServerConfig(server); nil != err {
errCh <- err
} else {
errCh <- netex.ListenAndServe(listener, server)
}
}()
startDebugServer()
data := generateFicData(engine)
data["protocol"] = "http"
reportMetainfo(data)
stats.DoReport(PSM())
if err = waitSignal(errCh, hook); err != nil {
logs.Warnf("wait signal fail, err is %s", err)
return err
}
return nil
}
}
func ListenAndServe(l net.Listener, s *http.Server) error {
if strings.HasPrefix(l.Addr().String(), "/") {
return s.Serve(l)
} else {
return s.Serve(ApplyKeepAlive(l))
}
}
处理请求
匹配路由
- 匹配HTTP method对应的基数树
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context)
c.writermem.reset(w)
c.Request = req
c.reset()
engine.handleHTTPRequest(c)
engine.pool.Put(c)
}
func (engine *Engine) handleHTTPRequest(c *Context) {
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method != httpMethod {
continue
}
root := t[i].root
value := root.getValue(rPath, c.Params, unescape)
if value.handlers != nil {
c.handlers = value.handlers
c.Params = value.params
c.fullPath = value.fullPath
c.Next()
c.writermem.WriteHeaderNow()
return
}
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
- 基数树中寻找handlers
type nodeValue struct {
handlers HandlersChain
params Params
tsr bool
fullPath string
}
func (n *node) getValue(path string, po Params, unescape bool) (value nodeValue) {
value.params = po
walk:
for {
prefix := n.path
if path == prefix {
if value.handlers = n.handlers; value.handlers != nil {
value.fullPath = n.fullPath
return
}
if path == "/" && n.wildChild && n.nType != root {
value.tsr = true
return
}
indices := n.indices
for i, max := 0, len(indices); i < max; i++ {
if indices[i] == '/' {
n = n.children[i]
value.tsr = (len(n.path) == 1 && n.handlers != nil) ||
(n.nType == catchAll && n.children[0].handlers != nil)
return
}
}
return
}
if len(path) > len(prefix) && path[:len(prefix)] == prefix {
path = path[len(prefix):]
if !n.wildChild {
c := path[0]
indices := n.indices
for i, max := 0, len(indices); i < max; i++ {
if c == indices[i] {
n = n.children[i]
continue walk
}
}
value.tsr = path == "/" && n.handlers != nil
return
}
n = n.children[0]
switch n.nType {
......
default:
panic("invalid node type")
}
}
......
}
}
执行
- 执行中间件+业务逻辑的职责链
func (c *Context) Next() {
c.index++
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
- 如果自定义中间件,可以使用next()实现切面
func middleware(ctx *Context){
c.Next()
}
参考
|