Go语言HTTP Server源码分析

HTTP server,顾名思义,支持http协议的服务器,HTTP是一个简单的请求-响应协议,通常运行在TCP之上。通过客户端发送请求给服务器得到对应的响应。

HTTP server,顾名思义,支持http协议的服务器,HTTP是一个简单的请求-响应协议,通常运行在TCP之上。通过客户端发送请求给服务器得到对应的响应。

Go语言中HTTP Server:

HTTP server,顾名思义,支持http协议的服务器,HTTP是一个简单的请求-响应协议,通常运行在TCP之上。通过客户端发送请求给服务器得到对应的响应。

Go语言HTTP Server源码分析

HTTP服务简单实现

  1. packagemain
  2. import(
  3. "fmt"
  4. "net/http"
  5. )
  6. //③处理请求,返回结果
  7. funcHello(whttp.ResponseWriter,r*http.Request){
  8. fmt.Fprintln(w,"helloworld")
  9. }
  10. funcmain(){
  11. //①路由注册
  12. http.HandleFunc("/",Hello)
  13. //②服务监听
  14. http.ListenAndServe(":8080",nil)
  15. }

[[188005]]

你以为这样就结束了吗,不才刚刚开始。

源码分析

①路由注册

  1. funcHandleFunc(patternstring,handlerfunc(ResponseWriter,*Request)){
  2. DefaultServeMux.HandleFunc(pattern,handler)
  3. }

DefaultServeMux是什么?

DefaultServeMux是ServeMux的一个实例。

ServeMux又是什么?

  1. //DefaultServeMuxisthedefaultServeMuxusedbyServe.
  2. varDefaultServeMux=&defaultServeMux
  3. vardefaultServeMuxServeMux
  4. typeServeMuxstruct{
  5. musync.RWMutex
  6. mmap[string]muxEntry
  7. hostsbool
  8. }
  9. typemuxEntrystruct{
  10. explicitbool
  11. hHandler
  12. patternstring
  13. }

ServeMux主要通过map[string]muxEntry,来存储了具体的url模式和handler(此handler是实现Handler接口的类型)。通过实现Handler的ServeHTTP方法,来匹配路由(这一点下面源码会讲到)

很多地方都涉及到了Handler,那么Handler是什么?

  1. typeHandlerinterface{
  2. ServeHTTP(ResponseWriter,*Request)
  3. }

此接口可以算是HTTP Server一个枢纽

  1. func(mux*ServeMux)HandleFunc(patternstring,handlerfunc(ResponseWriter,*Request)){
  2. mux.Handle(pattern,HandlerFunc(handler))
  3. }
  4. typeHandlerFuncfunc(ResponseWriter,*Request)
  5. func(fHandlerFunc)ServeHTTP(wResponseWriter,r*Request){
  6. f(w,r)
  7. }

从代码中可以看出HandlerFunc是一个函数类型,并实现了Handler接口。当通过调用HandleFunc(),把Hello强转为HandlerFunc类型时,就意味着 Hello函数也实现ServeHTTP方法。

ServeMux的Handle方法:

  1. func(mux*ServeMux)Handle(patternstring,handlerHandler){
  2. mux.mu.Lock()
  3. defermux.mu.Unlock()
  4. ifpattern==""{
  5. panic("http:invalidpattern"+pattern)
  6. }
  7. ifhandler==nil{
  8. panic("http:nilhandler")
  9. }
  10. ifmux.m[pattern].explicit{
  11. panic("http:multipleregistrationsfor"+pattern)
  12. }
  13. ifmux.m==nil{
  14. mux.m=make(map[string]muxEntry)
  15. }
  16. //把handler和pattern模式绑定到
  17. //map[string]muxEntry的map上
  18. mux.m[pattern]=muxEntry{explicit:true,h:handler,pattern:pattern}
  19. ifpattern[0]!='/'{
  20. mux.hosts=true
  21. }
  22. //这里是绑定静态目录,不作为本片重点。
  23. n:=len(pattern)
  24. ifn>0&&pattern[n-1]=='/'&&!mux.m[pattern[0:n-1]].explicit{
  25. path:=pattern
  26. ifpattern[0]!='/'{
  27. path=pattern[strings.Index(pattern,"/"):]
  28. }
  29. url:=&url.URL{Path:path}
  30. mux.m[pattern[0:n-1]]=muxEntry{h:RedirectHandler(url.String(),StatusMovedPermanently),pattern:pattern}
  31. }
  32. }

