1.go工程化实践之前置知识
(1)传统mvc架构
即model view controller
- controller contains control logic
- model define data struct
(2)MVC -> DLC
- model -> dao(data access object)
- controller -> logic,control
(3)DDD驱动
- 贫血模式
- controller->logic->dao
- 业务逻辑都在logic层中
- 充血模式
- 解释比较抽象:如下
- 要让domain object 即 entity 有更多逻辑
- 通过聚合来组合entity的逻辑
- 实际的例子
public void saveCustomer { // 主流程根据不同的用例选择执行不同的对象函数 // 而不是用数据建模,将所有处理集中在一个函数中 public void updateColumnA(); public void updateColumnB(); public void updateColumnC(); }
(4)SOLID模式 接口隔离,依赖倒置等
// 业务层与model层之间用interface来做隔离,实现依赖导致
class OrderService {
public function saveOrder($order OrderInterface){
$order->save();
}
}
interface OrderInterface(){
public function save();
}
2.go工程化实践之架构实践
(1)整洁架构
-
核心观点:
- 不与框架绑定
// ctx不能与gin.context绑定,可以切换框架 // req需要再封装一层,不能直接与http.request绑定 func GetOrderHandler(ctx context.context, req interface) error { return nil }- 可测试
// 在没有ui,database环境,web server等外部环境的前提下做到可测试 // 1.基于mock实现 // 2.基于go monkeypatching实现 // 3.第三方类库mock例如: etcd mock,redis client mock // 4.标准库mock例如:httptest,gomock,testify- 不与ui绑定
// 可以在ui界面在命令行界面且切换- 不与具体数据库绑定
type OrderRespository interface{ // 抽象出接口层 // 业务依赖于接口 Get(ctx context.context, id int) ( *domain.Order, error ) }- 不依赖任何外部代理
-
具体案例
github.com/eminetto/clean-architecture-go-v2github.com/bxcodec/go-clean-arch
3.DDD领域驱动工程化架构
(1)基本名称
- 战略设计
- 整个软件系统的架构以及组织
- 战术设计
- 名词:值对象 Value Object,实体 Entity,聚合 Aggregate,聚合根 Aggregate Root,仓储 Repository,服务等概念
(2)实践
- 架构:六边形架构,即端口适配器架构。Ports:Interface,Adapter:Instance
- 值对象:例如,1,2,3共同组成了一组值对象
- Entity:实体的每个属性都可以理解为值对象
type Demo struct{ Id Name Gender }
- Aggregate
type Collection stuct { Id Tabs []Demo Created }- 聚合的粒度会比Entity更粗一点,由Entity以及值对象组成
- 聚合需要对聚合内的数据一致性负责
- 聚合外的一致性采用最终一致性保证
- 微软的聚合举例:
4.实践设计一个中台架构
(1)go在数据中台中的应用
- Apache Beam beam.apache.org 流式计算
- Go Mysql(解析binlog)
- log agent(收集日志)
(2)非典型的数据中台的迭代过程
- API要做到无状态,把数据的CRUD做成一个服务
type HashKey struct {
StorageType string
storageInternalType string
}
type Storate Interface {
func Get()
func Set()
func Incr()
}
- 将用户需要的指标定义为特征,管理在数据库中 例如:使用jpath包管理这些特征
// json:`{errcode:0,data:{game:alex,profile:{age:12}}}`
type TestJpath struct{
age int `jpath:data.profile.age`
}
- 当特征与特征之间产生依赖的时候
- 例如:查找订单的时候,需要查找用户绑定的取件人。 我们首先拿到的是订单记录的uid特征,根据uid特征查找到取件人特征uid,根据取件人uid再获取取件人信息。批量获取特征时,整个执行过程就是从叶子节点开始求值,直到根节点,这是一个拓扑排序的过程
- 拓扑排序的同源合并, 可以参考 文章(拓扑排序的同源合并以及存储实践)
- 输入参数一致,特征存储类型均为hash(有主键),特征的key template一致(都为key:value结构或者都为key:arr结构),特征的集群一致(例如:在一个redis集群中)
- 例如下面图示中,b,d这一层,在设计的时候
-
中台的配置管理,接入门户,流式计算,统一查询,DSL/UDF
- 首先到这一步的时候,只能参考业内先进的经验
- 例如流式计算可以通过sql抽象
-
Google的f1 Query,oltp,olap,etl的需求都在f1中进行了抽象
(3)中台的常见问题
- 中台系统式内部saas,非常复杂,建设成本很高
- 优先照顾大业务,忽视小业务的需求
- 有的直接替代了前台业务的功能
- 复杂的业务中台,对个别业务没有加速效应,反而拖累业务发展
- 某公司收购的新业务,接入中台一年都没有接完
(4)CRUD程序员怎么进步
-
重复的代码进行自动化,抽象化,配置化
-
多学习一下公司同类的需求怎么做,例如google(research.google/pubs)
5.微服务在复杂业务场景的拆分难题
(1)区分业务与领域
- 业务流程式不容易变化的
- 有了业务模型,就有了公司业务迭代以及优化的基础
- 业务流程一般是不容易变化的,只会微调的(例如购物:业务流程包括下单检查,下单,发货,收货;但是拼多多对购物的业务流程做了调整,比较明显的一点是取消了购物车)
(2)单体服务的困境
- 多个开发分团队共用一个代码仓库,解决代码冲突成本非常高。
- 每个上线批次的灰度时间叠加成本
服务拆分之后:
(3)微服务拆分之后的困境
- 服务发现
- 服务可观测性
- 服务间通信安全性
- 服务间调用稳定性
- 自动化部署
(4)微服务拆分实践
a.按照业务能力拆解
- 识别业务能力举例
- 供应商管理
- 餐馆管理 CourierService
- 送餐员管理 RestaurantService
- 消费者管理
- 消费者信息 ConsumerService
- 订单获取与履行
- 消费者订单管理 OrderService
- 餐馆订单管理 KitchenService
- 送餐管理
- 送餐员状态管理 DeliverService
- 配送管理
- 会计
- 消费者记账 AccountService
- 餐馆记账
- 配送员记账
- 供应商管理
b.按照子域进行分解
- 领域:你的公司在解决什么样的行业问题(例如:出行)
- 子域:领域内可以细分为哪些子域。(例如:出租车出行,快车出行,拼车出行)
- 限界上下文:实现这个子域的代码集合
c.拆分中注意的原则
- 单一职责原则(SRP)
- 闭包原则(CCP):如果两个类/服务经常一起修改,应该把他们放在一起
(5) 拆分后的集成模式
a.超时控制
- 需要有熔断保护
b.服务发现
- 注册中心注册服务(比较常用的模式)
- DNS解析进行服务发现(以前百度有做过这块的实践)
c.使用异步消息进行集成
- 延迟通知可以使用queue,但是不能取代rpc模式
- 即使基于queue解耦了,也会面临延时导致的一致性问题
d. Event Sourcing
- 数据库中不记录最终的状态,而是记录所有的修改历史,以方便回源
- 优化
- 每过一段时间进行一次快照计算
- 当前处于实践的案例比较少,还在摸索阶段
6.go的一些工具
- 初始化依赖注入工具,wire
wire.Build(NewServer,NewDb,NewEtcd,NewRedis,NewLogger)