使用kratos框架从0搭建微服务
使用Kratos从0搭建微服务
1、初始化项目
环境准备
安装相关环境以及工具:
kratos项目初始化
初始化项目
//创建项目
kratos new <Your project name>
//拉取项目依赖
go mod download
//运行项目
kratos run
//输出
INFO msg=config loaded: config.yaml format: yaml # 默认载入 configs/config.yaml 配置文件
INFO msg=[gRPC] server listening on: [::]:9000 # gRPC服务监听 9000 端口
INFO msg=[HTTP] server listening on: [::]:8000 # HTTP服务监听 8000 端口
//测试
curl 'http://127.0.0.1:8000/helloworld/kratos'
//输出
{
"message": "Hello kratos"
}
2、常用组件与中间件接入
日志(zap)
Installation
go get -u go.uber.org/zap
在kratos中引入zap日志库
// internal/pkg/zap
package zap
import (
"fmt"
"os"
"go.uber.org/zap/zapcore"
"github.com/go-kratos/kratos/v2/log"
"github.com/natefinch/lumberjack"
"go.uber.org/zap"
)
var _ log.Logger = (*ZapLogger)(nil)
type ZapLogger struct {
log *zap.Logger
Sync func() error
}
// Log 日志记录器接口实现
func (z *ZapLogger) Log(level log.Level, keyvals ...interface{}) error {
if len(keyvals) == 0 || len(keyvals)%2 != 0 {
z.log.Warn(fmt.Sprint("Key Values must appear in pairs: ", keyvals))
return nil
}
var data []zap.Field
for i := 0; i < len(keyvals); i += 2 {
data = append(data, zap.Any(fmt.Sprint(keyvals[i]), keyvals[i+1]))
}
switch level {
case log.LevelDebug:
z.log.Debug("", data...)
case log.LevelInfo:
z.log.Info("", data...)
case log.LevelWarn:
z.log.Warn("", data...)
case log.LevelError:
z.log.Error("", data...)
case log.LevelFatal:
z.log.Fatal("", data...)
}
return nil
}
// Logger 配置zap日志,将zap日志库引入
func Logger() log.Logger {
encoder := zapcore.EncoderConfig{
TimeKey: "time",
LevelKey: "level",
NameKey: "zap",
CallerKey: "caller",
MessageKey: "msg",
StacktraceKey: "stack",
EncodeTime: zapcore.ISO8601TimeEncoder,
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeDuration: zapcore.SecondsDurationEncoder,
EncodeCaller: zapcore.FullCallerEncoder,
}
return NewZapLogger(
encoder,
zap.NewAtomicLevelAt(zapcore.DebugLevel),
zap.AddStacktrace(
zap.NewAtomicLevelAt(zapcore.ErrorLevel)),
zap.AddCaller(),
zap.AddCallerSkip(2),
zap.Development(),
)
}
// NewZapLogger return a zap zap.
func NewZapLogger(encoder zapcore.EncoderConfig, level zap.AtomicLevel, opts ...zap.Option) *ZapLogger {
//设置日志级别
level.SetLevel(zap.InfoLevel)
var core zapcore.Core
core = zapcore.NewCore(
zapcore.NewConsoleEncoder(encoder), // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout)), // 打印到控制台
level, // 日志级别
)
//可选打印到控制台并输出到文件
/* core = zapcore.NewCore(
zapcore.NewJSONEncoder(encoder), // 编码器配置
zapcore.NewMultiWriteSyncer(zapcore.AddSync(os.Stdout), zapcore.AddSync(getLogWriter())), // 打印到控制台和文件
level, // 日志级别
)*/
zapLogger := zap.New(core, opts...)
return &ZapLogger{log: zapLogger, Sync: zapLogger.Sync}
}
// 日志自动切割,采用 lumberjack 实现的
func getLogWriter() zapcore.WriteSyncer {
lumberJackLogger := &lumberjack.Logger{
Filename: "tmp/test.log",
MaxSize: 10,
MaxBackups: 5,
MaxAge: 30,
Compress: false,
}
return zapcore.AddSync(lumberJackLogger)
}
修改main.go
//日志初始化
loggers := log.With(zap.Logger(),
"caller", log.DefaultCaller,
"service.id", id,
"service.name", Name,
"service.version", Version,
"trace_id", tracing.TraceID(),
"span_id", tracing.SpanID(),
)
使用方式
//在internal/server/grpc.go&http.go中引入日志中间件
logging.Server(logger), //日志中间件
配置(apollo)
获取apollo配置模块,将其下载到本地internal/pkg/apollo
https://github.com/go-kratos/kratos/tree/main/contrib/config/apollo
修改main.go
//远程配置中心初始化
c := config.New(
config.WithSource(
apollo.NewSource(
apollo.WithAppID(appId), //APPID
apollo.WithCluster(cluster), //集群环境
apollo.WithEndpoint(endpoint), //获取配置地址
apollo.WithNamespace(namespace), //命名空间,除却properties其他结构配置都需加后缀
apollo.WithEnableBackup(),
apollo.WithSecret(secret)), //密钥
),
)
链路追踪(jaeger)
在main.go中引入链路追踪
//设置全局链路追踪程序
func setTracerProvider(url string) error {
// Create the Jaeger exporter
exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(url)))
if err != nil {
return err
}
//配置链路追踪
tp := trace.NewTracerProvider(
// 采样率设置(100%)
trace.WithSampler(trace.ParentBased(trace.TraceIDRatioBased(1.0))),
// 采样地址
trace.WithBatcher(exp),
// 程序信息设置
trace.WithResource(resource.NewWithAttributes(
semconv.SchemaURL,
semconv.DBSystemMySQL,
semconv.ServiceNameKey.String(bc.Dbs.ProjectName),
attribute.String("ID", id),
attribute.String("Env", bc.Dbs.Env),
attribute.String("Version", bc.Dbs.Version),
)),
)
//注册全局链路追踪
otel.SetTracerProvider(tp)
return nil
}
main方法中调用此方法
//链路追踪初始化
err := setTracerProvider("http//jaeger采集地址")
if err != nil {
panic(err)
}
server中http与grpc引入链路追踪中间件
tracing.Server(), //链路追踪中间件
服务注册发现(nacos)
在internal/data初始化nacos客户端
// NewNacosConf 初始化nacos客户端服务
func NewNacosConf(conf *conf.Dbs, logger log.Logger) vo.NacosClientParam {
sc := []constant.ServerConfig{
*constant.NewServerConfig(conf.Nacos.Ip, conf.Nacos.Port),
}
cc := &constant.ClientConfig{
NamespaceId: conf.Nacos.NamespaceId,
TimeoutMs: conf.Nacos.TimeoutMs,
NotLoadCacheAtStart: conf.Nacos.NotLoadCacheAtStart,
LogDir: conf.Nacos.LogDir,
CacheDir: conf.Nacos.CacheDir,
LogLevel: conf.Nacos.LogLevel,
}
return vo.NacosClientParam{
ClientConfig: cc,
ServerConfigs: sc,
}
}
编写发现与注册服务
// NewDiscovery nacos服务发现注入
func NewDiscovery(param vo.NacosClientParam) registry.Discovery {
client, err := clients.NewNamingClient(param)
if err != nil {
panic(err)
}
return nacos.New(client)
}
// NewRegistrar 服务注册业务注入
func NewRegistrar(param vo.NacosClientParam) registry.Registrar {
client, err := clients.NewNamingClient(param)
if err != nil {
panic(err)
}
return nacos.New(client)
}
记得在wire.NewSet中引入三个方法
修改main.go newApp
//方法引入registry.Registrar参数
//在方法内调用registry.Registrar参数
kratos.Registrar(r),
orm框架(ent)
参考https://github.com/go-kratos/examples/blob/main/blog/internal/data/data.go
监控(prometheus)
参考https://go-kratos.dev/docs/component/middleware/metrics