服务计算——web扩展任务

1.http的关键类型及接口

1.Handler接口

所有请求的处理器、路由ServeMux都满足该接口。

type Handler interface {
   ServeHTTP(ResponseWriter, *Request)
}

接口定义声明了函数签名,任何结构只要实现了与接口函数签名相同的方法,就等同于实现了接口。

2.ServeMux结构体

HTTP请求的多路转接器(路由),它负责将每一个接收到的请求的URL与一个注册模式的列表进行匹配,并调用和URL最匹配的模式的处理器。

  • ServeMux结构体:mu是互斥锁,m是所要用的路由,key是挂载的路径,对应的值则是响应的muxEntry,hosts是指明了是否在每个路径中都声明hostnames。

  • muxEntry结构体:h就是注册的处理过程.pattern与ServeMux中m的key相同,也就是说是我们注册的路径,而explicit的用途其实是比较隐晦的。程序会根据explicit判别这个路径的Handler是否是用户注册的。如果是程序为了Redirect(详细点击这里)而设定的,那么在它是可以被覆盖的,否则就是不可以被覆盖的.

type ServeMux struct {
        mu    sync.RWMutex
        m     map[string]muxEntry
        hosts bool // whether any patterns contain hostnames
}

type muxEntry struct {
        explicit bool
        h        Handler
        pattern  string
}

3.http.HandlerFunc函数类型

默认实现了ServeHTTP的接口。

type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r) 
}

4.Server接口

默认实现了ServeHTTP的接口。

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()

}
...
// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// If srv.Addr is blank, ":http" is used.
// ListenAndServe always returns a non-nil error.
func (srv *Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }

    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})

}

2.http包的运行机制

  • 创建Listen Socket, 监听指定的端口, 等待客户端请求到来。

  • Listen Socket接受客户端的请求, 得到Client Socket, 接下来通过Client Socket与客户端通信。

  • 处理客户端的请求, 首先从Client Socket读取HTTP请求的协议头, 如果是POST方法, 还可能要读取客户端提交的数据, 然后交给相应的handler处理请求, handler处理完毕准备好客户端需要的数据, 通过Client Socket写给客户端。

1

1.注册路由
可以用http.HandlerFunc,默认的路由来注册处理函数,其源码选取了DefaultServeMux作为multiplexer。DefaultServeMux是http默认使用的ServeMux,如果我们没有自定义ServeMux,系统默认使用这个ServeMux。var DefaultServeMux = NewServeMux()

// HandleFunc registers the handler function for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    DefaultServeMux.HandleFunc(pattern, handler)
}
...
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
    mux.Handle(pattern, HandlerFunc(handler))
}

handle函数是把handler和pattern模式绑定到map[string]muxEntry的map上

// 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 " + pattern)
    }

    if handler == nil {
        panic("http: nil handler")
    }

    if mux.m[pattern].explicit {
        panic("http: multiple registrations for " + pattern)
    }

    mux.m[pattern] = muxEntry{explicit: true, h: handler, pattern: pattern}
    if pattern[0] != '/' {
        mux.hosts = true
    }

    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.

    n := len(pattern)

    if n > 0 && pattern[n-1] == '/' && !mux.m[pattern[0:n-1]].explicit {
        // If pattern contains a host name, strip it and use remaining
        // path for redirect.
        path := pattern

        if pattern[0] != '/' {
            // In pattern, at least the last character is a '/', so
            // strings.Index can't be -1.
            path = pattern[strings.Index(pattern, "/"):]
        }

        url := &url.URL{Path: path}
        mux.m[pattern[0:n-1]] = muxEntry{h: RedirectHandler(url.String(), StatusMovedPermanently), pattern: pattern}
    }
}

2.监听端口

通过ListenAndServe来监听,初始化一个server对象,调用server.ListenAndServe(),而该方法内部其实调用了net.Listen(“tcp”,addr),也就是底层用TCP协议搭建了一个服务,监听设置的端口。然后调用srv.Serve(net.Listener)函数,

这个函数处理接收客户端的请求信息。这个函数里起了一个for循环,通过Listener接收请求,创建conn,开一个goroutine,把请求的数据当作参数给conn去服务:go c.serve(),即每次请求都是在新的goroutine中去服务,利于高并发。

// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
    server := &Server{Addr: addr, Handler: handler}
    return server.ListenAndServe()

}

...

// ListenAndServe listens on the TCP network address srv.Addr and then
// calls Serve to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
// If srv.Addr is blank, ":http" is used.
// ListenAndServe always returns a non-nil error.
func (srv *Server) ListenAndServe() error {
    addr := srv.Addr
    if addr == "" {
        addr = ":http"
    }
    ln, err := net.Listen("tcp", addr)
    if err != nil {
        return err
    }

    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

3.接收客户端的请求

监控之后如何接收客户端的请求呢?代码执行监控端口之后,调用了srv.Serve(net.Listener)函数,这个函数就是处理接收客户端的请求信息。这个函数里面起了一个for{},首先通过Listener接收请求,其次创建一个Conn,最后单独开了一个goroutine,把这个请求的数据当做参数扔给这个conn去服务:go c.serve()。这个就是高并发体现了,用户的每一次请求都是在一个新的goroutine去服务,相互不影响。

// Serve accepts incoming connections on the Listener l, creating a
// new service goroutine for each. The service goroutines read requests and
// then call srv.Handler to reply to them.
// Serve always returns a non-nil error.

func (srv *Server) Serve(l net.Listener) error {
    defer l.Close()
    var tempDelay time.Duration // how long to sleep on accept failure
    for {
        rw, e := l.Accept()
        if e != nil {
            if ne, ok := e.(net.Error); ok && ne.Temporary() {
                if tempDelay == 0 {
                    tempDelay = 5 * time.Millisecond
                } else {
                    tempDelay *= 2
                }
                if max := 1 * time.Second; tempDelay > max {
                    tempDelay = max
                }
                log.Printf("http: Accept error: %v; retrying in %v", e, tempDelay)
                time.Sleep(tempDelay)
                continue
            }
            return e
        }
        tempDelay = 0
        c, err := srv.newConn(rw)
        if err != nil {
            continue
        }
        go c.serve()
    }
}

那么如何具体分配到相应的函数来处理请求呢?conn首先会解析request:c.readRequest(),然后获取相应的

handler:handler := c.server.Handler

也就是我们刚才在调用函数ListenAndServe时候的第二个参数,我们前面例子传递的是nil,也就是为空,那么默认获取

handler = DefaultServeMux

那么这个变量用来做什么的呢?对,这个变量就是一个路由器,它用来匹配url跳转到其相应的handle函数,那么这个我们有设置过吗?有,我们调用的代码里面第一句不是调用了http.HandleFunc("/", sayhelloName)嘛。这个作用就是注册了请求/的路由规则,当请求uri为”/”,路由就会转到函数sayhelloNameDefaultServeMux会调用ServeHTTP方法,这个方法内部其实就是调用sayhelloName本身,最后通过写入response的信息反馈到客户端。

2

3.总结

首先调用Http.HandleFunc

按顺序做了几件事:

  1. 调用了DefaultServeMux的HandleFunc

  2. 调用了DefaultServeMux的Handle

  3. 往DefaultServeMux的map[string]muxEntry中增加对应的handler和路由规则,其次调用http.ListenAndServe(“:9090”, nil)

按顺序做了几件事情:

  1. 实例化Server

  2. 调用Server的ListenAndServe()

  3. 调用net.Listen(“tcp”, addr)监听端口

  4. 启动一个for循环,在循环体中Accept请求

  5. 对每个请求实例化一个Conn,并且开启一个goroutine为这个请求进行服务go c.serve()

  6. 读取每个请求的内容w, err := c.readRequest()

  7. 判断handler是否为空,如果没有设置handler(这个例子就没有设置handler),handler就设置为DefaultServeMux

  8. 调用handler的ServeHttp

  9. 在这个例子中,下面就进入到DefaultServeMux.ServeHttp

  10. 根据request选择handler,并且进入到这个handler的ServeHTTP

    mux.handler(r).ServeHTTP(w, r)

  11. 选择handler:

    • 判断是否有路由能满足这个request(循环遍历ServeMux的muxEntry)

    • 如果有路由满足,调用这个路由handler的ServeHTTP

    • 如果没有路由满足,调用NotFoundHandler的ServeHTTP

内容参考《Go web编程》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值