游戏列表异步实验

This commit is contained in:
zpc 2024-11-09 03:07:53 +08:00
parent 1fb5621af7
commit ea1fc8a322
9 changed files with 280 additions and 133 deletions

View File

@ -56,7 +56,7 @@ namespace CloudGaming.Api.Controllers
/// <returns></returns>
[HttpGet]
[RedisCache(2, 2, 0)]
public GameInfoDto GetGameInfo([FromQuery] string gameId)
public Task<GameInfoDto> GetGameInfo([FromQuery] string gameId)
{
GameBLL gamebll = new GameBLL(this.ServiceProvider);
return gamebll.GetGameInfo(gameId);
@ -68,7 +68,7 @@ namespace CloudGaming.Api.Controllers
/// <returns></returns>
[HttpGet]
[RedisCache(0, 5, 0)]
public List<GameListDto> GameRecommendations([FromQuery] string? gameId)
public Task<List<GameListDto>> GameRecommendations([FromQuery] string? gameId)
{
GameBLL gamebll = new GameBLL(this.ServiceProvider);

View File

@ -95,7 +95,7 @@ namespace CloudGaming.Code.Cache
get
{
return GameEntityCache?.DataList ?? new List<GameInfo>();
return GameEntityCache?.DataListAsync.Result;// ?? new List<GameInfo>();
}
}

View File

@ -10,6 +10,8 @@ using CloudGaming.GameModel.Db.Db_Game;
using HuanMeng.DotNetCore.CacheHelper;
using HuanMeng.DotNetCore.Redis;
using Microsoft.Extensions.Caching.Memory;
using Newtonsoft.Json;
using Org.BouncyCastle.Utilities.Collections;
@ -24,37 +26,63 @@ namespace CloudGaming.Code.Cache.Special
/// <summary>
/// 游戏缓存表
/// </summary>
public class GameEntityCache(DAO dao, IDatabase database, IMapper mapper, AppConfig appConfig) : CommonDataEntityCache<GameInfo>(GameEntityCache.GameEntityCacheLock, 60 * 60 * 24 * 7)
public class GameEntityCache(DAO dao, IDatabase database, IMapper mapper, AppConfig appConfig) :
CommonDataEntityCacheAsync<GameInfo>(GameEntityCache.SemaphoreSlim, 60 * 60 * 24)
//CommonDataEntityCache<GameInfo>(GameEntityCache.GameEntityCacheLock, 60 * 60 * 24 * 7)
{
public static object GameEntityCacheLock;
/// <summary>
/// 异步锁
/// </summary>
public static SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1);
/// <summary>
/// 内存key
/// </summary>
public override string key => $"{appConfig.Identifier}:game:gameInfo";
/// <summary>
/// 缓存锁key
/// </summary>
public string locaKey => $"lock:gameInfo";
/// <summary>
/// redis 缓存key
/// </summary>
public string RedisKey => $"cache:game:gameInfo";
/// <summary>
/// 缓存时间
/// </summary>
public const int CacheHoursTime = 10;
public override List<GameInfo> GetDataList()
/// <summary>
/// 游戏数据
/// </summary>
/// <returns></returns>
public override async Task<List<GameInfo>> GetDataListAsync()
{
var gameCbtList = dao.DaoPhone.Context.T_GameCBT.AsNoTracking().Where(it => it.IsOnline).ToList() ?? new List<T_GameCBT>();
var gameListDict = dao.DaoGame.Context.T_Game_List.AsNoTracking().ToDictionary(g => g.GameId);
var gameChildList = dao.DaoGame.Context.T_Game_ChildList.AsNoTracking().ToList();
var gameTypesDict = dao.DaoGame.Context.T_Game_Types.AsNoTracking().ToDictionary(type => type.TypeId);
var gameTagsDict = dao.DaoGame.Context.T_Game_Tags.AsNoTracking().ToDictionary(tag => tag.TagId);
//游戏分享头像
var gameUserShare = dao.DaoGame.Context.T_Game_UserShare.AsNoTracking().GroupBy(it => it.GameId).ToDictionary(it => it.Key, it => it.Last().NickName);
var appConfig = dao.DaoExt.Context.T_App_Config.AsNoTracking().Where(it => it.ConfigType == 3).ToList();
var gameCbtList = await dao.DaoPhone.Context.T_GameCBT.AsNoTracking().Where(it => it.IsOnline).ToListAsync() ?? new List<T_GameCBT>();
//游戏列表
var gameListDict = await dao.DaoGame.Context.T_Game_List.AsNoTracking().ToDictionaryAsync(g => g.GameId);
//游戏标签、分类关联
var gameChildList = await dao.DaoGame.Context.T_Game_ChildList.AsNoTracking().ToListAsync();
//游戏分类
var gameTypesDict = await dao.DaoGame.Context.T_Game_Types.AsNoTracking().ToDictionaryAsync(type => type.TypeId);
//标签
var gameTagsDict = await dao.DaoGame.Context.T_Game_Tags.AsNoTracking().ToDictionaryAsync(tag => tag.TagId);
//游戏用户分享
var gameUserShare = await dao.DaoGame.Context.T_Game_UserShare.AsNoTracking().GroupBy(it => it.GameId).ToDictionaryAsync(it => it.Key, it => it.Last().NickName);
//游戏配置
var appConfig = await dao.DaoExt.Context.T_App_Config.AsNoTracking().Where(it => it.ConfigType == 3).ToListAsync();
//统一消耗配置
var config = appConfig.GetAppConfig(3, 1, null);
var defaultConsumeDiamondNumHour = 0;
if (!string.IsNullOrEmpty(config?.ConfigValue))
{
int.TryParse(config?.ConfigValue, out defaultConsumeDiamondNumHour);
}
//var lorem = new Bogus.DataSets.Lorem(locale: "zh_CN");
//生成用户昵称
var faker = new Faker("zh_CN");
//f.r
//lorem.
//组装游戏
var gameInfos = gameCbtList
.Where(gameCbt => gameListDict.ContainsKey(gameCbt.GameId))
.Select(gameCbt =>
@ -76,7 +104,6 @@ namespace CloudGaming.Code.Cache.Special
gameInfo.ConsumeDiamondNumHour = defaultConsumeDiamondNumHour;
}
if (gameInfo.ConsumeDiamondNumHour > 0)
{
gameInfo.GameDetailsofCharges = $"游戏资费:游玩按分钟计费,{gameInfo.ConsumeDiamondNumHour}钻石/小时。";
@ -92,85 +119,93 @@ namespace CloudGaming.Code.Cache.Special
return gameInfos;
}
/// <summary>
///
/// </summary>
public override List<GameInfo> DataList
public override Task<List<GameInfo>> DataListAsync
{
get
{
start:
if (_dataList != null) return _dataList;
if (_dataList != null) return Task.FromResult(_dataList);
var tempDataList = MemoryCacheHelper.GetCache<List<GameInfo>>(key);
if (tempDataList != null)
{
_dataList = tempDataList;
return _dataList;
return Task.FromResult(_dataList);
}
long hashLength = database.HashLength(RedisKey);
return Task.Run(async () =>
{
start:
long hashLength = await database.HashLengthAsync(RedisKey);
if (hashLength > 0)
{
var hashEntries = database.HashGetAll(RedisKey);
var hashEntries = await database.HashGetAllAsync(RedisKey);
var list = hashEntries
.Where(entry => !string.IsNullOrEmpty(entry.Value))
.Select(entry => JsonConvert.DeserializeObject<GameInfo>(entry.Value))
.ToList();
MemoryCacheHelper.SetCache(key, list, 10);
MemoryCacheHelper.SetCache(key, list, TimeSpan.FromHours(CacheHoursTime));
_dataList = list;
return _dataList;
}
if (!database.StringSetLock(locaKey, "1", 5))
{
await Task.Delay(50);
goto start;
}
tempDataList ??= GetDataList();
try
{
tempDataList ??= await GetDataListAsync();
MemoryCacheHelper.SetCache(key, tempDataList, TimeSpan.FromHours(CacheHoursTime));
var serializedGameInfos = tempDataList
.Select(info => new HashEntry($"gameInfo:{info.GameId}", JsonConvert.SerializeObject(info)))
.ToArray();
database.HashSet(RedisKey, serializedGameInfos);
MemoryCacheHelper.SetCache(key, tempDataList, 60 * 60);
await database.HashSetAsync(RedisKey, serializedGameInfos);
// 设置过期时间为1天
await database.KeyExpireAsync(RedisKey, TimeSpan.FromDays(1));
_dataList = tempDataList;
}
finally
{
database.KeyDelete(locaKey);
}
return _dataList;
});
}
}
//public Task<List<GameInfo>> DataListAsync
//{
// get
// {
// return new List<GameInfo>();
// }
//}
private Dictionary<string, GameInfo> gameInfoDic;
/// <summary>
/// 游戏详情
/// </summary>
public Dictionary<string, GameInfo> GameInfoDic
public Task<Dictionary<string, GameInfo>> GameInfoAsync
{
get
{
if (gameInfoDic == null)
if (gameInfoDic != null)
{
gameInfoDic = DataList.ToDictionary(it => it.GameId);
return Task.FromResult(gameInfoDic);
}
return gameInfoDic;
return Task.Run(async () => { gameInfoDic = (await DataListAsync).ToDictionary(it => it.GameId); return gameInfoDic; });
}
}
/// <summary>
/// 获取游戏详情
/// </summary>
/// <param name="gameId"></param>
/// <returns></returns>
public GameInfo? this[string? gameId]
public Task<GameInfo?> this[string? gameId]
{
get
{
@ -178,15 +213,11 @@ namespace CloudGaming.Code.Cache.Special
{
return null;
}
return GameInfoDic[gameId] ?? null;
return Task.Run(async () => (await GameInfoAsync)[gameId] ?? null);
}
}
private List<GameExtendedAttribute> GetGameExtendedAttributes<T>(
List<T_Game_ChildList> gameChildList,
string gameId,
int childType,
Dictionary<int, T> dictionary) where T : class
private List<GameExtendedAttribute> GetGameExtendedAttributes<T>(List<T_Game_ChildList> gameChildList, string gameId, int childType, Dictionary<int, T> dictionary) where T : class
{
return gameChildList
.Where(it => it.GameId == gameId && it.ChildType == childType && (it.ChildId ?? 0) > 0)
@ -217,25 +248,26 @@ namespace CloudGaming.Code.Cache.Special
public override bool ClearData()
{
lock (GameEntityCacheLock)
{
database.KeyDelete(key);
MemoryCacheHelper.DelCache(key);
database.KeyDelete(RedisKey);
_dataList = null;
gameInfoDic = null;
}
return true;
}
public override void ReloadData()
{
lock (lockObj)
{
database.KeyDelete(key);
database.KeyDelete(RedisKey);
MemoryCacheHelper.DelCache(key);
_dataList = null;
var x = DataList;
}
var x = DataListAsync.Result;
}
}
}

View File

@ -26,39 +26,33 @@ namespace CloudGaming.Code.Epg
public async Task<List<EpgCategoryDto>> GetHomeInfo()
{
var listQueryable = GetEpgCategory(EpgEnum.EpgCatIdName.).AsQueryable();
if (IsChecking)
{
listQueryable = listQueryable.Where(it => it.ShowStatus == 0 || it.ShowStatus == 2).AsQueryable();
}
else
{
listQueryable = listQueryable.Where(it => it.ShowStatus == 0 || it.ShowStatus == 1).AsQueryable();
}
var list = listQueryable.OrderBy(it => it.OrderId).ToList();
int[] showStatus = IsChecking ? [0, 2] : [0, 1];
var list = listQueryable.Where(it => showStatus.Contains(it.ShowStatus)).OrderBy(it => it.OrderId).ToList();
List<EpgCategoryDto> epgCategoryDtos = new List<EpgCategoryDto>();
list.ForEach(it =>
foreach (var it in list)
{
var list = new List<EpgInfo>()
var epglist = new List<EpgInfo>()
{
//STEAMCLOUD
};
var epgList = GetEpgList(it.Id);
epgList?.ForEach(item =>
foreach (var item in epgList)
{
//如果首页展示数量小于集合数量,则退出
if (it.ShowNumIndex < list.Count)
if (it.ShowNumIndex < epglist.Count)
{
return;
break;
}
var epgInfo = item.ToEpgInfo(Cache.GameEntityCache);
var epgInfo = await item.ToEpgInfo(Cache.GameEntityCache);
if (epgInfo != null)
{
list.Add(epgInfo);
epglist.Add(epgInfo);
}
}
});
if (list.Count > 0)
if (epglist.Count > 0)
{
EpgCategoryDto epgCategoryDto = new EpgCategoryDto()
{
@ -66,12 +60,13 @@ namespace CloudGaming.Code.Epg
CategoryType = it.IdName,
IsQuickStartPopUp = it.IsQuickStartPopUp ?? false,
ShowNum_Index = it.ShowNumIndex ?? 0,
EpgList = list
EpgList = epglist
};
epgCategoryDtos.Add(epgCategoryDto);
}
});
}
return epgCategoryDtos;
}
/// <summary>

