zoukankan      html  css  js  c++  java
  • go http server 编程实践及源码分析

    第一种:最简单的

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "hello http server!")
    }
    
    func main() {
    	http.HandleFunc("/", myHandler)
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

    上面的变种1:

    //上面的一个变种
    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "hello http server 1!")
    }
    
    func main() {
    	//// 通过 HandlerFunc 把函数转换成 Handler 接口的实现对象
    	handler := http.HandlerFunc(myHandler)
    	http.Handle("/", handler)
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

    ListenAndServe函数负责监听并处理连接。

    第二种:Handler接口

    上面的那种方式发挥余地太小,比如我想设置server的Timeout时间都不能设置。这时候我们就用到了 自定义的server

    type Server struct {
        Addr		string		//TCP address to listen on
        Handler		Handler		//handler to invoke
        ReadTimeout	time.Duration	//maximum duration before timing out read of the request
        WriteTimeout time.Duration	//maximum duration before timing out write of the response
        TLSConfig	*tls.Config
        ...
    }
    //Handler是一个interface,定义如下
    type Handler interface {
        ServeHTTP(ResponseWrite, *Request)
    }
    

    所以只要我们实现了Handler接口的方法ServeHTTP就可以自定义我们的server了。示例代码如下

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    type myHandler struct{}
    
    func (myHandler myHandler) ServeHTTP(w ResponseWrite, r *Request) {
    	fmt.Fprintf(w, "hello serverhttp 1")
    }
    
    func main() {
    	server := http.Server{
    		Addr:        ":8080",
    		Handler:     &myHandler{},
    		ReadTimeout: 3 * time.Second,
    	}
    	log.Fatal(server.ListenAndServe(":8080", nil))
    }
    
    

    还有下面这种:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    type myHandler struct{}
    
    func (myHandler *myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "hello, server 2!")
    }
    
    func main() {
    	http.Handle("/", &myHandler{})
    	log.Fatal(http.ListenAndServe(":8080", nil))
    }
    

    还有这种:

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    type MyHandler struct{}
    
    func (myHandler *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    	switch r.URL.Path {
    	case "/":
    		fmt.Fprintf(w, "URL.Path = %q
    ", r.URL.Path)
    	case "/hello":
    		for k, v := range r.Header {
    			fmt.Fprintf(w, "Header[%q] = %q
    ", k, v)
    		}
    	default:
    		fmt.Fprintf(w, "404 not found: %s
    ", r.URL)
    	}
    }
    
    func main() {
    	myHandler := new(MyHandler)
    	log.Fatal(http.ListenAndServe(":8080", myHandler))
    }
    

    第三种:多个url的ServeMux

    ServeMux可以注册多个URL和handler的对应关系,并自动把请求转移到对应的handler进行处理。

    package main
    
    import (
    	"fmt"
    	"log"
    	"net/http"
    )
    
    func myHandler(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "hello server!")
    }
    
    func yourHandler(w http.ResponseWriter, r *http.Request) {
    	fmt.Fprintf(w, "your handler")
    }
    
    func main() {
    	mux := http.NewServeMux()
    	mux.HandleFunc("/my", myHandler)
    	mux.HandleFunc("/your", yourHandler)
    
    	log.Fatal(http.ListenAndServe(":8080", mux))
    }
    
    
    • 通过 NewServeMux 生成了 ServerMux 结构,URL 和 handler 是通过它注册的
    • http.ListenAndServe 方法第二个参数变成了上面的 mux 变量

    思考:上面写的那些程序是怎么实现的?

    一个HTTP请求过程要认识几个概念,服务器端的几个重要概念:

    • Request: 用户请求的信息,这个可以用来处理用户的请求信息,POST,url,cookie,header等信息
    • Response:服务器返回给用户的信息
    • Conn:每次连接的信息
    • Handler:处理请求和生成返回处理信息的逻辑

    url路由到处理函数是怎么实现的?就是上面所说的Handler的处理逻辑。
    我们先来看看golang自带的包 net/http 下的程序,主要程序在 net/http/server.go 文件中。

    经过追踪程序,去掉其他的细节末节,处理url到handler的一个最重要函数是 func (mux *ServeMux) Handle(pattern string, handler Handler),即使是func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))这个处理函数,最后也是Handle函数来处理的,看看它们具体的代码:

    //具体处理url路由到Handler
    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
        }
    }
    
    // HandleFunc registers the handler function for the given pattern.
    //路由url到HandleFunc,与上面的函数(func (mux *ServeMux) Handle(pattern string, handler Handler))有什么区别?
    //这个HandleFunc是具体的处理函数,而上面的Handle函数是一个接口,一个类型只要实现接口里面的方法,那么就可以传入进来作为Handler进行处理
    //我的理解就是提供了2中不同的处理url路由的方法
    //其实这个函数最后处理还是用了上面的Handle()方法
    func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
        if handler == nil {
            panic("http: nil handler")
        }
        mux.Handle(pattern, HandlerFunc(handler))
    }
    

    有了处理函数,肯定会有另外一个疑问,url是怎么映射到处理函数的呢?其实我们从上面方法就可以看出来,方法有一个接收体 mux *ServeMux ,看看这个具体代码是什么?

    // 一个结构体,存储了url,也存储了对于的Handler,在muxEntry里
    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
    }
    
    // 定义了存储的路由和根据路由来处理逻辑的Handler
    type muxEntry struct {
        h       Handler   //根据路由具体的处理接口
        pattern string   //定义的路由
    }
    
    // DefaultServeMux is the default ServeMux used by Serve.
    var DefaultServeMux = &defaultServeMux
    var defaultServeMux ServeMux
    

    上面的muxEntry struct 结构体有一个h字段,他是 Handler接口,它具体是什么呢?

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

    它是处理HTTP请求的一个接口。这也是设计好程序的一个方法,依赖接口,而不是具体的实现。
    也就是说只要实现了这个接口里的方法ServeHTTP(ResponseWriter, *Request),就是处理HTTP请求了。
    包里面就有一个这样的程序:

    //装饰器模式
    type HandlerFunc func(ResponseWriter, *Request)
    // ServeHTTP calls f(w, r). 实现了 Handler interface里的方法
    func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
        f(w, r)
    }
    

    包里还定义了默认处理方法,如下

    func Handle(pattern string, handler Handler) { 
        DefaultServeMux.Handle(pattern, handler) 
    }
    
    func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
        DefaultServeMux.HandleFunc(pattern, handler)
    }
    

    其实我们看看上面的方法,是不是调用的最开始我们分析的DefaultServeMux.Handle(pattern, handler)DefaultServeMux.HandleFunc(pattern, handler)
    2个处理函数 HandleHandleFunc

    包里有默认处理函数,那我们能不能自定义函数处理呢?
    答案:当然可以的。你看 第二种:Handler接口 就是自定义的处理函数,因为我们定义一个类型struct,然后它实现了Handler 接口方法 ServeHTTP , 而处理url路由到Handler的方法第二个参数就是Handler接口,
    看看方法:func (mux *ServeMux) Handle(pattern string, handler Handler)。 所以当然是可以的。

    这也是一个依赖接口设计的好处,不仅官方包可以写处理函数,我们也可以自定义处理函数。但是Handle函数参数不用修改。只要传一个实现了接口方法的类型数据就可以了。

    update: 2020.01.20 晚

  • 相关阅读:
    cnpm与npm指定有什么区别?
    Node.js与VUE安装及环境配置之Windows篇
    kafka和rabbitmq对比
    .NET笔记题库(一)
    API网关的用处
    C#问答题与附解收集(三)
    Error Code: 1175
    有时间测试dism
    sublime 3103liense
    weblogic 安装和部署项目(原创)
  • 原文地址:https://www.cnblogs.com/jiujuan/p/11762587.html
Copyright © 2011-2022 走看看