使用goland追踪阅读ListenAndServe源码,剖析服务器启动流程

  • ListenAndServe阅读

  1. func ListenAndServe(addr string, handler Handler) error {
  2. //1. 创建server
  3. server := &Server{Addr: addr, Handler: handler}
  4. //2. 启动server
  5. return server.ListenAndServe()
  6. }

注意:创建一个server,启动server,我们也可以按照这2个步骤去创建一个web服务

  • Server结构阅读

  1. // 基类Closer接口,关闭所有链接停止服务
  2. type Closer interface {
  3. Close() error
  4. }
  5.  
  6. // 检查服务是否存活,里面定义了接口,接口的另类定义使用
  7. // 奇怪的行为,不确定为什么这么做
  8. func http2h1ServerKeepAlivesDisabled(hs *Server) bool {
  9. var x interface{} = hs
  10. // 临时定义接口,使用【奇怪的使用方法】
  11. type I interface {
  12. doKeepAlives() bool
  13. }
  14. if hs, ok := x.(I); ok {
  15. return !hs.doKeepAlives()
  16. }
  17. return false
  18. }
  19.  
  20. //Server
  21. // A Server defines parameters for running an HTTP server.
  22. // The zero value for Server is a valid configuration.
  23. type Server struct {
  24. Addr string // 监听的TCP地址
  25. Handler Handler // 注册的路由处理方法
  26.  
  27. // 如果服务需要支持https协议 那么需要相应的配置
  28. TLSConfig *tls.Config
  29.  
  30. //读超时设置
  31. ReadTimeout time.Duration
  32.  
  33. // 读取请求头超时设置
  34. ReadHeaderTimeout time.Duration
  35.  
  36. // 写超时
  37. WriteTimeout time.Duration
  38.  
  39. // 请求直接最长的空闲时长
  40. IdleTimeout time.Duration
  41.  
  42. // 请求头最大的容量
  43. MaxHeaderBytes int
  44.  
  45. // HTTPS协议相关
  46. TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
  47.  
  48. // 可以添回调函数,当客户端处于哪个状态时候可以执行某些动作
  49. ConnState func(net.Conn, ConnState)
  50.  
  51. // 错误日志器,不设置默认使用内置logger模块
  52. ErrorLog *log.Logger
  53.  
  54. //原子操作,是否保持长连接
  55. disableKeepAlives int32 // accessed atomically.
  56. //原子操作,服务要关闭了
  57. inShutdown int32 // accessed atomically (non-zero means we're in Shutdown)
  58. // https相关操作 用于初始化
  59. nextProtoOnce sync.Once // guards setupHTTP2_* init
  60. nextProtoErr error // result of http2.ConfigureServer if used
  61. // 互斥锁 保证资源的安全
  62. mu sync.Mutex
  63. // 服务套接字表,监听socket表
  64. listeners map[*net.Listener]struct{}
  65. // 存活的客户端链接表
  66. activeConn map[*conn]struct{}
  67. //用于通知服务关闭了
  68. doneChan chan struct{}
  69.  
  70. // 注册服务器关闭执行的一些行为
  71. onShutdown []func()
  72. }

注意:一般创建server只需要Addr与handler即可

  • ListenAndServe阅读

    监听并启动服务

  1. func (srv *Server) ListenAndServe() error {
  2. // 判断服务器是不是已经关闭了
  3. if srv.shuttingDown() {
  4. return ErrServerClosed
  5. }
  6. // 获取要绑定监听的地址
  7. addr := srv.Addr
  8. if addr == "" {
  9. addr = ":http"
  10. }
  11. // 创建用于监监听socket链接
  12. ln, err := net.Listen("tcp", addr)
  13. if err != nil {
  14. return err
  15. }
  16. // tcpKeepAliveListener 设置监听超时,在accept的时不会一直阻塞 设置一个超时操作
  17. //启动服务
  18. return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
  19. }

srv.Serve源码阅读

  1. func (srv *Server) Serve(l net.Listener) error {
  2. // 测试用的钩子函数,其他时候没有用的
  3. if fn := testHookServerServe; fn != nil {
  4. fn(srv, l) // call hook with unwrapped listener
  5. }
  6. // sync.once 创建一个once对象,用于防止多次关闭链接
  7. l = &onceCloseListener{Listener: l}
  8. // 结束的时候关闭监听socket
  9. defer l.Close()
  10.  
  11. // 设置http2相关的设置
  12. if err := srv.setupHTTP2_Serve(); err != nil {
  13. return err
  14. }
  15.  
  16. // 把监听socket添加监听表
  17. if !srv.trackListener(&l, true) {
  18. return ErrServerClosed
  19. }
  20. // 结束的时候从监听表删除
  21. defer srv.trackListener(&l, false)
  22.  
  23. // 设置临时过期时间,当accept发生 错误的时候等待一段时间
  24. var tempDelay time.Duration // how long to sleep on accept failure
  25. // 设置context 主要用于取消任务
  26. baseCtx := context.Background() // base is always background, per Issue 16220
  27. // 注意ctx把server本身传递进去了,用于传递
  28. ctx := context.WithValue(baseCtx, ServerContextKey, srv)
  29. // 循环监听客户端到来
  30. for {
  31. // accept 阻塞等待客户单到来
  32. rw, e := l.Accept()
  33. // 错误后处理逻辑
  34. if e != nil {
  35. // 尝试检查下服务是不是关闭了
  36. select {
  37. // 关闭则返回错误
  38. case <-srv.getDoneChan():
  39. return ErrServerClosed
  40. default:
  41. }
  42. // 检查错误类型,如果是链接被重置
  43. if ne, ok := e.(net.Error); ok && ne.Temporary() {
  44. // 设置超时
  45. if tempDelay == 0 {
  46. tempDelay = 5 * time.Millisecond
  47. } else {
  48. tempDelay *= 2
  49. }
  50. if max := 1 * time.Second; tempDelay > max {
  51. tempDelay = max
  52. }
  53. // 输出重新等待
  54. srv.logf("http: Accept error: %v; retrying in %v", e, tempDelay)
  55. // 休眠一段时间
  56. time.Sleep(tempDelay)
  57. continue
  58. }
  59. return e
  60. }
  61. // 没有错误设置tempDelay为0
  62. tempDelay = 0
  63. // 创建server连接,server连接包含了与客户端通讯的socket以及server相关的信息
  64. c := srv.newConn(rw)
  65. // 更新链接状态
  66. c.setState(c.rwc, StateNew) // before Serve can return
  67. // 启动goroutine处理socket
  68. go c.serve(ctx)
  69. }
  70. }

server conn结构体阅读

  1. // 服务端链接结构体
  2. type conn struct {
  3. // 链接绑定服务
  4. server *Server
  5.  
  6. // 用于取消任务的ctxFunc
  7. cancelCtx context.CancelFunc
  8.  
  9. // socket 通讯用的底层socket
  10. rwc net.Conn
  11.  
  12. // 客户端地址127.0.0.0:5678
  13. remoteAddr string
  14.  
  15. // tls 状态
  16. tlsState *tls.ConnectionState
  17.  
  18. // werr is set to the first write error to rwc.
  19. // 第一次写出现错误的时候设置
  20. werr error
  21.  
  22. // r is bufr's read source.
  23. // 用于读取请求的对象,主要用于读取数据的
  24. r *connReader
  25.  
  26. // bufr reads from r.
  27. // r读取的数据存储buf
  28. bufr *bufio.Reader
  29.  
  30. // bufw writes to checkConnErrorWriter{c}, which populates werr on error.
  31. // 写buf
  32. bufw *bufio.Writer
  33.  
  34. // lastMethod is the method of the most recent request
  35. // on this connection, if any.
  36. // 最后一次请求,方法 是post还是其他等
  37. lastMethod string
  38.  
  39. // 当前的请求
  40. curReq atomic.Value // of *response (which has a Request in it)
  41.  
  42. // 当前cnn状态
  43. curState struct{ atomic uint64 } // packed (unixtime<<8|uint8(ConnState))
  44.  
  45. //保护hijackedv
  46. mu sync.Mutex
  47.  
  48. // hijackedv is whether this connection has been hijacked
  49. //表示是否支持用户劫持链接【主要用于切换协议的】
  50. hijackedv bool
  51. }

