首先,要认识一个贯穿始终的接口http.Handler
typeHandlerinterface{ ServeHTTP(ResponseWriter,*Request) }其中,两个参数,一个是表示响应的接口,另一个表示请求。具体方法先忽略:
typeResponseWriterinterface{ }使用时,这个函数指这定地址和对应的handler
funcListenAndServe(addrstring,handlerHandler)再看下http包内的一个重要函数,Handle,可见,传入的是一个监听的httppath,第二个参数是上述的handler.
funcHandle(patternstring,handlerHandler)看一下如何使用的:
使用接口形式的Handle+ListenAndServe
typeImpHandlerstruct{} func(hImpHandler)ServeHTTP(whttp.ResponseWriter,r*http.Request){//实现方法 w.Write([]byte("haha")) } funcmain(){ http.Handle("/",ImpHandler{}) http.ListenAndServe(":12345",nil) }这里,http消息来了应该是在底层直接调用对应的ServeHTTP。具体是怎么调到的,一层层来看。
首先看下http.Handle做了什么。
funcHandle(patternstring,handlerHandler){DefaultServeMux.Handle(pattern,handler)}可见,这个Handle函数底层封装了一个对象,其实是对此对象DefaultServeMux进行调用。
这类型如下:
typeServeMuxstruct{ musync.RWMutex mmap[string]muxEntry hostsbool//whetheranypatternscontainhostnames } typemuxEntrystruct{ explicitbool hHandler patternstring }可见,http的path和对应的处理handler的关系以muxEntry维护在这个默认的hash表m中。http.Handle传入的两个参数以hash形式保存在内部的全局变量DefaultServeMux中。
到此,只是在http业务层面上将相关信息保存下,最后在http请求来时的ListenAndServe中,才进行连接的处理。
funcListenAndServe(addrstring,handlerHandler)error{ server:=&Server{Addr:addr,Handler:handler} returnserver.ListenAndServe() }同样,ListenAndServe本身只是一个对外接口,内部也有相应对象Server进行封装。前面说过这个方法是处理连接层面的事,那么这个server就是tcpserver的一个抽象。
另一方面,这里又传入了一个handler,这是干吗用的?这里传的是nil,后面再看。
func(srv*Server)ListenAndServe()error{ addr:=srv.Addr ifaddr==""{ addr=":http" } ln,err:=net.Listen("tcp",addr)//创建监听了 iferr!=nil{ returnerr } returnsrv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)}) }可见,这里直接就监听TCP连接了。其中的ln是个Listener接口,代码这样写比较漂亮:
//MultiplegoroutinesmayinvokemethodsonaListenersimultaneously. typeListenerinterface{ //Acceptwaitsforandreturnsthenextconnectiontothelistener. Accept()(Conn,error) //Closeclosesthelistener. //AnyblockedAcceptoperationswillbeunblockedandreturnerrors. Close()error //Addrreturnsthelistenersnetworkaddress. Addr()Addr } //这里实现得比较好,覆盖了一个Accept方法,在其中加入了keepAlived的选项。其他两个方法仍旧使用原listener的 typetcpKeepAliveListenerstruct{ *net.TCPListener//外层可直接调它的方法不需要指定成员 } func(lntcpKeepAliveListener)Accept()(cnet.Conn,errerror){ tc,err:=ln.AcceptTCP() iferr!=nil{ return } tc.SetKeepAlive(true) tc.SetKeepAlivePeriod(3*time.Minute) returntc,nil }继续看Server的连接监听处理:
func(srv*Server)Serve(lnet.Listener)error{ deferl.Close() iffn:=testHookServerServe;fn!=nil{ fn(srv,l) } vartempDelaytime.Duration//howlongtosleeponacceptfailure iferr:=srv.setupHTTP2_Serve();err!=nil{ returnerr } ////////////////skip for{ rw,e:=l.Accept()//取出一个连接,对应accept ife!=nil{ ifne,ok:=e.(net.Error);ok&&ne.Temporary(){ iftempDelay==0{ tempDelay=5*time.Millisecond }else{ tempDelay*=2 } ifmax:=1*time.Second;tempDelay>max{ tempDelay=max } srv.logf("http:Accepterror:%v;retryingin%v",e,tempDelay) time.Sleep(tempDelay) continue } returne } tempDelay=0 c:=srv.newConn(rw) c.setState(c.rwc,StateNew)//beforeServecanreturn goc.serve(ctx) } }可见,调用Listener的Accept()后,形成一个抽象的连接,再启单独协程去处理它。
协程内读出对应的数据后,会进行如下调用,此调用将http的业务与底层的tcp连接结合了起来:
serverHandler{c.server}.ServeHTTP(w,w.req)看下面,最终回调回去了。
//serverHandlerdelegatestoeithertheserversHandleror //DefaultServeMuxandalsohandles"OPTIONS*"requests. typeserverHandlerstruct{ srv*Server } func(shserverHandler)ServeHTTP(rwResponseWriter,req*Request){ handler:=sh.srv.Handler ifhandler==nil{ handler=DefaultServeMux } ifreq.RequestURI=="*"&&req.Method=="OPTIONS"{ handler=globalOptionsHandler{} } handler.ServeHTTP(rw,req) } //最终回到最开始注册Handle的地方,进行ServeHTTP的调用 func(mux*ServeMux)ServeHTTP(wResponseWriter,r*Request){ ifr.RequestURI=="*"{ ifr.ProtoAtLeast(1,1){ w.Header().Set("Connection","close") } w.WriteHeader(StatusBadRequest) return } h,_:=mux.Handler(r) h.ServeHTTP(w,r) }最终调用到了上文的DefaultServeMux中来。
以上是http一的基础的结构,下面是一些衍生的用法。
使用HandleFunc+ListenAndServe
funcmain(){ fmt.Println("Hello.") http.HandleFunc("/",func(whttp.ResponseWriter,r*http.Request){ w.Write([]byte("haha2")) }) http.ListenAndServe(":12346",nil) }其中,func可使用闭包也可不用。
看下面代码:
//HandleFuncregistersthehandlerfunctionforthegivenpattern. func(mux*ServeMux)HandleFunc(patternstring,handlerfunc(ResponseWriter,*Request)){ mux.Handle(pattern,HandlerFunc(handler)) } //HandleFuncregistersthehandlerfunctionforthegivenpattern //intheDefaultServeMux. //ThedocumentationforServeMuxexplainshowpatternsarematched. funcHandleFunc(patternstring,handlerfunc(ResponseWriter,*Request)){ DefaultServeMux.HandleFunc(pattern,handler) }可见,HandlerFunc只是对Handler的封装,下面同样是通过DefaultServeMux来进行。
这里的重点是以下的写法,用一个函数来实现某个接口,虽然这接口底层仍然是调用函数本身,这样就可以直接用函数和之前的接口匹配:
//TheHandlerFunctypeisanadaptertoallowtheuseof //ordinaryfunctionsasHTTPhandlers.Iffisafunction //withtheappropriatesignature,HandlerFunc(f)isa //Handlerthatcallsf. typeHandlerFuncfunc(ResponseWriter,*Request) //ServeHTTPcallsf(w,r). func(fHandlerFunc)ServeHTTP(wResponseWriter,r*Request){ f(w,r) }实际上的效果是,明明只写了一个函数func(ResponseWriter,*Request),但其他代码却可以通过golang的隐式接口方式通过另一个你不知道的函数调用你!这里,不知道的函数就是ServeHTTP。
Handle掌握了,这里的HandleFunc就容易了。
更进一步,ServeMux也是可以使用自定义的值。这时,传入http.ListenAndServe的第二个参数就是这个mux。
funcNewServeMux()*ServeMux{returnnew(ServeMux)}这个ServeMux,本身又是隐式实现了Handler。
再次回到这里,可见最终是调到了ServerMux这里:
func(shserverHandler)ServeHTTP(rwResponseWriter,req*Request){ handler:=sh.srv.Handler ifhandler==nil{ handler=DefaultServeMux } ifreq.RequestURI=="*"&&req.Method=="OPTIONS"{ handler=globalOptionsHandler{} } handler.ServeHTTP(rw,req) }总结下:
http包给外面提供了三个层次的接口,每个层次暴露的东西不一样:
第一层:只需要关心处理逻辑,直接以HandleFunc实现;
第二层:以Handle实现,这一层,对外额外暴露了一个Handler接口,需要用户关注一个ServeHTTP的函数;底层仍然是通过DefaultMux来实现。
第三层:对外暴露了一个ServeMux,处理请求的方法注册到这个ServeMux上,将ServeMux传入。
原文链接: