Re: Аццкий ад и асинхронная асинхронность
От: ifle  
Дата: 12.11.16 14:02
Оценка: 2 (1) -2
Здравствуйте, Artem Korneev, Вы писали:

AK>
AK>public async Task InvokeHttpCall(Func<HttpClient, Task<HttpResponseMessage>> callback)
AK>{
AK>    using (var httpClient = new HttpClient())
AK>    {
AK>        HttpResponseMessage response = null;
AK>        var http = httpClient;

AK>        await this.retryPolicy.ExecuteAndCaptureAsync(
AK>            async () =>
AK>                await Task.Run(
AK>                    async () => response = await callback.Invoke(http)));
AK>    }
AK>}
AK>


В вашем коде есть один косяк не имеющий отношения к async. httpclient должен быть статическим
private static HttpClient httpClient = new HttpClient();

YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE
Re[2]: Аццкий ад и асинхронная асинхронность
От: Doc Россия http://andrey.moveax.ru
Дата: 30.11.16 09:53
Оценка: 46 (1)
Здравствуйте, Sinix, Вы писали:

S>А если бы товарищ автор Polly читал бы гадлайны, то код выглядел бы как

S>
S>        response = await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http)).Result;
S>


await вместе с Result?
Re[3]: Аццкий ад и асинхронная асинхронность
От: Sinix  
Дата: 19.11.16 15:48
Оценка: 3 (1)
Здравствуйте, SergASh, Вы писали:

SAS>Что за гайдлайны такие? Ссылочку можно попросить?


Конкретно по таскам ничего свежее Task-based Asynchronous Pattern (TAP) не выходило, но тут конкретики и не требуется.

Достаточно общих соображений из FDG book:

Framework Design Principle
Frameworks must be designed starting from a set of usage scenarios and code samples implementing these scenarios.

Если непонятно, про что речь — тынц на подробное объяснение.

На конкретном примере:
// 1. У нас есть действие, которое что-то возвращает
var result = await GetResultAsync(someParams);

// 2. Мы хотим запустить это действие в каком-то контексте
using(var contex = GetCaptureContext(someParams))
{
  var result = await GetResultAsync(someParams);
}

// 3. Мы хотим сами управлять вызовами внутри контекста, к примеру, запускать код несколько раз.
//    Оборачиваем действие в делегат, получаем
var result = await retryPolicy.ExecuteAndCaptureAsync(() => GetResultAsync(someParams)));


// 4. Мы хотим кроме самого результата вернуть что-то ещё, к примеру, количество итераций
var executeResult = await retryPolicy.ExecuteAndCaptureAsync(() => GetResultAsync(someParams)));
var result = executeResult.Result;


Смысл такой: каждый шажок позволяет решить _реальную_ проблему и сам по себе не создаёт новых. С мелкими шажками это отследить очень легко и не требуется прыгать назад чтобы уточнять, не забыл ли чего. Слона едят по кусочкам.

Если на этот принцип забить и замахнуться на слона целиком, то получается сегодняшний пример: код умеет всё, кроме изначального сценария
Что самое обидное, вот в этот момент люди _никогда_ не пробуют остановиться и поискать момент, в который всё пошло не так. Вместо этого проблему маскируют новыми костылями и так до тех пор, пока объём костылей не перерастёт на порядок объём собственно полезного кода.
Re[2]: Аццкий ад и асинхронная асинхронность
От: alexander_r  
Дата: 12.11.16 09:55
Оценка: +1
Здравствуйте, Sinix, Вы писали:


S>Здравствуйте, Artem Korneev, Вы писали:


AK>>Хочется всё это переписать к более понятному виду.


S>Использовать await в каждом методе по цепочке вызовов абсолютно необязательно.


S>
S>public async Task InvokeHttpCall(Func<HttpClient, Task<HttpResponseMessage>> callback)
S>{
S>    using (var httpClient = new HttpClient())
S>    {
S>        HttpResponseMessage response = null;
S>        var http = httpClient;

S>        await this.retryPolicy.ExecuteAndCaptureAsync(() => 
S>                callback.Invoke(http).ContinueWith(t => response = t.Result));
S>    }
S>}
S>


S>А если бы товарищ автор Polly читал бы гадлайны, то код выглядел бы как

S>
S>        response = await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http)).Result;
S>

S>я бы на вашем месте завёл бы issue на это дело.

callback.Invoke(http) <- зачем Invoke ???

response = await this.retryPolicy.ExecuteAndCaptureAsync(() => callback(http)).Result;
Re[2]: Аццкий ад и асинхронная асинхронность
От: fddima  
Дата: 12.11.16 17:20
Оценка: +1
Здравствуйте, ifle, Вы писали:

I>В вашем коде есть один косяк не имеющий отношения к async. httpclient должен быть статическим

Статическим ему конечно же быть вредно, а вот переиспользовать инстанс в рамках клиента — может быть и нужно, если хочется получить пул соединений в рамках группы соединений (если грубо, на каждый инстанс HttpClient — своя группа соединений).

PS: А ещё лучше выбросить к черту весь этот недосетевой стэк. Но это уже скорее из области вредных советов в рамках данного форума.
Re[3]: Аццкий ад и асинхронная асинхронность
От: Sinix  
Дата: 30.11.16 09:56
Оценка: +1
Здравствуйте, Doc, Вы писали:


Doc>await вместе с Result?

Скобки потерялись
       response = (await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http))).Result;
// или
       var executeResult = await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http));
       response = executeResult.Result;
Аццкий ад и асинхронная асинхронность
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 12.11.16 05:33
Оценка:
Написал я тут в коде одну конструкцию, которая пугает меня своей сложностью.
Это — кусок кода класса RestServiceClient, предназначенного для отправки запросов HTTP-серверам. Эта функция создаёт HttpClient и вызывает коллбэк, указанный в качестве параметра.

public async Task InvokeHttpCall(Func<HttpClient, Task<HttpResponseMessage>> callback)
{
    using (var httpClient = new HttpClient())
    {
        HttpResponseMessage response = null;
        var http = httpClient;

        await this.retryPolicy.ExecuteAndCaptureAsync(
            async () =>
                await Task.Run(
                    async () => response = await callback.Invoke(http)));
    }
}


Сделано это для того чтоб не повторять один и тот же код для обёртки каждого типа HTTP-запроса. Здесь я привожу упрощённый код, реально там ещё установка разных заголовков аутентификации, замеры времени обработки запроса и т.д. Результат выполнения запроса (переменная response) используется потом для возврата HttpStatusCode. Там несколько похожих методов, с десериализацией результата и без него, я привожу здесь простейший вариант. Используется этот метод так:

public async Task DeleteAsync(Uri uri)
{
    await this.httpCallsInvoker.InvokeHttpCall((httpClient) => httpClient.DeleteAsync(uri));
}


Здесь идёт обёртка для DELETE-запоса, остальные типы запросов реализованы аналогично, но с дополнительными параметрами.

Вот эта строчка:

        await this.retryPolicy.ExecuteAndCaptureAsync


Использует библиотеку Polly для применения разных политик обработки ошибок соединения (retry, circuit breaker, и т.д.). Оно тоже всё асинхронное и принимает коллбэки в качестве параметров.

Так вот.. это всё работает, но результат мне самому кажется излишне сложным. Особенно меня смущает вот эта лестница из async/await:

        await this.retryPolicy.ExecuteAndCaptureAsync(
            async () =>
                await Task.Run(
                    async () => response = await callback.Invoke(http)));


Хочется всё это переписать к более понятному виду, потому как сейчас, кроме меня, этот код врятли кто-нибудь разберёт в моей команде. Коллеги, подскажите, как и с какой стороны к этому лучше подходить? Нет ли тут каких-то явных косяков, которые я не замечаю? Мне кажется, что что-то в этой последовательности async/await можно упростить, но я пока не смог.
С уважением, Artem Korneev.
Re: Аццкий ад и асинхронная асинхронность
От: TK Лес кывт.рф
Дата: 12.11.16 07:14
Оценка:
Здравствуйте, Artem Korneev, Вы писали:


