325 lines
9.9 KiB
Go
325 lines
9.9 KiB
Go
|
package service
|
|||
|
|
|||
|
import (
|
|||
|
"context"
|
|||
|
"fmt"
|
|||
|
"strconv"
|
|||
|
"strings"
|
|||
|
"sync"
|
|||
|
"time"
|
|||
|
|
|||
|
"go-common/app/interface/main/dm2/conf"
|
|||
|
"go-common/app/interface/main/dm2/dao"
|
|||
|
"go-common/app/interface/main/dm2/model"
|
|||
|
"go-common/app/interface/main/dm2/model/oplog"
|
|||
|
accountCli "go-common/app/service/main/account/api"
|
|||
|
arcCli "go-common/app/service/main/archive/api/gorpc"
|
|||
|
assMdl "go-common/app/service/main/assist/model/assist"
|
|||
|
assCli "go-common/app/service/main/assist/rpc/client"
|
|||
|
coinCli "go-common/app/service/main/coin/api/gorpc"
|
|||
|
figureCli "go-common/app/service/main/figure/rpc/client"
|
|||
|
filterCli "go-common/app/service/main/filter/api/grpc/v1"
|
|||
|
locCli "go-common/app/service/main/location/rpc/client"
|
|||
|
memberCli "go-common/app/service/main/member/api/gorpc"
|
|||
|
relCli "go-common/app/service/main/relation/rpc/client"
|
|||
|
seqMdl "go-common/app/service/main/seq-server/model"
|
|||
|
seqCli "go-common/app/service/main/seq-server/rpc/client"
|
|||
|
spyCli "go-common/app/service/main/spy/rpc/client"
|
|||
|
thumbupApi "go-common/app/service/main/thumbup/api"
|
|||
|
|
|||
|
ugcPayCli "go-common/app/service/main/ugcpay/api/grpc/v1"
|
|||
|
seasonCli "go-common/app/service/openplatform/pgc-season/api/grpc/season/v1"
|
|||
|
"go-common/library/ecode"
|
|||
|
"go-common/library/log"
|
|||
|
"go-common/library/log/infoc"
|
|||
|
"go-common/library/sync/pipeline/fanout"
|
|||
|
"go-common/library/xstr"
|
|||
|
|
|||
|
"golang.org/x/sync/singleflight"
|
|||
|
)
|
|||
|
|
|||
|
func keySubject(tp int32, oid int64) string {
|
|||
|
return fmt.Sprintf("sub_local_%d_%d", tp, oid)
|
|||
|
}
|
|||
|
|
|||
|
func keyXML(tp int32, oid int64) string {
|
|||
|
return fmt.Sprintf("%d_%d", tp, oid)
|
|||
|
}
|
|||
|
|
|||
|
func keySeg(tp int32, oid, cnt, num int64) string {
|
|||
|
return fmt.Sprintf("%d_%d_%d_%d", tp, oid, cnt, num)
|
|||
|
}
|
|||
|
|
|||
|
func keyDuration(tp int32, oid int64) string {
|
|||
|
return fmt.Sprintf("d_%d_%d", tp, oid)
|
|||
|
}
|
|||
|
|
|||
|
type broadcast struct {
|
|||
|
Aid int64
|
|||
|
Rnd int64
|
|||
|
*model.DM
|
|||
|
}
|
|||
|
|
|||
|
// Service dm2 service
|
|||
|
type Service struct {
|
|||
|
conf *conf.Config
|
|||
|
dao *dao.Dao
|
|||
|
arcRPC *arcCli.Service2
|
|||
|
accountRPC accountCli.AccountClient
|
|||
|
assRPC *assCli.Service
|
|||
|
coinRPC *coinCli.Service
|
|||
|
relRPC *relCli.Service
|
|||
|
thumbupRPC thumbupApi.ThumbupClient
|
|||
|
locationRPC *locCli.Service
|
|||
|
cache *fanout.Fanout
|
|||
|
realname map[int64]int64 // key:分区id,value:cid,即该分区中大于cid的视频开启实名制
|
|||
|
singleGroup singleflight.Group
|
|||
|
arcTypes map[int16]int16
|
|||
|
broadcastChan chan *broadcast
|
|||
|
// seq serer
|
|||
|
seqDmArg *seqMdl.ArgBusiness
|
|||
|
seqSubtitleArg *seqMdl.ArgBusiness
|
|||
|
seqRPC *seqCli.Service2
|
|||
|
// dm xml and dm seg local cache
|
|||
|
localCache map[string][]byte
|
|||
|
assistLogChan chan *assMdl.ArgAssistLogAdd
|
|||
|
// send operation log with infoc2
|
|||
|
dmOperationLogSvc *infoc.Infoc
|
|||
|
opsLogCh chan *oplog.Infoc
|
|||
|
// dm monitor merge proc
|
|||
|
moniOidMap map[int64]struct{}
|
|||
|
oidLock sync.Mutex
|
|||
|
// subtitle singleFlight
|
|||
|
subtitleSingleGroup singleflight.Group
|
|||
|
subtitleLans model.SubtitleLans
|
|||
|
// block
|
|||
|
memberRPC *memberCli.Service
|
|||
|
// filter
|
|||
|
filterRPC filterCli.FilterClient
|
|||
|
//figure
|
|||
|
figureRPC *figureCli.Service
|
|||
|
// ugc pay
|
|||
|
ugcPayRPC ugcPayCli.UGCPayClient
|
|||
|
// spy RPC
|
|||
|
spyRPC *spyCli.Service
|
|||
|
// season
|
|||
|
seasonRPC seasonCli.SeasonClient
|
|||
|
// garbageDanmu
|
|||
|
garbageDanmu bool
|
|||
|
// broadcast limit
|
|||
|
broadcastLimit int
|
|||
|
broadcastlimitInterval int
|
|||
|
// view localcache
|
|||
|
localViewCache map[string]*model.ViewDm
|
|||
|
// bnj shield
|
|||
|
aidSheild map[int64]struct{}
|
|||
|
midsSheild map[int64]struct{}
|
|||
|
}
|
|||
|
|
|||
|
// New return a service instance.
|
|||
|
func New(c *conf.Config) (s *Service) {
|
|||
|
s = &Service{
|
|||
|
conf: c,
|
|||
|
dao: dao.New(c),
|
|||
|
arcRPC: arcCli.New2(c.ArchiveRPC),
|
|||
|
assRPC: assCli.New(c.AssistRPC),
|
|||
|
coinRPC: coinCli.New(c.CoinRPC),
|
|||
|
relRPC: relCli.New(c.RelationRPC),
|
|||
|
locationRPC: locCli.New(c.LocationRPC),
|
|||
|
cache: fanout.New("cache", fanout.Worker(1), fanout.Buffer(1024)),
|
|||
|
realname: make(map[int64]int64),
|
|||
|
arcTypes: make(map[int16]int16),
|
|||
|
broadcastChan: make(chan *broadcast, 1024),
|
|||
|
localCache: make(map[string][]byte),
|
|||
|
seqDmArg: &seqMdl.ArgBusiness{BusinessID: c.Seq.DM.BusinessID, Token: c.Seq.DM.Token},
|
|||
|
seqSubtitleArg: &seqMdl.ArgBusiness{BusinessID: c.Seq.Subtitle.BusinessID, Token: c.Seq.Subtitle.Token},
|
|||
|
seqRPC: seqCli.New2(c.SeqRPC),
|
|||
|
assistLogChan: make(chan *assMdl.ArgAssistLogAdd, 1024),
|
|||
|
dmOperationLogSvc: infoc.New(c.Infoc2),
|
|||
|
opsLogCh: make(chan *oplog.Infoc, 1024),
|
|||
|
moniOidMap: make(map[int64]struct{}),
|
|||
|
memberRPC: memberCli.New(c.MemberRPC),
|
|||
|
figureRPC: figureCli.New(c.FigureRPC),
|
|||
|
spyRPC: spyCli.New(c.SpyRPC),
|
|||
|
garbageDanmu: c.Switch.GarbageDanmu,
|
|||
|
broadcastLimit: c.BroadcastLimit.Limit,
|
|||
|
broadcastlimitInterval: c.BroadcastLimit.Interval,
|
|||
|
localViewCache: make(map[string]*model.ViewDm),
|
|||
|
aidSheild: make(map[int64]struct{}),
|
|||
|
midsSheild: make(map[int64]struct{}),
|
|||
|
}
|
|||
|
for idStr, cid := range s.conf.Realname.Threshold {
|
|||
|
ids, err := xstr.SplitInts(idStr)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
for _, id := range ids {
|
|||
|
if _, ok := s.realname[id]; !ok {
|
|||
|
s.realname[id] = cid
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
accountRPC, err := accountCli.NewClient(c.AccountRPC)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
s.accountRPC = accountRPC
|
|||
|
ugcPayRPC, err := ugcPayCli.NewClient(c.UgcPayRPC)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
s.ugcPayRPC = ugcPayRPC
|
|||
|
filterRPC, err := filterCli.NewClient(s.conf.FilterRPC)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
s.filterRPC = filterRPC
|
|||
|
seasonRPC, err := seasonCli.NewClient(c.SeasonRPC)
|
|||
|
if err != nil {
|
|||
|
panic(fmt.Sprintf("seasonCli.NewClient.error(%v)", err))
|
|||
|
}
|
|||
|
s.seasonRPC = seasonRPC
|
|||
|
thumbupRPC, err := thumbupApi.NewClient(c.ThumbupRPC)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
s.thumbupRPC = thumbupRPC
|
|||
|
subtitleLans, err := s.dao.SubtitleLans(context.Background())
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
s.subtitleLans = model.SubtitleLans(subtitleLans)
|
|||
|
go s.broadcastproc()
|
|||
|
go s.archiveTypeproc()
|
|||
|
go s.localcacheproc()
|
|||
|
go s.assistLogproc()
|
|||
|
go s.oplogproc()
|
|||
|
go s.monitorproc()
|
|||
|
go s.viewProc()
|
|||
|
go s.shieldProc()
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
// Ping service ping.
|
|||
|
func (s *Service) Ping(c context.Context) (err error) {
|
|||
|
return s.dao.Ping(c)
|
|||
|
}
|
|||
|
|
|||
|
func (s *Service) archiveTypeproc() {
|
|||
|
var mu sync.Mutex
|
|||
|
for {
|
|||
|
rmap, err := s.dao.TypeMapping(context.TODO())
|
|||
|
if err != nil {
|
|||
|
log.Error("load archive types error(%v)", err)
|
|||
|
} else {
|
|||
|
mu.Lock()
|
|||
|
s.arcTypes = rmap
|
|||
|
mu.Unlock()
|
|||
|
}
|
|||
|
time.Sleep(5 * time.Minute)
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (s *Service) broadcastproc() {
|
|||
|
broadcastFmt := `["%.2f,%d,%d,%d,%d,%d,%d,%s,%d","%s"]`
|
|||
|
for m := range s.broadcastChan {
|
|||
|
if m.Pool == model.PoolSpecial {
|
|||
|
continue
|
|||
|
}
|
|||
|
if m.State != model.StateNormal && m.State != model.StateMonitorAfter {
|
|||
|
continue
|
|||
|
}
|
|||
|
if err := s.dao.BroadcastLimit(context.TODO(), m.Oid, m.Type, s.broadcastLimit, s.broadcastlimitInterval); err != nil {
|
|||
|
if err != ecode.LimitExceed {
|
|||
|
log.Error("dao.BroadcastLimit(oid:%v) error(%v)", m.Oid, err)
|
|||
|
}
|
|||
|
continue
|
|||
|
}
|
|||
|
hash := model.Hash(m.Mid, uint32(m.Content.IP))
|
|||
|
msg := strings.Replace(m.Content.Msg, `\`, `\\`, -1)
|
|||
|
msg = strings.Replace(msg, `"`, `\"`, -1)
|
|||
|
info := fmt.Sprintf(broadcastFmt, float32(m.Progress)/1000.0, m.Content.Mode, m.Content.FontSize,
|
|||
|
m.Content.Color, m.Ctime, m.Rnd, m.Pool, hash, m.ID, msg)
|
|||
|
if err := s.dao.BroadcastInGoim(context.TODO(), m.Oid, m.Aid, []byte(info)); err != nil {
|
|||
|
log.Error("dao.BroadcastInGoim(cid:%d,aid:%d,info:%s) error(%v)", m.Oid, m.Aid, info, err)
|
|||
|
} else {
|
|||
|
log.Info("BroadcastInGoim(%s) succeed", info)
|
|||
|
}
|
|||
|
if err := s.dao.Broadcast(context.Background(), m.Oid, m.Aid, info); err != nil {
|
|||
|
log.Error("dao.Broadcast(cid:%d,aid:%d,info:%s) error(%v)", m.Oid, m.Aid, info, err)
|
|||
|
} else {
|
|||
|
log.Info("broadcast(%s) succeed", info)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (s *Service) localcacheproc() {
|
|||
|
for {
|
|||
|
s.loadLocalcache(s.conf.Localcache.Oids)
|
|||
|
time.Sleep(time.Duration(s.conf.Localcache.Expire))
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
func (s *Service) oplogproc() {
|
|||
|
for opLog := range s.opsLogCh {
|
|||
|
if len(opLog.Subject) == 0 || len(opLog.CurrentVal) == 0 || opLog.Source <= 0 ||
|
|||
|
opLog.Operator <= 0 || opLog.OperatorType <= 0 {
|
|||
|
log.Warn("oplogproc() it is an illegal log, warn(%v, %v, %v)", opLog.Subject, opLog.Subject, opLog.CurrentVal)
|
|||
|
continue
|
|||
|
} else {
|
|||
|
for _, dmid := range opLog.DMIds {
|
|||
|
if dmid > 0 {
|
|||
|
s.dmOperationLogSvc.Info(opLog.Subject, strconv.FormatInt(opLog.Oid, 10), strconv.Itoa(opLog.Type),
|
|||
|
strconv.FormatInt(dmid, 10), opLog.Source.String(), opLog.OriginVal,
|
|||
|
opLog.CurrentVal, strconv.FormatInt(opLog.Operator, 10), opLog.OperatorType.String(),
|
|||
|
opLog.OperationTime, opLog.Remark)
|
|||
|
} else {
|
|||
|
log.Warn("oplogproc() it is an illegal log, for dmid value, warn(%d, %+v)", dmid, opLog)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// OpLog put a new infoc format operation log into the channel
|
|||
|
func (s *Service) OpLog(c context.Context, cid, operator, OperationTime int64, typ int, dmids []int64, subject, originVal, currentVal, remark string, source oplog.Source, operatorType oplog.OperatorType) (err error) {
|
|||
|
infoLog := new(oplog.Infoc)
|
|||
|
infoLog.Oid = cid
|
|||
|
infoLog.Type = typ
|
|||
|
infoLog.DMIds = dmids
|
|||
|
infoLog.Subject = subject
|
|||
|
infoLog.OriginVal = originVal
|
|||
|
infoLog.CurrentVal = currentVal
|
|||
|
infoLog.OperationTime = strconv.FormatInt(OperationTime, 10)
|
|||
|
infoLog.Source = source
|
|||
|
infoLog.OperatorType = operatorType
|
|||
|
infoLog.Operator = operator
|
|||
|
infoLog.Remark = remark
|
|||
|
select {
|
|||
|
case s.opsLogCh <- infoLog:
|
|||
|
default:
|
|||
|
err = fmt.Errorf("opsLogCh full")
|
|||
|
log.Error("opsLogCh full (%v)", infoLog)
|
|||
|
}
|
|||
|
return
|
|||
|
}
|
|||
|
|
|||
|
func (s *Service) monitorproc() {
|
|||
|
for {
|
|||
|
time.Sleep(3 * time.Second)
|
|||
|
s.oidLock.Lock()
|
|||
|
oidMap := s.moniOidMap
|
|||
|
s.moniOidMap = make(map[int64]struct{})
|
|||
|
s.oidLock.Unlock()
|
|||
|
for oid := range oidMap {
|
|||
|
sub, err := s.dao.Subject(context.TODO(), model.SubTypeVideo, oid)
|
|||
|
if err != nil || sub == nil {
|
|||
|
continue
|
|||
|
}
|
|||
|
if err := s.updateMonitorCnt(context.TODO(), sub); err != nil {
|
|||
|
log.Error("s.updateMonitorCnt(%+v) error(%v)", sub, err)
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|