开始之前
在阅读这两个包之前,我们应该先了解一些http相关的基本概念。
HTTP 是一种通信协议,用于在 Web 浏览器(客户端)和 Web 服务器(服务器端)之间传输数据。
在实际开发过程中,您可能会使用某种编程语言(如 Go)和相应的 Web 框架(如 go-zero、)来实现 HTTP 服务器、路由和处理程序等功能。这些框架为您提供了便捷的工具和抽象,使得构建 Web 应用程序变得更加简单。
一些基本概念:
- server:服务器是一个软件程序或硬件设备,负责接收和处理客户端发送的请求,并将响应发送回客户端。HTTP 服务器的主要任务是监听和处理 HTTP 请求,然后生成和发送 HTTP 响应。
route:路由是一个映射,将特定的 HTTP 请求(由请求方法和请求路径组成)映射到特定的处理函数。在 Web 应用程序中,路由定义了如何处理不同类型的客户端请求。
Route(路由):路由是一个映射关系,它将一个 HTTP 请求的方法(如 GET、POST 等)和路径(如 /users、/posts 等)映射到特定的处理程序(handler)。当一个 HTTP 请求到达服务器时,服务器根据请求的方法和路径找到对应的路由,然后执行关联的处理程序来生成 HTTP 响应。在实际开发中,您通常需要为应用程序的每个功能或端点定义一个或多个路由。 Router(路由器):路由器是一个组件,它负责管理和维护应用程序的所有路由。路由器的主要职责是接收 HTTP 请求,查找与请求匹配的路由,然后调用关联的处理程序。此外,路由器还可能提供诸如 URL 参数解析、中间件支持、自定义错误处理等附加功能。在使用 Web 框架时,路由器通常是框架提供的核心组件之一。
- Handler:处理程序是一个函数,负责处理特定类型的 HTTP 请求。当路由器接收到与处理程序关联的请求时,它会调用处理程序。处理程序负责解析请求、执行必要的操作(如数据库查询)并生成适当的 HTTP 响应。
- Bind:绑定是将路由与处理程序关联起来的过程。在绑定路由之后,当服务器收到与路由匹配的 HTTP 请求时,它会自动调用关联的处理程序。通常,这一过程发生在服务器初始化时。一旦完成绑定,服务器就可以根据请求方法和路径找到相应的处理程序并执行。
- Middleware:中间件是一种特殊类型的处理程序,它可以在请求到达最终处理程序之前或在最终处理程序生成响应之后执行额外操作。中间件可用于实现诸如身份验证、授权、日志记录、错误处理等功能。
开始
让我们继续从主函数出发
func main() {
flag.Parse()
var c config.Config
conf.MustLoad(*configFile, &c)
ctx := svc.NewServiceContext(c)
server := rest.MustNewServer(c.RestConf)
defer server.Stop()
handler.RegisterHandlers(server, ctx)
fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)
server.Start()
}
其中的
server := rest.MustNewServer(c.RestConf)
便是server.go的对外接口
func MustNewServer(c RestConf, opts ...RunOption) *Server {
server, err := NewServer(c, opts...)
if err != nil {
log.Fatal(err)
}
return server
}
MustNewServer 函数用于创建一个新的 Server 实例,它接收以下两个参数:
- c RestConf:这是一个 RestConf 类型的参数,它包含了服务器的配置信息,例如主机名、端口、中间件设置、超时等。在创建一个新的服务器实例时,这些配置信息将用于初始化服务器的各种属性。
- opts ...RunOption:这是一个可变参数,表示可以接受任意数量的 RunOption 类型参数。RunOption 是一个函数类型,它接受一个指向 Server 类型的指针作为参数。在创建服务器实例之后,这些 RunOption 函数可以用于自定义服务器的行为。您可以传递任意数量的 RunOption 函数,它们将按照传入的顺序依次执行。
当您调用 MustNewServer 函数时,它将尝试使用提供的配置信息和选项创建一个新的 Server 实例。如果创建过程中出现错误,函数将记录错误并终止程序。如果成功创建服务器实例,函数将返回这个实例。
这里提及了RunOption 类型参数,在这之前我们需要先翻到server.go的开头,找到类型定义
type (
// RunOption defines the method to customize a Server.
RunOption func(*Server)
// A Server is a http server.
Server struct {
ngin *engine
router httpx.Router
}
)
runnoption定义了服务器的自定义方法。它接受一个指向 Server 类型的指针作为参数。您可以在这个函数中执行任何自定义操作,以便根据需要调整或修改服务器实例的行为。
举个例子,假设您希望在服务器启动时打印一条日志消息,您可以定义如下的 opt1 函数:
config := RestConf{...} // 用实际配置替换省略号
opt1 := func(s *Server) {
log.Println("Server is starting...")
}
opt2 := func(s *Server) {
// 在这里执行其他自定义操作
}
server := MustNewServer(config, opt1, opt2)
当您将这个函数作为参数传递给 MustNewServer 函数时,它将在服务器实例创建后执行,输出相应的日志消息。
然后,很显然的,这个函数只是一个接口,在调用之后,它直接将参数传递给了NewServer函数,它只是实现了报错功能,所以主要功能的实现我们还是得看NewServer函数。
func NewServer(c RestConf, opts ...RunOption) (*Server, error) {
if err := c.SetUp(); err != nil {
return nil, err
}
server := &Server{
ngin: newEngine(c),
router: router.NewRouter(),
}
opts = append([]RunOption{WithNotFoundHandler(nil)}, opts...)
for _, opt := range opts {
opt(server)
}
return server, nil
}
主要的实现在于
- 调用 c.SetUp() 来设置并验证配置对象。server的配置写在另一个go文件,serviceconf.go
package service
import (
"log"
"github.com/zeromicro/go-zero/core/load"
"github.com/zeromicro/go-zero/core/logx"
"github.com/zeromicro/go-zero/core/proc"
"github.com/zeromicro/go-zero/core/prometheus"
"github.com/zeromicro/go-zero/core/stat"
"github.com/zeromicro/go-zero/core/trace"
"github.com/zeromicro/go-zero/internal/devserver"
)
const (
// DevMode means development mode.
DevMode = "dev"
// TestMode means test mode.
TestMode = "test"
// RtMode means regression test mode.
RtMode = "rt"
// PreMode means pre-release mode.
PreMode = "pre"
// ProMode means production mode.
ProMode = "pro"
)
// A ServiceConf is a service config.
type ServiceConf struct {
Name string
Log logx.LogConf
Mode string `json:",default=pro,options=dev|test|rt|pre|pro"`
MetricsUrl string `json:",optional"`
// Deprecated: please use DevServer
Prometheus prometheus.Config `json:",optional"`
Telemetry trace.Config `json:",optional"`
DevServer devserver.Config `json:",optional"`
}
// MustSetUp sets up the service, exits on error.
func (sc ServiceConf) MustSetUp() {
if err := sc.SetUp(); err != nil {
log.Fatal(err)
}
}
// SetUp sets up the service.
func (sc ServiceConf) SetUp() error {
if len(sc.Log.ServiceName) == 0 {
sc.Log.ServiceName = sc.Name
}
if err := logx.SetUp(sc.Log); err != nil {
return err
}
sc.initMode()
prometheus.StartAgent(sc.Prometheus)
if len(sc.Telemetry.Name) == 0 {
sc.Telemetry.Name = sc.Name
}
trace.StartAgent(sc.Telemetry)
proc.AddShutdownListener(func() {
trace.StopAgent()
})
if len(sc.MetricsUrl) > 0 {
stat.SetReportWriter(stat.NewRemoteWriter(sc.MetricsUrl))
}
devserver.StartAgent(sc.DevServer)
return nil
}
func (sc ServiceConf) initMode() {
switch sc.Mode {
case DevMode, TestMode, RtMode, PreMode:
load.Disable()
stat.SetReporter(nil)
}
}
- 创建一个新的 Server 实例,其中包含一个新的 engine 实例(用于处理请求)和一个新的 router 实例(用于处理路由)。分别涉及engine.go和route包,在此不过多深究
- 接下来,将 WithNotFoundHandler(nil) 函数添加到 opts 函数切片的开头。这个函数用于设置一个默认的 404 Not Found 处理程序。当然,您也可以通过传入其他 RunOption 函数来覆盖这个默认值。
- 接着,遍历 opts 切片中的所有 RunOption 函数,并使用当前服务器实例作为参数执行它们。这允许您在创建服务器实例时应用任何自定义行为。
现在我们已经能得到了一个Server,对于Server,server.go 文件中还提供以下函数:
与服务器启动和运行相关的函数:
func (s *Server) Start() error: 启动服务器并开始监听请求。
func (s *Server) Stop(): 停止服务器并释放资源。注册路由和中间件的函数:
func (s *Server) AddRoutes(routes RouteCollection): 添加一组路由(RouteCollection)到服务器实例。 func (s *Server) AddMiddlewares(middlewares ...Middleware): 添加一组中间件到服务器实例。 func (s *Server) AddMiddleware(middleware Middleware): 添加一个中间件到服务器实例。 func (s *Server) SetUnauthorizedCallback(callback handler.UnauthorizedCallback): 设置 JWT 授权失败时的回调函数。 func (s *Server) SetUnsignedCallback(callback handler.UnsignedCallback): 设置签名验证失败时的回调函数。
RunOption 相关的函数,用于自定义服务器行为:
func WithNotFoundHandler(handler http.Handler) RunOption: 设置用于处理 404 Not Found 请求的处理器。 func WithJwt(jwtConf config.JwtConf) RunOption: 为服务器实例添加 JWT 验证功能。 func WithSignature(signatureConf config.SignatureConf) RunOption: 为服务器实例添加签名验证功能。 func WithTlsConfig(cfg *tls.Config) RunOption: 为服务器实例设置 TLS 配置。
由于这些函数大多是调用Server结构体中的engine和route的方法实现的,在此先不进行阅读,在之后的源码阅读中再详细学习其功能的实现
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。