kratos 学习笔记(三)

框架组件

1. API 定义

API 与用户的通信协议,通常是 REST API 和 RPC API 作为传输层协议,而 Kratos 主要参考 Google API 指南,实现了对应通信协议支持,并且遵守了 gRPC API 使用 HTTP 映射功能进行 JSON/HTTP 的支持。

也就是通过定义 proto 即可使用 REST API 和 RPC API,通过类似 Google API 的仓库方式进行 API Schema 的管理。

2. 定义接口

通过 Protobuf IDL 定义对应的 REST API 和 gRPC API:

api/helloworld/v1/greeter.proto

syntax="proto3";package helloworld.v1;import"google/api/annotations.proto";option go_package ="github.com/go-kratos/service-layout/api/helloworld/v1;v1";
option java_multiple_files =true;
option java_package ="dev.kratos.api.helloworld.v1";
option java_outer_classname ="HelloWorldProtoV1";// The greeting service definition.
serviceGreeter{
// Sends a greeting
rpcSayHello(HelloRequest)returns(HelloReply){
option(google.api.http)={
// 定义一个 GET 接口,并且把 name 映射到 HelloRequestget:"/helloworld/{name}",
// 可以添加附加接口additional_bindings {
// 定义一个 POST 接口,并且把 body 映射到 HelloRequestpost:"/v1/greeter/say_hello",body:"*",
}
};
}
}// The request message containing the user's name.
messageHelloRequest{
string name =1;
}// The response message containing the greetings
messageHelloReply{
string message =1;

3.生成接口

# 生成 proto 模板
kratos proto add api/helloworld/v1/greeter.proto
# 生成 client 源码
kratos proto client api/helloworld/v1/greeter.proto
# 生成 server 源码
kratos proto server api/helloworld/v1/greeter.proto -t internal/service
client:
|____api
| |____helloworld
| | |____v1
| | | |____greeter.pb.go
| | | |____greeter.proto
| | | |____greeter_http.pb.go
| | | |____greeter_grpc.pb.goserver:
| |____service
| | |____greeter.go

4.注册接口

HTTP API 是通过 protoc-gen-go-http 插件进行生成 http.Handler,然后可以注册到 HTTP Server 中:

import"github.com/go-kratos/kratos/v2/transport/http"greeter :=&GreeterService{}
srv := http.NewServer(http.Address(":8000"))
srv.HandlePrefix("/", v1.NewGreeterHandler(greeter))

gRPC API 是通过 protoc-gen-go-grpc 插件进行生成 gRPC Register,然后可以注册到 GRPC Server 中;

import"github.com/go-kratos/kratos/v2/transport/grpc"greeter :=&GreeterService{}
srv := grpc.NewServer(grpc.Address(":9000"))
v1.RegisterGreeterServer(srv, greeter)

5.References

  • https://cloud.google.com/apis/design
  • https://cloud.google.com/endpoints/docs/grpc/transcoding
  • https://github.com/googleapis/googleapis
  • https://go-kratos.dev/docs/guide/api-protobuf/
  • https://developers.google.com/protocol-buffers/docs/style
  • https://developers.google.com/protocol-buffers/docs/proto3
  • https://colobu.com/2017/03/16/Protobuf3-language-guide/

6. 配置

微服务或者说云原生应用的配置最佳实践是将配置文件和应用代码分开管理——不将配置文件放入代码仓库,也不打包进容器镜像,而是在服务运行时,把配置文件挂载进去或者直接从配置中心加载。Kratos的config组件就是用来帮助应用从各种配置源加载配置。

设计理念

1.支持多种配置源

Kratos定义了标准化的Source和Watcher接口来适配各种配置源。

框架内置了本地文件file和环境变量env的实现。

另外,在contrib/config下面,我们也提供了如下的配置中心的适配供使用:

  • apollo
  • consul
  • etcd
  • kubernetes
  • nacos

如果上述的配置加载方式无法涵盖您的环境,您也可以通过实现接口来适配您自己的配置加载方式。

2.支持多种配置格式

配置组件复用了 encoding中的反序列化逻辑作为配置解析使用。默认支持以下格式的解析:

  • json
  • proto
  • xml
  • yaml

框架将根据配置文件类型匹配对应的Codec,进行配置文件的解析。您也可以通过实现Codec并用 encoding.RegisterCodec方法,将它注册进去,来解析其它格式的配置文件。

配置文件类型的提取,根据配置源具体实现不同而略有区别,内置的file是把文件后缀作为文件类型的,其它配置源插件的具体逻辑请参考对应的文档。

3.热更新

Kratos的config组件支持配置的热更新,您可以使用配置中心配合config的热更新功能,在服务不重新发布/不停机/不重启的情况下,在线更新服务的配置,修改服务的一些行为。

4.配置合并

在config组件中,所有的配置源中的配置(文件)将被逐个读出,分别解析成map,并合并到一个map中去。因此在加载完毕后,不需要再理会配置的文件名,不用文件名来进行查找,而是用内容中的结构来对配置的值进行索引即可。设计和编写配置文件时,请注意 各个配置文件中,根层级的key不要重复,否则可能会被覆盖

举例:

有如下两个配置文件:

# 文件1
foo:
baz:"2"
biu:"example"
hello:
a: b
# 文件2
foo:
bar:3
baz: aaaa
hey:
good: bad
qux: quux

.Load后,将被合并为如下的结构:

{"foo":{"baz":"aaaa",
"bar":3,
"biu":"example"
},
"hey":{"good":"bad",
"qux":"quux"
},
"hello":{"a":"b"
}
}

我们可以发现,配置文件的各层级将分别合并,在key冲突时会发生覆盖,而具体的覆盖顺序,会由配置源实现中的读取顺序决定,因此这里重新提醒一下, 各个配置文件中,根层级的key不要重复,也不要依赖这个覆盖的特性 ,从根本上避免不同配置文件的内容互相覆盖造成问题。

在使用时,可以用 .Value("foo.bar")直接获取某个字段的值,也可以用 .Scan方法来将整个map读进某个结构体中,具体使用方式请看下文。

5. 使用

初始化配置源

使用file,即从本地文件加载: 这里的path就是配置文件的路径,这里也可以填写一个目录名,这样会将整个目录中的所有文件进行解析加载,合并到同一个map中。

import(
"github.com/go-kratos/kratos/v2/config"
"github.com/go-kratos/kratos/v2/config/file"
)path :="configs/config.yaml"
c := config.New(config.WithSource(file.NewSource(path),
)
)

如果想用外部的配置中心,可以在contrib/config里面找一个,以consul为例:

import(
"github.com/go-kratos/kratos/contrib/config/consul/v2"
"github.com/hashicorp/consul/api"
)consulClient, err := api.NewClient(&api.Config{Address:"127.0.0.1:8500",
})
if err !=nil{panic(err)
}
cs, err := consul.New(consulClient, consul.WithPath("app/cart/configs/"))
if err !=nil{panic(err)
}
c := config.New(config.WithSource(cs))

不同的配置源插件使用方式略有差别,您可以参考它们各自的文档或examples。

6.读取配置

首先要定义一个结构体用来解析字段,如果您使用的是kratos-layout创建的项目,可以参考后面讲解kratos-layout的部分,使用proto文件定义配置和生成struct。

我们这里演示的是手工定义结构,您需要在结构体上用json tag来定义您配置文件的字段。

var v struct{Service struct{Name    string`json:"name"`Version string`json:"version"`
}`json:"service"`
}

使用之前创建好的config实例,调用 .Scan方法,读取配置文件的内容到结构体中,这种方式适用于完整获取整个配置文件的内容。

// Unmarshal the config to struct
if err := c.Scan(&v); err !=nil{panic(err)
}
fmt.Printf("config: %+v", v)

使用config实例的 .Value方法,可以单独获取某个字段的内容。

name, err := c.Value("service.name").String()
if err !=nil{panic(err)
}
fmt.Printf("service: %s", name)

7.监听配置变更

通过 .Watch方法,可以监听配置中某个字段的变更,在本地或远端的配置中心有配置文件变更时,执行回调函数进行自定义的处理

if err := c.Watch("service.name",func(key string, value config.Value){fmt.Printf("config changed: %s = %v\n", key, value)
// 在这里写回调的逻辑
}); err !=nil{log.Error(err)
}

8.读取环境变量

如果有配置需要从环境变量读取,请使用以下方式:

配置环境变量配置源env:

c := config.New(config.WithSource(
// 添加前缀为 KRATOS_ 的环境变量,不需要的话也可以设为空字符串env.NewSource("KRATOS_"),
// 添加配置文件file.NewSource(path),
))// 加载配置源:
if err := c.Load(); err !=nil{log.Fatal(err)
}// 获取环境变量 KRATOS_PORT 的值,这里用去掉前缀的名称进行读取
port, err := c.Value("PORT").String()

除了上面使用Value方法直接读的方式,也可以在配置文件内容里使用占位符来把环境变量中的值渲染进去:

service:name:"kratos_app"
http:server:# 使用 service.name 的值name:"${service.name}"# 使用环境变量 PORT 替换,若不存在,使用默认值 8080port:"${PORT:8080}"# 使用环境变量 TIMEOUT 替换,无默认值timeout:"$TIMEOUT"

8.配置解析Decoder

Decoder用于将配置文件内容用特定的反序列化方法解析出来,默认decoder会根据文件的类型自动识别类型并解析,通常情况不需要自定义这个,您可以通过后文的实现Codec的方式来注册更多文件类型。

在初始化config时加入 WithDecoder参数,可以将Decoder覆盖为自定义的逻辑。如下代码展示了配置自定义Decoder的方法,这里使用了yaml库解析所有配置文件,您可以使用这种方式来使用特定的配置文件解析方法,但更推荐使用后文的实现Codec的方式,能同时支持多种格式的解析。

import"gopkg.in/yaml.v2"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)
}),
)

