天天看点

Golang Http Server源码阅读前言几个重要概念具体分析具体例子分析后记

这篇文章出现的理由是业务上需要创建一个Web Server。创建web是所有语言出现必须实现的功能之一了。在nginx+fastcgi+php广为使用的今天,这里我们不妨使用Go来进行web服务器的搭建。

使用Go搭建Web服务器的包有很多,大致有下面几种方法,直接使用net包,使用net.http包,使用第三方包(比如gorilla)。使用net包就需要从tcp层开始封装,耗费人力物力极大,果断舍弃。直接使用封装好的net.http和第三方包才是上策。这里我们就选择了使用官方提供的net.http包来搭建web服务。另外附带一句,gorilla的第三方包现在使用还是非常广的,文档也是比较全的,有兴趣的同学可以考虑使用一下。

net.http包里面有很多文件,都是和http协议相关的,比如设置cookie,header等。其中最重要的一个文件就是server.go了,这里我们阅读的就是这个文件。

ResponseWriter: 生成Response的接口

Handler: 处理请求和生成返回的接口

ServeMux: 路由,后面会说到ServeMux也是一种Handler

Conn : 网络连接

(具体的说明直接以注释形式放在代码中)

1

2

3

<code>type Handler </code><code>interface</code> <code>{</code>

<code>    </code><code>ServeHTTP(ResponseWriter, *Request)  </code><code>// 具体的逻辑函数</code>

<code>}</code>

实现了handler接口的对象就意味着往server端添加了处理请求的逻辑。

下面是三个接口(ResponseWriter, Flusher, Hijacker):

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

<code>// ResponseWriter的作用是被Handler调用来组装返回的Response的</code>

