ListenAndServe源码剖析
使用goland追踪阅读ListenAndServe源码,剖析服务器启动流程
ListenAndServe阅读
func ListenAndServe(addr string, handler Handler) error {
//1. 创建server
server := &Server{Addr: addr, Handler: handler}
//2. 启动server
return server.ListenAndServe()
}
注意:创建一个server,启动server,我们也可以按照这2个步骤去创建一个web服务
Server结构阅读
// 基类Closer接口,关闭所有链接停止服务
type Closer interface {
Close() error
} // 检查服务是否存活,里面定义了接口,接口的另类定义使用
// 奇怪的行为,不确定为什么这么做
func http2h1ServerKeepAlivesDisabled(hs *Server) bool {
var x interface{} = hs
// 临时定义接口,使用【奇怪的使用方法】
type I interface {
doKeepAlives() bool
}
if hs, ok := x.(I); ok {
return !hs.doKeepAlives()
}
return false
} //Server
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
Addr string // 监听的TCP地址
Handler Handler // 注册的路由处理方法 // 如果服务需要支持https协议 那么需要相应的配置
TLSConfig *tls.Config //读超时设置
ReadTimeout time.Duration // 读取请求头超时设置
ReadHeaderTimeout time.Duration // 写超时
WriteTimeout time.Duration // 请求直接最长的空闲时长
IdleTimeout time.Duration // 请求头最大的容量
MaxHeaderBytes int // HTTPS协议相关
TLSNextProto map[string]func(*Server, *tls.Conn, Handler) // 可以添回调函数,当客户端处于哪个状态时候可以执行某些动作
ConnState func(net.Conn, ConnState) // 错误日志器,不设置默认使用内置logger模块
ErrorLog *log.Logger //原子操作,是否保持长连接
disableKeepAlives int32 // accessed atomically.
//原子操作,服务要关闭了
inShutdown int32 // accessed atomically (non-zero means we're in Shutdown)
// https相关操作 用于初始化
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
// 互斥锁 保证资源的安全
mu sync.Mutex
// 服务套接字表,监听socket表
listeners map[*net.Listener]struct{}
// 存活的客户端链接表
activeConn map[*conn]struct{}
//用于通知服务关闭了
doneChan chan struct{} // 注册服务器关闭执行的一些行为
onShutdown []func()
}
注意:一般创建server只需要Addr与handler即可
ListenAndServe阅读
监听并启动服务
func (srv *Server) ListenAndServe() error {
// 判断服务器是不是已经关闭了
if srv.shuttingDown() {
return ErrServerClosed
}
// 获取要绑定监听的地址
addr := srv.Addr
if addr == "" {
addr = ":http"
}
// 创建用于监监听socket链接
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
// tcpKeepAliveListener 设置监听超时,在accept的时不会一直阻塞 设置一个超时操作
//启动服务
return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}
srv.Serve源码阅读
func (srv *Server) Serve(l net.Listener) error {
// 测试用的钩子函数,其他时候没有用的
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
// sync.once 创建一个once对象,用于防止多次关闭链接
l = &onceCloseListener{Listener: l}
// 结束的时候关闭监听socket
defer l.Close()
// 设置http2相关的设置
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
// 把监听socket添加监听表
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
// 结束的时候从监听表删除
defer srv.trackListener(&l, false)
// 设置临时过期时间,当accept发生 错误的时候等待一段时间
var tempDelay time.Duration // how long to sleep on accept failure
// 设置context 主要用于取消任务
baseCtx := context.Background() // base is always background, per Issue 16220
// 注意ctx把server本身传递进去了,用于传递
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
// 循环监听客户端到来
for {
// accept 阻塞等待客户单到来
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
tempDelay = 0
// 创建server连接,server连接包含了与客户端通讯的socket以及server相关的信息
c := srv.newConn(rw)
// 更新链接状态
c.setState(c.rwc, StateNew) // before Serve can return
// 启动goroutine处理socket
go c.serve(ctx)
}
}
server conn结构体阅读
// 服务端链接结构体
type conn struct {
// 链接绑定服务
server *Server // 用于取消任务的ctxFunc
cancelCtx context.CancelFunc // socket 通讯用的底层socket
rwc net.Conn // 客户端地址127.0.0.0:5678
remoteAddr string // tls 状态
tlsState *tls.ConnectionState // werr is set to the first write error to rwc.
// 第一次写出现错误的时候设置
werr error // r is bufr's read source.
// 用于读取请求的对象,主要用于读取数据的
r *connReader // bufr reads from r.
// r读取的数据存储buf
bufr *bufio.Reader // bufw writes to checkConnErrorWriter{c}, which populates werr on error.
// 写buf
bufw *bufio.Writer // lastMethod is the method of the most recent request
// on this connection, if any.
// 最后一次请求,方法 是post还是其他等
lastMethod string // 当前的请求
curReq atomic.Value // of *response (which has a Request in it) // 当前cnn状态
curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState)) //保护hijackedv
mu sync.Mutex // hijackedv is whether this connection has been hijacked
//表示是否支持用户劫持链接【主要用于切换协议的】
hijackedv bool
}
ListenAndServe调用图示