9.配置处理Resolver

Resolver用于对解析完毕后的map结构进行再次处理,默认resolver会对配置中的占位符进行填充。您可以通过在初始化config时加入 WithResolver参数,来覆盖resolver的行为。

c := config.New(config.WithSource(file.NewSource(flagconf),
),config.WithResolver(func(input map[string]interface{})(err error){// 在这里对input进行处理即可
// 您可能需要定义一个递归的函数,来处理嵌套的map结构
return
}),
)

10.支持其它格式的配置文件

首先实现Codec,这里以yaml为例

import(
"github.com/go-kratos/kratos/v2/encoding"
"gopkg.in/yaml.v3"
)const Name ="myyaml"funcinit(){encoding.RegisterCodec(codec{})
}// codec is a Codec implementation with yaml.
type codec struct{}func(codec)Marshal(v interface{})([]byte,error){return yaml.Marshal(v)
}func(codec)Unmarshal(data []byte, v interface{})error{return yaml.Unmarshal(data, v)
}func(codec)Name()string{return Name
}

然后注册该Codec 这里由于我们把注册代码 encoding.RegisterCodec(codec{})写在了包的 init方法中,所以在包被import的时候,将会运行这个 init方法,也就是进行注册。所以您可以在代码入口(比如 main.go)对它进行注册

