Redis/NewLife.Redis/Services/RedisCacheProvider.cs

135 lines
5.2 KiB
C#
Raw 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.

using NewLife.Configuration;
using NewLife.Log;
using NewLife.Model;
namespace NewLife.Caching.Services;
/// <summary>Redis缓存服务。由Redis提供标准缓存和队列服务锁定配置名RedisCache可以在配置中心统一设置</summary>
/// <remarks>
/// 根据实际开发经验,即使在分布式系统中,也有大量的数据是不需要跨进程共享的,因此本接口提供了两级缓存。
/// 借助该缓存架构可以实现各功能模块跨进程共享数据分布式部署时可用Redis需要考虑序列化成本。
///
/// 使用队列时,可根据是否设置消费组来决定使用简单队列还是完整队列。
/// 简单队列如RedisQueue可用作命令队列Topic很多但几乎没有消息。
/// 完整队列如RedisStream可用作消息队列Topic很少但消息很多并且支持多消费组。
/// </remarks>
public class RedisCacheProvider : CacheProvider
{
#region
private FullRedis? _redis;
private FullRedis? _redisQueue;
/// <summary>队列</summary>
public FullRedis? RedisQueue { get => _redisQueue; set => _redisQueue = value; }
#endregion
#region
/// <summary>实例化Redis缓存服务</summary>
public RedisCacheProvider() { }
/// <summary>实例化Redis缓存服务自动创建FullRedis对象</summary>
/// <param name="serviceProvider"></param>
public RedisCacheProvider(IServiceProvider serviceProvider)
{
var config = serviceProvider?.GetService<IConfigProvider>();
config ??= JsonConfigProvider.LoadAppSettings();
if (config != null) Init(config, serviceProvider);
}
#endregion
#region
/// <summary>初始化</summary>
/// <param name="config"></param>
/// <param name="serviceProvider"></param>
public void Init(IConfigProvider config, IServiceProvider? serviceProvider = null)
{
var cacheConn = config["RedisCache"];
var queueConn = config["RedisQueue"];
// 实例化全局缓存和队列,如果未设置队列,则使用缓存对象
FullRedis? redis = null;
if (!cacheConn.IsNullOrEmpty())
{
if (serviceProvider != null)
{
//redis = serviceProvider.GetService<FullRedis>();
//if (redis != null && redis.Name != "RedisCache") redis = null;
redis = new FullRedis(serviceProvider, "RedisCache")
{
Log = serviceProvider.GetService<ILog>()!,
Tracer = serviceProvider.GetService<ITracer>(),
};
}
else
{
redis = new FullRedis { Name = "RedisCache", Log = XTrace.Log };
redis.Init(cacheConn);
}
_redis = redis;
_redisQueue = redis;
Cache = redis;
}
if (!queueConn.IsNullOrEmpty())
{
if (serviceProvider != null)
{
//redis = serviceProvider.GetService<FullRedis>();
//if (redis != null && redis.Name != "RedisQueue") redis = null;
redis = new FullRedis(serviceProvider, "RedisQueue")
{
Log = serviceProvider.GetService<ILog>()!,
Tracer = serviceProvider.GetService<ITracer>(),
};
}
else
{
redis = new FullRedis { Name = "RedisQueue", Log = XTrace.Log };
redis.Init(queueConn);
}
_redisQueue = redis;
}
}
/// <summary>获取队列。各功能模块跨进程共用的队列默认使用LIST带消费组时使用STREAM</summary>
/// <remarks>
/// 使用队列时,可根据是否设置消费组来决定使用简单队列还是完整队列。
/// 简单队列如RedisQueue可用作命令队列Topic很多但几乎没有消息。
/// 完整队列如RedisStream可用作消息队列Topic很少但消息很多并且支持多消费组。
/// </remarks>
/// <typeparam name="T">消息类型。用于消息生产者时可指定为Object</typeparam>
/// <param name="topic">主题</param>
/// <param name="group">消费组。未指定消费组时使用简单队列如RedisQueue指定消费组时使用完整队列如RedisStream</param>
/// <returns></returns>
public override IProducerConsumer<T> GetQueue<T>(String topic, String? group = null)
{
if (_redisQueue != null)
{
IProducerConsumer<T>? queue = null;
if (group.IsNullOrEmpty())
{
queue = _redisQueue.GetQueue<T>(topic);
XTrace.WriteLine("[{0}]队列消息数:{1}", topic, queue.Count);
}
else
{
var rs = _redisQueue.GetStream<T>(topic);
//rs.Group = group;
rs.SetGroup(group);
queue = rs;
XTrace.WriteLine("[{0}/{2}]队列消息数:{1}", topic, queue.Count, group);
}
return queue;
}
return base.GetQueue<T>(topic, group);
}
#endregion
}