欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

ListenAndServe源码剖析

程序员文章站 2022-04-14 18:58:25
使用goland追踪阅读ListenAndServe源码,剖析服务器启动流程 ListenAndServe阅读 ListenAndServe阅读 注意:创建一个server,启动server,我们也可以按照这2个步骤去创建一个web服务 Server结构阅读 Server结构阅读 注意:一般创建se ......

使用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源码剖析