import _"path/to/your/codec"

随后,config组件就能把上面代码中 const Name = "myyaml"这部分作为格式类型名,调用该Codec解析这个文件。

11. kratos-layout

理念

1.项目结构

layout中涉及到配置文件有以下部分,简单介绍一下它们的作用

  • cmd/server/main.go 这个是服务的入口,我们默认使用了内置的config/file组件从本地文件系统读取配置文件,默认会读取相对路径 configs目录,您可以修改这个文件里 config.New()参数中使用的配置源,从其它配置源(比如配置中心)进行加载配置。配置在这里将被加载到 conf.Bootstrap结构体中,这个结构体的内容可以通过依赖注入,注入到服务内部的其它层,比如server或data,这样各层就能读取到各自需要的配置,完成自己的初始化。
  • configs/config.yaml 这是一个示例配置文件,configs目录的内容通常不参与服务的生产环境运行,您可以用它来进行本地开发时的配置文件的加载,方便应用能本地能跑起来调试,不要将生产环境的配置放在这里。
  • internal/conf 在这里放配置文件的结构定义,我们在这里使用 .proto文件来进行配置定义,然后通过在根目录执行 make config,就可以将对应 .pb.go文件生成到相同目录下供使用。在初始状态下,这个 conf.proto所定义的结构,就是 configs/config.yaml的结构,请保持两者一致。
  • make config Makefile中的这个指令,用于生成 .proto定义的配置对应的 .pb.go文件(就是调了一下protoc),要记得每次修改定义后,一定要执行这个指令来重新生成go文件

