golang代码执行顺序

Posted 黑手党老k

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang代码执行顺序相关的知识,希望对你有一定的参考价值。

一:首先man.go,整个程序的入口

func main() {
    beego.Run()
}

然后beego.run()代码

复制代码
// Run beego application.
// beego.Run() default run on HttpPort
// beego.Run(":8089")
// beego.Run("127.0.0.1:8089")
func Run(params ...string) {
    if len(params) > 0 && params[0] != "" {
        strs := strings.Split(params[0], ":")
        if len(strs) > 0 && strs[0] != "" {
            HttpAddr = strs[0]
        }
        if len(strs) > 1 && strs[1] != "" {
            HttpPort, _ = strconv.Atoi(strs[1])
        }
    }
    initBeforeHttpRun()

    if EnableAdmin {
        go beeAdminApp.Run()
    }

    BeeApp.Run()
}
复制代码

可以看出来,beego.run()可以带参数。

beego.run()在默认的主机、端口号上运行,beego.run(port)在给定的端口号、默认的主机上运行。beego.run(addr:post)在给定的主机和端口上运行。

下面看看initBeforeHttpRun()的代码。

复制代码
func initBeforeHttpRun() {
    // if AppConfigPath not In the conf/app.conf reParse config
    if AppConfigPath != filepath.Join(AppPath, "conf", "app.conf") {
        err := ParseConfig()
        if err != nil && AppConfigPath != filepath.Join(workPath, "conf", "app.conf") {
            // configuration is critical to app, panic here if parse failed
            panic(err)
        }
    }

    // do hooks function
    for _, hk := range hooks {
        err := hk()
        if err != nil {
            panic(err)
        }
    }

    if SessionOn {
        var err error
        sessionConfig := AppConfig.String("sessionConfig")
        if sessionConfig == "" {
            sessionConfig = `{"cookieName":"` + SessionName + `",` +
                `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
                `"providerConfig":"` + SessionSavePath + `",` +
                `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
                `"sessionIDHashFunc":"` + SessionHashFunc + `",` +
                `"sessionIDHashKey":"` + SessionHashKey + `",` +
                `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
                `"domain":"` + SessionDomain + `",` +
                `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
        }
        GlobalSessions, err = session.NewManager(SessionProvider,
            sessionConfig)
        if err != nil {
            panic(err)
        }
        go GlobalSessions.GC()
    }

    err := BuildTemplate(ViewsPath)
    if err != nil {
        if RunMode == "dev" {
            Warn(err)
        }
    }

    middleware.VERSION = VERSION
    middleware.AppName = AppName
    middleware.RegisterErrorHandler()

    if EnableDocs {
        Get("/docs", serverDocs)
        Get("/docs/*", serverDocs)
    }

    //init mime
    AddAPPStartHook(initMime)
}
复制代码

可以看到首先拼凑出来的是conf配置文件的路劲,如果存在然后调用ParseConfig()解析conf。

然后是

 for _, hk := range hooks {
        err := hk()
        if err != nil {
            panic(err)
        }
    }

hooks的定义

type hookfunc func() error //hook function to run
var hooks []hookfunc       //hook function slice to store the hookfunc
func init() {
    hooks = make([]hookfunc, 0)
}

hooks是一个hookfun的切片,

// The hookfunc will run in beego.Run()
// such as sessionInit, middlerware start, buildtemplate, admin start
func AddAPPStartHook(hf hookfunc) {
    hooks = append(hooks, hf)
}

上面的代码是在启动的时候添加自己的方法hook。也就是hooks是在启动之前,留给用户初始化一些东西的时候。

