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

本文关键字:最佳 方法 是什么 web 应用程序 创建 | 更新日期: 2023-09-27 18:08:18

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

有几件事需要注意,我们有数百万的图像(以tb为单位)。一段时间未被浏览的缓存图像将被删除。我们有一个web农场,但是每个web服务器都有自己的本地缓存(原始存储在另一个服务器上)。我们还将重新生成的图像放在第二层缓存中,以便其他web服务器可以检查图像是否被缓存,如果是,则复制到本地。

我考虑过使用锁(我在这里发布了一个我正在考虑使用的类)。但这显然不会与第二层缓存工作,我不确定如果它是一个好主意,一般在web服务器上使用锁(虽然我不确定为什么,只是一堆模糊的参考它是一个坏主意)。

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

任何想法都很感激。

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

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

如果可以的话,我会尽量避免使用锁——尤其是在不需要在这里使用锁的情况下。您还希望避免基于另一台机器的处理而锁定一台机器。如果两台机器创建相同的调整大小的图像,我假设它们是相同的。因此,如果两台机器碰巧调整同一个问题,因为它们都错过了缓存,那么它的效率只是稍微低一点(浪费时间),但很可能比锁定(可能是死锁)和尝试优化边缘情况要好。

一种选择是在本地创建调整大小的图像,并将缓存项放入中央队列(数据库?在中央服务上的内存中?),要么使用数据,要么使用如何从前端机器提取数据的参考。集中式缓存队列是串行处理的。如果在多个机器调整队列大小和处理队列项之间将两个副本放入队列中,那么这无关紧要,因为处理副本将简单地将其拉出,因为它已经在磁盘上。

首先,生成带有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;
    } 

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

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

其他必须存储TB的大型应用程序(如Symantec Enterprise Vault)也做同样的事情。

这应该与需要控制数据库中数据的编辑/更新的web应用程序没有什么不同。

就我所尝试的而言,成功地将图像存储为数据库中的blob字段。我控制了blob编辑,就像控制其他数据字段一样。

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

作为替代如果您负担不起高度可扩展的关系型数据库管理系统……您可以将文件名/路径存储在文件系统中,而不是作为blob存储在数据库中。数据库为映像提供唯一的键。对任何图像的所有访问都必须通过其数据库记录完成。每次生成新映像时,按照指定的顺序在原子事务下进行以下操作

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

这是您必须处理的突发事件:如果最后一步不成功(可能是系统/电源故障),db记录将被回滚,并且您将有一个孤立映像。或者,如果数据库更新失败,新存储的映像将作为孤儿结束。

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

对于更健壮的解决方案,请参考我的web应用缓存技术的描述:

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

我建议两个本质上相似的解决方案。其中之一是使用WCF服务层。在此服务中,您可以使用并发字典。您应该以这样的方式开发哈希码,相同的图像将创建相同的哈希。因此,您将在并发字典中拥有该图像的单个实例。您也可以添加时间戳到您的类,这将表示图像。它可能会有用处。一旦生成了图像,就可以在类中使用生成图像的位置更新这个类。你可以有一个大标志如果你有另一个请求请求调整大小它会指示这个图像正在被处理。然后你忽略了这个请求。您不仅可以使用并发字典,还可以再次锁定字典中的单个键。但是如果你使用一个位标志作为CurrentlyProcessing,你就不需要锁了。在我看来,这将是一个非常快速和有效的解决方案。

另一个解决方案是分布式散列表,例如appfabric缓存。与上面的逻辑相同

你觉得怎么样?

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

  • 如果一个服务器开始调整一个特定的大小,并且抵抗过程以某种方式"卡住"怎么办?如果你实现了你所描述的,那么所有其他服务器将等待该服务器完成…不确定这是否会带来良好的用户体验
  • OTOH如果你不执行,你只是失去了一点时间,但没有面对解决上述问题…

我肯定会实现某种DB或(中央)内存缓存的内容(图像id)的第二层缓存,以避免机器在复制调整大小的图像到缓存时陷入冲突…

如果您想让客户端能够一次处理一个随机图像,首先在提交请求时在视图状态中存储一个标志。该标志在提交数据时升起,并在完成图像处理时重置。当你得到一个请求时,只需检查flag是否升起。如果触发拒绝处理图像

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

希望对你有帮助