2.配置生成命令

我们已经把根据proto生成结构体的指令预置在Makefile里面了,通过在项目根目录下执行 make config即可生成。它实际上是调用了 protoc工具,扫描internal目录下的proto文件进行生成。

3.使用Protobuf定义配置

正如前文所说,我们可以在代码中直接用struct来定义配置结构进行解析。但您可能会发现,我们的最佳实践项目模板kratos-layout中采用了Protobuf来定义配置文件的结构。通过Protobuf定义,我们可以同时支持多种格式如 jsonxml或者 yaml等多种配置格式统一解析,这样在读配置时会变得非常方便。

layout中使用了如下的 .proto文件定义配置文件的字段:

syntax="proto3";
package kratos.api;option go_package ="github.com/go-kratos/kratos-layout/internal/conf;conf";import"google/protobuf/duration.proto";messageBootstrap{
Server server =1;
}messageServer{
messageHTTP{
string network =1;
string addr =2;
google.protobuf.Duration timeout =3;
}
messageGRPC{
string network =1;
string addr =2;
google.protobuf.Duration timeout =3;
}
HTTP http =1;
GRPC grpc =2;
}

我们可以看出,Protobuf的定义结构清晰,并且可以指定字段的类型,这在后续的配置文件解析中可以起到校验的作用,保证加载配置文件的有效性。

在定义好结构后,我们需要用 protoc工具来生成对应的 .pb.go代码,也就是相应的Go struct和序列化反序列化代码,供我们使用。

使用

1.定义

修改 internal/conf/config.proto文件的内容,在这里使用Protobuf IDL定义你配置文件的结构。您也可以在这个目录下创建新的proto文件来定义额外的配置格式。

2.生成

在项目根目录执行下面的命令即可生成用来解析配置文件的结构体:

make config

执行成功后,您应该能看到 config.pb.go生成在 config.proto文件的旁边,您就可以使用里面的结构体,比如 Bootstrap来读取您的配置。

3.使用

读取配置项、监听配置变更和其它高级用法等使用方面的内容,与前文介绍的一致,这里就不再赘述。

12.扩展阅读

  • config example 样例代码

