Рубрики
Без рубрики

RPC организации GraphQL на Java

Вся эта статья доступна в моем репозитории jufab /… Помеченный java, graphql, grpc, helidon.

Вся эта статья доступна в моем репозитории jufab/graphql-grpc-хелидон Лабораторная работа по GraphQL и gRPC с Helidon

В этой статье представлены GraphQL и gRPC, реализованные с помощью Java.

В том же случае использования я собираюсь показать вам, как раскрывать и реализовывать эти протоколы.

Я использую простой вариант использования, но не только вариант использования одной таблицы… Хорошо, просто 2 таблицы с отношением один к одному.

Чтобы сделать эту статью, я использую Хелидон .

Почему Шелдон? Почему бы и нет! … серьезно, потому что эта структура предлагает возможность реализовать эти протоколы ( и, конечно же, для меня открыть эту структуру 😉 )

Содержание

  • О Хелидоне
  • Пример использования
    • Определение
    • Клиент базы данных Helidon
  • Проект инициализации
  • GraphQL
    • Схема
    • Знаток
    • Реализация
    • Тест
  • gRPC
    • Буферы протокола
    • Знаток
    • Реализация
    • Тест
  • Вывод

О Хелидоне

Helidon – это java-фреймворк с 2 версиями:

  • Хелидон СЕ: Реактивная микроструктура.
  • Helidon MP: Реализация микропрофиля.

В этих 2 версиях Helidon предлагает множество возможностей для реализации RPC организации GraphQL (это веская причина использовать его для этой статьи).

Существует множество других возможностей, таких как реактивные потоки, реактивные сообщения или предопределенные проверки работоспособности или показатели…

И предоставляет возможность создать его с помощью docker или GraalVM и развернуть с помощью Kubernetes.

Так что попробуйте это для удовольствия, а почему бы и нет для вашего проекта 😉

Пример использования

Определение

Просто простой пример использования: человек с адресом => Адрес указан в отдельной таблице для этой статьи.

Итак, у нас есть:

Клиент базы данных Helidon

Для базы данных и моего варианта использования я использовал базу данных H2 в памяти и Helidon DB-клиент .

Клиент Helidon DB – это реактивный API для доступа к базе данных. Вы можете получить доступ с помощью драйвера JDBC (H2, Oracle, MySQL …) или напрямую с помощью MongoDB.

Для этой статьи я использовал JDBC. Поэтому я добавил эти зависимости:

     
         io.helidon.dbclient 
         helidon-dbclient
     
     
         io.helidon.dbclient 
         helidon-dbclient-jdbc
     

Вся реализация базы данных находится здесь, в этом модуле maven

Он содержит 2 важных файла в ресурсы :

  • db.yaml : содержит конфигурацию для базы данных h2
  • заявления.yaml : содержит все инструкции, такие как создание таблиц или последовательностей…

Проект инициализации

Используйте архетип из документации

mvn -U archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion=2.2.1 \
    -DgroupId=fr.jufab \
    -DartifactId=graphql-helidon \
    -Dpackage=fr.jufab.graphql

и

mvn -U archetype:generate -DinteractiveMode=false \
    -DarchetypeGroupId=io.helidon.archetypes \
    -DarchetypeArtifactId=helidon-quickstart-se \
    -DarchetypeVersion=2.2.1 \
    -DgroupId=fr.jufab \
    -DartifactId=grpc-helidon \
    -Dpackage=fr.jufab.grpc

Я преобразовал проекты в 3 модуля maven и удалил всю виртуальную машину Graal или конструктор докеров для уменьшенной базы данных в модуле GraphQL или gRPC.

Или вы можете использовать helidon cli для управления своим проектом. Или вы можете использовать helidon cli для управления своим проектом.

GraphQL

Итак, теперь давайте поговорим о GraphQL.

GraphQL – это язык запросов, работающий на одной конечной точке. он разговаривает на языке JSON. Он определяет схему, описывает допустимые атрибуты, операции и т. Д./| А GraphQL – это спецификация

Схема

Чтобы определить GraphQL, нам нужно использовать схему с этой информацией:

  • Объект: состав ответа объекта
  • Запрос: запрос для запроса объектов или объектов массива.
  • Мутация: для сохранения/изменения объектов.
  • Подписка: для создания двунаправленного канала связи с использованием WebSocket

В случае использования схема, доступная здесь , является:

# Objects
type Person {
    id: ID!
    firstname: String!
    lastname: String!
    age: Int
    gender: Gender!
    address: Address
}

enum Gender {
    WOMAN,
    MAN
}

