一个简单的 web 服务 demo
首先是一个基于 go 的标准库 net/http 的 web 服务的 demo:
1 | |
如何注册 handler
上面的 demo 中用了三种看似不同的方式注册了三个 handler,但实际上用的都是 net/http 包中的两个函数:
1 | |
其中,pattern 是请求的路径,也就是 URL,而第二个参数略有不同,从函数的名字可以看出,一个是实现了 Handler 接口的类型,而另一个则是一个函数类型的参数,其函数签名是 func(ResponseWriter, *Request)。事实上,Handler 接口是这样子的:
1 | |
而第三种中的 http.HandlerFunc() 实际上就是一个适配器:
1 | |
所以三种方法本质都是一样的,都会交给 DefaultServeMux 来处理,而 DefaultServeMux 是个 ServerMux 类型,以 key-value 的形式保存了所有的 url-handler 映射
1 | |
来看一下这个映射是怎么保存(注册)的:
1 | |
OK,到现在为止,handler 的注册过程基本捋通了,还算简单。那么问题来了,http.ListenAndServe(":8090", nil) 开启 web 服务后,是怎么和上面注册的 handler 关联上的呢??
请求是怎么到 handler 上的
如果我们从 http.ListenAndServe 一路点进去的话,就会发现。。。迷失了,这里稍稍过一下:
1 | |
1 | |
这个方法有点长,先只看最关键的地方:
1 | |
可以看到经典的几个步骤:accept ➡️ 建立新连接 ➡️ 开启一个 gorotine 来处理
这个 c.server() 方法也很长,找到这一行:
1 | |
1 | |
好了,到这里,算是关联上了,那么是怎么执行呢?继续往下:
1 | |
当然,这个里头还是有一定的道道的,这边就不展开了。
从上面的简单分析可以看出,这个 go 自带的默认路由器是真的很简单的,无法满足高级一点的一些需求,比如:
- 按请求方法分类进行路由(GET、POST、PUT、DELETE等);
- 不支持参数设定,例如
/user/:uid这种泛类型匹配
所以就需要自定义路由。
自定义路由
鉴于上面所说的默认路由器的缺点,这边实现一个简单的自定义路由器,来提供根据请求方法进行路由的功能
1 | |
跑起来试试:
1 | |
以上就完成了一个简易的自定义路由。
END