Actual Spring Boot 2.0 Series (6) — несколько реализаций одномашинных задач синхронизации

Spring Boot Java задняя часть Микросервисы Архитектура сервер Spring gradle

предисловие

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

Статьи из этой серии

  1. Actual Spring Boot 2.0 Series (1) — Создание образов Docker с помощью Gradle
  2. Actual Spring Boot 2.0 Series (2) — глобальная обработка исключений и тестирование
  3. Actual Spring Boot 2.0 Series (3) — Подробное объяснение асинхронных вызовов с использованием @Async
  4. Actual Spring Boot 2.0 Series (4) — Использование WebAsyncTask для обработки асинхронных задач
  5. Actual Spring Boot 2.0 Series (5) — прослушиватель, сервлет, фильтр и перехватчик
  6. Actual Spring Boot 2.0 Series (6) — несколько реализаций одномашинных задач синхронизации

Общее этосистема финансовых услугPush-обратный вызов.Как правило, заказ платежной системы не получает содержание обратного успешного обратного вызова.непрерывный обратный вызов, этот обратный вызов обычнозадача на времяЧто нужно сделать.

И естьгенерация отчета, мы вообщенебольшой трафикКогда эта операция завершена, вы также можете использоватьзадача на времяЧто нужно сделать.

текст

Несколько способов планирования задач

Timer

ЭтоJavaавтономныйjava.util.Timerкласс, который позволяет планироватьjava.util.TimerTaskЗадача. Использование этого метода позволяет вашей программе следовать определенномуЧастотавыполнить, но не вуказанное времябегать. Сейчас вообще используется меньше.

ScheduledExecutorService

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

Spring Task

Spring 3.0принести позжеTask,служба поддержкиМногопоточностьрасписание, которое можно рассматривать каклегкийизQuartz, и используя болееQuartzГораздо проще, но подходит дляодин узелизПланирование задач по расписанию.

Quartz

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

Введение в выражения Cron

Cronвыражение представляет собой строку, начинающуюся с5или6Кусоккосмосразделенный, разделенный6или7Кусокплощадь, каждое поле представляет значение,CronСуществует два следующих формата синтаксиса:

  1. Seconds Minutes Hours DayofMonth Month DayofWeek Year
  2. Seconds Minutes Hours DayofMonth Month DayofWeek

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

поле разрешение разрешенные специальные символы
второй 0-59 , - * /
Минута 0-59 , - * /
Час 0-23 , - * /
Дата 1-31 , - * / L W C
месяц 1-12 или ЯНВАРЬ-ДЕКАБРЬ , - * /
Неделю 1-7 или вс-сб , - * / L C #
год (необязательно) Оставьте пустым, 1970-2099 гг. , - * /

Как показывает приведенное выше выражение:

  • ""персонаж:используется для указания всех значений. Например, «каждую минуту» представлено в поле минут.

  • "-"персонаж:используется для указания диапазона. Например: «10-12» означает «10 часов, 11 часов, 12 часов» в поле часов.

  • ","персонаж:используется для указания дополнительных значений. Например: «ПН,СР,ПТ» означает «понедельник, среда, пятница» в поле недели.

  • "?"персонаж:Используется только в полях даты и недели. Он используется для указания «неявных значений». Это полезно, когда вам нужно указать что-то через одно из этих двух полей. Посмотрите на пример ниже, и вы все поймете.

  • символ "Л":Укажите день (последний день) месяца или недели. Сокращение от «Последний». Но «L» означает разные значения в неделе и месяце, например: «L» в подразделе месяца относится к последнему дню месяца - 31 января, 28 февраля.

    • Он просто представлен как «7» или символ «SAT», если он находится в поле дня недели.
    • Если он стоит после значения в поле недели, это означает «значение последней недели месяца», например, «6L» означает последнюю пятницу месяца.
  • Символ «В»:Может использоваться только в поле месяца, которое указывает ближайшее к указанной дате воскресенье.

  • "#"персонаж:Может использоваться только в поле недели, которое указывает количество недель в месяце.