View File

@ -20,7 +20,7 @@ namespace CloudGaming.Code.Epg
/// <param name="epgCfg"></param>
/// <param name="gameEntityCache"></param>
/// <returns></returns>
public static EpgInfo ToEpgInfo(this T_Epg_Cfg epgCfg, GameEntityCache gameEntityCache)
public static async Task<EpgInfo> ToEpgInfo(this T_Epg_Cfg epgCfg, GameEntityCache gameEntityCache)
{
if (epgCfg.ResType == (int)EpgEnum.EpgResType. && string.IsNullOrEmpty(epgCfg.ResId))
{
@ -41,7 +41,7 @@ namespace CloudGaming.Code.Epg
if (epgCfg.ResType == (int)EpgEnum.EpgResType.)
{
var gameInfo = gameEntityCache[epgCfg.ResId];
var gameInfo = await gameEntityCache[epgCfg.ResId];
if (gameInfo == null)
{
return null;

View File

@ -24,10 +24,8 @@ namespace CloudGaming.Code.Game
/// <returns></returns>
public async Task<List<GameExtendedAttribute>> GetGameTypeListAsync()
{
var gameList = Cache.GameEntityCache?.DataList ?? new List<GameInfo>();
var gameTypes = await Task.Run(() =>
{
return CloudGamingCacheExtend.GetDataEntityCache<T_Game_Types>(this, it => it.IsOnline)?.DataList
var gameList = await Cache.GameEntityCache.DataListAsync;
var list = CloudGamingCacheExtend.GetDataEntityCache<T_Game_Types>(this, it => it.IsOnline)?.DataList
.Where(it => gameList.Any(item => item.GameType?.Any(gameType => gameType.Id == it.TypeId) ?? false))
.OrderBy(it => it.OrderId)
.Select(it => new GameExtendedAttribute
@ -37,9 +35,9 @@ namespace CloudGaming.Code.Game
OrderId = it.OrderId
})
.ToList();
});
return list;
return gameTypes;
}
/// <summary>
@ -49,14 +47,8 @@ namespace CloudGaming.Code.Game
/// <returns></returns>
public async Task<List<GameListDto>> GetGameListAsync(int typeId)
{
var gameListDto = await Task.Run(() =>
{
var gameList = Cache.GameEntityCache?.DataList ?? new List<GameInfo>();
var x = gameList.Where(it => it.GameType?.Any(item => item.Id == typeId) ?? false).Select(it => new
GameListDto(it)).ToList();
return x;
});
var gameList = await Cache.GameEntityCache.DataListAsync;
var gameListDto = gameList.Where(it => it.GameType?.Any(item => item.Id == typeId) ?? false).Select(it => new GameListDto(it)).ToList();
return gameListDto;
}
@ -65,13 +57,13 @@ namespace CloudGaming.Code.Game
/// </summary>
/// <param name="gameId"></param>
/// <returns></returns>
public GameInfoDto GetGameInfo(string gameId)
public async Task<GameInfoDto> GetGameInfo(string gameId)
{
if (string.IsNullOrEmpty(gameId))
{
return null;
}
var game = Cache.GameEntityCache[gameId];
var game = await Cache.GameEntityCache[gameId];
if (game == null)
{
return null;
@ -85,12 +77,12 @@ namespace CloudGaming.Code.Game
/// </summary>
/// <param name="gameId"></param>
/// <returns></returns>
public List<GameListDto> GameRecommendations(string gameId)
public async Task<List<GameListDto>> GameRecommendations(string gameId)
{
List<GameInfo>? gameInfos = null;
if (!string.IsNullOrEmpty(gameId))
{
var game = Cache.GameEntityCache[gameId];
var game = await Cache.GameEntityCache[gameId];
if (game != null)
{
var gameTagIds = game.GameTags.Select(it => it.Id);

View File

@ -32,7 +32,7 @@ namespace CloudGaming.Code.MiddlewareExtend
{
// 检查当前请求是否需要缓存
var cacheAttribute = GetCacheAttribute(context);
if (cacheAttribute == null)
if (cacheAttribute == null || true)
{
await _next(context);
return;

View File

@ -1,11 +1,14 @@
using HuanMeng.DotNetCore.CacheHelper.Contract;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace HuanMeng.DotNetCore.CacheHelper
@ -54,15 +57,11 @@ namespace HuanMeng.DotNetCore.CacheHelper
if (tempDataList == null)
{
lock (lockObj)
{
tempDataList = MemoryCacheHelper.GetCache<List<T>>(key);
if (tempDataList == null)
{
tempDataList = GetDataList();
MemoryCacheHelper.SetCache(key, tempDataList, cacheTime);
}
}
}
_dataList = JsonConvert.DeserializeObject<List<T>>(JsonConvert.SerializeObject(tempDataList));
}
return _dataList ?? new List<T>();
@ -95,4 +94,120 @@ namespace HuanMeng.DotNetCore.CacheHelper
}
}
}
/// <summary>
/// 异步缓存
/// </summary>
/// <typeparam name="T"></typeparam>
public abstract class CommonDataEntityCacheAsync<T> : ICacheClearData, ICacheReloadData where T : class
{
/// <summary>
/// 过期时间
/// </summary>
protected int cacheTime;
private readonly SemaphoreSlim _semaphore; // 异步锁
/// <summary>
///
/// </summary>
/// <param name="semaphore">异步锁new SemaphoreSlim(initialCount: 1, maxCount: 1); </param>
/// <param name="cacheTime"></param>
protected CommonDataEntityCacheAsync(SemaphoreSlim semaphore, int cacheTime = 36000)
{
//new SemaphoreSlim(initialCount: 1, maxCount: 1);
this._semaphore = semaphore;
this.cacheTime = cacheTime;
}
/// <summary>
///
/// </summary>
public abstract string key { get; }
/// <summary>
/// 缓存数据
/// </summary>
protected List<T>? _dataList;
/// <summary>
/// 数据
/// </summary>
public virtual Task<List<T>> DataListAsync
{
get
{
// 如果已有缓存数据,直接返回
if (_dataList != null)
{
return Task.FromResult(_dataList);
}
// 尝试从缓存获取
var tempDataList = MemoryCacheHelper.GetCache<List<T>>(key);
if (tempDataList != null)
{
_dataList = JsonConvert.DeserializeObject<List<T>>(JsonConvert.SerializeObject(tempDataList)) ?? new List<T>();
return Task.FromResult(_dataList);
}
return Task.Run(async () =>
{
// 进行异步加锁,避免多线程竞争
await _semaphore.WaitAsync();
try
{
// 重新检查缓存
tempDataList = MemoryCacheHelper.GetCache<List<T>>(key);
if (tempDataList == null)
{
tempDataList = await GetDataListAsync(); // 异步获取数据
MemoryCacheHelper.SetCache(key, tempDataList, cacheTime);
}
_dataList = JsonConvert.DeserializeObject<List<T>>(JsonConvert.SerializeObject(tempDataList)) ?? new List<T>();
return _dataList;
}
finally
{
_semaphore.Release(); // 释放锁
}
});
}
}
/// <summary>
/// 获取缓存数据
/// </summary>
public abstract Task<List<T>> GetDataListAsync();
/// <summary>
/// 清除缓存
/// </summary>
/// <returns></returns>
public virtual bool ClearData()
{
MemoryCacheHelper.DelCache(key);
_dataList = null;
return true;
}
/// <summary>
/// 重新加载缓存
/// </summary>
public virtual void ReloadData()
{
var tempDataList = GetDataListAsync().Result;
MemoryCacheHelper.SetCache(key, tempDataList, cacheTime);
_dataList = tempDataList;
}
}
}

View File

@ -1,4 +1,5 @@
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
@ -40,6 +41,18 @@ namespace HuanMeng.DotNetCore.CacheHelper
cache.Set(cacheName, val, TimeSpan.FromSeconds(cacheTime));
}
/// <summary>
/// 设置缓存
/// </summary>
/// <param name="cacheName"></param>
/// <param name="val"></param>
/// <param name="cacheTime">单位秒默认1小时</param>
public static void SetCache(string cacheName, object val, TimeSpan timeSpan)
{
cache.Set(cacheName, val, timeSpan);
}
/// <summary>
/// 删除缓存
/// </summary>