推荐看一下 Kratos 官方文档 更加流畅观看此文章,本机器这里已经安装好了 kratos、proto、wire、make
等所需的命令工具
1.先下载beer-shop模板
git clone https://github.com/go-kratos/beer-shop.git
2.删除api和app下的全部文件
3.修改go.mod文件
把module github.com/go-kratos/beer-shop 改成beer-shop,执行go mod vendor命令
4.生成proto相关文件
api下新建test目录,执行kratos proto add api/test/service/v1/test.proto命令
修改test.proto文件,如下代码
syntax = "proto3";package api.test.service.v1;import "google/api/annotations.proto";option go_package = "github.com/go-kratos/beer-shop/api/test/service/v1;v1";service Test {rpc CreateTest (CreateTestRequest) returns (CreateTestReply){option (google.api.http) = {get: "/test/create/{name}"};};rpc UpdateTest (UpdateTestRequest) returns (UpdateTestReply);rpc DeleteTest (DeleteTestRequest) returns (DeleteTestReply);rpc GetTest (GetTestRequest) returns (GetTestReply);rpc ListTest (ListTestRequest) returns (ListTestReply);
}message CreateTestRequest {string name = 1;
}
message CreateTestReply {string message = 1;
}message UpdateTestRequest {int64 id = 1;string name = 2;
}
message UpdateTestReply {string message = 1;
}message DeleteTestRequest {}
message DeleteTestReply {}message GetTestRequest {int64 id = 1;
}
message GetTestReply {string message = 1;
}message ListTestRequest {}
message ListTestReply {}
5.然后生成proto代码
kratos proto client api/test/service/v1/test.proto
6.app下新增test/service文件夹
7.在service下,新增cmd/server文件夹
在servser下新增main.go文件
package mainimport ("flag""os""beer-shop/app/test/service/internal/conf""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/registry""github.com/go-kratos/kratos/v2/transport/grpc""github.com/go-kratos/kratos/v2/transport/http""go.opentelemetry.io/otel/exporters/jaeger""go.opentelemetry.io/otel/sdk/resource"tracesdk "go.opentelemetry.io/otel/sdk/trace"semconv "go.opentelemetry.io/otel/semconv/v1.4.0"
)// go build -ldflags "-X main.Version=x.y.z"
var (// Name is the name of the compiled software.Name = "beer.test.service"// Version is the version of the compiled software.Version string// flagconf is the config flag.flagconf string
)func init() {flag.StringVar(&flagconf, "conf", "../../configs", "config path, eg: -conf config.yaml")
}func newApp(logger log.Logger, gs *grpc.Server, rr registry.Registrar, hs *http.Server) *kratos.App {return kratos.New(kratos.Name(Name),kratos.Version(Version),kratos.Metadata(map[string]string{}),kratos.Logger(logger),kratos.Server(gs,hs,),kratos.Registrar(rr),)
}func main() {flag.Parse()logger := log.With(log.NewStdLogger(os.Stdout),"service.name", Name,"service.version", Version,"ts", log.DefaultTimestamp,"caller", log.DefaultCaller,)c := config.New(config.WithSource(file.NewSource(flagconf),),)if err := c.Load(); err != nil {panic(err)}var bc conf.Bootstrapif err := c.Scan(&bc); err != nil {panic(err)}var rc conf.Registryif err := c.Scan(&rc); err != nil {panic(err)}exp, err := jaeger.New(jaeger.WithCollectorEndpoint(jaeger.WithEndpoint(bc.Trace.Endpoint)))if err != nil {panic(err)}tp := tracesdk.NewTracerProvider(tracesdk.WithBatcher(exp),tracesdk.WithResource(resource.NewSchemaless(semconv.ServiceNameKey.String(Name),)),)app, cleanup, err := initApp(bc.Server, &rc, bc.Data, logger, tp)if err != nil {panic(err)}defer cleanup()// start and wait for stop signalif err := app.Run(); err != nil {panic(err)}
}
8.servsr新增wire.go文件
// +build wireinject// The build tag makes sure the stub is not built in the final build.package mainimport ("beer-shop/app/test/service/internal/biz""beer-shop/app/test/service/internal/conf""beer-shop/app/test/service/internal/data""beer-shop/app/test/service/internal/server""beer-shop/app/test/service/internal/service""github.com/go-kratos/kratos/v2""github.com/go-kratos/kratos/v2/log""github.com/google/wire"tracesdk "go.opentelemetry.io/otel/sdk/trace"
)// initApp init kratos application.
func initApp(*conf.Server, *conf.Registry, *conf.Data, log.Logger, *tracesdk.TracerProvider) (*kratos.App, func(), error) {panic(wire.Build(server.ProviderSet, data.ProviderSet, biz.ProviderSet, service.ProviderSet, newApp))
}
9.在service下,新增configs文件夹
configs下新增config.yaml文件(数据库根据自己配置修改)
trace:endpoint: http://127.0.0.1:14268/api/traces
server:http:addr: 0.0.0.0:8000timeout: 1sgrpc:addr: 0.0.0.0:9000timeout: 1s
data:database:driver: mysqlsource: root:root@tcp(127.0.0.1:3306)/test?parseTime=trueredis:addr: 127.0.0.1:6379read_timeout: 0.2swrite_timeout: 0.2s
configs下新增registry.yaml文件
consul:address: 127.0.0.1:8500scheme: http
11.app/test/service下新增internal/biz文件夹
biz文件下新增test.go
package bizimport ("context""github.com/go-kratos/kratos/v2/log"
)type Test struct {Id int64Name string
}type TestRepo interface {CreateTest(ctx context.Context, c *Test) (*Test, error)GetTest(ctx context.Context, id int64) (*Test, error)UpdateTest(ctx context.Context, c *Test) (*Test, error)ListTest(ctx context.Context, pageNum, pageSize int64) ([]*Test, error)
}type TestUseCase struct {repo TestRepolog *log.Helper
}func NewTestUseCase(repo TestRepo, logger log.Logger) *TestUseCase {return &TestUseCase{repo: repo, log: log.NewHelper(log.With(logger, "module", "usecase/test"))}
}func (uc *TestUseCase) Create(ctx context.Context, u *Test) (*Test, error) {return uc.repo.CreateTest(ctx, u)
}func (uc *TestUseCase) Get(ctx context.Context, id int64) (*Test, error) {return uc.repo.GetTest(ctx, id)
}func (uc *TestUseCase) Update(ctx context.Context, u *Test) (*Test, error) {return uc.repo.UpdateTest(ctx, u)
}func (uc *TestUseCase) List(ctx context.Context, pageNum, pageSize int64) ([]*Test, error) {return uc.repo.ListTest(ctx, pageNum, pageSize)
}
biz文件下新增biz.go
package bizimport "github.com/google/wire"// ProviderSet is biz providers.
var ProviderSet = wire.NewSet(NewTestUseCase)
12.app/test/service下新增internal/conf文件夹
在conf下新增conf.proto
syntax = "proto3";
package kratos.api;option go_package = "app/test/service/internal/conf;conf";import "google/protobuf/duration.proto";message Bootstrap {Trace trace = 1;Server server = 2;Data data = 3;
}message Trace {string endpoint = 1;
}message Server {message HTTP {string network = 1;string addr = 2;google.protobuf.Duration timeout = 3;}message GRPC {string network = 1;string addr = 2;google.protobuf.Duration timeout = 3;}HTTP http = 1;GRPC grpc = 2;
}message Data {message Database {string driver = 1;string source = 2;}message Redis {string network = 1;string addr = 2;google.protobuf.Duration read_timeout = 3;google.protobuf.Duration write_timeout = 4;}Database database = 1;
}message Registry {message Consul {string address = 1;string scheme = 2;}Consul consul = 1;
}
在conf目录下执行以下命令,生成对应的conf.pb.go
protoc --go_out=paths=source_relative:. conf.proto
13.app/test/service下新增internal/data文件夹
在data下新增test.go
package dataimport ("context""time""gorm.io/gorm""beer-shop/pkg/util/pagination""github.com/go-kratos/kratos/v2/log""beer-shop/app/test/service/internal/biz"
)var _ biz.TestRepo = (*testRepo)(nil)type testRepo struct {data *Datalog *log.Helper
}type Test struct {gorm.ModelId int64Name stringCreatedAt time.TimeUpdatedAt time.Time
}func NewTestRepo(data *Data, logger log.Logger) biz.TestRepo {return &testRepo{data: data,log: log.NewHelper(log.With(logger, "module", "data/test")),}
}func (r *testRepo) CreateTest(ctx context.Context, b *biz.Test) (*biz.Test, error) {o := Test{Id: b.Id, Name: b.Name}result := r.data.db.WithContext(ctx).Create(&o)return &biz.Test{Id: o.Id,Name: o.Name,}, result.Error
}func (r *testRepo) GetTest(ctx context.Context, id int64) (*biz.Test, error) {o := Test{}result := r.data.db.WithContext(ctx).First(&o, id)return &biz.Test{Id: o.Id,}, result.Error
}func (r *testRepo) UpdateTest(ctx context.Context, b *biz.Test) (*biz.Test, error) {o := Test{}result := r.data.db.WithContext(ctx).First(&o, b.Id)if result.Error != nil {return nil, result.Error}o.Name = b.Nameresult = r.data.db.WithContext(ctx).Save(&o)if result.Error != nil {return nil, result.Error}return &biz.Test{Id: o.Id,}, nil
}func (r *testRepo) ListTest(ctx context.Context, pageNum, pageSize int64) ([]*biz.Test, error) {var os []Testresult := r.data.db.WithContext(ctx).Limit(int(pageSize)).Offset(int(pagination.GetPageOffset(pageNum, pageSize))).Find(&os)if result.Error != nil {return nil, result.Error}rv := make([]*biz.Test, 0)for _, o := range os {rv = append(rv, &biz.Test{Id: o.Id,})}return rv, nil
}
在data下新增data.go
package dataimport ("github.com/go-kratos/kratos/v2/log""github.com/google/wire""gorm.io/driver/mysql""gorm.io/gorm""beer-shop/app/test/service/internal/conf"// init mysql driver_ "github.com/go-sql-driver/mysql"
)// ProviderSet is data providers.
var ProviderSet = wire.NewSet(NewData, NewDB, NewTestRepo)// Data .
type Data struct {db *gorm.DBlog *log.Helper
}func NewDB(conf *conf.Data, logger log.Logger) *gorm.DB {log := log.NewHelper(log.With(logger, "module", "test-service/data/gorm"))db, err := gorm.Open(mysql.Open(conf.Database.Source), &gorm.Config{})if err != nil {log.Fatalf("failed opening connection to mysql: %v", err)}if err := db.AutoMigrate(&Test{}); err != nil {log.Fatal(err)}return db
}// NewData .
func NewData(db *gorm.DB, logger log.Logger) (*Data, func(), error) {log := log.NewHelper(log.With(logger, "module", "test-service/data"))d := &Data{db: db,log: log,}return d, func() {}, nil
}
14.app/test/service下新增internal/server文件夹
在server下新增grpc.go
package serverimport ("beer-shop/api/test/service/v1""beer-shop/app/test/service/internal/conf""beer-shop/app/test/service/internal/service""github.com/go-kratos/kratos/v2/log"tracesdk "go.opentelemetry.io/otel/sdk/trace""github.com/go-kratos/kratos/v2/middleware/logging""github.com/go-kratos/kratos/v2/middleware/recovery""github.com/go-kratos/kratos/v2/middleware/tracing""github.com/go-kratos/kratos/v2/transport/grpc"
)// NewGRPCServer new a gRPC server.
func NewGRPCServer(c *conf.Server, logger log.Logger, tp *tracesdk.TracerProvider, s *service.TestService) *grpc.Server {var opts = []grpc.ServerOption{grpc.Middleware(recovery.Recovery(),tracing.Server(tracing.WithTracerProvider(tp)),logging.Server(logger),),}if c.Grpc.Network != "" {opts = append(opts, grpc.Network(c.Grpc.Network))}if c.Grpc.Addr != "" {opts = append(opts, grpc.Address(c.Grpc.Addr))}if c.Grpc.Timeout != nil {opts = append(opts, grpc.Timeout(c.Grpc.Timeout.AsDuration()))}srv := grpc.NewServer(opts...)v1.RegisterTestServer(srv, s)return srv
}
在server下新增http.go
package serverimport ("beer-shop/api/test/service/v1""beer-shop/app/test/service/internal/conf""beer-shop/app/test/service/internal/service""github.com/go-kratos/kratos/v2/log""github.com/go-kratos/kratos/v2/transport/http"tracesdk "go.opentelemetry.io/otel/sdk/trace""github.com/go-kratos/kratos/v2/middleware/logging""github.com/go-kratos/kratos/v2/middleware/recovery""github.com/go-kratos/kratos/v2/middleware/tracing"
)// NewGRPCServer new a gRPC server.
func NewHTTPServer(c *conf.Server, logger log.Logger, tp *tracesdk.TracerProvider, s *service.TestService) *http.Server {var opts = []http.ServerOption{http.Middleware(recovery.Recovery(),tracing.Server(tracing.WithTracerProvider(tp)),logging.Server(logger),),}if c.Http.Network != "" {opts = append(opts, http.Network(c.Http.Network))}if c.Http.Addr != "" {opts = append(opts, http.Address(c.Http.Addr))}if c.Http.Timeout != nil {opts = append(opts, http.Timeout(c.Http.Timeout.AsDuration()))}srv := http.NewServer(opts...)v1.RegisterTestHTTPServer(srv, s)return srv
}
在server下新增server.go
package serverimport ("beer-shop/app/test/service/internal/conf""github.com/go-kratos/kratos/v2/registry""github.com/google/wire"consul "github.com/go-kratos/kratos/contrib/registry/consul/v2"consulAPI "github.com/hashicorp/consul/api"
)// ProviderSet is server providers.
var ProviderSet = wire.NewSet(NewGRPCServer,NewHTTPServer, NewRegistrar)func NewRegistrar(conf *conf.Registry) registry.Registrar {c := consulAPI.DefaultConfig()c.Address = conf.Consul.Addressc.Scheme = conf.Consul.Schemecli, err := consulAPI.NewClient(c)if err != nil {panic(err)}r := consul.New(cli, consul.WithHealthCheck(false))return r
}
15.app/test/service下新增internal/service文件夹
在service下新增test.go文件
package serviceimport ("context"v1 "beer-shop/api/test/service/v1""beer-shop/app/test/service/internal/biz"
)func (s *TestService) CreateTest(ctx context.Context, req *v1.CreateTestRequest) (*v1.CreateTestReply, error) {x, err := s.oc.Create(ctx, &biz.Test{Name: req.Name})if err != nil {return nil, err}return &v1.CreateTestReply{Message: x.Name,}, nil
}func (s *TestService) GetTest(ctx context.Context, req *v1.GetTestRequest) (*v1.GetTestReply, error) {x, err := s.oc.Get(ctx, req.Id)if err != nil {return nil, err}return &v1.GetTestReply{Message: x.Name,}, nil
}func (s *TestService) UpdateTest(ctx context.Context, req *v1.UpdateTestRequest) (*v1.UpdateTestReply, error) {x, err := s.oc.Update(ctx, &biz.Test{})if err != nil {return nil, err}return &v1.UpdateTestReply{Message: x.Name,}, nil
}func (s *TestService) ListTest(ctx context.Context, req *v1.ListTestRequest) (*v1.ListTestReply, error) {return &v1.ListTestReply{}, nil
}
在service下新增service.go文件
package serviceimport (v1 "beer-shop/api/test/service/v1""beer-shop/app/test/service/internal/biz""github.com/go-kratos/kratos/v2/log""github.com/google/wire"
)// ProviderSet is service providers.
var ProviderSet = wire.NewSet(NewTestService)type TestService struct {v1.UnimplementedTestServeroc *biz.TestUseCaselog *log.Helper
}func NewTestService(oc *biz.TestUseCase, logger log.Logger) *TestService {return &TestService{oc: oc,log: log.NewHelper(log.With(logger, "module", "service/test"))}
}
15.重新执行命令
kratos proto client api/test/service/v1/test.proto
go mod vendor
在app\test\service\cmd\server下执行wire命令
wire
windows下启动consul服务
consul agent -dev
16.执行kratos runm命令运行项目
在游览器访问 http://localhost:8000/test/create/123
17.最后附上目录截图