最近公司大部分项目开始往golang换, api的框架选定使用gin, 于是将 gin的源码看了一遍, 会用几篇文章将gin的流程及流程做一个梳理, 下面进入正题.

gin框架预览

上图大概是 gin里面比较重要的模块. 从 gin的官方第一个demo入手.

package main

import "github.com/gin-gonic/gin"

func main() {

r := gin.Default()

r.GET("/ping", func(c *gin.Context) {

c.JSON(200, gin.H{

"message": "pong",

})

})

r.Run() // listen and serve on 0.0.0.0:8080

}

r.Run()的源码:

func (engine *Engine) Run(addr ...string) (err error) {

defer func() { debugPrintError(err) }()

address := resolveAddress(addr)

debugPrint("Listening and serving HTTP on %s\n", address)

err = http.ListenAndServe(address, engine)

return

}

然后看到开始调用的是 http.ListenAndServe(address,engine), 这个函数是 net/http的函数. 然后请求数据就在 net/http开始流转.

所以, gin源码阅读系列就是要弄明白以下几个问题:

  1. request数据是如何流转的

  2. gin框架到底扮演了什么角色

  3. 请求从gin流入net/http, 最后又是如何回到gin中

  4. gin的context为何能承担起来复杂的需求

  5. gin的路由算法

  6. gin的中间件是什么

  7. gin的Engine具体是个什么东西

  8. net/http的requeset, response都提供了哪些有用的东西

request数据是如何流转的

先不使用 gin, 直接使用 net/http来处理http请求

func main() {

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello World"))

})

if err := http.ListenAndServe(":8000", nil); err != nil {

fmt.Println("start http server fail:", err)

}

}

在浏览器中输入 localhost:8000, 会看到 HelloWorld. 下面利用这个简单demo看下 request的流转流程.

HTTP是如何建立起来的

简单的说一下http请求是如何建立起来的(需要有基本的网络基础, 可以找相关的书籍查看, 推荐看 UNIX网络编程卷1:套接字联网API)

TCP/IP五层模型下, HTTP位于 应用层, 需要有 传输层来承载 HTTP协议. 传输层比较常见的协议是 TCP, UDP, SCTP等. 由于 UDP不可靠, SCTP有自己特殊的运用场景, 所以一般情况下 HTTP是由 TCP协议承载的(可以使用wireshark抓包然后查看各层协议)

使用 TCP协议的话, 就会涉及到 TCP是如何建立起来的. 面试中能够常遇到的名词 三次握手, 四次挥手就是在这里产生的. 具体的建立流程就不在陈述了, 大概流程就是图中左半边

所以说, 要想能够对客户端http请求进行回应的话, 就首先需要建立起来TCP连接, 也就是 socket. 下面要看下 net/http是如何建立起来 socket?

net/http是如何建立 socket

从图上可以看出, 不管server代码如何封装, 都离不开 bind, listen, accept这些函数. 就从上面这个简单的demo入手查看源码.

func main() {

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello World"))

})

if err := http.ListenAndServe(":8000", nil); err != nil {

fmt.Println("start http server fail:", err)

}

}

注册路由

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {

w.Write([]byte("Hello World"))

})

这段代码是在注册一个路由及这个路由的handler到 DefaultServeMux

// server.go:L2366-2388

func (mux *ServeMux) Handle(pattern string, handler Handler) {

mux.mu.Lock()

defer mux.mu.Unlock()

if pattern == "" {

panic("http: invalid pattern")

}

if handler == nil {

panic("http: nil handler")

}

if _, exist := mux.m[pattern]; exist {

panic("http: multiple registrations for " + pattern)

}

if mux.m == nil {

mux.m = make(map[string]muxEntry)

}

mux.m[pattern] = muxEntry{h: handler, pattern: pattern}

if pattern[0] != '/' {

mux.hosts = true

}

}

可以看到这个路由注册太过简单了, 也就给 gin, iris, echo等框架留下了扩展的空间, 后面详细说这个东西

