Introduction
becausePrevious articleAsked, every time a request comes, one will be issuedgoroutinue
The result may be a tree-shaped request graph. If a timeout occurs during execution of the node below, coroutines will accumulate, so timeout control is necessary. Generally, the implementation is designed by a top-level Context for top-down transmission, so it can avoid multiple execution exceptions from one place. I will not explain the too many details of Context here. If necessary, I will introduce a separate introduction to Context. Let's take a look at how the source code is designed:
Context
// Context's methods may be called by multiple goroutines simultaneously. type Context interface { // When the Context is cancelled or reaches deadline, return a closed channel Done() <-chan struct{} } // Function handletype CancelFunc func()
The two most important points of design intention are how to actively end the downstream, and the other is how to notify the upstream to end the downstream.
The former usesCancelFunc
The latter usesDone
, the latter needs to be monitored continuously, so the channel's return value is used for monitoring
//Create Exit Contextfunc WithCancel(parent Context)(ctx Context,cancel CancelFunc){} //Create a Context with a timeout timefunc WithTimeout(parent Context,timeout )(Context,CancelFunc){} //Create a Context with a deadlinefunc WithDeadline(parent Context,d )(Context,CancelFunc){}
WithCancel/WithTimeout/WithDeadline
The end notification is automatically triggered through a timer, which means that aDone
child node and return the child node'sCancelFunc
Function handle.
Encapsulate custom Context
You can define your own Context, which first has the most basic request and response parameters. Finally, because you think about the writer that concurrently writes resposne, you need to add lock member variables and timeout flags to prevent repeated writes.
package framework import ( "context" "encoding/json" "net/http" "sync" ) type Context struct { Request * ResponseWriter hasTimeOut bool // Whether to timeout flag bit writerMux * } func NewContext()*Context{ return &Context{} } func (ctx *Context) BaseContext() { return () } func (ctx *Context) Done() <-chan struct{} { return ().Done() } func (ctx *Context)SetHasTimeOut(){ =true } func (ctx *Context)HasTimeOut()bool{ return } // Method to encapsulate a Json by yourselffunc (ctx *Context) Json(status int, obj interface{}) (err error) { if (){ return nil } bytes, err := (obj) (status) _, err = (bytes) return } // Expose the lock to the outsidefunc (ctx *Context) WriterMux() * { return } // Unified processor Controller methodtype ControllerHandler func(c *Context) error
The business method uses the Context encapsulated by itself, which takes into account timeout control, concurrent read and write, and processing panic
package main import ( "context" "fmt" "testdemo1/coredemo/framework" "time" ) func FooController(ctx *) error { durationCtx, cancel := ((), ) defer cancel() finish := make(chan struct{}, 1) panicChan := make(chan interface{}, 1) go func() { defer func() { if p := recover(); p != nil { panicChan <- p } }() ( * 10) finish <- struct{}{} }() select { case p := <-panicChan: // panic ("panic:",p) ().Lock() // Prevent the messaging of multiple coroutines from being out of order defer ().Unlock() (500, "panic") case <-finish: // Exit normally (200, "ok") ("finish") case <-(): // Timeout event ().Lock() defer ().Unlock() (500, "timed out") () // Prevent multiple coroutines from repeatedly writing timeout logs } return nil }
The serverHandler class can handle the request logic first, and you can register the corresponding mapper and method
package framework import ( "net/http" ) type Core struct { RouterMap map[string]ControllerHandler } func (c Core) ServeHTTP(writer , request *) { (writer, request) } func NewCore() *Core { return &Core{ RouterMap:make(map[string]ControllerHandler,0), } } // Register Get methodfunc (c *Core) Get(pattern string, handler ControllerHandler) { ["get"+"-"+pattern]=handler } // Register Post methodfunc (c *Core) Post(pattern string, handler ControllerHandler) { ["post"+"-"+pattern]=handler }
Register the router unified management and register it into our corresponding http method into our request logic class
package main import "testdemo1/coredemo/framework" func registerRouter(core *){ // Set up the controller ("foo",FooController) }
Finally, the main program executes http service listening and calls to register the initial router! Pass it into our customizedContext
package main import ( "log" "net/http" "testdemo1/coredemo/framework" ) func main() { server:=&{Addr: ":8080",Handler: ()} // Register router registerRouter(()) err := () if err!=nil{ (err) } }
This article ends here! You can implement it yourself and experience it. The actual source code packaging is similar to gin~
usNext articlegoodbye
This is the article about the analysis of the implementation principle of Golang HTTP service timeout control. For more related content on Golang HTTP service timeout control, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!