代码之家  ›  专栏  ›  技术社区  ›  Quango

通过ASP.NET WebService承载和关闭一次性连接

  •  0
  • Quango  · 技术社区  · 7 年前

    我们有一个供应商服务,我们通过TCP/IP连接访问它,但是我们在任何时候都只能有一个活动的登录。当从多个应用程序访问此服务时,我们设置了一个Web服务(asmx),并添加了一个 Mutex 为了控制访问,所以一次只能打一个电话。

    但是,服务必须以当前代码(如下)连接、登录、发出命令,然后每次注销。

      using (var client = new Client(connectionSettings))
                        {
                            try
                            {
                                client.Login();
                                response.Response = client.SendCommand(command);
                                response.ErrorCode = WebserviceErrorCode.Success;
                                _lastSuccessfulConnection = DateTime.Now;
                            }
                            catch (IOException oex)
                            {
                                response.ErrorMessage = oex.Message;
                                response.ErrorCode = WebserviceErrorCode.UnabletoConnect;
                                return response;
                            }
                            catch (System.Net.Sockets.SocketException socEx)
                            {
                                response.ErrorMessage = socEx.Message;
                                response.ErrorCode = WebserviceErrorCode.SocketException;
                                return response;
                            }
    
                            client.Logout();
                        }
    

    如果有几个客户机发出请求,这显然是低效的——我们宁愿保持连接打开,比如说10秒,然后如果没有更多的命令进入,就关闭它。

    我正在尝试找出在ASP.NET上安全地执行此操作的最佳方法。我们应该使用绞火,队列背景工作项吗?

    1 回复  |  直到 7 年前
        1
  •  1
  •   Quango    7 年前

    经过一些研究,我决定使用 QueueBackgroundWorkItem

    这将调度一个独立于请求线程的后台任务,因此将在请求完成后继续运行。

    注: 这不是一个持久的排队系统(例如,如果Web进程重新启动,它不会存储这些排队系统),但是由于这只是关闭一个连接,所以对于这个场景来说是可以的。

    实施

    当向我的服务发出请求时,它使用 static 类以获取单个客户端(使用 Mutex )发出命令。然后它调用这个例程:

    private static void CreateTimeoutCheck()
    {
          Action<CancellationToken> workItem = TimeoutConnection;
          System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(workItem);
          Log.Information("A timeout check has been created");
    }
    

    这将在后台调用此方法:

     /// <summary>
     /// Job that runs in background, waits 10 seconds then attempts to close the client
     /// </summary>
     /// <param name="cancellationToken"></param>
     private static async void TimeoutConnection(CancellationToken cancellationToken)
     {
         Log.Information("Timeout check waiting...");
         await Task.Delay(timeoutMS);
         Log.Information("Timeout check being made");
         CheckForTimeout();
     }
    

    有一件事让我对这种方法感到不安,那就是我在每个请求的threadpool中生成了一个工作项,如果服务被大量使用,这可能会导致一个问题。然而,每一个都只是进行异步等待,所以我希望这不会引起问题。服务是内部使用的,所以我们控制对它的请求的数量/频率——如果这是外部公开的,我会更担心。

    这个 CheckForTimeout 方法非常简单:

    /// <summary>
    /// Check to see if we have had more recent commands
    /// </summary>
    private static void CheckForTimeout()
    {
        var secsSinceLastCmd = DateTime.Now.Subtract(_lastCommand).TotalSeconds;
        if (secsSinceLastCmd >= clientTimeoutSecs)
        {
            // logout and close client
            Log.Information($"Client timeout: {secsSinceLastCmd:0.0} sec since last command");
            CloseClient();
        }
    }
    

    我将监视更新后的服务一段时间,以确保它正常工作,但到目前为止的测试表明它按预期工作。