• Kratos战神微服务框架(二)


    Kratos战神微服务框架(二)

    项目结构

    在这里插入图片描述

    api编写

    protobuf编写

    syntax = "proto3";
    package realworld.v1;
    import "google/api/annotations.proto";
    option go_package = "realworld/api/realworld/v1;v1";
    
    
    // The greeting service definition.
    service RealWorld {
          rpc Login(LoginRequest) returns (UserReply) {
            option (google.api.http) = {
              post: "/api/users/login",
              body: "*", // 注意post请求一定交加body项
            };
          }
      }
      
      message LoginRequest{
         message User {
           string email = 1;
           string password = 2;
         }
         User user = 1;
    }
    
      
      message UserReply {
          message User {
            string email = 1;
            string token = 2;
            string username = 3;
            string bio = 4;
            string image = 5;
          }
          User user = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    使用makefile

    注:需要安装make指令

    GOPATH:=$(shell go env GOPATH)
    VERSION=$(shell git describe --tags --always)
    INTERNAL_PROTO_FILES=$(shell find internal -name *.proto)
    API_PROTO_FILES=$(shell find api -name *.proto)
    
    .PHONY: init
    # init env
    init:
       go install google.golang.org/protobuf/cmd/protoc-gen-go@latest
       go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
       go install github.com/go-kratos/kratos/cmd/kratos/v2@latest
       go install github.com/go-kratos/kratos/cmd/protoc-gen-go-http/v2@latest
       go install github.com/google/gnostic/cmd/protoc-gen-openapi@latest
    
    .PHONY: config
    # generate internal proto
    config:
       protoc --proto_path=./internal \
              --proto_path=./third_party \
              --go_out=paths=source_relative:./internal \
              ./internal/conf/*.proto
    #         $(INTERNAL_PROTO_FILES)
    
    .PHONY: api
    # generate api proto
    api:
       protoc --proto_path=./api \
              --proto_path=./third_party \
              --go_out=paths=source_relative:./api \
              --go-http_out=paths=source_relative:./api \
              --go-grpc_out=paths=source_relative:./api \
              --openapi_out=fq_schema_naming=true,default_response=false:. \
              ./api/realworld/v1/*.proto
    #         $(API_PROTO_FILES)
    
    .PHONY: build
    # build
    build:
       mkdir -p bin/ && go build -ldflags "-X main.Version=$(VERSION)" -o ./bin/ ./...
    
    .PHONY: generate
    # generate
    generate:
       go mod tidy
       go get github.com/google/wire/cmd/wire@latest
       go generate ./...
    
    .PHONY: wire
    # wire
    wire:
       cd cmd/realworld/ && wire
    
    .PHONY: run
    # run
    run:
       kratos run
    
    .PHONY: all
    # generate all
    all:
       make api;
       make config;
       make generate;
    
    # show help
    help:
       @echo ''
       @echo 'Usage:'
       @echo ' make [target]'
       @echo ''
       @echo 'Targets:'
       @awk '/^[a-zA-Z\-\_0-9]+:/ { \
       helpMessage = match(lastLine, /^# (.*)/); \
          if (helpMessage) { \
             helpCommand = substr($$1, 0, index($$1, ":")-1); \
             helpMessage = substr(lastLine, RSTART + 2, RLENGTH); \
             printf "\033[36m%-22s\033[0m %s\n", helpCommand,helpMessage; \
          } \
       } \
       { lastLine = $$0 }' $(MAKEFILE_LIST)
    
    .DEFAULT_GOAL := help
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82

    注:默认是linux命令,windwos需要修改路径

    service层接口实现

    进入internal/service目录

    service.go

    package service
    
    import (
       "github.com/go-kratos/kratos/v2/log"
       "github.com/google/wire"
       v1 "helloworld/api/realworld/v1"
       "helloworld/internal/biz"
    )
    
    // ProviderSet is service providers.
    var ProviderSet = wire.NewSet(NewRealWorldService) //依赖注入
    
    type RealWorldService struct {
       v1.UnimplementedRealWorldServer
        
       pu  *biz.SocialUsecase
       uc  *biz.UserUsecase
       log *log.Helper
    }
    
    func NewRealWorldService(uc *biz.UserUsecase, logger log.Logger) *RealWorldService {
       return &RealWorldService{uc: uc, log: log.NewHelper(logger)}
    }
    
    
    //实现方法
    func (s *RealWorldService) Login(ctx context.Context, req *v1.LoginRequest) (*v1.UserReply, error) {
    	return &v1.UserReply{
    		User: &v1.UserReply_User{
    			Username: "jtyyds",
    		},
    	}, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    biz层

    package biz
    
    import "github.com/google/wire"
    
    // ProviderSet is biz providers.
    var ProviderSet = wire.NewSet(NewSocialUsecase, NewUserUsecase) //依赖注入
    
    
    type User struct {
    	Email        string
    	Username     string
    	Bio          string
    	Image        string
    	PasswordHash string
    }
    
    type UserLogin struct {
    	Email    string
    	Username string
    	Token    string
    	Bio      string
    	Image    string
    }
    
    // bcrypt方法加密
    func hashPassword(pwd string) string {
    	password, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Printf("%v", password)
    	return string(password)
    }
    // 密码进行比较
    func verifyPassword(hashed, input string) bool {
    	err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(input))
    	if err != nil {
    		return false
    	}
    	return true
    }
    
    // 接口方法
    type UserRepo interface {
    	CreateUser(ctx context.Context, user *User) error
    	GetUserByEmail(ctx context.Context, email string) (*User, error)
    }
    
    
    
    type ProfileRepo interface {
    }
    
    type UserUsecase struct {
    	ur   UserRepo
    	pr   ProfileRepo
    	jwtc *conf.JWT
    	log  *log.Helper
    }
    
    func NewUserUsecase(ur UserRepo, pr ProfileRepo, jwtc *conf.JWT, logger log.Logger) *UserUsecase {
    	return &UserUsecase{ur: ur, pr: pr, jwtc: jwtc, log: log.NewHelper(logger)}
    }
    
    func (uc *UserUsecase) generateToken(username string) string {
    	return auth.GenerateToken(uc.jwtc.Token, username)
    }
    
    // 注册的业务逻辑
    func (uu *UserUsecase) Register(ctx context.Context, username, email, password string) (*UserLogin, error) {
    	u := &User{
    		Email:        email,
    		Username:     username,
    		PasswordHash: hashPassword(password),
    	}
    	if err := uu.ur.CreateUser(ctx, u); err != nil {
    		return nil, err
    	}
    	return &UserLogin{
    		Email:    email,
    		Username: username,
    		Token:    uu.generateToken(username),
    	}, nil
    }
    
    // 登录的业务逻辑实现
    func (uu *UserUsecase) Login(ctx context.Context, email, password string) (*UserLogin, error) {
    	u, err := uu.ur.GetUserByEmail(ctx, email)
    	if err != nil {
    		return nil, err
    	}
    	b := verifyPassword(u.PasswordHash, password)
    	if b == true {
    		return nil, errors.New("Loin")
    	}
    	return &UserLogin{
    		Email:    u.Email,
    		Username: u.Username,
    		Bio:      u.Bio,
    		Image:    u.Image,
    		Token:    uu.generateToken(u.Username),
    	}, nil
    }
    
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    data层

    data.go

    package data
    
    import (
       "fmt"
       "github.com/go-kratos/kratos/v2/log"
       "github.com/google/wire"
       "gorm.io/driver/mysql"
       "gorm.io/gorm"
       "helloworld/internal/conf"
    )
    
    // ProviderSet is data providers.
    var ProviderSet = wire.NewSet(NewData, NewDB, NewUserRepo, NewProfileRepo) //依赖注入
    
    // Data .
    type Data struct {
       // TODO wrapped database client
       db *gorm.DB
    }
    
    // NewData .
    func NewData(c *conf.Data, logger log.Logger, db *gorm.DB) (*Data, func(), error) {
       cleanup := func() {
          log.NewHelper(logger).Info("closing the data resources")
       }
       return &Data{db: db}, cleanup, nil
    }
    
    // 连接mysql数据库
    func NewDB(c *conf.Data) *gorm.DB {
       fmt.Println(c.Database.Dsn)
       db, err := gorm.Open(mysql.Open(c.Database.Dsn), &gorm.Config{})
       if err != nil {
          panic("failed")
       }
       if err := db.AutoMigrate(); err != nil {
          panic(err)
       }
       return db
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    package data
    
    import (
       "context"
    
       "github.com/go-kratos/kratos/v2/log"
       "helloworld/internal/biz"
    )
    
    type userRepo struct {
       data *Data
       log  *log.Helper
    }
    
    func NewUserRepo(data *Data, logger log.Logger) biz.UserRepo {
       return &userRepo{
          data: data,
          log:  log.NewHelper(logger),
       }
    }
    
    func (r *userRepo) CreateUser(ctx context.Context, g *biz.User) error { // 可以实现操作数据库,这个并没有实现
       return nil
    }
    
    func (r *userRepo) GetUserByEmail(ctx context.Context, email string) (*biz.User, error) {// 可以实现操作数据库,这个并没有实现
       return nil, nil
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    configs配置文件

    server:
      http:
        addr: 0.0.0.0:8000
        timeout: 1s
      grpc:
        addr: 0.0.0.0:9000
        timeout: 1s
    
    data:
      database:
        driver: mysql
        dsn: "root:123456@tcp(127.0.0.1:3306)/realworld?charset=utf8mb4&parseTime=True&loc=Local"
    
    jwt:
      secret : "hello"
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    修改配置文件后,需要修改internal/conf下的conf.proto

    syntax = "proto3";
    package kratos.api;
    
    option go_package = "helloworld/internal/conf;conf";
    
    import "google/protobuf/duration.proto";
    
    message Bootstrap {
      Server server = 1;
      Data data = 2;
      JWT jwt = 3;
    }
    
    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 dsn = 2;
      }
      Database database = 1;
    }
    message JWT {
      string token = 1;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39

    使用make config生成conf.pb.go

    注:是否需要修改config的makefile

  • 相关阅读:
    springboot集成uid-generator生成分布式id
    [网络/HTTPS/Java] PKI公钥基础设施体系:数字证书(X.509)、CA机构 | 含:证书管理工具(jdk keytool / openssl)
    【EXCEL】详解使用python读写EXCEL文件(xlrd,xlwt)
    园子的第一款简陋鼠标垫,是否是您值得拥有的周边
    Netbeans介绍
    el-menu-item使用自定义图标、使用图片做图标
    iTextSharp 使用详解
    简易的聊天界面以及聊天机器人的实现
    ArrayList 源码解析(JDK1.8)
    rabbitmq消息投递失败
  • 原文地址:https://blog.csdn.net/qq_53267860/article/details/125546126