使用go-zero快速构建微服务
本文是对 使用go-zero快速构建微服务[1]的亲手实践
编写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 AddHandlerget /add (addReq) returns (addResp)@handler CheckHandlerget /check (checkReq) returns (checkResp)
}
cd api && goctl api go -api bookstore.api -dir .

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

按提示下载,再次运行:


{"@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
修改后文件内容如下:
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=.

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);
}
goctl rpc protoc check.proto --go_out=./pb --go-grpc_out=./pb --zrpc_out=.

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服务

修改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,如下:
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 .

修改add rpc和check rpc,调用crud+cache代码
修改rpc/add/etc/add.yaml和rpc/check/etc/check.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"

完整项目代码[2]
参考资料
使用go-zero快速构建微服务: http://www.jikejiaocheng.com/c/gozero-microservices.html
[2]完整项目代码: https://github.com/cuishuang/bookstore
本文由 mdnice 多平台发布
相关文章:

使用go-zero快速构建微服务
本文是对 使用go-zero快速构建微服务[1]的亲手实践 编写API Gateway代码 mkdir bookstore && cd bookstorego mod init bookstore mkdir api && goctl api -o api/bookstore.api syntax "v1"info(title: "xx使用go-zero"desc: "xx用…...

Java开发 - Redis事务怎么用?
前言 最近博主感觉捅了Redis窝,从Redis主从,哨兵,集群,集群原理纷纷讲了一遍,不知道大家都学会了多少,想着送佛送到西,不如再添一把火,所以今天带给大家的博客是Redis事务ÿ…...
Windows Server 2019安装使用PostgreSQL 15
主要是参考这篇文章来做的: Windows11安装配置PostgreSQL(图文详细教程)_win11安装postgres 并管理工具_return strxi的博客-CSDN博客 1. 下载的是postgresql 15.3 windows x64-86版本 Community DL Page 2. 安装时一定要右击安装exe文件…...

中科驭数亮相DPU峰会,分享HADOS软件生态实践和大数据计算方案,再获评“匠芯技术奖”
又是一年相逢时,8月4日,第三届DPU峰会在北京开幕,本届峰会由中国通信学会指导,江苏省未来网络创新研究院主办,SDNLAB社区承办,以“智驱创新芯动未来”为主题,沿袭技术创新、生态协同的共创效应&…...
chrome、edge、Firefox关闭音量提醒控件显示
文章目录 1. Chrome2. edge3. firefox 1. Chrome 在地址栏输入: chrome://flags/#hardware-media-key-handling 将Hardware Media Key Handling的状态设为Disabled 2. edge 在地址栏输入 edge://flags/#hardware-media-key-handling 将Hardware Media Key Handling的状态…...

3.7v升压5v4A芯片用什么型号
问:我需要一个能够将3.7V锂电池的电压升压到5V,并且能够提供4A的电流输出的芯片。请问有什么推荐的型号吗? 答:小编为您推荐AH6922B芯片,它具备以下特点来满足您的需求: 1. 输入电压范围适配:…...

鉴源实验室丨SOME/IP协议安全攻击
作者 | 张昊晖 上海控安可信软件创新研究院工控网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 01 引 言 随着汽车行业对于数据通信的需求不断增加,SOME/IP作为支持汽车以太网进程和设备间通信的一种通信协议应…...

什么?200?跨域?
情景复现 今天我遇到了一件很奇怪的事情就是,当我请求后端网关,然后通过网关去请求相应的服务,都进行了跨域处理 但是,奇怪的是我在请求的时候,回来的响应码是200,但是报错了,报的还是200的同…...

【数据结构与算法——TypeScript】算法的复杂度分析、 数组和链表的对比
【数据结构与算法——TypeScript】 算法的复杂度分析 什么是算法复杂度(现实案例)? ❤️🔥 前面已经解释了什么是算法? 其实就是解决问题的一系列步骤操作、逻辑。 ✅ 对于同一个问题,我们往往有很多种解决思路和方法&#x…...

搜索综合训练
搜索综合训练 选数详细注释的代码 小木棍详细注释的代码 费解的开关详细注释的代码 选数 详细注释的代码 #include <iostream> #include <vector>using namespace std;// 判断一个数是否为素数 bool isPrime(int num) {if (num < 1)return false;// 判断从2到s…...
snowboy+新一代kaldi(k2-fsa)sherpa-onnx实现离线语音识别【语音助手】
背景 本系列主要目标初步完成一款智能音箱的基础功能,包括语音唤醒、语音识别(语音转文字)、处理用户请求(比如查天气等,主要通过rasa自己定义意图实现)、语音合成(文字转语音)功能。 语音识别、语音合成采用离线方式实现。 语…...

APT80DQ20BG-ASEMI快恢复二极管80A 200V
编辑:ll APT80DQ20BG-ASEMI快恢复二极管80A 200V 型号:APT80DQ20BG 品牌:ASEMI 芯片个数:双芯片 封装:TO-3P 恢复时间:≤50ns 工作温度:-55C~150C 浪涌电流:600A*2 正向电流…...

Go的任务调度单元与并发编程
摘要:本文由葡萄城技术团队于CSDN原创并首发。转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具、解决方案和服务,赋能开发者。 前言 本文主要介绍Go语言、进程、线程、协程的出现背景原因以及Go 语言如何解决协程的…...
PDFbox教程_编程入门自学教程_菜鸟教程-免费教程分享
教程简介 PDFBox是一个开源Java库,支持PDF文档的开发和转换.使用此库,您可以开发用于创建,转换和操作PDF文档的Java程序.除此之外,PDFBox还包括一个命令行实用程序,用于使用可用的PDF对PDF执行各种操作Jar文件. PDFB…...
Node.js-模块化理解及基本使用
模块化的定义 讲一个复杂的程序文件按照一定的规则拆分成多个独立的小文件,这些小文件就是小模块,这就是模块化。 每个小模块内部的数据是私有的,可以暴露内部数据给外部其他模块使用。 模块化优点 减少命名的冲突提高复用性提高可维护性按需…...
arguments 和 剩余参数
加油 !! 💕 文章目录 前言一、arguments二、arguments转成array三、箭头函数不绑定arguments四、剩余参数 ... 前言 其实在es6之后不推荐使用 arguments , 建议使用剩余参数 一、arguments arguments 是一个 对应于 传递给函数的参数 的 类数…...
【BASH】回顾与知识点梳理(十二)
【BASH】回顾与知识点梳理 十二 十二. Linux 文件与目录管理12.1 目录与路径相对路径与绝对路径相对路径的用途绝对路径的用途 12.2 目录的相关操作cd (change directory, 变换目录)pwd (Print Working Directory, 显示目前所在的目录)mkdir (make directory, 建立新目录)rmdir…...

本地构建包含java和maven的镜像
目录 1.前提条件 2.下载 2.1.创建Dockerfile 3.构建镜像 参考文章 1.前提条件 本地环境需要的系统和软件 win10 Docker Desktop Powershell 图1 Win10安装Docker后,直接在Powershell使用Docker命令 有些Developer不习惯win10系统,却想要使用Lin…...
Programming abstractions in C阅读笔记:p76-p83
《Programming Abstractions In C》学习第42天,p76-p73总结。 一、技术总结 1.数组和指针 在C语言中,数组和指针非常相似,相似到必须将它们同时考虑,当看到数组就应该想到指针,当看到指针就应该想到数组。示例…...
已解决(三个问题)|neo4j Failed authentication attempt for ‘meter‘ from 127.0.0.1
问题1 py2neo.errors.ConnectionUnavailable: Connection has been closed 问题2 neo4j Failed authentication attempt for ‘meter’ from 127.0.0.1 问题3 py2neo.errors,ClientError: [Security.Unauthorized] Invalid username or password. 作者:xiao黄 博客地址:http…...
多场景 OkHttpClient 管理器 - Android 网络通信解决方案
下面是一个完整的 Android 实现,展示如何创建和管理多个 OkHttpClient 实例,分别用于长连接、普通 HTTP 请求和文件下载场景。 <?xml version"1.0" encoding"utf-8"?> <LinearLayout xmlns:android"http://schemas…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)
概述 在 Swift 开发语言中,各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过,在涉及到多个子类派生于基类进行多态模拟的场景下,…...

理解 MCP 工作流:使用 Ollama 和 LangChain 构建本地 MCP 客户端
🌟 什么是 MCP? 模型控制协议 (MCP) 是一种创新的协议,旨在无缝连接 AI 模型与应用程序。 MCP 是一个开源协议,它标准化了我们的 LLM 应用程序连接所需工具和数据源并与之协作的方式。 可以把它想象成你的 AI 模型 和想要使用它…...

Linux-07 ubuntu 的 chrome 启动不了
文章目录 问题原因解决步骤一、卸载旧版chrome二、重新安装chorme三、启动不了,报错如下四、启动不了,解决如下 总结 问题原因 在应用中可以看到chrome,但是打不开(说明:原来的ubuntu系统出问题了,这个是备用的硬盘&a…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...

在Mathematica中实现Newton-Raphson迭代的收敛时间算法(一般三次多项式)
考察一般的三次多项式,以r为参数: p[z_, r_] : z^3 (r - 1) z - r; roots[r_] : z /. Solve[p[z, r] 0, z]; 此多项式的根为: 尽管看起来这个多项式是特殊的,其实一般的三次多项式都是可以通过线性变换化为这个形式…...
LRU 缓存机制详解与实现(Java版) + 力扣解决
📌 LRU 缓存机制详解与实现(Java版) 一、📖 问题背景 在日常开发中,我们经常会使用 缓存(Cache) 来提升性能。但由于内存有限,缓存不可能无限增长,于是需要策略决定&am…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...
面试高频问题
文章目录 🚀 消息队列核心技术揭秘:从入门到秒杀面试官1️⃣ Kafka为何能"吞云吐雾"?性能背后的秘密1.1 顺序写入与零拷贝:性能的双引擎1.2 分区并行:数据的"八车道高速公路"1.3 页缓存与批量处理…...