AK>Так вот.. это всё работает, но результат мне самому кажется излишне сложным. Особенно меня смущает вот эта лестница из async/await:


AK>
AK>        await this.retryPolicy.ExecuteAndCaptureAsync(
AK>            async () =>
AK>                await Task.Run(
AK>                    async () => response = await callback.Invoke(http)));
AK>


А кто вас заставляет использовать эти все async / await?

await this.retryPolicy.ExecuteAndCaptureAsync(() => Task.Run(() => response = callback.Invoke(http)));
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Re: Аццкий ад и асинхронная асинхронность
От: Sinix  
Дата: 12.11.16 08:57
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

AK>Хочется всё это переписать к более понятному виду.


Использовать await в каждом методе по цепочке вызовов абсолютно необязательно.

public async Task InvokeHttpCall(Func<HttpClient, Task<HttpResponseMessage>> callback)
{
    using (var httpClient = new HttpClient())
    {
        HttpResponseMessage response = null;
        var http = httpClient;

        await this.retryPolicy.ExecuteAndCaptureAsync(() => 
                callback.Invoke(http).ContinueWith(t => response = t.Result));
    }
}


А если бы товарищ автор Polly читал бы гадлайны, то код выглядел бы как
        response = (await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http))).Result;

я бы на вашем месте завёл бы issue на это дело.
Отредактировано 30.11.2016 9:57 Sinix . Предыдущая версия .
Re[3]: Аццкий ад и асинхронная асинхронность
От: Sinix  
Дата: 12.11.16 11:43
Оценка:
Здравствуйте, alexander_r, Вы писали:


_>callback.Invoke(http) <- зачем Invoke ???


Затем, что копипаста
Re[2]: Аццкий ад и асинхронная асинхронность
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 13.11.16 05:50
Оценка:
Здравствуйте, ifle, Вы писали:

I>В вашем коде есть один косяк не имеющий отношения к async. httpclient должен быть статическим


Не подходит по условиям использования.
Этот RestClient используется в multi-tenant облачном сервисе, т.е. там два последовательных запроса могут придти от разных пользователей разных организаций. В этом HttpClient есть поле DefaultRequestHeaders, где выставляются хэдеры авторизации. Т.е. сделав статическим, мы сразу ломаем всю аутентификацию.

Используется всё это сравнительно редко (по крайней мере, пока), так что проблем из-за пересоздания объекта HttpClient нет. Но за информацию спасибо.
С уважением, Artem Korneev.
Re[3]: Аццкий ад и асинхронная асинхронность
От: fddima  
Дата: 13.11.16 07:52
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

AK>Не подходит по условиям использования.

Тут важнее — нужен пулинг соединений или нет? А то при достаточно небольшой нагрузке можно все tcp порты скушать (предполагая что удаленный хост один, и есть че пулить).

AK>В этом HttpClient есть поле DefaultRequestHeaders, где выставляются хэдеры авторизации. Т.е. сделав статическим, мы сразу ломаем всю аутентификацию.

Можно спокойно передавать все необходимые хедеры собственно в момент запроса (при подготовке HttpRequestMessage).

AK>Используется всё это сравнительно редко (по крайней мере, пока), так что проблем из-за пересоздания объекта HttpClient нет.

А, ну тогда наверное ок.
Re: Маленький оффтоп.
От: Sharov Россия  
Дата: 14.11.16 11:06
Оценка:
Здравствуйте, Artem Korneev, Вы писали:

На RestSharp для нужд смотрели?
Кодом людям нужно помогать!
Re[2]: Аццкий ад и асинхронная асинхронность
От: Tom Россия http://www.RSDN.ru
Дата: 14.11.16 11:09
Оценка:
Здравствуйте, TK, Вы писали:

