在没文档指导下阅读源码比较麻烦。

我们知道,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的源码相关推荐

  1. Transformers包tokenizer.encode()方法源码阅读笔记

    Transformers包tokenizer.encode()方法源码阅读笔记_天才小呵呵的博客-CSDN博客_tokenizer.encode

  2. golang学习之negroni/gizp源码分析

    在 Go 语言里,Negroni 是一个很地道的 Web 中间件,它是一个具备微型.非嵌入式.鼓励使用原生 net/http 库特征的中间件.利用它地Use功能,我们可以很简单地自定义中间件并使用.其 ...

  3. mysql源码包和二进制包_Linux软件包(源码包和二进制包)

    Linux下的软件包众多,且几乎都是经 GPL 授权.免费开源(无偿公开源代码)的.这意味着如果你具备修改软件源代码的能力,只要你愿意,可以随意修改. GPL,全称 General Public Li ...

  4. 易启秀20150629完整包微场景制作源码,新增1.4G素材包,全新后台UI设计+采集

    易启秀20150629完整包微场景制作源码,新增1.4G素材包,全新后台UI设计+采集 本文由 穷站长 于 2015-7-18 9:23 Saturday 发布在 php源码 评论(46) 最新版易启 ...

  5. HanLP自然语言处理包开源(包含源码)

    HanLP自然语言处理包开源(包含源码) 支持中文分词(N-最短路分词.CRF分词.索引分词.用户自定义词典.词性标注),命名实体识别(中国人名.音译人名.日本人名.地名.实体机构名识别),关键词提取 ...

  6. .jar文件还原java文件_从jar包还原出java源码(项目文件)

    原文转载至:https://blog.csdn.net/mxmxz/article/details/73043156 上周接到个新任务,一个遗留的接口工程需要改造,然而根据前任开发留下的文档看,这个工 ...

  7. 微信小程序:开心锤锤超火动态表情包微信小程序源码下载自动采集

    这是一款表情包小程序源码 大家刷抖音的时候应该都刷过开心锤锤这个网红卡通短视频吧 现在这一款小程序就是和它有关的 里面的表情包呢大部分都是动态表情包(斗图的时候是不是更炫) 至于里面的表情包人物的就都 ...

  8. 【微信小程序系列】小程序简单连接后端数据库完整示例(附免费下载的源码)(Servlet)

    [微信小程序系列]小程序简单连接后端数据库完整示例(附免费下载的源码)(Servlet) 登录页面 login.wxml <view class="page">< ...

  9. RPM包安装或者安装源码包

    在windows下安装一个软件很轻松,只要双击.exe的文件,安装提示连续"下一步"即可,然而linux系统下安装一个软件似乎并不那么轻松了,因为我们不是在图形界面下.所以你要学会 ...

最新文章

  1. 微生物常见20种培养基配方
  2. swif-自动引用计数
  3. npm常用命令:ini他、install、remove及编译运行工程、使用淘宝npm镜像
  4. 白话经典算法系列之一 冒泡排序的三种实现
  5. bkg bnc_BNC的完整形式是什么?
  6. (转载)arcgis for js - 解决加载天地图和WMTS服务,WMTS服务不显示的问题,以及wmts服务密钥。...
  7. mysql+imx6+移植_imx6ulevk---MfgTool的使用心得
  8. linux安装sublime,linux 安装sublime text3
  9. php单例模式详解,PHP 单例模式解析和实战
  10. pdfFactory 7PDF 虚拟打印机官方注册码版下载
  11. 基于51单片机的知识
  12. 时间排序的SACK未确认报文链表
  13. _3_body_标签
  14. python人工智能是什么意思_python人工智能是什么意思
  15. JAVAWeb学习笔记2020/5/15——JavaScript高级笔记
  16. MPU6050姿态融合解算(DMP)
  17. 算法001-C++从0到1实现链表v1
  18. Markdown基本使用
  19. 服务器网卡驱动是在哪个文件夹,DL380G5服务器网卡驱动的安装
  20. 华纳云:香港机房基础网络架构

热门文章

  1. 26 27 28 副词
  2. Python复盘股票_3. 超短的复盘框架
  3. Codeforces Round #176 (Div. 2) D. Shifting(模拟,STLdeque应用)
  4. 化妆品行业电商平台系统解决方案
  5. ElasticSearch必知必会-基础篇
  6. 折线多边形的原位放大
  7. 微信js-sdk 微信自定义分享显示图片和描述不显示
  8. UltraEdit 25以后的版本 绕过试用期
  9. 基于容器的后端服务架构
  10. 报错WARNING: Ignoring invalid distribution -pencv-python