

还在手撸Go微服务?快来试试go-zero,超乎你的想象!
source link: https://studygolang.com/articles/30535
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Github
https://github.com/tal-tech/go-zero
快速构建高并发微服务
0. 为什么说做好微服务很难?
要想做好微服务,我们需要理解和掌握的知识点非常多,从几个维度上来说:
-
基本功能层面
- 并发控制&限流,避免服务被突发流量击垮
- 服务注册与服务发现,确保能够动态侦测增减的节点
- 负载均衡,需要根据节点承受能力分发流量
- 超时控制,避免对已超时请求做无用功
- 熔断设计,快速失败,保障故障节点的恢复能力
-
高阶功能层面
- 请求认证,确保每个用户只能访问自己的数据
- 链路追踪,用于理解整个系统和快速定位特定请求的问题
- 日志,用于数据收集和问题定位
- 可观测性,没有度量就没有优化
对于其中每一点,我们都需要用很长的篇幅来讲述其原理和实现,那么对我们后端开发者来说,要想把这些知识点都掌握并落实到业务系统里,难度是非常大的,不过我们可以依赖已经被大流量验证过的框架体系。 go-zero微服务框架 就是为此而生。
另外,我们始终秉承 工具大于约定和文档 的理念。我们希望尽可能减少开发人员的心智负担,把精力都投入到产生业务价值的代码上,减少重复代码的编写,所以我们开发了 goctl
工具。
下面我通过短链微服务来演示通过 go-zero 快速的创建微服务的流程,走完一遍,你就会发现:原来编写微服务如此简单!
1. 什么是短链服务?
短链服务就是将长的URL网址,通过程序计算等方式,转换为简短的网址字符串。
写此短链服务是为了从整体上演示go-zero构建完整微服务的过程,算法和实现细节尽可能简化了,所以这不是一个高阶的短链服务。
2. 短链微服务架构图
Transform RPC
3. goctl各层代码生成一览
所有绿色背景的功能模块是自动生成的,按需激活,红色模块是需要自己写的,也就是增加下依赖,编写业务特有逻辑,各层示意图分别如下:
- API Gateway
- RPC
- model
下面我们来一起完整走一遍快速构建微服务的流程,Let’s Go
!:runner:♂️
4. 准备工作
- 安装etcd, mysql, redis
-
安装goctl工具
GO111MODULE=on GOPROXY=https://goproxy.cn/,direct go get -u github.com/tal-tech/go-zero/tools/goctl
- 创建工作目录
shorturl
- 在
shorturl
目录下执行go mod init shorturl
初始化go.mod
5. 编写API Gateway代码
-
通过goctl生成
api/shorturl.api
并编辑,为了简洁,去除了文件开头的info
,代码如下:type ( expandReq struct { shorten string `form:"shorten"` } expandResp struct { url string `json:"url"` } ) type ( shortenReq struct { url string `form:"url"` } shortenResp struct { shorten string `json:"shorten"` } ) service shorturl-api { @server( handler: ShortenHandler ) get /shorten(shortenReq) returns(shortenResp) @server( handler: ExpandHandler ) get /expand(expandReq) returns(expandResp) }
type用法和go一致,service用来定义get/post/head/delete等api请求,解释如下:
service shorturl-api { @server handler get /shorten(shortenReq) returns(shortenResp)
-
使用goctl生成API Gateway代码
goctl api go -api shorturl.api -dir .
生成的文件结构如下:
. ├── api │ ├── etc │ │ └── shorturl-api.yaml // 配置文件 │ ├── internal │ │ ├── config │ │ │ └── config.go // 定义配置 │ │ ├── handler │ │ │ ├── expandhandler.go // 实现expandHandler │ │ │ ├── routes.go // 定义路由处理 │ │ │ └── shortenhandler.go // 实现shortenHandler │ │ ├── logic │ │ │ ├── expandlogic.go // 实现ExpandLogic │ │ │ └── shortenlogic.go // 实现ShortenLogic │ │ ├── svc │ │ │ └── servicecontext.go // 定义ServiceContext │ │ └── types │ │ └── types.go // 定义请求、返回结构体 │ ├── shorturl.api │ └── shorturl.go // main入口定义 ├── go.mod └── go.sum
-
启动API Gateway服务,默认侦听在8888端口
go run shorturl.go -f etc/shorturl-api.yaml
-
测试API Gateway服务
curl -i "http://localhost:8888/shorten?url=http://www.xiaoheiban.cn"
返回如下:
HTTP/1.1 200 OK Content-Type: application/json Date: Thu, 27 Aug 2020 14:31:39 GMT Content-Length: 15 {"shortUrl":""}
可以看到我们API Gateway其实啥也没干,就返回了个空值,接下来我们会在rpc服务里实现业务逻辑
internal/svc/servicecontext.go internal/logic goctl
6. 编写transform rpc服务
-
在
rpc/transform
目录下编写transform.proto
文件可以通过命令生成proto文件模板
goctl rpc template -o transform.proto
修改后文件内容如下:
syntax = "proto3"; package transform; message expandReq { string shorten = 1; } message expandResp { string url = 1; } message shortenReq { string url = 1; } message shortenResp { string shorten = 1; } service transformer { rpc expand(expandReq) returns(expandResp); rpc shorten(shortenReq) returns(shortenResp); }
-
用
goctl
生成rpc代码,在rpc/transform
目录下执行命令goctl rpc proto -src transform.proto
文件结构如下:
rpc/transform ├── etc │ └── transform.yaml // 配置文件 ├── internal │ ├── config │ │ └── config.go // 配置定义 │ ├── logic │ │ ├── expandlogic.go // expand业务逻辑在这里实现 │ │ └── shortenlogic.go // shorten业务逻辑在这里实现 │ ├── server │ │ └── transformerserver.go // 调用入口, 不需要修改 │ └── svc │ └── servicecontext.go // 定义ServiceContext,传递依赖 ├── pb │ └── transform.pb.go ├── transform.go // rpc服务main函数 ├── transform.proto └── transformer ├── transformer.go // 提供了外部调用方法,无需修改 ├── transformer_mock.go // mock方法,测试用 └── types.go // request/response结构体定义
直接可以运行,如下:
$ go run transform.go -f etc/transform.yaml Starting rpc server at 127.0.0.1:8080...
etc/transform.yaml
文件里可以修改侦听端口等配置
7. 修改API Gateway代码调用transform rpc服务
-
修改配置文件
shorturl-api.yaml
,增加如下内容Transform: Etcd: Hosts: localhost:2379 Key: transform.rpc
注意:这个网站md支持不友好,localhost:2379原文为
- localhost:2379
-
修改
internal/config/config.go
如下,增加transform服务依赖type Config struct { rest.RestConf Transform rpcx.RpcClientConf // 手动代码 }
-
修改
internal/svc/servicecontext.go
,如下:type ServiceContext struct { Config config.Config Transformer transformer.Transformer // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ Config: c, Transformer: transformer.NewTransformer(rpcx.MustNewClient(c.Transform)), // 手动代码 } }
通过ServiceContext在不同业务逻辑之间传递依赖
-
修改
internal/logic/expandlogic.go
里的Expand
方法,如下:func (l *ExpandLogic) Expand(req types.ExpandReq) (*types.ExpandResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Expand(l.ctx, &transformer.ExpandReq{ Shorten: req.Shorten, }) if err != nil { return nil, err } return &types.ExpandResp{ Url: resp.Url, }, nil // 手动代码结束 }
通过调用 transformer
的 Expand
方法实现短链恢复到url
-
修改
internal/logic/shortenlogic.go
,如下:func (l *ShortenLogic) Shorten(req types.ShortenReq) (*types.ShortenResp, error) { // 手动代码开始 resp, err := l.svcCtx.Transformer.Shorten(l.ctx, &transformer.ShortenReq{ Url: req.Url, }) if err != nil { return nil, err } return &types.ShortenResp{ Shorten: resp.Shorten, }, nil // 手动代码结束 }
通过调用 transformer
的 Shorten
方法实现url到短链的变换
至此,API Gateway修改完成,虽然贴的代码多,但是期中修改的是很少的一部分,为了方便理解上下文,我贴了完整代码,接下来处理CRUD+cache
8. 定义数据库表结构,并生成CRUD+cache代码
- shorturl下创建
rpc/transform/model
目录:mkdir -p rpc/transform/model
-
在rpc/transform/model目录下编写创建shorturl表的sql文件
shorturl.sql
,如下:CREATE TABLE `shorturl` ( `shorten` varchar(255) NOT NULL COMMENT 'shorten key', `url` varchar(255) NOT NULL COMMENT 'original url', PRIMARY KEY(`shorten`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-
创建DB和table
create database gozero;
source shorturl.sql;
-
在
rpc/transform/model
目录下执行如下命令生成CRUD+cache代码,-c
表示使用redis cache
goctl model mysql ddl -c -src shorturl.sql -dir .
也可以用
datasource
命令代替ddl
来指定数据库链接直接从schema生成生成后的文件结构如下:
rpc/transform/model ├── shorturl.sql ├── shorturlmodel.go // CRUD+cache代码 └── vars.go // 定义常量和变量
9. 修改shorten/expand rpc代码调用crud+cache代码
-
修改
rpc/transform/etc/transform.yaml
,增加如下内容:DataSource: root:@tcp(localhost:3306)/gozero Table: shorturl Cache: Host: localhost:6379
注意:
Host: localhost:6379
原文为- Host: localhost:6379
,为了避免md支持不友好问题可以使用多个redis作为cache,支持redis单点或者redis集群
-
修改
rpc/transform/internal/config.go
,如下:type Config struct { rpcx.RpcServerConf DataSource string // 手动代码 Table string // 手动代码 Cache cache.CacheConf // 手动代码 }
增加了mysql和redis cache配置
-
修改
rpc/transform/internal/svc/servicecontext.go
,如下:type ServiceContext struct { c config.Config Model *model.ShorturlModel // 手动代码 } func NewServiceContext(c config.Config) *ServiceContext { return &ServiceContext{ c: c, Model: model.NewShorturlModel(sqlx.NewMysql(c.DataSource), c.Cache, c.Table), // 手动代码 } }
-
修改
rpc/transform/internal/logic/expandlogic.go
,如下:func (l *ExpandLogic) Expand(in *expand.ExpandReq) (*expand.ExpandResp, error) { // 手动代码开始 res, err := l.svcCtx.Model.FindOne(in.Shorten) if err != nil { return nil, err } return &transform.ExpandResp{ Url: res.Url, }, nil // 手动代码结束 }
-
修改
rpc/shorten/internal/logic/shortenlogic.go
,如下:func (l *ShortenLogic) Shorten(in *shorten.ShortenReq) (*shorten.ShortenResp, error) { // 手动代码开始,生成短链接 key := hash.Md5Hex([]byte(in.Url))[:6] _, err := l.svcCtx.Model.Insert(model.Shorturl{ Shorten: key, Url: in.Url, }) if err != nil { return nil, err } return &transform.ShortenResp{ Shorten: key, }, nil // 手动代码结束 }
至此代码修改完成,凡事手动修改的代码我加了标注
10. 完整调用演示
-
shorten api调用
curl -i "http://localhost:8888/shorten?url=http://www.xiaoheiban.cn"
返回如下:
HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:49:49 GMT Content-Length: 21 {"shorten":"f35b2a"}
-
expand api调用
curl -i "http://localhost:8888/expand?shorten=f35b2a"
返回如下:
HTTP/1.1 200 OK Content-Type: application/json Date: Sat, 29 Aug 2020 10:51:53 GMT Content-Length: 34 {"url":"http://www.xiaoheiban.cn"}
11. Benchmark
因为写入依赖于mysql的写入速度,就相当于压mysql了,所以压测只测试了expand接口,相当于从mysql里读取并利用缓存,shorten.lua里随机从db里获取了100个热key来生成压测请求
可以看出在我的MacBook Pro上能达到3万+的qps。
12. 完整代码
https://github.com/tal-tech/go-zero/tree/master/example/shorturl
12. 总结
我们一直强调 工具大于约定和文档 。
go-zero不只是一个框架,更是一个建立在框架+工具基础上的,简化和规范了整个微服务构建的技术体系。
我们在保持简单的同时也尽可能把微服务治理的复杂度封装到了框架内部,极大的降低了开发人员的心智负担,使得业务开发得以快速推进。
通过go-zero+goctl生成的代码,包含了微服务治理的各种组件,包括:并发控制、自适应熔断、自适应降载、自动缓存控制等,可以轻松部署以承载巨大访问量。
有任何好的提升工程效率的想法,随时欢迎交流!:clap:
有疑问加站长微信联系

Recommend
-
118
正在阅读: 【图解】儿童触网低龄化超乎你想象【图解】儿童触网低龄化超乎你想象3岁小孩已经会用QQ与微信,1/5学龄前儿童日均上网超过半个小时。
-
91
新浪科技讯北京时间11月17日下午消息,特斯拉于北京时间今日中午发布了众人期待已久的特斯拉Semi电动卡车,此外还给了一个惊喜——升级版特斯拉Roadster电动跑车。特斯拉Semi电动卡车特斯拉的发布会还是和以往一样气势炫酷。在特斯拉卡车
-
15
厉害,超乎想象,鹅厂程序员占比近 7 成非著名程序员公众号「非著名程序员」主理人,程序员/复业者/生涯规划师
-
3
你还在手撕微服务?快试试 go-zero 的微服务自动生成 mob604756f0bbf4 · 大约7小时之前 · 9 次点击 · 预计阅读时间 1 分钟 · 大约8小时之前 开始浏览 ...
-
7
玩家手里的卡牌,可能有超乎想象的价值本文来自微信公众号:触乐(ID:chuappgame),作者:杨宗硕,题图来自:unsplash最近一年,有不少关于卡牌交易的新闻被报道出来。像是...
-
9
拉长时间轴来看,苹果的能量仍将超乎你的想象! 2021年08月05日14:16 智通财经APP 我有话说(0人参与) 收藏本文 ...
-
4
机械硬盘随机IO慢的超乎你的想象 Original...
-
10
新农民超乎你想象!90后学霸用AI种番茄_新浪财经_新浪网 股市瞬息万变,投资难以决策?来#A股参谋部#超话聊一聊,
-
6
30系显卡库存超乎想象!曝RTX 4080/4070/4060被叫停:今年不发了 2022-07-17 22:29 出处/作者:快科技 整合编辑:佚名 0
-
1
超乎想象,原来防晒霜广告还能这么玩?! 作者: River ...
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK