239 lines
5.4 KiB
Go
239 lines
5.4 KiB
Go
|
package service
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"math"
|
||
|
"strconv"
|
||
|
"strings"
|
||
|
"sync"
|
||
|
"time"
|
||
|
|
||
|
"go-common/app/interface/live/push-live/conf"
|
||
|
"go-common/app/interface/live/push-live/dao"
|
||
|
"go-common/library/cache/redis"
|
||
|
"go-common/library/log"
|
||
|
"go-common/library/queue/databus"
|
||
|
)
|
||
|
|
||
|
var (
|
||
|
_limitDecreaseUUIDKey = "ld:%s" // 接口请求防重复key
|
||
|
|
||
|
errLimitRequestRepeat = errors.New("limit decrease request repeat")
|
||
|
errConvertMidString = errors.New("convert mid string error")
|
||
|
errConvertBusiness = errors.New("convert business error")
|
||
|
)
|
||
|
|
||
|
// Service struct
|
||
|
type Service struct {
|
||
|
c *conf.Config
|
||
|
dao *dao.Dao
|
||
|
liveStartSub *databus.Databus
|
||
|
liveCommonSub *databus.Databus
|
||
|
wg sync.WaitGroup
|
||
|
closeCh chan bool
|
||
|
pushTypes []string
|
||
|
intervalExpired int32
|
||
|
mutex sync.RWMutex
|
||
|
}
|
||
|
|
||
|
// New init
|
||
|
func New(c *conf.Config) (s *Service) {
|
||
|
s = &Service{
|
||
|
c: c,
|
||
|
dao: dao.New(c),
|
||
|
liveStartSub: databus.New(c.LiveRoomSub),
|
||
|
liveCommonSub: databus.New(c.LiveCommonSub),
|
||
|
closeCh: make(chan bool),
|
||
|
pushTypes: make([]string, 0, 4),
|
||
|
mutex: sync.RWMutex{},
|
||
|
}
|
||
|
s.wg.Add(1)
|
||
|
go s.loadPushConfig()
|
||
|
|
||
|
for i := 0; i < c.Push.ConsumerProcNum; i++ {
|
||
|
s.wg.Add(1)
|
||
|
go s.liveMessageConsumeproc()
|
||
|
}
|
||
|
|
||
|
return s
|
||
|
}
|
||
|
|
||
|
// loadPushConfig Load push config
|
||
|
func (s *Service) loadPushConfig() {
|
||
|
var ctx = context.TODO()
|
||
|
defer s.wg.Done()
|
||
|
for {
|
||
|
select {
|
||
|
case _, ok := <-s.closeCh:
|
||
|
if !ok {
|
||
|
log.Info("[service.push|loadPushConfig] s.loadPushConfig is closed by closeCh")
|
||
|
return
|
||
|
}
|
||
|
default:
|
||
|
}
|
||
|
// get push delay time
|
||
|
interval, err := s.dao.GetPushInterval(ctx)
|
||
|
if err != nil || interval < 0 {
|
||
|
time.Sleep(time.Duration(time.Minute))
|
||
|
continue
|
||
|
}
|
||
|
s.mutex.Lock()
|
||
|
s.intervalExpired = interval
|
||
|
s.mutex.Unlock()
|
||
|
|
||
|
// get push options
|
||
|
types, err := s.dao.GetPushConfig(ctx)
|
||
|
if err != nil || len(types) == 0 {
|
||
|
time.Sleep(time.Duration(time.Minute))
|
||
|
continue
|
||
|
}
|
||
|
s.mutex.Lock()
|
||
|
s.pushTypes = types
|
||
|
s.mutex.Unlock()
|
||
|
|
||
|
time.Sleep(time.Duration(time.Minute))
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// safeGetExpired
|
||
|
func (s *Service) safeGetExpired() int32 {
|
||
|
s.mutex.RLock()
|
||
|
expired := s.intervalExpired
|
||
|
s.mutex.RUnlock()
|
||
|
return expired
|
||
|
}
|
||
|
|
||
|
// LimitDecrease do mid string limit decrease
|
||
|
func (s *Service) LimitDecrease(ctx context.Context, business, targetID, uuid, midStr string) (err error) {
|
||
|
var (
|
||
|
f *dao.Filter
|
||
|
mids []int64
|
||
|
b int
|
||
|
)
|
||
|
|
||
|
// 判断请求是否重复
|
||
|
err = s.limitDecreaseUnique(getUniqueKey(business, targetID, uuid))
|
||
|
if err != nil {
|
||
|
log.Error("[service.service|LimitDecrease] limitDecreaseUnique error(%v), uuid(%s), business(%s), targetID(%s), mid(%s)",
|
||
|
err, uuid, business, targetID, midStr)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
b, err = strconv.Atoi(business)
|
||
|
if err != nil {
|
||
|
log.Error("[service.service|LimitDecrease] strconv business params error(%v)", err)
|
||
|
err = errConvertBusiness
|
||
|
return
|
||
|
}
|
||
|
filterConf := &dao.FilterConfig{
|
||
|
Business: b,
|
||
|
DailyExpired: dailyExpired(time.Now())}
|
||
|
|
||
|
// convert mid string to []int64
|
||
|
mids, err = s.convertStrToInt64(midStr)
|
||
|
if err != nil {
|
||
|
log.Error("[service.service|LimitDecrease] convertStrToInt64 error(%v), business(%s), uuid(%s), mids(%s)",
|
||
|
err, business, uuid, midStr)
|
||
|
err = errConvertMidString
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// aysnc decrease limit
|
||
|
f, err = s.dao.NewFilter(filterConf)
|
||
|
if err != nil {
|
||
|
log.Error("[service.service|LimitDecrease] new filter error(%v), business(%s), uuid(%s), mids(%v)",
|
||
|
err, business, uuid, mids)
|
||
|
return
|
||
|
}
|
||
|
go f.BatchDecreaseLimit(ctx, mids)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// Ping Service
|
||
|
func (s *Service) Ping(c context.Context) (err error) {
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Close Service
|
||
|
func (s *Service) Close() {
|
||
|
close(s.closeCh)
|
||
|
s.subClose()
|
||
|
s.wg.Wait()
|
||
|
s.dao.Close()
|
||
|
}
|
||
|
|
||
|
// subClose Close all sub channels
|
||
|
func (s *Service) subClose() {
|
||
|
s.liveCommonSub.Close()
|
||
|
s.liveStartSub.Close()
|
||
|
}
|
||
|
|
||
|
// dailyExpired
|
||
|
func dailyExpired(from time.Time) float64 {
|
||
|
tm1 := time.Date(from.Year(), from.Month(), from.Day(), 0, 0, 0, 0, from.Location())
|
||
|
tm2 := tm1.AddDate(0, 0, 1)
|
||
|
return math.Floor(tm2.Sub(from).Seconds())
|
||
|
}
|
||
|
|
||
|
// convertStrToInt64 convert mid string to []int64 slice
|
||
|
func (s *Service) convertStrToInt64(m string) (mInts []int64, err error) {
|
||
|
var (
|
||
|
mSplit []string
|
||
|
errCount int
|
||
|
)
|
||
|
if m == "" {
|
||
|
return
|
||
|
}
|
||
|
mSplit = strings.Split(m, ",")
|
||
|
for _, mStr := range mSplit {
|
||
|
mInt, convErr := strconv.Atoi(mStr)
|
||
|
if convErr != nil {
|
||
|
log.Error("[service.push|formatMidstr] convert mid(%v), error(%v)", mStr, convErr)
|
||
|
errCount++
|
||
|
continue
|
||
|
}
|
||
|
mInts = append(mInts, int64(mInt))
|
||
|
}
|
||
|
if errCount == len(mSplit) {
|
||
|
err = fmt.Errorf("[service.push|formatMidstr] convert all mid failed, midstr(%s)", m)
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// limitDecreaseUnique
|
||
|
func (s *Service) limitDecreaseUnique(key string) (err error) {
|
||
|
var (
|
||
|
conn redis.Conn
|
||
|
reply interface{}
|
||
|
)
|
||
|
defer func() {
|
||
|
if conn != nil {
|
||
|
conn.Close()
|
||
|
}
|
||
|
}()
|
||
|
conn, err = redis.Dial(s.c.Redis.PushInterval.Proto, s.c.Redis.PushInterval.Addr, s.dao.RedisOption()...)
|
||
|
if err != nil {
|
||
|
log.Error("[service.service|limitDecreaseUnique] redis.Dial error(%v)", err)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// redis cache exists judgement
|
||
|
reply, err = conn.Do("SET", key, time.Now(), "EX", dailyExpired(time.Now()), "NX")
|
||
|
if err != nil {
|
||
|
return
|
||
|
}
|
||
|
// key exists
|
||
|
if reply == nil {
|
||
|
err = errLimitRequestRepeat
|
||
|
return
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// getUniqueKey get request unique key
|
||
|
func getUniqueKey(a, b, c string) string {
|
||
|
return fmt.Sprintf(_limitDecreaseUUIDKey, a+b+c)
|
||
|
}
|