01|net/http:使用标准库搭建Server并不是那么简单
该思维导图由 AI 生成,仅供参考
Web Server 的本质
- 深入了解
- 翻译
- 解释
- 总结
本文深入探讨了使用Golang中net/http标准库搭建Web Server的复杂性和重要性。作者首先解释了Web Server的本质,并深入讲解了HTTP协议的特点和工作原理。文章讨论了是否一定要使用标准库net/http,并指出了自行封装HTTP库的可能性和一些大厂的做法。强调了学习net/http标准库的重要性,并提出了快速掌握代码库的技巧。通过对库方法和结构体的分析,读者能够快速了解整个库的结构和功能。文章总结了构建HTTP客户端和服务端的功能,以及HTTP协议的各个部分的具体数据结构负责。这篇文章对于深入理解net/http标准库和避免底层原理导致问题的危险性具有重要意义。文章内容涉及技术性较强,适合对Golang Web开发感兴趣的读者阅读学习。
《手把手带你写一个 Web 框架》,新⼈⾸单¥59
全部留言(29)
- 最新
- 精选
- 程旭1. http.FileServer 创建 FileHandler 数据结构 2. FileHandler 结构体中包含 FileSystem 接口,FileSystem 接口包含Open 方法 3. http.Dir 的 Open 方法 实现 FileSystem 接口 的 Open 方法 4. http.Dir 的 Open 方法对表示字符串的文件路径进行判断: (1)先判断 分隔符是否为 "/"且该字符串中是否包含分隔符,若不满足 返回 nil 和 error信息 "http: invalid character in file path" (2)将 http.Dir 从 Dir 类型转换为 string 类型,判断该是否为空,若为空,将 dir 赋值为 "." (3)使用 path.Clean ,filepath.FromSlash 和 filepath.Join 方法获得路径全名 (4)使用 os.Open 方法打开文件,如果打开失败,返回错误信息,如果成功以读模式打开文件
作者回复: 你好,你的逻辑是正确的,不过可能过多关注分支细节。在使用思维导图的时候,如果对于比较复杂的逻辑,我们需要分析哪些是关键节点,哪些是非关键节点。 比如FileServer, 其关键点有两个: 1 fileHandler 我们能和ListenAndServe 连接起来,它提供了ServeHTTP的方法, 这个是请求处理的入口函数 2 FileServer 最本质的函数是封装了io.CopyN,基本逻辑是: 如果是读取文件夹,则遍历文件夹内所有文件,将文件名直接输出返回值。 如果是读取文件,则设置文件的阅读指针(如果需要多次读取文件,创建goroutine,且为每个goroutine创建阅读指针),使用io.CopyN读取文件内容输出返回值。
2021-09-18322 - return很赞, 读源码的 思路和 思维导图 很值得学习
作者回复: 恩,我平时阅读源码就是这样阅读的。经验之谈。感谢支持。
2021-09-1416 - 好家庭老师,请问 go c.serve(connCtx) 里面为什么还有一个循环?c值得是一个connection,我理解不是每个连接处理一次就好了吗,为啥还有一个for循环呢? ··· / Serve a new connection. func (c *conn) serve(ctx context.Context) { c.remoteAddr = c.rwc.RemoteAddr().String() ctx = context.WithValue(ctx, LocalAddrContextKey, c.rwc.LocalAddr()) defer func() { if err := recover(); err != nil && err != ErrAbortHandler { const size = 64 << 10 buf := make([]byte, size) buf = buf[:runtime.Stack(buf, false)] c.server.logf("http: panic serving %v: %v\n%s", c.remoteAddr, err, buf) } if !c.hijacked() { c.close() c.setState(c.rwc, StateClosed) } }() ... for { w, err := c.readRequest(ctx) if c.r.remain != c.server.initialReadLimitSize() { // If we read any bytes off the wire, we're active. c.setState(c.rwc, StateActive) } if err != nil { const errorHeaders = "\r\nContent-Type: text/plain; charset=utf-8\r\nConnection: close\r\n\r\n" ... } ... ```
作者回复: http服务在启动的时候,会默认开启keep-alive机制。keep-alive机制就是一个连接在一个请求结束之后,并不关闭当前连接,在下个请求的时候也能使用这个连接。这个for循环就是为keep-alive机制服务的。在服务一个连接的时候,处理完一个请求,并不关闭这个连接,而是循环等待下个请求。 那要关闭keep-alive怎么办呢?你也可以在for循环中的w.conn.server.doKeepAlives 看到,它判断如果服务端的 disableKeepAlives 不是0,则设置了关闭keep-alive,则就不进行for循环了。
2021-09-169 - Middleware目录有点不清晰,从零开始,那么是不是应该给出建立合适的文件目录结构,命名。我们也能跟着上手敲一遍。比如这个 framework .目录是如何命名。希望老师真的能手把手
作者回复: 你好,在后面有一章会专门介绍目录的章节,叫《如何系统设计框架的整体目录》。每一个章节,我都有存放源码在github项目的对应分支,如果希望跟着上手敲一遍,可以跟着文章敲完代码,跟着对应分支对一遍。https://github.com/gohade/coredemo/tree/geekbang/01 谢谢支持,另外为你的ID middleware 点个赞。
2021-09-1439 - Groot一篇文章值回票价,感觉后续的文章都是在做慈善 😂 受益匪浅,感谢分享 👍
作者回复: 你好,非常感谢支持。非常高兴本篇能让你有一些收获。后续文章是介绍写框架的过程的。感谢。
2021-09-1834 - ghostwritten打卡第二天: https://github.com/gohade/coredemo/blob/geekbang/01/go.mod https://datatracker.ietf.org/doc/html/rfc2616 https://github.com/valyala/fasthttp https://pkg.go.dev/net/http@go1.15.5 Web Server 第一个go架构:net/http 熟悉库技巧:库函数 > 结构定义 > 结构函数 查看库命令: go doc net/http | grep "^func go doc net/http | grep "^type"|grep struct 结构函数如下: // 创建一个Foo路由和处理函数 http.Handle("/foo", fooHandler) // 创建一个bar路由和处理函数 http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path)) }) // 监听8080端口 log.Fatal(http.ListenAndServe(":8080", nil)) 画源代码分析图,学会脑图构思是关键(略) 流程: - 第一层,标准库创建 HTTP 服务是通过创建一个 Server 数据结构完成的; - 第二层,Server 数据结构在 for 循环中不断监听每一个连接; - 第三层,每个连接默认开启一个 Goroutine 为其服务; - 第四、五层,serverHandler 结构代表请求对应的处理逻辑,并且通过这个结构进行具体业务逻辑处理; - 第六层,Server 数据结构如果没有设置处理函数 Handler,默认使用 `DefaultServerMux` 处理请求; - 第七层,`DefaultServerMux` 是使用 map 结构来存储和查找路由规则。 创建框架的 Server 结构 1.创建一个 coredemo/framework/core.go,实现具体业务逻辑 // 框架核心结构 type Core struct { } // 初始化框架核心结构 func NewCore() *Core { return &Core{} } // 框架核心结构实现Handler接口 func (c *Core) ServeHTTP(response http.ResponseWriter, request *http.Request) { // TODO } 2.创建一个 coredemo/main.go,创建服务的方法 `ListenAndServe` 先定义了监听信息 `net.Listen`,然后调用 `Serve` 函数,实现对外提供服务
作者回复: 👍坚持一起仗剑走天涯
2021-09-223 - 布丁老厮老师,HTTP 库 Server 的代码流程是不是应该为:创建服务 -> 监听请求 -> 创建连接 -> 处理请求 要更准确一点?因为net.Listen是在srv.newConn之前进行的。
作者回复: 是的,确实是顺序错误,已经联系编辑进行修改了。感谢提醒。
2021-09-173 - 逗逼章鱼FileServer 的主要流程前面五层应该都一样,第六层开始不一样,FileServer --> fileHandler --> fileHandler里实现的 ServeHTTP --> serveFile 。
作者回复: 是的,主要在于第六层的ServeHTTP,不同的handler实现的ServeHTTP是不一样的。
2021-09-152 - Ppppppp前两天再看这一块儿,画了好几个”蜘蛛图“,人都看蒙了。感谢感谢!!
作者回复: 感谢
2023-02-14归属地:江苏1 - Geek_8ed998老师为啥你代码里mani.go里面import 是"coredemo/framework" ,而我这里按你一样的目录结构要写成"./framework"才能找到包
作者回复: 这个是gomod的机制,如果你的项目在gomod中命名为coredemo, 那么coredemo/framework就会去你的项目下的framework目录查询,当然你这里也能写成./framework。但是别人用到你的库的时候就会出现错误
2021-12-09