From 46f9de01d667416b057affdc4bef34cea4f5f6e1 Mon Sep 17 00:00:00 2001 From: Master Kwoth Date: Tue, 12 Sep 2017 22:27:51 +0200 Subject: [PATCH] Using redis to cache avatar images, reduced xp image size. --- NadekoBot.sln | 5 +++- .../Modules/Xp/Services/XpService.cs | 29 +++++++++---------- src/NadekoBot/Modules/Xp/Xp.cs | 8 +++-- src/NadekoBot/NadekoBot.cs | 1 + src/NadekoBot/NadekoBot.csproj | 6 ++++ src/NadekoBot/Services/IDataCache.cs | 14 +++++++++ src/NadekoBot/Services/Impl/RedisCache.cs | 28 ++++++++++++++++++ 7 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 src/NadekoBot/Services/IDataCache.cs create mode 100644 src/NadekoBot/Services/Impl/RedisCache.cs diff --git a/NadekoBot.sln b/NadekoBot.sln index 8c235940..42b343e5 100644 --- a/NadekoBot.sln +++ b/NadekoBot.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26430.12 +VisualStudioVersion = 15.0.26730.3 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}" EndProject @@ -33,4 +33,7 @@ Global GlobalSection(NestedProjects) = preSolution {45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4} + EndGlobalSection EndGlobal diff --git a/src/NadekoBot/Modules/Xp/Services/XpService.cs b/src/NadekoBot/Modules/Xp/Services/XpService.cs index 78dcc5f9..f82741bf 100644 --- a/src/NadekoBot/Modules/Xp/Services/XpService.cs +++ b/src/NadekoBot/Modules/Xp/Services/XpService.cs @@ -36,6 +36,7 @@ namespace NadekoBot.Modules.Xp.Services private readonly IImagesService _images; private readonly Logger _log; private readonly NadekoStrings _strings; + private readonly IDataCache _cache; private readonly FontCollection _fonts = new FontCollection(); public const int XP_REQUIRED_LVL_1 = 36; @@ -54,9 +55,6 @@ namespace NadekoBot.Modules.Xp.Services private readonly ConcurrentQueue _addMessageXp = new ConcurrentQueue(); - private readonly ConcurrentDictionary _imageStreams - = new ConcurrentDictionary(); - private readonly Timer updateXpTimer; private readonly HttpClient http = new HttpClient(); private FontFamily _usernameFontFamily; @@ -69,7 +67,7 @@ namespace NadekoBot.Modules.Xp.Services public XpService(CommandHandler cmd, IBotConfigProvider bc, IEnumerable allGuildConfigs, IImagesService images, - DbService db, NadekoStrings strings) + DbService db, NadekoStrings strings, IDataCache cache) { _db = db; _cmd = cmd; @@ -77,6 +75,7 @@ namespace NadekoBot.Modules.Xp.Services _images = images; _log = LogManager.GetCurrentClassLogger(); _strings = strings; + _cache = cache; //load settings allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null); @@ -665,20 +664,20 @@ namespace NadekoBot.Modules.Xp.Services { var avatarUrl = stats.User.RealAvatarUrl(); - byte[] s; - if (!_imageStreams.TryGetValue(avatarUrl, out s)) + var (succ, data) = await _cache.TryGetImageDataAsync(avatarUrl); + if (!succ) { using (var temp = await http.GetStreamAsync(avatarUrl)) { var tempDraw = Image.Load(temp); tempDraw = tempDraw.Resize(69, 70); ApplyRoundedCorners(tempDraw, 35); - s = tempDraw.ToStream().ToArray(); + data = tempDraw.ToStream().ToArray(); } - _imageStreams.AddOrUpdate(avatarUrl, s, (k, v) => s); + await _cache.SetImageDataAsync(avatarUrl, data); } - var toDraw = Image.Load(s); + var toDraw = Image.Load(data); img.DrawImage(toDraw, @@ -699,20 +698,20 @@ namespace NadekoBot.Modules.Xp.Services var imgUrl = stats.User.Club.ImageUrl; try { - byte[] s; - if (!_imageStreams.TryGetValue(imgUrl, out s)) + var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl); + if (!succ) { using (var temp = await http.GetStreamAsync(imgUrl)) { var tempDraw = Image.Load(temp); tempDraw = tempDraw.Resize(45, 45); ApplyRoundedCorners(tempDraw, 22.5f); - s = tempDraw.ToStream().ToArray(); + data = tempDraw.ToStream().ToArray(); } - _imageStreams.AddOrUpdate(imgUrl, s, (k, v) => s); + await _cache.SetImageDataAsync(imgUrl, data); } - var toDraw = Image.Load(s); + var toDraw = Image.Load(data); img.DrawImage(toDraw, 1, @@ -724,7 +723,7 @@ namespace NadekoBot.Modules.Xp.Services _log.Warn(ex); } } - + img.Resize(432, 211); var arr = img.ToStream().ToArray(); //_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB()); diff --git a/src/NadekoBot/Modules/Xp/Xp.cs b/src/NadekoBot/Modules/Xp/Xp.cs index ad687792..ee88aa00 100644 --- a/src/NadekoBot/Modules/Xp/Xp.cs +++ b/src/NadekoBot/Modules/Xp/Xp.cs @@ -26,12 +26,16 @@ namespace NadekoBot.Modules.Xp public async Task Experience([Remainder]IUser user = null) { user = user ?? Context.User; - + var sw = Stopwatch.StartNew(); await Context.Channel.TriggerTypingAsync(); var img = await _service.GenerateImageAsync((IGuildUser)user); - + sw.Stop(); + _log.Info("Generating finished in {0:F2}s", sw.Elapsed.TotalSeconds); + sw.Restart(); await Context.Channel.SendFileAsync(img.ToStream(), $"{user.Id}_xp.png") .ConfigureAwait(false); + sw.Stop(); + _log.Info("Sending finished in {0:F2}s", sw.Elapsed.TotalSeconds); } [NadekoCommand, Usage, Description, Aliases] diff --git a/src/NadekoBot/NadekoBot.cs b/src/NadekoBot/NadekoBot.cs index a92380ca..a85da294 100644 --- a/src/NadekoBot/NadekoBot.cs +++ b/src/NadekoBot/NadekoBot.cs @@ -139,6 +139,7 @@ namespace NadekoBot .AddManual>(AllGuildConfigs) //todo wrap this .AddManual(this) .AddManual(uow) + .AddManual(new RedisCache()) .LoadFrom(Assembly.GetEntryAssembly()) .Build(); diff --git a/src/NadekoBot/NadekoBot.csproj b/src/NadekoBot/NadekoBot.csproj index ff8ed03e..1a5bf786 100644 --- a/src/NadekoBot/NadekoBot.csproj +++ b/src/NadekoBot/NadekoBot.csproj @@ -21,6 +21,7 @@ 1.0.0.0 nadeko_icon.ico win7-x64 + Debug;Release;global_nadeko @@ -78,6 +79,7 @@ + @@ -95,6 +97,10 @@ latest + + latest + + diff --git a/src/NadekoBot/Services/IDataCache.cs b/src/NadekoBot/Services/IDataCache.cs new file mode 100644 index 00000000..67223dfe --- /dev/null +++ b/src/NadekoBot/Services/IDataCache.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace NadekoBot.Services +{ + public interface IDataCache + { + Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key); + Task SetImageDataAsync(string key, byte[] data); + } +} diff --git a/src/NadekoBot/Services/Impl/RedisCache.cs b/src/NadekoBot/Services/Impl/RedisCache.cs new file mode 100644 index 00000000..b4d9a89d --- /dev/null +++ b/src/NadekoBot/Services/Impl/RedisCache.cs @@ -0,0 +1,28 @@ +using StackExchange.Redis; +using System.Threading.Tasks; + +namespace NadekoBot.Services.Impl +{ + public class RedisCache : IDataCache + { + private readonly ConnectionMultiplexer _redis; + private readonly IDatabase _db; + + public RedisCache() + { + _redis = ConnectionMultiplexer.Connect("localhost"); + _db = _redis.GetDatabase(); + } + + public async Task<(bool Success, byte[] Data)> TryGetImageDataAsync(string key) + { + byte[] x = await _db.StringGetAsync("image_" + key); + return (x != null, x); + } + + public Task SetImageDataAsync(string key, byte[] data) + { + return _db.StringSetAsync("image_" + key, data); + } + } +}