preface

Reported a driving school, when more than two months did not send an article, driving test last week finally ended, after this have to make up for the first two months of the article. Before, I set a goal to read the source code of Beego, Iris, gin and other GO frameworks. Before, I have published an article that is too beeGO – Golang framework analysis – Beego. Today, I bring the analysis of IRIS of GO framework, mainly explaining a life cycle process of IRIS framework.

If you haven’t read golang beego before reading this article, you should check it out. Golang Beego explains how to start an HTTP server.

The installation

Install using Glide:

glide get github.com/kataras/iris
glide get github.com/kataras/golog
Copy the code

Start a simple IRIS HTTP service:

//main.go
package main

import "github.com/kataras/iris"

func main() {
	app := iris.Default()
	app.Get("/ping", func(ctx iris.Context) {
		ctx.JSON(iris.Map{
			"message": "pong",
		})
	})
	app.Run(iris.Addr(": 8888"))}Copy the code

Life cycle of IRIS

Access to a larger picture source address to check the CDN. Tigerb. Cn / 20190628234814 PNG

The figure above is a life cycle flow chart of IRIS framework that I sorted out when I read the IRIS code. Generally divided into four major parts:

The orange part

Initialize the iris. Application:

  • Create iris. Application
  • Create APIBuilder(routes for methods like app.get () are registered here)
  • Create a Router(through which each HTTP request is processed)

The blue part

Register routing to app.APIBuilder

The purple part

Initialize an HTTP. Server

The green part

Build route handler& start HTTP server:

  • registeredapp.APIBuildertoapp.Router.routesProvider
  • registeredapp.APIBuilder.routesThe route toapp.Router.requestHandler
  • Start the HTTP server

Key code parsing

  1. Create an Iris Application
// Application first look at our Iris Application structure composition
type Application struct {
    // Our routes are registered with the APIBuilder
    *router.APIBuilder
    // *router.Router implements the ServeHTTP method and is ultimately assigned to &http.server{}.handler
    *router.Router
    // Request context pool
    ContextPool    *context.Pool
    / / configuration items
    config    *Configuration
    / / log
    logger    *golog.Logger
    / / view
    view    view.View
    // Perform once
    once    sync.Once
    / / the mutex
    mu    sync.Mutex
    Hosts            []*host.Supervisor
    hostConfigurators    []host.Configurator
}

// Create an iris application instance
// Why not just New?
// Because there are two Handles registered in Default
// 1. Recover panic.
// 2. Request log
app := iris.Default()

func Default(a) *Application {
	app := New()
    // The Use of the APIBuilder
	app.Use(recover.New())
    // The Use of the APIBuilder
    app.Use(requestLogger.New())
    
	return app
}

// app := New(
app := &Application{
    config:     &config,
    logger:     golog.Default,
    // Critical: our routes are registered with the APIBuilder
    APIBuilder: router.NewAPIBuilder(),
    Router implements the ServeHTTP method and is ultimately assigned to &http.server{}.handler
    Router:     router.NewRouter(),
}

// Register the middleware requested by the API
func (api *APIBuilder) Use(handlers ... context.Handler) {
	api.middleware = append(api.middleware, handlers...)
}
Copy the code
  1. aboutrouter.NewAPIBuilder()

The routes attribute of the APIBuilder is key, and finally all the routes we define are registered here.

// APIBuilder
api := &APIBuilder{
    macros:            macro.Defaults,
    errorCodeHandlers: defaultErrorCodeHandlers(),
    reporter:          errors.NewReporter(),
    relativePath:      "/".// The final routes we define are registered here
    routes:            new(repository),
}

// The repository structure
type repository struct {
	routes []*Route
}
Copy the code

Conclusion: The user is registered with app.apiBuilder.routes

  1. aboutrouter.NewRouter()

Router.newrouter () returns a pointer to &Router{} with three key attributes and a ServeHTTP member method.

Three key attributes:

  • mainHandler http.HandlerFunc
  • requestHandler RequestHandler
  • routesProvider RoutesProvider

ServeHTTP implements ServeHTTP(w http.responseWriter, r * http.request), which accepts the Request and executes it.

// implement ServeHTTP func (router *Router) ServeHTTP(w http.ResponseWriter, R * http.request) {mainHandler router.mainHandler(w, r)}Copy the code
func NewRouter(a) *Router { return &Router{} }

type Router struct {
	mu sync.Mutex 
    requestHandler RequestHandler   
    // Each HTTP request executes mainHandler
	mainHandler    http.HandlerFunc 
	wrapperFunc    func(http.ResponseWriter, *http.Request, http.HandlerFunc)

	cPool          *context.Pool r
	routesProvider RoutesProvider} / /implement ServeHTTP
func (router *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    // Each HTTP request executes mainHandler
	router.mainHandler(w, r)
}
Copy the code

Conclusion: mainHandler is executed for every HTTP request

  1. Registered routing

Apibuilder.routes To app.apiBuilder.routes

//router
func (api *APIBuilder) Get(relativePath string, handlers ... context.Handler) *Route {
	return api.Handle(http.MethodGet, relativePath, handlers...)
}

route := &Route{
    Name:            defaultName,
    Method:          method,
    methodBckp:      method,
    Subdomain:       subdomain,
    tmpl:            tmpl,
    Path:            path,
    Handlers:        handlers,
    MainHandlerName: mainHandlerName,
    FormattedPath:   formattedPath,
}
Copy the code
  1. Build request Handler
// Start the routeApp. The Run () ⬇ ️/ / buildApp. The Build () ⬇ ️// Build the route
app.Router.BuildRouter(app.ContextPool, routerHandler, app.APIBuilder, false) ⬇ ️// Build request Handler
// Register the API registered with app.APIBuilder into requestHandler
// Because we found below that requests are processed from router. RequestHandlerRequestHandler. Build (routesProvider) ⬇ ️/ / assignmentRouter. requestHandler = requestHandler router.routesProvider = routesProvider ⬇️// The important place where mainHandler is assigned
// This is the code that accPET requests actually execute
// The truth is out there
// the important
router.mainHandler = func(w http.ResponseWriter, r *http.Request) {
    // Build the request context
    ctx := cPool.Acquire(w, r)
    // Process the request
    router.requestHandler.HandleRequest(ctx)
    // Release the request contextCPool. Release (CTX)} ⬇ ️// Actually process the request hungry place
// The route matches here
func (h *routerHandler) HandleRequest(ctx context.Context)
Copy the code
  1. Start the HTTP Server

Finally, we start the HTTP server, which is basically the same as most Golang HTTP service starts.

// Assign the IP +port of the HTTP service
iris.Addr(": 8888") ⬇ ️// Create http.Server and start the anonymous method of the service
func Addr(addr string, hostConfigs ... host.Configurator) Runner {
	return func(app *Application) error {
		returnapp.NewHost(&http.Server{Addr: addr}). Configure(hostConfigs...) .listenandServe ()}} ⬇️// app.NewHost(&http.Server{Addr: addr})
// This is where app.Router is assigned to the http.Server Handler
if srv.Handler == nil{srv.handler = app.router} ⬇️// Start the serviceSu. Server. Serve (l) ⬇ ️/ / the accept requestL.A. ccept () ⬇ ️// Start a goroutine to process the request
goC.s. erve (CTX) ⬇ ️// At last the truth came out
serverHandler{c.server}.ServeHTTP(w, w.req)
Copy the code

conclusion

Finally, let’s briefly review the above process:

The Golang Framework Analysis series is linked below:

  • Golang Framework Analysis – Beego
  • Golang Framework analysis – Iris