<code>type ResponseWriter </code><code>interface</code> <code>{</code>

<code>    </code><code>// 这个方法返回Response返回的Header供读写</code>

<code>    </code><code>Header() Header</code>

<code>    </code><code>// 这个方法写Response的Body</code>

<code>    </code><code>Write([]</code><code>byte</code><code>) (</code><code>int</code><code>, error)</code>

<code>    </code> 

<code>    </code><code>// 这个方法根据HTTP State Code来写Response的Header</code>

<code>    </code><code>WriteHeader(</code><code>int</code><code>)</code>

<code>// Flusher的作用是被Handler调用来将写缓存中的数据推给客户端</code>

<code>type Flusher </code><code>interface</code> <code>{</code>

<code>    </code><code>// 这个方法将写缓存中数据推送给客户端</code>

<code>    </code><code>Flush()</code>

<code>// Hijacker的作用是被Handler调用来关闭连接的</code>

<code>type Hijacker </code><code>interface</code> <code>{</code>

<code>    </code><code>// 这个方法让调用者主动管理连接</code>

<code>    </code><code>Hijack() (net.Conn, *bufio.ReadWriter, error)</code>

<code> </code> 

实现这三个接口的结构是response(这个结构是http包私有的,在文档中并没有显示,需要去看源码)

<code>// response包含了所有server端的http返回信息</code>

<code>type response </code><code>struct</code> <code>{</code>

<code>    </code><code>conn          *conn         </code><code>// 保存此次HTTP连接的信息</code>

<code>    </code><code>req           *Request </code><code>// 对应请求信息</code>

<code>    </code><code>chunking      </code><code>bool</code>     <code>// 是否使用chunk</code>

<code>    </code><code>wroteHeader   </code><code>bool</code>     <code>// header是否已经执行过写操作</code>

<code>    </code><code>wroteContinue </code><code>bool</code>     <code>// 100 Continue response was written</code>

<code>    </code><code>header        Header   </code><code>// 返回的http的Header</code>

<code>    </code><code>written       int64    </code><code>// Body的字节数</code>

<code>    </code><code>contentLength int64    </code><code>// Content长度</code>

<code>    </code><code>status        </code><code>int</code>      <code>// HTTP状态</code>

<code>    </code><code>needSniff     </code><code>bool</code>     <code>// 是否需要使用sniff。(当没有设置Content-Type的时候,开启sniff能根据HTTP body来确定Content-Type)</code>

<code>    </code><code>closeAfterReply </code><code>bool</code>     <code>//是否保持长链接。如果客户端发送的请求中connection有keep-alive,这个字段就设置为false。</code>

<code>    </code><code>requestBodyLimitHit </code><code>bool</code> <code>//是否requestBody太大了(当requestBody太大的时候,response是会返回411状态的,并把连接关闭)</code>

在response中是可以看到

<code>func (w *response) Header() Header</code>

<code>func (w *response) WriteHeader(code </code><code>int</code><code>)</code>

<code>func (w *response) Write(data []</code><code>byte</code><code>) (n </code><code>int</code><code>, err error)</code>

<code>func (w *response) Flush()</code>

<code>func (w *response) Hijack() (rwc net.Conn, buf *bufio.ReadWriter, err error)</code>

这么几个方法。所以说response实现了ResponseWriter,Flusher,Hijacker这三个接口

handlerFunc是经常使用到的一个type

<code>// 这里将HandlerFunc定义为一个函数类型,因此以后当调用a = HandlerFunc(f)之后, 调用a的ServeHttp实际上就是调用f的对应方法</code>

<code>type HandlerFunc func(ResponseWriter, *Request)</code>

<code>// ServeHTTP calls f(w, r).</code>

<code>func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {</code>

<code>    </code><code>f(w, r)</code>

这里需要多回味一下了,这个HandlerFunc定义和ServeHTTP合起来是说明了什么?说明HandlerFunc的所有实例是实现了ServeHttp方法的。另,实现了ServeHttp方法就是什么?实现了接口Handler!

所以你以后会看到很多这样的句子:

<code>func AdminHandler(w ResponseWriter, r *Request) {</code>

<code>    </code><code>...</code>

<code>handler := HandlerFunc(AdminHandler)</code>

<code>handler.ServeHttp(w,r)</code>

请不要讶异,你明明没有写ServeHttp,怎么能调用呢? 实际上调用ServeHttp就是调用AdminHandler。

好吧,理解这个也花了我较长时间,附带上一个play.google写的一个小例子

<a href="http://play.golang.org/p/nSt_wcjc2u">http://play.golang.org/p/nSt_wcjc2u</a>

有兴趣继续研究的同学可以继续试验下去

如果你理解了HandlerFunc,你对下面两个句子一定不会讶异了

<code>func NotFound(w ResponseWriter, r *Request) { Error(w, </code><code>"404 page not found"</code><code>, StatusNotFound) }</code>

<code>func NotFoundHandler() Handler { </code><code>return</code> <code>HandlerFunc(NotFound) }</code>

下面接着看Server.go

它就是http包中的路由规则器。你可以在ServerMux中注册你的路由规则,当有请求到来的时候,根据这些路由规则来判断将请求分发到哪个处理器(Handler)。

它的结构如下:

<code>type ServeMux </code><code>struct</code> <code>{</code>

<code>    </code><code>mu sync.RWMutex   </code><code>//锁,由于请求设计到并发处理,因此这里需要一个锁机制</code>

<code>    </code><code>m  map[</code><code>string</code><code>]muxEntry  </code><code>// 路由规则,一个string对应一个mux实体,这里的string就是我注册的路由表达式</code>

下面看一下muxEntry

<code>type muxEntry </code><code>struct</code> <code>{</code>

<code>    </code><code>explicit</code> <code>bool</code>   <code>// 是否精确匹配</code>

<code>    </code><code>h        Handler </code><code>// 这个路由表达式对应哪个handler</code>

看到这两个结构就应该对请求是如何路由的有思路了:

ServeMux定义的方法有:

<code>func (mux *ServeMux) match(path </code><code>string</code><code>) Handler   </code><code>//根据path获取Handler</code>

<code>func (mux *ServeMux) handler(r *Request) Handler  </code><code>//根据Request获取Handler,内部实现调用match</code>

<code>func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) </code><code>//!!这个说明,ServeHttp也实现了Handler接口,它实际上也是一个Handler!内部实现调用handler</code>

<code>func (mux *ServeMux) Handle(pattern </code><code>string</code><code>, handler Handler) </code><code>//注册handler方法</code>

<code>func (mux *ServeMux) HandleFunc(pattern </code><code>string</code><code>, handler func(ResponseWriter, *Request))  </code><code>//注册handler方法(直接使用func注册)</code>

在godoc文档中经常见到的DefaultServeMux是http默认使用的ServeMux

var DefaultServeMux = NewServeMux()

如果我们没有自定义ServeMux,系统默认使用这个ServeMux。

换句话说,http包外层(非ServeMux)中提供的几个方法:

<code>func Handle(pattern </code><code>string</code><code>, handler Handler) { DefaultServeMux.Handle(pattern, handler) }</code>

<code>func HandleFunc(pattern </code><code>string</code><code>, handler func(ResponseWriter, *Request)) {</code>

<code>    </code><code>DefaultServeMux.HandleFunc(pattern, handler)</code>

实际上就是调用ServeMux结构内部对应的方法。

下面还剩下一个Server结构

<code>type Server </code><code>struct</code> <code>{</code>

<code>    </code><code>Addr           </code><code>string</code>        <code>// 监听的地址和端口</code>

<code>    </code><code>Handler        Handler       </code><code>// 所有请求需要调用的Handler(实际上这里说是ServeMux更确切)如果为空则设置为DefaultServeMux</code>

<code>    </code><code>ReadTimeout    time.Duration </code><code>// 读的最大Timeout时间</code>

<code>    </code><code>WriteTimeout   time.Duration </code><code>// 写的最大Timeout时间</code>

<code>    </code><code>MaxHeaderBytes </code><code>int</code>           <code>// 请求头的最大长度</code>

<code>    </code><code>TLSConfig      *tls.Config   </code><code>// 配置TLS</code>

Server提供的方法有:

<code>func (srv *Server) Serve(l net.Listener) error   </code><code>//对某个端口进行监听,里面就是调用for进行accept的处理了</code>

<code>func (srv *Server) ListenAndServe() error  </code><code>//开启http server服务,内部调用Serve</code>

<code>func (srv *Server) ListenAndServeTLS(certFile, keyFile </code><code>string</code><code>) error </code><code>//开启https server服务,内部调用Serve</code>

当然Http包也直接提供了方法供外部使用,实际上内部就是实例化一个Server,然后调用ListenAndServe方法

<code>func ListenAndServe(addr </code><code>string</code><code>, handler Handler) error   </code><code>//开启Http服务</code>

<code>func ListenAndServeTLS(addr </code><code>string</code><code>, certFile </code><code>string</code><code>, keyFile </code><code>string</code><code>, handler Handler) error </code><code>//开启HTTPs服务</code>

<code>func HelloServer(w http.ResponseWriter, req *http.Request) {</code>

<code>    </code><code>io.WriteString(w, </code><code>"hello, world!\n"</code><code>)</code>

<code>func main() {</code>

<code>    </code><code>http.HandleFunc(</code><code>"/hello"</code><code>, HelloServer)</code>

<code>    </code><code>err := http.ListenAndServe(</code><code>":12345"</code><code>, nil)</code>

<code>    </code><code>if</code> <code>err != nil {</code>

<code>        </code><code>log.Fatal(</code><code>"ListenAndServe: "</code><code>, err)</code>

<code>    </code><code>}</code>

按顺序做了几件事:

1 调用了DefaultServerMux的HandleFunc

2 调用了DefaultServerMux的Handle

3 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则

按顺序做了几件事情:

1 实例化Server

2 调用Server的ListenAndServe()

3 调用net.Listen("tcp", addr)监听端口

4 启动一个for循环,在循环体中Accept请求

5 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()

6 读取每个请求的内容w, err := c.readRequest()

7 判断header是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux

8 调用handler的ServeHttp

9 在这个例子中,下面就进入到DefaultServerMux.ServeHttp

10 根据request选择handler,并且进入到这个handler的ServeHTTP

       mux.handler(r).ServeHTTP(w, r)

11 选择handler:

    A 判断是否有路由能满足这个request(循环遍历ServerMux的muxEntry)

    B 如果有路由满足,调用这个路由handler的ServeHttp

    C 如果没有路由满足,调用NotFoundHandler的ServeHttp

对于net.http包中server的理解是非常重要的。理清serverMux, responseWriter, Handler, HandlerFunc等常用结构和函数是使用go web的重要一步。个人感觉由于go中文档较少,像这样有点复杂的包,看godoc的效果就远不如直接看代码来的快和清晰了。实际上在理解了http包后,才会对godoc中出现的句子有所理解。后续还会写一些文章关于使用net.http构建web server的。请期待之。

本文转自轩脉刃博客园博客,原文链接:http://www.cnblogs.com/yjf512/archive/2012/08/22/2650873.html,如需转载请自行联系原作者