前言
go http.ListenAndServe 链路跟踪
go 能很轻易的启动一个服务 如下
package main
import ("http")
func main() {
http.HandleFunc("/ping", ping)
http.ListenAndServe(":8080", nil)
}
func ping(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("pong"))
return
}
本文主要研究ListenAndServe方法做了什么操作
一、http.ListenAndServe
监听基于tcp的网络地址addr
客户端链接之后,connect回调带有处理程序的Serve来处理
设置 keep-alives 保持tcp链接。
// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
二、server.ListenAndServe
func (srv *Server) ListenAndServe() error {
// 判断service是否关闭
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
// 监听tcp服务端口 (创建socket、绑定端口、监听tcp)
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
//
return srv.Serve(ln)
}
三、server.Serve
func (srv *Server) Serve(l net.Listener) error {
// 测试testHookServerServe,一般启动testHookServerServe是nil
if fn := testHookServerServe; fn != nil {
fn(srv, l) // call hook with unwrapped listener
}
origListener := l
l = &onceCloseListener{Listener: l}
defer l.Close()
// 检查是否需要升级http2
if err := srv.setupHTTP2_Serve(); err != nil {
return err
}
// sync.WaitGroup add(1)
if !srv.trackListener(&l, true) {
return ErrServerClosed
}
// sync.WaitGroup Done()
defer srv.trackListener(&l, false)
// 创建上下文
baseCtx := context.Background()
if srv.BaseContext != nil {
baseCtx = srv.BaseContext(origListener)
if baseCtx == nil {
panic("BaseContext returned a nil context")
}
}
var tempDelay time.Duration // how long to sleep on accept failure
ctx := context.WithValue(baseCtx, ServerContextKey, srv)
for {
// 处理客户端请求
rw, err := l.Accept()
if err != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(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", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
// 一般启动http.ListenAndServe() ConnContext是nil
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
// 封装自身的Server.conn
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
// 处理Handle方法
go c.serve(connCtx)
}
}
四、http.conn.serve
func (c *conn) serve(ctx context.Context) {
c.remoteAddr = c.rwc.RemoteAddr().String()
ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())
var inFlightResponse *response
defer func() {
// 接收painc
// 回收goroutine资源
// 关闭connect
}()
if tlsConn, ok := c.rwc.(*tls.Conn); ok {
// 处理http2请求
}
// HTTP/1.x from here on.
ctx, cancelCtx := context.WithCancel(ctx)
c.cancelCtx = cancelCtx
defer cancelCtx()
c.r = &connReader{conn: c}
c.bufr = newBufioReader(c.r)
c.bufw = newBufioWriterSize(checkConnErrorWriter{c}, 4<<10)
for {
w, err := c.readRequest(ctx)
if c.r.remain != c.server.initialReadLimitSize() {
// If we read any bytes off the wire, we're active.
c.setState(c.rwc, StateActive, runHooks)
}
if err != nil {
// 处理读取request的error
}
// 支持 Header Expect
req := w.req
if req.expectsContinue() {
if req.ProtoAtLeast(1, 1) && req.ContentLength != 0 {
// Wrap the Body reader with one that replies on the connection
req.Body = &expectContinueReader{readCloser: req.Body, resp: w}
w.canWriteContinue.setTrue()
}
} else if req.Header.get("Expect") != "" {
w.sendExpectationFailed()
return
}
c.curReq.Store(w)
if requestBodyRemains(req.Body) {
registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
} else {
w.conn.r.startBackgroundRead()
}
// HTTP cannot have multiple simultaneous active requests.[*]
// Until the server replies to this request, it can't read another,
// so we might as well run the handler in this goroutine.
// [*] Not strictly true: HTTP pipelining. We could let them all process
// in parallel even if their responses need to be serialized.
// But we're not going to implement HTTP pipelining because it
// was never deployed in the wild and the answer is HTTP/2.
inFlightResponse = w
// 处理handle逻辑
serverHandler{c.server}.ServeHTTP(w, w.req)
inFlightResponse = nil
w.cancelCtx()
if c.hijacked() {
return
}
w.finishRequest()
if !w.shouldReuseConnection() {
if w.requestBodyLimitHit || w.closedRequestBodyEarly() {
c.closeWriteAndWait()
}
return
}
c.setState(c.rwc, StateIdle, runHooks)
// 存储response
c.curReq.Store((*response)(nil))
if !w.conn.server.doKeepAlives() {
// We're in shutdown mode. We might've replied
// to the user without "Connection: close" and
// they might think they can send another
// request, but such is life with HTTP/1.1.
return
}
if d := c.server.idleTimeout(); d != 0 {
c.rwc.SetReadDeadline(time.Now().Add(d))
if _, err := c.bufr.Peek(4); err != nil {
return
}
}
c.rwc.SetReadDeadline(time.Time{})
}
}
总结
以上就是http.ListenAndServe 链路跟踪。本文仅仅介绍了serve的实现过程,有了基础过程的认识,实现起来更能得心应手