【问题标题】:ASP.NET - Async - A system that syncs between two other systemsASP.NET - 异步 - 在其他两个系统之间同步的系统
【发布时间】:2017-11-30 15:28:13
【问题描述】:

我正在尝试创建一个在其他两个系统的数据之间同步的系统。

我使用 VB 处理 ASP.NET Web 表单(无法更改)

主要的系统功能将采用 Web API 的形式,由计划任务或其他事件触发,例如网站表单或登录页面。

这些函数中的每一个都会调用 System 1 的 REST API 和 System 2 的 REST API,对数据进行操作,然后调用各个系统的 REST API 来更新双方。

我选择使用 ASP.NET Web Api 2 作为主系统,使用 HttpClient 调用函数。

每天将有数十万笔交易。很多!所以我选择对 HttpClient 使用 async/await 方法,(因为有很多同时处理的长请求),但是 ASP.NET 似乎迫使我将所有功能从下到上转换,包括 Web Api 函数和数据库访问异步!

所以我最终将所有函数更改为 Async,并且在所有调用它们的函数中我都使用 Await。

似乎一切正常。我不知道我所做的是否正确,而且我发现无法检查我的函数是否真的异步工作。

关于ConfigureAwait(False) 我添加它是因为我没有与上下文相关的操作。如果有,我将删除此声明。我读过建议在可能的情况下使用它。 (我说的对吗?)

小评论: 为什么我的解决方案很好???我试图通过创建更多的工作线程来避免阻塞主线程。为什么这是一个更好的解决方案?我正在创建很多等待,每个等待都启动一个工人。是不是比同步方案更糟糕的做法????

谁能告诉我我正在做的事情是否正确 - 请解释原因或是否有其他方法可以解决这种情况?

这里是示例代码(仅相关部分)。对您所看到的内容进行一点解释:

  • WebApi 控制器具有登录功能
  • Lo​​gin 函数异步调用 System1.DoSomething
  • System1.DoSomething 调用私有函数 System1.SetCredentials 异步
  • System1.DoSomething 也使用 HttpHandler 来获取数据 来自 System1 的 API 的异步
  • System1.SetCredentials 函数异步调用 MyLoginManager.GetCredentials
  • MyLoginManager.GetCredentials 使用 DBHelper.ExecuteReaderAsync 异步调用数据库
  • DBHelper.ExecuteReaderAsync 函数调用 ExecuteReaderAsync 异步地并且还使用异步地打开连接 OpenAsync() 函数

WebAPI 控制器

     Public Class WebApiController
   Inherits ApiController
     Public Async Function Login() As Threading.Tasks.Task(Of IHttpActionResult)
        Dim result As MyResult= Await System1.DoSomething().ConfigureAwait(False)
     End Function

System1 类

 Public Class System1
    Public Shared Async Function DoSomething() As Task(Of MyResult)
        Try
            Using client As New HttpClient
                client.BaseAddress = New Uri("blablabla")
                Await SetCredentials(client).ConfigureAwait(False)
                Dim response As HttpResponseMessage = Await client.GetAsync(urlParameters).ConfigureAwait(False)

                ... More code
            End Using
        Catch ex As Exception
        End Try
    End Function

 Private Shared Async Function SetCredentials(client As HttpClient) As Task
            Dim auth As BasicAuthenticationData = Await MyLoginManager.GetCredentials.ConfigureAwait(False)
            Dim credentials As String = Cryptography.EncodeBase64(String.Format("{0}\{1}:{2}", auth.userName, auth.userPassword))
            client.DefaultRequestHeaders.Add("Authorization", "Basic " & credentials)

            ... More code
        End Function
    End Class

MyLoginManager 类

  Public Class MyLoginManager
  Public Shared Async Function GetCredentials() As Threading.Tasks.Task(Of BasicAuthenticationData)
            Dim auth As New BasicAuthenticationData

            Dim dbConn As String = DBConnection.GetConnection(True)
            Dim q As String = "SELECT * FROM BlaBlaBla"

            Using sdr As SqlDataReader = Await DBHelper.ExecuteReaderAsync(dbConn, q, Nothing).ConfigureAwait(False)
                 ... More code
            End Using

            Return auth
        End Function
