使用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 .

alt
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
alt

按提示下载,再次运行:

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

alt

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

这是因为:

alt

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

alt

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

alt

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

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

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

alt

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

alt

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

alt

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

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


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

alt alt

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


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

alt

通过调用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(255NOT 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 .

alt

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

alt

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


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

alt
alt

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

alt
alt

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

alt
alt

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

alt

项目使用


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

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

全部启动:

alt
alt
alt

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

调用add api,新增图书

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

alt

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


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

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

alt
alt

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

alt

完整项目代码[2]

参考资料

[1]

使用go-zero快速构建微服务: http://www.jikejiaocheng.com/c/gozero-microservices.html

[2]

完整项目代码: https://github.com/cuishuang/bookstore

本文由 mdnice 多平台发布

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/28674.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文件或目录损坏且无法读取

如上图报错,我们直接用cmd命令输入【CHKDSK C: /F】然后回车 电脑重启后可以了,希望能帮助各位小伙伴

算法通关村——迭代实现二叉树的前中后序遍历

前言 递归就是每次执行方法调用都会先把当前的局部变量、参数值和返回地址等压入栈中,后面在递归返回的时候,从栈顶弹出上一层的各项参数继续执行,这就是递归为什么能够自动返回并执行上一层的方法的原因。因此,我们也可以模拟一个…

git命令

1. git安装后-指定名称和邮箱 $ git config --global user.name "Your Name"$ git config --global user.email "emailexample.com" 2. 创建版本库 $ mkdir learngit //创建 $ cd learngit //使用 $ pwd //查看当前目录 $ git init //初始化&#xff0c…

HBase Shell 操作

1、基本操作 1.1、进入HBase客户端命令行 前提是先启动hadoop集群和zookeeper集群。 bin/hbase shell 1.2、查看帮助命令 helphelp 查看指定命令的语法规则 查看 list_namespace 的用法(‘记得加单引号’) help list_namespace 2、namespace 我们…

EVE-NG MPLS 静态 LSP

1 拓扑 2 配置步骤 2.1 配置接口IP 和路由 LER1 interface GigabitEthernet1/0ip address 10.1.1.1 255.255.255.0quitinterface GigabitEthernet2/0ip address 11.1.1.1 255.255.255.0quitip route-static 21.1.1.0 24 10.1.1.2VPC1 ip 11.1.1.100/24 11.1.1.1 配置完成后…

应用在室外LED电子显示屏中的MiniLED背光

LED电子显示屏是一种通过控制半导体发光二极管的显示方式,是由几万–几十万个半导体发光二极管像素点均匀排列组成。它利用不同的材料可以制造不同色彩的LED像素点,以显示文字、图形、图像、动画、行情、视频、录像信号等各种信息的显示屏幕。 LED显示屏…

web集群学习:基于nginx的反向代理和负载均衡

目录 一,反向代理 1,环境准备 2,配置代理服务器 3,在物理机上一管理员身份打开文本编辑器,编辑C:\Windows\System32\drivers\etc目录下的hosts文件 4,访问测试 5,查看日志,并记…

【100天精通python】Day30:使用python操作数据库_数据库基础入门

专栏导读 专栏订阅地址:https://blog.csdn.net/qq_35831906/category_12375510.html 1 数据库基础知识介绍 1.1 什么是数据库? 数据库是一个结构化存储和组织数据的集合,它可以被有效地访问、管理和更新。数据库的目的是为了提供一种可靠的…

React 脚手架

1.React 定义 React 脚手架(React boilerplate)是一种预先设置好的、可以快速启动 React 项目的工具。脚手架已经包含了 React、Webpack、Babel、ESLint、Jest 等一些常用的工具和库,并已经配置好了这些工具的参数,可以直接使用和…

vim的使用

vim文本编辑器 vim介绍命令模式光标移动选中内容复制内容粘贴内容删除撤销/恢复字符转换 编辑模式末行模式保存/退出查找行号显示文件切换 扩展 vim介绍 vim是Linux自带的文本编辑器,具有命令模式、编辑模式、末行模式三种模式。 模式间的切换: 命令模…

让三驾马车奔腾:华为如何推动空间智能化发展?

上个月,国务院常务会议审议通过了《关于促进家居消费的若干措施》,其中明确提出了“推动单品智能向全屋智能发展创新培育智能消费”“开展数字家庭建设试点”等推动全屋智能拼配发展的建议与方案。 可以说,以整屋为单位的空间智能品类&#x…

基于Java+SpringBoot+Vue的时间管理系统设计与实现(源码+LW+部署文档等)

博主介绍: 大家好,我是一名在Java圈混迹十余年的程序员,精通Java编程语言,同时也熟练掌握微信小程序、Python和Android等技术,能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架…

数据链路层概述

数据传输过程如下: 数据包按上述过程传输,详见(计算机网络概述三)。在分析数据链路层时可以假象成其沿着水平传播。 这三段链路层的传播方式可能会有所不同。 基本概念: 链路:指一个节点到相邻节点的一段物…

influxDB

文章目录 版本2.0 数据结构Organization 组织Bucket 存储桶Measurementtagfieldtimestamp retention policy (RP) 保留策略Point 一条数据Series 一组数据 写入gzip压缩 查询FluxInfluxQL 官网 https://docs.influxdata.com/v1.8 中文翻译文档 https://influxdb-v1-docs-cn.cno…

MFC第二十九天 CView类的分支(以及其派生类的功能)、MFC六大关键技术

文章目录 CView类的分支CEditViewCHtmlViewMainFrm.h CMainFrame 类的接口CMainView .h CListCtrl与CListView的创建原理 CTreeViewCTreeCtrl类简介CTreeCtrl类的原理以及常用功能 MFC六大关键技术视图和带分割栏的框架开发与消息路由CLeftView.cppCRightView.hCRightView.cppC…

在Vue里,将当前窗口截图,并将数据base64转为png格式传给服务器

目录 前言 1、将当前窗口截图,并将数据存储下来 2、定义将base64转png的方法 3、完整代码 总结 前言 记录来源于需求 1、将当前窗口截图,并将数据存储下来 export default { data() {return {image: // 存储数据} }mounted() {setTimeout(() >…

Flink多流处理之connect拼接流

Flink中的拼接流connect的使用其实非常简单,就是leftStream.connect(rightStream)的方式,但是有一点我们需要清楚,使用connect后并不是将两个流给串联起来了,而是将左流和右流建立一个联系,作为一个大的流,并且这个大的流可以使用相同的逻辑处理leftStream和rightStream,也可以…

Python基础算法训练——函数与递归(51~55)

Python基础算法训练——函数与递归(51~55)51. 回文平方数 【题目描述】 回文数是指从左向右念和从右向左念都一样的数。如 12321 就是一个典型的回文数。 给定一个进制 B(2≤B≤20,由十进制表示),输出所有的大于等于 1 小于等于 300(十进制下)且它的平方用 B 进制表示时是…

java 合并两个List<String>

在 Java 中合并两个 List<String> 可以使用 addAll 方法或者使用 Stream 的 concat 方法。以下是两种常见的实现方法&#xff1a;1. 使用 addAll 方法&#xff1a; java List<String> list1 new ArrayList<>(Arrays.asList("A", "B", &…

《学习笔记》NC文件提取前-了解NC文件基本属性信息

NC文件提取前-了解NC文件基本属性信息 NetCDF是一组软件库和独立于机器的数据格式&#xff0c;支持创建、访问和共享面向阵列的科学数据。 在NC文件提取前&#xff0c;我们需要了解NC文件的基本信息&#xff1a;比如属性、维度、变量以及每个变量的基本情况。了解这些信息方便…