【问题标题】:How to ensure an asmx web service only gets called once at a time?如何确保一次只调用一次 asmx Web 服务?
【发布时间】:2010-03-07 18:43:30
【问题描述】:

我有一个 asmx Web 服务,应该只允许一次响应 1 个客户端。

换句话说,如果服务被客户端 A 调用,而服务器 B 调用,我希望 B 等待 A 完成,然后 B 可以得到服务。

如果这太复杂了,那么在 A 使用服务期间,来自 B 的调用至少会失败并出现用户定义的错误。

原因是服务严重依赖 IO 操作和 XML 序列化,因此服务不能同时被超过 1 个客户端调用是至关重要的。

提前致谢

【问题讨论】:

    标签: .net web-services asmx


    【解决方案1】:
    static object _LockObject = new object();
    
    void WebServiceCall()
    {
        lock(_LockObject)
        {
            // Do work...
        }
    }
    

    创建一个您调用lock() 的静态对象。 lock() 语句将阻止其他调用执行其中的代码,直到获得锁的第一次执行完成。

    请注意,根据您的超时设置,B 可能会因超时而失败,具体取决于 A 完成所需的时间。

    更新:是的,您可以使用 Monitor 类代替 lock()。您可以使用Monitor.TryEnter() 方法检查对象是否已被锁定(即:如果您想返回错误而不是等待)。

    更多详情:

    来自http://msdn.microsoft.com/en-us/library/aa664735(VS.71).aspx

    形式的锁语句

    lock (x) ...
    

    其中 x 是引用类型的表达式,完全等价于

    System.Threading.Monitor.Enter(x);
    try {
       ...
    }
    finally {
       System.Threading.Monitor.Exit(x);
    }
    

    来自http://msdn.microsoft.com/en-us/library/de0542zz.aspx

    使用 Enter 获取作为参数传递的对象上的 Monitor。如果另一个线程对该对象执行了Enter,但还没有执行对应的Exit,则当前线程将阻塞,直到另一个线程释放该对象。

    所以代码知道阻塞而不是跳过只是设计使然。如果需要,可以使用 Monitor.TryEnter() 方法跳过。

    【讨论】:

    • 请原谅我的无知,但这对我来说毫无意义,你能详细说明一下吗?谢谢
    • @JL:添加了更多细节。如果您有具体问题想了解更多详情,请告诉我。
    • 是否也可以判断一个对象的锁定状态?
    • 好吧最后一个问题,上面的代码怎么知道等待,而不是跳过代码块?
    • @JL:更多细节。基本上代码知道等待,因为另一段代码/Web服务调用已经在_LockObject上执行了Monitor.Enter(),但还没有调用Monitor.Exit()
    【解决方案2】:

    我不知道它是如何在 .net 中实现的,但我猜你想实现一个锁对象,它在被服务时由一个请求“拥有”,并且一次不能给多个请求.在 Java 中,我可能会在一些全局对象上进行同步。

    但要小心,注意常见的并发问题...例如,对于一个简单的实现,一个请求检查锁并发现它可用,然后休眠,第二个请求检查锁并获取它,然后是第一个请求唤醒,认为锁是空闲的并且坏事发生了)。还要确保您处理请求处理崩溃而锁定到位的情况。

    由于制作基于 Web 的非并发系统的使用模式(当然在 Java servlet 世界中)被视为一种不好的做法,我建议您在服务已经存在的情况下抛出错误使用而不是阻止,因为可能难以判断阻止的影响。

    【讨论】:

    • 你的意思是标记一个 asp.net 全局应用程序变量?
    • 就像我说的,我不确定 asp.net 的详细信息,因为我不使用该语言,但您需要一些对所有请求都可见的对象,可用于宣传正在处理请求。然后,该请求可以检查该对象,并且只有在没有其他请求正在进行时才继续。不过要小心,注意死锁的可能性,并确保处理请求处理崩溃而锁定到位的情况。
    【解决方案3】:

    我了解一次只处理一个请求的要求,但我不认为一次只允许一个请求是答案。

    一些答案​​建议阻止请求。虽然这将在小范围内起作用,但这会导致诸如超时扩展能力到更多服务器等问题。

    另一种方法是,您在每个请求进入时对其进行服务,并将其放置在 队列 中以供以后处理。该队列一次只能处理一项任务(或取决于您的服务器的繁忙程度)。

    在完成后,可以通过多种方式通知原始请求者。一种方法是轮询以查看原始请求是否已完成,或者它是否仍在等待处理的队列中,可能使用生成的令牌(例如 GUID)。

    【讨论】:

    • +1 鉴于现在可用的其他信息,我同意排队请求听起来是正确的想法。
    【解决方案4】:

    我不知道您为什么要这样做,但无论如何,这可能是一个有效的方案。试试看 Linux 的 APT 包管理器是如何获取锁的:

    为了防止产生多个包管理器实例,包管理器要发挥作用,需要锁定锁定文件并将其 PID 写入其中。

    同样,您可以在虚拟主机的根目录中创建一个文件。连接客户端后,锁定该文件,即在其中写入内容。当它完成时,使文件为空。在尝试将其锁定之前,请尝试查看里面是否有东西。如果是,则将错误消息返回给客户端。

    【讨论】:

    • 这是一个很好的答案,但是您忘记了一个重要的点 - 重新启动能力。如果 ws 线程死了,你如何采取措施确保文件被重置,否则你可能会遇到服务调用被锁定到所有客户端的情况。
    • 糟糕!我没想到。如果客户端不释放锁(清除文件),则该人必须手动清除它。这个问题的第一个答案(上面)是正确的。干杯!!
    猜你喜欢
    • 1970-01-01
    • 2021-11-30
    • 2020-06-01
    • 2013-03-27
    • 2018-12-14
    • 2011-05-09
    • 1970-01-01
    • 2011-04-05
    • 1970-01-01
    相关资源
    最近更新 更多