上面的流程就完成了路由注册。

②服务监听

  1. typeServerstruct{
  2. Addrstring
  3. HandlerHandler
  4. ReadTimeouttime.Duration
  5. WriteTimeouttime.Duration
  6. TLSConfig*tls.Config
  7. MaxHeaderBytesint
  8. TLSNextProtomap[string]func(*Server,*tls.Conn,Handler)
  9. ConnStatefunc(net.Conn,ConnState)
  10. ErrorLog*log.Logger
  11. disableKeepAlivesint32nextProtoOncesync.Once
  12. nextProtoErrerror
  13. }
  14. funcListenAndServe(addrstring,handlerHandler)error{
  15. server:=&Server{Addr:addr,Handler:handler}
  16. returnserver.ListenAndServe()
  17. }
  18. //初始化监听地址Addr,同时调用Listen方法设置监听。
  19. //***将监听的TCP对象传入Serve方法:
  20. func(srv*Server)ListenAndServe()error{
  21. addr:=srv.Addr
  22. ifaddr==""{
  23. addr=":http"
  24. }
  25. ln,err:=net.Listen("tcp",addr)
  26. iferr!=nil{
  27. returnerr
  28. }
  29. returnsrv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
  30. }

Serve(l net.Listener)为每个请求开启goroutine的设计,保证了go的高并发。

  1. func(srv*Server)Serve(lnet.Listener)error{
  2. deferl.Close()
  3. iffn:=testHookServerServe;fn!=nil{
  4. fn(srv,l)
  5. }
  6. vartempDelaytime.Duration//howlongtosleeponacceptfailure
  7. iferr:=srv.setupHTTP2_Serve();err!=nil{
  8. returnerr
  9. }
  10. srv.trackListener(l,true)
  11. defersrv.trackListener(l,false)
  12. baseCtx:=context.Background()//baseisalwaysbackground,perIssue16220
  13. ctx:=context.WithValue(baseCtx,ServerContextKey,srv)
  14. ctx=context.WithValue(ctx,LocalAddrContextKey,l.Addr())
  15. //开启循环进行监听
  16. for{
  17. //通过Listener的Accept方法用来获取连接数据
  18. rw,e:=l.Accept()
  19. ife!=nil{
  20. select{
  21. case<-srv.getDoneChan():
  22. returnErrServerClosed
  23. default:
  24. }
  25. ifne,ok:=e.(net.Error);ok&&ne.Temporary(){
  26. iftempDelay==0{
  27. tempDelay=5*time.Millisecond
  28. }else{
  29. tempDelay*=2
  30. }
  31. ifmax:=1*time.Second;tempDelay>max{
  32. tempDelay=max
  33. }
  34. srv.logf("http:Accepterror:%v;retryingin%v",e,tempDelay)
  35. time.Sleep(tempDelay)
  36. continue
  37. }
  38. returne
  39. }
  40. tempDelay=0
  41. //通过获得的连接数据,创建newConn连接对象
  42. c:=srv.newConn(rw)
  43. c.setState(c.rwc,StateNew)//beforeServecanreturn
  44. //开启goroutine发送连接请求
  45. goc.serve(ctx)
  46. }
  47. }

