zoukankan      html  css  js  c++  java
  • 路由与http服务

    本文主要讲解go语言web编程中的路由与http服务基本原理。

    首先,使用go语言启动一个最简单的http服务:

    package main
    
    import (
    	"log"
    	"net/http"
    )
    
    func main() {
    	http.HandleFunc("/", sayHello)
    	log.Println("server running...")
    	log.Fatal(http.ListenAndServe("localhost:4000", nil))
    }
    
    func sayHello(writer http.ResponseWriter, req *http.Request) {
    	writer.Write([]byte("hello world!"))
    }
    

    编译,运行,浏览器访问 http://localhost:4000/ ,输出 hello world! 。
    总的来说,这段代码只是做了两件事情,第一,注册路由,指定客户端请求路径对应的响应函数:
    http.HandleFunc("/", sayHello)
    第二,启动http服务,监听端口,接受并响应客户端请求:
    http.ListenAndServe("localhost:4000", nil)

     

    先看第一件事情——注册路由,指定请求路径对应的响应函数。
    首先看 http.HandleFunc() 函数源码:

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

    其中 DefaultServeMux 是go的默认路由器,所以注册路由实际上是由路由器进行的,http.HandleFunc() 函数只是对它进行封装,那么路由器的结构是怎么样的呢?
    源码可见:

    type ServeMux struct {
    	mu    sync.RWMutex
    	m     map[string]muxEntry
    	es    []muxEntry
    	hosts bool
    }
    type muxEntry struct {
    	h       Handler
    	pattern string
    }
    

    其中 ServeMux 结构中的 map[string]muxEntry 就是用来保存请求路径与响应函数之间的映射。从 muxEntry 结构定义可知,响应函数的类型为 Handler,而Handler实际上是一个接口类型,源码如下:

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

    所以,响应函数需要实现这个接口,才能进行路由注册。
    源码中声明了一个 HandlerFunc 类型,就实现了 Handler 接口:

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

    所以,只要我们的响应函数满足结构 func (http.ResponseWriter, *http.Request) ,即可进行路由注册,注册路由时,路由器会将其类型强制转换为 HandlerFunc 。其中,http.ResponseWriter参数包含了响应头、响应数据等响应相关信息,而http.Request参数则包含了请求头、请求参数等请求相关信息。

     

    再看第二件事情,启动http服务,监听端口,接受并响应客户端请求。
    首先看 http.ListenAndServe() 函数源码:

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

    其中 Server 即为http服务器类型,其结构如下(省略了部分字段):

    type Server struct {
    	Addr    string
    	Handler Handler
    ......
    }
    

    其中 Addr 为服务器监听的ip与端口字符串,Handler 为路由器,指定其为 nil 时,go会使用它的默认路由器 DefaultServeMux (调用 http.HandleFunc() 方法注册路由时就是注册到这个默认的路由器)。
    服务器监听端口,接受客户端请求,并做出响应,这个过程可借助《go web编程》中的一张图示来帮助理解:

    图中有两个红色矩形标记,第一个,说明针对客户端的每一个请求,go都会使用一个Goroutine进行响应,保证每个请求都能独立,相互不会阻塞,可以高效响应网络事件;第二,最终调用默认路由器的 ServeHTTP(w ResponseWriter, r *Request) 方法进行路由,从请求路径与响应函数的映射中找到对应的handler,最后调用handler的 ServeHTTP(w ResponseWriter, r *Request) 方法,从上面 HandlerFunc 类型的 ServeHTTP(w ResponseWriter, r *Request) 方法可知,其实最后调用的就是我们注册路由时定义的响应函数本身。

    使用go默认路由器的不足之处是,不满足RESTful规则,而且对请求路径的路由只支持绝对匹配,不支持正则匹配。如果想设计一些特殊、简便的路由,需要设计一个自定义路由器,并让go的http服务器使用这个自定义路由器。关于自定义路由器的设计,可以参考笔者另一篇博文:go web编程——自定义路由设计

    借鉴:
    《Go Web编程》

  • 相关阅读:
    集合的一些操作总结
    字符串的操作
    python字典的操作总结
    python中的列表知识总结
    Python利用文件操作实现用户名的存储登入操作
    如何理解:城市的“信息化→智能化→智慧化”
    程序员必备技能-怎样快速接手一个项目
    程序员的职业规划
    只要 8 个步骤,学会这个 Docker 命令终极教程!
    使用GitLab实现CI/CD
  • 原文地址:https://www.cnblogs.com/wujuntian/p/11858632.html
Copyright © 2011-2022 走看看