看了一下net/http包
浅析net/http
好tm难,理不清。
先来一段代码
package main
import (
"net/http"
)
func Index(w http.ResponseWriter, r *http.Request){
w.Write([]byte("hello world"))
}
func main() {
http.HandleFunc("/",Index)
http.ListenAndServe(":8080",nil)
}
这段代码做了什么呢?
简单来说,就是我们访问本机的8080端口加上路径就会调用Index给我们在网页上返回“hello world”,那这是怎么一个过程呢?
我们从HandleFunc与ListenAndServe两个函数入手
HandleFunc
其代码和注释如下:
这个函数的任务就是把handler方法注册,和pattern指定的参数绑定上。
handler必须是func(ResponseWriter, *Request) 类型的方法,ResponseWriter实现了io.Writer接口,可以直接使用.write()方法或使用fmt.Fprintln()向其中写入内容
// 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)
}
这里有个DefaultServeMux,这个对象是net/http包的的全局变量,它的类型是
ServeMux
结构如下
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
}
DefaultServeMux是这个包给我们提供的默认实例,我们也可以自己声明一个ServeMux对象进行使用。
函数注册流程
这里展示了两个HandleFunc,第一个以ServeMux的指针类型为接收器,第二个没有接收器,会在内部通过DefaultServeMux调用第一个,注册进默认实例。
// HandleFunc registers the handler function for the given pattern.
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
if handler == nil {
panic("http: nil handler")
}
mux.Handle(pattern, HandlerFunc(handler))
}
// 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)
}
然后真正的注册就来了,那就是Handle函数
这个函数的第二个参数HandlerFunc(handler)是一个类型转换,转换为HandlerFunc类型,而HandlerFunc通过实现ServeHTTP方法,而实现了Handler接口,Go语言中允许为函数类型定义方法。
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
Handle函数的定义就是如下这样
// 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) {
.....
}
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.
func Handle(pattern string, handler Handler) { DefaultServeMux.Handle(pattern, handler) }
它只能接收Handler类型。
随后Handle方法将转换后的方法注册到ServeMux
ListenAndServe
函数注册后,调用ListenAndServe(":8080",nil)监听本机的8080端口。
// ListenAndServe listens on the TCP network address addr and then calls
// Serve with handler to handle requests on incoming connections.
// Accepted connections are configured to enable TCP keep-alives.
//
// The handler is typically nil, in which case the DefaultServeMux is used.
//
// ListenAndServe always returns a non-nil error.
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
这个函数创建了一个Server对象,Server结构体有比较多的字段,我们可以用这些字段来调节Web服务器参数
// A Server defines parameters for running an HTTP server.
// The zero value for Server is a valid configuration.
type Server struct {
Addr string
Handler Handler
TLSConfig *tls.Config
ReadTimeout time.Duration
ReadHeaderTimeout time.Duration
WriteTimeout time.Duration
IdleTimeout time.Duration
MaxHeaderBytes int
TLSNextProto map[string]func(*Server, *tls.Conn, Handler)
ConnState func(net.Conn, ConnState)
ErrorLog *log.Logger
BaseContext func(net.Listener) context.Context
ConnContext func(ctx context.Context, c net.Conn) context.Context
inShutdown atomicBool // true when server is in shutdown
disableKeepAlives int32 // accessed atomically.
nextProtoOnce sync.Once // guards setupHTTP2_* init
nextProtoErr error // result of http2.ConfigureServer if used
mu sync.Mutex
listeners map[*net.Listener]struct{}
activeConn map[*conn]struct{}
doneChan chan struct{}
onShutdown []func()
}
随后由Server调用“ListenAndServe”
func (srv *Server) ListenAndServe() error {
if srv.shuttingDown() {
return ErrServerClosed
}
addr := srv.Addr
if addr == "" {
addr = ":http"
}
ln, err := net.Listen("tcp", addr)
if err != nil {
return err
}
return srv.Serve(ln)
}
这个方法做的事情就是创建一个Listener ln,然后将ln作为参数交给Server.Serve方法执行。
Serve方法做的事情就是开启一个无限for循环,然后不断调用Listener.Accept()方法,去接收新连接,然开启新的goroutine处理新连接。
func (srv *Server) Serve(l net.Listener) error {
for {
rw, err := l.Accept()
if err != nil {
select {
case <-srv.getDoneChan():
return ErrServerClosed
default:
}
if ne, ok := err.(net.Error); ok && ne.Temporary() {
if tempDelay == 0 {
tempDelay = 5 * time.Millisecond
} else {
tempDelay *= 2
}
if max := 1 * time.Second; tempDelay > max {
tempDelay = max
}
srv.logf("http: Accept error: %v; retrying in %v", err, tempDelay)
time.Sleep(tempDelay)
continue
}
return err
}
connCtx := ctx
if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}
tempDelay = 0
c := srv.newConn(rw)
c.setState(c.rwc, StateNew, runHooks) // before Serve can return
go c.serve(connCtx)
}
}
这个方法中用到了指数退避策略。就是Accept发生错误后,将error转换为net.Error类型,然后判断这个错误是否是临时的,如果是临时的,就Sleep一段时间后再试。每发生一次sleep时间翻倍,最多1s。获得新连接后封装出一Conn对象,创建一个goroutine运行其serve()方法。
关于对Accept错误的处理:这种处理方式好像在gRPC中也有
临时错误可能有很多原因,比如等待过程中有中断要处理,那么linux系统可能返回EAGAIN错误(资源临时不可用),这种请求应该重试他。
而serve方法我们忽他中间 的很多步骤,它的运行大概是这样的流程:
for {
w, err := c.readRequest(ctx)
serverHandler{c.server}.ServeHTTP(w, w.req)
w.finishRequest()
}
就是不断读取客户端发起的请求,创建serverHandler对象调用其ServeHTTP()方法去处理请求,然后做清理工作。
ServerHandler相关的代码如下
// serverHandler delegates to either the server's Handler or
// DefaultServeMux and also handles "OPTIONS *" requests.
type serverHandler struct {
srv *Server
}
func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
handler := sh.srv.Handler
if handler == nil {
handler = DefaultServeMux
}
//省略
handler.ServeHTTP(rw, req)
}
handler := sh.srv.Handler就是从Server对象中获取Handler,就是http.ListrnAndServe()的第二个参数,如果设为nil,这个东西就是DefaultServeMux,会调用DefaultServeMux的ServeHTTP()方法。
就是这个方法
// ServeHTTP dispatches the request to the handler whose
// pattern most closely matches the request URL.
func (mux *ServeMux) ServeHTTP(w ResponseWriter, r *Request) {
if r.RequestURI == "*" {
if r.ProtoAtLeast(1, 1) {
w.Header().Set("Connection", "close")
}
w.WriteHeader(StatusBadRequest)
return
}
h, _ := mux.Handler(r)
h.ServeHTTP(w, r)
}
mux.Handler会根据请求方法、请求主机、请求url的路径返回格式的handler。
在匹配中要注意:
- 若注册了/目录,所以未匹配上的就匹配到/
- 最长前缀匹配,注册了/abc/,则/abc/d回匹配到/abc
- 注册路径只有以/结尾才能出发如若注册了/abc,则/abc/a匹配不到/abc。
第三的Go Web框架大多是基于net/http实现自己的ServeMux对象。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)