启动http服务
//使用gin.Run()启动http服务
//使用gin.RunTLS启动https服务
func (engine *Engine) Run(addr ...string) (err error) {//获取端口,默认:8080address := resolveAddress(addr)debugPrint("Listening and serving HTTP on %s\n", address)//调用go实现好的http功能包,实现http功能//传入engine.Handler(),返回一个http Handler,由engine实现了该Handler函数ServeHTTP err = http.ListenAndServe(address, engine.Handler())return
}
gin.Run()函数调用go的官方包启动了一个http服务,并实现了http服务的回调ServeHTTP函数。当请求来的时候会调用gin的ServeHTTP函数
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {//使用对象池初始化Contextc := engine.pool.Get().(*Context)//初始化writermem//writermem继承http.ResponseWriter。用来http回复c.writermem.reset(w)c.Request = req//初始化Contextc.reset()//处理请求engine.handleHTTPRequest(c)//移出对象池engine.pool.Put(c)
}
func (engine *Engine) handleHTTPRequest(c *Context) {httpMethod := c.Request.Method//省略一些参数初始化// Find root of the tree for the given HTTP methodt := engine.treesfor i, tl := 0, len(t); i < tl; i++ {if t[i].method != httpMethod {continue}root := t[i].root// 遍历请求树,找到方法value := root.getValue(rPath, c.params, c.skippedNodes, unescape)if value.params != nil {c.Params = *value.params}// 开始调用中间件if value.handlers != nil {c.handlers = value.handlersc.fullPath = value.fullPathc.Next()c.writermem.WriteHeaderNow()return}//处理重定向if httpMethod != http.MethodConnect && rPath != "/" {if value.tsr && engine.RedirectTrailingSlash {redirectTrailingSlash(c)return}if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) {return}}break}//处理404请求if engine.HandleMethodNotAllowed {for _, tree := range engine.trees {if tree.method == httpMethod {continue}if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil {c.handlers = engine.allNoMethodserveError(c, http.StatusMethodNotAllowed, default405Body)return}}}c.handlers = engine.allNoRouteserveError(c, http.StatusNotFound, default404Body)
}
中间间调用与停止
调用
请求进来后后,调用Nex()函数后,所有的中间件(这里包括请求的处理函数)都会遍历运行
func (c *Context) Next() {c.index++//index大于handlers数量后停止后面中间件的运行for c.index < int8(len(c.handlers)) {c.handlers[c.index](c)c.index++}
}
Next()调用顺序分析
//中间件1
func M1(c *gin.Context) {fmt.Println("==============M1-A")c.Next()fmt.Println("==============M1-B")
}//中间件2
func M2(c *gin.Context) {fmt.Println("==============M2-A")
}//请求的执行函数
func List(c *gin.Context) {fmt.Println("user/list")
}//请求/user/list后的打印
==============M1-A
==============M2-A
user/list
==============M1-B
分析:
NEXT()调用c.handlers[c.index](c)执行第一个中间件,在调用第一个中间件函数M1执行中途调用了Next(),进入Next(),函数下标c.index++,指向下一个中间件函数,等待下一个中间件函数M2,以及M2后面的处理函数。直到所有的函数都调用完,回到M1,执行后面的逻辑,打印M1-B
停止
停止就很简单,把该次的调用index数值设为abortIndex,abortIndex为handlers的最大数量combineHandlers函数里做了限制handler添加的最大数量
func (c *Context) Abort() {c.index = abortIndex
}
关于Http Server
Gin http请求并发的关键在于调用了Go的原生Http框架,这个框架有点大,我们可以慢慢地去探索一下
ListenAndServe是gin启动http服务的调用函数
func ListenAndServe(addr string, handler Handler) error {//初始化httpServer服务结构体 server := &Server{Addr: addr, Handler: handler}//启动服务监听return server.ListenAndServe()
}//server.ListenAndServe()
func (srv *Server) ListenAndServe() error {//此处省掉部分参数配置//启动tcp监听 ln, err := net.Listen("tcp", addr)if err != nil {return err}//传入tcp listen return srv.Serve(ln)
}//srv.Serve(ln)
func (srv *Server) Serve(l net.Listener) error {//去掉一些空判断 //初始化onceCloseListener结构体//onceCloseListener继承net.Listener,再加sync.Once//保证tcp只被关闭一次 origListener := ll = &onceCloseListener{Listener: l}defer l.Close()//初始化http2协议 if err := srv.setupHTTP2_Serve(); err != nil {return err}//把当前Listener当成key,value为空结构体存入map中,trackListener重要代码如下//s.listeners[ln] = struct{}{} //ln为Listener为指针&l //s.listenerGroup.Add(1)if !srv.trackListener(&l, true) {return ErrServerClosed}//从map中删除listener defer srv.trackListener(&l, false)//初始化goroutine的context//context是go原生提供用于goroutine的数据共享和流程控制 baseCtx := context.Background()if srv.BaseContext != nil {baseCtx = srv.BaseContext(origListener)if baseCtx == nil {panic("BaseContext returned a nil context")}}var tempDelay time.Duration // how long to sleep on accept failure//把这个Server做为value存入context中 ctx := context.WithValue(baseCtx, ServerContextKey, srv)for {//等待请求接入 rw, err := l.Accept()//tcp出错,等待 tempDelay毫秒 后重试if err != nil {//srv是否正在关闭 if srv.shuttingDown() {return ErrServerClosed}if ne, ok := err.(net.Error); ok && ne.Temporary() {if tempDelay == 0 {tempDelay = 5 * time.Millisecond} else {tempDelay *= 2}if max := 1 * time.Second; tempDelay > max {tempDelay = max}srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)time.Sleep(tempDelay)continue}return err}connCtx := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0//初始化一个conn结构体//conn结构体,绑定了本Server和Accept()返回的net.Conn c := srv.newConn(rw)//设置状态标志//此处的c.rwc就是上面的rw c.setState(c.rwc, StateNew, runHooks) // before Serve can return//启动一个goroutine,处理http请求 go c.serve(connCtx)}
}
func (c *conn) serve(ctx context.Context) {if ra := c.rwc.RemoteAddr(); ra != nil {c.remoteAddr = ra.String()}ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())var inFlightResponse *response//处理异常和关闭TCP defer func() {if err := recover(); err != nil && err != ErrAbortHandler {const size = 64 << 10buf := make([]byte, size)buf = buf[:runtime.Stack(buf, false)]c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf)}if inFlightResponse != nil {inFlightResponse.cancelCtx()}if !c.hijacked() {if inFlightResponse != nil {inFlightResponse.conn.r.abortPendingRead()inFlightResponse.reqBody.Close()}c.close()c.setState(c.rwc, StateClosed, runHooks)}}()//省略掉处理https请求代码// HTTP/1.x from here on.//由BaseContext创建一个cancelContext ctx, cancelCtx := context.WithCancel(ctx)c.cancelCtx = cancelCtxdefer cancelCtx()//创建一个读Buffer和写Buffer c.r = &connReader{conn: c}c.bufr = newBufioReader(c.r)c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)for {//读出一个Respon w, err := c.readRequest(ctx)if c.r.remain != c.server.initialReadLimitSize() {// If we read any bytes off the wire, we're active.c.setState(c.rwc, StateActive, runHooks)}if err != nil {//省略错误处理 }// Expect 100 Continue supportreq := w.reqif req.expectsContinue() {if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {// Wrap the Body reader with one that replies on the connectionreq.Body = &expectContinueReader{readCloser: req.Body, resp: w}w.canWriteContinue.Store(true)}} else if req.Header.get("Expect") != "" {w.sendExpectationFailed()return}c.curReq.Store(w)if requestBodyRemains(req.Body) {registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)} else {w.conn.r.startBackgroundRead()}// HTTP cannot have multiple simultaneous active requests.[*]// Until the server replies to this request, it can't read another,// so we might as well run the handler in this goroutine.// [*] Not strictly true: HTTP pipelining. We could let them all process// in parallel even if their responses need to be serialized.// But we're not going to implement HTTP pipelining because it// was never deployed in the wild and the answer is HTTP/2.inFlightResponse = w//调用ServeHTTP回调serverHandler{c.server}.ServeHTTP(w, w.req)inFlightResponse = nilw的cancelCtx与c的cancelCtx不是同一个cancelCtxw.cancelCtx()//是否被劫持if c.hijacked() {return}//把response的buffer,flush出去,并关闭一些bufferw.finishRequest()c.rwc.SetWriteDeadline(time.Time{})if !w.shouldReuseConnection() {if w.requestBodyLimitHit || w.closedRequestBodyEarly() {c.closeWriteAndWait()}return}c.setState(c.rwc, StateIdle, runHooks)c.curReq.Store(nil)if !w.conn.server.doKeepAlives() {// We're in shutdown mode. We might've replied// to the user without "Connection: close" and// they might think they can send another// request, but such is life with HTTP/1.1.return}//设置超时关闭时间if d := c.server.idleTimeout(); d != 0 {c.rwc.SetReadDeadline(time.Now().Add(d))} else {c.rwc.SetReadDeadline(time.Time{})}// Wait for the connection to become readable again before trying to// read the next request. This prevents a ReadHeaderTimeout or// ReadTimeout from starting until the first bytes of the next request// have been received.if _, err := c.bufr.Peek(4); err != nil {return}c.rwc.SetReadDeadline(time.Time{})}
}