ListenAndServe源码剖析的更多相关文章
- 豌豆夹Redis解决方案Codis源码剖析:Dashboard
豌豆夹Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更 ...
- 豌豆夹Redis解决方案Codis源码剖析:Proxy代理
豌豆夹Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy base ...
- 这可能是最容易理解的 Go Mutex 源码剖析
Hi,大家好,我是 haohongfan. 上一篇文章<一文完全掌握 Go math/rand>,我们知道 math/rand 的 global rand 有一个全局锁,我的文章里面有一句 ...
- jQuery之Deferred源码剖析
一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...
- Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现
声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...
- Apache Spark源码剖析
Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著 ISBN 978-7-121-25420- ...
- 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析
项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现
SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...
随机推荐
- sqlserver日志处理不当而造成的隐患
sqlserver日志处理不当而造成的隐患 事故背景:一大早还在路上,群里陆续有人反馈系统一直报错 “Unknown error 258”,后来查询日志发现错误日志 第一反应是不是数据库连接不够用了? ...
- Linux电源管理(7)_Wakeup events framework
1. 前言 本文继续"Linux电源管理(6)_Generic PM之Suspend功能"中有关suspend同步以及PM wakeup的话题.这个话题,是近几年Linux ker ...
- 编译安装基于 fastcgi 模式的多虚拟主机的wordpress和discuz的LAMP架构
目录 实现CentOS 7 编译安装基于 fastcgi 模式的多虚拟主机的wordpress和discuz的LAMP架构 准备环境: 准备软件版本: 主机名修改用以区分 数据库服务器 实现数据库二进 ...
- 推荐一种非常好的新版DSP库源码移植方式,含V7,V6和V5的IAR以及MDK5的AC5和AC6版本
说明: 1.新版CMSIS V5.6里面的DSP库比以前的版本人性化了好多. 2.本帖为大家分享一种源码的添加方式,之前一直是用的库方便,不方便查看源码部分. 3.DSP教程可以还看第1版的,在我们的 ...
- 持续集成(CI):Jmeter+Ant+Jenkins定时构建
这里Jenkins的安装部署以及工程项目的整体配置不做赘述,其它博文已经说明,这里主要是赘述Ant的相关配置,build.xml文件配置以及项目中的部分配置 一.build.xml 在Ant的安装目录 ...
- weed3-1.hello world
Weed3 一个微型ORM框架(只有0.1Mb哦) 源码:https://github.com/noear/weed3 源码:https://gitee.com/noear/weed3 05年的时候开 ...
- 【实习第十天】odoo开发基础整合
前言 发文时间是2019年7月19日.提一下学习odoo的感受,odoo目前在国内并不是很流行,且主流是在企业型软件,所以导致目前odoo在网上的文献很少,学习相对来说比其他框架吃力.以下为大家总结1 ...
- 【iOS】Swipe与Pan区别分析
By definition, a swipe gesture is necessarily also a pan gesture -- both involve translational movem ...
- c++ 拷贝构造函数(重点在内含指针的浅拷贝和深拷贝)
今天同事问了一个关于拷贝构造函数的问题,类中包含指针的情况,今天就来说说c++的拷贝构造函数. c++的拷贝构造函数是构造函数的一种,是对类对象的初始化,拷贝构造函数只有一个参数就是本类的引用. 注意 ...
- OpenTSDB 简单使用 .NET
OpenTSDB是基于Hbase的时序数据库[时间序列数据库].不具备通用性,主要针对具有时间特性和需求的数据,如监控数据.温度变化数据等. 1.安装OpenTSDB 安装前一定要安装HBase,相关 ...