serve()为核心,读取对应的连接数据进行分配

  1. func(c*conn)serve(ctxcontext.Context){
  2. c.remoteAddr=c.rwc.RemoteAddr().String()
  3. //连接关闭相关的处理
  4. deferfunc(){
  5. iferr:=recover();err!=nil&&err!=ErrAbortHandler{
  6. constsize=64<<10
  7. buf:=make([]byte,size)
  8. buf=buf[:runtime.Stack(buf,false)]
  9. c.server.logf("http:panicserving%v:%v\n%s",c.remoteAddr,err,buf)
  10. }
  11. if!c.hijacked(){
  12. c.close()
  13. c.setState(c.rwc,StateClosed)
  14. }
  15. }()
  16. .....
  17. ctx,cancelCtx:=context.WithCancel(ctx)
  18. c.cancelCtx=cancelCtx
  19. defercancelCtx()
  20. c.r=&connReader{conn:c}
  21. c.bufr=newBufioReader(c.r)
  22. c.bufw=newBufioWriterSize(checkConnErrorWriter{c},4<<10)
  23. for{
  24. //读取客户端的请求
  25. w,err:=c.readRequest(ctx)
  26. ifc.r.remain!=c.server.initialReadLimitSize(){
  27. //Ifwereadanybytesoffthewire,we'reactive.
  28. c.setState(c.rwc,StateActive)
  29. }
  30. .................
  31. //处理网络数据的状态
  32. //Expect100Continuesupport
  33. req:=w.req
  34. ifreq.expectsContinue(){
  35. ifreq.ProtoAtLeast(1,1)&&req.ContentLength!=0{
  36. //WraptheBodyreaderwithonethatrepliesontheconnection
  37. req.Body=&expectContinueReader{readCloser:req.Body,resp:w}
  38. }
  39. }elseifreq.Header.get("Expect")!=""{
  40. w.sendExpectationFailed()
  41. return
  42. }
  43. c.curReq.Store(w)
  44. ifrequestBodyRemains(req.Body){
  45. registerOnHitEOF(req.Body,w.conn.r.startBackgroundRead)
  46. }else{
  47. ifw.conn.bufr.Buffered()>0{
  48. w.conn.r.closeNotifyFromPipelinedRequest()
  49. }
  50. w.conn.r.startBackgroundRead()
  51. }
  52. //调用serverHandler{c.server}.ServeHTTP(w,w.req)
  53. //方法处理请求
  54. serverHandler{c.server}.ServeHTTP(w,w.req)
  55. w.cancelCtx()
  56. ifc.hijacked(){
  57. return
  58. }
  59. w.finishRequest()
  60. if!w.shouldReuseConnection(){
  61. ifw.requestBodyLimitHit||w.closedRequestBodyEarly(){
  62. c.closeWriteAndWait()
  63. }
  64. return
  65. }
  66. c.setState(c.rwc,StateIdle)
  67. c.curReq.Store((*response)(nil))
  68. if!w.conn.server.doKeepAlives(){
  69. return
  70. }
  71. ifd:=c.server.idleTimeout();d!=0{
  72. c.rwc.SetReadDeadline(time.Now().Add(d))
  73. if_,err:=c.bufr.Peek(4);err!=nil{
  74. return
  75. }
  76. }
  77. c.rwc.SetReadDeadline(time.Time{})
  78. }
  79. }

③处理请求,返回结果

serverHandler 主要初始化路由多路复用器。如果server对象没有指定Handler,则使用默认的DefaultServeMux作为路由多路复用器。并调用初始化Handler的ServeHTTP方法。

  1. typeserverHandlerstruct{
  2. srv*Server
  3. }
  4. func(shserverHandler)ServeHTTP(rwResponseWriter,req*Request){
  5. handler:=sh.srv.Handler
  6. ifhandler==nil{
  7. handler=DefaultServeMux
  8. }
  9. ifreq.RequestURI=="*"&&req.Method=="OPTIONS"{
  10. handler=globalOptionsHandler{}
  11. }
  12. handler.ServeHTTP(rw,req)
  13. }

