Перейти к основному содержанию

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);  }}
graph LR AsyncHttpServer --> Eventloop
  • Согласно этому графику, Service Graph запускает Eventloop первым. После этого запускается зависимый AsyncHttpServer .
  • Когда приложение остановится, службы будут остановлены в обратном направлении: AsyncHttpServer первым и Eventloop следующим.

Примеры

note

Чтобы запустить примеры, необходимо клонировать ActiveJ с GitHub

git clone https://github.com/activej/activej

И импортируйте его как проект Maven. Посмотрите тег v5.4.3. Перед запуском примеров выполните сборку проекта. Эти примеры расположены по адресу 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. В результате мы имеем следующий граф услуг:

graph LR id1(EmailService) --> id2(AuthService) id2 --> id3(DBService) id1 --> id3 id2 --> Executor id2 --> Eventloop

А 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);  }}

Полный текст примера смотрите на GitHub.