go-common/app/interface/main/dm2/service/service.go

325 lines
9.9 KiB
Go
Raw Normal View History

2019-04-22 10:49:16 +00:00
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分区idvalue: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)
}
}
}
}