【问题标题】:Best approach to timeout a function after a certain period of time and retry在一段时间后使函数超时并重试的最佳方法
【发布时间】:2018-09-13 16:15:24
【问题描述】:

只是一点点背景。我们正在从将散列在数据库中的用户密码存储到直接针对 Active Directory 进行身份验证。不幸的是,我们有如此多的内部应用程序,因此目前无法选择更新每个应用程序。

因此,我认为我可以为 SQL 构建一个 .NET CLR 程序集并修改现有的身份验证存储过程,以根据我们的域控制器而不是表来验证用户密码,而不是要求我们所有的开发人员更改他们的代码。

这在我们的初始测试中是成功的,除非由于任何原因(关闭、重新启动、网络等)无法与域控制器通信。发生这种情况时,我们会超时。我添加了一个 try/catch 和 while 循环,只是跳到下一个域控制器并尝试对其进行身份验证,但在尝试对下一个服务器进行身份验证之前,第一次尝试仍然需要超过 20 秒才能失败。因为这是一个 SQL 程序集/函数,并且在存储过程中被调用,所以 20 秒太长了。

修改我的代码并使对服务器的身份验证尝试失败并尝试下一个服务器的最佳方法是什么,比如 3 秒后?以某种方式线程化?

Imports System
Imports System.Data
Imports System.Collections
Imports System.Text
Imports Microsoft.SqlServer.Server
Imports System.Data.SqlTypes
Imports System.Runtime.InteropServices
Imports System.DirectoryServices.AccountManagement

Partial Public Class UserDefinedFunctions

    <SqlFunction(Name:="ValidCredentials")>
    Public Shared Function ValidCredentials(ByVal Domain As SqlString, ByVal Username As SqlString, ByVal Password As SqlString) As SqlBoolean
        'Validates the username and password are correct against the domain.

        Dim ServerNum As Integer = 1
        Dim Success As Boolean = False
        Dim ValidCreds As SqlBoolean = False

        'Loop through all 4 domain controllers to attempt authentication against each of them.
        While Success = False And ServerNum <= 4

            Try

                Dim pc As PrincipalContext
                If Domain = "employees" Then
                    pc = New PrincipalContext(ContextType.Domain, "DC-E-0" & ServerNum.ToString & ".employees.ourdomain.loc", "EMPLOYEES\BindAcct", "PASS")
                Else
                    pc = New PrincipalContext(ContextType.Domain, "DC-S-0" & ServerNum.ToString & ".students.ourdomain.loc", "STUDENTS\BindAcct", "PASS")
                End If

                ValidCreds = pc.ValidateCredentials(Username, Password)
                Success = True
                Return ValidCreds

            Catch ex As Exception

                ValidCreds = False
                Success = False
                ServerNum += 1

            End Try

        End While

        Return ValidCreds

    End Function

End Class

【问题讨论】:

  • ValidateCredential 无法验证是否会抛出异常?
  • 是的,它会抛出异常。如果 DC-E-01 关闭、重新启动等,在网络上找不到服务器或类似的东西。当抛出异常时,它会尝试连接到第二个服务器“DC-E-02”,依此类推直到它最终可以验证。这是意料之中的,但是如果它无法与第一个服务器通信,它应该尝试第二个,然后是第三个,然后是第四个,直到找到一个可以验证的服务器。问题是该函数需要很长时间才能引发异常。我想在 3 秒后中止该尝试并转移到下一个服务器。

标签: c# sql .net vb.net active-directory


【解决方案1】:

您可以使用单独的线程来执行此操作:

Set timeout to an operation

代码在 C# 中,但应该很容易转换。

【讨论】:

  • 我能够成功地采用这种方法。谢谢!
【解决方案2】:

您可以使用以下代码行在 sql server 2008 或更高版本上为您的函数设置超时值。把它放在你的块的顶部:

SET LOCK_TIMEOUT 3000;  
GO

超时值以milliseconds 表示。

根据MS Documentation

当等待锁超过超时值时,会报错 返回。

这将导致您的函数花费不到 3 秒或失败。然后你应该捕获异常并在需要时回滚。

【讨论】:

  • 我不希望 SQL 语句失败。我希望程序集中的方法失败(更快或退出)并尝试针对下一个服务器进行身份验证。直到它从 4 个域控制器之一得到正确响应。
  • 我认为您需要找到一种解决方法,没有太多替代方法:c
  • 必须有一种方法可以将函数内部的调用线程化,并在一定时间后中止以尝试下一个服务器。
  • 也许您可以使用@Simo 的解决方案并捕获返回的特定错误,然后递归调用您的函数。
猜你喜欢
  • 2019-06-27
  • 1970-01-01
  • 2021-06-02
  • 2017-10-24
  • 2021-10-15
  • 2016-02-12
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
相关资源
最近更新 更多