进程虚拟地址空间
- Code Segment 代码段(程序要执行的指令)
- Data Segment 数据段(全局变量、静态数据)
- Heap 堆(需要程序手动释放)(c\c++ 手动垃圾回收容易出现 悬挂指针-释放早了、内存泄漏-忘了释放)
- Stack 栈(函数局部变量、参数和返回值)函数调用完成后销毁(随着函数调用栈的销毁而释放内存)
栈、Data Segment 数据段上的对象作为root
基于它们 2个追踪
能追踪到的数据就代表是存活有引用的数据
栈、Data Segment 数据段上的对象作为root
基于它们 2个追踪
能追踪到的数据就代表是存活有引用的数据
// 发送
chan <- xxx
// 接收
<- chan
// 关闭
close(chan)
// 缓冲区
make(chan int,5)
这篇文章写的太细致了,amazing
// InitJaeger 初始化一个opentracing.Tracer链路追踪实例
// 100%的请求都会记录跨度
// 初始化jaeger的指标
func InitJaeger(service string) (opentracing.Tracer, io.Closer) {
cfg := &jaegerConfig.Configuration{
ServiceName: service, // 指定了要被追踪的服务的名称
Sampler: &jaegerConfig.SamplerConfig{
// 采用恒定采样策略
// 意味着对于每一个请求或操作,都会按照固定的方式决定是否进行追踪
Type: "const",
// 与 Type 字段配合,决定具体的采样行为
Param: 1,
},
Reporter: &jaegerConfig.ReporterConfig{
// 设置为 true,表示要记录追踪的跨度(Span)信息到日志中
LogSpans: true,
// 指定了 Jaeger 收集器(Collector)的端点地址
// 追踪数据最终需要发送到 Jaeger 的收集器进行处理和存储
// 通过设置这个字段,告诉程序将追踪数据发送到哪里
// 指定客户端(应用程序)将追踪数据发送到的目标地址,即 Jaeger 收集器(Collector)的端点
// TODO 客户端主动push
// Jaeger 的这种配置下,是客户端主动将数据推送给收集器,
// 这种方式使得客户端对数据的发送有更多的控制权,能够根据自身的情况(如数据量、网络状况等)来决定何时发送数据
// 而不是等待服务端来请求
// TODO 需要做优化更改为kafka - 异步PUSH
CollectorEndpoint: config.Conf.JaegerConfig.Addr,
// 可选项
// 如果不配置CollectorEndpoint的话就需要配置这个Agent代理人端口
// jaeger-agent会接收这些数据,进行缓冲和批量处理后,再发送给jaeger-collector
// 将数据发送到jaeger - agent的6831/udp端口
LocalAgentHostPort: "127.0.0.1:6831",
},
}
// 基于前面初始化好的配置结构体 cfg
// 使用 NewTracer 方法来创建一个 Jaeger 追踪器(Tracer)以及一个用于关闭追踪器相关资源的函数 closer
// jaegerConfig.Logger(jaeger.StdLogger) 是在为追踪器设置日志记录器
tracer, closer, err := cfg.NewTracer(jaegerConfig.Logger(jaeger.StdLogger))
if err != nil {
logger.Fatal(err)
}
return tracer, closer
}
// 创建一个 Jaeger 追踪器(Tracer)
tracer, _ := InitJaeger(fmt.Sprintf("%s:%s",
config.Conf.Application.Name,
config.Conf.Application.Version))
var mutex sync.Mutex
mutex.Lock()
// 访问共享资源的临界区代码
mutex.Unlock()
枚举值注释必须完善
禁止出现硬编码
有基本的单元测试验证(尽量使用断言而不是手动log人工判定单测是否通过)
接口粒度应该细小、接口依赖应该明确、接口入参出参应该明确,对象化,最好可以做到依赖抽象而不是直接依赖具体的实现
禁止在接口之中有隐藏的依赖条件(入参之中没有的参数比如环境变量,入参应该是决定输出的唯一条件)
接口颗粒度要小一些(入参、出参尽可能简化易读),如果逻辑过于复杂应该对象化拆分,确保大接口拆出的小接口都走单元测试
禁止IDE右侧出现notice黄色告警(单词拼写错误、命名不规范、公有方法没注释等)
所有的指针访问都需要判定是否为nil
所有的数组访问都需要判定length防止数组越界
所有的除数都要判定被除数不是0
关于同步转异步,取决于业务场景。如果是接口响应不强依赖执行结果的,比如用户关注频道后发送欢迎语,关注成功后异步发送欢迎语即可,不能阻塞主要流程,因为用户端不强依赖欢迎语,但是需要快速进入主页面。
包命名规范
禁止滥用init,个人建议整个项目不应该有init,main函数也是唯一的执行入口
禁止滥用全局变量,应该保证依赖的关系足够清晰明了
模块依赖管理,包与包之间的依赖关系清晰(按功能分块按业务水平分层,按数据流垂直分层,每一层之间尽可能解耦),比如三层架构、领域驱动模型等都是追求分层,数据对象模型每一层之间都是解耦的,比如vo(view object视图)和持久化层对象po(数据库映射对象)肯定是转换过的,而不是直接将po往http对外接口抛。
模块依赖管理,包的依赖应当尽可能放在私有属性(并且以选项模式注入依赖)。优点在于依赖会更加清晰容易管理,以及对于单元测试会友好很多。比如:
├── application | 应用入口|可以理解为三层架构之中的 UI 表示层
│ ├── admin_service|实现后台管理系统CRUD当前微服务数据需要用到的 RPC 接口
│ │ ├── dto | 数据传输对象实体
│ │ └── grpc | RPC接口实现
│ ├── event | 内置事件 | 比如定时器 | 统一实现一个抽象 interface{ start stop } | 这里注意下就是这个包是不允许有init函数的,禁止自身调用自身,它的调用必须显式地写在 applicition.Init或者编译入口main函数之中
│ └── front_service
│ ├── dtos | 数据传输对象对外的restful接口用到的对象
│ └── http | controller层 | 如果涉及2个领域比如用户中心领域和商户领域 | 在这里将2个领域组装数据 | 这里获取到的各个domain object是一个复杂的对象转
│ └────── init.go | 有一个包变量App结构体 | 首先初始化db\kafka\redis\mongodb\es、再初始化domain service对象,然后以选项模式注入DB对象到领域对象,这里的init也不是用go的init而是Init暴露的方法 ,我这里几乎是禁止用隐藏的init函数
换为api 用的dto
├── config
│ └── files | 里面只有1个.toml文件 | 目前已经弃用 | 配置已经改用从nacos读取
├── domain
│ ├── common | 通用域 | 通用域是被其他域依赖 | 依赖注入也是选项模式注入到其他领域的私有属性 | 域之间是互相独立的互不访问的而通用域则可以注入进其他领域 | 还有支撑域等我这里分的没有那么细致
│ │ ├── entity | 域实体 domain.object 领域对象 | 当前的领域对外暴露的实体
│ │ └── repository | 仓储层
│ │ └──----------- po.go | persistent object 持久化层对象
│ │ └──----------- repository.go | 仓储层对外提供的接口的抽象
│ │ └──----------- repository_realization1.go | 仓储层CRUD的实现一 | 基于MySQL
│ │ └──----------- repository_realization2.go | 仓储层CRUD的实现二 | 基于Oracle | 正常只有1个实现
│ │ service.go | 领域对外暴露的服务 - 同样需要抽象和实现 | 这里有很多的 po 转 do 的操作
│ ├── user
│ │ ├── entity
│ │ └── repository
│ │ service.go
│ ├── good
│ │ ├── entity
│ │ ├── enum
│ │ └── repository
│ │ service.go
├── global | 全局的工具类 - 偏业务 | 可以被domain\event依赖的
│ ├── cache | 缓存key管理
│ ├── enum | 一些通用的枚举值
│ └── router | 路由 | restful
├── cmd | 运行入口 main.go | 运行逻辑大致是 配置加载 > application.Init > rpc.server.register > prometheus/event > run
├── interfaces
└── tools | 工具类 - 剥离业务 - 一小部分特有的 | 大部分的util工具被封装到团队独立的git仓库 | go.mod引用爱用哪个版本用哪个
└── utils