服务监听及响应

上面路由已经注册到 net/http了, 下面就该如何建立socket了, 以及最后又如何取到已经注册到的路由, 将正确的响应信息从handler中取出来返回给客户端

if err := http.ListenAndServe(":8000", nil); err != nil {

fmt.Println("start http server fail:", err)

}

// net/http/server.go:L3002-3005

func ListenAndServe(addr string, handler Handler) error {

server := &Server{Addr: addr, Handler: handler}

return server.ListenAndServe()

}

// net/http/server.go:L2752-2765

func (srv *Server) ListenAndServe() error {

// ... 省略代码

ln, err := net.Listen("tcp", addr) //

if err != nil {

return err

}

return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})

}

// net/http/server.go:L2805-2853

func (srv *Server) Serve(l net.Listener) error {

// ... 省略代码

for {

rw, e := l.Accept() //

if e != nil {

select {

case srv.getDoneChan():

return ErrServerClosed

default:

}

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 := srv.newConn(rw)

c.setState(c.rwc, StateNew) // before Serve can return

go c.serve(ctx) //

}

}

// net/http/server.go:L1739-1878

func (c *conn) serve(ctx context.Context) {

// ... 省略代码

serverHandler{c.server}.ServeHTTP(w, w.req)

w.cancelCtx()

if c.hijacked() {

return

}

w.finishRequest()

// ... 省略代码

}

// net/http/server.go:L2733-2742

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {

handler := sh.srv.Handler

if handler == nil {

handler = DefaultServeMux

}

if req.RequestURI == "*" && req.Method == "OPTIONS" {

handler = globalOptionsHandler{}

}

handler.ServeHTTP(rw, req)

}

// net/http/server.go:L2352-2362

func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {

if r.RequestURI == "*" {

if r.ProtoAtLeast(1, 1) {

w.Header().Set("Connection", "close")

}

w.WriteHeader(StatusBadRequest)

return

}

h, _ := mux.Handler(r) //

h.ServeHTTP(w, r)

}

// net/http/server.go:L1963-1965

func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {

f(w, r)

}

这基本是整个过程的代码了. 基本上是:

  1. ln,err:=net.Listen("tcp",addr)做了 初试化了socketbindlisten的操作.

  2. rw,e:=l.Accept()进行accept, 等待客户端进行连接

  3. go c.serve(ctx) 启动新的goroutine来处理本次请求. 同时主goroutine继续等待客户端连接, 进行高并发操作

  4. h,_:=mux.Handler(r) 获取注册的路由, 然后拿到这个路由的handler, 然后将处理结果返回给客户端

从这里也能够看出来, net/http基本上提供了全套的服务.

为什么会出现很多go框架

// net/http/server.go:L2218-2238

func (mux *ServeMux) match(path string) (h Handler, pattern string) {

// Check for exact match first.

v, ok := mux.m[path]

if ok {

return v.h, v.pattern

}

// Check for longest valid match.

var n = 0

for k, v := range mux.m {

if !pathMatch(k, path) {

continue

}

if h == nil || len(k) > n {

n = len(k)

h = v.h

pattern = v.pattern

}

}

return

}

从这段函数可以看出来, 匹配规则过于简单, 当能匹配到路由的时候就返回其对应的handler, 当不能匹配到时就返回 /. 所以 net/http的路由匹配无法满足复杂的需求开发. 所以基本所有的go框架干的最主要的一件事情就是重写 net/http的route

所以我们直接说 gin就是一个httprouter也不过分, 当然 gin也提供了其他比较主要的功能, 后面会一一介绍

还有一个go框架要实现的东西是 http.ResponseWriter

综述, net/http基本已经提供 http服务的70%的功能, 那些号称贼快的go框架, 基本上都是提供一些功能, 让我们能够更好的处理客户端发来的请求.

  1. T

android tcp socket框架_最流行的 Web 框架 Gin 源码阅读相关推荐

  1. python records库_你的第一份Python库源码阅读:records库

    基本介绍 records是kennethreitz的for Humans™系列,使用原生sql去操作大多数的关系型数据库(Postgresql, MySQL, SQLite, Oracle和 MS-S ...

  2. Go框架 gin 源码学习--路由的实现原理剖析

    往期回顾: gin源码解析 - gin 与 net/http 的关系 gin 源码解析 - 详解http请求在gin中的流转过程 上面两篇文章基本讲清楚了 Web Server 如何接收客户端请求,以 ...

  3. Flask 框架 是 Python 中最流行的 Web 框架之一

    Flask 是 Python 中最流行的 Web 框架之一,以小巧.灵活.可扩展性强著称, 熟练掌握Flask 框架,深入解析Flask 框架的精髓,希望掌握Flask 最佳实践项目. Flask W ...

  4. java 友好时间显示_仿微信的IM聊天时间显示格式(含iOS/Android/Web实现)[图文+源码]...

    本文为原创分享,转载请注明出处. 1.引言 即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成"年/月/日 时: ...

  5. java毕业设计——基于Java+Socket的视频会议系统设计与实现(毕业论文+程序源码)——视频会议系统

    基于Java+Socket的视频会议系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Java+Socket的视频会议系统设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦.需要下 ...

  6. 仿微信的IM聊天时间显示格式(含iOS/Android/Web实现)[图文+源码]

    本文为原创分享,转载请注明出处. 1.引言 即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成"年/月/日 时: ...

  7. Android Pie源码阅读 -----深入理解init(一)

    在阅读源码之前,我们应要熟悉整个Android的系统架构,在针对某一层进行深入理解,否则东看西看没有方向没有目标会导致思绪紊乱,这样读源码的效果不佳 这篇文章主要参考gityuan阅读Android ...

  8. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...

  9. 源码 状态机_[源码阅读] 阿里SOFA服务注册中心MetaServer(1)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简介 1.2 SOFARegistry 总体架构 1.3 为什么要分层 0 ...

最新文章

  1. java将一个类到多个类,java-设计模式,将转换应用于多个类中的多个...
  2. ArcSDE建Table在ArcCatalog中不可见
  3. 带你学python基础:文件读写,俗称IO操作
  4. TCP为什么需要三次握手和四次挥手
  5. 移动设备测试 6 free mobile device emulators for testing your site
  6. KafkaProducer介绍
  7. Openssl 嵌入式arm移植笔记
  8. C++之指针探究(十三):函数指针数组
  9. 【数字识别】基于matlab离散Hopfield神经网络数字识别【含Matlab源码 226期】
  10. 数据库锁机制和CAS概念
  11. mo java_mojava和 high sierra系统区别?
  12. 7-1 愿天下有情人都是失散多年的兄妹 (25 分)
  13. 云服务器流量是什么东西_云服务器带宽与流量都有什么关系?不限制流量服务器...
  14. 超过2T硬盘用不了,怎么办?
  15. Android Parcel对象详解
  16. Codeforces - King Kog‘s Reception
  17. 自然语言处理之中文文本分析(jieba分词、词袋doc2bow、TFIDF文本挖掘)
  18. Excel函数 - 提取月份,转换月份格式
  19. java学习视频网课收集
  20. 再次慨叹生命的脆弱,我辈当惜之

热门文章

  1. 1.20 正则表达式详解
  2. 刷算法题总结的一些结论公式
  3. 寒假每日一题2022【week1 完结】
  4. 2021算法竞赛入门班第八节课【数学】习题
  5. 1026 Table Tennis (30 分) 未完成【难度: 难 / 知识点: 模拟】
  6. qmoc文件_手动生成MOC文件
  7. python oracle数据库开发_python连接Oracle数据库
  8. 完全二叉树最小深度_二叉树:我有多少个节点?
  9. python 求连线相似度_Python分析《都挺好》中的人物关系,苏大强与蔡根花是真爱?...
  10. 推荐 19 个 github 超牛逼项目!