开始
var c config.Config //配置
ctx := svc.NewServiceContext(c)
server := rest.MustNewServer(c.RestConf) //生成server
handler.RegisterHandlers(server, ctx) //路由注册
server.Start() //启动http 服务
上面的代码是 go-zero 自带的 goctl 工具自动生成的代码。
初始化 Server
server := rest.MustNewServer(c.RestConf)
说明:engine 和 router 后面再介绍,先来看看整体流程
注册路由
可以看到有一个 rest.Route 的 struct:
所有的路由都通过这种方式注册,这部分由 goctl 自动生成,减轻了不少工作量。
Route 包含三个属性:
Method:http 请求方式:post/get 等;
Path:就是 url 后面的自愿路径;
Handler:是实际处理器,请求最终会根据路由而执行不同的 handler,这个是 go 官方 net/http 里面定义的,所以 go-zero 的 http 也是在 net/http 基础上实现的。
启动 http 服务:server.Start ()
Start () 的具体实现是调用了初始化时 engin 的 start,继续往下看:
// StartHttp starts a http server.
func StartHttp(host string, port int, handler http.Handler, opts ...StartOption) error {
return start(host, port, handler, func(srv *http.Server) error {
return srv.ListenAndServe()
}, opts...)
}
//具体的启动方法
func start(host string, port int, handler http.Handler, run func(srv *http.Server) error,
opts ...StartOption) (err error) {
//net/http 包的Server
server := &http.Server{
Addr: fmt.Sprintf("%s:%d", host, port),
Handler: handler,//go-zero 的路由系统,
}
for _, opt := range opts {
opt(server)
}
waitForCalled := proc.AddWrapUpListener(func() {
if e := server.Shutdown(context.Background()); err != nil {
logx.Error(e)
}
})
defer func() {
if err == http.ErrServerClosed {
waitForCalled()
}
}()
return run(server) //实质是执行:http.Server.ListenAndServe()
}
作为对比,我们看看原始 net/http 开启 http:
http.HandleFunc("/api/v1/user", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(501)
w.Write([]byte("error msg."))
})
http.ListenAndServe(":8081", nil)
//http.ListenAndServe本质上也是用http.Server.ListenAndServe()
func ListenAndServe(addr string, handler Handler) error {
server := &Server{Addr: addr, Handler: handler}
return server.ListenAndServe()
}
到这里,也就完成了 go-zero http 服务启动,最终是由 net/http 去完成启动,go-zero 加入了自己的路由系统代替默认的路由,因此需要的时候我们也可以参考实现自己的路由系统,从而撸出一个自己的 go web 框架。
go-zero 路由系统
回到上面的 NewServer,注意到有:
server := &Server{
ngin: newEngine(c),
router: router.NewRouter(),
}
进 router.NewRouter 看看
// NewRouter returns a httpx.Router.
func NewRouter() httpx.Router {
return &patRouter{
trees: make(map[string]*search.Tree),
}
}
// Router interface represents a http router that handles http requests.
type Router interface {
http.Handler
Handle(method, path string, handler http.Handler) error
SetNotFoundHandler(handler http.Handler)
SetNotAllowedHandler(handler http.Handler)
}
type patRouter struct {
trees map[string]*search.Tree //key是http method,路由就放入search.Tree里面
notFound http.Handler
notAllowed http.Handler
}
可以看到最终的路由是有一个 patRouter 的结构体,实现了 Router 接口的相关方法,而所有的路由都放在一个按不同 http method 区分的 search.Tree 里面
这个 search.Tree 是变种前缀树:
当有新的 http 请求进来后就会通过这树找到对应的 Handler,然后进行处理。