golang的http server使用及原理

古城微笑少年丶 2022-10-16 05:51 196阅读 0赞

目录

golang的http server使用

使用方式1:

使用方式2:

使用方式3:

http server原理

方式一和方式二对比,

方式二方式三对比


golang的http server使用

参考标准库手册有明确说明http://doc.golang.ltd/

使用方式1:

  1. http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
  2. fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  3. })
  4. http.ListenAndServe(":8080", nil)

使用方式2:

  1. type FooHandler struct {
  2. }
  3. func (f FooHandler)ServeHTTP(w ResponseWriter, r *Request) {
  4. fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  5. }
  6. fooHandler := FooHandler{}
  7. http.Handle("/foo", fooHandler)
  8. http.ListenAndServe(":8080", nil)

使用方式3:

  1. type FooHandler struct {
  2. }
  3. func (f FooHandler)ServeHTTP(w ResponseWriter, r *Request) {
  4. fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
  5. }
  6. fooHandler := FooHandler{}
  7. s := &http.Server{
  8. Addr: ":8080",
  9. Handler: fooHandler,
  10. ReadTimeout: 10 * time.Second,
  11. WriteTimeout: 10 * time.Second,
  12. MaxHeaderBytes: 1 << 20,
  13. }
  14. s.ListenAndServe()

http server原理

方式一和方式二对比,

不同的是

http.HandleFunc 和 http.Handle 都是用于注册路由,可以发现两者的区别在于第二个参数,前者是一个具有 func(w http.ResponseWriter, r *http.Requests) 签名的函数,而后者是一个结构体,该结构体实现了 func(w http.ResponseWriter, r *http.Requests) 签名的方法。

查看其源码可发现两个方式都是通过DefaultServeMux 调用 Handle 方法来完成路由的注册。

http.HandleFunc源码

  1. func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  2. DefaultServeMux.HandleFunc(pattern, handler)
  3. }
  4. // HandleFunc registers the handler function for the given pattern.
  5. // ----------重点关注----------
  6. func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
  7. if handler == nil {
  8. panic("http: nil handler")
  9. }
  10. // ----------重点关注----------
  11. mux.Handle(pattern, HandlerFunc(handler))
  12. }

http.Handle源码:

  1. func Handle(pattern string, handler Handler) {
  2. DefaultServeMux.Handle(pattern, handler)
  3. }

这两种方式,一个传入Handler接口,一个传入handle函数,最后 DefaultServeMux.Handle(pattern, handler)的时候,使用的是Handler接口,那么就需要一个将函数,转化为Handler接口的方法

由上面源码可看到,这个方法貌似是 // —————重点关注————- mux.Handle(pattern, HandlerFunc(handler)),但发现HandlerFunc并不是一个函数,而是一个类型,handler只是构建此类型对象的一个参数。

在其ServeHTTP方法中调用了自己。HandlerFunc并不是一个函数,而是一个类型,但是其实现了ServeHTTP方法,所以也是Handler接口。所以就能直接使用。

看其源码:

  1. type HandlerFunc func(ResponseWriter, *Request)
  2. // ServeHTTP calls f(w, r).
  3. func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  4. f(w, r)
  5. }

这就是一个源码里将函数转化为接口的一个巧妙的方法,值得学习

方式二方式三对比

两种方式基本一致,不同的是,一个使用net/http的ListenAndServe函数监听,一个使用http.Server.ListenAndServe方法监听,那么有啥不同呢?

看源码:

  1. func ListenAndServe(addr string, handler Handler) error {
  2. server := &Server{Addr: addr, Handler: handler}
  3. return server.ListenAndServe()
  4. }

发现函数里面也是实例化一个Server结构,然后调用其ListenAndServe方法,方式2和方式3其实都是一样的,只是方式3,server结构自己定义,结构中的参数可以自己定义,而通过函数的方式,则是函数中定义的。

注意:

除了上面的不同外,还有一处不同,即方式2的hand是Nil,而方式3的hand是传入的Handler对象。

那么,这又有啥不同呢?

通过方式1和方式2的对比,可知通过函数http.hand或者http.HandFunc,最终都是 DefaultServeMux.HandleFunc(pattern, handler),因为他们的hand都是Nil。

而方式3的hand不是Nil,会调用到哪里呢?

看源码:

  1. func (srv *Server) ListenAndServe() error {
  2. ...
  3. return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
  4. }

继续

  1. func (srv *Server) Serve(l net.Listener) error {
  2. ...
  3. go c.serve(ctx)
  4. ...
  5. }

继续跟踪

  1. func (c *conn) serve(ctx context.Context) {
  2. ...
  3. serverHandler{c.server}.ServeHTTP(w, w.req)
  4. ...
  5. }

继续跟踪

  1. func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
  2. handler := sh.srv.Handler
  3. if handler == nil {
  4. handler = DefaultServeMux
  5. }
  6. if req.RequestURI == "*" && req.Method == "OPTIONS" {
  7. handler = globalOptionsHandler{}
  8. }
  9. handler.ServeHTTP(rw, req)
  10. }

好了,到这里终于明白了。

如果没有Handler,也就是为Nil的情况,那么久使用DefaultServeMux作为handler,难怪方式1,方式2,传的handler都是Nil,而hand和HandFunc都是调用的DefaultServeMux的方法。

而如果传了handler,那么久调用自定义的handler的ServeHTTP方法。

发表评论

表情:
评论列表 (有 0 条评论,196人围观)

还没有评论,来说两句吧...

相关阅读

    相关 Golanghttp server

    基于HTTP构建的网络应用包括两个端,即客户端(Client)和服务端(Server)。两个端的交互行为包括从客户端发出request、服务端接受request进行处理并返回r