Каждый элемент может явно указывать значение (например,6), интервал (например,9-12), список (например,9,11,13) или подстановочный знак (например,*)."день месяца"и"день недели"Эти два элементавзаимоисключающий, поэтому это следует сделать, установиввопросительный знак(?), чтобы указать, какое поле вы не хотите устанавливать. В таблице ниже показаны некоторыеcronвыразительныйпримери их значение:

выражение значение
"0 0 12 * * ?" Запуск каждый день в 12:00
"0 15 10 ? * *" Запуск каждый день в 10:15
"0 15 10 * * ?" Запуск каждый день в 10:15
"0 15 10 * * ? *" Запуск каждый день в 10:15
"0 15 10 * * ? 2005" Выстрел в 10:15 каждый день в 2005 году.
"0 * 14 * * ?" Срабатывает каждую минуту с 14:00 до 14:59 каждый день
"0 0/5 14 * * ?" Срабатывает каждые 5 минут с 14:00 до 14:55 каждый день
"0 0/5 14,18 * * ?" Срабатывает каждые 5 минут с 14:00 до 14:55 и каждые 5 минут с 18:00 до 18:55.
"0 0-5 14 * * ?" Срабатывает каждую минуту с 14:00 до 14:05 каждый день
"0 10,44 14 ? 3 WED" Каждую среду марта в 14:10 и 14:44.
"0 15 10 ? * MON-FRI" Запускается в 10:15 с понедельника по пятницу
"0 15 10 15 * ?" Запускается в 10:15 утра 15-го числа каждого месяца
"0 15 10 L * ?" Запускается в 10:15 в последний день месяца
"0 15 10 ? * 6L" Загорается в последнюю пятницу месяца в 10:15.
"0 15 10 ? * 6L 2002-2005" Выстрел в 10:15 в последнюю пятницу каждого месяца с 2002 по 2005 год.
"0 15 10 ? * 6#3" Срабатывает в 10:15 в третью пятницу месяца.
0 6 * * * каждое утро в 6 утра
0 /2 * * каждые два часа
0 23-7/2, 8 * * * Каждые два часа с 23:00 до 8:00, в 8:00
0 11 4 * 1-3 4 числа каждого месяца и каждый понедельник-среда в 11:00
0 4 1 1 * 1 января в 4 утра

Подготовка окружающей среды

Настройка зависимостей градиента

использоватьSpring InitializerСоздаватьgradleпроектspring-boot-scheduler-task-management, добавляйте связанные зависимости при создании. получить начальныйbuild.gradleследующее:

buildscript {
    ext {
        springBootVersion = '2.0.3.RELEASE'
    }
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:${springBootVersion}")
    }
}

apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'org.springframework.boot'
apply plugin: 'io.spring.dependency-management'

group = 'io.ostenant.springboot.sample'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = 1.8

repositories {
    mavenCentral()
}


dependencies {
    compile('org.springframework.boot:spring-boot-starter')
    compile('org.springframework.boot:spring-boot-starter-web')
    testCompile('org.springframework.boot:spring-boot-starter-test')
}

существуетSpring BootКонфигурация начального класса@EnableSchedulingАннотация включенаSpringВстроенная функция обработки времени.

@SpringBootApplication
@EnableScheduling
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Настройка задач таймера

этоAPIВ настоящее время он редко используется в проекте, а пример кода приводится напрямую. Подробное введение см.API.Timerтолько внутринить,Если естьнесколько задачтогда это будетПоследовательное исполнение, такая задачазадерживатьивремя обращенияПроблемы возникнут.

TimerService.java

public class TimerService {
    private static final Logger LOGGER = LoggerFactory.getLogger(TimerService.class);
    private AtomicLong counter = new AtomicLong();

    public void schedule() {
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                long count = counter.incrementAndGet();
                LOGGER.info("Schedule timerTask {} times", count);
            }
        };
        Timer timer = new Timer();
        timer.schedule(timerTask, 1000L, 10 * 1000L;
    }
}

Приведенный выше код определяетTimerTask,существуетTimerTaskНакопитьколичество казней, и пройтиslf4jПечать (со своим временем выполнения). затем пройтиTimerинструмент планированияTimerTaskзадачи, настройкиВремя задержки инициализацииза1s,Интервал выполнения по времениза10s, тестовый код выглядит следующим образом:

public static void main(String[] args) {
    TimerService timerService = new TimerService();
    timerService.schedule();
}

Глядя на результаты теста, вы можете найтиTimerTaskНастроенные задачи каждые10sВыполняется один раз, поток выполнения по умолчаниюTimer-0эта нить.

17:48:18.731 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 1 times
17:48:28.730 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 2 times
17:48:38.736 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 3 times
17:48:48.738 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 4 times
17:48:58.743 [Timer-0] INFO io.ostenant.springboot.sample.timer.TimerService - Schedule timerTask 5 times

Настройка задачи ScheduledExecutorService

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

Создайте тему с количеством4изпул потоков задач, в то же время и совершить его4Временная задача для тестирования отложенных задачПараллельная обработка. воплощать в жизньScheduledExecutorServiceизscheduleWithFixedDelay()метод установки пула потоков задачНачальное время задержки задачиза2секунды, и последний размомент времени исполненияпосле10секунд до выполнения следующей задачи.

public void scheduleWithFixedDelay() {
    ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
    for (int i = 0; i < 4; i++) {
        scheduledExecutor.scheduleWithFixedDelay(() -> {
            try {
                TimeUnit.MILLISECONDS.sleep(10 * 1000L);
            } catch (InterruptedException e) {
                LOGGER.error("Interrupted exception", e);
            }
            long count = counter.incrementAndGet();
            LOGGER.info("Schedule executor {} times with fixed delay", count);
        }, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
    }
    LOGGER.info("Start to schedule");
}

Результаты теста следующие, мы можем обнаружить, что каждый20интервал секунд, будет4Запланированные задачи выполняются одновременно. Потому что, когда пул потоков задач инициализируется, мы одновременно отправляемся в пул потоков4задача, эточетыре задачибудет полностью использовать пул потоков4потоки для выполнения задач.

20Как появились секунды? первая из каждой задачивременной интервалУстановить как10второй. Во-вторых, потому чтоwithFixedDelayстратегия, то есть то, что выполняет текущая задачаВремя окончания, в качестве следующей задачи задержкизапустить узел синхронизации, и каждая задача приостанавливается во время выполнения10секунд, общее время равно20секунды.

19:42:02.444 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:42:14.449 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times with fixed delay
19:42:14.449 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times with fixed delay
19:42:14.449 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times with fixed delay
19:42:14.449 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times with fixed delay
19:42:34.458 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times with fixed delay
19:42:34.458 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times with fixed delay
19:42:34.458 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times with fixed delay
19:42:34.458 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times with fixed delay

Создайте тему с количеством4изпул потоков задач, в то же время и совершить его4Временная задача для тестирования отложенных задачПараллельная обработка. Выполняйте каждую задачу отдельноScheduledExecutorServiceизscheduleAtFixedRate()метод установки пула потоков задачНачальное время задержки задачиза2секунды, и последний развремя начала выполненияпосле10секунд до выполнения следующей задачи.

public void scheduleAtFixedRate() {
    ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(4);
    for (int i = 0; i < 4; i++) {
        scheduledExecutor.scheduleAtFixedRate(() -> {
            long count = counter.incrementAndGet();
            LOGGER.info("Schedule executor {} times at fixed rate", count);
        }, 2000L, 10 * 1000L, TimeUnit.MILLISECONDS);
    }
    LOGGER.info("Start to schedule");
}

Результаты теста следующие, мы можем обнаружить, что каждый10интервал секунд, будет4Каждая синхронизированная задача выполняется в одно и то же время, поскольку при инициализации пула потоков задач мы отправляемся в пул потоков одновременно.4задача, эточетыре задачибудет полностью использовать пул потоков4потоки для выполнения задач.

19:31:46.837 [main] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Start to schedule
19:31:48.840 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 1 times at fixed rate
19:31:48.840 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 3 times at fixed rate
19:31:48.840 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 2 times at fixed rate
19:31:48.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 4 times at fixed rate
19:31:58.839 [pool-1-thread-2] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 6 times at fixed rate
19:31:58.840 [pool-1-thread-4] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 8 times at fixed rate
19:31:58.839 [pool-1-thread-3] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 7 times at fixed rate
19:31:58.839 [pool-1-thread-1] INFO io.ostenant.springboot.sample.executor.ScheduledExecutorsService - Schedule executor 5 times at fixed rate

Настройка задач Spring Task

Springпри условии@ScheduledАннотация для достижениязадача на время,@Scheduledпараметры приемлемыдваНастройки синхронизации, один обычно используетсяВыражение зеленого времени cron = "*/10 * * * * *", другойfixedRate = 10 * 1000L, оба представляют каждый10Выполнять целевую задачу раз в секунду.

Описание параметра:

  • @Scheduled(fixedRate = 10 * 1000L): последний развремя начала выполненияпосле10Выполнить снова в секундах.
@Scheduled(fixedRate = 10 * 1000L)
public void scheduleAtFixedRate() throws Exception {
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times at fixed rate", count);
}
  • @Scheduled(fixedDelay = 10 * 1000L): последний размомент времени выполненияпосле10Выполнить снова в секундах.
@Scheduled(fixedDelay = 10 * 1000L)
public void scheduleWithFixedDelay() throws Exception {
    try {
        TimeUnit.MILLISECONDS.sleep(10 * 1000L);
    } catch (InterruptedException e) {
        LOGGER.error("Interrupted exception", e);
    }
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with fixed delay", count);
}
  • @Scheduled(initialDelay = 2000L, fixedRate = 10 * 1000L): первая задержка2Выполните в секундах, затем нажмитеfixedRateправила за10Выполнять раз в секунду.
@Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
    try {
        TimeUnit.MILLISECONDS.sleep(10 * 1000L);
    } catch (InterruptedException e) {
        LOGGER.error("Interrupted exception", e);
    }
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with fixed delay", count);
}
  • @Scheduled(cron = "0/10 * * * * *"): согласноcronопределение выражения, любое другое10Выполнять раз в секунду.