End Class

DBHelper 类

Public Class DBHelper
    Public Shared Async Function ExecuteReaderAsync(ByVal dbConnection As String, ByVal commandText As String, ByVal params() As SqlParameter) As Threading.Tasks.Task(Of SqlDataReader)
            Dim dbConnectionAsync As String = New SqlConnectionStringBuilder(dbConnection) With {
                .AsynchronousProcessing = True
            }.ToString

            Dim objConn As New SqlConnection(dbConnectionAsync)

            Dim oc As New SqlCommand(commandText, objConn)
            Dim sdr As SqlDataReader

            ' Throws a custom exception if there is a problem
            Try

                Await oc.Connection.OpenAsync.ConfigureAwait(False)
                sdr = Await oc.ExecuteReaderAsync(CommandBehavior.CloseConnection).ConfigureAwait(False)

                Return sdr
            Catch ex As Exception
            End Try
        End Function
End Class

【问题讨论】:

    标签: asp.net vb.net asynchronous asp.net-web-api async-await


    【解决方案1】:

    不要在你的 web api 项目中添加ConfigureAwait。在库代码中使用它。

    最大的问题是,当您调用使用ConfigureAwait 的方法时,您将丢失上下文。您的上下文包括会话等重要细节,因此在调用使用 ConfigureAwait 的库代码之前,您需要确保从 HttpContext 捕获所需的任何细节。

    【讨论】:

    • 我不太明白您所说的“库代码”是什么意思。什么是图书馆代码?您建议删除配置错误。我可以在需要上下文的地方执行此操作,但如果我不管理会话也不需要上下文,我可以安全地添加它。我说的对吗?
    • 还有其他主题。代码是否正确?这是应该如何编写异步代码层次结构的吗?这段代码可以同时管理数千个请求而不阻塞主线程?创建太多工作线程不会有风险吗?我真的很困惑。我想异步化 httpclient 部分,但 Asp.net 几乎迫使我将所有代码都转换为异步。我现在不知道是非。
    【解决方案2】:

    您应该在来自任何控制器操作的任何等待后面添加对ConfigureAwait(false) 的调用。

    作为Mister Epic said,如果您调用ConfigureAwait(false),您将丢失HttpContext.Current 之类的东西,并且您不应该在控制器操作方法上这样做。但这也意味着每次您不会发生上下文切换。

    您应该做的是将控制器操作方法中的逻辑提取到它们自己的方法中(最好在它们自己的类中),并传递它们完成工作所需的所有内容。

    所以,你做错的唯一一件事就是在控制器操作方法中调用ConfigureAwait(false)。

    【讨论】:

    • 你的意思是WebApi控制器对吧?里面有一个函数进入 System1 类,所以正如你所说,我在控制器内部没有逻辑。我在一个单独的类中调用了 System1 的逻辑。因此,根据你们所说的(@paulo morgado 和@mister epic),我必须删除的唯一 ConfigureAwait(false) 语句是 WebApi 控制器的 inaide。这是正确的吗?
    • 我不明白我什么时候会发生上下文切换。当您包含 ConfigureWait(false) 语句时,会发生上下文切换。如果我不添加它,上下文将保持与以前相同
    • 当有SynchronizationContext 时发生上下文切换,例如在 ASP.NET(非核心)、WinForms、WPF、UWP 等中。如果不使用 ConfigureAwait(false),而不是继续运行 where它登陆了,它将被发布到SynchronizationContext。
    • @AsafEpelbaum 您不会为每个 await 创建一个 worder - await 将线程返回到线程池(通常......)。这对于应用程序正在等待操作已完成的信号的 I/O 操作很有用。当您的应用程序等待时,您可以让您的线程无用地等待,或者您可以将其交还给线程池,在那里它可以被分派以执行一些有用的工作。
    • @AsafEpelbaum,该博客上与 async-await 相关的所有内容都是最新的,您似乎真的需要阅读所有内容。读不读由你决定。
    猜你喜欢
    • 2015-07-30
    • 2017-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-04-19
    • 1970-01-01
    • 1970-01-01
    • 2020-09-09
    相关资源
    最近更新 更多