Using redis to cache avatar images, reduced xp image size.

This commit is contained in:
Master Kwoth 2017-09-12 22:27:51 +02:00
parent 90b698f18e
commit 46f9de01d6
7 changed files with 73 additions and 18 deletions

View File

@ -1,7 +1,7 @@
 
Microsoft Visual Studio Solution File, Format Version 12.00 Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15 # Visual Studio 15
VisualStudioVersion = 15.0.26430.12 VisualStudioVersion = 15.0.26730.3
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}"
EndProject EndProject
@ -33,4 +33,7 @@ Global
GlobalSection(NestedProjects) = preSolution GlobalSection(NestedProjects) = preSolution
{45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2} {45EC1473-C678-4857-A544-07DFE0D0B478} = {04929013-5BAB-42B0-B9B2-8F2BB8F16AF2}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5F3F555C-855F-4BE8-B526-D062D3E8ACA4}
EndGlobalSection
EndGlobal EndGlobal

View File

@ -36,6 +36,7 @@ namespace NadekoBot.Modules.Xp.Services
private readonly IImagesService _images; private readonly IImagesService _images;
private readonly Logger _log; private readonly Logger _log;
private readonly NadekoStrings _strings; private readonly NadekoStrings _strings;
private readonly IDataCache _cache;
private readonly FontCollection _fonts = new FontCollection(); private readonly FontCollection _fonts = new FontCollection();
public const int XP_REQUIRED_LVL_1 = 36; public const int XP_REQUIRED_LVL_1 = 36;
@ -54,9 +55,6 @@ namespace NadekoBot.Modules.Xp.Services
private readonly ConcurrentQueue<UserCacheItem> _addMessageXp private readonly ConcurrentQueue<UserCacheItem> _addMessageXp
= new ConcurrentQueue<UserCacheItem>(); = new ConcurrentQueue<UserCacheItem>();
private readonly ConcurrentDictionary<string, byte[]> _imageStreams
= new ConcurrentDictionary<string, byte[]>();
private readonly Timer updateXpTimer; private readonly Timer updateXpTimer;
private readonly HttpClient http = new HttpClient(); private readonly HttpClient http = new HttpClient();
private FontFamily _usernameFontFamily; private FontFamily _usernameFontFamily;
@ -69,7 +67,7 @@ namespace NadekoBot.Modules.Xp.Services
public XpService(CommandHandler cmd, IBotConfigProvider bc, public XpService(CommandHandler cmd, IBotConfigProvider bc,
IEnumerable<GuildConfig> allGuildConfigs, IImagesService images, IEnumerable<GuildConfig> allGuildConfigs, IImagesService images,
DbService db, NadekoStrings strings) DbService db, NadekoStrings strings, IDataCache cache)
{ {
_db = db; _db = db;
_cmd = cmd; _cmd = cmd;
@ -77,6 +75,7 @@ namespace NadekoBot.Modules.Xp.Services
_images = images; _images = images;
_log = LogManager.GetCurrentClassLogger(); _log = LogManager.GetCurrentClassLogger();
_strings = strings; _strings = strings;
_cache = cache;
//load settings //load settings
allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null); allGuildConfigs = allGuildConfigs.Where(x => x.XpSettings != null);
@ -665,20 +664,20 @@ namespace NadekoBot.Modules.Xp.Services
{ {
var avatarUrl = stats.User.RealAvatarUrl(); var avatarUrl = stats.User.RealAvatarUrl();
byte[] s; var (succ, data) = await _cache.TryGetImageDataAsync(avatarUrl);
if (!_imageStreams.TryGetValue(avatarUrl, out s)) if (!succ)
{ {
using (var temp = await http.GetStreamAsync(avatarUrl)) using (var temp = await http.GetStreamAsync(avatarUrl))
{ {
var tempDraw = Image.Load(temp); var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(69, 70); tempDraw = tempDraw.Resize(69, 70);
ApplyRoundedCorners(tempDraw, 35); 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, img.DrawImage(toDraw,
@ -699,20 +698,20 @@ namespace NadekoBot.Modules.Xp.Services
var imgUrl = stats.User.Club.ImageUrl; var imgUrl = stats.User.Club.ImageUrl;
try try
{ {
byte[] s; var (succ, data) = await _cache.TryGetImageDataAsync(imgUrl);
if (!_imageStreams.TryGetValue(imgUrl, out s)) if (!succ)
{ {
using (var temp = await http.GetStreamAsync(imgUrl)) using (var temp = await http.GetStreamAsync(imgUrl))
{ {
var tempDraw = Image.Load(temp); var tempDraw = Image.Load(temp);
tempDraw = tempDraw.Resize(45, 45); tempDraw = tempDraw.Resize(45, 45);
ApplyRoundedCorners(tempDraw, 22.5f); 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, img.DrawImage(toDraw,
1, 1,
@ -724,7 +723,7 @@ namespace NadekoBot.Modules.Xp.Services
_log.Warn(ex); _log.Warn(ex);
} }
} }
img.Resize(432, 211);
var arr = img.ToStream().ToArray(); var arr = img.ToStream().ToArray();
//_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB()); //_log.Info("{0:F2} KB", arr.Length * 1.0f / 1.KB());

View File

@ -26,12 +26,16 @@ namespace NadekoBot.Modules.Xp
public async Task Experience([Remainder]IUser user = null) public async Task Experience([Remainder]IUser user = null)
{ {
user = user ?? Context.User; user = user ?? Context.User;
var sw = Stopwatch.StartNew();
await Context.Channel.TriggerTypingAsync(); await Context.Channel.TriggerTypingAsync();
var img = await _service.GenerateImageAsync((IGuildUser)user); 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") await Context.Channel.SendFileAsync(img.ToStream(), $"{user.Id}_xp.png")
.ConfigureAwait(false); .ConfigureAwait(false);
sw.Stop();
_log.Info("Sending finished in {0:F2}s", sw.Elapsed.TotalSeconds);
} }
[NadekoCommand, Usage, Description, Aliases] [NadekoCommand, Usage, Description, Aliases]

View File

@ -139,6 +139,7 @@ namespace NadekoBot
.AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this .AddManual<IEnumerable<GuildConfig>>(AllGuildConfigs) //todo wrap this
.AddManual<NadekoBot>(this) .AddManual<NadekoBot>(this)
.AddManual<IUnitOfWork>(uow) .AddManual<IUnitOfWork>(uow)
.AddManual<IDataCache>(new RedisCache())
.LoadFrom(Assembly.GetEntryAssembly()) .LoadFrom(Assembly.GetEntryAssembly())
.Build(); .Build();

View File

@ -21,6 +21,7 @@
<FileVersion>1.0.0.0</FileVersion> <FileVersion>1.0.0.0</FileVersion>
<ApplicationIcon>nadeko_icon.ico</ApplicationIcon> <ApplicationIcon>nadeko_icon.ico</ApplicationIcon>
<RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers> <RuntimeIdentifiers>win7-x64<!--;ubuntu.14.04-x64;osx.10.10-x64 --></RuntimeIdentifiers>
<Configurations>Debug;Release;global_nadeko</Configurations>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition=" '$(Version)' == '' "> <PropertyGroup Condition=" '$(Version)' == '' ">
@ -78,6 +79,7 @@
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="NLog" Version="5.0.0-beta03" /> <PackageReference Include="NLog" Version="5.0.0-beta03" />
<PackageReference Include="NYoutubeDL" Version="0.4.4" /> <PackageReference Include="NYoutubeDL" Version="0.4.4" />
<PackageReference Include="StackExchange.Redis" Version="1.2.6" />
<PackageReference Include="System.ValueTuple" Version="4.4.0-preview1-25305-02" /> <PackageReference Include="System.ValueTuple" Version="4.4.0-preview1-25305-02" />
<PackageReference Include="System.Xml.XPath" Version="4.3.0" /> <PackageReference Include="System.Xml.XPath" Version="4.3.0" />
</ItemGroup> </ItemGroup>
@ -95,6 +97,10 @@
<LangVersion>latest</LangVersion> <LangVersion>latest</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='global_nadeko|AnyCPU'">
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup> <ItemGroup>
<DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" /> <DotNetCliToolReference Include="Microsoft.EntityFrameworkCore.Tools.DotNet" Version="1.0.0" />
<DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" /> <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="1.0.0" />

View File

@ -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);
}
}

View File

@ -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);
}
}
}