介绍交易系统核心服务在可用性可靠性高性能上的一些思考

ET6工程化演进之路

提纲

1、模块介绍
2、以一笔交易例,贯穿调用链路
3、可用性、性能优化、代码可维护性 各举一例
4、展望未来
5、总结

关注点

  • 安全 客户资产安全是第一要务
  • 准确
  • 稳定 系统稳定和业务连续性
  • 高效 在以上基础上追求更高的业务处理能力

可用性/准确性

  • 衡量可用性标准

  • 可靠消息队列

    • ack = waitforall,isr 选举 数据冗余 防丢失 容错性 高吞吐量 低延迟

    • 同步通信机制造成的阻塞,另一方面通过消息队列进行业务解耦。

    • mysql高并发下性能瓶颈,主从架构。

    • 未来如果做清算,补偿等功能都无需侵入核心服务,可扩展性高

    • 最大尝试,超时控制

    • 本地数据库,正反向消息机制+定时任务(幂等),补单对账,终极兜底

    • 一致性提升, 多副本冗余 可靠性和鲁棒性

  • 业务并发问题
    1)交易类型之间
    2)手动触发跟自动触发之间
    应对

    • 引入用户锁,同一个用户同一时刻只能执行一个事物
    • 交易系统的所有订单是一个有序队列。不同的用户在同一时刻下单,也必须由定序系统确定先后顺序。冻结保证金.
  • Price 作为交易系统的血液

    • 准确
    • 顺序(全局)
    • 完整(丢失情况)
    • 实时(延迟情况)
    • kafka 有序队列保证顺序
    • 根据系统运行状况,动态丢弃报价,防止剥头皮(滑点),当然丢弃策略理论上是比较严格的,且可追溯的。
  • 隔夜息

    • 打包成kafka事物消息
  • 充分充足的集成测试和压力测试:包括不限于单用户单品种,单用户多品种,多用户多品种,系统和手动交叉

性能优化

  • 减少syscall
  • decimal ,作为系统最重要数据类型 自研10x 于shopspring
  • 日志异步
  • 内存复用
  • 底层热点函数去锁,zero malloc
  • calc 并发优化,至今最大的性能优化 持仓单10倍增长
  • sync.pool减少堆对象生成,时间轮来减少锁竞争,分段锁map

可维护性

  • account
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
//顶层 对外暴露 service抽象
type AccountService interface{
CreateAccount (account *AccountInfo) error)
}
GetAccountService() AccountService { }

//service实现层
type asImpl struct {
//dao层抽象
AccountOperator
Decorator
}

type Decorator interface {
PubEvent(event interface{})
}
type AccountDecorator struct {}
func (a *AccountDecorator) PubEvent(event interface{}) {
events.GetEventService().Transaction(AccountTopic, event)
}

type AccountOperator interface {
//dao层方法
}
type accountOperator struct {
//dao层抽象
Decorator
}

//入口
func GetAccountService() AccountService {
sync.Once.Do{
//Dao
actrepo := mysql.GetAccountRepository()
//service层
op := server.InitAccountOperator(actrepo, &server.AccountDecorator{})
AccountServiceInstance = server.NewAccountServiceImpl(op)
}
// AccountServiceInstance 实现了 AccountService 接口
return AccountServiceInstance
}
// 总结:对外提供服务接口,中间层提供接口实现,底层为数据访问层
// 逐层接口调用,逐层组合继承接口,中间通过私有局部变量进行数据保护
// MVC分层架构, 依赖注入,显示声明,面向接口编程,支持扩展,开闭原则


  • trade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//模版模式vs面向过程
//流程稳定不变,各自实现有差异
//之前所有交易类型融合在一起,ifelse套娃,在一个大代码段里,混乱,难读,易改错,业务边界混乱
//改造后:
//通过接口将交易流程抽象
type operater interface {
reqArgs2TransArgs(args *server.TradeRequestArgs) *server.TradeTransArgs
trade(args *server.TradeTransArgs, in server.Ret) server.Ret
handleTradeError()
integrateResult(args *server.TradeRequestArgs) server.Ret
syncOrder() error
syncAccount() error
syncKafka(topic string) error
getTradeResult() *db.TradeTransResult
getIntegrateResult() *db.TradeTransResult
}

//模版方法 定义业务骨架
func operate(ctx , args, operater) {
transArgs := op.reqArgs2TransArgs(args)
if ret := op.trade(transArgs, checkRet);ret!= server.RetOK {
op.handleTradeError()
return op.getTradeResult(), ret
}

ret = op.integrateResult(args)

op.syncKafka(tableTopic)
op.syncOrder()
op.syncAccount()

return op.getIntegrateResult(), checkRet
}

// 工厂方法 各种交易类型去多态的实现
func newOperater(args *server.TradeRequestArgs, checkRes *db.TradeTransResult) operater {
switch args.Type {
case constant.TTOrderMKOpen:
return newOperaterMkOpen(checkRes)
case constant.TTOrderMKModify:
return newOperaterMkModify(checkRes)
case constant.TTOrderMKClose:
return newOperaterMkClose(checkRes)
// 通过组合实现继承 , so就走平仓逻辑,所以so直接继承mkclose
case constant.TTBRActivateSO:
return newOperaterMkSo(checkRes)
}
}
//总结:变化的算法子类继承并具体实现:变化的部分子类(单一职责原则)只需要具体实现抽象的部分即可,方便扩展,且可无限扩展。
//对operate流程封闭,对扩展交易类型开放
//各种交易类型去多态的实现。 业务边界清晰,提升代码易读性。

  • 其他栗子:

    • throwBuffer 中 选项模式//pipeline

    • configer :symbol source session 静态数据 ,集成在同一个模块,对crud等功能抽象,对外提供的能力

  • 其他隐性共识:
    • defer清理资源
    • 错误包装 error.Wrapping,业务场景上抛error 需要添加上下文,以便错误消息提供更多信息
    • 不要panic
    • 显式初始化
    • 锁变量
    • 全局变量要具有描述性,局部变量要短i,o,u

框架工具清单

  • xorm

  • sarama

  • redigo

  • govalidator

  • logrus/file-rotatelogs

  • golang-lru

  • 计划开源自研工具:ringbuffer、decimal、gpool、gosafe、snow-short

  • gomonkey

  • goconvey

  • sentry-go

  • jaeger

  • gopsutil

  • ansible+jenkins

  • chaosBlade

  • etcd

  • elg / pg

展望未来

  • 上图

总结

  • 介绍了项目现有功能 并通过一笔交易介绍了主要业务流程
  • 就像cap一样 不可能全都要,所以我们按关注点在开发中有所侧重
  • 在服务可用性和准确性上 通过kafka解决服务拆分和性能问题,用户锁解决单用户并发问题
  • 介绍了几个高收益的性能优化点
  • 通过介绍项目中用到的几个设计模式,使项目尽可能跟高内聚低耦合沾边。