ListenAndServe调用图示

ListenAndServe源码剖析的更多相关文章

  1. 豌豆夹Redis解决方案Codis源码剖析:Dashboard

    豌豆夹Redis解决方案Codis源码剖析:Dashboard 1.不只是Dashboard 虽然名字叫Dashboard,但它在Codis中的作用却不可小觑.它不仅仅是Dashboard管理页面,更 ...

  2. 豌豆夹Redis解决方案Codis源码剖析:Proxy代理

    豌豆夹Redis解决方案Codis源码剖析:Proxy代理 1.预备知识 1.1 Codis Codis就不详细说了,摘抄一下GitHub上的一些项目描述: Codis is a proxy base ...

  3. 这可能是最容易理解的 Go Mutex 源码剖析

    Hi,大家好,我是 haohongfan. 上一篇文章<一文完全掌握 Go math/rand>,我们知道 math/rand 的 global rand 有一个全局锁,我的文章里面有一句 ...

  4. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  5. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  6. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  7. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  8. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

  9. SpringMVC源码剖析(四)- DispatcherServlet请求转发的实现

    SpringMVC完成初始化流程之后,就进入Servlet标准生命周期的第二个阶段,即“service”阶段.在“service”阶段中,每一次Http请求到来,容器都会启动一个请求线程,通过serv ...

随机推荐

  1. 高版本Visual Studio和低版本ArcGIS共存 工具箱没有控件的解决方法

    转载请声明.博客园/B站/CSDN/知乎/小专栏 @秋意正寒 欢迎访问小专栏,更多WebGIS开发(Cesium等)经验分享:https://xiaozhuanlan.com/gishome 众所周知 ...

  2. 关于使用DB2数据库的项目后台报-420错误码的问题

    ###  Error querying database.  Cause: com.ibm.db2.jcc.am.SqlDataException: DB2 SQL Error: SQLCODE=-4 ...

  3. Newifi-mini OpenWrt 下 EAP-PEAP,EAP-TLS 企业级无线认证及 FreeRadius3

    Newifi-mini OpenWrt 下 EAP-PEAP,EAP-TLS 企业级无线认证及 FreeRadius3 转载注明来源: 本文链接 来自osnosn的博客,写于 2019-07-15. ...

  4. Flask 特殊装饰器

    请求进入函数之前 before_request # -*- coding: utf-8 -*-   from flask import Flask, session, redirect, reques ...

  5. IDEA中使用Maven模板创建Servelet项目并使用Tomcat来运行项目

    首先需要正确安装Maven和Tomact,Maven安装和Tomact安装步骤,参见别的文章. 一.创建Maven工作空间 点击Finish按钮后,耐心等待.直到出现BUILD SUCCESS为止. ...

  6. mysql order by limit 的一个坑

    分页查询的时候遇到的坑: 发现的问题: 对单个无索引的字段进行排序后limit .发现当被排序字段有相同值时并且在limit范围内,取的值并不是正常排序后的值, 也就是说,当排在第N行的数据可取key ...

  7. JS---DOM---自定义属性引入和移除

    总结:在html标签中添加的自定义属性, 如果想要获取这个属性的值, 需要使用getAttribute("自定义属性的名字")才能获取这个属性的值 html标签中有没有什么自带的属 ...

  8. Docker容器镜像打成tar包

    简述需求: 在现在容器上保存镜像进行打包,在另一台服务上使用 首先查看下现有要打tar包的容器 docker ps -a 接下来用commit参数进行保存镜像, -a 提交人的姓名  -m “提交内容 ...

  9. C lang:Protect array data——Const

    Xx_Introduction Use pointer translate parameter array original data will change data,and use const p ...

  10. 发送RCS成功的消息log_1

    //12-02 16:39:00.869323 24174 27394 I CarrierServices: [1172] cpb.x: Send INVITE//12-02 16:39:00.920 ...