使用go-zero快速构建微服务

本文是对 使用go-zero快速构建微服务的亲手实践

编写API Gateway代码

bash 复制代码
mkdir bookstore && cd bookstore
go mod init bookstore

mkdir api && goctl api -o api/bookstore.api

api 复制代码
syntax = "v1"

info(
    title: "xx使用go-zero"
    desc: "xx用来上手go-zero"
    author: "xxxx"
    email: "xxxx@gmail.com"
)

type (
    addReq struct {
        book string `form:"book"`
        price int64 `form:"price"`
    }

    addResp struct {
        ok bool `json:"ok"`
    }
)

type (
    checkReq struct {
        book string `form:"book"`
    }

    checkResp struct {
        found bool `json:"found"`
        price int64 `json:"price"`
    }
)


service bookstore-api {
    @handler AddHandler
    get /add (addReq) returns (addResp)

    @handler CheckHandler
    get /check (checkReq) returns (checkResp)
}

cd api && goctl api go -api bookstore.api -dir .

go 复制代码
api
├── bookstore.api                  // api定义
├── bookstore.go                   // main入口定义
├── etc
│   └── bookstore-api.yaml         // 配置文件
└── internal
    ├── config
    │   └── config.go              // 定义配置
    ├── handler
    │   ├── addhandler.go          // 实现addHandler
    │   ├── checkhandler.go        // 实现checkHandler
    │   └── routes.go              // 定义路由处理
    ├── logic
    │   ├── addlogic.go            // 实现AddLogic
    │   └── checklogic.go          // 实现CheckLogic
    ├── svc
    │   └── servicecontext.go      // 定义ServiceContext
    └── types
        └── types.go               // 定义请求、返回结构体

go run bookstore.go -f etc/bookstore-api.yaml

启动API Gateway服务,默认侦听在8888端口

因为默认生成的api/etc/bookstore-api.yml为:

yaml 复制代码
Name: bookstore-api
Host: 0.0.0.0
Port: 8888

按提示下载,再次运行:

yaml 复制代码
{"@timestamp":"2023-02-16T16:31:09.658+08:00","caller":"stat/usage.go:61","content":"CPU: 0m, MEMORY: Alloc=2.5Mi, TotalAlloc=2.5Mi, Sys=14.5Mi, NumGC=0","level":"stat"}
{"@timestamp":"2023-02-16T16:31:09.662+08:00","caller":"load/sheddingstat.go:61","content":"(api) shedding_stat [1m], cpu: 0, total: 0, pass: 0, drop: 0","level":"stat"}
{"@timestamp":"2023-02-16T16:31:15.044+08:00","caller":"stat/metrics.go:210","content":"(bookstore-api) - qps: 0.0/s, drops: 0, avg time: 0.0ms, med: 0.0ms, 90th: 0.0ms, 99th: 0.0ms, 99.9th: 0.0ms","level":"stat"}

会定时(默认一分钟)输出cpu,内存等的统计信息,可以通过 logx.DisableStat()禁用 (可以做到自定义模板.tpl里)

返回的是null,并不是预期的{"found":false,"price":0}

这是因为:

resp是一个指针,这样直接return会是nil,需要如下显式声明

重启服务,再次发起请求,这样的response就符合预期了~

目前只返回了个空值,接下来会在rpc服务里实现业务逻辑

可以修改internal/svc/servicecontext.go来传递服务依赖(如果需要,比如Config,Auth,后续用到的RPC等)

实现逻辑可以修改internal/logic下的对应文件(如果接口较多,可以在.api里定义不同的group,使用goctl生成代码时,会自动在logic下根据group名称创建不同的文件夹)

可以通过goctl生成各种客户端语言的api调用代码(供客户端同学使用;支持多种语言)

编写RPC代码

编写add rpc服务

切到bookstore目录下

mkdir -p rpc/add && cd rpc/add

goctl rpc template -o add.proto

修改后文件内容如下:

proto 复制代码
syntax = "proto3";

package add;

option go_package = "./pb";

message addReq {
  string book = 1;
  int64 price = 2;
}

message addResp {
  bool ok = 1;
}

service adder {
  rpc add(addReq) returns(addResp);
}

goctl rpc protoc add.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.

yaml 复制代码
rpc/add
├── add.go                      // rpc服务main函数
├── add.proto                   // rpc接口定义
├── adder
│   ├── adder.go                // 提供了外部调用方法,无需修改
│   ├── adder_mock.go           // mock方法,测试用
│   └── types.go                // request/response结构体定义
├── etc
│   └── add.yaml                // 配置文件
├── internal
│   ├── config
│   │   └── config.go           // 配置定义
│   ├── logic
│   │   └── addlogic.go         // add业务逻辑在这里实现
│   ├── server
│   │   └── adderserver.go      // 调用入口, 不需要修改
│   └── svc
│       └── servicecontext.go   // 定义ServiceContext,传递依赖
└── pb
    └── add.pb.go

go run add.go -f etc/add.yaml 可运行该服务

默认每隔一分钟输出cpu和内存信息

less 复制代码
{"@timestamp":"2023-02-16T20:02:10.640+08:00","caller":"stat/usage.go:61","content":"CPU: 0m, MEMORY: Alloc=3.3Mi, TotalAlloc=6.2Mi, Sys=15.9Mi, NumGC=3","level":"stat"}
{"@timestamp":"2023-02-16T20:02:10.656+08:00","caller":"load/sheddingstat.go:61","content":"(rpc) shedding_stat [1m], cpu: 0, total: 0, pass: 0, drop: 0","level":"stat"}