type Address {
    id: ID!
    street: String!
    zipCode: String!
    city: String!
}
# Query
type Query {
    personById(id: ID!): Person
    personsByFirstName(firstname: String!): [Person]
    persons:[Person]
}
# Mutation
type Mutation {
    createPersonWithAddress(firstname: String!, lastname: String!, age: Int, gender: Gender, street: String!, zipCode: String!, city: String!):Person
    createPerson(firstname: String!, lastname: String!, age: Int, gender: Gender, idAddress: ID!):Person
    createAddress(street: String!, zipCode: String!, city: String!): Address
}

Знаток

В helidon SE есть возможность использовать GraphQL с библиотекой helidon GraphQL.

Более подробная информация об этой интеграции содержится в документации

На данный момент эта функция является экспериментальной … но она работает для этого проекта:)

Чтобы вставить GraphQL в проект, я добавил зависимость в pom.xml файл


  io.helidon.graphql
  helidon-graphql-server

Эта зависимость использует GraphQL java версии 15.0 .

Реализация

Сервер

GraphQL должен быть зарегистрирован на веб-сервере.

В Основном классе он был зарегистрирован здесь:

WebServer server = WebServer.builder()
        .routing(Routing.builder()
            .register(health)                   // Health at "/health"
            .register(MetricsSupport.create())  // Metrics at "/metrics"
            .register(GraphQlSupport.create(buildSchema(dbClient)))
            .build())
        .config(config.get("server"))
        .build();

Точно такая же линия:

register(GraphQlSupport.create(buildSchema(dbClient)))

Класс поддержки GraphQL использует схему GraphQL. Метод buildSchema() создает настроенную схему GraphQL.

private static GraphQLSchema buildSchema(DbClient dbClient) {
    SchemaParser schemaParser = new SchemaParser();
    Resource schemaResource = Resource.create(PERSON_GRAPHQLS);
    TypeDefinitionRegistry typeDefinitionRegistry =
        schemaParser.parse(schemaResource.string(StandardCharsets.UTF_8));
    SchemaGenerator schemaGenerator = new SchemaGenerator();
    return schemaGenerator.makeExecutableSchema(typeDefinitionRegistry,
        buildRuntimeWiring(dbClient));
  }

это требует:

  • схема: person.graphqls
  • анализатор: для создания экземпляра реестра определения типа
  • Подключение во время выполнения: для подключения данных к устройству извлечения данных

Метод построения проводки во время выполнения соединяет объект (Лицо, адрес) с устройством сбора данных.

private static RuntimeWiring buildRuntimeWiring(DbClient dbClient) {
  AddressRepository addressRepository = new AddressRepository(dbClient);
  AddressDataFetcher addressDataFetcher = new AddressDataFetcher(addressRepository);
  PersonRepository personRepository = new PersonRepository(dbClient);
  PersonDataFetcher personDataFetcher = new PersonDataFetcher(personRepository);
  return RuntimeWiring.newRuntimeWiring()
    .type(TypeRuntimeWiring.newTypeWiring("Query")
      .dataFetcher("persons", personDataFetcher.getPersons()))
    .type(TypeRuntimeWiring.newTypeWiring("Query")
      .dataFetcher("personById", personDataFetcher.getPersonById()))
    .type(TypeRuntimeWiring.newTypeWiring("Query")
     .dataFetcher("personsByFirstName", personDataFetcher.getPersonsByFirstName()))
    .type(TypeRuntimeWiring.newTypeWiring("Person")
     .dataFetcher("address", addressDataFetcher.getAddressById()))
    .type(TypeRuntimeWiring.newTypeWiring("Mutation")
     .dataFetcher("createPersonWithAddress", personDataFetcher.createPersonWithAddress()))
    .type(TypeRuntimeWiring.newTypeWiring("Mutation").dataFetcher("createAddress",
      addressDataFetcher.createAddress()))
    .build();
}

В этом методе есть все запросы или мутации, и все они связаны с устройством извлечения данных

Устройство для сбора данных

Все объекты будут связаны с объектом Средство извлечения данных . DataFetcher отвечает за загрузку объектов для запроса или сохранение объектов для мутации.

Как это работает:

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

вот так ( Выборка данных о человеке ):

