Files
kim.dev.6789 e50142a3b9 复制项目
2026-01-14 22:16:44 +08:00

150 lines
4.0 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package redis
import (
"context"
"errors"
"fmt"
"strconv"
"strings"
"time"
"git.imall.cloud/openim/open-im-server-deploy/pkg/common/storage/cache/cachekey"
"git.imall.cloud/openim/protocol/constant"
"github.com/openimsdk/tools/errs"
"github.com/redis/go-redis/v9"
)
const onlineUserCountHistorySeparator = ":"
// OnlineUserCountSample 在线人数历史采样点
type OnlineUserCountSample struct {
// Timestamp 采样时间(毫秒时间戳)
Timestamp int64
// Count 采样在线人数
Count int64
}
// GetOnlineUserCount 读取在线人数缓存
func GetOnlineUserCount(ctx context.Context, rdb redis.UniversalClient) (int64, error) {
if rdb == nil {
return 0, errs.ErrInternalServer.WrapMsg("redis client is nil")
}
val, err := rdb.Get(ctx, cachekey.OnlineUserCountKey).Result()
if err != nil {
if errors.Is(err, redis.Nil) {
return 0, err
}
return 0, errs.Wrap(err)
}
count, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return 0, errs.WrapMsg(err, "parse online user count failed")
}
return count, nil
}
// RefreshOnlineUserCount 刷新在线人数缓存
func RefreshOnlineUserCount(ctx context.Context, rdb redis.UniversalClient) (int64, error) {
if rdb == nil {
return 0, errs.ErrInternalServer.WrapMsg("redis client is nil")
}
var (
cursor uint64
total int64
)
now := strconv.FormatInt(time.Now().Unix(), 10)
for {
keys, nextCursor, err := rdb.Scan(ctx, cursor, fmt.Sprintf("%s*", cachekey.OnlineKey), constant.ParamMaxLength).Result()
if err != nil {
return 0, errs.Wrap(err)
}
for _, key := range keys {
count, err := rdb.ZCount(ctx, key, now, "+inf").Result()
if err != nil {
return 0, errs.Wrap(err)
}
if count > 0 {
total++
}
}
cursor = nextCursor
if cursor == 0 {
break
}
}
if err := rdb.Set(ctx, cachekey.OnlineUserCountKey, total, 0).Err(); err != nil {
return 0, errs.Wrap(err)
}
return total, nil
}
// AppendOnlineUserCountHistory 写入在线人数历史采样
func AppendOnlineUserCountHistory(ctx context.Context, rdb redis.UniversalClient, timestamp int64, count int64) error {
if rdb == nil {
return errs.ErrInternalServer.WrapMsg("redis client is nil")
}
if timestamp <= 0 {
return errs.ErrArgs.WrapMsg("invalid timestamp")
}
member := fmt.Sprintf("%d%s%d", timestamp, onlineUserCountHistorySeparator, count)
if err := rdb.ZAdd(ctx, cachekey.OnlineUserCountHistoryKey, redis.Z{
Score: float64(timestamp),
Member: member,
}).Err(); err != nil {
return errs.Wrap(err)
}
// 清理历史数据,避免无界增长
retentionMs := int64(cachekey.OnlineUserCountHistoryRetention / time.Millisecond)
cutoff := timestamp - retentionMs
if cutoff > 0 {
if err := rdb.ZRemRangeByScore(ctx, cachekey.OnlineUserCountHistoryKey, "0", strconv.FormatInt(cutoff, 10)).Err(); err != nil {
return errs.Wrap(err)
}
}
return nil
}
// GetOnlineUserCountHistory 读取在线人数历史采样
func GetOnlineUserCountHistory(ctx context.Context, rdb redis.UniversalClient, startTime int64, endTime int64) ([]OnlineUserCountSample, error) {
if rdb == nil {
return nil, errs.ErrInternalServer.WrapMsg("redis client is nil")
}
if startTime <= 0 || endTime <= 0 || endTime <= startTime {
return nil, nil
}
// 包含endTime的数据使用endTime作为最大值
values, err := rdb.ZRangeByScore(ctx, cachekey.OnlineUserCountHistoryKey, &redis.ZRangeBy{
Min: strconv.FormatInt(startTime, 10),
Max: strconv.FormatInt(endTime, 10),
}).Result()
if err != nil {
if errors.Is(err, redis.Nil) {
return nil, nil
}
return nil, errs.Wrap(err)
}
if len(values) == 0 {
return nil, nil
}
samples := make([]OnlineUserCountSample, 0, len(values))
for _, val := range values {
parts := strings.SplitN(val, onlineUserCountHistorySeparator, 2)
if len(parts) != 2 {
continue
}
ts, err := strconv.ParseInt(parts[0], 10, 64)
if err != nil {
continue
}
cnt, err := strconv.ParseInt(parts[1], 10, 64)
if err != nil {
continue
}
samples = append(samples, OnlineUserCountSample{
Timestamp: ts,
Count: cnt,
})
}
return samples, nil
}