go-restful实战与深入分析之源码篇
来源:互联网 发布:excel如何将数据分组 编辑:程序博客网 时间:2024/04/30 20:57
上一篇分析了go 原生的http服务。下面开始介绍go restful源码分析,有了上一篇的铺垫这篇文章讲解起来就很简单了。和go 的http一样启动监听:
wsContainer := restful.NewContainer() server := &http.Server{Addr: ":8080", Handler: wsContainer}
不用多说这个wsContainer肯定也是实现了Handler接口的。看代码:
func NewContainer() *Container { return &Container{ webServices: []*WebService{}, ServeMux: http.NewServeMux(), isRegisteredOnRoot: false, containerFilters: []FilterFunction{}, doNotRecover: true, recoverHandleFunc: logStackOnRecover, serviceErrorHandleFunc: writeServiceError, router: CurlyRouter{}, contentEncodingEnabled: false}}func (c *Container) ServeHTTP(httpwriter http.ResponseWriter, httpRequest *http.Request) { c.ServeMux.ServeHTTP(httpwriter, httpRequest)}
上面有两个方式分别是定义container和实现Handler接口的的ServeHTTP()方法。那么当服务请求过来后就可以调用这个方法了。这个方法又调用c.ServeMux.ServeHTTP,是不是回到上一篇了,对,就练到一起了。
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) { if r.RequestURI == "*" { if r.ProtoAtLeast(1, 1) { w.Header().Set("Connection", "close") } w.WriteHeader(StatusBadRequest) return } h, _ := mux.Handler(r) h.ServeHTTP(w, r)}
那么先看他是怎么注册和查找方法的呢?
ws := new(restful.WebService) ws. Path("/users"). Consumes(restful.MIME_XML, restful.MIME_JSON). Produces(restful.MIME_JSON, restful.MIME_XML) // you can specify this per route as well ws.Route(ws.GET("/{user-id}").To(u.findUser)) ws.Route(ws.POST("").To(u.updateUser)) ws.Route(ws.PUT("/{user-id}").To(u.createUser)) ws.Route(ws.DELETE("/{user-id}").To(u.removeUser)) container.Add(ws)
先看看Route方法,这里面记录所以路径和路径对应的方法,全部放到[]Route这个切片当中。
func (w *WebService) Route(builder *RouteBuilder) *WebService { w.routesLock.Lock() defer w.routesLock.Unlock() builder.copyDefaults(w.produces, w.consumes) w.routes = append(w.routes, builder.Build()) return w}
一个container里面多个webservice。可以通过Container的Add注册加入到webServices这个切片中。
func (c *Container) Add(service *WebService) *Container { c.webServicesLock.Lock() defer c.webServicesLock.Unlock() // if rootPath was not set then lazy initialize it if len(service.rootPath) == 0 { service.Path("/") } // cannot have duplicate root paths for _, each := range c.webServices { if each.RootPath() == service.RootPath() { log.Printf("[restful] WebService with duplicate root path detected:['%v']", each) os.Exit(1) } } // If not registered on root then add specific mapping if !c.isRegisteredOnRoot { c.isRegisteredOnRoot = c.addHandler(service, c.ServeMux) } c.webServices = append(c.webServices, service) return c}
注册webservice的本质是注册各种handler看addHandler这个方法。
func (c *Container) addHandler(service *WebService, serveMux *http.ServeMux) bool { pattern := fixedPrefixPath(service.RootPath()) // check if root path registration is needed if "/" == pattern || "" == pattern { serveMux.HandleFunc("/", c.dispatch) return true } // detect if registration already exists alreadyMapped := false for _, each := range c.webServices { if each.RootPath() == service.RootPath() { alreadyMapped = true break } } if !alreadyMapped { serveMux.HandleFunc(pattern, c.dispatch) if !strings.HasSuffix(pattern, "/") { serveMux.HandleFunc(pattern+"/", c.dispatch) } } return false}
通过serveMux.HandleFunc注册,这也和上一篇的文章对应。不过这次注册的方法c.dispatch有点特别,具体看下面的方法,
func (c *Container) dispatch(httpWriter http.ResponseWriter, httpRequest *http.Request) { writer := httpWriter // CompressingResponseWriter should be closed after all operations are done defer func() { if compressWriter, ok := writer.(*CompressingResponseWriter); ok { compressWriter.Close() } }() // Instal panic recovery unless told otherwise if !c.doNotRecover { // catch all for 500 response defer func() { if r := recover(); r != nil { c.recoverHandleFunc(r, writer) return } }() } // Install closing the request body (if any) defer func() { if nil != httpRequest.Body { httpRequest.Body.Close() } }() // Detect if compression is needed // assume without compression, test for override if c.contentEncodingEnabled { doCompress, encoding := wantsCompressedResponse(httpRequest) if doCompress { var err error writer, err = NewCompressingResponseWriter(httpWriter, encoding) if err != nil { log.Print("[restful] unable to install compressor: ", err) httpWriter.WriteHeader(http.StatusInternalServerError) return } } } // Find best match Route ; err is non nil if no match was found var webService *WebService var route *Route var err error func() { c.webServicesLock.RLock() defer c.webServicesLock.RUnlock() webService, route, err = c.router.SelectRoute( c.webServices, httpRequest) }() if err != nil { // a non-200 response has already been written // run container filters anyway ; they should not touch the response... chain := FilterChain{Filters: c.containerFilters, Target: func(req *Request, resp *Response) { switch err.(type) { case ServiceError: ser := err.(ServiceError) c.serviceErrorHandleFunc(ser, req, resp) } // TODO }} chain.ProcessFilter(NewRequest(httpRequest), NewResponse(writer)) return } wrappedRequest, wrappedResponse := route.wrapRequestResponse(writer, httpRequest) // pass through filters (if any) if len(c.containerFilters)+len(webService.filters)+len(route.Filters) > 0 { // compose filter chain allFilters := []FilterFunction{} allFilters = append(allFilters, c.containerFilters...) allFilters = append(allFilters, webService.filters...) allFilters = append(allFilters, route.Filters...) chain := FilterChain{Filters: allFilters, Target: func(req *Request, resp *Response) { // handle request by route after passing all filters route.Function(wrappedRequest, wrappedResponse) }} chain.ProcessFilter(wrappedRequest, wrappedResponse) } else { // no filters, handle request by route route.Function(wrappedRequest, wrappedResponse) }}
这个里面方法里面通过SelectRoute选取对应的webservice和route,具体实现在:
func (c CurlyRouter) SelectRoute( webServices []*WebService, httpRequest *http.Request) (selectedService *WebService, selected *Route, err error) { requestTokens := tokenizePath(httpRequest.URL.Path) detectedService := c.detectWebService(requestTokens, webServices) if detectedService == nil { if trace { traceLogger.Printf("no WebService was found to match URL path:%s\n", httpRequest.URL.Path) } return nil, nil, NewError(http.StatusNotFound, "404: Page Not Found") } candidateRoutes := c.selectRoutes(detectedService, requestTokens) if len(candidateRoutes) == 0 { if trace { traceLogger.Printf("no Route in WebService with path %s was found to match URL path:%s\n", detectedService.rootPath, httpRequest.URL.Path) } return detectedService, nil, NewError(http.StatusNotFound, "404: Page Not Found") } selectedRoute, err := c.detectRoute(candidateRoutes, httpRequest) if selectedRoute == nil { return detectedService, nil, err } return detectedService, selectedRoute, nil}
然后通过route.Function(wrappedRequest, wrappedResponse)调用具体的方法。
0 0
- go-restful实战与深入分析之源码篇
- go-restful实战与深入分析之使用篇
- go-restful实战与深入分析之基础篇
- 【原创】k8s源码分析------第三方库go-restful分析
- 深入源码分析go类型系统
- go-restful http server 框架 源码解析与说明
- java源码分析之TreeMap深入篇
- Prometheus 实战于源码分析之API与联邦
- HandlerThread源码分析与实战
- Go语言RESTful API开发实战
- go-restful之hello world
- go database/sql 源码分析 -题外篇
- [源码分析]Community Server的MemberRole之Membership深入篇
- zookeeper实战与源码分析----zookeeper安装
- flannel 实战与源码分析(一)
- flannel 实战与源码分析(二)
- flannel 实战与源码分析(三)
- flannel 实战与源码分析(四)
- 修心悟道:清心寡欲:厚积薄发
- 一个完整的通过encoding/json信息传递格式,采用http.POST利用进行与服务器通信,并对返回结果进行解析处理的典型案例
- mysql#1045错误如何消除
- 行级元素实例代码(注释)
- 机器学习实战(五) 模型保存(持久化)
- go-restful实战与深入分析之源码篇
- Pupils Redistribution
- JAVA-关于集合的一些问题
- 为Sublime text3设置浏览器快捷键
- ubuntu 启动流程
- Selenium+PhantomJS爬取淘宝
- 题目1364:v字仇杀队
- 【form表单】的两个重要属性
- Android 架构师如何选择网络框架