go kratos 学习笔记(三)相关推荐

  1. J2EE学习笔记三:EJB基础概念和知识 收藏

    J2EE学习笔记三:EJB基础概念和知识 收藏 EJB正是J2EE的旗舰技术,因此俺直接跳到这一章来了,前面的几章都是讲Servlet和JSP以及JDBC的,俺都懂一些.那么EJB和通常我们所说的Ja ...

  2. tensorflow学习笔记(三十二):conv2d_transpose (解卷积)

    tensorflow学习笔记(三十二):conv2d_transpose ("解卷积") deconv解卷积,实际是叫做conv_transpose, conv_transpose ...

  3. Ethernet/IP 学习笔记三

    Ethernet/IP 学习笔记三 原文为硕士论文: 工业以太网Ethernet/IP扫描器的研发 知网网址: http://kns.cnki.net/KCMS/detail/detail.aspx? ...

  4. iView学习笔记(三):表格搜索,过滤及隐藏列操作

    iView学习笔记(三):表格搜索,过滤及隐藏某列操作 1.后端准备工作 环境说明 python版本:3.6.6 Django版本:1.11.8 数据库:MariaDB 5.5.60 新建Django ...

  5. 吴恩达《机器学习》学习笔记三——多变量线性回归

    吴恩达<机器学习>学习笔记三--多变量线性回归 一. 多元线性回归问题介绍 1.一些定义 2.假设函数 二. 多元梯度下降法 1. 梯度下降法实用技巧:特征缩放 2. 梯度下降法的学习率 ...

  6. Python基础学习笔记三

    Python基础学习笔记三 print和import print可以用,分割变量来输出 import copy import copy as co from copy import deepcopy ...

  7. Mr.J-- jQuery学习笔记(三十二)--jQuery属性操作源码封装

    扫码看专栏 jQuery的优点 jquery是JavaScript库,能够极大地简化JavaScript编程,能够更方便的处理DOM操作和进行Ajax交互 1.轻量级 JQuery非常轻巧 2.强大的 ...

  8. MYSQL学习笔记三:日期和时间函数

    MYSQL学习笔记三:日期和时间函数 1. 获取当前日期的函数和获取当前时间的函数 /*获取当前日期的函数和获取当前时间的函数.将日期以'YYYY-MM-DD'或者'YYYYMMDD'格式返回 */ ...

  9. ROS学习笔记三:创建ROS软件包

    ,# ROS学习笔记三:创建ROS软件包 catkin软件包的组成 一个软件包必须满足如下条件才能被称之为catkin软件包: 这个软件包必须包含一个catkin编译文件package.xml(man ...

最新文章

  1. PC市场连续5年滑坡,但对惠普戴尔来说是好消息
  2. 关于Android真机调測Profiler
  3. mysql表空间大小_浅谈mysql中各种表空间(tablespaces)的概念
  4. 你怎么理解程序设计和c语言,【答题】C语言程序设计问题与解释实验
  5. [UVALive 3661] Animal Run
  6. 录入班级学生姓名科目成绩_如何利用钉钉发布考试成绩
  7. C++ primer plus学习笔记 (3) _函数
  8. 关于日期 显示当前日期
  9. win10 清理回收站右键
  10. pandas获取全部列名_pandas获取全部列名_pandas DataFrame数据重命名列名的几种方式...
  11. win10系统下摄像头无法打开的解决方法
  12. word转pdf excel转pdf ppt转pdf pdf转word pdf转excel pdf转ppt pdf转jpg
  13. 关于常用第三方统计平台比较
  14. 气相色XD谱灵敏度解析及载气不纯解决方案
  15. 用python实现字符串数据的标签数字化
  16. 逆向-360逆向writeup
  17. 【详细整理机房布线工艺,布出更快更漂亮的网线!】
  18. excel----身份证号校验位excel公式分析
  19. ADI Blackfin DSP处理器-BF533的开发详解4:中断的使用方法(含源代码)
  20. XP安装SQLSERVER企业版

热门文章

  1. 补天平台安全问题收录情况分享记录
  2. Grasshopper 学习笔记 2 Geometry 几何体(基本几何体、相关操作、自由曲面)
  3. 发现一个免费邮件代发平台,sendpost.cn
  4. CentOS安装MySQL(YUM源安装)
  5. SAP MRP 消耗的策略及逻辑
  6. HIH8121(HIH8000系列)温湿度传感器驱动代码-基于STM32 HAL库
  7. 【转】KMP算法详解
  8. 计算机元机键盘插口,键盘细节以及接口设计_msi微星 GE62 2QE-052CN_笔记本评测-中关村在线...
  9. 【复杂网络社团发现】GN算法边介数详解
  10. 字节投资首位情感连接的数字人?数字人构建方法和元宇宙身份系统