golang的http分析
首先,要认识一个贯穿始终的接口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
//Addrreturnsthelistener'snetworkaddress.
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)
看下面,最终回调回去了。
//serverHandlerdelegatestoeithertheserver'sHandleror
//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传入。
原文链接: