Service Graph
Обзор
ServiceGraph - это мощный инструмент для управления запуском и остановкой сервисов. При запуске он создает граф всех необходимых служб на основе предоставленных зависимостей. Этот график используется для одновременного запуска и остановки служб, что приводит к ускорению времени запуска без вмешательства служб друг в друга.
Характеристики
- Предназначен для использования в сочетании с ActiveJ Inject и Launcher как средство запуска/остановки сервисов приложения в соответствии с их графом зависимостей.
- Он запускает службы, следуя многопоточному алгоритму обхода графа: сначала службы листьев и так далее.
- Он останавливает обслуживание в обратном направлении
- Граф зависимостей сервисов автоматически строится на основе графа зависимостей ActiveJ Inject , но может быть настроен зависимостями, указанными пользователем.
- Поддерживает множество стандартных сервисов, таких как ThreadPool, Closeables, DataSource , а также специфические для Active сервисы, такие как eventloops, async-серверы и async-сервисы.
- Может быть настроен на поддержку других услуг с помощью адаптеров, предоставляемых пользователем
Чтобы получить базовое представление о роли ServiceGraph, давайте рассмотрим очень простой пример HTTP-сервера:
public final class HttpHelloWorldExample extends HttpServerLauncher { @Provides AsyncServlet servlet() { return request -> HttpResponse.ok200().withPlainText("Hello World"); }
public static void main(String[] args) throws Exception { Launcher launcher = new HttpHelloWorldExample(); launcher.launch(args); }}
- Это приложение расширяет предопределенные
HttpServerLauncher
который характеризуетсяServiceGraphModule
HttpServerLauncher
использует две службы:AsyncHttpServer
иEventloop
.
- Согласно этому графику, Service Graph запускает
Eventloop
первым. После этого запускается зависимыйAsyncHttpServer
. - Когда приложение остановится, службы будут остановлены в обратном направлении:
AsyncHttpServer
первым иEventloop
следующим.
Примеры
note
Чтобы запустить примеры, необходимо клонировать ActiveJ с GitHub
git clone https://github.com/activej/activej
И импортируйте его как проект Maven. Посмотрите тег v5.5. Перед запуском примеров выполните сборку проекта. Эти примеры расположены по адресу activej/examples/core/boot.
SimpleServiceExample
В этом примере мы создаем приложение, которое расширяет Launcher и имеет простую пользовательскую службу, которая в основном только запускается и останавливается:
public class SimpleServiceExample extends Launcher { public static void main(String[] args) throws Exception { SimpleServiceExample example = new SimpleServiceExample(); example.launch(args); }
@Inject CustomService customService;
@Override protected Module getModule() { return ServiceGraphModule.create(); }
@Inject private static class CustomService implements Service { @Override public CompletableFuture<?> start() { System.out.println("|SERVICE STARTING|"); return CompletableFuture.completedFuture(null); }
@Override public CompletableFuture<?> stop() { System.out.println("|SERVICE STOPPING|"); return CompletableFuture.completedFuture(null); } }
@Override protected void run() { System.out.println("|RUNNING|"); }}
Полный текст примера смотрите на GitHub.
EventloopServiceExample
Service Graph также может запускать и останавливать ваши пользовательские службы:
public class EventloopServiceExample extends Launcher {
@Provides Eventloop eventloop() { return Eventloop.create(); }
@Provides @Eager CustomEventloopService customEventloopService(Eventloop eventloop) { return new CustomEventloopService(eventloop); }
@Override protected Module getModule() { return ServiceGraphModule.create(); }
@Override protected void run() { System.out.println("|RUNNING|"); }
private static final class CustomEventloopService implements EventloopService { private final Eventloop eventloop;
public CustomEventloopService(Eventloop eventloop) { this.eventloop = eventloop; }
@Override public @NotNull Eventloop getEventloop() { return eventloop; }
@Override public @NotNull Promise<?> start() { System.out.println("|CUSTOM EVENTLOOP SERVICE STARTING|"); return Promises.delay(Duration.ofMillis(10)) .whenResult(() -> System.out.println("|CUSTOM EVENTLOOP SERVICE STARTED|")); }
@Override public @NotNull Promise<?> stop() { System.out.println("|CUSTOM EVENTLOOP SERVICE STOPPING|"); return Promises.delay(Duration.ofMillis(10)) .whenResult(() -> System.out.println("|CUSTOM EVENTLOOP SERVICE STOPPED|")); } }
public static void main(String[] args) throws Exception { EventloopServiceExample example = new EventloopServiceExample(); example.launch(args); }}
Полный текст примера смотрите на GitHub.
AdvancedServiceExample
Service Graph может управлять более сложными зависимостями между сервисами. Например, предположим, что у нас есть прототип почтового сервиса . Для его правильной работы необходимы две службы - служба авторизации и служба базы данных. Служба авторизации также требует наличия службы базы данных, наряду с Eventloop и Executor. В результате мы имеем следующий граф услуг:
А ServiceGraphModule
запустит и остановит все эти службы в правильном порядке:
=== STARTING APPLICATION
Started java.util.concurrent.ExecutorStarted io.activej.eventloop.EventloopStarted AdvancedServiceExample$DBService
Started AdvancedServiceExample$AuthService
Started AdvancedServiceExample$EmailService
=== STOPPING APPLICATION
Stopped AdvancedServiceExample$EmailService
Stopped AdvancedServiceExample$AuthService
Stopped java.util.concurrent.ExecutorStopped io.activej.eventloop.EventloopStopped AdvancedServiceExample$DBService
Это приложение выглядит следующим образом:
public class AdvancedServiceExample extends Launcher { @Provides @Eager DBService dbService() { return new DBService(); }
@Provides @Eager EmailService emailService(DBService dbService, AuthService authService) { return new EmailService(dbService, authService); }
@Provides @Eager AuthService authService(Eventloop eventloop, Executor executor, DBService dbService) { return new AuthService(eventloop, executor, dbService); }
@Provides Eventloop eventloop() { return Eventloop.create().withCurrentThread(); }
@Provides Executor executor() { return Executors.newCachedThreadPool(); }
@Override protected Module getModule() { return ServiceGraphModule.create(); }
@SuppressWarnings("FieldCanBeLocal") private static class AuthService implements EventloopService { private final Eventloop eventloop; private final Executor executor; private final DBService dbService;
public AuthService(Eventloop eventloop, Executor executor, DBService dbService) { this.executor = executor; this.eventloop = eventloop; this.dbService = dbService; }
@Override public @NotNull Eventloop getEventloop() { return eventloop; }
@Override public @NotNull Promise<?> start() { System.out.println("AuthService starting"); return Promise.ofBlocking(executor, () -> System.out.println("AuthService started")); }
@Override public @NotNull Promise<?> stop() { return Promise.ofBlocking(executor, () -> System.out.println("AuthService stopped")); } }
private static class DBService implements Service { @Override public CompletableFuture<?> start() { System.out.println("DBService is starting"); return CompletableFuture.runAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } System.out.println("DBService is started"); }); }
@Override public CompletableFuture<?> stop() { System.out.println("DBService is stopping"); return CompletableFuture.runAsync(() -> { try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } System.out.println("DBService is stopped"); }); } }
@SuppressWarnings("FieldCanBeLocal") private static class EmailService implements Service { private final DBService service; private final AuthService authService;
public EmailService(DBService service, AuthService authService) { this.service = service; this.authService = authService; }
@Override public CompletableFuture<?> start() { System.out.println("EmailService is starting"); return CompletableFuture.runAsync(() -> { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); } System.out.println("EmailService is started"); }); }
@Override public CompletableFuture<?> stop() { System.out.println("EmailService is stopping"); return CompletableFuture.runAsync(() -> System.out.println("EmailService is stopped")); } }
@Override protected void run() { }
public static void main(String[] args) throws Exception { AdvancedServiceExample example = new AdvancedServiceExample(); example.launch(args); }}