У нас есть SOA для нашего решения. Мы используем .net framework 4.5.1, asp.net mvc 4.6, сервер sql, сервер Windows Server и сервер идентификации thinktecture 3 (для вызовов на основе токенов на основе токенов).
Структура решения выглядит следующим образом:
Приложение mvc frontend взаимодействует с нашим приложением webapi через оболочку httpClient. Вот общий код оболочки 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 (вызовы async-методов как синхронизация), а иногда мы используем этот общий метод непосредственно с ожиданием - ожидание async-задачи вроде:
var result = await _resourceServerRestClient.GetAsync<ServiceOperation<DailyAgendaModel>>("dailyAgenda/" + id);
Итак, вот наша проблема:
Когда мы тестируем наше приложение mvc с помощью jmeter (для выполнения определенного теста нагрузки /10 потоков за 1 секунду), через пару минут приложение mvc перестает работать [исключение отменяется из-за таймаута] (возможно, только 1-2 тайм-аута запросов): HttpResponseMessage response = await client.GetAsync(uri);
. Но после этого запроса все запросы будут сбойными, как в строке. Так что приложение mvc висит на 2-15 минут (случайно), но в это время я могу отправлять новые запросы от почтальона на webapi. Они в порядке, я имею в виду, что webapi хорошо реагирует. Через пару минут приложение mvc вернется в нормальное состояние.
Примечание. У нас есть балансировка нагрузки для mvc-ui и webapi. Потому что иногда мы получаем 120 тыс. Запросов в минуту в напряженный день. Но он дает такую же ошибку, если нет балансировки нагрузки перед приложением webapi или mvc. Так что это не проблема LB.
Примечание2: Мы пытались использовать RestSharp для связи mvc-ui и webapi. Мы получили такую же ошибку. Когда reuqest терпит неудачу, все запросы будут сбой в строке. Это похоже на сетевую ошибку, но мы не можем найти для нее доказательства.
Вы можете увидеть какую-либо ошибку в моей обертке httpClient? или лучший вопрос:
В вашем решении, как ваше приложение mvc общается с вашим приложением webapi? Каковы лучшие практики здесь?
Update1. Мы переместили проекты .net 4.5.1 - 4.6.1. Тот же тупик произошел снова. И чем мы временно перемещали все исходные коды слоя: "Бизнес и репозиторий" как уровень DLL. Сейчас нет веб-страниц между бизнесом и уровнем презентации. Мертвый замок решен. Мы по-прежнему ищем, почему коды httpClientWrapper не работают должным образом, когда мы вызываем методы webapi из наших контроллеров web-приложений.