我有一个重新调整图片大小的网络应用程序。重新调整大小的图像将写入磁盘以缓存它们。防止多个同时请求生成相同图像的最佳方法是什么?
值得注意的是,我们有数百万张图片(以兆兆字节为单位)。删除了一段时间未查看的缓存图像。我们有一个Web场,但每个Web服务器都有自己的本地缓存(原件存储在另一台服务器上)。我们还将重新调整大小的图像放置在第二层缓存中,以便其他Web服务器可以检查图像是否缓存,如果是,则将其复制到本地。
我考虑过使用锁(我发布了一个我正在考虑使用here的课程)。但这显然不适用于第二层缓存,我不确定在Web服务器上使用锁一般是不是一个好主意(虽然我不知道为什么,只是一堆模糊的引用这是一个坏主意。)
我还考虑过编写一个我可以在开始创建映像之前检查的临时文件,但我担心Windows不会100%正常清理文件(锁定问题等)。
任何想法都表示赞赏。
答案 0 :(得分:1)
您是否考虑过使用中间件,例如MSMQ或ActiveMQ?一旦提交了对Web服务器的图像调整大小请求,它就会进入队列。单独的应用程序将检查队列,调整图像大小并将其保存到缓存中。
答案 1 :(得分:1)
如果可以,我会避免锁定 - 特别是因为你不需要在这里锁定。您还希望避免基于另一台机器处理的一台机器锁定。如果两台机器创建相同的已调整大小的图像,我认为它们将是相同的。因此,如果两台机器碰巧调整了相同的问题,因为它们都错过了缓存,那么它的效率(浪费的时间)就会略微降低,但很可能比锁定(并且可能是死锁)并尝试优化边缘情况更好。
一种选择是在本地创建调整大小的图像,并将缓存的项目排入中央队列(数据库?在中央服务的内存中?),或者使用数据,或者使用参考如何从前端机器中提取它。集中式缓存队列是串行处理的。如果两个重复项在多个计算机调整大小的时间之间进入队列并且队列项可以处理,则无关紧要,因为处理副本只会因为它已经在磁盘上而将其拉出来。
答案 2 :(得分:1)
首先,使用GUID生成文件名,以便您知道您不会有重复的文件名。
Guid.NewGuid()
然后使用以下代码防止锁定图像: -
public static Image GetImageWithoutLocking(string workingPathFileName)
{
Image returnImage = null;
try
{
using (FileStream fileStream = new FileStream(Path.Combine(LivePaths.WorkingFolder, workingPathFileName), FileMode.Open, FileAccess.Read))
{
byte[] img;
img = new byte[fileStream.Length];
fileStream.Read(img, 0, img.Length);
fileStream.Close();
returnImage = Image.FromStream(new MemoryStream(img));
img = null;
}
}
catch
{
throw;
}
return returnImage;
}
我有非常有效地运行此代码,这是我找到的唯一方法,以确保文件永远不会被锁定。
答案 3 :(得分:0)
使用数据库列出文件哈希是最快捷的方法。然后,这可以在所有层之间共享,它还允许您卸载任何锁定到Transactional SQL(T-SQL)。
其他需要存储TB的大型应用程序(如Symantec Enterprise Vault)也会做同样的事情。
答案 4 :(得分:0)
它应该与需要控制数据库中数据编辑/更新的Web应用程序没有什么不同。
据我所知,成功地将图像存储为数据库中的blob字段。我控制了blob编辑,就像任何其他数据字段一样。
这意味着您必须熟悉Web服务如何与数据库一起处理冲突和并发控制。
作为替代 如果你买不起高度可扩展的rdbms ...... 您可以存储文件名/路径,而不是在数据库中存储为blob,而实际图像存储在文件系统中。数据库提供图像的唯一键。所有对任何图像的访问都必须在其数据库记录中完成。每次生成新图像时,都会按照规定的顺序在原子事务下进行以下操作
这是您必须要处理的意外情况:如果最后一步不成功(可能是系统/电源故障),则会回滚数据库记录,您将拥有一个孤儿图像。或者,如果db更新失败,新存储的图像最终将成为孤儿。
因此,为了保持文件系统的正常和清除孤儿,您可能会删除超过24小时的图像。
有关更强大的解决方案,请参阅我的Web应用程序缓存技术的说明:
http://h2g2java.blessedgeek.com/2010/04/page-caching-using-request-parametric.html
答案 5 :(得分:0)
我会建议两种性质相似的解决方案。其中之一是使用WCF服务层。在此服务中,您可以使用并发字典。您应该以这样的方式开发哈希码,即相同的图像将创建相同的哈希。因此,您将在并发字典中拥有单个图像实例。您也可以在您的班级中添加代表图像的时间戳。它可能有用。生成图像后,您可以使用生成图像的位置更新类中的此类。并且你可以有一个大的标志,如果你有另一个要求调整大小的请求,则表明正在处理这个图像。然后你忽略那个请求。您不仅要使用并发字典,还可以再次锁定字典中的单个键。但如果您使用位标记作为CurrentlyProcessing,则不需要锁定。这将是一个非常快速有效的解决方案,IMO。
另一个解决方案是分布式哈希表,例如appfabric缓存。与上述逻辑相同。
你觉得怎么样?答案 6 :(得分:0)
我不确定你是否真的需要解决这一问题 - 请考虑以下几点:
我肯定会将第二层缓存的内容(图像ID)的某种DB或(中央)内存缓存实现,以便在将已调整大小的图像复制到缓存...
答案 7 :(得分:0)
如果您希望让客户端能够一次处理一个随机图像,那么当您对请求进行求和时,您会在视图状态中存储一个标记。提交数据时会引发该标志,并在完成图像处理后重置标志。当您收到请求时,只需检查标志是否被引发。如果提出拒绝处理图像。
在第二种情况下,即如果您想假装用户提交相同的图像,您可以在viewstate中存储图像的名称和大小(按字节),当用户选择图像时,您可以比较图像的名称和大小。处理图像之前的图像。如果图像的大小和名称与您在viewstate中存储的图像相同,则拒绝处理图像。否则你会处理它。
希望它可以帮到你。