11 KiB
11 KiB
UDP数据推送与Redis缓存方案
1. 需求分析
当UDP数据到达时,需要将数据推送到一些外部接口,但这些接口所需的数据(如房间号)在UDP数据中并不包含,而是存储在数据库中。为了减少数据库查询开销,提高推送效率,需要:
- 定期从数据库获取必要信息并更新到Redis
- UDP数据到达时,从Redis快速获取所需信息
- 实现高效的数据推送机制
2. 解决方案设计
2.1 定时任务设计
创建一个定时任务,定期从ApiController获取主机信息并更新到Redis:
public class HostInfoSyncJob : IJob
{
public void Execute()
{
try
{
// 创建HttpClient实例
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(System.Configuration.ConfigurationManager.AppSettings["CurrentUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 调用API获取所有主机信息
var response = client.GetAsync("api/GetAllHostInfo").Result;
if (response.IsSuccessStatusCode)
{
var hostInfos = response.Content.ReadAsAsync<List<HostInfoDto>>().Result;
// 更新Redis缓存
foreach (var hostInfo in hostInfos)
{
// 缓存主机基本信息
string hostInfoKey = CacheKey.HostInfo_Key_HostNumber + "_" + hostInfo.HostNumber;
CSRedisCacheHelper.Forever<HostInfoDto>(hostInfoKey, hostInfo);
// 缓存主机号到房间号的映射
string hostToRoomKey = CacheKey.HostToRoom_Mapping + "_" + hostInfo.HostNumber;
CSRedisCacheHelper.Forever<string>(hostToRoomKey, hostInfo.RoomNumber);
// 缓存房间号到主机号的映射
string roomToHostKey = CacheKey.RoomToHost_Mapping + "_" + hostInfo.RoomNumber;
CSRedisCacheHelper.Forever<string>(roomToHostKey, hostInfo.HostNumber);
}
}
}
}
catch (Exception ex)
{
log4net.LogManager.GetLogger(typeof(HostInfoSyncJob)).Error("同步主机信息到Redis失败: " + ex.Message);
}
}
}
2.2 ApiController扩展
在ApiController中添加获取所有主机信息的接口:
/// <summary>
/// 获取所有主机信息,用于定时同步到Redis
/// </summary>
/// <returns></returns>
public ActionResult GetAllHostInfo()
{
try
{
List<HostInfoDto> hostInfos = new List<HostInfoDto>();
// 获取所有酒店
var hotels = SysHotelManager.LoadAll();
foreach (var hotel in hotels)
{
// 获取酒店下所有主机
var hosts = HostManager.LoadAll(hotel.ID);
foreach (var host in hosts)
{
HostInfoDto dto = new HostInfoDto
{
HostNumber = host.HostNumber,
RoomNumber = host.RoomNumber,
HotelID = hotel.ID,
HotelCode = hotel.Code,
HotelName = hotel.Name,
Status = host.Status,
RoomStatus = host.RoomStatus?.Name,
MAC = host.MAC
};
hostInfos.Add(dto);
}
}
return Json(hostInfos, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
logger.Error("获取所有主机信息失败: " + ex.Message);
return Json(new List<HostInfoDto>(), JsonRequestBehavior.AllowGet);
}
}
2.3 UDP数据处理与推送
在UDP数据处理模块中,从Redis获取所需信息并推送到外部接口:
public class UdpDataPusher
{
private static readonly string PushApiUrl = System.Configuration.ConfigurationManager.AppSettings["PushApiUrl"];
public static void PushUdpData(string hostNumber, string udpData)
{
try
{
// 从Redis获取主机信息
string hostInfoKey = CacheKey.HostInfo_Key_HostNumber + "_" + hostNumber;
var hostInfo = CSRedisCacheHelper.Get<HostInfoDto>(hostInfoKey);
if (hostInfo == null)
{
// 如果Redis中没有,从数据库获取并更新Redis
hostInfo = GetHostInfoFromDatabase(hostNumber);
if (hostInfo == null)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("无法获取主机信息: " + hostNumber);
return;
}
// 更新到Redis
CSRedisCacheHelper.Forever<HostInfoDto>(hostInfoKey, hostInfo);
}
// 构建推送数据
PushData pushData = new PushData
{
HostNumber = hostNumber,
RoomNumber = hostInfo.RoomNumber,
HotelCode = hostInfo.HotelCode,
HotelName = hostInfo.HotelName,
UdpData = udpData,
Timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")
};
// 推送到外部接口
SendPushRequest(pushData);
}
catch (Exception ex)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("推送UDP数据失败: " + ex.Message);
}
}
private static HostInfoDto GetHostInfoFromDatabase(string hostNumber)
{
// 创建HttpClient实例
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(System.Configuration.ConfigurationManager.AppSettings["CurrentUrl"]);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
// 调用API获取主机信息
var response = client.GetAsync($"api/GetHostInfo?hostNumber={hostNumber}").Result;
if (response.IsSuccessStatusCode)
{
return response.Content.ReadAsAsync<HostInfoDto>().Result;
}
}
return null;
}
private static void SendPushRequest(PushData data)
{
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(PushApiUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var content = new StringContent(JsonConvert.SerializeObject(data), Encoding.UTF8, "application/json");
var response = client.PostAsync("", content).Result;
if (!response.IsSuccessStatusCode)
{
log4net.LogManager.GetLogger(typeof(UdpDataPusher)).Error("推送失败: " + response.StatusCode);
}
}
}
}
2.4 缓存键设计
在CacheKey类中添加新的缓存键常量:
public class CacheKey
{
// 现有缓存键...
// 新增缓存键
public static string HostToRoom_Mapping = "HostToRoom_Mapping";
public static string RoomToHost_Mapping = "RoomToHost_Mapping";
public static string HostInfo_Key_HostNumber = "HostInfo_Key_HostNumber";
}
2.5 数据模型设计
public class HostInfoDto
{
public string HostNumber { get; set; }
public string RoomNumber { get; set; }
public int HotelID { get; set; }
public string HotelCode { get; set; }
public string HotelName { get; set; }
public bool Status { get; set; }
public string RoomStatus { get; set; }
public string MAC { get; set; }
}
public class PushData
{
public string HostNumber { get; set; }
public string RoomNumber { get; set; }
public string HotelCode { get; set; }
public string HotelName { get; set; }
public string UdpData { get; set; }
public string Timestamp { get; set; }
}
2.6 定时任务注册
在Global.asax.cs中注册定时任务:
protected override void Application_Start(object sender, EventArgs e)
{
// 现有代码...
// 注册定时任务
JobManager.AddJob(
() => new HostInfoSyncJob().Execute(),
s => s.ToRunEvery(5).Minutes() // 每5分钟执行一次
);
// 现有代码...
}
3. 性能优化
3.1 缓存策略优化
- 缓存过期时间:对于主机信息,使用永久缓存,只有在定时任务执行时更新
- 缓存预热:系统启动时执行一次全量同步,确保Redis中有初始数据
- 增量更新:定时任务可以只更新发生变化的主机信息,减少网络传输
3.2 并发处理优化
- 异步推送:使用异步方式推送数据,避免阻塞UDP处理线程
- 批量处理:对于短时间内的多个UDP数据,进行批量推送
- 连接池:使用HttpClient连接池,减少连接建立开销
3.3 错误处理优化
- 重试机制:推送失败时进行有限次数的重试
- 降级策略:当Redis不可用时,直接从数据库获取数据
- 监控告警:对推送失败的情况进行监控和告警
4. 部署与配置
4.1 配置文件设置
在Web.config中添加必要的配置:
<appSettings>
<!-- 现有配置... -->
<!-- 推送API配置 -->
<add key="PushApiUrl" value="http://example.com/api/push" />
<!-- 定时同步间隔(分钟) -->
<add key="SyncIntervalMinutes" value="5" />
</appSettings>
4.2 依赖项
- Newtonsoft.Json
- FluentScheduler(用于定时任务)
- CSRedis(用于Redis操作)
5. 测试方案
5.1 功能测试
- 定时同步测试:验证定时任务是否正常执行,Redis中是否有数据
- UDP数据推送测试:模拟UDP数据,验证是否能正确获取信息并推送
- 边界情况测试:测试Redis不可用、网络异常等情况
5.2 性能测试
- 并发测试:模拟多个UDP数据同时到达,测试推送性能
- 响应时间测试:测量从UDP数据到达至推送完成的时间
- 负载测试:测试系统在高负载下的稳定性
6. 总结
本方案通过以下步骤实现了UDP数据的高效推送:
- 定时同步:定期从数据库获取主机信息并更新到Redis
- 快速获取:UDP数据到达时,从Redis快速获取所需信息
- 异步推送:使用异步方式将数据推送到外部接口
- 容错处理:当Redis不可用时,从数据库获取数据作为降级方案
该方案显著减少了数据库查询开销,提高了UDP数据处理和推送的效率,适用于高并发场景下的实时数据推送需求。