创建项目

首先需要安装好对应的依赖环境,以及工具:

  1. go

  2. protoc
    • go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
  3. protoc-gen-go
    • go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
# 创建项目模板
kratos new helloworld cd helloworld
# 拉取项目依赖
go mod download
# 生成proto模板
kratos proto add api/helloworld/helloworld.proto
# 生成proto源码
kratos proto client api/helloworld/helloworld.proto
# 生成server模板
kratos proto server api/helloworld/helloworld.proto -t internal/service

执行命令后,会在当前目录下生成一个 service 工程,工程骨架如下,具体的工程骨架说明可以访问 layout

运行项目

# 生成所有proto源码、wire等等
go generate ./... # 编译成可执行文件
go build -o ./bin/ ./... # 运行项目
./bin/helloworld -conf ./configs

看到如下输出则证明项目启动正常

level=INFO module=app service_id=7114ad8a-b3bf-11eb-a1b9-f0189850d2cb service_name=  version=
level=INFO module=transport/grpc msg=[gRPC] server listening on: [::]:9000
level=INFO module=transport/http msg=[HTTP] server listening on: [::]:8000

测试接口

curl 'http://127.0.0.1:8000/helloworld/krtaos'

输出:
{
"message": "Hello kratos"
}

应用是如何跑起来的?

通过上面的图例,我们可以直观观察到应用的调用链,简化来说如下图流程所示

1. 注入依赖并调用 newApp() 方法

// helloword/cmd/main.go
func main() {
flag.Parse()
logger := log.NewStdLogger(os.Stdout) // 调用 go-kratos/kratos/v2/config,创建 config 实例,并指定了来源和配置解析方法
c := config.New(
config.WithSource(
file.NewSource(flagconf),
),
config.WithDecoder(func(kv *config.KeyValue, v map[string]interface{}) error {
return yaml.Unmarshal(kv.Value, v)
}),
)
if err := c.Load(); err != nil {
panic(err)
} // 将配置扫描到,通过 proto 声明的 conf struct 上
var bc conf.Bootstrap
if err := c.Scan(&bc); err != nil {
panic(err)
} // 通过 wire 将依赖注入,并调用 newApp 方法
app, cleanup, err := initApp(bc.Server, bc.Data, logger)
if err != nil {
panic(err)
}
// 省略代码...
}

2. 创建 kratos 实例

项目 main.go 的 newApp() 方法中,调用了 go-kratos/kratos/v2/app.go 中的 kratos.New() 方法

// helloword/cmd/main.go
func newApp(logger log.Logger, hs *http.Server, gs *grpc.Server) *kratos.App {
return kratos.New(
// 配置应用
kratos.Name(Name),
kratos.Version(Version),
kratos.Metadata(map[string]string{}),
kratos.Logger(logger),
// kratos.Server() 传入的 http/grpc 服务会通过 buildInstance() 转换成registry.ServiceInstance struct*
kratos.Server(
hs,
gs,
),
)
}

该方法会返回一个 App struct,包含 Run()Stop() 方法

// go-kratos/kratos/v2/app.go
type App struct {
opts options //配置
ctx context.Context // 上下文
cancel func() // context 的取消方法
instance *registry.ServiceInstance //通过 kratos.Server()声明的实例,并通过 buildInstance() 转换后的 *registry.ServiceInstance struct
log *log.Helper // 日志
} // Run executes all OnStart hooks registered with the application's Lifecycle.
func (a *App) Run() error {
// 省略代码...
} // Stop gracefully stops the application.
func (a *App) Stop() error {
// 省略代码...
}

3. 调用 Run() 方法#

项目在 main 方法中调用了 kratos.App structRun() 方法.

// helloword/cmd/main.go
// 省略代码...
// 启动 Kratos
if err := app.Run(); err != nil {
panic(err)
}

Run() 方法的实现细节

// go-kratos/kratos/v2/app.go
func (a *App) Run() error {
a.log.Infow(
"service_id", a.opts.id,
"service_name", a.opts.name,
"version", a.opts.version,
)
g, ctx := errgroup.WithContext(a.ctx)
// 遍历通过 kratos.Server() 声明的服务实例
for _, srv := range a.opts.servers {
srv := srv
// 执行两个goroutine, 用于处理服务启动和退出
g.Go(func() error {
<-ctx.Done() // 阻塞,等待调用 cancel 方法
return srv.Stop() // 协程退出后,调用实例的停止方法
})
g.Go(func() error {
return srv.Start() // 调用实例的运行方法
})
}
// 判断是否调用 kratos.Registrar() 配置了注册发现中心
if a.opts.registrar != nil {
// 将实例注册到注册中心
if err := a.opts.registrar.Register(a.opts.ctx, a.instance); err != nil
return err
}
}
// 监听进程退出信号
c := make(chan os.Signal, 1)
signal.Notify(c, a.opts.sigs...) // 处理进程退出和 context 退出
g.Go(func() error {
for {
select {
case <-ctx.Done():
return ctx.Err()
case <-c:
// 调用 kratos.App 的停止方法
a.Stop()
}
}
})
if err := g.Wait(); err != nil && !errors.Is(err, context.Canceled) {
return err
}
return nil
}

4. 应用退出

Kratos 实例在启动时,监听了系统的进程退出信号,当收到退出信号时,kratos 会调用 App structStop() 方法

// go-kratos/kratos/v2/app.go
func (a *App) Stop() error {
// 判断是否有注册中心配置
if a.opts.registrar != nil {
// 在注册中心中将实例注销
if err := a.opts.registrar.Deregister(a.opts.ctx, a.instance); err != nil {
return err
}
}
// 控制 goroutine 的退出,当调用 a.cancel()时,Run()方法中 监听的 <-ctx.Done() 收到消息后,没有阻塞后,方法会调用 server 的 Stop()方法,停止服务
if a.cancel != nil {
a.cancel()
}
return nil
}

文章转自:

通过 layout 探索 kratos 运行原理的更多相关文章

  1. Asp.net WebPages框架运行原理浅析(转)

    在Asp.net4和4.5中,新增了WebPages Framework,编写页面代码使用了新的Razor语法,代码更加的简洁和符合Web标准,编写方式更接近于PHP和以前的Asp,和使用 WebFo ...

  2. ASP.NET的运行原理与运行机制

    在Asp.net4和4.5中,新增了WebPages Framework,编写页面代码使用了新的Razor语法,代码更加的简洁和符合Web标准,编写方式更接近于PHP和以前的Asp,和使用WebFor ...

  3. ASP.NET Core 运行原理解剖[1]:Hosting

    ASP.NET Core 是新一代的 ASP.NET,第一次出现时代号为 ASP.NET vNext,后来命名为ASP.NET 5,随着它的完善与成熟,最终命名为 ASP.NET Core,表明它不是 ...

  4. ASP.NET Core 运行原理解剖[2]:Hosting补充之配置介绍

    在上一章中,我们介绍了 ASP.NET Core 的启动过程,主要是对 WebHost 源码的探索.而本文则是对上文的一个补充,更加偏向于实战,详细的介绍一下我们在实际开发中需要对 Hosting 做 ...

  5. ASP.NET Core 运行原理解剖[3]:Middleware-请求管道的构成

    在 ASP.NET 中,我们知道,它有一个面向切面的请求管道,有19个主要的事件构成,能够让我们进行灵活的扩展.通常是在 web.config 中通过注册 HttpModule 来实现对请求管道事件监 ...

  6. ASP.NET Core 运行原理解剖[4]:进入HttpContext的世界

    HttpContext是ASP.NET中的核心对象,每一个请求都会创建一个对应的HttpContext对象,我们的应用程序便是通过HttpContext对象来获取请求信息,最终生成响应,写回到Http ...

  7. ASP.NET Core 运行原理解剖[5]:Authentication

    在现代应用程序中,认证已不再是简单的将用户凭证保存在浏览器中,而要适应多种场景,如App,WebAPI,第三方登录等等.在 ASP.NET 4.x 时代的Windows认证和Forms认证已无法满足现 ...

  8. iBatis.Net的基本情况和运行原理

    转载http://www.cnblogs.com/13590/archive/2013/02/27/2934580.html 摘要:介绍iBatis.Net的基本情况和运行原理,运行环境中各参数的配置 ...

  9. Android开发学习笔记(二)——编译和运行原理(1)

    http://www.cnblogs.com/Pickuper/archive/2011/06/14/2078969.html 接着上一篇的内容,继续从全局了解Android.在清楚了Android的 ...

随机推荐

  1. jquery 阻止表单提交方法

    <form name="message_form" action="?m=mobilecenter&c=index&a=service" ...

  2. Git基本理论---重点

    Git本地有三个工作区域: 工作目录(Working Directory): 存放项目代码 暂存区(Stage/Index) : 临时存放文件的改动,保存的是文件列表信息 资源库 (Repositor ...

  3. noip模拟测试31

    终于有时间写博客了,前面一直咕咕咕都快变成一只公鸡了......这次考试,真的很意外,我在考场上觉得自己打出了T1的正解,样例一拍就过,还跑得嘎嘎快,然后T2,T3码了两个暴力,觉得自己应该能100p ...

  4. Mybatis学习笔记-ResultMap结果集映射

    解决属性名与字段名不一致的问题 新建项目 --> 测试实体类字段不一致的情况 数据库字段:id,name,pwd 实体类属性:id,name,password 输出结果 User{id=1, n ...

  5. 树莓派3B/3B+/4B 刷机装系统烧录镜像教程

    树莓派3B/3B+/4B 刷机装系统烧录镜像教程 树莓派 背景故事 刚拿到树莓派的第一件事,应该就是要装系统了,那么应该怎么操作呢?下面就给大家介绍一下吧. 硬件准备 树莓派:3B/3B+/4B,本教 ...

  6. [TensorFlow2.0]-Fashion-MNIST本地数据集及fit_generator()的使用

    本人人工智能初学者,现在在学习TensorFlow2.0,对一些学习内容做一下笔记.笔记中,有些内容理解可能较为肤浅.有偏差等,各位在阅读时如有发现问题,请评论或者邮箱(右侧边栏有邮箱地址)提醒. 若 ...

  7. java常见面试题目

    (面的初级到中级之间的,却有很多高级的问题) 总结一些常见的遇见的面试题(来自各方面收集) 1.开发中Java用了比较多的数据结构有哪些? 2.谈谈你对HashMap的理解,底层原理的基本实现,Has ...

  8. 十进制转十六进制 BASIC-10

    十进制转十六进制 import java.util.Scanner; public class 十进制转十六进制 { /* 十六进制数是在程序设计时经常要使用到的一种整数的表示方式. * 它有0,1, ...

  9. const变量通过指针修改问题

    const的变量在特定情况下可以通过指针修改,但是在另一些情况下是不能通过指针修改. 以下是VC6下才测试. 1. 不能修改的情况 #include int const a = 10; void ma ...

  10. Mantis安装过程笔记

    安装平台:Windows Server 2003 R2 Enterprise x64 Edition 软件: EasyPHP-5.3.6.1 mantisbt-1.2.6 安装过程: 首先安装Easy ...