我有通过REST接口获取输入的Web服务。 REST调用不会返回任何有意义的数据,因此传入Web服务的任何内容都只记录在数据库中,就是这样。它是我公司内部使用的分析服务,可以对其网页上收到的Web请求进行一些特殊处理。因此,尽可能少的回复时间非常重要。
我已经尽可能地优化了代码,尽可能快地做出响应。但是,在将响应发送回Web客户端之前,数据库保持打开的时间仍会使连接打开的时间超过我的预期。
代码看起来基本上是这样的,就像ASP.NET MVC一样,使用在IIS 7上运行的Entity Framework,如果这很重要的话。
public ActionResult Add(/*..bunch of parameters..*/) {
using (var db = new Entities()) {
var log = new Log {
// populate Log from parameters
}
db.AddToLogs(log);
db.SaveChanges();
}
return File(pixelImage, "image/gif");
}
有没有办法将数据库插入卸载到另一个进程,因此几乎立即返回对客户端的响应?
我正考虑将所有内容包装在另一个线程的using
块中,以使数据库插入异步,但不知道这是否是将响应释放回客户端的最佳方法。 / p>
如果你想要实现这个目标,你会建议什么?
答案 0 :(得分:3)
如果请求必须可靠,那么您需要将其写入数据库。例如。如果您的退货意味着“我已经支付了商家”,那么您在实际提交数据库之前就无法退货。如果处理时间很长,则存在基于数据库的异步模式,使用表作为队列或使用内置队列(如Asynchronous procedure execution)。但是,当需要进行繁重且冗长的处理时,这些适用,而不是简单的日志插入。
当您只想插入日志记录(访问者/网址跟踪内容)时,最简单的解决方案是使用CLR的线程池,只需使用queue the work,例如:
...
var log = new Log {// populate Log from parameters}
ThreadPool.QueueUserWorkItem(stateInfo=>{
var queueLog = stateInfo as Log;
using (var db = new Entities())
{
db.AddToLogs(queuedLog);
db.SaveChanges();
}
}, log);
...
这很简单,它可以让ASP处理程序线程尽快返回响应。但它有一些缺点:
通过使用真正的异步数据库调用,通过SqlCommand.BeginExecuteXXX并将连接上的AsynchronousProcessing设置为true,可以解决最后一个问题。不幸的是,AFAIK EF还没有真正的异步执行,所以你不得不求助于SqlClient层(SqlConnection,SqlCommand)。但是这个解决方案无法解决第一个问题,当页面命中率如此之高以至于这个日志记录(=每次点击时写入)成为一个关键瓶颈。
如果第一个问题是真实的,那么没有线程和/或生产者/消费者的魔法可以缓解它。如果你有一个崩溃率与写入率可伸缩性问题('待处理'队列在内存中增长),你必须在数据库层中更快地写入(更快的IO,特殊日志刷新IO)和/或你必须聚合写道。而不是记录每个请求,只需增加内存计数器并定期将它们写为聚合。
答案 1 :(得分:1)
我在过去一年左右的时间里一直在研究多层解决方案,这需要这种功能,而这正是我一直在做的。
我有一个单例,负责基于ITask
接口在后台运行任务。然后我只用我的单例注册一个新的ITask
并将控制从我的主线程传递回客户端。
答案 2 :(得分:1)
创建一个单独的线程来监视内存队列中的全局。如果您的请求将其信息放在队列中并返回,则线程将该项目从队列中取出并将其发布到数据库。
在负载很重的情况下,如果线程滞后于请求,您的队列就会增长。
此外,如果您丢失了计算机,您将丢失所有未处理的队列条目。
您是否可以接受这些限制,您需要做出决定。
一个更正式的机制是使用一些实际的中间件消息传递系统(JMS在Java领域,dunno相当于.NET,但肯定有一些东西)。
答案 3 :(得分:0)
在我花费大量时间进行优化之前,我确定时间会在哪里。像这样的连接具有显着的延迟开销(检查this)。只是为了咧嘴笑,让你的服务成为一个NOP,看看它是如何表现的。
在我看来,'async-ness'需要在客户端上 - 它应该启动对您服务的调用并继续前进,特别是因为它不关心结果?
我还怀疑,如果NOP的性能在你的局域网上是可以容忍的,那么这将是一个不同的故事。
答案 4 :(得分:0)
取决于:当您返回客户端时,您是否需要100%确定数据是否存储在数据库中?
采取这种情况:
您还需要通过启动新线程而不是保存到数据库来检查保存的毫秒数。
与响应时间的节省相比,增加的复杂性和维护成本可能过高。而且响应时间的节省可能很低,以至于不会被注意到。