//------------------------------------------------------------------------------
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权
// CSDN博客:https://blog.csdn.net/qq_40374647
// 哔哩哔哩视频:https://space.bilibili.com/94253567
// Gitee源代码仓库:https://gitee.com/RRQM_Home
// Github源代码仓库:https://github.com/RRQM
// API首页:https://touchsocket.net/
// 交流QQ群:234762506
// 感谢您的下载和使用
//------------------------------------------------------------------------------
using System;
using System.Threading.Tasks;
using TouchSocket.Core;
using TouchSocket.Resources;
namespace TouchSocket.Dmtp.Redis;
///
/// DmtpRedisActor 类,实现了 IDmtpRedisActor 接口。
/// 该类通过 Redis 操作,为分布式消息传输协议(Dmtp)提供演员(Actor)模型的实现。
///
internal sealed class DmtpRedisActor :DisposableObject, IDmtpRedisActor
{
///
/// 初始化DmtpRedisActor类的新实例。
///
/// 一个IDmtpActor接口的实现,用于处理actor的具体逻辑。
public DmtpRedisActor(IDmtpActor dmtpActor)
{
this.DmtpActor = dmtpActor;
}
///
public BytesSerializerConverter Converter { get; set; }
///
public IDmtpActor DmtpActor { get; }
///
public ICache ICache { get; set; }
///
public int Timeout { get; set; } = 30 * 1000;
///
public async Task AddAsync(string key, TValue value, int duration = 60000)
{
var cache = new CacheEntry(key)
{
Duration = TimeSpan.FromSeconds(duration)
};
if (!(value is byte[]))
{
cache.Value = this.Converter.Serialize(null, value);
}
return await this.AddCacheAsync(cache).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
///
public async Task AddCacheAsync(ICacheEntry entity)
{
return !await this.ContainsCacheAsync(entity.Key).ConfigureAwait(EasyTask.ContinueOnCapturedContext) && await this.SetCacheAsync(entity).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
///
public async Task ClearCacheAsync()
{
var package = new RedisRequestWaitPackage
{
packageType = RedisPackageType.Clear
};
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
try
{
using (var byteBlock = new ByteBlock(1024*64))
{
var block = byteBlock;
package.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
case WaitDataStatus.SetRunning:
{
if (waitData.WaitResult.Status == 1)
{
return;
}
else
{
throw new Exception(waitData.WaitResult.Message);
}
}
case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription());
case WaitDataStatus.Canceled:
case WaitDataStatus.Default:
case WaitDataStatus.Disposed:
default:
throw new Exception(TouchSocketDmtpStatus.UnknownError.GetDescription());
}
}
finally
{
this.DmtpActor.WaitHandlePool.Destroy(waitData);
}
}
///
public async Task ContainsCacheAsync(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key));
}
var package = new RedisRequestWaitPackage
{
key = key,
packageType = RedisPackageType.Contains
};
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
try
{
using (var byteBlock = new ByteBlock(1024*64))
{
var block = byteBlock;
package.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
case WaitDataStatus.SetRunning:
{
return waitData.WaitResult.Status == 1
? true
: waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message);
}
case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription());
case WaitDataStatus.Canceled:
case WaitDataStatus.Default:
case WaitDataStatus.Disposed:
default:
throw new Exception(TouchSocketDmtpStatus.UnknownError.GetDescription());
}
}
finally
{
this.DmtpActor.WaitHandlePool.Destroy(waitData);
}
}
///
public async Task GetAsync(string key)
{
var cache = await this.GetCacheAsync(key).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
if (cache != null)
{
if (cache.Value is null)
{
return default;
}
if (cache.Value is TValue value1)
{
return value1;
}
var value = (TValue)this.Converter.Deserialize(null, cache.Value, typeof(TValue));
return value;
}
return default;
}
///
public async Task> GetCacheAsync(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key));
}
var package = new RedisRequestWaitPackage()
{
key = key,
packageType = RedisPackageType.Get
};
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
try
{
using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024))
{
var block = byteBlock;
package.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
case WaitDataStatus.SetRunning:
{
var responsePackage = (RedisResponseWaitPackage)waitData.WaitResult;
return responsePackage.Status == 1
? new CacheEntry(key)
{
Value = responsePackage.value
}
: responsePackage.Status == byte.MaxValue ? new CacheEntry(key) : (ICacheEntry)default;
}
case WaitDataStatus.Overtime: throw new TimeoutException(TouchSocketDmtpStatus.Overtime.GetDescription());
case WaitDataStatus.Canceled:
case WaitDataStatus.Default:
case WaitDataStatus.Disposed:
default:
throw new TimeoutException(TouchSocketDmtpStatus.UnknownError.GetDescription());
}
}
finally
{
this.DmtpActor.WaitHandlePool.Destroy(waitData);
}
}
///
/// 处理收到的消息
///
/// 接收到的消息对象
/// 返回一个异步任务,指示处理是否成功
public async Task InputReceivedData(DmtpMessage message)
{
if (message.ProtocolFlags == this.m_redis_Request)
{
var waitResult = new RedisResponseWaitPackage();
try
{
var package = new RedisRequestWaitPackage();
var block = message.BodyByteBlock;
package.Unpackage(ref block);
waitResult.Sign = package.Sign;
switch (package.packageType)
{
case RedisPackageType.Set:
{
var success = this.ICache.SetCache(new CacheEntry(package.key)
{
Duration = package.timeSpan.Value,
Value = package.value
});
waitResult.Status = success ? (byte)1 : byte.MaxValue;
break;
}
case RedisPackageType.Get:
{
var cache = this.ICache.GetCache(package.key);
if (cache != null)
{
waitResult.Status = 1;
waitResult.value = cache.Value;
}
else
{
waitResult.Status = byte.MaxValue;
}
}
break;
case RedisPackageType.Contains:
{
waitResult.Status = this.ICache.ContainsCache(package.key) ? (byte)1 : byte.MaxValue;
}
break;
case RedisPackageType.Remove:
{
waitResult.Status = this.ICache.RemoveCache(package.key) ? (byte)1 : byte.MaxValue;
}
break;
case RedisPackageType.Clear:
{
this.ICache.ClearCache();
waitResult.Status = 1;
}
break;
default:
return true;
}
}
catch (Exception ex)
{
waitResult.Status = 2;
waitResult.Message = ex.Message;
}
using (var byteBlock = new ByteBlock(1024*64))
{
var block = byteBlock;
waitResult.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Response, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
return true;
}
else if (message.ProtocolFlags == this.m_redis_Response)
{
var waitResult = new RedisResponseWaitPackage();
var block = message.BodyByteBlock;
waitResult.Unpackage(ref block);
this.DmtpActor.WaitHandlePool.SetRun(waitResult);
return true;
}
return false;
}
///
public async Task RemoveCacheAsync(string key)
{
if (string.IsNullOrEmpty(key))
{
throw new ArgumentException($"“{nameof(key)}”不能为 null 或空。", nameof(key));
}
var package = new RedisRequestWaitPackage
{
key = key,
packageType = RedisPackageType.Remove
};
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
try
{
using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024))
{
var block = byteBlock;
package.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
case WaitDataStatus.SetRunning:
{
return waitData.WaitResult.Status == 1
? true
: waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message);
}
case WaitDataStatus.Overtime: throw new TimeoutException(Resources.TouchSocketDmtpStatus.Overtime.GetDescription());
case WaitDataStatus.Canceled: return false;
case WaitDataStatus.Default:
case WaitDataStatus.Disposed:
default:
throw new TimeoutException(Resources.TouchSocketDmtpStatus.UnknownError.GetDescription());
}
}
finally
{
this.DmtpActor.WaitHandlePool.Destroy(waitData);
}
}
///
public async Task SetAsync(string key, TValue value, int duration = 60000)
{
var cache = new CacheEntry(key)
{
Duration = TimeSpan.FromSeconds(duration),
Value = value is byte[] bytes ? bytes : this.Converter.Serialize(null, value)
};
return await this.SetCacheAsync(cache).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
///
public async Task SetCacheAsync(ICacheEntry cache)
{
if (string.IsNullOrEmpty(cache.Key))
{
throw new ArgumentException($"“{nameof(cache.Key)}”不能为 null 或空。", nameof(cache.Key));
}
if (cache is null)
{
throw new ArgumentNullException(nameof(cache));
}
var package = new RedisRequestWaitPackage
{
key = cache.Key,
timeSpan = cache.Duration,
value = cache.Value,
packageType = RedisPackageType.Set
};
var waitData = this.DmtpActor.WaitHandlePool.GetWaitDataAsync(package);
try
{
using (var byteBlock = new ByteBlock((package.value == null ? 0 : package.value.Length) + 1024))
{
var block = byteBlock;
package.Package(ref block);
await this.DmtpActor.SendAsync(this.m_redis_Request, byteBlock.Memory).ConfigureAwait(EasyTask.ContinueOnCapturedContext);
}
switch (await waitData.WaitAsync(this.Timeout).ConfigureAwait(EasyTask.ContinueOnCapturedContext))
{
case WaitDataStatus.SetRunning:
{
return waitData.WaitResult.Status == 1 || (waitData.WaitResult.Status == byte.MaxValue ? false : throw new Exception(waitData.WaitResult.Message));
}
case WaitDataStatus.Overtime: throw new TimeoutException(Resources.TouchSocketDmtpStatus.Overtime.GetDescription());
case WaitDataStatus.Canceled: return false;
case WaitDataStatus.Default:
case WaitDataStatus.Disposed:
default:
throw new TimeoutException(TouchSocketDmtpStatus.UnknownError.GetDescription());
}
}
finally
{
this.DmtpActor.WaitHandlePool.Destroy(waitData);
}
}
///
/// 设置处理协议标识的起始标识。
///
///
public void SetProtocolFlags(ushort start)
{
this.m_redis_Request = start++;
this.m_redis_Response = start++;
}
#region 字段
private ushort m_redis_Request;
private ushort m_redis_Response;
#endregion 字段
}