Go的http有两个核心功能:Conn、ServeMux(多路复用器)
Go 提供了一系列用于创建 Web 服务器的标准库,而且通过 Go 创建一个服务器的 步骤非常简单,只要通过 net/http 包调用 ListenAndServe 函数并传入网络地址以及负责处理请求的处理器( handler )作为参数就可以了。如果网络地址参数为空字符串,那 么服务器默认使用 80 端口进行网络连接;如果处理器参数为 nil,那么服务器将使用默认的多路复用器 DefaultServeMux。Conn的goroutine(这部分内容放在go语言并发编程当中,感兴趣的同学可先学习)。
与我们一般编写的http服务器不同, Go为了实现高并发和高性能, 使用了goroutines来处理Conn的读写事件, 这样每个请求都能保持独立,相互不会阻塞,可以高效的响应网络事件。这是Go高效的保证。Go在等待客户端请求里面是这样写的:
c, err := srv.newConn(rw)
if err != nil {
continue
}
go c.serve()
这里我们可以看到客户端的每次请求都会创建一个Conn,这个Conn里面保存了该次请求的信息,然后再传递到对应的handler,该handler中便可以读取到相应的header信息,这样保证了每个请求的独立性。
ServeMux的自定义
我们前面小节讲述conn.server的时候,其实内部是调用了http包默认的路由器,通过路由器把本次请求的信息传递到了后端的处理函数。那么这个路由器是怎么实现的呢?它的结构如下:
type ServeMux struct {
mu sync.RWMutex //锁,由于请求涉及到并发处理,因此这里需要一个锁机制
m map[string]muxEntry // 路由规则,一个string对应一个mux实体,这里的string就是注册的路由表达式
hosts bool // 是否在任意的规则中带有host信息
}
下面看一下muxEntry
type muxEntry struct {
explicit bool // 是否精确匹配
h Handler // 这个路由表达式对应哪个handler
pattern string //匹配字符串
}
接着看一下Handler的定义
type Handler interface {
ServeHTTP(ResponseWriter, *Request) // 路由实现器
}
Handler是一个接口,但是前一小节中的sayhelloName函数并没有实现ServeHTTP这个接口,为什么能添加呢?原来在http包里面还定义了一个类型HandlerFunc,我们定义的函数sayhelloName就是这个HandlerFunc调用之后的结果,这个类型默认就实现了ServeHTTP这个接口,即我们调用了HandlerFunc(f),强制类型转换f成为HandlerFunc类型,这样f就拥有了ServeHTTP方法。
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
路由器里面存储好了相应的路由规则之后,那么具体的请求又是怎么分发的呢?请看下面的代码,默认的路由器实现了ServeHTTP:
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
w.Header().Set("Connection", "close")
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
如上所示路由器接收到请求之后,如果是*那么关闭链接,不然调用mux.Handler®返回对应设置路由的处理Handler,然后执行h.ServeHTTP(w, r)
也就是调用对应路由的handler的ServerHTTP接口,那么mux.Handler®怎么处理的呢?
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
if r.Method != "CONNECT" {
if p := cleanPath(r.URL.Path); p != r.URL.Path {
_, pattern = mux.handler(r.Host, p)
return RedirectHandler(p, StatusMovedPermanently), pattern
}
}
return mux.handler(r.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
}
Go其实支持外部实现的路由器 ListenAndServe的第二个参数就是用以配置外部路由器的,它是一个Handler接口,即外部路由器只要实现了Handler接口就可以,我们可以在自己实现的路由器的ServeHTTP里面实现自定义路由功能。
如下代码所示,我们自己实现了一个简易的路由器
package main
import (
"fmt"
"net/http"
)
type MyMux struct {
}
func (p *MyMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if r.URL.Path == "/" {
sayhelloName(w, r)
return
}
http.NotFound(w, r)
return
}
func sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello myroute!")
}
func main() {
mux := &MyMux{}
http.ListenAndServe(":9090", mux)
}
Go 提供了一系列用于创建 Web 服务器的标准库,而且通过 Go 创建一个服务器的 步骤非常简单,只要通过 net/http 包调用 ListenAndServe 函数并传入网络地址以及负责处理请求的处理器( handler )作为参数就可以了。
如果网络地址参数为空字符串,那 么服务器默认使用 80 端口进行网络连接;如果处理器参数为 nil,那么服务器将使用默认的多路复用器 DefaultServeMux,当然,我们也可以通过调用 NewServeMux 函数创建一个多路复用器。多路复用器接收到用户的请求之后根据请求的 URL 来判断使用哪 个处理器来处理请求,找到后就会重定向到对应的处理器来处理请求
使用默认的多路复用器(DefaultServeMux)
package main
import (
"fmt"
"net/http"
)
type DefineServerMux struct{}
func (dsm *DefineServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "创建自定义的多路复用处理器defineServerMux")
}
func ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "HandleFunc将一个普通的函数ServeHTTP包装成Handle")
}
func main() {
defineServerMux := DefineServerMux{}
http.Handle("/defineServerMux", &defineServerMux)
http.HandleFunc("/",ServeHTTP)
//这里我们看到了,第二个参数要求传递的是一个handle,但是为什么handle设置为nil
http.ListenAndServe(":8080", nil)
}
使用自己创建的多路复用器
在创建服务器时,我们还可以通过 NewServeMux 方法创建一个多路复用器
func NewServeMux() *ServeMux
NewServeMux创建并返回一个新的*ServeMux
package main
import (
"fmt"
"net/http"
)
type DefineServerMux struct{}
func (dsm *DefineServerMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "创建自定义的多路复用处理器defineServerMux")
}
func main() {
defineServerMux := DefineServerMux{}
http.Handle("/defineServerMux", &defineServerMux)
http.ListenAndServe(":8080", nil)
}
ServeMux类型是HTTP请求的多路转接器。它会将每一个接收的请求的URL与一个注册模式的列表进行匹配,并调用和URL最匹配的模式的处理器。结构体 ServeMux 的相关方法
func (*ServeMux) Handle
func (mux *ServeMux) Handle(pattern string, handler Handler)
Handle注册HTTP处理器handler和对应的模式pattern。如果该模式已经注册有一个处理器,Handle会panic。
func (*ServeMux) HandleFunc
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request))
HandleFunc注册一个处理器函数handler和对应的模式pattern。
func (*ServeMux) Handler
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string)
Handler根据r.Method、r.Host和r.URL.Path等数据,返回将用于处理该请求的HTTP处理器。它总是返回一个非nil的处理器。如果路径不是它的规范格式,将返回内建的用于重定向到等价的规范路径的处理器。
Handler也会返回匹配该请求的的已注册模式;在内建重定向处理器的情况下,pattern会在重定向后进行匹配。如果没有已注册模式可以应用于该请求,本方法将返回一个内建的"404 page not found"处理器和一个空字符串模式。
func (*ServeMux) ServeHTTP
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request)
ServeHTTP将请求派遣到与请求的URL最匹配的模式对应的处理器。
使用实现ServerHTTP的类型来自定义的处理器处理请求
package main
import (
"fmt"
"net/http"
)
type MyHandlers struct{}
func (m *MyHandlers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "通过自己创建的处理器处理请求!")
}
func main() {
myHandler := MyHandlers{}
http.Handle("/myHandler", &myHandler)
http.ListenAndServe(":8080", nil)
}
通过 Server 结构对服务器进行更详细的配置
Server结构体也定义了实现http的相关方法
package main
import (
"fmt"
"net/http"
"strings"
"time"
)
type MyDefinitionMux struct{}
func (m *MyDefinitionMux)sayhelloName(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello 我是波哥,go语言体系课@luboke.com!") // 这个写入到 w 的是输出到客户端的
}
func (h *MyDefinitionMux) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "测试通过 Server 结构详细配置服务器")
//h.sayhelloName(w,r)
}
func main() {
myDefinitionMux := MyDefinitionMux{}
//对net/http包中 Server 结构体设置
server := http.Server{
Addr: ":8080",
//自定义handle
Handler: &myDefinitionMux,
ReadTimeout: 2 * time.Second,
}
server.ListenAndServe()
}
版权声明: 本文章为全栈编程go语言体系课视频教程配套电子书,版权归 全栈编程@luboke.com所有,欢迎免费学习,转载必须注明出处!但禁止任何商业用途,否则将受到法律制裁!
https://luboke.com/go/go2.7.html