从Web应用程序创建锁的最佳方法是什么?

我有一个重新调整图像大小的Web应用程序。 重新resize的图像将写入磁盘以缓存它们。 防止多个同时请求生成相同图像的最佳方法是什么?

有几点需要注意,我们有数百万张图像(以兆兆字节为单位)。 删除了一段时间未查看的缓存图像。 我们有一个Web场,但每个Web服务器都有自己的本地缓存(原件存储在另一台服务器上)。 我们还将重新resize的图像放置在第二层缓存中,以便其他Web服务器可以检查图像是否缓存,如果是,则将其复制到本地。

我考虑过使用锁(我发布了一个我正考虑在这里使用的课程)。 但这显然不适用于第二层缓存,我不确定在Web服务器上使用锁是否是一个好主意(虽然我不知道为什么,只是一堆模糊的引用这是一个坏主意)。

我还考虑编写一个临时文件,我可以在开始创建映像之前检查,但我担心Windows不会100%正常清理文件(锁定问题等)。

任何想法都表示赞赏。

您是否考虑过使用中间件,例如MSMQ或ActiveMQ? 一旦提交了对Web服务器的图像resize请求,它就会进入队列。 单独的应用程序将检查队列,调整图像大小并将其保存到缓存。

如果可以的话我会避免锁 – 特别是因为你不需要在这里锁定。 您还希望避免基于另一台机器处理的一台机器锁定。 如果两台机器创建相同的已resize的图像,我认为它们将是相同的。 因此,如果两台机器碰巧调整了相同的问题,因为它们都错过了缓存,那么效率稍差(浪费时间),但很可能比锁定(并且可能是死锁)并尝试优化边缘情况更好。

一种选择是在本地创建resize的图像,并将缓存的项目排入中央队列(数据库?在中央服务的内存中?),或者使用数据,或者使用参考如何从前端机器中提取它。 集中式缓存队列是串行处理的。 如果两个重复项在多个计算机resize的时间之间进入队列并且队列项可以处理,则无关紧要,因为处理副本只会因为它已经在磁盘上而将其拉出来。

首先,使用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; } 

我有这个代码非常有效地运行,这是我能找到的唯一方法,以确保该文件永远不会被锁定。

使用数据库列出文件哈希将是最快捷的方法。 然后,这可以在所有层之间共享,它还允许您卸载任何锁定到Transactional SQL(T-SQL)。

其他需要存储TB的大型应用程序(如Symantec Enterprise Vault)也会执行相同的操作。

它应该与需要控制数据库中数据的编辑/更新的Web应用程序没有区别。

据我所知,成功地将图像存储为数据库中的blob字段。 我控制blob编辑就像任何其他数据字段一样。

这意味着您必须熟悉Web服务如何与数据库一起处理冲突和并发控制。

作为替代方案如果您无法负担高度可扩展的rdbms …您可以存储文件名/路径,而不是在数据库中存储blob,实际图像存储在文件系统中。 数据库为图像提供唯一键。 所有对任何图像的访问都必须通过其数据库记录完成。 每次生成新图像时,都会按照规定的顺序在primefaces事务下进行

  1. 它存储在新名称/路径下
  2. 如果成功,则更新数据库记录
  3. 如果成功,则删除旧图像

这是您必须要处理的意外情况:如果最后一步不成功(可能是系统/电源故障),则将回滚数据库记录并且您将拥有孤立映像。 或者,如果db更新失败,新存储的图像将最终成为孤儿。

因此,为了保持文件系统的正常和清除孤儿,您可能会删除超过24小时的图像。

有关更强大的解决方案,请参阅我的Web应用程序缓存技术的说明:

http://h2g2java.blessedgeek.com/2010/04/page-caching-using-request-parametric.html

我建议2种性质相似的解决方案。 其中之一是使用WCF服务层。 在此服务中,您可以使用并发字典。 您应该以这样的方式开发哈希代码,即相同的图像将创建相同的哈希。 因此,您将在并发字典中拥有单个图像实例。 您还可以在您的class级中添加代表图像的时间戳。 它可能有用。 生成图像后,您可以使用生成图像的位置更新类中的此类。 并且你可以有一个大的标志,如果你有另一个要求resize的请求,则表明正在处理这个图像。 然后你忽略那个请求。 您不仅要使用并发字典,还可以再次锁定字典中的单个键。 但如果您使用位标记作为CurrentlyProcessing,则不需要锁定。 这将是一个非常快速和有效的解决方案,IMO。

另一个解决方案是分布式哈希表,例如appfabric缓存。 与上述逻辑相同。

你怎么看?

我不确定你是否真的需要解决这一问题 – 请考虑以下几点:

  • 如果一个服务器开始调整特定的大小并且电阻过程会以某种方式“卡住”怎么办? 如果您实现了所描述的内容,那么所有其他服务器将等待该服务器完成…不确定这会带来良好的用户体验
  • OTOH如果你没有实现你只是松了一点时间但没有遇到解决上述问题……

我肯定会将第二层缓存的内容(图像ID)的某种DB或(中央)内存缓存实现,以便在将resize的图像复制到缓存时进入冲突。 。

如果您希望一个客户端能够一次处理一个随机图像,那么当您对请求进行求和时,您会在视图状态中存储一个标记。 提交数据时会引发该标志,并在完成图像处理后重置标志。 当您收到请求时,只需检查标志是否被引发。 如果提出拒绝处理图像。

在第二种情况下,即,如果您想假装用户提交相同的图像,您可以在viewstate中存储图像的名称和大小(按字节),当用户选择图像时,您可以比较图像的名称和大小处理图像。 如果图像的大小和名称与您在viewstate中存储的图像相同,则拒绝处理图像。 否则你会处理它。

希望它可以帮到你。