@Scheduled(cron = "0/10 * * * * *")
public void scheduleWithCronExpression() throws Exception {
    long count = counter.incrementAndGet();
    LOGGER.info("Schedule executor {} times with ", count);
}

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

SpringTaskService.java

@Component
public class SpringTaskService {
    private static final Logger LOGGER = LoggerFactory.getLogger(SpringTaskService.class);
    private AtomicLong counter = new AtomicLong();

    @Scheduled(fixedDelay = 10 * 1000L)
    public void scheduleWithFixedDelay() throws Exception {
        try {
            TimeUnit.MILLISECONDS.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            LOGGER.error("Interrupted exception", e);
        }
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with fixed delay", count);
    }

    @Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L)
    public void scheduleWithinitialDelayAndFixedDelay() throws Exception {
        try {
            TimeUnit.MILLISECONDS.sleep(10 * 1000L);
        } catch (InterruptedException e) {
            LOGGER.error("Interrupted exception", e);
        }
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with fixed delay", count);
    }

    @Scheduled(fixedRate = 10 * 1000L)
    public void scheduleAtFixedRate() throws Exception {
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times at fixed rate", count);
    }

    @Scheduled(cron = "0/10 * * * * *")
    public void scheduleWithCronExpression() throws Exception {
        long count = counter.incrementAndGet();
        LOGGER.info("Schedule executor {} times with ", count);
    }
}

Просмотр журнала, задача каждые20Выполняется один раз с интервалом в несколько секунд. Каждый раз, когда запланированная задача находится в последнеммомент времени выполненияпосле10Секунды на повторное выполнение, заданные в задачевремя спатьза10второй. Здесь проверяется только @Scheduled(initialDelay = 2000L, fixedDelay = 10 * 1000L).

2018-06-25 18:00:53.051  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 1 times with fixed delay
2018-06-25 18:01:13.056  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 2 times with fixed delay
2018-06-25 18:01:33.061  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 3 times with fixed delay
2018-06-25 18:01:53.071  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 4 times with fixed delay
2018-06-25 18:02:13.079  INFO 5190 --- [pool-1-thread-1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 5 times with fixed delay

Настройка пула потоков задач

Вышеупомянутая конфигурация основана наодин потокпланирование задач, как ввестиМногопоточностьулучшатьЗадержка задачиизПараллельная обработкаспособность?

Spring BootпредоставилSchedulingConfigurerНастройте интерфейс. мы проходимScheduleConfigРеализация конфигурационного файлаScheduleConfigurationинтерфейс и переопределитьconfigureTasks()метод, чтобыScheduledTaskRegistrarЗарегистрироватьThreadPoolTaskSchedulerобъект потока задач.

@Configuration
public class ScheduleConfiguration implements SchedulingConfigurer {
    private static final Logger LOGGER = LoggerFactory.getLogger(ScheduleConfiguration.class);

    @Override
    public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
        taskRegistrar.setTaskScheduler(taskScheduler());
    }

    @Bean
    public ThreadPoolTaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(4);
        taskScheduler.setWaitForTasksToCompleteOnShutdown(true);
        taskScheduler.setThreadNamePrefix("schedule");
        taskScheduler.setRemoveOnCancelPolicy(true);
        taskScheduler.setErrorHandler(t -> LOGGER.error("Error occurs", t));
        return taskScheduler;
    }
}

запускатьSpring Bootцитата вышеSpringTaskServiceнастроен4Каждая запланированная задача вступит в силу в одно и то же время.

2018-06-20 20:37:50.746  INFO 8142 --- [      schedule1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 1 times at fixed rate
2018-06-20 20:38:00.001  INFO 8142 --- [      schedule3] i.o.s.sample.spring.SpringTaskService    : Schedule executor 2 times with 
2018-06-20 20:38:00.751  INFO 8142 --- [      schedule1] i.o.s.sample.spring.SpringTaskService    : Schedule executor 3 times at fixed rate
2018-06-20 20:38:02.748  INFO 8142 --- [      schedule2] i.o.s.sample.spring.SpringTaskService    : Schedule executor 4 times with fixed delay
2018-06-20 20:38:10.005  INFO 8142 --- [      schedule4] i.o.s.sample.spring.SpringTaskService    : Schedule executor 5 times with 
2018-06-20 20:38:10.747  INFO 8142 --- [      schedule3] i.o.s.sample.spring.SpringTaskService    : Schedule executor 6 times at fixed rate
2018-06-20 20:38:20.002  INFO 8142 --- [      schedule2] i.o.s.sample.spring.SpringTaskService    : Schedule executor 7 times with 
2018-06-20 20:38:20.747  INFO 8142 --- [      schedule4] i.o.s.sample.spring.SpringTaskService    : Schedule executor 8 times at fixed rate

журнал просмотра,префикс имени темызаschedule, его можно найтиSpring Taskбудет@ScheduledАннотированный настроен4задачи, распределенные по нашим настроеннымThreadPoolTaskSchedulerсередина4Потоки выполняются одновременно.

резюме

В этом документе представлены планирование и реализация задач синхронизации на основе одного узла, включаяJDKоригинальныйTimerиScheduledExecutorService,а такжеSpring 3.0на основе аннотацийSpring Taskпланирование задач. Кроме того, акцент делается наФиксированная задержка,фиксированная частотаиcron выражениеразница, иScheduledExecutorServiceиSpring SchedulerизПараллельная обработка пула потоковпрохождение теста.


Добро пожаловать в технический публичный аккаунт: Zero One Technology Stack

零壹技术栈

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