gpt4 book ai didi

c# - Mvc 应用程序异步方法挂起

转载 作者:IT王子 更新时间:2023-10-29 04:39:54 25 4
gpt4 key购买 nike

我们的解决方案采用 SOA。我们正在使用 .net framework 4.5.1、asp.net mvc 4.6、sql server、windows server 和 thinktecture identity server 3(用于基于 token 的 webapi 调用。)

解决方案结构如下;
enter image description here

我们的 mvc 前端应用程序通过 httpClient 包装器与我们的 webapi 应用程序对话。这是通用的 http 客户端包装器代码;

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace Cheetah.HttpClientWrapper
{
public class ResourceServerRestClient : IResourceServerRestClient
{
private readonly ITokenProvider _tokenProvider;

public ResourceServerRestClient(ITokenProvider tokenProvider)
{
_tokenProvider = tokenProvider;
}

public string BaseAddress { get; set; }

public Task<T> GetAsync<T>(string uri, string clientId)
{
return CheckAndInvokeAsync(async token =>
{
using (var client = new HttpClient())
{
ConfigurateHttpClient(client, token, clientId);

HttpResponseMessage response = await client.GetAsync(uri);

if (response.IsSuccessStatusCode)
{
return await response.Content.ReadAsAsync<T>();
}

var exception = new Exception($"Resource server returned an error. StatusCode : {response.StatusCode}");
exception.Data.Add("StatusCode", response.StatusCode);
throw exception;
}
});
}

private void ConfigurateHttpClient(HttpClient client, string bearerToken, string resourceServiceClientName)
{
if (!string.IsNullOrEmpty(resourceServiceClientName))
{
client.DefaultRequestHeaders.Add("CN", resourceServiceClientName);
}

if (string.IsNullOrEmpty(BaseAddress))
{
throw new Exception("BaseAddress is required!");
}

client.BaseAddress = new Uri(BaseAddress);
client.Timeout = new TimeSpan(0, 0, 0, 10);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", bearerToken);
}

private async Task<T> CheckAndInvokeAsync<T>(Func<string, Task<T>> method)
{
try
{
string token = await _tokenProvider.IsTokenNullOrExpired();

if (!string.IsNullOrEmpty(token))
{
return await method(token);
}

var exception = new Exception();
exception.Data.Add("StatusCode", HttpStatusCode.Unauthorized);
throw exception;
}
catch (Exception ex)
{
if (ex.Data.Contains("StatusCode") && ((HttpStatusCode)ex.Data["StatusCode"]) == HttpStatusCode.Unauthorized)
{
string token = await _tokenProvider.GetTokenAsync();

if (!string.IsNullOrEmpty(token))
{
return await method(token);
}
}

throw;
}
}

public void ThrowResourceServerException(List<string> messages)
{
string message = messages.Aggregate((p, q) => q + " - " + p);

var exception = new Exception(message);

exception.Data.Add("ServiceOperationException", message);

throw exception;
}
}
}

此外,有时这个 http 客户端包装器与 NitoAsync 管理器一起使用(将异步方法称为同步。),有时我们直接将此通用方法与 await 一起使用 - 异步任务等待;

var result = await _resourceServerRestClient.GetAsync<ServiceOperation<DailyAgendaModel>>("dailyAgenda/" + id);

所以这是我们的问题:

当我们使用 jmeter 测试我们的 mvc 应用程序时(用于进行某种负载测试/每 1 秒 10 个线程),几分钟后,mvc 应用程序停止工作 [异常是由于超时而取消任务](也许只有 1-2 个请求超时)在这一行:HttpResponseMessage response = await client.GetAsync(uri);。但是在该请求之后,所有请求都将失败,就像它们在行中一样。所以 mvc 应用程序挂起 2-15 分钟(随机),但在那段时间我可以从 postman 向 webapi 发送新请求。他们没问题,我的意思是 webapi 响应良好。几分钟后,mvc 应用程序恢复正常。

注意:我们有用于 mvc-ui 和 webapi 的负载均衡器。因为有时在忙碌的一天中我们会在一分钟内收到 120K 个请求。但是如果 webapi 或 mvc 应用程序前面没有负载平衡器,它会给出同样的错误。所以这不是 LB 的问题。

Note2:我们尝试使用 RestSharp 进行 mvc-ui 和 webapi 通信。我们在这里遇到了同样的错误。当一个reuqest失败时,所有的请求都会连续失败。看起来像是网络错误,但我们找不到证据。

你能在我的 httpClient 包装器上看到任何错误吗?或者更好的问题是;
在您的解决方案中,您的 mvc 应用程序如何与您的 webapi 应用程序通信?这里的最佳做法是什么?

Update1:我们将项目 .net 4.5.1 移到了 4.6.1。同样的僵局再次发生。然后我们临时移动了该层的所有源代码:“业务和存储库”作为 dll 级别。现在业务和表示级别之间没有 webapi。死锁解决了。我们仍在寻找为什么当我们从 Web 应用程序 Controller 调用 WebAPI 方法时 httpClientWrapper 代码无法正常工作。

最佳答案

better question is; In your solution, how is your mvc application communicating with your webapi application ? What are the best practices here ?

此处的最佳做法是让客户端(在您的情况下是浏览器)直接从 Web API Controller 检索数据,而 MVC Controller 仅提供纯 HTML View ,其中包括布局、样式( css)、视觉结构、脚本(即 javascript)等,而不是数据。

Browser communicating to MVC and Web API

图片来源:Ode to Code .顺便说一句,该网站上的作者也不推荐您的方法,尽管它被列为一个选项。

  1. 这服务器很好 SOC在您的 View 和您的数据之间,让您更容易地对任何一部分进行更改。
  2. 它允许客户端(浏览器)异步检索数据,从而创造更好的用户体验。

通过不这样做并在调用堆栈中添加网络请求步骤,您在数据流中创建了一个不必要的昂贵步骤(从 MVC Controller 调用到 Web API 部署)。执行过程中跨越的边界越多,执行速度越慢。

正如您已经想到的那样,快速的解决方案是直接从您的 MVC 项目调用您的业务代码库。这将避免额外的和不必要的网络步骤。这样做并没有错,而且许多更传统的站点在同一个调用中同时提供 View (html) 和数据。它实现了更紧密耦合的设计,但比您拥有的更好。

最好的长期解决方案是更改 MVC View ,以便它们直接调用您的 Web API 部署。这可以使用像 Angular 这样的框架来完成。 , React , Backbone等。如果 Web API 方法调用有限且预计不会增长,您还可以使用 JQuery或纯 javascript 但是 我不会尝试在此基础上构建复杂的应用程序,像 Angular 这样的框架是有原因的变得如此受欢迎。

至于这种情况下的实际底层技术问题,如果没有内存转储来查看是什么资源导致了死锁,我们无法确定。它可能就像确保您的 MVC 操作方法也返回一样简单 async Task<ActionResult> (而不仅仅是 ActionResult,我猜这就是您现在构建它们的方式)这样他们就可以调用 HttpClient使用实际的 async/await图案。老实说,因为它是一个糟糕的设计,所以我不会花任何时间来尝试让它发挥作用。

关于c# - Mvc 应用程序异步方法挂起,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35754166/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com