简单阅读golang的net/http包和Negroni的源码
在没文档指导下阅读源码比较麻烦。
我们知道,golang比起c/c++来说,已经内建了http服务的功能,而且因为golang的特性,性能不低。
下面通过阅读net/http的部分源码来看看它是怎么工作的:
func ListenAndServe(addr string, handler Handler) error {server := &Server{Addr: addr, Handler: handler}return server.ListenAndServe()
}
从ListenAndServe方法开始,这时直接调用http.ListenAndServe(addr,handler) ,它实际上是以自己接受到的参数来构造一个Server struct,然后再调用这个Server struct的ListenAndServe方法。这个Handler是用来决定使用的处理request的Handler。
func (srv *Server) ListenAndServe() error {addr := srv.Addrif addr == "" {addr = ":http"}ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
这个就是在用更底层的方法开始监听端口了,反回了srv.Serve(...)
func (srv *Server) Serve(l net.Listener) error {defer l.Close()if fn := testHookServerServe; fn != nil {fn(srv, l)}var tempDelay time.Duration // how long to sleep on accept failureif err := srv.setupHTTP2_Serve(); err != nil {return err}srv.trackListener(l, true)defer srv.trackListener(l, false)baseCtx := context.Background() // base is always background, per Issue 16220ctx := context.WithValue(baseCtx, ServerContextKey, srv)ctx = context.WithValue(ctx, LocalAddrContextKey, l.Addr())for {rw, e := l.Accept()if e != nil {select {case <-srv.getDoneChan():return ErrServerCloseddefault:}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 = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew) // before Serve can returngo c.serve(ctx)}
}
这里就是写一些上下文信息,然后是一个循环,等待客户端发来信息然后Accept,这个循环中大部分代码是在处理出错后的问题,暂时忽略,如果成功Accept,就新创建一个连接来处理它,注意循环最后的
go c.serve(ctx)
这是利用goroutine来实现并发,执行完这句会有一个goroutine来执行c.serve方法,然后循环不会被阻塞,继续接收信息。
再来看看默认的Handler的定义:
type ServeMux struct {mu sync.RWMutexm map[string]muxEntryhosts bool // whether any patterns contain hostnames
}
它是怎么进行路由的?
func (mux *ServeMux) match(path string) (h Handler, pattern string) {var n = 0for k, v := range mux.m {if !pathMatch(k, path) {continue}if h == nil || len(k) > n {n = len(k)h = v.hpattern = v.pattern}}return
}
上面这是默认的ServeMux用来匹配请求的match方法,它会找出和path匹配程度最高(就是前缀匹配最长)的一条路由记录,然后我们看看关键的pathMatch方法:
func pathMatch(pattern, path string) bool {if len(pattern) == 0 {// should not happenreturn false}n := len(pattern)if pattern[n-1] != '/' {return pattern == path}return len(path) >= n && path[0:n] == pattern
}
可以看出这个匹配方法简单,就是直接逐字匹配,并不支持正则表达式等更加高级的路由功能。
如果想要用更强的路由功能,可以用第三方的gorilla/mux来实现。
再来看看Negroni的代码,这个包为golang实现了简单的中间件(middleware)机制。
type HandlerFunc func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc)func (h HandlerFunc) ServeHTTP(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {h(rw, r, next)
}
上面是一个定义,类似于http包中原生的HandlerFunc,它也有一个ServeHTTP方法,但注意,他们的参数多了一个。这样做是为了让Negroni兼容非Negroni自带的函数,也就是为这些函数加上ServeHTTP方法
type middleware struct {handler Handlernext *middleware
}func (m middleware) ServeHTTP(rw http.ResponseWriter, r *http.Request) {m.handler.ServeHTTP(rw, r, m.next.ServeHTTP)
}
中间件的定义,可以看到当一个中间件调用ServeHTTP方法时,实际上是Handler起作用。将会一层一层调用下去。
func Wrap(handler http.Handler) Handler {return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {handler.ServeHTTP(rw, r)next(rw, r)})
}func WrapFunc(handlerFunc http.HandlerFunc) Handler {return HandlerFunc(func(rw http.ResponseWriter, r *http.Request, next http.HandlerFunc) {handlerFunc(rw, r)next(rw, r)})
}
这两个函数很有意思,Negroni可以直接用http.Handler和http.HandlerFunc来作为中间件,是怎么实现的?上面就是关键步骤,上面两个
Warp方法将http.Handler和http.HandlerFunc“包裹”住,构造了并返回一个有3个参数的HandlerFunc函数,这样就能放入中间件中使用了!
总结:golang编程,博大精深,知其原理不易,熟用之更加不易。
简单阅读golang的net/http包和Negroni的源码相关推荐
- Transformers包tokenizer.encode()方法源码阅读笔记
Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode
- golang学习之negroni/gizp源码分析
在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它是一个具备微型.非嵌入式.鼓励使用原生 net/http 库特征的中间件.利用它地Use功能,我们可以很简单地自定义中间件并使用.其 ...
- mysql源码包和二进制包_Linux软件包(源码包和二进制包)
Linux下的软件包众多,且几乎都是经 GPL 授权.免费开源(无偿公开源代码)的.这意味着如果你具备修改软件源代码的能力,只要你愿意,可以随意修改. GPL,全称 General Public Li ...
- 易启秀20150629完整包微场景制作源码,新增1.4G素材包,全新后台UI设计+采集
易启秀20150629完整包微场景制作源码,新增1.4G素材包,全新后台UI设计+采集 本文由 穷站长 于 2015-7-18 9:23 Saturday 发布在 php源码 评论(46) 最新版易启 ...
- HanLP自然语言处理包开源(包含源码)
HanLP自然语言处理包开源(包含源码) 支持中文分词(N-最短路分词.CRF分词.索引分词.用户自定义词典.词性标注),命名实体识别(中国人名.音译人名.日本人名.地名.实体机构名识别),关键词提取 ...
- .jar文件还原java文件_从jar包还原出java源码(项目文件)
原文转载至:https://blog.csdn.net/mxmxz/article/details/73043156 上周接到个新任务,一个遗留的接口工程需要改造,然而根据前任开发留下的文档看,这个工 ...
- 微信小程序:开心锤锤超火动态表情包微信小程序源码下载自动采集
这是一款表情包小程序源码 大家刷抖音的时候应该都刷过开心锤锤这个网红卡通短视频吧 现在这一款小程序就是和它有关的 里面的表情包呢大部分都是动态表情包(斗图的时候是不是更炫) 至于里面的表情包人物的就都 ...
- 【微信小程序系列】小程序简单连接后端数据库完整示例(附免费下载的源码)(Servlet)
[微信小程序系列]小程序简单连接后端数据库完整示例(附免费下载的源码)(Servlet) 登录页面 login.wxml <view class="page">< ...
- RPM包安装或者安装源码包
在windows下安装一个软件很轻松,只要双击.exe的文件,安装提示连续"下一步"即可,然而linux系统下安装一个软件似乎并不那么轻松了,因为我们不是在图形界面下.所以你要学会 ...
最新文章
- 微生物常见20种培养基配方
- swif-自动引用计数
- npm常用命令:ini他、install、remove及编译运行工程、使用淘宝npm镜像
- 白话经典算法系列之一 冒泡排序的三种实现
- bkg bnc_BNC的完整形式是什么?
- (转载)arcgis for js - 解决加载天地图和WMTS服务,WMTS服务不显示的问题,以及wmts服务密钥。...
- mysql+imx6+移植_imx6ulevk---MfgTool的使用心得
- linux安装sublime,linux 安装sublime text3
- php单例模式详解,PHP 单例模式解析和实战
- pdfFactory 7PDF 虚拟打印机官方注册码版下载
- 基于51单片机的知识
- 时间排序的SACK未确认报文链表
- _3_body_标签
- python人工智能是什么意思_python人工智能是什么意思
- JAVAWeb学习笔记2020/5/15——JavaScript高级笔记
- MPU6050姿态融合解算(DMP)
- 算法001-C++从0到1实现链表v1
- Markdown基本使用
- 服务器网卡驱动是在哪个文件夹,DL380G5服务器网卡驱动的安装
- 华纳云:香港机房基础网络架构
热门文章
- 26 27 28 副词
- Python复盘股票_3. 超短的复盘框架
- Codeforces Round #176 (Div. 2) D. Shifting(模拟,STLdeque应用)
- 化妆品行业电商平台系统解决方案
- ElasticSearch必知必会-基础篇
- 折线多边形的原位放大
- 微信js-sdk 微信自定义分享显示图片和描述不显示
- UltraEdit 25以后的版本 绕过试用期
- 基于容器的后端服务架构
- 报错WARNING: Ignoring invalid distribution -pencv-python