复制代码
if SessionOn {
        var err error
        sessionConfig := AppConfig.String("sessionConfig")
        if sessionConfig == "" {
            sessionConfig = `{"cookieName":"` + SessionName + `",` +
                `"gclifetime":` + strconv.FormatInt(SessionGCMaxLifetime, 10) + `,` +
                `"providerConfig":"` + SessionSavePath + `",` +
                `"secure":` + strconv.FormatBool(EnableHttpTLS) + `,` +
                `"sessionIDHashFunc":"` + SessionHashFunc + `",` +
                `"sessionIDHashKey":"` + SessionHashKey + `",` +
                `"enableSetCookie":` + strconv.FormatBool(SessionAutoSetCookie) + `,` +
                `"domain":"` + SessionDomain + `",` +
                `"cookieLifeTime":` + strconv.Itoa(SessionCookieLifeTime) + `}`
        }
        GlobalSessions, err = session.NewManager(SessionProvider,
            sessionConfig)
        if err != nil {
            panic(err)
        }
        go GlobalSessions.GC()
    }

    err := BuildTemplate(ViewsPath)
    if err != nil {
        if RunMode == "dev" {
            Warn(err)
        }
    }
复制代码

下面就开始来解析conf文件。如果sessionConfig为空,就使用默认的json数据。然后就开始根据提供的config配置文件创建一个sessionmanager对象

这是session.NewManager()方法的实现

复制代码
// Create new Manager with provider name and json config string.
// provider name:
// 1. cookie
// 2. file
// 3. memory
// 4. redis
// 5. mysql
// json config:
// 1. is https  default false
// 2. hashfunc  default sha1
// 3. hashkey default beegosessionkey
// 4. maxage default is none
func NewManager(provideName, config string) (*Manager, error) {
    provider, ok := provides[provideName]
    if !ok {
        return nil, fmt.Errorf("session: unknown provide %q (forgotten import?)", provideName)
    }
    cf := new(managerConfig)
    cf.EnableSetCookie = true
    err := json.Unmarshal([]byte(config), cf)
    if err != nil {
        return nil, err
    }
    if cf.Maxlifetime == 0 {
        cf.Maxlifetime = cf.Gclifetime
    }
    err = provider.SessionInit(cf.Maxlifetime, cf.ProviderConfig)
    if err != nil {
        return nil, err
    }
    if cf.SessionIDHashFunc == "" {
        cf.SessionIDHashFunc = "sha1"
    }
    if cf.SessionIDHashKey == "" {
        cf.SessionIDHashKey = string(generateRandomKey(16))
    }

    return &Manager{
        provider,
        cf,
    }, nil
}
复制代码

可以推测。session.NewManager(SessionProvider,sessionConfig)中SessionProvider是一个全局变量,使用的是在config.go中默认的SessionProvider = "memory",然后改方法返回的是一个Manager的指针对象,即*Manager,所以GlobalSessions是一个*Manager对象。

然后启动一个携程执行GC()方法。下面是GC的源码

// Start session gc process.
// it can do gc in times after gc lifetime.
func (manager *Manager) GC() {
    manager.provider.SessionGC()
    time.AfterFunc(time.Duration(manager.config.Gclifetime)*time.Second, func() { manager.GC() })
}

所以上面的代码是一个无限循环,每隔一段time。DUration之后执行GC().

err := BuildTemplate(ViewsPath)
    if err != nil {
        if RunMode == "dev" {
            Warn(err)
        }
    }

这里就开始编译模板了。

复制代码
// build all template files in a directory.
// it makes beego can render any template file in view directory.
func BuildTemplate(dir string) error {
    if _, err := os.Stat(dir); err != nil {
        if os.IsNotExist(err) {
            return nil
        } else {
            return errors.New("dir open err")
        }
    }
    self := &templatefile{
        root:  dir,
        files: make(map[string][]string),
    }
    err := filepath.Walk(dir, func(path string, f os.FileInfo, err error) error {
        return self.visit(path, f, err)
    })
    if err != nil {
        fmt.Printf("filepath.Walk() returned %v\\n", err)
        return err
    }
    for _, v := range self.files {
        for _, file := range v {
            t, err := getTemplate(self.root, file, v...)
            if err != nil {
                Trace("parse template err:", file, err)
            } else {
                BeeTemplates[file] = t
            }
        }
    }
    return nil
}
复制代码

首先判断目录是否存在。目录ViewsPath在config中有初始化。然后初始化templatefile结构体,filepath.Walk()走一边目录里的文件,记录在self.files里面。循环self.files中的file(map[dir][]file]),用getTemplate获取template.Template实例,保存在beego.BeeTemplates(map[string]template.Template)。

然后是

