Skip to content

四、启动流程详解 ​

本章将深入分析 Kratos 的启动流程,从 kratos.New() 到 app.Run() 的完整过程。

4.1 入口函数分析 ​

让我们从一个典型的 Kratos 入口开始:

go
// main.go
package main

import (
    "github.com/go-kratos/kratos/v2"
    "github.com/go-kratos/kratos/v2/config"
    "github.com/go-kratos/kratos/v2/config/file"
    "github.com/go-kratos/kratos/v2/log"
    "github.com/go-kratos/kratos/v2/transport/http"
)

func main() {
    // 1. 加载配置
    c := config.New(
        config.WithSource(file.NewSource("configs/config.yaml")),
    )
    defer c.Close()
    
    // 2. 创建应用
    app := kratos.New(
        kratos.Name("hello-kratos"),
        kratos.Version("v1.0.0"),
        kratos.Server(
            http.NewServer(
                http.Address(":8080"),
            ),
        ),
    )
    
    // 3. 启动应用
    app.Run()
}

4.2 kratos.New() 流程 ​

4.2.1 New() 源码 ​

go
// kratos/app.go
func New(opts ...Option) *App {
    // 1. 创建带取消的上下文
    ctx, cancel := context.WithCancel(context.Background())
    
    // 2. 创建应用实例
    app := &App{
        ctx:    ctx,
        cancel: cancel,
        opts:   newOptions(opts),
    }
    
    // 3. 构建服务
    app.build()
    
    // 4. 返回应用
    return app
}

4.2.2 流程图解 ​

kratos.New(opts...)
        │
        ▼
┌───────────────────┐
│  创建 context      │  ctx, cancel := context.WithCancel(...)
└───────────────────┘
        │
        ▼
┌───────────────────┐
│  创建 App 实例     │  app := &App{...}
└───────────────────┘
        │
        ▼
┌───────────────────┐
│  构建服务 (build)  │  app.build()
└───────────────────┘
        │
        ▼
┌───────────────────┐
│   返回 App        │  return app
└───────────────────┘

4.3 build() 内部实现 ​

4.3.1 build() 源码 ​

go
// kratos/app.go
func (app *App) build() {
    // 1. 设置日志
    if app.opts.logger != nil {
        log.SetLogger(app.opts.logger)
    }
    
    // 2. 设置信号处理
    app.handleSignal()
    
    // 3. 构建所有服务
    for _, srv := range app.opts.servers {
        app.wg.Add(1)
        go func(srv Server) {
            // 在 goroutine 中启动服务
            srv.Start()
            // 服务停止后,减少 WaitGroup 计数
            app.wg.Done()
        }(srv)
    }
}

4.3.2 build 流程图解 ​

app.build()
    │
    ├── 1. 设置日志
    │       │
    │       ▼
    │   log.SetLogger(opts.logger)
    │
    ├── 2. 设置信号处理
    │       │
    │       ▼
    │   app.handleSignal() ──→ 监听 SIGINT/SIGTERM
    │
    └── 3. 启动服务
            │
            ▼
        for srv in servers:
            │
            ▼
        ┌─────────────────────┐
        │  go srv.Start()     │  ← 并发启动
        └─────────────────────┘
                │
                ▼
        ┌─────────────────────┐
        │  app.wg.Add(1)      │  ← 计数+1
        └─────────────────────┘

4.4 app.Run() 流程 ​

4.4.1 Run() 源码 ​

go
// kratos/app.go
func (app *App) Run() error {
    // 1. 等待所有服务启动完成
    //    (WaitGroup 计数为 0 表示服务都启动了)
    app.wg.Wait()
    
    // 2. 等待退出信号
    <-app.ctx.Done()
    
    // 3. 优雅关闭
    return app.shutdown()
}

4.4.2 Run 流程图解 ​

app.Run()
        │
        ▼
┌───────────────────┐
│  app.wg.Wait()   │  ← 等待所有服务启动完成
└───────────────────┘
        │
        ▼
┌───────────────────┐
│  <-app.ctx.Done()│  ← 等待退出信号
└───────────────────┘
        │
        ▼
┌───────────────────┐
│  app.shutdown()  │  ← 优雅关闭
└───────────────────┘
        │
        ▼
    for srv in servers:
        │
        ▼
    ┌─────────────────────┐
    │  srv.GracefulStop() │  ← 优雅停止每个服务
    └─────────────────────┘

4.5 信号处理 ​

4.5.1 handleSignal() 源码 ​

