留言本-CmdEye技术交流博客
留言本-CmdEye技术交流博客
留言本-CmdEye技术交流博客

揭晓 Go web 框架底层原理!

你还在背 web 框架的面试题么?

你还在为那些面试题里面的知识点,搞得一头问号么?

你还在为刁钻的面试官问 web 框架发愁么?

…….

这些我都知道,我都知道。

乖,别哭了 (,,´•ω•)ノ

所以,我准备开设一个系列文章,我们自己来动手实现一个 web 框架,以练带学,让你正在去了解 web 框架背后的逻辑。

契机

Go 语言发展至今也有些年头了,对于刚接触 Go web 框架的同学:

第一关就是我该学什么框架?

然后网上一搜,主流的有 beego、gin、echo等,特别多。

于是就站在了十字路口,不知道往哪边走。

如果问我怎么选的话,我肯定不做选择,全学。

啊~~ (别打,别打,疼,我说逗的)

这个得根据你的需求,如果你接下来的项目还是那种单体服务,就上大而全的框架的比如beego,因为他库多、功能全。

如果是前后段分离的项目可以学 gin,因为他小而精。

读过源码么?

过了框架选择这关,有志之士就会开始思考,这框架咋实现的?

或者去工作时,面试官总会问,你读过源码么?

反正就是挺头大的,于是我就想着,要不我们自己也实现一个框架吧。

我们不求他能被推广使用,自己撸一个框架之后,再去面对面试官的调戏,我应该就可以投去不懈的眼神了,然后说:

咯,这是我自己撸了的一个 web 框架。

想象总是美好的,现实是骨感的。

其实自己撸一个 web 框架之后,再去使用别的框架也会更顺手一些。

于是说干就干,我就开了这个系列教程。

声明:

我们这个框架参考了很多 Gin 框架里面的思路,但是我们不会把全部功能都实现。

我们只会把关键的 路由、中间件、模板渲染 这三个核心模块完成,其他附带一些 http 基础、上下文、错误恢复这些的知识点分享,感兴趣的可以在这基础上继续扩展。

HTTP 基础

要想自己写一个 web 框架,一些基础的 http 知识是必须要知道的。

我们先用 go 的标准库来实现一个 http 服务:

func main() {
    http.HandleFunc("/", handler)
    http.ListenAndServe(":8000", nil)
}
**
func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "hello my path is %q\n", r.URL.Path)
}

然后 go run main.go 即可访问看到返回 hello my path is /

为什么我们还要用框架么?

到这里很多同学肯定就会问了,既然标准库已经能实现 http 服务了,那我们还用框架来干啥?

其实 net/http 只是提供了基础的Web功能,即监听端口,映射静态路由,解析HTTP报文。

但一些 web 开发中简单的需求并不支持,比如:

  • 动态路由:例如hello/:namehello/*这类的规则。
  • 鉴权:没有分组/统一鉴权的能力,需要在每个路由映射的handler中实现。
  • 模板:没有统一简化的HTML机制。

没有框架的话,我们就需要自己去手动实现这些。

Kun框架

这个教程我们将使用 Go 语言实现一个简单的 Web 框架,起名叫做Kun,因为我网名是 锟 ,所以就取了一个拼音,我也没去检索网上有没有这个框架。

Go语言内置了 net/http库,封装了HTTP网络编程的基础的接口,我们实现的Kun Web 框架也是基于net/http的。

再回到最初的那段代码:

main 的最后一行,是用来启动 Web 服务的,第一个参数是地址,:8000 表示在 8000 端口监听。

第二个参数则代表处理所有的HTTP请求的实例,我们传的nil 代表使用标准库中的实例处理。

第二个参数,就是我们基于net/http标准库实现Web框架的入口。

为什么这么说呢?

第二个参数的类型是什么呢?

通过查看net/http的源码可以发现,Handler是一个接口,需要实现方法 ServeHTTP ,也就是说,只要传入任何实现了 ServerHTTP 接口的实例,所有的 HTTP 请求,就都交给了该实例处理了。

于是,我们可以基于这个思路改下我们的代码:

// 这是我们的引擎结构体
type Engine struct{}

func (engine *Engine) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 switch req.URL.Path {
 case "/":
  fmt.Fprintf(w, "hello my path is %q\n", req.URL.Path)
 default:
  fmt.Fprintf(w, "404 not found: %s\n", req.URL)
 }
}

func main() {
 engine := new(Engine)
 log.Fatal(http.ListenAndServe(":8000", engine))
}

再次启动,你会发现和之前的效果一样。

现在我们完全接管了http请求过来的处理逻辑,后面的文章我们就可以在这个基础上继续叠加了。我们下一篇文章再见!

原文:https://mp.weixin.qq.com/s/JD8ZHhsJTnG6jPnULIh3gg

温馨提示:本文最后更新于2022-07-25 15:22:16,某些文章具有时效性,若有错误或已失效,请在下方留言或联系CmdEye
© 版权声明
THE END
喜欢就支持一下吧
点赞7 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容