func test1() { http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello world!") }) err := http.ListenAndServe(":9001", nil) if err != nil { log.Fatal("ListenAndServer:", err) }}在使用ListenAndServe这个方法时,系统就会给我们指派一个路由器,DefaultServeMux是系统默认使用的路由器,如果ListenAndServe这个方法的第2个参数传入nil,系统就会默认使用DefaultServeMux。当然,这里也可以传入自定义的路由器。
先看http.HandleFunc("/", ...),从HandleFunc方法点进去,如下:
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { DefaultServeMux.HandleFunc(pattern, handler)}在这里调用了DefaultServeMux的HandleFunc方法,这个方法有两个参数,pattern是匹配的路由规则,handler表示这个路由规则对应的处理方法,并且这个处理方法有两个参数。
(资料图)
在我们书写的代码示例中,pattern对应/,handler对应sayHello,当我们在浏览器中输入http://localhost:9001时,就会触发匿名函数。
我们再顺着DefaultServeMux的HandleFunc方法继续点下去,如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) { if handler == nil { panic("http: nil handler") } mux.Handle(pattern, HandlerFunc(handler))}在这个方法中,路由器又调用了Handle方法,注意这个Handle方法的第2个参数,将之前传入的handler这个响应方法强制转换成了HandlerFunc类型。
这个HandlerFunc类型到底是个什么呢?如下:
type HandlerFunc func(ResponseWriter, *Request)看来和我们定义的"/"的匿名函数的类型都差不多。但是!!! 这个HandlerFunc默认实现了ServeHTTP接口!这样HandlerFunc对象就有了ServeHTTP方法!如下:
// ServeHTTP calls f(w, r).func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r)}接下来,我们返回去继续看mux的Handle方法,也就是这段代码mux.Handle(pattern, HandlerFunc(handler))。这段代码做了哪些事呢?源码如下
// Handle registers the handler for the given pattern.// If a handler already exists for pattern, Handle panics.func (mux *ServeMux) Handle(pattern string, handler Handler) { mux.mu.Lock() defer mux.mu.Unlock() if pattern == "" { panic("http: invalid pattern") } if handler == nil { panic("http: nil handler") } if _, exist := mux.m[pattern]; exist { panic("http: multiple registrations for " + pattern) } if mux.m == nil { mux.m = make(map[string]muxEntry) } e := muxEntry{h: handler, pattern: pattern} mux.m[pattern] = e if pattern[len(pattern)-1] == "/" { mux.es = appendSorted(mux.es, e) } if pattern[0] != "/" { mux.hosts = true }}主要就做了一件事,向DefaultServeMux的map[string]muxEntry中增加对应的路由规则和handler。
map[string]muxEntry是个什么鬼?
map是一个字典对象,它保存的是key-value。[string]表示这个字典的key是string类型的,这个key值会保存我们的路由规则。muxEntry是一个实例对象,这个对象内保存了路由规则对应的处理方法。mux.es为模糊匹配 有长倒短排序 比如有路由/hello/访问/hello/world时没有路由 会落到/hello/上找到相应代码,如下:
// 路由器type ServeMux struct { mu sync.RWMutex m map[string]muxEntry es []muxEntry // slice of entries sorted from longest to shortest. hosts bool // whether any patterns contain hostnames}type muxEntry struct { h Handler pattern string}// 路由响应方法type Handler interface { ServeHTTP(ResponseWriter, *Request)}第二部分主要就是研究这句代码err := http.ListenAndServe(":9001",nil),也就是ListenAndServe这个方法。从这个方法点进去,如下:
func ListenAndServe(addr string, handler Handler) error { server := &Server{Addr: addr, Handler: handler} return server.ListenAndServe()}在这个方法中,初始化了一个server对象,然后调用这个server对象的ListenAndServe方法,在这个方法中,如下:
func (srv *Server) ListenAndServe() error { if srv.shuttingDown() { return ErrServerClosed } addr := srv.Addr if addr == "" { addr = ":http" } ln, err := net.Listen("tcp", addr) if err != nil { return err } return srv.Serve(ln)}在这个方法中,调用了net.Listen("tcp", addr),也就是底层用TCP协议搭建了一个服务,然后监控我们设置的端口。
代码的最后,调用了srv的Serve方法,如下:
func (srv *Server) Serve(l net.Listener) error { if fn := testHookServerServe; fn != nil { fn(srv, l) // call hook with unwrapped listener } origListener := l l = &onceCloseListener{Listener: l} defer l.Close() if err := srv.setupHTTP2_Serve(); err != nil { return err } if !srv.trackListener(&l, true) { return ErrServerClosed } defer srv.trackListener(&l, false) baseCtx := context.Background() if srv.BaseContext != nil { baseCtx = srv.BaseContext(origListener) if baseCtx == nil { panic("BaseContext returned a nil context") } } var tempDelay time.Duration // how long to sleep on accept failure ctx := context.WithValue(baseCtx, ServerContextKey, srv) for { rw, err := l.Accept() if err != nil { select { case <-srv.getDoneChan(): return ErrServerClosed default: } if ne, ok := err.(net.Error); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay) time.Sleep(tempDelay) continue } return err } connCtx := ctx if cc := srv.ConnContext; cc != nil { connCtx = cc(connCtx, rw) if connCtx == nil { panic("ConnContext returned nil") } } tempDelay = 0 c := srv.newConn(rw) c.setState(c.rwc, StateNew, runHooks) // before Serve can return go c.serve(connCtx) }}最后3段代码比较重要,也是Go语言支持高并发的体现,如下:
c := srv.newConn(rw)c.setState(c.rwc, StateNew, runHooks) // before Serve can returngo c.serve(connCtx)上面那一大坨代码,总体意思是进入方法后,首先开了一个for循环,在for循环内时刻Accept请求,请求来了之后,会为每个请求创建一个Conn,然后单独开启一个goroutine,把这个请求的数据当做参数扔给这个Conn去服务:go c.serve()。用户的每一次请求都是在一个新的goroutine去服务,每个请求间相互不影响。
在conn的serve方法中,有一句代码很重要,如下:
serverHandler{c.server}.ServeHTTP(w, w.req)表示serverHandler也实现了ServeHTTP接口,ServeHTTP方法实现如下:
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) { handler := sh.srv.Handler if handler == nil { handler = DefaultServeMux } if req.RequestURI == "*" && req.Method == "OPTIONS" { handler = globalOptionsHandler{} } if req.URL != nil && strings.Contains(req.URL.RawQuery, ";") { var allowQuerySemicolonsInUse int32 req = req.WithContext(context.WithValue(req.Context(), silenceSemWarnContextKey, func() { atomic.StoreInt32(&allowQuerySemicolonsInUse, 1) })) defer func() { if atomic.LoadInt32(&allowQuerySemicolonsInUse) == 0 { sh.srv.logf("http: URL query contains semicolon, which is no longer a supported separator; parts of the query may be stripped when parsed; see golang.org/issue/25192") } }() } handler.ServeHTTP(rw, req)}在这里如果handler为空(这个handler就可以理解为是我们自定义的路由器),就会使用系统默认的DefaultServeMux,代码的最后调用了DefaultServeMux的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是Handler接口对象 h.ServeHTTP(w, r) //调用Handler接口对象的ServeHTTP方法实际上就调用了我们定义的sayHello方法}func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) { // CONNECT requests are not canonicalized. if r.Method == "CONNECT" { // If r.URL.Path is /tree and its handler is not registered, // the /tree -> /tree/ redirect applies to CONNECT requests // but the path canonicalization does not. if u, ok := mux.redirectToPathSlash(r.URL.Host, r.URL.Path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } return mux.handler(r.Host, r.URL.Path) } // All other requests have any port stripped and path cleaned // before passing to mux.handler. host := stripHostPort(r.Host) path := cleanPath(r.URL.Path) // If the given path is /tree and its handler is not registered, // redirect for /tree/. if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok { return RedirectHandler(u.String(), StatusMovedPermanently), u.Path } if path != r.URL.Path { _, pattern = mux.handler(host, path) u := &url.URL{Path: path, RawQuery: r.URL.RawQuery} return RedirectHandler(u.String(), StatusMovedPermanently), pattern } return mux.handler(host, r.URL.Path)}func (mux *ServeMux) handler(host, path string) (h Handler, pattern string) { mux.mu.RLock() defer mux.mu.RUnlock() // Host-specific pattern takes precedence over generic ones if mux.hosts { h, pattern = mux.match(host + path) } if h == nil { h, pattern = mux.match(path) } if h == nil { h, pattern = NotFoundHandler(), "" } return}func (mux *ServeMux) match(path string) (h Handler, pattern string) { // Check for exact match first. v, ok := mux.m[path] if ok { return v.h, v.pattern } // Check for longest valid match. mux.es contains all patterns // that end in / sorted from longest to shortest. for _, e := range mux.es { if strings.HasPrefix(path, e.pattern) { return e.h, e.pattern } } return nil, ""}它会根据用户请求的URL到路由器里面存储的map中匹配,匹配成功就会返回存储的handler,调用这个handler的ServeHTTP()就可以执行到相应的处理方法了,这个处理方法实际上就是我们刚开始定义的sayHello(),只不过这个sayHello()被HandlerFunc又包了一层,因为HandlerFunc实现了ServeHTTP接口,所以在调用HandlerFunc对象的ServeHTTP()时,实际上在ServeHTTP ()的内部调用了我们的sayHello()。
http.ListenAndServe(":9090",nil)serverserver的ListenAndServe()server的Serve方法,开启for循环,在循环中Accept请求Conn,并且开启一个goroutine为这个请求进行服务go c.serve()c.readRequest()serverHandler的ServeHTTP(),如果handler为空,就把handler设置为系统默认的路由器DefaultServeMuxhandler的ServeHTTP()=>实际上是调用了DefaultServeMux的ServeHTTP()ServeHTTP()中会调用路由对应处理handlerhandler中会执行sayHello()有一个需要注意的点: DefaultServeMux和路由对应的处理方法handler都实现了ServeHTTP接口,他们俩都有ServeHTTP方法,但是方法要达到的目的不同,在DefaultServeMux的ServeHttp()里会执行路由对应的处理handler的ServeHttp()。
package muximport ( "net/http" "strings")type muxEntry struct { h TesthandleFunc}type TesthandleFunc func(http.ResponseWriter, *http.Request)type TestHandler struct { routes map[string]map[string]muxEntry}func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path if route, ok := h.routes[method]; ok { if entry, ok := route[path]; ok { entry.h(w, r) return } } w.WriteHeader(http.StatusNotFound)}func Newhandler() *TestHandler { return &TestHandler{routes: make(map[string]map[string]muxEntry)}}func (h *TestHandler) Handle(method, path string, handler TesthandleFunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]muxEntry) } h.routes[method][path] = muxEntry{handler}}package mainimport ( "fmt" "net/http" "study/mux")func main() { handler := mux.Newhandler() handler.Handle("GET", "/hello", func(rw http.ResponseWriter, r *http.Request) { rw.Write([]byte("Hello World")) }) handler.Handle("Post", "/hello/world", func(rw http.ResponseWriter, r *http.Request) { fmt.Fprintln(rw, "你好") }) http.ListenAndServe(":9002", handler)}自定义context
package routerimport ( "encoding/json" "net/http" "strings")type Context struct { w http.ResponseWriter r *http.Request}func (c *Context) Json(code int, v interface{}) { c.w.Header().Set("Content-Type", "application/json") c.w.WriteHeader(code) s, _ := json.Marshal(v) c.w.Write(s)}type Routerfunc func(c *Context)type RouterHandler struct { routes map[string]map[string]Routerfunc}func NewRouterHandler() *RouterHandler { return &RouterHandler{routes: make(map[string]map[string]Routerfunc)}}func (h *RouterHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { method := strings.ToUpper(r.Method) path := r.URL.Path c := &Context{w: w, r: r} if route, ok := h.routes[method]; ok { if h, ok := route[path]; ok { h(c) return } } w.WriteHeader(http.StatusNotFound)}func (h *RouterHandler) Handle(method, path string, handler Routerfunc) { method = strings.ToUpper(method) if _, ok := h.routes[method]; !ok { h.routes[method] = make(map[string]Routerfunc) } h.routes[method][path] = handler}func (r *RouterHandler) Run(addr string) error { return http.ListenAndServe(addr, r)}type Engine struct { RouterGroup pool sync.Pool trees methodTrees}// trietype RouterGroup struct { basePath string engine *Engine}func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) { c := engine.pool.Get().(*Context) // 从pool 拿出一个context c.writermem.reset(w) // 记录http.ResponseWriter 及 *http.Request c.Request = req c.reset() // 重置上一个留下的值 engine.handleHTTPRequest(c) engine.pool.Put(c) // 把用完的context放回池子}// get: /bac添加路由
func (group *RouterGroup) handle(httpMethod, relativePath string, handlers HandlersChain) IRoutes { absolutePath := group.calculateAbsolutePath(relativePath) handlers = group.combineHandlers(handlers) group.engine.addRoute(httpMethod, absolutePath, handlers) return group.returnObj()}Context
type Context struct { Request *http.Request Writer ResponseWriter Params Params handlers HandlersChain index int8 fullPath string engine *Engine params *Params skippedNodes *[]skippedNode // This mutex protect Keys map mu sync.RWMutex // Keys is a key/value pair exclusively for the context of each request. Keys map[string]interface{} // Errors is a list of errors attached to all the handlers/middlewares who used this context. Errors errorMsgs // Accepted defines a list of manually accepted formats for content negotiation. Accepted []string // queryCache use url.ParseQuery cached the param query result from c.Request.URL.Query() queryCache url.Values // formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH, // or PUT body parameters. formCache url.Values // SameSite allows a server to define a cookie attribute making it impossible for // the browser to send this cookie along with cross-site requests. sameSite http.SameSite}func (c *Context) Next() { c.index++ for c.index < int8(len(c.handlers)) { c.handlers[c.index](c) c.index++ }}func (engine *Engine) handleHTTPRequest(c *Context) { httpMethod := c.Request.Method rPath := c.Request.URL.Path unescape := false if engine.UseRawPath && len(c.Request.URL.RawPath) > 0 { rPath = c.Request.URL.RawPath unescape = engine.UnescapePathValues } if engine.RemoveExtraSlash { rPath = cleanPath(rPath) } // Find root of the tree for the given HTTP method t := engine.trees for i, tl := 0, len(t); i < tl; i++ { if t[i].method != httpMethod { continue } root := t[i].root // Find route in tree value := root.getValue(rPath, c.params, c.skippedNodes, unescape) if value.params != nil { c.Params = *value.params } if value.handlers != nil { c.handlers = value.handlers c.fullPath = value.fullPath c.Next() c.writermem.WriteHeaderNow() return } if httpMethod != http.MethodConnect && rPath != "/" { if value.tsr && engine.RedirectTrailingSlash { redirectTrailingSlash(c) return } if engine.RedirectFixedPath && redirectFixedPath(c, root, engine.RedirectFixedPath) { return } } break } if engine.HandleMethodNotAllowed { for _, tree := range engine.trees { if tree.method == httpMethod { continue } if value := tree.root.getValue(rPath, nil, c.skippedNodes, unescape); value.handlers != nil { c.handlers = engine.allNoMethod serveError(c, http.StatusMethodNotAllowed, default405Body) return } } } c.handlers = engine.allNoRoute serveError(c, http.StatusNotFound, default404Body)} 标签:
net http路由注册```gofunctest1(){http HandleFunc(" ",func(whttp
又一个大冷门,又一个亚洲之光,今年世界杯真是太多意外惊喜了。昨天晚
产后一个多月,已经出了月子,身体也恢复得差不多了,不用过多忌口,水
本篇文章给大家谈谈同余定理内容,以及同余定理求余数对应的知识点,希
连日来,奢侈品品牌路易威登与三家咖啡店联名活动持续引发关注。580元
首先要看这款借了多长时间,从借款日期起20年内的可以提起诉讼。其二,
眼下,正是花椒成熟的季节。贵州省遵义市务川仡佬族苗族自治县种植
大家好,小福来为大家解答以上的问题。化工易贸二氯蒽醌,化工易贸这个
AIGC的爆发除了带来算力上的挑战,对网络的要求也达到了前所未有的高度
1、所需工具:PS软件通过PS对一寸照片进行排版并打印步骤如下:注意事
河北唐山某电影院内,一名男子在电影《消失的她》放映结束后,手捧大束
随着夏日气息渐浓,近日,北京环球度假区推出首个夏季体验“环球酷爽夏
截止目前2023年调整退休人员基本养老金的方案已经公布,但是2023年养老
特许经营15年!山东临沂市平邑县城区环卫市场化作业项目采购意向公布!
2023年5月,全国发行新增债券3019亿元,其中一般债券264亿元、专项债券
为进一步扩大禁毒宣传覆盖面,提升群众禁毒意识,在第36个国际禁毒日,
一、题文试管在燃烧时导致试管破裂的原因至少三种二、解答1没有预热(
“特价商品,概不退换”“本卡最终解释权归本公司所有”“卡内金额过期
1、《炮兵进行曲》是一首军乐曲,是中国人民解放军炮兵的标识性音乐,
近日,三星公司在韩国推出了以口袋妖怪为主题的无线耳机,包括口袋妖怪
今天的大雨,如约而至。小伙伴们,也没忘记抓拍。 截至28日10时至14时
突发!6月28日,常州一工厂突发爆炸,现场黑烟滚滚,民警和消防员已第
自己煮火锅吃,卫生实惠!味道也可以按自己的喜好!可以去超市买包火锅
6月27日上午,2023中德(西安)经贸合作交流会在西安高新区正式开幕。
中国航空新闻网讯:据飞行国际6月27日报道,俄罗斯联邦航空监管机构已
近日,吉林省公安厅高速公路公安局吉林分局巡逻三大队民警在珲乌高速公
波罗斯是目前和埼玉战斗的角色中最强的一个,毕竟他是能将埼玉直接从地
中新网长春6月28日电(记者郭佳)长光卫星技术股份有限公司(下称“长光卫
竞技方面,在京多安到来后,巴萨评估了球队的阵容情况,因为在布斯克茨
阳曲县公交车免费乘坐为方便阳曲县群众“五月十三”庙会期间前往会场赶
Copyright © 2015-2022 西南服装网版权所有 备案号:皖ICP备2022009963号-8 联系邮箱:39 60 29 14 2@qq.com