StackExchange.Redis не читает ответы при большой нагрузке - PullRequest
0 голосов
/ 05 мая 2018

Мы используем библиотеку StackExchange.Redis в большом проекте ASP.Net Core Web-API и имеем некоторые проблемы с производительностью при большой нагрузке.

Несмотря на то, что чтение и запись в Redis с использованием этой библиотеки прекрасно работают при случайных запросах, библиотека полностью прекращает обработку ответов redis при больших нагрузках на сервер.

Чтобы воспроизвести проблему, мы написали минимальный тестовый сервис. Это наш API-метод в ASP.Net Core Controller-Class:

namespace WebApplication2.Controllers
{
    [Route("api/[controller]")]
    public class TestController : Controller
    {
        private readonly ConnectionMultiplexer _client;

        public TestController(ConnectionMultiplexer client)
        {
            _client = client;
        }

        [HttpGet("many")]
        public async Task<IActionResult> GetAsync()
        {
            var database = _client.GetDatabase();

            Console.WriteLine("-- StringSetAsync");
            await database.StringSetAsync($"testkey:{Guid.NewGuid()}", "test").ConfigureAwait(false);
            Console.WriteLine("-- -- Return");

            return Ok();
        }
    }
}

И мы подключаемся к нашему единственному серверу Redis, используя:

var client = ConnectionMultiplexer.Connect(new ConfigurationOptions {
    EndPoints = { { "10.200.1.100", 6379 } },
    AbortOnConnectFail = false,
    ConnectTimeout = 15000
}, Console.Out);

Вывод при случайных запросах (как и ожидалось):

-- StringSetAsync
-- -- Return
-- StringSetAsync
-- -- Return
-- StringSetAsync
-- StringSetAsync
-- -- Return
-- -- Return
...

Выход при большой нагрузке (wrk -t8 -c400 -d60s --latency http://localhost/api/test/many):

-- StringSetAsync
-- StringSetAsync
-- StringSetAsync
-- StringSetAsync
-- StringSetAsync
<<< Forever until we kill the request-spamming with wrk >>>
-- -- Return
-- -- Return
-- -- Return
-- -- Return
-- -- Return

В Wireshark мы видим, что Redis мгновенно отвечает на команды SET, но похоже, что StackExchange.Redis не читает входящие ответы TCP и, следовательно, асинхронное выполнение StringSetAsync никогда не завершается.

Кроме того, приложение начинает потреблять все больше и больше памяти из-за тысяч задач, ожидающих их выполнения.

Мы попробовали некоторую отладку, и эта строка https://github.com/StackExchange/StackExchange.Redis/blob/master/StackExchange.Redis/StackExchange/Redis/PhysicalConnection.cs#L716 никогда не достигается во время рассылки спама до тех пор, пока мы не остановим wrk. Затем он выполняется много раз, пока все ответы Redis не будут обработаны.

Эта ошибка не возникает при выполнении многих параллельных команд Redis с использованием цикла while / for вручную, поэтому мы считаем, что это каким-то образом связано с асинхронным выполнением основного метода ASP.Net.

РЕДАКТИРОВАТЬ: Как предположил Орёл Эраки, мы попытались воспроизвести это с синхронными API-методами, и проблема не устранена.

[HttpGet("sync")]
public IActionResult Get()
{
    var database = _client.GetDatabase();

    Console.WriteLine("-- StringSet");
    database.StringSet($"testkey:{Guid.NewGuid()}", "test");
    Console.WriteLine("-- -- Return");

    return Ok();
}

Мы используем версию 1.2.4, потому что она включена в ASP.Net Core, но мы можем воспроизвести ее и с последней версией 1.2.6.

Мы уже сообщали об этой проблеме в библиотеку, но, похоже, никому нет до этого дела. https://github.com/StackExchange/StackExchange.Redis/issues/826

Заранее спасибо за любую помощь!

1 Ответ

0 голосов
/ 03 мая 2019

Эта проблема была исправлена ​​во время основной перезаписи некоторого кода связи в выпуске 2.0. Пожалуйста, смотрите связанную проблему и информацию о выпуске для получения дополнительной информации: https://github.com/StackExchange/StackExchange.Redis/issues/871

...