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

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

编写API Gateway代码

mkdir bookstore && cd bookstore
go mod init bookstore

mkdir api && goctl api -o api/bookstore.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 .

微信截图_20230812185358.png

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为:

Name: bookstore-api
Host: 0.0.0.0
Port: 8888

微信截图_20230812185458.png

按提示下载,再次运行:

微信截图_20230812185513.png

微信截图_20230812200419.png

{"@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里)

微信截图_20230812200517.png

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

这是因为:

微信截图_20230812200532.png

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

微信截图_20230812200603.png

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

微信截图_20230812200620.png

目前只返回了个空值,接下来会在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

修改后文件内容如下:

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=.

微信截图_20230812200825.png

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和内存信息

{"@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

修改后文件内容如下:

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);
}

微信截图_20230812200943.png

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,增加如下内容

Add:
  Etcd:
    Hosts:
      - localhost:2379
    Key: add.rpc
Check:
  Etcd:
    Hosts:
      - localhost:2379
    Key: check.rpc

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

微信截图_20230812201048.png

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

微信截图_20230812201104.png

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

微信截图_20230812201142.png

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

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


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

微信截图_20230812201154.png

微信截图_20230812201205.png

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


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

微信截图_20230812201250.png

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


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


bookstore下创建rpc/model目录

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

在rpc/model目录下编写创建book表的sql文件book.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

create database gozero;
source book.sql;

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

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

微信截图_20230812201356.png

修改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

微信截图_20230812201434.png

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


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

微信截图_20230812201452.png

微信截图_20230812201500.png

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

微信截图_20230812201555.png

微信截图_20230812201606.png

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

微信截图_20230812201615.png

微信截图_20230812201624.png

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

微信截图_20230812201715.png

项目使用


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

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

全部启动:

微信截图_20230812201728.png

微信截图_20230812201736.png

微信截图_20230812201743.png

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

调用add api,新增图书

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

微信截图_20230812201828.png

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


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

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

微信截图_20230812201846.png

微信截图_20230812201856.png

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

微信截图_20230812201936.png

目录
相关文章
|
4月前
|
监控 算法 NoSQL
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
🌟蒋星熠Jaxonic:Go微服务限流熔断实践者。分享基于滑动窗口、令牌桶与自适应阈值的智能防护体系,助力高并发系统稳定运行。
Go 微服务限流与熔断最佳实践:滑动窗口、令牌桶与自适应阈值
|
4月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
10月前
|
监控 Java Go
无感改造,完美监控:Docker 多阶段构建 Go 应用无侵入观测
本文将介绍一种基于 Docker 多阶段构建的无侵入 Golang 应用观测方法,通过此方法用户无需对 Golang 应用源代码或者编译指令做任何改造,即可零成本为 Golang 应用注入可观测能力。
489 85
|
7月前
|
监控 安全 Go
使用Go语言构建网络IP层安全防护
在Go语言中构建网络IP层安全防护是一项需求明确的任务,考虑到高性能、并发和跨平台的优势,Go是构建此类安全系统的合适选择。通过紧密遵循上述步骤并结合最佳实践,可以构建一个强大的网络防护系统,以保障数字环境的安全完整。
171 12
|
8月前
|
NoSQL Go API
MCP 官方开源 Registry 注册服务:基于 Go 和 MongoDB 构建
作为 `registry` 项目的贡献者,我很高兴能参与这个社区驱动的开源项目,也期待它不断发展壮大。本文将对 `registry` 服务进行介绍,为项目的推广尽一份绵薄之力。
294 3
MCP 官方开源 Registry 注册服务:基于 Go 和 MongoDB 构建
|
7月前
|
Java Shell Maven
【Azure Container App】构建Java应用镜像时候遇无法编译错误:ERROR [build 10/10] RUN ./mvnw.cmd dependency:go-offline -B -Dproduction package
在部署Java应用到Azure Container App时,构建镜像过程中出现错误:“./mvnw.cmd: No such file or directory”。尽管项目根目录包含mvnw和mvnw.cmd文件,但依然报错。问题出现在Dockerfile构建阶段执行`./mvnw dependency:go-offline`命令时,系统提示找不到可执行文件。经过排查,确认是mvnw文件内容异常所致。最终通过重新生成mvnw文件解决该问题,镜像成功构建。
319 1
|
7月前
|
存储 中间件 网络安全
在Go中构建应用级IP防火墙机制
使用Go构建应用级别的IP防火墙机制不仅能够为你的应用程序增加一层额外的安全性,还能够通过自定义中间件的方式让你有更多控制力,来决定哪些客户端可以或不可以访问你的服务。
225 8
|
Go 开发工具
百炼-千问模型通过openai接口构建assistant 等 go语言
由于阿里百炼平台通义千问大模型没有完善的go语言兼容openapi示例,并且官方答复assistant是不兼容openapi sdk的。 实际使用中发现是能够支持的,所以自己写了一个demo test示例,给大家做一个参考。
|
8月前
|
JSON 编解码 API
Go语言网络编程:使用 net/http 构建 RESTful API
本章介绍如何使用 Go 语言的 `net/http` 标准库构建 RESTful API。内容涵盖 RESTful API 的基本概念及规范,包括 GET、POST、PUT 和 DELETE 方法的实现。通过定义用户数据结构和模拟数据库,逐步实现获取用户列表、创建用户、更新用户、删除用户的 HTTP 路由处理函数。同时提供辅助函数用于路径参数解析,并展示如何设置路由器启动服务。最后通过 curl 或 Postman 测试接口功能。章节总结了路由分发、JSON 编解码、方法区分、并发安全管理和路径参数解析等关键点,为更复杂需求推荐第三方框架如 Gin、Echo 和 Chi。
|
8月前
|
开发框架 JSON 中间件
Go语言Web开发框架实践:使用 Gin 快速构建 Web 服务
Gin 是一个高效、轻量级的 Go 语言 Web 框架,支持中间件机制,非常适合开发 RESTful API。本文从安装到进阶技巧全面解析 Gin 的使用:快速入门示例(Hello Gin)、定义 RESTful 用户服务(增删改查接口实现),以及推荐实践如参数校验、中间件和路由分组等。通过对比标准库 `net/http`,Gin 提供更简洁灵活的开发体验。此外,还推荐了 GORM、Viper、Zap 等配合使用的工具库,助力高效开发。