Веб-асинхронная задача WebAsyncTask в сценарии с высокой степенью параллелизма

Spring

Синхронный и асинхронный

Веб-синхронный вызов

Браузер/клиент инициирует запрос, веб-сервер запускает поток для обработки запроса, и когда запрос обрабатывается, веб-сервер возвращает результат обработки, который представляет собой синхронный вызов.

WebAsyncTask-同步调用

Эта статья и ссылка на изображение (то же самое ниже) - Mu Zixu: асинхронный вызов [WebAsyncTask]

В обычных сценариях, если нагрузка на сервер невелика, а серверная служба мощная, проблем с синхронными вызовами не возникает.

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

WebAsyncTask-同步调用

Веб-асинхронный вызов

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

Давайте возьмем в качестве аналогии пример работы по дому: в прошлом, когда вы использовали огонь для приготовления пищи, вы могли только стоять рядом, чтобы добавить дрова и огонь, а затем ставить посуду, когда рис был готов; после того, как рисоварка закончила добавили необходимые материалы для приготовления и выдали инструкцию по приготовлению, рисоварка начнет работать сама по себе, и вы сможете выйти поставить посуду.Когда рис будет готов, рисоварка автоматически подаст звуковой сигнал.Уведомит вас, чтобы прийти забрать съев еду, ее эффективность значительно повысится, а ваши возможности параллельной обработки резко возрастут.

После Spring MVC 3.2 была введена асинхронная обработка запросов на основе Servlet 3, которая может реализовывать следующие асинхронные вызовы.

WebAsyncTask-异步调用

Сравнение синхронных и асинхронных вызовов веб-запросов

Давайте быстро продемонстрируем использование WebAsyncTask и разницу между синхронными и асинхронными вызовами на простом примере синхронного и асинхронного сравнения.

две задачи

Сначала определите контроллер и добавьте два метода для представления двух задач, которые должны выполняться веб-запросом, и эти две задачи будут выполняться в течение 3 секунд соответственно.


import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * WebAsyncTask
 *
 * @author ijiangtao
 * @create 2019-07-03 11:31
 **/
@RestController
public class WebAsyncTaskController {

    private Map<String, String> buildResult() {
        System.out.println("building result");
        Map<String, String> result = new HashMap<>();
        try {
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 1 * 1000; i++) {
            result.put(i + "-key", i + "value");
        }
        return result;
    }

    private void doTask() {
        System.out.println("do some tasks");
        try {
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Выполнять веб-запросы синхронно

@GetMapping("/r1")
public Map<String, String> r1() {

    Instant now = Instant.now();

    Map<String, String> result = buildResult();

    doTask();

    System.out.println("r1 time consumption: " + ChronoUnit.SECONDS.between(now, Instant.now()) + " seconds");

    return result;
}

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

Выполнять веб-запросы асинхронно

@GetMapping("/r2")
public WebAsyncTask<Map<String, String>> r2() {

    Instant now = Instant.now();

    Callable<Map<String, String>> callable = new Callable<Map<String, String>>() {
        @Override
        public Map<String, String> call() throws Exception {
            return buildResult();
        }
    };

    doTask();

    WebAsyncTask<Map<String, String>> webAsyncTask = new WebAsyncTask<>(callable);

    System.out.println("r2 time consumption: " + ChronoUnit.SECONDS.between(now, Instant.now()) + " seconds");

    return webAsyncTask;
}

Для асинхронно выполняемых веб-запросов мы определяем задачу возврата результата веб-запроса, реализуя метод call интерфейса Callable, и выполняем задачу через WebAsyncTask.Когда задача вызывается и возвращается немедленно, другие задачи могут выполняться в параллельно, и, наконец, когда выполнение WebAsyncTask завершается. Позднее веб-запрос возвращается.

Настроить асинхронное выполнение веб-запросов

Если вы просто хотите добиться асинхронной и параллельной обработки, вы также можете использовать механизм Future, предоставляемый JDK.Прелесть WebAsyncTask заключается в том, что он предоставляет конфигурацию тайм-аута, асинхронный исполнитель задач и обратный вызов завершения выполнения, исключение выполнения и обратный вызов после тайм-аута.

package net.ijiangtao.tech.demo.webasynctask.controller;

import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;

import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

/**
 * WebAsyncTask
 *
 * @author ijiangtao
 * @create 2019-07-03 11:31
 **/
@RestController
public class WebAsyncTask2Controller {
    
    @GetMapping("/r3")
    public WebAsyncTask<Map<String, String>> r2() {

        Instant now = Instant.now();

        Callable<Map<String, String>> callable = new Callable<Map<String, String>>() {
            @Override
            public Map<String, String> call() throws Exception {
                return buildResult();
            }
        };

        SimpleAsyncTaskExecutor executor = new SimpleAsyncTaskExecutor();
        executor.setThreadNamePrefix("WebAsyncTask2-");

        WebAsyncTask<Map<String, String>> webAsyncTask = new WebAsyncTask<>(2 * 1000L, executor, callable);

        webAsyncTask.onCompletion(new Runnable() {
            @Override
            public void run() {
                System.out.println("Completion");
            }
        });

        webAsyncTask.onError(new Callable<Map<String, String>>() {
            @Override
            public Map<String, String> call() throws Exception {
                System.out.println("Error");
                return new HashMap<>();
            }
        });

        webAsyncTask.onTimeout(new Callable<Map<String, String>>() {
            @Override
            public Map<String, String> call() throws Exception {
                System.out.println("Timeout");
                Map<String, String> timeOutResutl = new HashMap<>();
                timeOutResutl.put("timeout", "result");
                return timeOutResutl;
            }
        });

        doTask();

        System.out.println("r2 time consumption: " + ChronoUnit.SECONDS.between(now, Instant.now()) + " seconds");

        return webAsyncTask;
    }


    private Map<String, String> buildResult() {
        System.out.println("building result");
        Map<String, String> result = new HashMap<>();
        try {
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
        for (int i = 0; i < 1 * 1000; i++) {
            result.put(i + "-key", i + "value");
        }
        return result;
    }

    private void doTask() {
        System.out.println("do some tasks");
        try {
            Thread.sleep(3 * 1000);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

Пример в коде демонстрирует тайм-аутonTimeoutболее поздний механизм обработки.

WebAsyncTask-异步调用

Суммировать

В этой статье объясняетсяWebAsyncTaskМеханизм реализации,WebAsyncTaskРеализован асинхронный вызов веб-запросов. Основная цель — освободить контейнерные потоки и повысить пропускную способность сервера. Это очень практично в сценариях с большим трафиком и высокой степенью параллелизма (таких как паническая покупка и т. д.).


Ссылки по теме

связанные ресурсы

Пример исходного кода Github этой статьи

Справочная статья

SpringBoot WebAsyncTask

Асинхронный вызов [WebAsyncTask]


Wechat-westcall