编写check rpc服务

切到bookstore目录下

mkdir -p rpc/check && cd rpc/check

goctl rpc template -o check.proto

修改后文件内容如下:

proto 复制代码
syntax = "proto3";

package check;

option go_package = "./pb";

message checkReq {
  string book = 1;
}

message checkResp {
  bool found = 1;
  int64 price = 2;
}

service checker {
  rpc check(checkReq) returns(checkResp);
}

goctl rpc protoc check.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.

yaml 复制代码
rpc/check
├── check.go                    // rpc服务main函数
├── check.proto                 // rpc接口定义
├── checker
│   ├── checker.go              // 提供了外部调用方法,无需修改
│   ├── checker_mock.go         // mock方法,测试用
│   └── types.go                // request/response结构体定义
├── etc
│   └── check.yaml              // 配置文件
├── internal
│   ├── config
│   │   └── config.go           // 配置定义
│   ├── logic
│   │   └── checklogic.go       // check业务逻辑在这里实现
│   ├── server
│   │   └── checkerserver.go    // 调用入口, 不需要修改
│   └── svc
│       └── servicecontext.go   // 定义ServiceContext,传递依赖
└── pb
    └── check.pb.go

go run check.go -f etc/check.yaml 可运行该服务

修改etc/check.yaml的端口为8081(因为8080已经被add服务使用了)

再回去修改API Gateway代码,调用add/check rpc服务

api/etc/bookstore-api.yaml,增加如下内容

yaml 复制代码
Add:
  Etcd:
    Hosts:
      - localhost:2379
    Key: add.rpc
Check:
  Etcd:
    Hosts:
      - localhost:2379
    Key: check.rpc

通过etcd自动去发现可用的add和check服务

修改api/internal/config/config.go如下,增加add&check服务依赖

修改api/internal/svc/servicecontext.go,如下:

通过ServiceContext在不同业务逻辑之间传递依赖

(问:怎么解决依赖注入问题)

修改api/internal/logic/addlogic.go里的Add方法,如下:

通过调用adder的Add方法实现添加图书到bookstore系统

修改api/internal/logic/checklogic.go里的Check方法,如下:

通过调用checker的Check方法实现从bookstore系统中查询图书的价格

定义数据库表结构,并生成CRUD+cache代码

bookstore下创建rpc/model目录

mkdir -p rpc/model (不过一般习惯把这个model文件夹抽出来,和api,rpc在一层)

在rpc/model目录下编写创建book表的sql文件book.sql,如下:

sql 复制代码
CREATE TABLE `book`
(
  `book` varchar(255) NOT NULL COMMENT 'book name',
  `price` int NOT NULL COMMENT 'book price',
  PRIMARY KEY(`book`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

进入mysql命令行,创建DB和table

ini 复制代码
create database gozero;
source book.sql;

在rpc/model目录下执行如下命令生成CRUD+cache代码,-c表示使用redis cache

goctl model mysql ddl -c -src book.sql -dir .

修改add rpc和check rpc,调用crud+cache代码

修改rpc/add/etc/add.yaml和rpc/check/etc/check.yaml,均增加如下内容:

yaml 复制代码
DataSource: root:123456@@tcp(xxx.xxx.xx.xx:3306)/gozero
#DataSource: root:123456@@tcp(localhost:3306)/gozero?charset=utf8mb4&parseTime=true&loc=Asia%2FShanghai #可以这样指定一些其他信息
Table: book
Cache:
  - Host: localhost:6379

可以使用多个redis作为cache,支持redis单点或者redis集群

修改rpc/add/internal/config.go和rpc/check/internal/config.go,如下:

修改rpc/add/internal/svc/servicecontext.go和rpc/check/internal/svc/servicecontext.go,如下:

修改rpc/add/internal/logic/addlogic.go,如下

修改rpc/check/internal/logic/checklogic.go,如下:

项目使用

需要先全部启动api服务所依赖的rpc服务。如果先启动api,则会报错:

error: context deadline exceeded, make sure rpc service "add.rpc" is already started

全部启动:

(后面可以 -f指定不同环境的xxx.yaml)

调用add api,新增图书

curl -i "http://localhost:8888/add?book=Bible&price=10"

此时看数据库,book表里新增了一行数据

调用check api,检查某本图书的价格

curl -i "http://localhost:8888/check?book=Bible"

重启check rpc,再次执行curl -i "http://localhost:8888/check?book=Bible"

完整项目代码

相关推荐
mldlds1 分钟前
Spring Boot 集成 Kettle
java·spring boot·后端
zopple8 小时前
常见的 Spring 项目目录结构
java·后端·spring
cjy0001119 小时前
springboot的 nacos 配置获取不到导致启动失败及日志不输出问题
java·spring boot·后端
小江的记录本10 小时前
【事务】Spring Framework核心——事务管理:ACID特性、隔离级别、传播行为、@Transactional底层原理、失效场景
java·数据库·分布式·后端·sql·spring·面试
sheji341610 小时前
【开题答辩全过程】以 基于springboot的校园失物招领系统为例,包含答辩的问题和答案
java·spring boot·后端
程序员cxuan11 小时前
人麻了,谁把我 ssh 干没了
人工智能·后端·程序员
wuyikeer12 小时前
Spring Framework 中文官方文档
java·后端·spring
Victor35612 小时前
MongoDB(61)如何避免大文档带来的性能问题?
后端
Victor35612 小时前
MongoDB(62)如何避免锁定问题?
后端
wuyikeer13 小时前
Spring BOOT 启动参数
java·spring boot·后端