Gin 服务框架服务端使用示例:
package main
import "github.com/gin-gonic/gin"
func main(){
// 建立http路由
router := gin.Default()
router.GET("/gin/test/", func(context *gin.Context) {
context.JSON(200,gin.H{
"msg":"success",
})
})
router.Run(":6789")
}
建立Gin路由
返回一个“引擎“,定义其日志组件和panic处理组件
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
engine := New()
// 默认只开起日志和异常恢复的中间件
engine.Use(Logger(), Recovery())
return engine
}
// New returns a new blank Engine instance without any middleware attached.
// By default the configuration is:
// - RedirectTrailingSlash: true
// - RedirectFixedPath: false
// - HandleMethodNotAllowed: false
// - ForwardedByClientIP: true
// - UseRawPath: false
// - UnescapePathValues: true
func New() *Engine {
engine := &Engine{
// 定义根路径的handler
RouterGroup: RouterGroup{
Handlers: nil,
basePath: "/",
root: true,
},
// 定义模版 map[string]interface{}
FuncMap: template.FuncMap{},
RedirectTrailingSlash: true,
RedirectFixedPath: false,
HandleMethodNotAllowed: false,
ForwardedByClientIP: true,
AppEngine: defaultAppEngine,
UseRawPath: false,
UnescapePathValues: true,
MaxMultipartMemory: defaultMultipartMemory,
trees: make(methodTrees, 0, 9),
delims: render.Delims{Left: "{{", Right: "}}"},
secureJsonPrefix: "while(1);",
}
engine.RouterGroup.engine = engine
// sync.Pool
engine.pool.New = func() interface{} {
// 分配新context,实际上就是缓冲区
return engine.allocateContext()
}
return engine
}
router 建立路由
// 定义所有路由
type methodTrees []methodTree
type methodTree struct {
method string
root *node
}
type Engine struct {
RouterGroup
...
trees methodTrees
pool sync.Pool // gin context 缓存池,可reuse
...
}
type RouterGroup struct {
// 中间件数组
Handlers HandlersChain
basePath string
engine *Engine
root bool
}
// 注册中间件
func (group *RouterGroup) Use(middleware ...HandlerFunc) IRoutes {
group.Handlers = append(group.Handlers, middleware...)
return group.returnObj()
}
// 注册访问路由
func (group *RouterGroup) Any(relativePath string, handlers ...HandlerFunc) IRoutes {
group.handle("GET", relativePath, handlers)
group.handle("POST", relativePath, handlers)
group.handle("PUT", relativePath, handlers)
group.handle("PATCH", relativePath, handlers)
group.handle("HEAD", relativePath, handlers)
group.handle("OPTIONS", relativePath, handlers)
group.handle("DELETE", relativePath, handlers)
group.handle("CONNECT", relativePath, handlers)
group.handle("TRACE", relativePath, handlers)
return group.returnObj()
}
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes {
// 计算绝对路径
absolutePath := group.calculateAbsolutePath(relativePath)
// 执行方法与中间件方法合并为一个数组
handlers = group.combineHandlers(handlers)
group.engine.addRoute(httpMethod, absolutePath, handlers)
return group.returnObj()
}
// 把新路由加入树中,每个方法是一个树的根节点
func (engine *Engine) addRoute(method, path string, handlers HandlersChain) {
...
root := engine.trees.get(method)
if root == nil {
root = new(node)
engine.trees = append(engine.trees, methodTree{method: method, root: root})
}
// 大体形成类似于字典树[路径=>handlers]
// 具体实现逻辑可参考 github.com/gin-gonic/gin/tree.go,httprouter相关参考:http://www.okyes.me/2016/05/08/httprouter.html
root.addRoute(path, handlers)
}
Run Gin Server
// 相当于go net/http 封装
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
// gin engine 实现了net/http的Handler,相当于gin自己做路由分发
err = http.ListenAndServe(address, engine)
return
}
// net/http 监听端口
func ListenAndServe(addr string, handler Handler) error {
// new server
server := &Server{Addr: addr, Handler: handler}
// listen the port
return server.ListenAndServe() // 底层逻辑调用Server函数,监听端口+接收可读事件+goroutine处理请求
}
// 监听端口+接收可读事件+goroutine处理请求
func (srv *Server) Serve(l net.Listener) error {
defer l.Close()
// 没有事件到达时,sleep多久
var tempDelay time.Duration // how long to sleep on accept failure
srv.trackListener(l, true)
defer srv.trackListener(l, false)
// 主goroutine
baseCtx := context.Background() // base is always background, per Issue 16220
// 绑定参数
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())
// accept request
for {
// 端口是否可读
rw, e := l.Accept()
if e != nil { //不可读或者端口异常、服务异常
select {
case <-srv.getDoneChan(): // 如果 http 服务关闭,直接退出程序
return ErrServerClosed
default:
}
if ne, ok := e.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 { // 如果tempDelay == 0
tempDelay = 5 * time.Millisecond // 则 tempDelay == 5ms
} else {
tempDelay *= 2 // 否则 tempDelay * 2
}
// tempDelay限定最大1s
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
time.Sleep(tempDelay)
continue
}
return e
}
// 可读=>tempDelay sleep 时间归0
tempDelay = 0
// 读取http数据
c := srv.newConn(rw)
// 标示请求为新建状态StateNew,除此之外还有:StateActive、StateIdle、StateHijacked、StateClosed
c.setState(c.rwc, StateNew) // before Serve can return
// goroutine处理子context
go c.serve(ctx)
}
}
// conn中有
func (srv *Server) newConn(rwc net.Conn) *conn {
c := &conn{
server: srv,
rwc: rwc, //conn,涉及文件描述符操作:可参考 net/fd_unix.go
}
return c
}
handler conn
// 接上,子context绑定了一些参数,同时在子context中获取http请求相关信息,例如ip、port、urlpath、body等
func (c *conn) serve(ctx context.Context) {
// get remote addr
c.remoteAddr = c.rwc.RemoteAddr().String()
... // 处理异常
// 如果是https请求
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
// ... 设置读超时
// ... 设置写超时
// ssl/tls握手失败,参考链接:http://www.ruanyifeng.com/blog/2014/02/ssl_tls.html
c.tlsState = new(tls.ConnectionState)
*c.tlsState = tlsConn.ConnectionState()
... // 没理解这段校验代码,跳过
}
// HTTP/1.x from here on.
// 派生可cancel的context,即父context若完成或超时,子context也会cancel,防止资源泄漏
ctx, cancelCtx := context.WithCancel(ctx)
c.cancelCtx = cancelCtx
defer cancelCtx()
// 获取reader和writer
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
for {
// 组装response对象,其中包括request对象
w, err := c.readRequest(ctx)
if c.r.remain != c.server.initialReadLimitSize() {
// 修改当前context状态为活动中
c.setState(c.rwc, StateActive)
}
if err != nil {
...
// 组装过程出现问题,输出一些内容,然后直接退出
return
}
// 对req进行校验,以及包装其body,方便代码复用获取body
// 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}
}
} else if req.Header.get("Expect") != "" {
... // 这个校验没理解
return
}
// curReq atomic.Value
// 线程安全,conn关联response
c.curReq.Store(w)
// 异步读取body,通过sync.Cond 阻塞,直到完成读取唤醒阻塞
if requestBodyRemains(req.Body) {
registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
} else {
if w.conn.bufr.Buffered() > 0 {
w.conn.r.closeNotifyFromPipelinedRequest()
}
w.conn.r.startBackgroundRead()
}
// 处理http
serverHandler{c.server}.ServeHTTP(w, w.req)
// 结束请求
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
// 是否reuse
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
c.closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle)
c.curReq.Store((*response)(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))
if _, err := c.bufr.Peek(4); err != nil {
return
}
}
c.rwc.SetReadDeadline(time.Time{})
}
}
// goroutine 读取
func (cr *connReader) startBackgroundRead() {
cr.lock()
defer cr.unlock()
...
cr.inRead = true
cr.conn.rwc.SetReadDeadline(time.Time{})
go cr.backgroundRead()
}
// sync.Cond 逻辑处理
func (cr *connReader) backgroundRead() {
n, err := cr.conn.rwc.Read(cr.byteBuf[:])
cr.lock()
if n == 1 {
cr.hasByte = true
// We were at EOF already (since we wouldn't be in a
// background read otherwise), so this is a pipelined
// HTTP request.
cr.closeNotifyFromPipelinedRequest()
}
if ne, ok := err.(net.Error); ok && cr.aborted && ne.Timeout() {
// Ignore this error. It's the expected error from
// another goroutine calling abortPendingRead.
} else if err != nil {
cr.handleReadError(err)
}
cr.aborted = false
cr.inRead = false
cr.unlock()
// 唤醒阻塞
cr.cond.Broadcast()
}
处理http请求
// 是个interface
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
// gin 实现了这个接口,net/http调用跳至此
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
c := engine.pool.Get().(*Context) //reuse
c.writermem.reset(w)
c.Request = req
c.reset() //reset
engine.handleHTTPRequest(c)
engine.pool.Put(c) //reuse
}
// 大体逻辑
func (engine *Engine) handleHTTPRequest(context *Context) {
httpMethod := context.Request.Method
path := context.Request.URL.Path
unescape := false
if engine.UseRawPath && len(context.Request.URL.RawPath) > 0 {
path = context.Request.URL.RawPath
unescape = engine.UnescapePathValues
}
// 根据urlpath从字典树结构取出相对应的处理handler数组
// 忽略寻找路由相关逻辑
t := engine.trees
for i, tl := 0, len(t); i < tl; i++ {
if t[i].method == httpMethod {
root := t[i].root
// 找到对于的handler数组
handlers, params, tsr := root.getValue(path, context.Params, unescape)
if handlers != nil {
context.handlers = handlers
context.Params = params
context.Next()
// 写resp header
context.writermem.WriteHeaderNow()
return
}
...
}
}
...
// 404
}
// next遍历handler方法数组,然后调用
func (c *Context) Next() {
c.index++
s := int8(len(c.handlers))
for ; c.index < s; c.index++ {
c.handlers[c.index](c)
}
}
// gin context 定义,并非context
type Context struct {
writermem responseWriter
Request *http.Request
Writer ResponseWriter
Params Params
handlers HandlersChain // handler 数组
index int8 // 当前handler index
engine *Engine
// c.Set
Keys map[string]interface{}
...
}