Asp.Net 具有 Redis 实现的核心在高流量时给出 502 错误



在我们的应用程序中,我们的用户流量很大,每秒大约 2000 个请求。 我们已经在 Asp.Net 核心中创建了应用程序并使用了dapper。我们使用 redis 缓存管理器来实现分布式缓存目的。 当我们托管此站点并每秒检查它很少(20 或 30)个请求时,它工作正常。但是,当我们每秒点击超过 50 个请求时,站点会给出
502 - Web 服务器在充当网关或代理服务器时收到无效响应。
我们将 redis 缓存更改为内存缓存,然后它开始对所有每秒 2000 个请求正常工作。 我们正在使用 redis 版本 3.2.100 因此,在这里使用 redis,我们无法为更多请求运行此站点,并在流量大的情况下收到 502 错误。

为 Redis 缓存编写的代码

using Common;
using Common.DependencyInjection;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Text;
namespace Service.Caching
{
[TransientDependency(ServiceType = typeof(ICacheManager))]
public class RedisCacheManager : ICacheManager
{
private readonly string _redisHost;
private readonly int _redisPort;
private readonly string _redisPassword;
private readonly ConfigurationOptions _configurationOptions;
public RedisCacheManager()
{
_redisHost = ConfigItems.RedisHost;
_redisPassword = ConfigItems.RedisPassword;
_redisPort = ConfigItems.RedisPort;
_configurationOptions = new ConfigurationOptions();
configurationOptions.EndPoints.Add(_redisHost, redisPort);
_configurationOptions.Ssl = false;
//_configurationOptions.Password = _redisPassword;
_configurationOptions.AbortOnConnectFail = false;
_configurationOptions.SyncTimeout = int.MaxValue;
_configurationOptions.DefaultVersion = new Version(2, 8, 8);
_configurationOptions.WriteBuffer = 10000000;
_configurationOptions.KeepAlive = 180;
}
/// <summary>
/// Gets or sets the value associated with the specified key.
/// </summary>
/// <typeparam name="T">Type</typeparam>
/// <param name="key">The key of the value to get.</param>
/// <returns>
/// The value associated with the specified key.
/// </returns>
public T Get<T>(string key)
{
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var db = connection.GetDatabase(-1);
//return (T)(object)db.StringGet(key);
return (T)ConvertToObject<T>(db.StringGet(key));
}
}
/// <summary>
/// Adds the specified key and object to the cache.
/// </summary>
/// <param name="key">key</param>
/// <param name="data">Data</param>
/// <param name="cacheTime">Cache time</param>
public void Set(string key, object data, int cacheTime)
{
if (data == null)
return;
DateTime expireDate;
if (cacheTime == 99)
expireDate = DateTime.Now + TimeSpan.FromSeconds(30);
else
expireDate = DateTime.Now + TimeSpan.FromMinutes(cacheTime);
var value = (RedisValue)ConvertToByteArray(data);
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var db = connection.GetDatabase(-1);
db.StringSet(key, value, new TimeSpan(expireDate.Ticks));
}
}
/// <summary>
/// Gets a value indicating whether the value associated with the specified key is cached
/// </summary>
/// <param name="key">key</param>
/// <returns>
/// Result
/// </returns>
public bool IsSet(string key)
{            
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var db = connection.GetDatabase(-1);
return db.KeyExists(key);
}
}
/// <summary>
/// Removes the value with the specified key from the cache
/// </summary>
/// <param name="key">/key</param>
public void Remove(string key)
{
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var db = connection.GetDatabase(-1);
db.KeyDelete(key);
}
}
/// <summary>
/// Removes items by pattern
/// </summary>
/// <param name="pattern">pattern</param>
public void RemoveByPattern(string pattern)
{            
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var server = connection.GetServer(_redisHost, _redisPort);
var keysToRemove = server.Keys(pattern: "*" + pattern + "*");//-1, pattern);
foreach (var key in keysToRemove)
Remove(key);
}
}
/// <summary>
/// Clear all cache data
/// </summary>
public void Clear()
{
using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
{
var server = connection.GetServer(_redisHost, _redisPort);
//var keysToRemove = server.Keys(-1);
var keysToRemove = server.Keys();
foreach (var key in keysToRemove)
Remove(key);
}
}
/// <summary>
/// Converts to byte array.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>System.Byte[].</returns>
private byte[] ConvertToByteArray(object data)
{
if (data == null)
return null;
string serialize = JsonConvert.SerializeObject(data);
return Encoding.UTF8.GetBytes(serialize);
}
/// <summary>
/// Converts to object.
/// </summary>
/// <param name="data">The data.</param>
/// <returns>System.Object.</returns>
private T ConvertToObject<T>(byte[] data)
{
try
{
return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(data));
}
catch (Exception ex)
{
return (T)Activator.CreateInstance(typeof(T));
}
}
}

}

您对 Redis 缓存中的获取/设置/删除键/值的调用可能需要更长的时间,因为您要在对 Redis 的每个操作上创建新的连接多路复用器。

https://gist.github.com/JonCole/925630df72be1351b21440625ff2671f#file-redis-bestpractices-stackexchange-redis-md 在使用StackExchange.Redis时有一些最佳实践。

https://learn.microsoft.com/en-us/azure/redis-cache/cache-dotnet-how-to-use-azure-redis-cache#connect-to-the-cache 显示了建议的使用模式。

另外,看看StackExchange.Redis文档(https://stackexchange.github.io/StackExchange.Redis/Basics),其中说"因为ConnectionMultiplexer做了很多事情,它被设计为在调用者之间共享和重用。不应为每个操作创建连接多路复用器。它是完全线程安全的,并为此用法做好了准备。在所有后续示例中,将假定您存储了一个连接多路复用器实例以供重用"。

最新更新