golang的http server使用及原理

本文详细介绍了Go语言中http服务器的三种使用方式,包括http.HandleFunc、http.Handle和自定义结构体实现Handler接口。通过源码分析,揭示了它们之间的区别与联系,以及http服务器的工作原理。同时,对比了使用http.ListenAndServe和http.Server.ListenAndServe的区别,强调了自定义Server结构体的灵活性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

golang的http server使用

使用方式1:

使用方式2:

使用方式3:

http server原理

方式一和方式二对比,

方式二方式三对比


golang的http server使用

参考标准库手册有明确说明http://doc.golang.ltd/

使用方式1:

http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})
http.ListenAndServe(":8080", nil)

使用方式2:

type FooHandler struct {
}

func (f FooHandler)ServeHTTP(w ResponseWriter, r *Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}

fooHandler :=  FooHandler{}

http.Handle("/foo", fooHandler)

http.ListenAndServe(":8080", nil)

使用方式3:

type FooHandler struct {
}

func (f FooHandler)ServeHTTP(w ResponseWriter, r *Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
}

fooHandler :=  FooHandler{}

s := &http.Server{
	Addr:           ":8080",
	Handler:        fooHandler,
	ReadTimeout:    10 * time.Second,
	WriteTimeout:   10 * time.Second,
	MaxHeaderBytes: 1 << 20,
}
s.ListenAndServe()

http server原理

方式一和方式二对比,

不同的是

http.HandleFunc 和 http.Handle 都是用于注册路由,可以发现两者的区别在于第二个参数,前者是一个具有 func(w http.ResponseWriter, r *http.Requests) 签名的函数,而后者是一个结构体,该结构体实现了 func(w http.ResponseWriter, r *http.Requests) 签名的方法。

查看其源码可发现两个方式都是通过DefaultServeMux 调用 Handle 方法来完成路由的注册。

http.HandleFunc源码

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

// 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))
}

http.Handle源码:

func Handle(pattern string, handler Handler) {
    DefaultServeMux.Handle(pattern, handler)
}

这两种方式,一个传入Handler接口,一个传入handle函数,最后 DefaultServeMux.Handle(pattern, handler)的时候,使用的是Handler接口,那么就需要一个将函数,转化为Handler接口的方法

由上面源码可看到,这个方法貌似是    // ----------重点关注---------  mux.Handle(pattern, HandlerFunc(handler)),但发现HandlerFunc并不是一个函数,而是一个类型,handler只是构建此类型对象的一个参数。

在其ServeHTTP方法中调用了自己。HandlerFunc并不是一个函数,而是一个类型,但是其实现了ServeHTTP方法,所以也是Handler接口。所以就能直接使用。

看其源码:

type HandlerFunc func(ResponseWriter, *Request)

// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

这就是一个源码里将函数转化为接口的一个巧妙的方法,值得学习

方式二方式三对比

两种方式基本一致,不同的是,一个使用net/http的ListenAndServe函数监听,一个使用http.Server.ListenAndServe方法监听,那么有啥不同呢?

看源码:

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

发现函数里面也是实例化一个Server结构,然后调用其ListenAndServe方法,方式2和方式3其实都是一样的,只是方式3,server结构自己定义,结构中的参数可以自己定义,而通过函数的方式,则是函数中定义的。

注意:

除了上面的不同外,还有一处不同,即方式2的hand是Nil,而方式3的hand是传入的Handler对象。

那么,这又有啥不同呢?

通过方式1和方式2的对比,可知通过函数http.hand或者http.HandFunc,最终都是   DefaultServeMux.HandleFunc(pattern, handler),因为他们的hand都是Nil。

而方式3的hand不是Nil,会调用到哪里呢?

看源码:

func (srv *Server) ListenAndServe() error {
    ...
    return srv.Serve(tcpKeepAliveListener{ln.(*net.TCPListener)})
}

继续

func (srv *Server) Serve(l net.Listener) error {
    ...
    go c.serve(ctx)
    ...
}

继续跟踪

func (c *conn) serve(ctx context.Context) {
    ...
    serverHandler{c.server}.ServeHTTP(w, w.req)
    ...
}

继续跟踪

func (sh serverHandler) ServeHTTP(rw ResponseWriter, req *Request) {
	handler := sh.srv.Handler
	if handler == nil {
		handler = DefaultServeMux
	}
	if req.RequestURI == "*" && req.Method == "OPTIONS" {
		handler = globalOptionsHandler{}
	}
	handler.ServeHTTP(rw, req)
}

好了,到这里终于明白了。

如果没有Handler,也就是为Nil的情况,那么久使用DefaultServeMux作为handler,难怪方式1,方式2,传的handler都是Nil,而hand和HandFunc都是调用的DefaultServeMux的方法。

而如果传了handler,那么久调用自定义的handler的ServeHTTP方法。

 

 

