我有一个将文件写入磁盘和其他东西到数据库的Web服务。每次写入整个操作需要1-2秒。
可以同时从多个客户端调用不太可能的服务。假设有20个客户端同时调用Web服务,必须同步写入操作。在这种情况下,一些客户端可以获得超时异常,因为它们必须等待很多秒。
有什么好的做法可以解决这种情况吗?就像现在一样,方法是同步的(并且可能导致饥饿/超时)。
我是否应该通过删除synchronized
关键字让所有线程进入write方法并将其任务放入任务队列以避免超时?这是一个正确的方式来解决这个问题吗?
答案 0 :(得分:1)
删除synchronized
并将其单独放入任务队列对您没有帮助(因为这实际上是同步为您做的事情)。但是,如果您在将其放入队列后立即响应Web请求,那么您将减少响应时间。但是以一些可靠性为代价,因为用户将得到确认工作已完成并且工作不会真正完成(系统可能在工作完成之前崩溃)。
答案 1 :(得分:1)
Francis Upton的做法确实是一种公认的做法。
另一个是,进行更细粒度的同步。您可以同步对应该同步的精确不变量的访问,而不是同步类的所有读/写方法。
然而更好的是完全摆脱同步。这可以使用java.util.concurrent包。这个包引入了使用Non-Blocking Algorithms的新集合(在java中使用Compare-Ans-Swap原子指令实现)。这些集合(例如ConcurrentHashMap)在扩展时可以提供更好的吞吐量。
您可以在this article中了解更多相关信息。
答案 2 :(得分:1)
在这种类型的实现(增加负载下的慢速服务)中,您希望尽可能多地进行异步,包括超时处理(如果基于服务器)和所需的I / O.不要让你的客户端响应线程等待这些耗时的操作,保持服务器对新请求的响应,而是激活所需的操作(可能是动态线程池)并让回调处理结果,是否超时,完成I / O或错误。
根据首先发生的情况发送相应的响应,但是如果发送错误/超时消息然后完成的I / O到达(由于I / O和计时器之间的竞争条件),请准备好回滚I / O )。这意味着服务器中需要事务语义。
随着负载的增长,这个领域变得越来越复杂,但早期的良好设计应该允许您随着负载的增长而扩展。理想情况下,客户端服务线程根本不应该阻塞。