热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【kratos学习】从零使用kratos创建一个CRUD的demo,是gorm做数据库操作,经过几天研究,调试代码开源放到github上,还是非常方便的。服务也实现了CRUD的http接口。

目录前言1,关于kratos2,使用4,使用grpcul进行服务查看调用5,创建数据CRUD使用gorm6&#


目录

  • 前言
  • 1,关于kratos
  • 2,使用
  • 4,使用 grpcul进行服务查看调用
  • 5,创建数据CRUD使用gorm
  • 6,做CRUD接口
  • 7,总结


前言



本文的原文连接是:
https://blog.csdn.net/freewebsys/article/details/124262158

未经博主允许不得转载。
博主地址是:http://blog.csdn.net/freewebsys


1,关于kratos



Kratos 一套轻量级 Go 微服务框架,包含大量微服务相关框架及工具。

https://go-kratos.dev/docs/

网络上面的demo并不全面,从头来一点点进行研究学习。
学到老活到老。


2,使用



golang 需要使用1.18 的最新版本进行开发吧。
然后安装kratos 工具:

国内下载:
https://golang.google.cn/dl/

语法中文学习:
https://www.runoob.com/go/go-tutorial.html

golang install 加速:

https://goproxy.io/zh/

#kratos 基础工具命令
go install github.com/go-kratos/kratos/cmd/kratos/v2@latest#golang grpc 特别好的工具,可以直接调用grpc 服务go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latestkratos 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 tidygo mod vendorcd 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 bizimport ("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 int64UserName stringPassword stringAge uint32Phone stringAddress stringCreatedAt 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 updateDelete(context.Context, int64) errorFindByID(context.Context, int64) (*UserInfo, error)FindAll(context.Context) ([]*UserInfo, error)
}// UserInfoUsecase is a UserInfo usecase.
type UserInfoUsecase struct {repo UserInfoRepoIflog *log.Helper
}// NewUserInfoUsecase new a UserInfo usecase.
func NewUserInfoUsecase(repo UserInfoRepoIf, logger log.Logger) *UserInfoUsecase {return &UserInfoUsecase{repo: 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 dataimport ("context""kratos-crud/internal/biz""github.com/go-kratos/kratos/v2/log"
)type userInfoRepo struct {data *Datalog *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) // 返回 errorif userInfo.Id > 0 { //更新 userInfo := biz.UserInfo{}var oldUserInfo biz.UserInforesult1 := repo.data.db.First(&oldUserInfo, "id = ? ", userInfo.Id) // 通过id进行数据查询repo.log.Debug("result.Error :", result1.Error) // 返回 errorrepo.log.Debug("save userInfo :", userInfo)if result1.Error != nil { // 有错误返回return userInfo, result1.Error} else {oldUserInfo.UserName = userInfo.UserNameoldUserInfo.Password = userInfo.PasswordoldUserInfo.Age = userInfo.AgeoldUserInfo.Phone = userInfo.PhoneoldUserInfo.Address = userInfo.Addressresult := repo.data.db.Save(&oldUserInfo) // 通过数据的指针来创建repo.log.Debug("result.Error :", result.Error) // 返回 errorrepo.log.Debug("save userInfo :", userInfo)return userInfo, result1.Error}} else { // 创建result := repo.data.db.Create(userInfo) // 通过数据的指针来创建repo.log.Debug("result.Error :", result.Error) // 返回 errorrepo.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.UserInfo{Id: id}) // 通过id删除数据repo.log.Debug("result.Error :", result.Error) // 返回 errorreturn 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) // 返回 errorreturn &userInfo, result.Error
}func (repo *userInfoRepo) FindAll(ctx context.Context) ([]*biz.UserInfo, error) {repo.log.Debug("FindAll :") //var userInfoList []*biz.UserInforesult := repo.data.db.Find(&userInfoList) // 通过数据查询repo.log.Debug("result.Error :", result.Error) // 返回 errorreturn 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 serviceimport ("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.UnimplementedUserInfoServiceServeruc *biz.UserInfoUsecaselog *log.Helper
}// NewUserInfoService new a UserInfo service.
func NewUserInfoService(uc *biz.UserInfoUsecase, logger log.Logger) *UserInfoService {log := log.NewHelper(logger)return &UserInfoService{uc: 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.CommReply{Message: "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.CommReply{Message: "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.UserInfoReply{Message: "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.ListUserInfoReply{Message: "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


推荐阅读
  • Go Cobra命令行工具入门教程
    本文介绍了Go语言实现的命令行工具Cobra的基本概念、安装方法和入门实践。Cobra被广泛应用于各种项目中,如Kubernetes、Hugo和Github CLI等。通过使用Cobra,我们可以快速创建命令行工具,适用于写测试脚本和各种服务的Admin CLI。文章还通过一个简单的demo演示了Cobra的使用方法。 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • tcpdump 4.5.1 crash 深入分析
    tcpdump 4.5.1 crash 深入分析 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了一种轻巧方便的工具——集算器,通过使用集算器可以将文本日志变成结构化数据,然后可以使用SQL式查询。集算器利用集算语言的优点,将日志内容结构化为数据表结构,SPL支持直接对结构化的文件进行SQL查询,不再需要安装配置第三方数据库软件。本文还详细介绍了具体的实施过程。 ... [详细]
  • 本文介绍了在Windows系统下安装Python、setuptools、pip和virtualenv的步骤,以及安装过程中需要注意的事项。详细介绍了Python2.7.4和Python3.3.2的安装路径,以及如何使用easy_install安装setuptools。同时提醒用户在安装完setuptools后,需要继续安装pip,并注意不要将Python的目录添加到系统的环境变量中。最后,还介绍了通过下载ez_setup.py来安装setuptools的方法。 ... [详细]
  • DSP中cmd文件的命令文件组成及其作用
    本文介绍了DSP中cmd文件的命令文件的组成和作用,包括链接器配置文件的存放链接器配置信息、命令文件的组成、MEMORY和SECTIONS两个伪指令的使用、CMD分配ROM和RAM空间的目的以及MEMORY指定芯片的ROM和RAM大小和划分区间的方法。同时强调了根据不同芯片进行修改的必要性,以适应不同芯片的存储用户程序的需求。 ... [详细]
  • VSCode快速查看函数定义和代码追踪方法详解
    本文详细介绍了在VSCode中快速查看函数定义和代码追踪的方法,包括跳转到定义位置的三种方式和返回跳转前的位置的快捷键。同时,还介绍了代码追踪插件的使用以及对符号跳转的不足之处。文章指出,直接跳转到定义和实现的位置对于程序员来说非常重要,但需要语言本身的支持。以TypeScript为例,按下F12即可跳转到函数的定义处。 ... [详细]
  • 本文详细介绍了在Centos7上部署安装zabbix5.0的步骤和注意事项,包括准备工作、获取所需的yum源、关闭防火墙和SELINUX等。提供了一步一步的操作指南,帮助读者顺利完成安装过程。 ... [详细]
  • 服务网关与流量网关
    一、为什么需要服务网关1、什么是服务网关传统的单体架构中只需要开放一个服务给客户端调用,但是微服务架构中是将一个系统拆分成多个微服务,如果没有网关& ... [详细]
  • TiDB | TiDB在5A级物流企业核心系统的应用与实践
    TiDB在5A级物流企业核心系统的应用与实践前言一、业务背景科捷物流概况神州金库简介二、现状与挑战神州金库现有技术体系业务挑战应对方案三、TiDB解决方案测试迁移收益问题四、说在最 ... [详细]
author-avatar
网赚交流大厅算_817
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有