这里就是之前提到的匹配路由的具体代码

  1. func(mux*ServeMux)ServeHTTP(wResponseWriter,r*Request){
  2. ifr.RequestURI=="*"{
  3. ifr.ProtoAtLeast(1,1){
  4. w.Header().Set("Connection","close")
  5. }
  6. w.WriteHeader(StatusBadRequest)
  7. return
  8. }
  9. //匹配注册到路由上的handler函数
  10. h,_:=mux.Handler(r)
  11. //调用handler函数的ServeHTTP方法
  12. //即Hello函数,然后把数据写到http.ResponseWriter
  13. //对象中返回给客户端。
  14. h.ServeHTTP(w,r)
  15. }
  16. func(mux*ServeMux)Handler(r*Request)(hHandler,patternstring){
  17. ifr.Method!="CONNECT"{
  18. ifp:=cleanPath(r.URL.Path);p!=r.URL.Path{
  19. _,pattern=mux.handler(r.Host,p)
  20. url:=*r.URL
  21. url.Path=p
  22. returnRedirectHandler(url.String(),StatusMovedPermanently),pattern
  23. }
  24. }
  25. returnmux.handler(r.Host,r.URL.Path)
  26. }
  27. func(mux*ServeMux)handler(host,pathstring)(hHandler,patternstring){
  28. mux.mu.RLock()
  29. defermux.mu.RUnlock()
  30. //Host-specificpatterntakesprecedenceovergenericones
  31. ifmux.hosts{
  32. //如127.0.0.1/hello
  33. h,pattern=mux.match(host+path)
  34. }
  35. ifh==nil{
  36. //如/hello
  37. h,pattern=mux.match(path)
  38. }
  39. ifh==nil{
  40. h,pattern=NotFoundHandler(),""
  41. }
  42. return
  43. }
  44. func(mux*ServeMux)match(pathstring)(hHandler,patternstring){
  45. varn=0
  46. fork,v:=rangemux.m{
  47. if!pathMatch(k,path){
  48. continue
  49. }
  50. //通过迭代m寻找出注册路由的patten模式
  51. //与实际url匹配的handler函数并返回。
  52. ifh==nil||len(k)>n{
  53. n=len(k)
  54. h=v.h
  55. pattern=v.pattern
  56. }
  57. }
  58. return
  59. }
  60. funcpathMatch(pattern,pathstring)bool{
  61. iflen(pattern)==0{
  62. //shouldnothappen
  63. returnfalse
  64. }
  65. n:=len(pattern)
  66. //如果注册模式与请求uri一样返回true,否则false
  67. ifpattern[n-1]!='/'{
  68. returnpattern==path
  69. }
  70. //静态文件匹配
  71. returnlen(path)>=n&&path[0:n]==pattern
  72. }

将数据写给客户端

  1. //主要代码,通过层层封装才走到这一步
  2. func(wcheckConnErrorWriter)Write(p[]byte)(nint,errerror){
  3. n,err=w.c.rwc.Write(p)
  4. iferr!=nil&&w.c.werr==nil{
  5. w.c.werr=err
  6. w.c.cancelCtx()
  7. }
  8. return
  9. }

serverHandler{c.server}.ServeHTTP(w, w.req)当请求结束后,就开始执行连接断开的相关逻辑。

总结

Go语言通过一个ServeMux实现了的路由多路复用器来管理路由。同时提供一个Handler接口提供ServeHTTP方法,实现handler接口的函数,可以处理实际request并返回response。

ServeMux和handler函数的连接桥梁就是Handler接口。ServeMux的ServeHTTP方法实现了寻找注册路由的handler的函数,并调用该handler的ServeHTTP方法。

所以说Handler接口是一个重要枢纽。

简单梳理下整个请求响应过程,如下图

Go语言HTTP Server源码分析

©本文为清一色官方代发,观点仅代表作者本人,与清一色无关。清一色对文中陈述、观点判断保持中立,不对所包含内容的准确性、可靠性或完整性提供任何明示或暗示的保证。本文不作为投资理财建议,请读者仅作参考,并请自行承担全部责任。文中部分文字/图片/视频/音频等来源于网络,如侵犯到著作权人的权利,请与我们联系(微信/QQ:1074760229)。转载请注明出处:清一色财经

(0)
打赏 微信扫码打赏 微信扫码打赏 支付宝扫码打赏 支付宝扫码打赏
清一色的头像清一色管理团队
上一篇 2023年5月4日 22:41
下一篇 2023年5月4日 22:41

相关推荐

发表评论

登录后才能评论

联系我们

在线咨询:1643011589-QQbutton

手机:13798586780

QQ/微信:1074760229

QQ群:551893940

工作时间:工作日9:00-18:00,节假日休息

关注微信