参考:https://studygolang.com/articles/25849?fr=sidebar

​ http://blog.csdn.net/gophers

实现一个最简短的hello world服务器


package mainimport "net/http"func main() {http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {w.Write([]byte(`hello world`))})http.ListenAndServe(":3000", nil)
}

http.ListenAndServe()到底做了什么?

http.ListenAndServe()用到的所有依赖都在Go源码中的/src/pkg/net/http/server.go文件中,我们可以看到它的定义:

ListenAndServe来监听TCP网络地址(srv.Addr),然后调用Serve来处理传入的请求;

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// If srv.Addr is blank, ":http" is used.
//
// ListenAndServe always returns a non-nil error. After Shutdown or Close,
// the returned error is ErrServerClosed.
func (srv *Server) ListenAndServe() error {if srv.shuttingDown() {return ErrServerClosed}addr := srv.Addr// 如果不指定服务器地址信息,默认以":http"作为地址信息if addr == "" {addr = ":http"}// 创建一个TCP Listener, 用于接收客户端的连接请求ln, err := net.Listen("tcp", addr)if err != nil {return err}return srv.Serve(ln)
}

最后调用了Server.Serve()并返回,继续来看Server.Serve():

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
//
// HTTP/2 support is only enabled if the Listener returns *tls.Conn
// connections and they were configured with "h2" in the TLS
// Config.NextProtos.
//
// Serve always returns a non-nil error and closes l.
// After Shutdown or Close, the returned error is ErrServerClosed.
func (srv *Server) Serve(l net.Listener) error {if fn := testHookServerServe; fn != nil {fn(srv, l) // call hook with unwrapped listener}origListener := ll = &onceCloseListener{Listener: l}defer l.Close()if err := srv.setupHTTP2_Serve(); err != nil {return err}if !srv.trackListener(&l, true) {return ErrServerClosed}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")}}// 接收失败时,休眠多长时间;休眠时间不断变长,知道等于time.Second(一千毫秒)var tempDelay time.Duration // how long to sleep on accept failurectx := context.WithValue(baseCtx, ServerContextKey, srv)for {// 等待新的连接建立rw, err := l.Accept()// 处理链接失败if err != nil {select {case <-srv.getDoneChan():return ErrServerCloseddefault:}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 := ctxif cc := srv.ConnContext; cc != nil {connCtx = cc(connCtx, rw)if connCtx == nil {panic("ConnContext returned nil")}}tempDelay = 0c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can return// 创建新的协程处理请求go c.serve(connCtx)}
}

Server.Serve()的整个逻辑大概是:首先创建一个上下文对象,然后调用ListenerAccept()等待新的连接建立;一旦有新的连接建立,则调用ServernewConn()创建新的连接对象 ,并将连接的状态标志为StateNew,然后开启一个新的goroutine处理连接请求。继续看一下conn.Serve()方法:

// Serve a new connection.
func (c *conn) serve(ctx context.Context) {c.remoteAddr = c.rwc.RemoteAddr().String()ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr())// ...// 延迟释放和TLS相关处理...for {// 循环调用readRequest()方法读取下一个请求并进行处理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)}...... // 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.// 对请求进行处理serverHandler{c.server}.ServeHTTP(w, w.req)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)// 将当前请求置为nilc.curReq.Store((*response)(nil))......c.rwc.SetReadDeadline(time.Time{})}
}

其中最关键的一行代码为serverHandler{c.server}.ServeHTTP(w, w.req),可以继续看一下serverHandler

// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {srv *Server
}func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {handler := sh.srv.Handlerif handler == nil {handler = DefaultServeMux}if req.RequestURI == "*" && req.Method == "OPTIONS" {handler = globalOptionsHandler{}}handler.ServeHTTP(rw, req)
}

这里的sh.srv.Handler就是最初在http.ListenAndServe()中传入的Handler对象,也就是我们自定义的ServeMux对象。如果该Handler对象为nil,则会使用默认的DefaultServeMux,最后调用ServeMuxServeHTTP()方法匹配当前路由对应的handler方法。

ServeMux是一个HTTP请求多路复用器,它将每个传入请求的URL与注册模式列表进行匹配,并调用与这个URL最匹配的模式的处理程序。

type ServeMux struct {mu    sync.RWMutexm     map[string]muxEntryes    []muxEntry // slice of entries sorted from longest to shortest.hosts bool       // whether any patterns contain hostnames
}func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {// CONNECT requests are not canonicalized.if r.Method == "CONNECT" {// If r.URL.Path is /tree and its handler is not registered,// the /tree -> /tree/ redirect applies to CONNECT requests// but the path canonicalization does not.if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}return mux.handler(r.Host, r.URL.Path)}// All other requests have any port stripped and path cleaned// before passing to mux.handler.host := stripHostPort(r.Host)path := cleanPath(r.URL.Path)// If the given path is /tree and its handler is not registered,// redirect for /tree/.if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {return RedirectHandler(u.String(), StatusMovedPermanently), u.Path}if path != r.URL.Path {_, pattern = mux.handler(host, path)url := *r.URLurl.Path = pathreturn RedirectHandler(url.String(), StatusMovedPermanently), pattern}return mux.handler(host, r.URL.Path)
}// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) {mux.mu.RLock()defer mux.mu.RUnlock()// Host-specific pattern takes precedence over generic onesif mux.hosts {h, pattern = mux.match(host + path)}if h == nil {h, pattern = mux.match(path)}if h == nil {h, pattern = NotFoundHandler(), ""}return
}// Find a handler on a handler map given a path string.
// Most-specific (longest) pattern wins.
func (mux *ServeMux) match(path string) (h Handler, pattern string) {// Check for exact match first.v, ok := mux.m[path]if ok {return v.h, v.pattern}// Check for longest valid match.  mux.es contains all patterns// that end in / sorted from longest to shortest.for _, e := range mux.es {if strings.HasPrefix(path, e.pattern) {return e.h, e.pattern}}return nil, ""
}

ServeMuxHandler方法就是根据url调用指定handler方法,handler方法的作用是调用match匹配路由。在 match 方法里我们看到之前提到的 map[string]muxEntry[]muxEntry,在 map[string]muxEntry 中查找是否有对应的路由规则存在;如果没有匹配的路由规则,则会进行近似匹配。

ServeMuxHandler方法中找到要执行的handler之后,就调用handlerserveHTTP方法。

http.ListenAndServe()到底做了什么?相关推荐

  1. volatile关键字到底做了什么?

    话不多说,直接贴代码 class Singleton {private static volatile Singleton instance;private Singleton(){}//双重判空pu ...

  2. 忠于职守 —— sysmon 线程到底做了什么?(九)

    在 runtime.main() 函数中,执行 runtime_init() 前,会启动一个 sysmon 的监控线程,执行后台监控任务: systemstack(func() { // 创建监控线程 ...

  3. ad中电容用什么封装_二极管在电路中到底做什么用的

    所有的电子电路中基本上都会用到二极管,它的特性也是非常之多,最主要就是单方向导电性,(单向导电性的两根引脚之间的电阻分为正向电阻和反向电阻两种).人们利用这些不同特性构成各种具体的应用电路,分析不同电 ...

  4. MySQL实战 | 01 当执行一条 select 语句时,MySQL 到底做了啥?

    原文链接:当执行一条 select 语句时,MySQL 到底做了啥? 也许,你也跟我一样,在遇到数据库问题时,总时茫然失措,想重启解决问题,又怕导致数据丢失,更怕重启失败,影响业务. 就算重启成功了, ...

  5. python中mod是什么意思_【python中,mod_python到底做了些什么呢?】mod python 教程

    python 编程小白 ,不会用doctest 请大神指教怎么用!! >>> >>> def is_between(v, lower, higher): ...   ...

  6. 又被黑!百度到底做错了什么?

    前几天中国互联网巨头的市值又"更新"了一下,没想到百度现在"沦落"成了其它互联网公司市值的衡量单位. 图片来源自腾讯新闻 这一次,怕百度被"黑&quo ...

  7. 软件在安装时,到底做了些什么?

    软件在安装时,到底做了些什么? 大家每天都在用电脑,可能也经常在自己的电脑上安装软件.就算自己没安装过,至少也看到人家安装过软件.在这里,我不是想教你怎么安装软件,而是想向你展示,软件在安装的过程中, ...

  8. free()到底做了什么

    1.问题 在LeetCode上做题偶然发现一道题:free内存后,还继续调用该指针,于是好奇,想了解free到底做了什么. 这段代码的free()掉了nextTemp结点的malloc内存,但后面还可 ...

  9. mysql add trandata_OGG add trandata 到底做了什么

    有的时候我们做OGG的时候add trandata会出现异常. 这里就剖析一下add trandata到底做了什么 GGSCI (yjfora81 as ggs_admin@testdb) 2> ...

最新文章

  1. 《工业大数据白皮书》2019版正式发布
  2. HDOJ/HDU 2565 放大的X(分段思考~)
  3. Windows MySQL8.0安装出错解决方案(Start Server 失败)
  4. Python入门100题 | 第019题
  5. HTML一个form表单中有两个(多个)submit,后台如何区分(纯HTML实现,无需javascript)
  6. 2019.08.08学习整理
  7. esxi挂载Linux的nfs盘,ESXi安装centos7挂载群晖NFS
  8. 【Linux开发】如何查看Linux kernel的内置模块驱动列表和进程ID
  9. python 线程锁_Python3多线程执行任务含线程同步锁
  10. android修改对话框大小设置,android – 如何更改对话框首选项消息的文本大小?...
  11. java导出excel弹出下载框_JavaWeb导出Excel文件并弹出下载框
  12. axure实现复选框全选_表格设置一键全选按钮,这样的打√方式,只需要三步搞定...
  13. BZOJ3637 Query on a tree VI
  14. 2022年 javaJDK下载安装步骤及环境变量配置【超详细】
  15. Siemens NX 1930中文版
  16. 制作ext4文件系统
  17. 【数据挖掘算法】(一)MSET 算法
  18. 300ETF期权和50ETF期权的区别
  19. Halcon union_straight_contours_xld详解
  20. Selenium 页面加载慢(一直转圈)

热门文章

  1. LeetCode之Merge Sorted Array
  2. linux之通过htop操作进程使用总结
  3. Andorid之BINDSERVICE的使用方法总结
  4. 【iVX 初级工程师培训教程 10篇文拿证】02 数值绑定及自适应网站制作
  5. 【一】Windows API 零门槛编程指南——MessageBox 基本使用及基础讲解
  6. (八)python3 只需3小时带你轻松入门——List 与 dict 的常用操作
  7. mysql交叉编译 cmake_CMake交叉编译配置
  8. mysql cbo优化器_查询优化器介绍 - PolarDB-X 云原生分布式数据库 - 阿里云
  9. 6部BBC “教材级” 地理纪录片,有生之年必看系列!
  10. 那些你从未见过的神奇物理化学实验,今天全给你整理了!