kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。
Posted freewebsys
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。相关的知识,希望对你有一定的参考价值。
目录
前言
本文的原文连接是:
https://blog.csdn.net/freewebsys/article/details/124262158
未经博主允许不得转载。
博主地址是:http://blog.csdn.net/freewebsys
1,关于kratos
Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。
网络上面的demo并不全面,从头来一点点进行研究学习。
学到老活到老。
2,使用
golang 需要使用1.18 的最新版本进行开发吧。
然后安装kratos 工具:
国内下载:
https://golang.google.cn/dl/
语法中文学习:
https://www.runoob.com/go/go-tutorial.html
golang install 加速:
#kratos 基础工具命令
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
#golang grpc 特别好的工具,可以直接调用grpc 服务
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
kratos new kratos-crud
cd kratos-crud
然后使用 make 工具,提供几个常用的功能,先把环境创建好。
make
Usage:
make [target]
Targets:
init init env
config generate internal proto
api generate api proto
build build
generate generate
all generate all
help show help
protobuffer 文档:
https://developers.google.cn/protocol-buffers/docs/proto3
在 ubuntu系统上安装protoc 工具,默认的工具是3.0.0的版本太低了。
否则报错误找不到
conf/conf.proto:22:5: "google.protobuf.Duration" is not defined.
重新安装 prototc 解决问题,估计是新的库中才有google的proto文件定义。
源码下载地址:
git clone https://github.com/protocolbuffers/protobuf.git
安装依赖的库:
sudo install -y autoconf automake libtool curl make g++ unzip
安装:
./autogen.sh
./configure
make && sudo make install
# 然后使用这个版本就可以了。
protoc --version
libprotoc 3.20.1-rc1
然后在执行 make all 就可以了:
go mod vendor
make all
有的时候 wire 不好用,可以直接修改成 wire 命令:
# makefile 的部分代码修改下命令:
.PHONY: generate
# generate
generate:
go mod tidy
go mod vendor
cd cmd/kratos-crud/ && wire
项目启动特别简单:使用kratos 命令就行
$ kratos run
INFO msg=config loaded: config.yaml format: yaml
INFO msg=[gRPC] server listening on: [::]:9000
INFO msg=[HTTP] server listening on: [::]:8000
可以看到grpc 服务在9000 端口,http 在8000 端口
4,使用 grpcul进行服务查看调用
可以使用上面的工具命令进行安装:
#golang grpc 特别好的工具,可以直接调用grpc 服务
go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
然后就可以使用了。进行grpc 服务接口的查看,调用。
#查看改地址端口服务
$ grpcurl -plaintext 127.0.0.1:9000 list
grpc.health.v1.Health
grpc.reflection.v1alpha.ServerReflection
helloworld.v1.Greeter
kratos.api.Metadata
#查看函数
$ grpcurl -plaintext 127.0.0.1:9000 list helloworld.v1.Greeter
helloworld.v1.Greeter.SayHello
#查看函数描述
$ grpcurl -plaintext 127.0.0.1:9000 describe helloworld.v1.Greeter.SayHello
helloworld.v1.Greeter.SayHello is a method:
rpc SayHello ( .helloworld.v1.HelloRequest ) returns ( .helloworld.v1.HelloReply )
option (.google.api.http) = get:"/helloworld/name" ;
#查看入参
$ grpcurl -plaintext 127.0.0.1:9000 describe .helloworld.v1.HelloRequest
helloworld.v1.HelloRequest is a message:
message HelloRequest
string name = 1;
#调用函数,并返回
$ grpcurl -d '"name": "zhangsan"' -plaintext 127.0.0.1:9000 helloworld.v1.Greeter.SayHello
"message": "Hello zhangsan"
5,创建数据CRUD使用gorm
https://gorm.io/zh_CN/docs/index.html
创建数据库,使用 gorm 操作数据库CRUD
使用root账号创建数据库和用户使用 demo demo 账号登录。
CREATE DATABASE IF NOT EXISTS demo DEFAULT CHARSET utf8 COLLATE utf8_general_ci;
grant all privileges on demo.* to demo@'%' identified by 'demo';
# 创建 userInfo 的用户表:
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(225) NOT NULL,
`password` varchar(225) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
发现在插入数据的时候偶尔比较慢,使用连接池解决:
2022/04/24 21:23:12 /media/test/NewDisk1/go/src/kratos-crud/internal/data/user_info_repo.go:24 SLOW SQL >= 200ms
[489.453ms] [rows:1] INSERT INTO `user_info` (`user_name`,`password`,`age`,`phone`,`address`) VALUES ('','',0,'','')
设置连接池,解决链接问题,但在更新的时候偶尔还是会有,已经比之前好多了。
sqlDB.SetMaxIdleConns(int(c.Database.MaxIdleConns))
// SetMaxIdleConns sets the maximum number of connections in the idle connection pool.
sqlDB.SetMaxOpenConns(int(c.Database.MaxOpenConns))
// SetMaxOpenConns sets the maximum number of open connections to the database.
sqlDB.SetConnMaxLifetime(time.Second * time.Duration(c.Database.ConnMaxLifetime))
// SetConnMaxLifetime sets the maximum amount of time a connection may be reused.
// 配置10 100 600 加大链接数量。
首先定义 biz 的接口:
package biz
import (
"context"
v1 "kratos-crud/api/helloworld/v1"
"time"
"github.com/go-kratos/kratos/v2/errors"
"github.com/go-kratos/kratos/v2/log"
)
var (
// ErrUserNotFound is user not found.
ErrUserNotFound = errors.NotFound(v1.ErrorReason_USER_NOT_FOUND.String(), "user not found")
)
/**
CREATE TABLE `user_info` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(225) NOT NULL,
`password` varchar(225) DEFAULT NULL,
`age` tinyint(4) DEFAULT NULL,
`phone` varchar(20) DEFAULT NULL,
`address` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
*/
// UserInfo model.
type UserInfo struct
Id int64
UserName string
Password string
Age uint32
Phone string
Address string
CreatedAt time.Time `gorm:"not null"`
UpdatedAt time.Time `gorm:"not null"`
func (UserInfo) TableName() string
return "user_info"
// UserInfoRepo is a Greater repo.
type UserInfoRepoIf interface
Save(context.Context, *UserInfo) (*UserInfo, error) // save or update
Delete(context.Context, int64) error
FindByID(context.Context, int64) (*UserInfo, error)
FindAll(context.Context) ([]*UserInfo, error)
// UserInfoUsecase is a UserInfo usecase.
type UserInfoUsecase struct
repo UserInfoRepoIf
log *log.Helper
// NewUserInfoUsecase new a UserInfo usecase.
func NewUserInfoUsecase(repo UserInfoRepoIf, logger log.Logger) *UserInfoUsecase
return &UserInfoUsecaserepo: repo, log: log.NewHelper(logger)
// SaveUserInfo
func (uc *UserInfoUsecase) SaveUserInfo(ctx context.Context, user *UserInfo) (*UserInfo, error)
uc.log.WithContext(ctx).Infof("CreateUserInfo: %v", user.UserName)
return uc.repo.Save(ctx, user)
// DeleteUserInfo
func (uc *UserInfoUsecase) DeleteUserInfo(ctx context.Context, id int64) error
uc.log.WithContext(ctx).Infof("DeleteUserInfo: %v", id)
return uc.repo.Delete(ctx, id)
// FindUserInfoByID
func (uc *UserInfoUsecase) FindUserInfoByID(ctx context.Context, id int64) (*UserInfo, error)
uc.log.WithContext(ctx).Infof("FindUserInfoByID: %v", id)
return uc.repo.FindByID(ctx, id)
// FindAllUserInfo
func (uc *UserInfoUsecase) FindAllUserInfo(ctx context.Context) ([]*UserInfo, error)
uc.log.WithContext(ctx).Infof("FindAllUserInfo ")
return uc.repo.FindAll(ctx)
然后对 user 表进行CRUD
package data
import (
"context"
"kratos-crud/internal/biz"
"github.com/go-kratos/kratos/v2/log"
)
type userInfoRepo struct
data *Data
log *log.Helper
// NewUserInfoRepo .
func NewUserInfoRepo(data *Data, logger log.Logger) biz.UserInfoRepoIf
return &userInfoRepo
data: data,
log: log.NewHelper(logger),
func (repo *userInfoRepo) Save(ctx context.Context, userInfo *biz.UserInfo) (*biz.UserInfo, error)
repo.log.Debug(" Save result :", userInfo) // 返回 error
if userInfo.Id > 0 //更新 userInfo := biz.UserInfo
var oldUserInfo biz.UserInfo
result1 := repo.data.db.First(&oldUserInfo, "id = ? ", userInfo.Id) // 通过id进行数据查询
repo.log.Debug("result.Error :", result1.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
if result1.Error != nil // 有错误返回
return userInfo, result1.Error
else
oldUserInfo.UserName = userInfo.UserName
oldUserInfo.Password = userInfo.Password
oldUserInfo.Age = userInfo.Age
oldUserInfo.Phone = userInfo.Phone
oldUserInfo.Address = userInfo.Address
result := repo.data.db.Save(&oldUserInfo) // 通过数据的指针来创建
repo.log.Debug("result.Error :", result.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
return userInfo, result1.Error
else // 创建
result := repo.data.db.Create(userInfo) // 通过数据的指针来创建
repo.log.Debug("result.Error :", result.Error) // 返回 error
repo.log.Debug("save userInfo :", userInfo)
return userInfo, result.Error
func (repo *userInfoRepo) Update(ctx context.Context, userInfo *biz.UserInfo) error
repo.log.Debug("Update :", userInfo) // 返回
return nil
func (repo *userInfoRepo) Delete(ctx context.Context, id int64) error
repo.log.Debug("Delete By Id :", id) // 返回
result := repo.data.db.Delete(&biz.UserInfoId: id) // 通过id删除数据
repo.log.Debug("result.Error :", result.Error) // 返回 error
return result.Error
func (repo *userInfoRepo) FindByID(ctx context.Context, id int64) (*biz.UserInfo, error)
repo.log.Debug("FindByID :", id) // 返回
userInfo := biz.UserInfo
result := repo.data.db.First(&userInfo, "id = ? ", id) // 通过id进行数据查询
repo.log.Debug("result.Error :", result.Error) // 返回 error
return &userInfo, result.Error
func (repo *userInfoRepo) FindAll(ctx context.Context) ([]*biz.UserInfo, error)
repo.log.Debug("FindAll :") //
var userInfoList []*biz.UserInfo
result := repo.data.db.Find(&userInfoList) // 通过数据查询
repo.log.Debug("result.Error :", result.Error) // 返回 error
return userInfoList, result.Error
可以使用GORM 对数据进行CRUD,其中save 方法比较特殊。判断id,然后执行create 或者save 执行更新操作。
6,做CRUD接口
首先定义protoc 接口:
syntax = "proto3";
package demo.v1;
import "google/api/annotations.proto";
option go_package = "kratos-crud/api/demo/v1;v1";
option java_multiple_files = true;
option java_package = "api.demo.v1";
option java_outer_classname = "UserInfoV1";
// The UserInfo service definition.
service UserInfoService
rpc Save(UserInfo) returns (CommReply)
option (google.api.http) =
post: "/userInfo/save",
body: "*"
;
rpc Delete(IdRequest) returns (CommReply)
option (google.api.http) =
post: "/userInfo/delete",
body: "*"
;
rpc Get(IdRequest) returns (UserInfoReply)
option (google.api.http) =
get: "/userInfo/get/id"
;
rpc List(ListRequest) returns (ListUserInfoReply)
option (google.api.http) =
post: "/userInfo/list",
body: "*"
;
// https://developers.google.cn/protocol-buffers/docs/proto3
// 定义一个公用类型
message UserInfo
int64 id = 1;
string userName = 2;
string password = 3;
uint32 age = 4;
string phone = 5;
string address = 6;
message IdRequest
int64 id = 1;
message ListRequest
string name = 1;
// return replay
message CommReply
int64 code = 1;
string message = 2;
message UserInfoReply
int64 code = 1;
string message = 2;
UserInfo userInfo = 3;
message ListUserInfoReply
int64 code = 1;
string message = 2;
repeated UserInfo userInfoList = 3;
其中保存,删除,查询使用POST方法,查询单个使用GET方法。当然删除也可以使用delete方法。
然后是service实现如下:
package service
import (
"context"
v1 "kratos-crud/api/helloworld/v1"
"kratos-crud/internal/biz"
"strconv"
"github.com/go-kratos/kratos/v2/log"
)
// UserInfoService is a UserInfo service.
type UserInfoService struct
v1.UnimplementedUserInfoServiceServer
uc *biz.UserInfoUsecase
log *log.Helper
// NewUserInfoService new a UserInfo service.
func NewUserInfoService(uc *biz.UserInfoUsecase, logger log.Logger) *UserInfoService
log := log.NewHelper(logger)
return &UserInfoServiceuc: uc, log: log
// Save
func (s *UserInfoService) Save(ctx context.Context, in *v1.UserInfo) (*v1.CommReply, error)
s.log.Info(in.GetUserName())
userInfo := biz.UserInfo
userInfo.Id = in.GetId()
userInfo.UserName = in.GetUserName()
userInfo.Password = in.GetPassword()
userInfo.Age = in.GetAge()
userInfo.Phone = in.GetPhone()
userInfo.Address = in.GetAddress()
g, err := s.uc.SaveUserInfo(ctx, &userInfo)
if err != nil
return nil, err
return &v1.CommReplyMessage: "Hello " + g.UserName, nil
// Delete
func (s *UserInfoService) Delete(ctx context.Context, in *v1.IdRequest) (*v1.CommReply, error)
err := s.uc.DeleteUserInfo(ctx, in.GetId())
if err != nil
return nil, err
return &v1.CommReplyMessage: "Delete " + strconv.FormatInt(in.GetId(), 10), nil
// Get
func (s *UserInfoService) Get(ctx context.Context, in *v1.IdRequest) (*v1.UserInfoReply, error)
userInfo, err := s.uc.FindUserInfoByID(ctx, in.GetId())
if err != nil
return nil, err
// 对象转换
userInfoReply := &v1.UserInfo
Id: userInfo.Id,
UserName: userInfo.UserName,
Password: userInfo.Password,
Age: userInfo.Age,
Phone: userInfo.Phone,
Address: userInfo.Address,
return &v1.UserInfoReplyMessage: "Get " + userInfo.UserName, UserInfo: userInfoReply, nil
// List
func (s *UserInfoService) List(ctx context.Context, in *v1.ListRequest) (*v1.ListUserInfoReply, error)
userInfoList, err := s.uc.FindAllUserInfo(ctx)
if err != nil
return nil, err
// log.Info(userList)
var userInfoReplyList []*v1.UserInfo
// 循环转换对象。
for _, userInfo := range userInfoList
userInfoReplyList = append(userInfoReplyList,
&v1.UserInfo
Id: userInfo.Id,
UserName: userInfo.UserName,
Password: userInfo.Password,
Age: userInfo.Age,
Phone: userInfo.Phone,
Address: userInfo.Address,
)
return &v1.ListUserInfoReplyMessage: "List UserInfo", UserInfoList: userInfoReplyList, nil
这里使用了一个特殊方法进行测试,就是vscode的 rest api 插件,保存一个 .rest 文件,然后直接执行就可以了:
### 增加数据
POST http://localhost:8000/userInfo/save HTTP/1.1
content-type: application/json
"userName":"Hendry",
"password":"pwd123456",
"age":25,
"phone":"13811223344",
"address":"北京王府井1号"
### 删除数据
POST http://localhost:8000/userInfo/delete HTTP/1.1
content-type: application/json
"id":1
### 更新
POST http://localhost:8000/userInfo/save HTTP/1.1
content-type: application/json
"id":2,
"userName":"HendryNew",
"password":"pwd123456New",
"age":35,
"phone":"13811223344New",
"address":"北京王府井1号New"
### 按Id查询
GET http://localhost:8000/userInfo/get/2 HTTP/1.1
content-type: application/json
### 查询全部
POST http://localhost:8000/userInfo/list HTTP/1.1
content-type: application/json
"id":200
使用情况:
7,总结
经过几天研究,终于完成了kratos的CRUD操作,解决若干问题。并上传了代码到github上。
发现网上的代码都是很简单的CRUD,并不全面。
完善了CURD的demo。
本文的原文连接是:
https://blog.csdn.net/freewebsys/article/details/124262158
博主地址是:https://blog.csdn.net/freewebsys
以上是关于kratos学习从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。的主要内容,如果未能解决你的问题,请参考以下文章
Kratos技术系列|从Kratos设计看Go微服务工程实践
使用 Microsoft 登录的 Ory / Kratos 登录页面?
kratoskratos 框架使用新的,还是非常好的技术框架,还要继续学习,掌握了proto 文件的使用wire的使用就方便了。