JavaRush /Курси /Модуль 5. Spring /Лекція 290: Підписки (Subscriptions) у GraphQL

Лекція 290: Підписки (Subscriptions) у GraphQL

Модуль 5. Spring
Рівень 15 , Лекція 9
Відкрита

Підписки в GraphQL (від англ. Subscriptions) — це механізм отримання даних від сервера в реальному часі, без потреби повторно відправляти запити. Якщо зовсім просто, підписки дозволяють користувачам "підключитися" до певних подій або змін даних, щоб отримувати повідомлення про це одразу після їх виникнення.

Підписки роблять ваш додаток більш динамічним і інтерактивним. Уявіть собі:

  • Чат-застосунок, який оновлює повідомлення в реальному часі, без потреби оновлювати сторінку.
  • Онлайн-аукціон, де користувачі миттєво бачать нові ставки.
  • Спортивний стрім, який у прямому ефірі оновлює рахунок матчу.

Підписки замінюють "громіздкі" механізми типу періодичної відправки запитів (polling) або довготривалих HTTP-з'єднань.


Як влаштовані підписки в GraphQL?

Підписки використовують WebSocket для встановлення постійного з'єднання між клієнтом і сервером. Коли клієнт підписується на певну подію, сервер підтримує відкритий канал і відправляє оновлення по мірі їх появи. Це як підписка на розсилку новин — ви один раз підписуєтесь, а далі отримуєте повідомлення.

Приклад підписки на нове повідомлення в чаті на мові GraphQL:


subscription OnNewMessage {
  newMessage(chatId: "123") {
    id
    content
    sender {
      name
    }
    timestamp
  }
}

Компоненти підписок

  1. Схема GraphQL: задає типи даних, які повертатимуться по підписці.
  2. Event Publisher: механізм для генерації подій на сервері (Spring Boot використовує Project Reactor для реактивних потоків).
  3. WebSocket-транспорт: забезпечує двосторонній зв'язок клієнта і сервера.

Реалізація підписок у Spring GraphQL

Давайте разом налаштуємо підписки для вашого Spring Boot застосунку з GraphQL.

1. Налаштування залежностей

Для роботи з підписками треба додати залежності в pom.xml. Не забудьте підключити GraphQL WebSocket-транспорт!


<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-spring-boot-starter</artifactId>
    <version>11.1.0</version>
</dependency>
<dependency>
    <groupId>com.graphql-java-kickstart</groupId>
    <artifactId>graphql-java-tools</artifactId>
    <version>11.1.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

Після додавання залежностей не забудьте виконати магічну команду mvn clean install, щоб Maven підтягнув усе необхідне.

2. Створення схеми підписки

Створимо файл schema.graphqls і опишемо нашу підписку.


type Subscription {
  messageAdded: Message
}

type Message {
  id: ID
  text: String
  sender: String
  timestamp: String
}

Тут ми створили тип Subscription з однією подією — messageAdded, яка повертає об'єкт типу Message.

3. Створення Event Publisher (генератор подій)

Тепер реалізуємо механізм публікації подій. Використаємо Spring Flux та реактивні потоки з Project Reactor.

Додамо клас MessagePublisher:


import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;

import java.util.ArrayList;
import java.util.List;

@Component
public class MessagePublisher {

    private final List<String> messages = new ArrayList<>();
    private FluxSink<String> sink;

    public Flux<String> getPublisher() {
        return Flux.create(emitter -> this.sink = emitter);
    }

    public void addMessage(String message) {
        messages.add(message);
        if (sink != null) {
            sink.next(message); // Публікуємо нове повідомлення
        }
    }
}

4. Реалізація підписки в GraphQL

Тепер створимо підписку в Spring GraphQL. Додамо клас MessageSubscription.


import org.springframework.graphql.data.method.annotation.SubscriptionMapping;
import org.springframework.stereotype.Controller;
import reactor.core.publisher.Flux;

@Controller
public class MessageSubscription {

    private final MessagePublisher messagePublisher;

    public MessageSubscription(MessagePublisher messagePublisher) {
        this.messagePublisher = messagePublisher;
    }

    @SubscriptionMapping
    public Flux<String> messageAdded() {
        return messagePublisher.getPublisher();
    }
}

Метод messageAdded підписується на потік подій, і щойно в MessagePublisher буде додано нове повідомлення, воно одразу відправиться клієнту.

5. Зміна існуючого сервісу для публікації подій

Оновимо сервіс, що додає повідомлення, щоб він публікував їх через наш MessagePublisher.


import org.springframework.stereotype.Service;

@Service
public class MessageService {

    private final MessagePublisher messagePublisher;

    public MessageService(MessagePublisher messagePublisher) {
        this.messagePublisher = messagePublisher;
    }

    public void addNewMessage(String message) {
        messagePublisher.addMessage(message);
        System.out.println("Повідомлення додано: " + message);
    }
}

Тестування підписок

Для тестування підписок використовуйте GraphQL Playground або Altair. Обидва інструменти підтримують WebSocket-з'єднання.

1. Запустіть застосунок.

2. Відкрийте GraphQL Playground і виконайте підписку:


subscription {
  messageAdded
}

3. В іншому вікні GraphQL надішліть мутацію для додавання повідомлення:


mutation {
  addMessage(text: "Привіт, GraphQL!") {
    id
    text
    timestamp
  }
}

Якщо все налаштовано правильно, ви миттєво побачите в вікні підписки нове повідомлення. Момент магії!


Побудова реактивних застосунків із підписками

Підписки ідеально інтегруються з реактивним підходом. Використовуючи WebFlux замість класичних потоків, ви можете створювати реактивні застосунки, які здатні обробляти тисячі підписників одночасно з мінімальними накладними витратами.

Практичний приклад: побудова системи сповіщень, яка розсилає оновлення всім підписаним користувачам, незалежно від їх кількості.


Типові помилки та поради

Робота з підписками не обходиться без поширених пасток. Наприклад:

  • WebSocket-з'єднання не встановлюється. Перевірте, чи увімкнено WebSocket у застосунку та чи правильно налаштований транспорт GraphQL.
  • Події приходять із затримкою. Це може бути через відсутність реальної асинхронної обробки подій.
  • Зникнення повідомлень. Якщо клієнт відключається, то поточна реалізація MessagePublisher втрачає дані. Для критичних систем варто додати буфер або використовувати брокер повідомлень (наприклад, Kafka).

Опановування механізмом підписок у GraphQL відкриває шлях до створення високоінтерактивних застосунків. Успіхів у розробці!

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