Добавлены ключи, отсутствующие в HttpContext.Session (параллелизм) - PullRequest
0 голосов
/ 11 апреля 2020

Я использую . NET Core 3.1 . Мне интересно, если HttpContext.Session.SetString(...) потокобезопасен ? Например, клиент делает два запроса на одно и то же действие контроллера (Test) одновременно. Контроллер добавляет строку в сессию (см. Пример ниже). Будет ли в конце сеанса два, один или ноль ключей (например, когда я обновлю sh страницу)?

public IActionResult Test()
{
    HttpContext.Session.SetString(Guid.NewGuid().ToString(), "test");
    return Ok();
}

Я сохраняю некоторые значения в сеансе при использовании DevExtreme FileUploader . Когда я загружаю несколько файлов одновременно, компонент делает несколько запросов одновременно и в конце, обычно в сеансе отсутствуют некоторые ключи . Я думаю, что происходит какое-то состояние гонки.

ДОБАВЛЕНО: КОД КЛИЕНТА

Я заметил, что сеансовые ключи отсутствуют, только если я использую method: 'POST' (есть только 1 ключ). Если я использую method: 'GET', есть 3 клавиши (правильно).

$(document).ready(function () {
    var method = 'GET'; // works (3 keys)
    //var method = 'POST'; // doesn't work (1 key)

    $('#fire').on('click', function () {
        $.when(
            $.ajax({
                url: 'Session/Test',
                method: method,
                success: function(){
                    console.log('response', 1);
                }
            }),

            $.ajax({
                url: 'Session/Test',
                method: method,
                success: function(){
                    console.log('response', 2);
                }
            }),

            $.ajax({
                url: 'Session/Test',
                method: method,
                success: function(){
                    console.log('response', 3);
                }
            })
        ).then(function () {
            alert('Done');
        });
    });
});

Ответы [ 2 ]

2 голосов
/ 12 апреля 2020

Предположим, вы используете реализацию сеанса по умолчанию, предлагаемую ASP. Net Core.

В терминах HttpContext.Session:

HttpContext.Session возвращает экземпляр DistributedSession, который внутренне использует Dictionary<TKey, TValaue>. Словарь не является потокобезопасным, поэтому, если вы обращаетесь к Session из нескольких потоков (например, Task.Run), это может привести к неожиданным результатам.

С точки зрения сеанса для различных запросов:

В ASP. Net Ядро, Session происходит от ISessionStore, который имеет кратковременное время жизни . Значение Session объект не является общим для запросов . Поэтому, если у вас есть одновременные запросы, каждый из которых будет иметь свой собственный объект Session.

С точки зрения условия гонки:

Реализация по умолчанию сеанса читает / записывает состояние сеанса с / на .AspNetCore.Session cook ie. Это может вызывать условия состязания.

Поскольку Session для каждого клиента, вы можете иметь условия состязания , когда у вас есть одновременные запросы от одного и того же клиента, которые касаются одинаковых бит тот же повар ie / состояние сеанса . Состояние гонки, однако, не из-за Session на стороне сервера. Это на самом деле вызвано управлением ie на стороне клиента.

Состояние сеанса неблокирующее. Если два запроса одновременно пытаются изменить содержимое сеанса, последний запрос переопределяет первый.

Рассмотрим этот пример:

Скажем, у вас есть действие контроллера, которое устанавливает Session с предоставленным value, а другое действие контроллера извлекает значение из Session и возвращает его в теле:

[HttpGet]
[Route("create")]
public IActionResult CreateSession([FromQuery]string value)
{
    HttpContext.Session.SetString("key", value);
    return Ok();
}

[HttpGet]
[Route("get")]
public IActionResult ReturnSession([FromQuery] string expected)
{
    var actual = HttpContext.Session.GetString("key");
    return Ok(new { actual, expected });
}

Если вы тестируете эти действия с HttpClient:

async Task TestSession(HttpClient client, string str)
{
    await client.GetAsync($"https://localhost:5001/session/create?value={str}");
    var r = await client.GetAsync($"https://localhost:5001/session/get?expected={str}");
    var session = await r.Content.ReadAsStringAsync();

    Console.WriteLine(session);
}

using (var client = new HttpClient())
{
    await TestSession(client, "abc");
}

Вывод должен выглядеть следующим образом:

{"actual":"abc","expected":"abc"}

Возникает проблема при одновременных запросах от одного и того же клиента:

using (var client = new HttpClient())
{
    var tasks = new List<Task>();
    for (var i = 0; i < 10; i++)
    {
        var str = i.ToString();
        tasks.Add(Task.Run(() => TestSession(client, str)));
    }

    await Task.WhenAll(tasks);
}

Вывод выглядит следующим образом:

{"actual":"2","expected":"1"}
{"actual":"3","expected":"6"}
{"actual":"4","expected":"5"}
{"actual":"4","expected":"3"}
{"actual":"4","expected":"2"}
{"actual":"8","expected":"4"}
{"actual":"7","expected":"8"}
{"actual":"7","expected":"7"}
{"actual":"9","expected":"0"}
{"actual":"9","expected":"9"}

В приведенном выше случае состояние сеанса было изменено с помощью запроса 3 , между create и get из request 6 , то есть запрос 6 не может правильно видеть состояние сеанса.

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

0 голосов
/ 11 апреля 2020

Нет, коллекции tnet не являются поточно-ориентированными и не являются сеансными. Если вы используете один и тот же сеанс более чем в одном потоке, вы должны считать его доступным только для чтения

...