android tcp socket框架_最流行的 Web 框架 Gin 源码阅读
最近公司大部分项目开始往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源码阅读系列
就是要弄明白以下几个问题:
request数据是如何流转的
gin框架到底扮演了什么角色
请求从gin流入net/http, 最后又是如何回到gin中
gin的context为何能承担起来复杂的需求
gin的路由算法
gin的中间件是什么
gin的Engine具体是个什么东西
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)
}
这基本是整个过程的代码了. 基本上是:
ln,err:=net.Listen("tcp",addr)
做了初试化了socket
,bind
,listen
的操作.rw,e:=l.Accept()
进行accept, 等待客户端进行连接go c.serve(ctx)
启动新的goroutine来处理本次请求. 同时主goroutine继续等待客户端连接, 进行高并发操作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框架, 基本上都是提供一些功能, 让我们能够更好的处理客户端发来的请求.
T
android tcp socket框架_最流行的 Web 框架 Gin 源码阅读相关推荐
- python records库_你的第一份Python库源码阅读:records库
基本介绍 records是kennethreitz的for Humans™系列,使用原生sql去操作大多数的关系型数据库(Postgresql, MySQL, SQLite, Oracle和 MS-S ...
- Go框架 gin 源码学习--路由的实现原理剖析
往期回顾: gin源码解析 - gin 与 net/http 的关系 gin 源码解析 - 详解http请求在gin中的流转过程 上面两篇文章基本讲清楚了 Web Server 如何接收客户端请求,以 ...
- Flask 框架 是 Python 中最流行的 Web 框架之一
Flask 是 Python 中最流行的 Web 框架之一,以小巧.灵活.可扩展性强著称, 熟练掌握Flask 框架,深入解析Flask 框架的精髓,希望掌握Flask 最佳实践项目. Flask W ...
- java 友好时间显示_仿微信的IM聊天时间显示格式(含iOS/Android/Web实现)[图文+源码]...
本文为原创分享,转载请注明出处. 1.引言 即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成"年/月/日 时: ...
- java毕业设计——基于Java+Socket的视频会议系统设计与实现(毕业论文+程序源码)——视频会议系统
基于Java+Socket的视频会议系统设计与实现(毕业论文+程序源码) 大家好,今天给大家介绍基于Java+Socket的视频会议系统设计与实现,文章末尾附有本毕业设计的论文和源码下载地址哦.需要下 ...
- 仿微信的IM聊天时间显示格式(含iOS/Android/Web实现)[图文+源码]
本文为原创分享,转载请注明出处. 1.引言 即时通讯IM应用中的聊天消息时间显示是个再常见不过的需求,现在都讲究用户体验,所以时间显示再也不能像传统软件一样简单粗地暴显示成"年/月/日 时: ...
- Android Pie源码阅读 -----深入理解init(一)
在阅读源码之前,我们应要熟悉整个Android的系统架构,在针对某一层进行深入理解,否则东看西看没有方向没有目标会导致思绪紊乱,这样读源码的效果不佳 这篇文章主要参考gityuan阅读Android ...
- CI框架源码阅读笔记4 引导文件CodeIgniter.php
到了这里,终于进入CI框架的核心了.既然是"引导"文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http:// ...
- 源码 状态机_[源码阅读] 阿里SOFA服务注册中心MetaServer(1)
[源码阅读] 阿里SOFA服务注册中心MetaServer(1) 0x00 摘要 0x01 服务注册中心 1.1 服务注册中心简介 1.2 SOFARegistry 总体架构 1.3 为什么要分层 0 ...
最新文章
- java将一个类到多个类,java-设计模式,将转换应用于多个类中的多个...
- ArcSDE建Table在ArcCatalog中不可见
- 带你学python基础:文件读写,俗称IO操作
- TCP为什么需要三次握手和四次挥手
- 移动设备测试 6 free mobile device emulators for testing your site
- KafkaProducer介绍
- Openssl 嵌入式arm移植笔记
- C++之指针探究(十三):函数指针数组
- 【数字识别】基于matlab离散Hopfield神经网络数字识别【含Matlab源码 226期】
- 数据库锁机制和CAS概念
- mo java_mojava和 high sierra系统区别?
- 7-1 愿天下有情人都是失散多年的兄妹 (25 分)
- 云服务器流量是什么东西_云服务器带宽与流量都有什么关系?不限制流量服务器...
- 超过2T硬盘用不了,怎么办?
- Android Parcel对象详解
- Codeforces - King Kog‘s Reception
- 自然语言处理之中文文本分析(jieba分词、词袋doc2bow、TFIDF文本挖掘)
- Excel函数 - 提取月份,转换月份格式
- java学习视频网课收集
- 再次慨叹生命的脆弱,我辈当惜之
热门文章
- 1.20 正则表达式详解
- 刷算法题总结的一些结论公式
- 寒假每日一题2022【week1 完结】
- 2021算法竞赛入门班第八节课【数学】习题
- 1026 Table Tennis (30 分) 未完成【难度: 难 / 知识点: 模拟】
- qmoc文件_手动生成MOC文件
- python oracle数据库开发_python连接Oracle数据库
- 完全二叉树最小深度_二叉树:我有多少个节点?
- python 求连线相似度_Python分析《都挺好》中的人物关系,苏大强与蔡根花是真爱?...
- 推荐 19 个 github 超牛逼项目!