// To get all Persons... no arguments
public DataFetcher> getPersons() {
    return environment -> personRepository.getPersons().collectList().get();
}
//To get all by FirstName
public DataFetcher> getPersonsByFirstName() {
    return environment -> personRepository.getPersonsByFirstName(environment.getArgument("firstname")).collectList().get();
}
// Or to create a person
public DataFetcher createPersonWithAddress() {
    return environment -> this.personRepository.createPerson(
        new Person(environment.getArgument("firstname"), environment.getArgument("lastname"),
          environment.getArgument("age"),
          new Address(environment.getArgument("street"), environment.getArgument("zipCode"),
            environment.getArgument("city")),
          Gender.valueOf(environment.getArgument("gender")))).get();
}

Дополнительная информация о получении данных => Извлечение данных

Тест

Для тестирования ресурсов я использую бессонница .

Вы можете получить доступ к запросам GraphQL таким образом,

просмотр информации о схеме,

и сделайте несколько просьб.

И это все для GraphQL…

gRPC

gRPC помогает вам создавать веб-сервисы. Это кросс-язык но этот проект – проект Java, так что …:)

Для определения и описания сервиса gRPC использует простой файл определения и формат буферов протокола.

Для получения дополнительной информации: Для получения дополнительной информации:

Буферы протокола

gRPC использует буферы протокола: gRPC использует буферы протокола:

Буферы протокола – это язык для сериализации структурированных данных.

Вы можете определить некоторые параметры для каждого языка: “пакет” для java или “objc_class_prefix” для Objective-C для префикса сгенерированных классов.

Для этой лабораторной работы я использовал синтаксическую версию “proto3”.

Файл Protobuf содержит:

  • сообщение: Данные объекта
  • перечисление: определить перечисление
  • сервис: Сервис для запроса, изменения или сохранения данных

Что касается вариантов использования, прото-файл доступен здесь имеет такое определение

syntax = "proto3";
option java_package = "fr.jufab.grpc.proto";
option java_multiple_files = true;
option java_generic_services = true;
option java_outer_classname = "Helidon";

enum Gender {
  WOMAN = 0;
  MAN = 1;
}

message Person {
  int32 id = 1;
  string firstname = 2;
  string lastname = 3;
  int32 age = 4;
  Gender gender = 5;
  Address address = 6;
}

message Address {
  int32 id = 1;
  string street = 2;
  string zipCode = 3;
  string city = 4;
}

message Persons {
  repeated Person persons=1;
}

message QueryPerson {
  int32 id=1;
  string firstname=2;
}

message PersonWithAddressToSave {
  string firstname = 1;
  string lastname = 2;
  int32 age = 3;
  Gender gender = 4;
  string street = 5;
  string zipCode = 6;
  string city = 7;
}

message PersonToSave{
  string firstname = 1;
  string lastname = 2;
  int32 age = 3;
  Gender gender = 4;
  int32 idAddress = 5;
}

message AddressToSave{
  string street = 1;
  string zipCode = 2;
  string city = 3;
}

message QueryAddress {
  int32 id = 1;
}

service PersonService {
  rpc persons(QueryPerson) returns (Persons);
  rpc personById(QueryPerson) returns (Person);
  rpc personsByFirstName(QueryPerson) returns (Persons);

  rpc createPersonWithAddress(PersonWithAddressToSave) returns (Person);
  rpc createPerson(PersonToSave) returns (Person);
}

service AddressService {
  rpc createAddress(AddressToSave) returns (Address);
  rpc addressById(QueryAddress) returns (Address);
}

Знаток

Helidon SE предлагает зависимость maven для развертывания сервера gRPC.

Более подробная информация об этой интеграции содержится в документации

Как и GraphQL, функция gRPC на данный момент является экспериментальной… но это работает для этого проекта:)

Чтобы вставить gRPC в проект, я добавил зависимость в pom.xml


    io.helidon.grpc
    helidon-grpc-server

Но для использования gRPC и особенно protobuf этого недостаточно…

Вы должны сгенерировать данные из файла protobuf с помощью специального плагина maven. Этот плагин использует компилятор буфера протокола.

В этом проекте для создания классов есть это определение maven в pom.xml .


    org.xolstice.maven.plugins
    protobuf-maven-plugin
    0.6.1
    
        com.google.protobuf:protoc:3.3.0:exe:${os.detected.classifier}
        grpc-java
        io.grpc:protoc-gen-grpc-java:1.4.0:exe:${os.detected.classifier}
    
    
        
            
                compile
                compile-custom
            
        
    

более подробная информация об этом плагине здесь более подробная информация об этом плагине здесь

Все классы генерируются в разделе “сгенерированные источники”.

Реализация

Сервер

Для запуска сервера gRPC Helidon предлагает специальный сервер для этой цели.

Вам нужно запустить Rpc-сервер следующим образом

