Ядро Asp.net HttpClient имеет много соединений TIME_WAIT или CLOSE_WAIT - PullRequest
0 голосов
/ 28 февраля 2019

Я использую AddHttpClient() внедрение зависимостей , чтобы добавить именованного клиента во временную службу.Иногда, когда я выполняю netstat -a на сервере, я вижу много открытых соединений со статусом TIME_WAIT или CLOSE_WAIT.Я считаю, что эти соединения занимают столько ресурсов, что другие TCP соединения не могут работать.Это возможно?Есть ли способ остановить это, и это безопасно?

public class Startup
{
    public Startup(IConfiguration configuration)
    {
        Configuration = configuration;
    }

    public IConfiguration Configuration { get; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        ServicePointManager.DefaultConnectionLimit = 200;

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

        services.AddHttpClient(FirebaseService.FirebaseServiceClient, ConfigureFirebaseClient);

        services.AddTransient<FirebaseService>();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseMvc();
    }

    void ConfigureFirebaseClient(HttpClient client)
    {
        var scopes = new string[] { "https://www.googleapis.com/auth/firebase.messaging" };

        Stream certificateStream = File.OpenRead("firebase-adminsdk.json");

        var serviceCredentials = GoogleCredential.FromStream(certificateStream);
        certificateStream.Close();

        var scopedCredentials = serviceCredentials.CreateScoped(scopes);
        var token = scopedCredentials.UnderlyingCredential.GetAccessTokenForRequestAsync().GetAwaiter().GetResult();
        client.SetBearerToken(token);
    }
}

public class FirebaseService
{
    public static string FirebaseServiceClient = "FirebaseServiceClient";

    private HttpClient _client;

    private readonly ILogger<FirebaseService> _logger;
    private readonly string _messagingUrl; 

    public FirebaseService(
        ILogger<FirebaseService> logger,
        IHttpClientFactory clientFactory)
    {
        _logger = logger;
        _messagingUrl = "https://fcm.googleapis.com/v1/projects/test2/messages:send";
        _client = clientFactory.CreateClient(FirebaseServiceClient);
    }

    public async Task<string> PostToFirebase(Dictionary<string, string> payload)
    {
        HttpResponseMessage result = null;
        string cont = null;
        try
        {
            var content = JsonConvert.SerializeObject(payload, Formatting.None);
            var stringContent = new StringContent(content, Encoding.UTF8, "application/json");

            result = await _client.PostAsync(_messagingUrl, stringContent);
            cont = await result.Content.ReadAsStringAsync();
            return cont;
        }
        finally
        {
            result?.Dispose();
        }
    }

}

public class ValuesController : ControllerBase
{
    private readonly IServiceProvider _serviceProvider;

    public ValuesController(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    [HttpGet]
    public async Task<IActionResult> Get()
    {
        var payload = new Dictionary<string, string>();
        List<Task> tasks = new List<Task>();
        for (int i = 0; i < 100; i++)
        {
            FirebaseService firebaseService = (FirebaseService)_serviceProvider.GetService(typeof(FirebaseService));
            var task = firebaseService.PostToFirebase(payload);
            tasks.Add(task);
            Console.WriteLine(i);
        }

        await Task.WhenAll(tasks.ToArray());

        //Console.WriteLine(result);

        return Ok();
    }

}

Ответы [ 3 ]

0 голосов
/ 05 марта 2019

CLOSE_WAIT - другая сторона закрыла соединение.

TIME_WAIT - локальная конечная точка (ваше приложение) закрыла соединение.

Оба соединения сохраняются еще несколько минут на случай, если с другой стороны будут задержаны пакеты.

«Я считаю, что эти соединения занимают столько ресурсов, что другие TCP-соединения не могут работать. Возможно ли это?»- Думаю, нет.Они просто держат порт открытым.Это зависит от того, сколько их.Если у вас будет несколько сотен, с вами все будет в порядке.

"Есть ли способ остановить это и безопасно ли это?"- Я так не думаю.Все они имеют одинаковый PID, поэтому, если вы попытаетесь убить одно, все ваше приложение закроется.

С нетерпением ждем лучших ответов.

0 голосов
/ 15 марта 2019

Я понял, что соединения, с которыми у меня были проблемы, происходили не из client.PostAsync().Фактически они были из запросов аутентификации токена Firebase в действии Client Configuration IHttpClientFactory.Поэтому, когда я переключился на одноэлементное или статическое свойство, CreateClient(clientName) не вызывалось более одного раза, и проблема исчезла.Хотя это было ясно написано в документации, я пропустил это.Each time CreateClient is called, a new instance of HttpClient is created and the configuration action is called.

0 голосов
/ 05 марта 2019

Ну, это потому, что вы можете использовать его с неправильной методологией управления временем жизни.HttpClient имеет проблему исчерпания сокета, поэтому, если возможно, его следует использовать как одиночный.

Эта статья ответит на ваш вопрос.Также прочитайте введите описание ссылки здесь об обходном пути для изменений DNS.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...