C# 如何在Web API中维护请求的状态或队列
在这种情况下,我必须在一个WebAPI方法中接收请求,对这些请求进行排队,然后将批量发送到一个数据库(Solr实例) 我不确定如何维护来自多个来源的一批请求。现在,我正在以json格式将每个请求数据写入磁盘上的一个文件,稍后我将使用windows服务,遍历文件夹读取所有文件,更新数据库并删除这些文件 下面是我在我的Web API中所做的C# 如何在Web API中维护请求的状态或队列,c#,asp.net,solr,asp.net-web-api,solrnet,C#,Asp.net,Solr,Asp.net Web Api,Solrnet,在这种情况下,我必须在一个WebAPI方法中接收请求,对这些请求进行排队,然后将批量发送到一个数据库(Solr实例) 我不确定如何维护来自多个来源的一批请求。现在,我正在以json格式将每个请求数据写入磁盘上的一个文件,稍后我将使用windows服务,遍历文件夹读取所有文件,更新数据库并删除这些文件 下面是我在我的Web API中所做的 public void Post(LogEntry value) { value.EventID = Guid.NewGuid(); value
public void Post(LogEntry value)
{
value.EventID = Guid.NewGuid();
value.ServerTime = DateTime.UtcNow;
string json = JsonConvert.SerializeObject(value);
using(StreamWriter sw = new StreamWriter(value.EventID.ToString()))
{
sw.Write(json);
}
}
(此处EventID
为GUID)
这个过程看起来不正确,必须有一种方法来维护请求队列,但我不确定如何在多个请求期间维护队列
我这样做的原因是,在solr实例中批量插入比通过SolrNet插入单个记录更快。我希望在Web API上每秒至少收到100个请求。我想创建一批1000个请求,并每10秒更新一次solr实例。请不要认为我需要代码,只需要知道我应该采取什么策略来维护请求/状态队列 如果您使用的是.NET 4.0或更高版本,则可以使用并发队列: 这是一种使用队列的线程安全方式,然后可以在所需时间访问队列 编辑: 例如: 这将是队列的包装器:
public static class RequestQueue
{
private static ConcurrentQueue<int> _queue;
public static ConcurrentQueue<int> Queue
{
get
{
if (_queue == null)
{
_queue = new ConcurrentQueue<int>();
}
return _queue;
}
}
}
如果使用此示例,您将看到队列跨多个请求保存值。但是,由于它存在于内存中,因此如果应用程序池被回收(例如),那些排队的项目将消失
现在,您可以在创建另一个队列以存储传入值的同时,检查队列何时包含10个项目,然后将这些项目保存到数据库中
像这样:
public static class RequestQueue
{
private static ConcurrentQueue<int> _queue;
public static ConcurrentQueue<int> Queue
{
get
{
if (_queue == null)
{
_queue = new ConcurrentQueue<int>();
}
if (_queue.Count >= 10)
{
SaveToDB(_queue);
_queue = new ConcurrentQueue<int>();
}
return _queue;
}
}
public static void SaveToDB(ConcurrentQueue<int> queue)
{
foreach (var item in queue)
{
SaveItemToDB(item);
}
}
}
公共静态类请求队列
{
私有静态ConcurrentQueue\u队列;
公共静态并发队列
{
得到
{
如果(_queue==null)
{
_队列=新的ConcurrentQueue();
}
如果(_queue.Count>=10)
{
SaveToDB(_队列);
_队列=新的ConcurrentQueue();
}
返回队列;
}
}
公共静态void SaveToDB(ConcurrentQueue队列)
{
foreach(队列中的变量项)
{
SaveItemToDB(项目);
}
}
}
您需要稍微清理一下,但是这个设置应该可以工作。此外,在将队列转储到数据库和创建新实例时,可能需要一些锁定机制。我将编写一个控制台应用程序,其中有多个线程访问此队列以对其进行测试。您可以将请求排入内存中的队列,并使用Quartz.Net定期将它们发送到数据库。您只需在Global.asax.cs中进行如下操作:
public class RequestQueue
{
private readonly Queue<HttpRequest> _requestHistory;
private RequestQueue()
{
_requestHistory = new Queue<HttpRequest>();
}
private static RequestQueue _singleton;
public static RequestQueue Instance()
{
if (_singleton == null)
_singleton = new RequestQueue();
return _singleton;
}
public void Enqueue(HttpRequest request)
{
_requestHistory.Enqueue(request);
}
public void Flush()
{
while (_requestHistory.Count > 0)
{
var request = _requestHistory.Dequeue();
try
{
//Write request To Db
}
catch (Exception)
{
_requestHistory.Enqueue(request);
}
}
}
}
public class WebApiApplication : System.Web.HttpApplication
{
public WebApiApplication()
{
base.BeginRequest += delegate
{
RequestQueue.Instance().Enqueue(HttpContext.Current.Request);
};
}
private void InitializeQuartz()
{
ISchedulerFactory sf = new StdSchedulerFactory();
IScheduler sched = sf.GetScheduler();
DateTimeOffset runTime = DateBuilder.EvenMinuteDate(DateTime.UtcNow);
DateTimeOffset startTime = DateBuilder.NextGivenSecondDate(null, 5);
IJobDetail job = JobBuilder.Create<QueueConsumer>()
.WithIdentity("job1", "group1")
.Build();
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartAt(runTime)
.WithCronSchedule("5 0/1 * * * ?")
.Build();
sched.ScheduleJob(job, trigger);
sched.Start();
}
public class QueueConsumer : IJob
{
public void Execute(IJobExecutionContext context)
{
RequestQueue.Instance().Flush();
}
}
protected void Application_Start()
{
InitializeQuartz();
公共类请求队列
{
私有只读队列_requestHistory;
私有请求队列()
{
_requestHistory=新队列();
}
私有静态请求队列_singleton;
公共静态RequestQueue实例()
{
如果(_singleton==null)
_singleton=newrequestqueue();
返回单件;
}
公共无效排队(HttpRequest请求)
{
_requestHistory.Enqueue(请求);
}
公共图书馆
{
而(_requestHistory.Count>0)
{
var request=_requestHistory.Dequeue();
尝试
{
//向数据库写入请求
}
捕获(例外)
{
_requestHistory.Enqueue(请求);
}
}
}
}
公共类WebAPI应用程序:System.Web.HttpApplication
{
公共WebAPI应用程序()
{
base.BeginRequest+=委托
{
RequestQueue.Instance().Enqueue(HttpContext.Current.Request);
};
}
私有void InitializeQuartz()
{
isSchedulerFactory sf=新StdSchedulerFactory();
isScheduler sched=sf.GetScheduler();
DateTimeOffset运行时=DateBuilder.EvenMinuteDate(DateTime.UtcNow);
DateTimeOffset startTime=DateBuilder.NextGivenSecondDate(null,5);
IJobDetail job=JobBuilder.Create()
.WithIdentity(“工作1”、“组1”)
.Build();
ITrigger trigger=TriggerBuilder.Create()
.WithIdentity(“触发器1”、“组1”)
.StartAt(运行时)
.随附附件(“50/1***?”)
.Build();
sched.ScheduleJob(作业,触发器);
sched.Start();
}
公共类排队消费者:IJob
{
public void Execute(IJobExecutionContext上下文)
{
RequestQueue.Instance().Flush();
}
}
受保护的无效应用程序\u Start()
{
InitializeQuartz();
另一种解决方案是将记录保存在内存队列中,该队列与WebApi的进程不同。例如:MemcacheQueue
其中一些队列实现具有持久性功能,因此您在任何情况下都不会丢失数据。您应该尝试实现具有调度消息和在将来发送消息的功能,从服务总线文档中,您可以调度功能您可以调度任务或操作/lambda在给定的时间间隔内重复执行。 这意味着您可以拥有memeory缓存,并每10分钟将阵列内容写入solr/lucene impl,例如,这非常简单:
Schedule.Every(TimeSpan.FromMinutes(10)).Action(() => { < task to be executed > })
Schedule.Every(TimeSpan.FromMinutes(10)).Action(()=>{})
如果您需要更大的灵活性来设置schedueler,您可以将其集成到quartz.net中
具体情况如下:
- WCF as windows服务和NServiceBus应该共享相同的上下文,或者实现可以在系统的这两个不同部分之间共享的cacheManager<
Schedule.Every(TimeSpan.FromMinutes(10)).Action(() => { < task to be executed > })
public class ThresholdBuffer<T> { private ConcurrentBag<T> _buffer; private int _threshold; public ThresholdBuffer(int threshold) { _threshold = threshold; _buffer = new ConcurrentBag<T>(); } public void Add(T item) { _buffer.Add(item); if(_buffer.Count >= _threshold) { Recycle(); } } public void Recycle() { var value = Interlocked.Exchange<ConcurrentBag<T>>(ref _buffer, new ConcurrentBag<T>()); //flush value } }
public class ThresholdBuffer<T> { private ConcurrentBag<T> _buffer; private int _copacity; private int _threshold; public ThresholdBuffer(int threshold) { _threshold = threshold; _copacity = 0; _buffer = new ConcurrentBag<T>(); } public void Add(T item) { _buffer.Add(item); if (Interlocked.Increment(ref _copacity) == _threshold) { Recycle(); } } public void Recycle() { var value4flasshing = Interlocked.Exchange<ConcurrentBag<T>>(ref _buffer, new ConcurrentBag<T>()); Thread.VolatileWrite(ref _copacity, 0); } }