gin-net-http 2
目前知道gin是基于net/http进一步封装,来看下net/http是怎样写web的
package main
import (
"fmt"
"net/http"
)
func index(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World")
}
func main() {
http.HandleFunc("/", index)
http.ListenAndServe(":8080", nil)
}
首先,调用http.HandleFunc("/", index)
注册路径处理函数,这里将路径/
的处理函数设置为index
。处理函数的类型必须是:、
func (http.ResponseWriter, *http.Request)
其中*http.Request
表示 HTTP 请求对象,该对象包含请求的所有信息,如 URL、首部、表单内容、请求的其他内容等。
http.ResponseWriter
是一个接口类型:
// net/http/server.go
type ResponseWriter interface {
Header() Header
Write([]byte) (int, error)
WriteHeader(statusCode int)
}
用于向客户端发送响应,实现了ResponseWriter
接口的类型显然也实现了io.Writer
接口。所以在处理函数index
中,可以调用fmt.Fprintln()
向ResponseWriter
写入响应信息。
net/http
包中HandleFunc()
函数的源码:
// The documentation for ServeMux explains how patterns are matched.
func HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
DefaultServeMux.HandleFunc(pattern, handler)
}
接调用了一个名为DefaultServeMux
对象的HandleFunc()
方法。DefaultServeMux
是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
}
var DefaultServeMux = &defaultServeMux
var defaultServeMux ServeMux
像这种提供默认类型实例的用法在 Go 语言的各个库中非常常见,在默认参数就已经足够的场景中使用默认实现很方便。ServeMux
保存了注册的所有路径和处理函数的对应关系。ServeMux.HandleFunc()
方法如下:
func (mux *ServeMux) HandleFunc(pattern string, handler func(ResponseWriter, *Request)) {
mux.Handle(pattern, HandlerFunc(handler))
}
这里将处理函数handler
转为HandlerFunc
类型,然后调用ServeMux.Handle()
方法注册。注意这里的HandlerFunc(handler)
是类型转换,而非函数调用,类型HandlerFunc
的定义如下:
type HandlerFunc func(ResponseWriter, *Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
HandlerFunc
实际上是以函数类型func(ResponseWriter, *Request)
为底层类型,为HandlerFunc
类型定义了方法ServeHTTP
mux.Handle(pattern, HandlerFunc(handler)) 调用了func (mux *ServeMux) Handle(pattern string, handler Handler)
// 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) {
mux.mu.Lock()
defer mux.mu.Unlock()
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
}
}
也就是:
mux.Handle(pattern, HandlerFunc(handler))------>
func (mux *ServeMux) Handle(pattern string, handler Handler)
HandlerFunc(handler)---
--->Handler
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
HandlerFunc类型只是为了方便注册函数类型的处理器
通过HandleFunc()注册的称为处理函数,将通过Handle()注册的称为处理器
注册了处理逻辑后,调用http.ListenAndServe(":8080", nil)
监听本地计算机的 8080 端口,开始处理请求。下面看源码的处理:
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
ListenAndServe
创建了一个Server
类型的对象:
调用此对象的ListenAndServe。里面
先调用net.Listen()
监听端口,将返回的net.Listener
作为参数调用Server.Serve()
方法:
在Server.Serve()
方法中,使用一个无限的for
循环,不停地调用Listener.Accept()
方法接受新连接,开启新 goroutine 处理新连接:
func (srv *Server) Serve(l net.Listener) error {
var tempDelay time.Duration // how long to sleep on accept failure
for {
rw, err := l.Accept()
if err != nil {
-----------------
return err
}
tempDelay = 0
c := srv.newConn(rw)
go c.serve(connCtx)
}
}
func (c *conn) serve(ctx context.Context) {
----------------
for {
---------------------
w, err := c.readRequest(ctx)
serverHandler{c.server}.ServeHTTP(w, w.req)
w.finishRequest()
}
}
serve()
方法其实就是不停地读取客户端发送地请求,创建serverHandler
对象调用其ServeHTTP()
方法去处理请求,然后做一些清理工作。serverHandler
只是一个中间的辅助结构,代码如下:
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)
}
从Server
对象中获取Handler
,这个Handler
就是调用http.ListenAndServe()
时传入的第二个参数。在Hello World
的示例代码中,我们传入了nil
。所以这里handler
会取默认值DefaultServeMux
。调用DefaultServeMux.ServeHTTP()
方法处理请求:
而对于gin框架 这个位置handler部位nil,此时调用注册进来的ServerHTTP
// 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(r)
通过请求的路径信息查找处理器,然后调用处理器的ServeHTTP()
方法处理请求:
func (mux *ServeMux) Handler(r *Request) (h Handler, pattern string) {
--------
-------------------
// All other requests have any port stripped and path cleaned
// before passing to mux.handler.
host := stripHostPort(r.Host)
path := cleanPath(r.URL.Path)
// If the given path is /tree and its handler is not registered,
// redirect for /tree/. 如果则注册8080/hello; 请求8080/hello/ 则会匹配到8080/hello 然后返回301
if u, ok := mux.redirectToPathSlash(host, path, r.URL); ok {
return RedirectHandler(u.String(), StatusMovedPermanently), u.Path
}
// 注意这个位置 如果则注册8080/hello/; 请求8080/hello 则会匹配到8080/hello/ 然后返回301
if path != r.URL.Path { // 注意这个位置 如果则注册8080/hello; 请求8080/hello/ 则会匹配到8080/hello 然后返回301
_, pattern = mux.handler(host, path)
u := &url.URL{Path: path, RawQuery: r.URL.RawQuery}
return RedirectHandler(u.String(), StatusMovedPermanently), pattern
}
return mux.handler(host, r.URL.Path)
}
// handler is the main implementation of Handler.
// The path is known to be in canonical form, except for CONNECT methods.
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
}
这是默认net-http的逻辑,那么gin呢。由于gin是基于net/http封装的?那么其回调函数是啥?
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/", func(c *gin.Context) {
c.String(200, "Hello!!!!")
})
r.Run()
}
来看r.Run函数
// Run attaches the router to a http.Server and starts listening and serving HTTP requests.
// It is a shortcut for http.ListenAndServe(addr, router)
// Note: this method will block the calling goroutine indefinitely unless an error happens.
func (engine *Engine) Run(addr ...string) (err error) {
defer func() { debugPrintError(err) }()
address := resolveAddress(addr)
debugPrint("Listening and serving HTTP on %s\n", address)
err = http.ListenAndServe(address, engine.Handler())
return
}
和net-http web使用中唯一区别就是: http.ListenAndServe(address, engine.Handler()) 传入的第二个参数不一样
所以net-http会调用时gin 的handler.ServeHTTP(rw, req)函数也就是
http.ListenAndServe(address, engine.Handler())
func (engine *Engine) Handler() http.Handler {
if !engine.UseH2C {
return engine
}
h2s := &http2.Server{}
return h2c.NewHandler(engine, h2s)
}
func NewHandler(h http.Handler, s *http2.Server) http.Handler {
return &h2cHandler{
Handler: h,
s: s,
}
}
---->http.ListenAndServe(address, NewHandler())
注册的是h2cHandler mux, 所以调用的是h2cHandler的ServeHTTP
/ ServeHTTP implement the h2c support that is enabled by h2c.GetH2CHandler.
func (s h2cHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Handle h2c with prior knowledge (RFC 7540 Section 3.4)
if r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0" {
if http2VerboseLogs {
log.Print("h2c: attempting h2c with prior knowledge.")
}
conn, err := initH2CWithPriorKnowledge(w)
if err != nil {
if http2VerboseLogs {
log.Printf("h2c: error h2c with prior knowledge: %v", err)
}
return
}
defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{
Context: r.Context(),
BaseConfig: extractServer(r),
Handler: s.Handler,
SawClientPreface: true,
})
return
}
// Handle Upgrade to h2c (RFC 7540 Section 3.2)
if isH2CUpgrade(r.Header) {
conn, settings, err := h2cUpgrade(w, r)
if err != nil {
if http2VerboseLogs {
log.Printf("h2c: error h2c upgrade: %v", err)
}
w.WriteHeader(http.StatusInternalServerError)
return
}
defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{
Context: r.Context(),
BaseConfig: extractServer(r),
Handler: s.Handler,
UpgradeRequest: r,
Settings: settings,
})
return
}
s.Handler.ServeHTTP(w, r) ----->Engine.ServerHTTP(w.r)
return
}
gin 中Engine的ServerHTTP
// ServeHTTP conforms to the http.Handler interface.
func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
// 这里使用了对象池
c := engine.pool.Get().(*Context)
// Get对象后做初始化
c.writermem.reset(w)
c.Request = req
c.reset()
// 处理HTTP请求的函数
engine.handleHTTPRequest(c)
// 处理完请求后将对象放回池子
engine.pool.Put(c)
}
后面的逻辑见gin路由
对于HTTP2.0逻辑 如下:
conn, err := initH2CWithPriorKnowledge(w)
if err != nil {
if http2VerboseLogs {
log.Printf("h2c: error h2c with prior knowledge: %v", err)
}
return
}
defer conn.Close()
s.s.ServeConn(conn, &http2.ServeConnOpts{
Context: r.Context(),
BaseConfig: extractServer(r),
Handler: s.Handler,
SawClientPreface: true,
})
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理