TK>Здравствуйте, Artem Korneev, Вы писали:



AK>>Так вот.. это всё работает, но результат мне самому кажется излишне сложным. Особенно меня смущает вот эта лестница из async/await:


AK>>
AK>>        await this.retryPolicy.ExecuteAndCaptureAsync(
AK>>            async () =>
AK>>                await Task.Run(
AK>>                    async () => response = await callback.Invoke(http)));
AK>>


TK>А кто вас заставляет использовать эти все async / await?


TK>
TK>await this.retryPolicy.ExecuteAndCaptureAsync(() => Task.Run(() => response = callback.Invoke(http)));
TK>


Еси в конце концов всё равно у нас всё сводится к синхронному коду, то на кой стартавать таск вообще, можно прямо в ExecuteAndCaptureAsync делегат передать который синхронно будет выполняться
Народная мудрось
всем все никому ничего(с).
Re[3]: Аццкий ад и асинхронная асинхронность
От: Sinix  
Дата: 14.11.16 11:20
Оценка:
Здравствуйте, Tom, Вы писали:

Tom>Еси в конце концов всё равно у нас всё сводится к синхронному коду, то на кой стартавать таск вообще, можно прямо в ExecuteAndCaptureAsync делегат передать который синхронно будет выполняться


callback запускает новую задачу, её надо дождаться и запихнуть результат в response. А Task.Run тут вообще лишний.
Re[4]: Аццкий ад и асинхронная асинхронность
От: Tom Россия http://www.RSDN.ru
Дата: 14.11.16 11:29
Оценка:
S>А Task.Run тут вообще лишний.
Об этом и речь
Народная мудрось
всем все никому ничего(с).
Re[2]: Маленький оффтоп.
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 14.11.16 17:44
Оценка:
Здравствуйте, Sharov, Вы писали:

S>На RestSharp для нужд смотрели?


Нет, не смотрел. REST-клиента было проще реализовать, чем искать готовое.
Сейчас посмотрел на RestSharp, мне синтаксис не нравится.

var request = new RestRequest("resource/{id}", Method.POST);
...
IRestResponse response = client.Execute(request);
var content = response.Content;


У меня всё обёрнуто в дженерики и HTTP-методы имеют соответствующие обёртки в классе RestClient, т.е. я вызываю это как-то так:

var result = restClient.Get<MyDataType>("http://localhost")


И получаю в ответ уже десериализованный объект.
С уважением, Artem Korneev.
Re[2]: Аццкий ад и асинхронная асинхронность
От: SergASh  
Дата: 19.11.16 15:05
Оценка:
Здравствуйте, Sinix, Вы писали:

S>А если бы товарищ автор Polly читал бы гадлайны, то код выглядел бы как

S>
S>        response = await this.retryPolicy.ExecuteAndCaptureAsync(() => callback.Invoke(http)).Result;
S>


Что за гайдлайны такие? Ссылочку можно попросить?
Отредактировано 19.11.2016 15:05 SergASh . Предыдущая версия .
Re[2]: Аццкий ад и асинхронная асинхронность
От: Artem Korneev США https://www.linkedin.com/in/artemkorneev/
Дата: 27.11.16 06:25
Оценка:
Здравствуйте, TK, Вы писали:

TK>А кто вас заставляет использовать эти все async / await?


Без любого из этих async / await оно тупо не работало.
Я с той работы ушёл уже, не могу сейчас тот код пощупать. На днях попробую разобраться с .net core на своём домашнем Linux-десктопе, попробую ещё разок.
Вообще, у меня в голове крутится мысль, что лишние там не async / await, вернее, не только async / await, но и асинхронность тех методов в целом. Мне думается, что достаточно там вызывать ExecuteAndCaptureAsync, оно уже должно обеспечивать асинхронность.
Но надо попробовать. Как попробую — напишу.
С уважением, Artem Korneev.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.