复制代码
middleware.VERSION = VERSION
    middleware.AppName = AppName
    middleware.RegisterErrorHandler()

    if EnableDocs {
        Get("/docs", serverDocs)
        Get("/docs/*", serverDocs)
    }

    //init mime
    AddAPPStartHook(initMime)
复制代码

middleware包括的是错误处理的功能。如NotFound()、Forbidden()、Errorhandler()等等处理。。

随后的AddAPPStartHook(initMine)则是初始化所有的minetype类型的函数。

上面的代码实在beego项目启动前需要操作的比如初始化conf配置、编译模板文件、注册错误处理中间件、加载所有的mimetype类型、

然后继续回到beego.run()代码中间

    if EnableAdmin {
        go beeAdminApp.Run()
    }

    BeeApp.Run()

很简单。如果beego允许admin。则执行beeAdminApp。beeAdminApp也是一个*beego.adminApp,负责系统监控、性能检测、访问统计和健康检查等。然后猪线程运行BeeApp.Run()方法,开始执行beego。

下面看看beego.adminApp的代码

复制代码
// adminApp is an http.HandlerFunc map used as beeAdminApp.
type adminApp struct {
    routers map[string]http.HandlerFunc
}

// Route adds http.HandlerFunc to adminApp with url pattern.
func (admin *adminApp) Route(pattern string, f http.HandlerFunc) {
    admin.routers[pattern] = f
}

// Run adminApp http server.
// Its addr is defined in configuration file as adminhttpaddr and adminhttpport.
func (admin *adminApp) Run() {
    if len(toolbox.AdminTaskList) > 0 {
        toolbox.StartTask()
    }
    addr := AdminHttpAddr

    if AdminHttpPort != 0 {
        addr = fmt.Sprintf("%s:%d", AdminHttpAddr, AdminHttpPort)
    }
    for p, f := range admin.routers {
        http.Handle(p, f)
    }
    err := http.ListenAndServe(addr, nil)
    if err != nil {
        BeeLogger.Critical("Admin ListenAndServe: ", err)
    }
}
复制代码
复制代码
// task interface
type Tasker interface {
    GetStatus() string
    Run() error
    SetNext(time.Time)
    GetNext() time.Time
    SetPrev(time.Time)
    GetPrev() time.Time
}
AdminTaskList map[string]Tasker
复制代码
复制代码
// start all tasks
func StartTask() {
    isstart = true
    go run()
}

func run() {
    now := time.Now().Local()
    for _, t := range AdminTaskList {
        t.SetNext(now)
    }

    for {
        sortList := NewMapSorter(AdminTaskList)
        sortList.Sort()
        var effective time.Time
        if len(AdminTaskList) == 0 || sortList.Vals[0].GetNext().IsZero() {
            // If there are no entries yet, just sleep - it still handles new entries
            // and stop requests.
            effective = now.AddDate(10, 0, 0)
        } else {
            effective = sortList.Vals[0].GetNext()
        }
        select {
        case now = <-time.After(effective.Sub(now)):
            // Run every entry whose next time was this effective time.
            for _, e := range sortList.Vals {
                if e.GetNext() != effective {
                    break
                }
                go e.Run()
                e.SetPrev(e.GetNext())
                e.SetNext(effective)
            }
            continue
        case <-changed:
            continue
        case <-stop:
            return
        }
    }
}

// start all tasks
func StopTask() {
    isstart = false
    stop <- true
}

// add task with name
func AddTask(taskname string, t Tasker) {
    AdminTaskList[taskname] = t
    if isstart {
        changed <- true
    }
}

// add task with name
func DeleteTask(taskname string) {
    delete(AdminTaskList, taskname)
    if isstart {
        changed <- true
    }
}
复制代码

adminApp结构体里面只有map结构的router,toolbox.AdminTaskList是一个map类型的结构。如果AdminTaskList中间有Tasker。则开始执行StartTask(),而且,StartTask()方法中实现的是另外打开一个协程执行Run()方法。最后打开http.ListenAndServe()实现监听。

下面是BeeApp.Run()

复制代码
package beego

import (
    "fmt"
    "net"
    "net/http"
    "net/http/fcgi"
    "time"

    "github.com/astaxie/beego/context"
)

// FilterFunc defines filter function type.
type FilterFunc func(*context.Context)

