文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

一文读懂 Go Http Server 原理

2024-12-13 15:36

关注
package main

import (
"io"
"net/http"
)

func main() {
http.HandleFunc("/hello", hello)
http.ListenAndServe(":81", nil)
}

func hello(response http.ResponseWriter, request *http.Request) {
io.WriteString(response, "hello world")
}

这么简单,能与之一战的恐怕只有 Python 了吧,而且 Go 还能编译成可执行的二进制文件,你说牛啤不牛啤?

Http Server 如何处理连接?

我们从这一行代码看起

http.ListenAndServe(":81", nil)

从命名来看,这个方法干了两件事,监听并且服务,从方法的单一职责上来说,我觉得不ok,一个方法怎么能干两件事?但这是大佬写的代码,就很合理。

第一个参数Addr是要监听的地址和端口,第二个参数Handler一般是nil,它是真正的逻辑处理,但我们通常用第一行代码那样来注册处理器,这代码一看就感觉是把 path 映射到业务逻辑上,我们先大概了解,待会再来看它。

http.HandleFunc("/hello", hello)

如果了解过一点网络编程基础,就会知道操作系统提供了bind、listen、accept这样的系统调用,我们只要按顺序发起调用,就能组合出一个 Server。

Go 也是利用这些系统调用,把他们都封装在了ListenAndServe中。

Listen 往下追究就是系统调用,所以我们重点看 Serve:

把分支代码收起来,只看主干,发现是一个 for 循环里面在不停地 Accept,而这个 Accept 在没有连接时是阻塞的,当有连接时,起一个新的协程来处理。

Http Server 如何处理请求?

一些前置工作

处理请求的一行代码是,可以看出是每个连接单开了一个协程处理:

go c.serve(connCtx)

这里的 connCtx 代入了当前的 Server 对象:

ctx := context.WithValue(baseCtx, ServerContextKey, srv)
...
connCtx := ctx

而且还提供了修改它的 hook 方法 srv.ConnContext,可以在每次 Accept 时修改原始的 context

if cc := srv.ConnContext; cc != nil {
connCtx = cc(connCtx, rw)
if connCtx == nil {
panic("ConnContext returned nil")
}
}

它的定义是:

// ConnContext optionally specifies a function that modifies
// the context used for a new connection c. The provided ctx
// is derived from the base context and has a ServerContextKey
// value.
ConnContext func(ctx context.Context, c net.Conn) context.Context

但是如果按照我开头给的代码,你是没法修改 srv.ConnContext 的,可以改成这样来自定义:

func main() {
http.HandleFunc("/hello", hello)
server := http.Server{
Addr: ":81",
ConnContext: func(ctx context.Context, c net.Conn) context.Context {
return context.WithValue(ctx, "hello", "roshi")
},
}
server.ListenAndServe()
}

同样的 c.setState 也提供了 hook,可采取如上的方法设置,在每次连接状态改变时执行 hook 方法:

c.setState(c.rwc, StateNew, runHooks) // before Serve can return
// ConnState specifies an optional callback function that is
// called when a client connection changes state. See the
// ConnState type and associated constants for details.
ConnState func(net.Conn, ConnState)

开始真正干活

为了能看清楚 Accept 后,serve 方法到底干了什么,我们再简化一下:

func (c *conn) serve(ctx context.Context) {
...
for {
w, err := c.readRequest(ctx)
...
serverHandler{c.server}.ServeHTTP(w, w.req)
...
}
}

serve 也是一个大循环,循环里面主要是读取一个请求,然后将请求交给 Handler 处理。

为什么是一个大循环呢?因为每个 serve 处理的是一个连接,一个连接可以有多次请求。

读请求就显得比较枯燥乏味,按照Http协议,读出URL,header,body等信息。

这里有个细节是在每次读取了一个请求后,还开了一个协程去读下一个请求,也算是做了优化吧。

for {
w, err := c.readRequest(ctx)
...

if requestBodyRemains(req.Body) {
registerOnHitEOF(req.Body, w.conn.r.startBackgroundRead)
} else {
w.conn.r.startBackgroundRead() }
...
}

请求如何路由?

当读取到一个请求后,便进入这一行代码:

serverHandler{c.server}.ServeHTTP(w, w.req)

ServeHTTP 找到我们注册的 Handler 去处理,如果请求的URI 是 *或请求 Method 是 OPTIONS,则使用globalOptionsHandler,也就是说这类请求不需要我们手动处理,直接就返回了。

对于我们注册的 Handler 也需要去寻找路由,这个路由的规则还是比较简单,主要由如下三条:

举几个例子来理解一下:

注册路由为

http.HandleFunc("/hello", hello)
http.HandleFunc("127.0.0.1/hello", hello2)

此时如果执行

curl 'http://127.0.0.1:81/hello'

则会匹配到 hello2,但如果执行

curl 'http://localhost:81/hello'

就匹配的是 hello

如果注册路由为

http.HandleFunc("/hello", hello)
http.HandleFunc("127.0.0.1/hello/", hello2)

注意第二个最后还有个/,此时如果执行

curl 'http://127.0.0.1:81/hello/roshi'

也能匹配到 hello2,怎么样,是不是理解了?

找到路由之后就直接调用我们开头注册的方法,如果我们往 Response 中写入数据,就能返回给客户端,这样一个请求就处理完成了。

总结

最后我们回忆下 Go Http Server 的要点:


来源:捉虫大师内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