go
// kratos/app.go
func (app *App) handleSignal() {
    // 创建信号通道
    app.opts.sigCh = make(chan os.Signal, 1)
    
    // 监听信号
    signal.Notify(app.opts.sigCh, syscall.SIGINT, syscall.SIGTERM)
    
    // 启动信号处理协程
    go func() {
        select {
        case sig := <-app.opts.sigCh:
            // 收到信号,取消上下文
            app.cancel()
            
            // 记录日志
            log.Infof("收到信号: %s", sig)
        case <-app.ctx.Done():
            // 上下文已取消
        }
    }()
}

4.5.2 信号处理流程 ​

┌─────────────────────────────────────────────────────────────┐
│                    信号处理流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  系统信号 (SIGINT/SIGTERM)                                  │
│         │                                                   │
│         ▼                                                   │
│  ┌─────────────────────────────────────────────┐          │
│  │  signal.Notify(ch, SIGINT, SIGTERM)         │          │
│  └─────────────────────────────────────────────┘          │
│         │                                                   │
│         ▼                                                   │
│  ┌─────────────────────────────────────────────┐          │
│  │  go func() {                                 │          │
│  │      select {                                │          │
│  │          case sig := <-ch:                  │          │
│  │              app.cancel()  // 取消上下文      │          │
│  │          case <-app.ctx.Done():             │          │
│  │              // 退出                         │          │
│  │      }                                      │          │
│  │  }()                                         │          │
│  └─────────────────────────────────────────────┘          │
│         │                                                   │
│         ▼                                                   │
│  app.ctx.Done() 触发 ──► app.Run() 中的 <-app.ctx.Done()  │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.6 优雅关闭 ​

4.6.1 shutdown() 源码 ​

go
// kratos/app.go
func (app *App) shutdown() error {
    // 1. 记录日志
    log.Info("开始关闭应用...")
    
    // 2. 停止所有服务
    for _, srv := range app.opts.servers {
        // 优雅停止
        if err := srv.GracefulStop(); err != nil {
            log.Errorf("停止服务失败: %v", err)
        }
    }
    
    // 3. 记录日志
    log.Info("应用已关闭")
    
    return nil
}

4.6.2 优雅关闭流程 ​

┌─────────────────────────────────────────────────────────────┐
│                    优雅关闭流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  1. 停止接收新请求                                           │
│     └── Server 停止监听端口                                  │
│                                                             │
│  2. 等待正在处理的请求完成                                   │
│     └── 等待所有进行中的请求处理完成                          │
│                                                             │
│  3. 关闭连接                                                │
│     └── 关闭所有客户端连接                                   │
│                                                             │
│  4. 释放资源                                                │
│     └── 关闭数据库连接、缓存连接等                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.7 完整启动流程 ​

┌─────────────────────────────────────────────────────────────┐
│                    完整启动流程                              │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  用户代码                                                    │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  kratos.New(                                       │   │
│  │      kratos.Name("hello-kratos"),                  │   │
│  │      kratos.Version("v1.0.0"),                     │   │
│  │      kratos.Server(httpServer),                   │   │
│  │  )                                                │   │
│  └─────────────────────────────────────────────────────┘   │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  app.build()                                        │   │
│  │       │                                              │   │
│  │       ├── 设置日志                                   │   │
│  │       ├── 设置信号处理                               │   │
│  │       └── 启动所有 Server (并发)                     │   │
│  │            │                                        │   │
│  │            ▼                                        │   │
│  │       HTTP Server 启动监听 :8080                    │   │
│  │       gRPC Server 启动监听 :50051                    │   │
│  └─────────────────────────────────────────────────────┘   │
│       │                                                     │
│       ▼                                                     │
│  ┌─────────────────────────────────────────────────────┐   │
│  │  app.Run()                                          │   │
│  │       │                                              │   │
│  │       ├── app.wg.Wait() 等待服务启动完成             │   │
│  │       │                                              │   │
│  │       ├── <-app.ctx.Done() 等待退出信号              │   │
│  │       │                                              │   │
│  │       └── app.shutdown() 优雅关闭                     │   │
│  │            │                                        │   │
│  │            ▼                                        │   │
│  │       停止所有 Server                                │   │
│  └─────────────────────────────────────────────────────┘   │
│                                                             │
└─────────────────────────────────────────────────────────────┘

4.8 本章小结 ​

┌─────────────────────────────────────────────────────────────┐
│                      本章总结                                │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ✓ kratos.New() 创建应用并构建服务                          │
│                                                             │
│  ✓ app.build() 并发启动所有 Server                         │
│                                                             │
│  ✓ app.Run() 等待信号并优雅关闭                            │
│                                                             │
│  ✓ 使用 WaitGroup 协调服务启动和关闭                        │
│                                                             │
│  ✓ 支持 SIGINT/SIGTERM 信号处理                           │
│                                                             │
└─────────────────────────────────────────────────────────────┘

下章预告 ​

下一章我们将学习 HTTP Server 详解,包括 Server 的创建、启动和配置。

敬请期待!