启动http服务
Go
//使用gin.Run()启动http服务
//使用gin.RunTLS启动https服务
func (engine *Engine) Run(addr ...string) (err error) {
//获取端口,默认:8080
address := 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函数
Go
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
//使用对象池初始化Context
c := engine.pool.Get().(*Context)
//初始化writermem
//writermem继承http.ResponseWriter。用来http回复
c.writermem.reset(w)
c.Request = req
//初始化Context
c.reset()
//处理请求
engine.handleHTTPRequest(c)
//移出对象池
engine.pool.Put(c)
}
Go
func (engine *Engine) handleHTTPRequest(c *Context) {
httpMethod := c.Request.Method
//省略一些参数初始化
// Find root of the tree for the given HTTP method
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, c.skippedNodes, unescape)
if value.params != nil {
c.Params = *value.params
}
// 开始调用中间件
if value.handlers != nil {
c.handlers = value.handlers
c.fullPath = value.fullPath
c.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.allNoMethod
serveError(c, http.StatusMethodNotAllowed, default405Body)
return
}
}
}
c.handlers = engine.allNoRoute
serveError(c, http.StatusNotFound, default404Body)
}
中间间调用与停止
调用
请求进来后后,调用Nex()函数后,所有的中间件(这里包括请求的处理函数)都会遍历运行
Go
func (c *Context) Next() {
c.index++
//index大于handlers数量后停止后面中间件的运行
for c.index < int8(len(c.handlers)) {
c.handlers[c.index](c)
c.index++
}
}
Next()调用顺序分析
Go
//中间件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添加的最大数量
Go
func (c *Context) Abort() {
c.index = abortIndex
}
关于Http Server
Gin http请求并发的关键在于调用了Go的原生Http框架,这个框架有点大,我们可以慢慢地去探索一下
ListenAndServe是gin启动http服务的调用函数
Go
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 := l
l = &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 := ctx
if 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)
}
}
Go
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 << 10
buf := 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 = cancelCtx
defer 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 support
req := w.req
if req.expectsContinue() {
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
// Wrap the Body reader with one that replies on the connection
req.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 = nil
w的cancelCtx与c的cancelCtx不是同一个cancelCtx
w.cancelCtx()
//是否被劫持
if c.hijacked() {
return
}
//把response的buffer,flush出去,并关闭一些buffer
w.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{})
}
}