golang-go-zero-微服务实践-微服务实战系列
本系列为作者阅读公众号微服务实践的文章做的笔记
另外go-zero作者kevwan大佬的learnku博客也是同步更新的
工程结构
每个服务目录下我们又会分为多个服务,主要会有如下几类服务:
- api - 对外的BFF服务,接受来自客户端的请求,暴露HTTP接口
- rpc - 对内的微服务,仅接受来自内部其他微服务或者BFF的请求,暴露gRPC接口
- rmq - 负责进行流式任务处理,上游一般依赖消息队列,比如kafka等
- admin - 也是对内的服务,区别于rpc,更多的是面向运营侧的且数据权限较高,通过隔离可带来更好的代码级别的安全,直接提供HTTP接口
日志定义
|
|
服务依赖
yaml配置中给依赖rpc或注册中心设置NonBlock实现不等待依赖启动
|
|
并行调用
图片上传
在yaml配置文件里面配置第三方OSS的三个属性
|
|
在config.go里面添加接收参数
|
|
在svcCtx里面添加oss客户端
|
|
之后就可以在logic里面使用oss客户端上传图片了
|
|
上传图片前需要先自行创建bucket
缓存代码怎么写
基本使用
goctl生成model时添加-c参数
yaml配置文件
|
|
svc添加字段
|
|
缓存雪崩
两种类型
- 大量的数据同时过期:避免大量的数据设置相同的过期时间,在过期时间上加一个较小的随机数
- Redis出现了宕机:让数据库支持熔断,压力较大时丢弃部分请求
缓存击穿
热点数据过期失效:每次查询缓存的时候使用Expire给缓存续期,不存在时singleflight查询数据库
缓存穿透
访问的数据既不在缓存中,也不在数据库中:缓存一个空值,避免每次都透传到数据库
缓存一致性保证
先删缓存再更新数据库
删除和更新之间被其他线程读取会留存旧值:延时双删,更新完数据库的值后,sleep一小段时间,再进行一次缓存删除操作
先更新数据库再删除缓存
推荐方式,删除缓存的动作很快
重试机制
使用消息队列来保证正确删除缓存
并发读写
由于读取不在缓存中的数据的过程是读取数据库然后插入缓存,所以可能会出现抹去修改的情况:将读取缓存的set cache操作换成add cache,只有在缓存不存在的时候才成功写入
优化高并发
本地缓存
go-zero的collection中提供了Cache来实现本地缓存的功能,Cache提供了Get和Set方法
|
|
自动识别热点数据
滑动窗口统计请求次数,超过阈值升级为本地缓存
处理每秒上万次的下单请求
处理热点数据
优化:在内存中缓存热点数据
限制:限制单用户的请求次数,超过限制直接返回错误
隔离:服务隔离,即秒杀功能独立为一个服务,使用单独的Redis集群和单独的Mysql
流量削峰
消息队列
保证消息只被消费一次
kafka是能够保证"At Least Once"的机制的,消息不会丢失,但有可能会导致重复消费
保证在消息的生产和消费的过程是幂等的,不要出现相对加减,直接赋值
极致优化秒杀性能
批量数据聚合
批量数据聚合:减少网络IO和磁盘IO成本,将多条数据(如100条)聚合后进行处理。之前是1条对应一次网络IO和磁盘IO,现在就是100条对应1次
降低消息的消费延迟
- 增加消费者的数量
- 在一个消费者中增加消息处理的并行度
保证不会超卖
分布式锁