GrpcServer grpcServer = GrpcServer
        .create(GrpcServerConfiguration.create(config.get("grpcserver")),GrpcRouting.builder()
            .register(buildPersonServiceGrpc(dbClient)) //See after for this
            .register(buildAddressServiceGrpc(dbClient)) //See after for this
            .build())
        .start()
        .toCompletableFuture()
        .get(10, TimeUnit.SECONDS);

По умолчанию порт установлен на 1408, но вы можете переопределить его в своем yaml следующим образом:

grpcserver:
  port: 3333

Вы можете увидеть пример здесь

Услуга

Вы можете видеть на сервере, что сервис зарегистрирован “Personservicegrpc” или “AddressServiceGrpc”.

Эти 2 услуги доступны здесь и здесь

Эти сервисы реализуют сервисы, предоставляемые классами, сгенерированными Protobuf. Итак, в “Службе Person Grpc” этот класс реализует “Базу PersonServiceGrpc.PersonServiceImpl”.

По умолчанию вы не можете видеть этот класс “PersonServiceGrpc”, потому что вам нужно создать класс с файлом protobuf. Почему моя посылка находится в “fr.jufab.grpc.proto”? Потому что я использовал опцию в protobuf:

option java_package = "fr.jufab.grpc.proto";

Например, PersonServiceGrpc предоставляет мне все методы, определенные в protobuf.

Помните службу в протобуфе:

service PersonService {
  rpc persons(QueryPerson) returns (Persons);
  rpc personById(QueryPerson) returns (Person);
  rpc personsByFirstName(QueryPerson) returns (Persons);

  rpc createPersonWithAddress(PersonWithAddressToSave) returns (Person);
  rpc createPerson(PersonToSave) returns (Person);
}

и PersonServiceGrpc дает вам:

public static abstract class PersonServiceImplBase implements io.grpc.BindableService {

  /**
   */
  public void persons(fr.jufab.grpc.proto.QueryPerson request,
          io.grpc.stub.StreamObserver responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_PERSONS, responseObserver);
  }

  /**
   */
  public void personById(fr.jufab.grpc.proto.QueryPerson request,
          io.grpc.stub.StreamObserver responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_PERSON_BY_ID, responseObserver);
  }

  /**
   */
  public void personsByFirstName(fr.jufab.grpc.proto.QueryPerson request,
          io.grpc.stub.StreamObserver responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_PERSONS_BY_FIRST_NAME, responseObserver);
  }

  /**
   */
  public void createPersonWithAddress(fr.jufab.grpc.proto.PersonWithAddressToSave request,
          io.grpc.stub.StreamObserver responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_CREATE_PERSON_WITH_ADDRESS, responseObserver);
  }

  /**
   */
  public void createPerson(fr.jufab.grpc.proto.PersonToSave request,
          io.grpc.stub.StreamObserver responseObserver) {
    asyncUnimplementedUnaryCall(METHOD_CREATE_PERSON, responseObserver);
  }
}

Теперь просто реализуйте эти методы с помощью доступа к вашему репозиторию или БД.

Для метода “лица” в PersonGrpcService я реализовал его следующим образом:

@Override public void persons(QueryPerson request,
      StreamObserver responseObserver) {
    try {
      complete(responseObserver, buildPersonsGrpc(personRepository.getPersons()
          .collectList()
          .get()));
    } catch (InterruptedException e) {
      LOGGER.log(Level.SEVERE, "Error", e);
    } catch (ExecutionException e) {
      LOGGER.log(Level.SEVERE, "Error", e);
    }
  }

Вы можете увидеть всю реализацию в PersonGrpcService и

Тест

Для тестирования сервисов gRPC я использую бессонница тоже.

Вы можете получить доступ к сервисам gRPC таким образом,

загрузить файл protobuf,

выберите свой сервис для тестирования после добавления URL-адреса,

и используй это.

Вот и все для gRPC.

Вывод

GraphQL и gRPC предлагают один и тот же подход: использовать схему и генерировать ресурсы на основе описания.

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

gRPC обменивается данными по своему протоколу (HTTP/2), и вам необходимо создать свой сервис из protobuf.

На мой взгляд:

  • GraphQL может быть лучше для внешней экспозиции, например, для мобильных устройств, открытого API, конечно, в другом случае.
  • gRPC может быть лучше для внутренней связи, например, в облаке, с определенным клиентом, в кластере K8s, в информационной системе…

Конечно, это может зависеть от вашего использования. Возможно, эта статья поможет вам определиться с выбором.

Оригинал: “https://dev.to/jufab/graphql-or-grpc-in-java-3lah”