<think>嗯,用户想了解GolangHTTP模块的底层实现原理。我需要先回忆一下Go的net/http包的结构和底层机制。首先,HTTP模块分为客户端和服务端,用户可能想知道两者的实现,但问题里没有特别说明,可能需要都涵盖一下。 记得Go的HTTP服务端是基于goroutine和epoll(或者kqueue等)的事件驱动模型。每个连接进来后,会有一个goroutine处理。需要解释底层是如何监听端口,接受连接,以及处理请求的。比如,使用net.Listener来监听端口,然后进入循环接受连接,每个连接交给单独的goroutine处理。 客户端方面,可能需要提到Transport和Client的结构。Transport负责管理连接池,复用TCP连接,处理HTTP请求的发送和响应的接收。这里涉及到连接复用的机制,比如Keep-Alive,以及如何管理空闲连接。 还要考虑HTTP协议解析的部分,比如Request和Response的结构是如何解析的,可能涉及到Reader和Writer接口的使用。比如,服务端读取请求时,使用bufio.Reader来解析HTTP头部和正文,然后构造http.Request对象。 另外,中间件和处理器链路的实现也是重要部分。比如,ServeMux作为路由处理器,如何匹配URL路径,调用对应的HandlerFunc。还有中间件的实现,通过装饰器模式层层包装Handler。 底层网络库可能涉及到非阻塞I/O和事件循环,但Go通过goroutine简化了这一点,每个连接一个goroutine,同步处理,但底层可能还是用到了非阻塞的IO多路复用,比如在netpoller中。需要解释Go的运行时如何管理这些网络请求,避免阻塞。 可能还需要提到一些性能优化点,比如连接复用、减少内存分配、goroutine的轻量级特性带来的高并发能力。引用中的其他底层实现,比如channel、map、slice的实现,可能和HTTP模块的关系不大,但可以提到在处理并发请求时,goroutine之间的通信可能用到channel,但HTTP模块本身更多是处理网络IO。 用户的问题可能还涉及到如何高效处理大量并发连接,Go的并发模型如何支持这一点,比如每个连接一个goroutine,利用多核CPU,而netpoller负责将IO事件通知给goroutine,避免线程阻塞。 最后,需要确保回答结构清晰,分点说明服务端和客户端的实现原理,并结合底层数据结构、goroutine调度、连接管理等方面。可能还需要生成相关问题,比如HTTP模块如何处理高并发、性能优化方法、路由匹配机制等。</think>### Golang HTTP模块底层实现原理分步解析 #### 一、服务端实现核心机制 1. **网络监听层** - 使用$socket()$系统调用创建监听套接字,通过$bind()$和$listen()$完成端口绑定与监听 - 基于`epoll/kqueue`的I/O多路复用机制(通过Go运行时netpoller实现)实现非阻塞事件驱动[^2] - 每个TCP连接对应一个`net.Conn`结构体,底层通过`runtime.netpoll`实现高效事件通知 2. **连接处理模型** - 每个新连接创建独立goroutine处理 - 通过状态机解析HTTP协议: $$ \text{请求行} \rightarrow \text{请求头} \rightarrow \text{请求体} \rightarrow \text{响应生成} $$ - 使用`bufio.Reader`缓冲读取数据,通过有限状态机解析HTTP报文 3. **路由与处理器** - `ServeMux`实现路由匹配树结构 - 默认采用`path.Clean()`规范化URL路径 - 中间件通过`HandlerFunc`链式调用实现: $$ \text{Handler}_1(\text{Handler}_2(\text{Handler}_3(...))) $$ #### 二、客户端实现机制 1. **连接池管理** - `Transport`对象维护TCP连接池,默认最大空闲连接数100 - 支持HTTP/1.1 Keep-Alive和HTTP/2多路复用 - 空闲连接超时机制通过`IdleConnTimeout`控制(默认90秒) 2. **请求处理流程** ```go // 典型请求生命周期 Transport.GetConn() // 获取连接 -> writeRequest() // 写入请求 -> readResponse() // 读取响应 -> Transport.PutConn() // 回收连接 ``` #### 三、关键数据结构 1. **服务端核心结构** ```go type Server struct { Addr string Handler Handler // 处理器接口 ReadTimeout time.Duration WriteTimeout time.Duration TLSConfig *tls.Config } ``` 2. **请求上下文** ```go type Request struct { Method string URL *url.URL Proto string Header Header Body io.ReadCloser ContentLength int64 Host string // ... 其他元数据 } ``` #### 四、性能优化设计 1. **内存重用机制** - 通过`sync.Pool`缓存常用对象(如`bufio.Reader`) - 响应对象复用避免重复内存分配 2. **零拷贝优化** - 使用`sendfile`系统调用直接传输文件 - `io.Copy()`实现缓冲区复用
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值