• 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调用图示

  • 相关阅读:
    整数子数组求最大和
    四则运算实现
    四则运算
    2015年大二下学期读书计划
    java变量和数据类型
    jdk的安装和java的入门概念
    数据库的设计
    多表查询
    数据约束和外键
    表数据的简单查询语句
  • 原文地址:https://www.cnblogs.com/lurenq/p/11944524.html
Copyright © 2020-2023  润新知