/cmd 函数入口
--/app_1
--/app_2
--/crontab
/bootstrap 项目依赖启动,统一管理,不分散init
/configs 项目配置文件
/docs rpc规定文件、项目介绍文档
/gen rpc生成工具
/internal
--/middleware 中间件
--/models 结构体定义
--/handler 控制器,参数解析
--/service
----/order 订单服务
----/user 用户服务
--/router 路由管理
/server 管理不同协议启动的入口
--/http
--/websocket
--/rpc
--/mqtt
/pkg
-- /logger 自定义日志包
-- /database 数据库连接方法,参数传入连接信息
-- /errors 错误码管理
-- /thirdpary 第三方服务
/logs 存放日志信息
DDD需要拆分模块通信。
枚举错误码:
const (
// ValidateErr 校验错误
ValidateErr = iota + 40000
// RequireAuthErr 没有权限
RequireAuthErr
// NotFoundErr 没有记录
NotFoundErr
...
)
错误信息关键结构体
type bizErrWithCode struct {
code int
msg string
cause error
}
func Wrap(err error, code int, msg string) error {
if err == nil {
return nil
}
if e, ok := err.(*bizErrWithCode); ok {
return &bizErrWithCode{
code: e.code,
msg: msg,
cause: err,
}
}
return &bizErrWithCode{
code: code,
msg: msg,
cause: err,
}
}
Wrap 可以调用第三方包的i18配置,解析国际化配置文件信息
使用dfs算法,配合Go反射,匹配结构体类型,解析需要过滤的字段filterPhoto,过滤敏感字段
// sep 替换字符
const sep = "."
// MaxDepth 最大匹配深度
const MaxDepth = 8
// 需要匹配的字段
const (
filterPhoto = ".payload.params.photo" //微信上传的
)
单例模式开启日志记录,定义结构体,包含TraceID、RequestID、UserID、TenantID字段,通过context传递部分信息,转json存储
// 小写开头 单例获取
var iLog *logrus.Logger
// InitLog 交付给 bootstrap 初始化
func InitLogger() {
iLog = newLog("log/debug")
}
// newLog 新建日志对象
func newLog(logpath string) *logrus.Logger {
// 实例化
logger := logrus.New()
...
}
- gofmt,强制格式化
- 增加.golangci.yml配置,pre-commit自动检查
- 代码审核的时候检查,统一规范文档
http层,中间件解析,根据登录信息,context 自动携带 TenantID,Repository、Service层可以通过 context 传递读取 gorm,可以注入Scope兜底
func TenantScope(ctx context.Context) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
tenantID := tenancy.MustTenantID(ctx)
return db.Where("tenant_id = ?", tenantID)
}
}
成熟方案:Go,Casbin
RBAC(Role-Based Access Control,基于角色的权限控制)是一种通过角色将用户和权限连接起来的权限管理方法
用户表、角色表、接口表、角色接口权限表
支付必须以 支付单号 为核心幂等键
创建订单,防止重复创建,请求携带nonce,中间件5分钟过期+订单创建前检查
- 查询订单是否已支付
- 查询是否已有支付单
- 没有则创建 payment_no
MQ消费,需要建立消费表,保障消息不会重复消费、投递成功,和订单表处理时,携带事务。
1、前端抢购锁定按钮,不要重复触发 2、redis库存预扣减 3、MQ异步创建订单落库
request_id + tenant_id + sku_id + order_id 保持幂等
死信队列做超时未支付处理
- 先查缓存
- 缓存命中,直接返回
- 缓存未命中,查数据库
- 数据库结果写入缓存
- 返回数据
import _ "net/http/pprof"
go func() {
http.ListenAndServe("127.0.0.1:6060", nil)
}()
curl -o goroutine-1.txt http://127.0.0.1:6060/debug/pprof/goroutine?debug=2
1、数据的上下层传递,链路追踪 2、超时关闭整条链路
大对象不适合通过context传递
1、发布migration,数据库表版本自动更新 2、蓝绿版本,nginx平滑重启 3、docker容器平滑重启
编码模型:Codex/GPT、Claude
- 数据库的所有操作
- 订单数据的所有操作,涉及金融金钱的修改
- 写文件的操作
- 用户敏感信息的操作
曾经在3个月内重构了3年历史的Go项目
优先保障业务运行,日常的迭代开发。闲暇时间,根据业务影响情况,优先拆分杂糅的目录结构。通过新的脚手架目录,先完成外部流量的引导。然后再逐步拆分项目,完成重构。
团队整体技术水平情况,是否选用Gin,goMicro,是否微服务拆分
人员少情况:单体服务,monorepo,不拆分微服务,不使用DDD,优先使用Gin等框架 人员多,且跨部门开发语言不一致,使用RPC拆分 人员多,且负责业务不一样,使用微服务拆分,选用效率高的,支持面广的技术框架,例如Thift
优先保障业务上线,技术债务根据影响面情况解决,如果影响整体进度,影响交付的,则会上提出需要解决
如果业务影响面小,回报效益大的,可以处理优化任务
一年内,肯定就是 Claude GPT等这些大模型了,编码模式全部变了,以往要手敲代码,现在是复古程序员了。