// App defines beego application with a new PatternServeMux.
type App struct {
    Handlers *ControllerRegistor
    Server   *http.Server
}

// NewApp returns a new beego application.
func NewApp() *App {
    cr := NewControllerRegister()
    app := &App{Handlers: cr, Server: &http.Server{}}
    return app
}

// Run beego application.
func (app *App) Run() {
    addr := HttpAddr

    if HttpPort != 0 {
        addr = fmt.Sprintf("%s:%d", HttpAddr, HttpPort)
    }

    BeeLogger.Info("Running on %s", addr)

    var (
        err error
        l   net.Listener
    )
    endRunning := make(chan bool, 1)

    if UseFcgi {
        if HttpPort == 0 {
            l, err = net.Listen("unix", addr)
        } else {
            l, err = net.Listen("tcp", addr)
        }
        if err != nil {
            BeeLogger.Critical("Listen: ", err)
        }
        err = fcgi.Serve(l, app.Handlers)
    } else {
        app.Server.Addr = addr
        app.Server.Handler = app.Handlers
        app.Server.ReadTimeout = time.Duration(HttpServerTimeOut) * time.Second
        app.Server.WriteTimeout = time.Duration(HttpServerTimeOut) * time.Second

        if EnableHttpTLS {
            go func() {
                time.Sleep(20 * time.Microsecond)
                if HttpsPort != 0 {
                    app.Server.Addr = fmt.Sprintf("%s:%d", HttpAddr, HttpsPort)
                }
                err := app.Server.ListenAndServeTLS(HttpCertFile, HttpKeyFile)
                if err != nil {
                    BeeLogger.Critical("ListenAndServeTLS: ", err)
                    time.Sleep(100 * time.Microsecond)
                    endRunning <- true
                }
            }()
        }

        if EnableHttpListen {
            go func() {
                app.Server.Addr = addr
                err := app.Server.ListenAndServe()
                if err != nil {
                    BeeLogger.Critical("ListenAndServe: ", err)
                    time.Sleep(100 * time.Microsecond)
                    endRunning <- true
                }
            }()
        }
    }

    <-endRunning
}
复制代码

上面的代码首先是获取地址addr,然后执行fast-cgi,调用ListenAndServeTLS监听cgi服务,后面的是Http服务,调用ListenAndServe()监听http服务。

 

复制代码
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()
}
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.  If
// srv.Addr is blank, ":http" is used.
func (srv *Server) ListenAndServe() error {
    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)})
}

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each.  The service goroutines read requests and
// then call srv.Handler to reply to them.
func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(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", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        c.setState(c.rwc, StateNew) // before Serve can return
        go c.serve()
    }
}
复制代码

ListenAndServe()的功能:

1、初始化一个Server

2、调用Server的ListenAndServe()

3、调用net.Listen(“tcp”, addr)监听端口

4、启动一个for循环,在循环体中Accept请求

5、对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()

下面看看进入c.serve()方法的源码

复制代码
// Serve a new connection.
func (c *conn) serve() {
    origConn := c.rwc // copy it before it\'s set nil on Close or Hijack
    defer func() {
        if err := recover(); err != nil {
            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 !c.hijacked() {
            c.close()
            c.setState(origConn, StateClosed)
        }
    }()

    if tlsConn, ok := c.rwc.(*tls.Conn); ok {
        if d := c.server.ReadTimeout; d != 0 {
            c.rwc.SetReadDeadline(time.Now().Add(d))
        }
        if d := c.server.WriteTimeout; d != 0 {
            c.rwc.SetWriteDeadline(time.Now().Add(d))
        }
        if err := tlsConn.Handshake(); err != nil {
            c.server.logf("http: TLS handshake error from %s: %v", c.rwc.RemoteAddr(), err)
            return
        }
        c.tlsState = new(tls.ConnectionState)
        *c.tlsState = tlsConn.ConnectionState()
        if proto := c.tlsState.NegotiatedProtocol; validNPN(proto) {
            if fn := c.server.TLSNextProto[proto]; fn != nil {
                h := initNPNRequest{tlsConn, serverHandler{c.server}}
                fn(c.server, tlsConn, h)
            }
            return
        }
    }

    for {
        w, err := c.readRequest()
        if c.lr.N != c.server.initialLimitedReaderSize() {
            // If we read any bytes off the wire, we\'re active.
            c.setState(c.rwc, StateActive)
        }
        if err != nil {
            if err == errTooLarge {
                // Their HTTP client may or may not be
                // able to read this if we\'re
                // responding to them and hanging up
                // while they\'re still writing their
                // request.  Undefined behavior.
                io.WriteString(c.rwc, "HTTP/1.1 413 Request Entity Too Large\\r\\n\\r\\n")
                c.closeWriteAndWait()
                break
            } else if err == io.EOF {
                break // Don\'t reply
            } else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
                break // Don\'t reply
            }
            io.WriteString(c.rwc, "HTTP/1.1 400 Bad Request\\r\\n\\r\\n")
            break
        }

        // 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}
            }
            req.Header.Del("Expect")
        } else if req.Header.get("Expect") != "" {
            w.sendExpectationFailed()
            break
        }

        // 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.
        serverHandler{c.server}.ServeHTTP(w, w.req)
        if c.hijacked() {
            return
        }
        w.finishRequest()
        if w.closeAfterReply {
            if w.requestBodyLimitHit {
                c.closeWriteAndWait()
            }
            break
        }
        c.setState(c.rwc, StateIdle)
    }
}
复制代码

上面的代码实现:

1、读取每个请求的内容w, err := c.readRequest()

2、判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux

3、调用handler的ServeHttp

4、根据request选择handler,并且进入到这个handler的ServeHTTP

5、选择handler

 下面就开始Http请求的过程了。上文中提到的handler在beego项目中就是beego.ControllerRegistor结构体,下面是ControllerRegistor的源码。可以看见。实现了http.handler接口的ServeHTTP方法。而且,上下文对象context也被初始化了。后面的do_filter就是实现过滤方法的实现。然后就是判断请求的方法、seesion等函数。

而且还有一个很重要的就是runrouter、runMethod、findrouter、routerInfo这四个参数,在方法开头就已经定义了。

复制代码
// ControllerRegistor containers registered router rules, controller handlers and filters.
type ControllerRegistor struct {
    routers      map[string]*Tree
    enableFilter bool
    filters      map[int][]*FilterRouter
}
// Implement http.Handler interface.
func (p *ControllerRegistor) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
    defer p.recoverPanic(rw, r)
    starttime := time.Now()
    var runrouter reflect.Type
    var findrouter bool
    var runMethod string
    var routerInfo *controllerInfo

    w := &responseWriter{writer: rw}

    if RunMode == "dev" {
        w.Header().Set("Server", BeegoServerName)
    }

    // init context
    context := &beecontext.Context{
        ResponseWriter: w,
        Request:        r,
        Input:          beecontext.NewInput(r),
        Output:         beecontext.NewOutput(),
    }
    context.Output.Context = context
    context.Output.EnableGzip = EnableGzip

    // defined filter function
    do_filter := func(pos int) (started bool) {
        if p.enableFilter {
            if l, ok := p.filters[pos]; ok {
                for _, filterR := range l {
                    if ok, p := filterR.ValidRouter(r.URL.Path); ok {
                        context.Input.Params = p
                        filterR.filterFunc(context)
                        if w.started {
                            return true
                        }
                    }
                }
            }
        }

        return false
    }

    // filter wrong httpmethod
    if _, ok := HTTPMETHOD[r.Method]; !ok {
        http.Error(w, "Method Not Allowed", 405)
        goto Admin
    }

    // filter for static file
    if do_filter(BeforeStatic) {
        goto Admin
    }

    serverStaticRouter(context)
    if w.started {
        findrouter = true
        goto Admin
    }

    // session init
    if SessionOn {
        context.Input.CruSession = GlobalSessions.SessionStart(w, r)
        defer func() {
            context.Input.CruSession.SessionRelease(w)
        }()
    }

    if r.Method != "golang代码片段(摘抄)

代码片段 - Golang 实现简单的 Web 服务器

golang:defer执行顺序

代码片段 - Golang 实现集合操作

Golang基础defer执行顺序

Golang-程序流程控制 ifswitch