@ -9,9 +9,9 @@ import (
"sig-pub/pkg/client"
"sig-pub/pkg/client"
"sig-pub/pkg/data"
"sig-pub/pkg/data"
"sig-pub/pkg/mq"
"sig-pub/pkg/mq"
"sig-pub/pkg/publish"
"sig-pub/pkg/types"
"sig-pub/pkg/types"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/collect"
"sig-pub/pkg/utils/conver"
"sig-pub/pkg/utils/retry"
"sig-pub/pkg/utils/retry"
"sig-pub/pkg/utils/times"
"sig-pub/pkg/utils/times"
"sig-pub/pkg/zlog"
"sig-pub/pkg/zlog"
@ -29,7 +29,7 @@ type ExchangeService struct {
tradeInstanceAside * client . TradeInstanceAside
tradeInstanceAside * client . TradeInstanceAside
exchangeDataPersist * ExchangeDataPersist
exchangeDataPersist * ExchangeDataPersist
klinePublisher * Publisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ]
klinePublisher * publish . Publisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ]
}
}
// exchanges: 支持的数据源交易所
// exchanges: 支持的数据源交易所
@ -50,7 +50,7 @@ func NewExchangeService(
exchanges : exchangeState ,
exchanges : exchangeState ,
tradeInstanceAside : tradeInstanceAside ,
tradeInstanceAside : tradeInstanceAside ,
exchangeDataPersist : exchangeDataPersist ,
exchangeDataPersist : exchangeDataPersist ,
klinePublisher : NewPublisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ] ( 16 ) ,
klinePublisher : publish . NewPublisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ] ( 16 ) ,
}
}
}
}
@ -60,7 +60,7 @@ func (svc *ExchangeService) Init() (err error) {
}
}
// GetKlineSubscriber 订阅k线订阅器
// GetKlineSubscriber 订阅k线订阅器
func ( svc * ExchangeService ) GetKlineSubscriber ( ) ( subscriber * Publisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ] ) {
func ( svc * ExchangeService ) GetKlineSubscriber ( ) ( subscriber * publish . Publisher [ int64 , grpc . BidiStreamingServer [ pb . ReqStreamSubscribeKline , pb . RspStreamSubscribeKline ] ] ) {
subscriber = svc . klinePublisher
subscriber = svc . klinePublisher
return
return
}
}
@ -143,7 +143,11 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
if len ( channelK . Klines ) == 0 {
if len ( channelK . Klines ) == 0 {
continue
continue
}
}
receivedTs := time . Now ( ) . UnixMilli ( )
// receivedTs := time.Now().UnixMilli()
// if channelK.Klines[0].Interval == types.Interval1s {
// zlog.Debugf("tick delay: %dms", receivedTs-channelK.Klines[0].Ts-1000)
// }
// 交易所 instid 转 sig-instid
// 交易所 instid 转 sig-instid
var tradeInst * types . TradeInstance
var tradeInst * types . TradeInstance
exchangeInst , ok := exchange . ExchangeInsts . Load ( channelK . ExgInstId )
exchangeInst , ok := exchange . ExchangeInsts . Load ( channelK . ExgInstId )
@ -156,7 +160,8 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
// 升序排序
// 升序排序
collect . SortAsc ( channelK . Klines , func ( k * types . Kline ) int64 { return k . Ts } )
collect . SortAsc ( channelK . Klines , func ( k * types . Kline ) int64 { return k . Ts } )
// 检查已确认k线是否连续并补齐
// k线完整性检查, k线是否连续并补齐
padding := false
for _ , kline := range channelK . Klines {
for _ , kline := range channelK . Klines {
if kline . Confirm {
if kline . Confirm {
if kms , ok := kline . Interval . AddMul ( kline . Ts , 1 ) ; ok {
if kms , ok := kline . Interval . AddMul ( kline . Ts , 1 ) ; ok {
@ -165,19 +170,19 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
if lastConfirmK := exchangeInst . LastKline . Get ( kline . Interval ) ; lastConfirmK . Ts != 0 {
if lastConfirmK := exchangeInst . LastKline . Get ( kline . Interval ) ; lastConfirmK . Ts != 0 {
if expectTs , ok := lastConfirmK . Interval . AddMul ( lastConfirmK . Ts , 1 ) ; ok && expectTs != kline . Ts {
if expectTs , ok := lastConfirmK . Interval . AddMul ( lastConfirmK . Ts , 1 ) ; ok && expectTs != kline . Ts {
padding = true
startTs := time . Now ( ) . UnixMilli ( )
zlog . Warningf ( "fetching padding klines: inst=%s(%s), interval=%s, ts=%d~%d" , tradeInst . InstId , tradeInst . Exchange , kline . Interval , kline . Ts , lastConfirmK . Ts )
zlog . Warningf ( "fetching padding klines: inst=%s(%s), interval=%s, ts=%d~%d" , tradeInst . InstId , tradeInst . Exchange , kline . Interval , kline . Ts , lastConfirmK . Ts )
if err := svc . initialTradeInstanceKlines ( exchange , * tradeInst ) ; err != nil {
if err := svc . paddingTradeInstanceInterval Klines( 4 , exchange , * tradeInst , kline . Interval ) ; err != nil {
zlog . Errorf ( "fetch padding kline error: inst=%s(%s), interval=%s, ts=%d~%d, error=%v" , tradeInst . InstId , tradeInst . Exchange , kline . Interval , kline . Ts , lastConfirmK . Ts , err )
zlog . Errorf ( "fetch padding kline error: inst=%s(%s), interval=%s, ts=%d~%d, error=%v" , tradeInst . InstId , tradeInst . Exchange , kline . Interval , kline . Ts , lastConfirmK . Ts , err )
} else {
padding = false
zlog . Infof ( "fetched padding klines: inst=%s(%s), interval=%s, ts=%d~%d, use=%dms" , tradeInst . InstId , tradeInst . Exchange , kline . Interval , kline . Ts , lastConfirmK . Ts , time . Now ( ) . UnixMilli ( ) - startTs )
// flush vmtsdb to disk
if err = svc . exchangeDataPersist . vmtsdb . ForceFlush ( ) ; err != nil {
zlog . Errorf ( "flush vmts db error: " , err )
}
}
}
// zlog.Warningf("fetching padding klines: inst=%s(%s), interval=%s, ts=%d~%d", tradeInst.InstId, tradeInst.Exchange, kline.Interval, kline.Ts, lastConfirmK.Ts)
// paddingKlines, err := exchange.Fetcher.FetchHistoryKlines(context.Background(), tradeInst.ExchangeInstId, kline.Interval, kline.Ts, lastConfirmK.Ts)
// if err != nil {
// zlog.Errorf("fetch padding kline error: inst=%s(%s), interval=%s, ts=%d~%d, error=%v", tradeInst.InstId, tradeInst.Exchange, kline.Interval, kline.Ts, lastConfirmK.Ts, err)
// } else {
// zlog.Debugf("fetched padding klines: inst=%s(%s), interval=%s, ts=%d~%d, %#v", tradeInst.InstId, tradeInst.Exchange, kline.Interval, kline.Ts, lastConfirmK.Ts, paddingKlines)
// channelK.Klines = append(paddingKlines, channelK.Klines...)
// collect.SortAsc(channelK.Klines, func(k *types.Kline) int64 { return k.Ts })
// }
}
}
}
}
}
}
@ -188,9 +193,6 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
// 取出头尾k线
// 取出头尾k线
lastKline := channelK . Klines [ len ( channelK . Klines ) - 1 ]
lastKline := channelK . Klines [ len ( channelK . Klines ) - 1 ]
// 标记交易产品开始订阅k线时间
// exchangeInst.LiveKStartTs.SetIf(lastKline.Interval, lastKline.Ts, func(old int64) bool { return old == 0 })
// 记录实时k线
// 记录实时k线
exchangeInst . LiveKline . Set ( lastKline . Interval , * lastKline )
exchangeInst . LiveKline . Set ( lastKline . Interval , * lastKline )
@ -227,15 +229,14 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
if err != nil {
if err != nil {
zlog . Errorf ( "kline save to tsdb error: %v, %#v" , err , confirmKlines )
zlog . Errorf ( "kline save to tsdb error: %v, %#v" , err , confirmKlines )
} else {
} else {
// 初始化状态完成, 更新k线时间戳标记
// k线未缺失, 初始化状态完成, 更新k线时间戳标记
if exchangeInst . Status . Load ( ) == int32 ( data . StatusOk ) {
if ! padding && exchangeInst . Status . Load ( ) == int32 ( data . StatusOk ) {
latestK := collect . MustMax ( confirmKlines , func ( k * types . Kline ) int64 { return k . Ts } )
latestK := collect . MustMax ( confirmKlines , func ( k * types . Kline ) int64 { return k . Ts } )
// 标记确认k线
// 标记确认k线
tsKey , ex := svc . exchangeDataPersist . SaveHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , latestK . Interval , latestK . Ts )
tsKey , ex := svc . exchangeDataPersist . SaveHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , latestK . Interval , latestK . Ts )
if ex != nil {
if ex != nil {
zlog . Errorf ( "history mark inititaled ts error: key=%s, ts=%d, %v" , tsKey , latestK . Ts , ex )
zlog . Errorf ( "history mark inititaled ts error: key=%s, ts=%d, %v" , tsKey , latestK . Ts , ex )
}
}
// todo k线完整性检查
}
}
}
}
}
}
@ -254,9 +255,9 @@ func (svc *ExchangeService) consumerKline(exchange *Exchange, c <-chan *types.Ch
}
}
}
}
if useMs := time . Now ( ) . UnixMilli ( ) - receivedTs ; useMs > 10 {
// if useMs := time.Now().UnixMilli() - receivedTs; useMs > 10 {
zlog . Debugf ( "handle consume kline use: %dms" , useMs )
// zlog.Debugf("handle consume kline use: %dms", useMs)
}
// }
}
}
}
}
@ -284,10 +285,10 @@ func (svc *ExchangeService) initialKlines(exchange *Exchange, insts []types.Trad
var success , failed [ ] types . TradeInstance
var success , failed [ ] types . TradeInstance
for _ , inst := range insts {
for _ , inst := range insts {
err := svc . initial TradeInstanceKlines( exchange , inst )
err := svc . padding TradeInstanceKlines( exchange , inst )
status := data . StatusFailed
status := data . StatusFailed
if err != nil {
if err != nil {
zlog . Errorf ( "initial fetch trade instance error: %s(%s), err=%v" , inst . InstId , inst . Exchange , err )
zlog . Errorf ( "padding trade instance klines error: %s(%s), err=%v" , inst . InstId , inst . Exchange , err )
failed = append ( failed , inst )
failed = append ( failed , inst )
} else {
} else {
success = append ( success , inst )
success = append ( success , inst )
@ -307,16 +308,61 @@ func (svc *ExchangeService) initialKlines(exchange *Exchange, insts []types.Trad
zlog . Infof ( "%d insts initial finished, success %d, failed %d" , len ( insts ) , len ( success ) , len ( failed ) )
zlog . Infof ( "%d insts initial finished, success %d, failed %d" , len ( insts ) , len ( success ) , len ( failed ) )
}
}
// init TradeInstanceKlines 初始化交易产品历史k线数据
// padding TradeInstanceKlines 初始化交易产品历史k线数据
func ( svc * ExchangeService ) initial TradeInstanceKlines( exchange * Exchange , tradeInst types . TradeInstance ) ( err error ) {
func ( svc * ExchangeService ) padding TradeInstanceKlines( exchange * Exchange , tradeInst types . TradeInstance ) ( err error ) {
exchangeInst , ok := exchange . ExchangeInsts . Load ( tradeInst . ExchangeInstId )
exchangeInst , ok := exchange . ExchangeInsts . Load ( tradeInst . ExchangeInstId )
if ! ok {
if ! ok {
err = fmt . Errorf ( "not load exchange trade instance: %s" , tradeInst . ExchangeInstId )
err = fmt . Errorf ( "not load exchange trade instance: %s" , tradeInst . ExchangeInstId )
return
return
}
}
watch := times . NewWatch ( )
defer func ( ) {
if err != nil {
// 交易所k线初始化失败
exchangeInst . Status . Store ( int32 ( data . StatusFailed ) )
return
}
// 初始化成功
exchangeInst . Status . Store ( int32 ( data . StatusOk ) )
zlog . Infof ( "padding history kline finish: instId=%s(%s), use %s" , tradeInst . InstId , tradeInst . Exchange , watch . ElapsedFmt ( "." ) )
// flush vmtsdb to disk
retry . DoWithFixDelay ( 5 , time . Second , func ( retryTimes uint32 ) ( _ struct { } , err error ) {
if err = svc . exchangeDataPersist . vmtsdb . ForceFlush ( ) ; err != nil {
zlog . Errorf ( "flush vmts db error: " , err )
}
return
} )
} ( )
// 并发数
// 并发数
concurrent := max ( 8 , runtime . NumCPU ( ) * 2 )
concurrent := max ( 8 , runtime . NumCPU ( ) * 2 )
// 按周期分割成小任务
for interval := range types . SupportedIntervals {
err = svc . paddingTradeInstanceIntervalKlines ( concurrent , exchange , tradeInst , interval )
if err != nil {
zlog . Errorf ( "padding trade instance interval error: instId=%s(%s), interval=%s" , tradeInst . InstId , tradeInst . Exchange , interval , err )
return
}
}
return
}
// paddingTradeInstanceIntervalKlines 初始化交易产品指定周期历史k线数据
func ( svc * ExchangeService ) paddingTradeInstanceIntervalKlines ( concurrent int , exchange * Exchange , tradeInst types . TradeInstance , interval types . Interval ) ( err error ) {
intervalAdder , ok := types . SupportedIntervals [ interval ]
if ! ok {
err = fmt . Errorf ( "unsupport interval %s" , interval )
return
}
exchangeInst , ok := exchange . ExchangeInsts . Load ( tradeInst . ExchangeInstId )
if ! ok {
err = fmt . Errorf ( "unsupport interval exchange trade instance: %s(%s)" , tradeInst . ExchangeInstId , exchange . ExchangeType )
return
}
// 任务 channel
// 任务 channel
taskCh := make ( chan fetchKlineTask , concurrent )
taskCh := make ( chan fetchKlineTask , concurrent )
retryTaskCh := make ( chan fetchKlineTask , concurrent )
retryTaskCh := make ( chan fetchKlineTask , concurrent )
@ -324,35 +370,30 @@ func (svc *ExchangeService) initialTradeInstanceKlines(exchange *Exchange, trade
// 发布任务数, 成功任务数, 失败任务次数
// 发布任务数, 成功任务数, 失败任务次数
var pubTasks , subTasks , failTimes atomic . Int32
var pubTasks , subTasks , failTimes atomic . Int32
var pubTaskDone atomic . Bool // 所有任务已发布
var pubTaskDone atomic . Bool // 所有任务已发布
startTs := time . Now ( ) . UnixMilli ( )
watch := times . NewWatch ( )
ctx , cancel := context . WithCancel ( context . Background ( ) )
ctx , cancel := context . WithCancel ( context . Background ( ) )
defer func ( ) {
defer func ( ) {
if err != nil {
if err != nil {
// 交易所k线初始化失败
exchangeInst . Status . Store ( int32 ( data . StatusFailed ) )
return
return
}
}
exchangeInst . HistoryMarkTs . Range ( func ( _ int , interval types . Interval , ts int64 ) {
zlog . Infof ( "padding history kline finish: instId=%s(%s), interval=%s, pub=%d, sub=%d, fail=%d, use %s" , tradeInst . InstId , tradeInst . Exchange , interval , pubTasks . Load ( ) , subTasks . Load ( ) , failTimes . Load ( ) , watch . ElapsedFmt ( "." ) )
tsKey , ex := svc . exchangeDataPersist . SaveHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , interval , ts )
if ex != nil {
zlog . Errorf ( "history mark inititaled ts error: key=%s, ts=%d, %v" , tsKey , ts , ex )
}
} )
exchangeInst . Status . Store ( int32 ( data . StatusOk ) )
zlog . Infof ( "initial history kline finish: instId=%s(%s), pub=%d, sub=%d, fail=%d, use %s" , tradeInst . InstId , tradeInst . Exchange , pubTasks . Load ( ) , subTasks . Load ( ) , failTimes . Load ( ) , conver . TimeMilliFormat ( time . Now ( ) . UnixMilli ( ) - startTs , "." ) )
// flush vmtsdb to disk
markTs := exchangeInst . HistoryMarkTs . Get ( interval )
retry . DoWithFixDelay ( 5 , time . Second , func ( retryTimes uint32 ) ( _ struct { } , err error ) {
tsKey , ex := svc . exchangeDataPersist . SaveHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , interval , markTs )
if err = svc . exchangeDataPersist . vmtsdb . ForceFlush ( ) ; err != nil {
if ex != nil {
zlog . Errorf ( "flu sh v mts db error: " , err )
zlog . Errorf ( "save history mark ts error: key=%s, ts=%d, %v" , tsKey , markTs , ex )
}
}
return
} )
} ( )
} ( )
// progress monitor
// 任务进度日志(执行超过3s打印进度)
go func ( ) {
go func ( ) {
select {
case <- ctx . Done ( ) :
return
case <- time . After ( 3 * time . Second ) :
}
ticker := time . NewTicker ( time . Second )
ticker := time . NewTicker ( time . Second )
for {
for {
select {
select {
@ -360,12 +401,12 @@ func (svc *ExchangeService) initialTradeInstanceKlines(exchange *Exchange, trade
ticker . Stop ( )
ticker . Stop ( )
return
return
case <- ticker . C :
case <- ticker . C :
zlog . Debugf ( "processing initial history kline tasks: %s(%s) , pub %d, sub %d, fail %d" , tradeInst . InstId , tradeInst . Exchange , pubTasks . Load ( ) , subTasks . Load ( ) , failTimes . Load ( ) )
zlog . Debugf ( "processing padding history kline tasks: %s(%s), interval=%s , pub %d, sub %d, fail %d" , tradeInst . InstId , tradeInst . Exchange , interval , pubTasks . Load ( ) , subTasks . Load ( ) , failTimes . Load ( ) )
}
}
}
}
} ( )
} ( )
// fetch kline history task publish
// 任务发布器
go func ( ) {
go func ( ) {
defer func ( ) {
defer func ( ) {
pubTaskDone . Store ( true )
pubTaskDone . Store ( true )
@ -373,16 +414,18 @@ func (svc *ExchangeService) initialTradeInstanceKlines(exchange *Exchange, trade
if pubTasks . Load ( ) == 0 {
if pubTasks . Load ( ) == 0 {
cancel ( )
cancel ( )
}
}
// zlog.Infof("trade instance initial kline %s(%s), pub %d fetch tasks", tradeInst.InstId, tradeInst.Exchange, pubTasks.Load())
} ( )
} ( )
for interval , intervalAdder := range types . SupportedIntervals {
// history 未补全前, history写 kvdb ts mark, 补全后 ws live cnofirm 写 ts mark
// interval := types.Interval1d
beforeTs := int64 ( 0 )
// intervalAdder := types.SupportedIntervals[interval]
beforeTs , err := retry . DoWithStepDelay ( 10 , time . Second , func ( retryTimes uint32 ) ( markTs int64 , err error ) {
// history 未补全前, history写 kvdb ts mark, 补全后 ws live 写 ts mark
markTs , err = svc . exchangeDataPersist . GetHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , interval )
beforeTs , ex := svc . exchangeDataPersist . GetHistoryKlineMarkTs ( tradeInst . Exchange , tradeInst . InstId , interval )
if err != nil {
if ex != nil {
zlog . Error ( "get history kline mark ts error: " , err )
err = ex
}
return
} )
if err != nil {
zlog . Error ( err )
zlog . Error ( err )
cancel ( )
cancel ( )
return
return
@ -393,18 +436,13 @@ func (svc *ExchangeService) initialTradeInstanceKlines(exchange *Exchange, trade
// 不足100根,向前补齐100根一次拉取过来
// 不足100根,向前补齐100根一次拉取过来
total := ( time . Now ( ) . UnixMilli ( ) - beforeTs ) / intervalAdder ( 0 , 1 )
total := ( time . Now ( ) . UnixMilli ( ) - beforeTs ) / intervalAdder ( 0 , 1 )
if total < 100 {
if total < 100 {
beforeTs = max ( intervalAdder ( beforeTs , - 100 ) , KlineBefore0 )
beforeTs = max ( intervalAdder ( beforeTs , - 100 ) , intervalAdder ( KlineBefore0 , - 1 ) )
}
}
}
}
exchangeInst . HistoryMarkTs . Set ( interval , beforeTs )
for {
for {
// 判定订阅任务发布完成
// 判定订阅任务发布完成
// liveStartTs := exchangeInst.LiveKStartTs.Get(interval)
if intervalAdder ( beforeTs , 2 ) > time . Now ( ) . UnixMilli ( ) {
// if liveStartTs != 0 && beforeTs >= liveStartTs {
// break
// }
if beforeTs > time . Now ( ) . UnixMilli ( ) {
break
break
}
}
@ -427,7 +465,6 @@ func (svc *ExchangeService) initialTradeInstanceKlines(exchange *Exchange, trade
beforeTs = intervalAdder ( afterTs , - 1 )
beforeTs = intervalAdder ( afterTs , - 1 )
}
}
}
} ( )
} ( )
// 任务消费器 多协程并行
// 任务消费器 多协程并行
@ -747,6 +784,8 @@ func (svc *ExchangeService) HistoryKlineStream(req *pb.ReqHistoryKlineStream, st
if len ( klines ) == 0 {
if len ( klines ) == 0 {
return
return
}
}
// todo 检查k线是否连续进行补齐
lastK := klines [ len ( klines ) - 1 ]
lastK := klines [ len ( klines ) - 1 ]
// vmtsdb 数据刷盘30s延迟, 使用内存数据替代第一根k线
// vmtsdb 数据刷盘30s延迟, 使用内存数据替代第一根k线
lastConfirmK := exchangeInst . LastKline . Get ( interval )
lastConfirmK := exchangeInst . LastKline . Get ( interval )