Главная » Хабрахабр » [Перевод] GraphQL будущее микросервисов?

[Перевод] GraphQL будущее микросервисов?

GraphQL часто представляют как революционный путь дизайна веб API по сравнению с REST. Однако, если вы ближе посмотрите на эти технологии, то вы увидите, что между ними очень много различий. GraphQL относительно новое решение, исходники которого были открыты сообществу Фейсбуком в 2015 году. Сегодня REST все еще самая популярная парадигма, используемая для предоставления API и взаимодействия между микросервисами. Сможет ли GraphQL обогнать REST в будущем? Давайте посмотрим, как происходит микросервисное взаимодействие через GraphQL API с ипользованием Spring Boot и библиотеки GQL.
Начнем с архитектуры примера нашей системы. Предположим, что у нас есть три микросервиса, которые общаются друг с другом через URL полученные из Spring Cloud Eureka приложения.

image

Включаем поддержку GraphQL в Spring Boot

Мы можем легко включить поддержку для GraphQL на серверной стороне приложения Spring Boot с помощью стартеров. После добавления graphql-spring-boot-starter сервлет GraphQL будет автоматически доступен по пути /graphql. Мы можем переопределить этот путь по умолчанию через указание свойства graphql.servlet.mapping в application.yml файле. Мы также включаем GraphiQL – браузерную IDE для написание, проверки и тестирования GraphQL запросов и библиотеку GraphQL Java Tools, которая содержит полезные компоненты для создания запросов и мутаций. Благодаря этой библиотеки все файлы в classpath с расширением .graphqls будут использованы для создания определения схемы.

compile('com.graphql-java:graphql-spring-boot-starter:5.0.2')
compile('com.graphql-java:graphiql-spring-boot-starter:5.0.2') compile('com.graphql-java:graphql-java-tools:5.2.3')

Описание GrpahQL схемы

Каждое описание схемы содержит декларацию типов, связи между ними и множество операций включающих запросы для поиска объектов и мутаций для создания, обновления или удаления данных. Обычно мы начинаем с определения типа, который отвечает за домен описываемого объекта. Вы можете указать является ли поле обязательным с помощью ! символа или если это массив – […]. Описание должно содержать декларируемы тип или ссылку на другие типы доступные в спецификации.

type Employee { id: ID! organizationId: Int! departmentId: Int! name: String! age: Int! position: String! salary: Int!
}

Следующая часть определения схемы содержит декларации запросов и мутаций. Большинство запросов возвращают список объектов, которые помечены в схеме как [Employee]. Внутри типа EmployeeQueries мы объявляем все методы поиска, в то время как в типе EmployeeMutations методы для добавления, обновления и удаления сотрудников. Если вы передаете целый объект в метод, вы должны объявить его как input тип.

schema { query: EmployeeQueries mutation: EmployeeMutations
}
type EmployeeQueries { employees: [Employee] employee(id: ID!): Employee! employeesByOrganization(organizationId: Int!): [Employee] employeesByDepartment(departmentId: Int!): [Employee] }
type EmployeeMutations { newEmployee(employee: EmployeeInput!): Employee deleteEmployee(id: ID!) : Boolean updateEmployee(id: ID!, employee: EmployeeInput!): Employee
}
input EmployeeInput { organizationId: Int departmentId: Int name: String age: Int position: String salary: Int
}

Реализация запросов и мутаций

Благодаря автоконфигурации GraphQL Java Tools и Spring Boot GraphQL нам не нужно прилагать много усилий для имплементации запросов и мутаций в нашем приложении. Бин EmployeesQuery должен реализовывать интерфейс GraphQLQueryResolver. Основываясь на этом, Spring сможет автоматически найти и вызывать правильный метод как ответ на один из GraphQL запросов, которые были декларированы внутри схемы. Вот класс, содержащий имплементацию ответов на запросы:

@Component
public class EmployeeQueries implements GraphQLQueryResolver public List employeesByOrganization(Long organizationId) { LOGGER.info("Employees find: organizationId={}", organizationId); return repository.findByOrganization(organizationId); } public List employeesByDepartment(Long departmentId) { LOGGER.info("Employees find: departmentId={}", departmentId); return repository.findByDepartment(departmentId); } public Employee employee(Long id) { LOGGER.info("Employee find: id={}", id); return repository.findById(id); } }

Если вы хотите вызывать, например, метод employee(Long id), напишите следующий запрос. Чтобы протестировать его в вашем приложении используйте GraphiQL, который доступен по адресу /graphiql.

image

Несмотря на название EmployeeInput мы продолжаем использовать тот же доменный объект Employee, который возвращается запросом. Бину ответственному за имплементацию методов мутаций нужно реализовать интерфейс GraphQLMutationResolver.

@Component
public class EmployeeMutations implements GraphQLMutationResolver { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeQueries.class); @Autowired EmployeeRepository repository; public Employee newEmployee(Employee employee) { LOGGER.info("Employee add: employee={}", employee); return repository.add(employee); } public boolean deleteEmployee(Long id) { LOGGER.info("Employee delete: id={}", id); return repository.delete(id); } public Employee updateEmployee(Long id, Employee employee) { LOGGER.info("Employee update: id={}, employee={}", id, employee); return repository.update(id, employee); } }

И здесь мы используем GraphiQL для тестирования мутаций. Вот команда, которая добавляет новый employee и принимает ответ с id и name сотрудника.

image

На этом я приостанавливаю перевод данной статьи и пишу свое «лирическое отступление», а фактически подменяю описание части микросервисного взаимодейстивя через Apollo Client, на взаимодействие через библиотеку GQL и Unirest – библиотеки для выполнения HTTP запросов.

GraphQL клиент на Groovy.

Для создания GraphQL запросов в микросервисе department-servive я буду использовать Query builders:

String queryString = DSL.buildQuery { query('employeesByDepartment', [departmentId: departmentId]) { returns { id name position salary } }
}

Данная конструкция на DSL GQL создает запрос вида:

{ employeesByDepartment (departmentId: 1) { id name position salary } }

И, далее, я буду выполнять HTTP запрос по переданному в метод адресу.
Как формируется адрес запроса узнаем далее.

(Unirest.post(serverUrl) .body(JsonOutput.toJson([query: queryString])) .asJson() .body.jsonObject['data']['employeesByDepartment'] as List) .collect { JsonUtils.jsonToData(it.toString(), Employee.class) }

После получения ответа выполняем его преобразование из JSONObject к виду списка Employee.

GrpahQL клиент для микросервиса сотрудников

Рассмотрим реализацию микросервиса сотрудников. В этом примере я пользовался Eureka клиентом напрямую. eurekaClient получает все запущенные экземпляры сервисов, зарегистрированные как employee-service. Затем он случайно выбирает какой-то экземпляр из зарегистрированных (2). Далее, берет номер его порта и формирует адрес запроса (3) и передает его в объект EmployeeGQL который является GraphQL клиентом на Groovy и, который, описан в предыдущем пункте.

@Component
public class EmployeeClient { private static final Logger LOGGER = LoggerFactory.getLogger(EmployeeClient.class); private static final String SERVICE_NAME = "EMPLOYEE-SERVICE"; private static final String SERVER_URL = "http://localhost:%d/graphql"; Random r = new Random(); @Autowired private EurekaClient discoveryClient; // (1) public List<Employee> findByDepartment(Long departmentId) { Application app = discoveryClient.getApplication(SERVICE_NAME); InstanceInfo ii = app.getInstances().get(r.nextInt(app.size())); // (2) String serverUrl = String.format(SERVER_URL, ii.getPort()); // (3) EmployeeGQL clientGQL = new EmployeeGQL(); return clientGQL.getEmployeesByDepartmentQuery(serverUrl, departmentId.intValue()); // (4) } }

Далее, я «передаю» снова слово автору, точнее продолжаю перевод его статьи.

И наконец, EmployeeClient внедряется в класс который отвечает на запросы DepartmentQueries и используется внутри запроса departmentsByOrganizationWithEmployees.

public List<Department> departmentsByOrganizationWithEmployees(Long organizationId) { LOGGER.info("Departments find: organizationId={}", organizationId); List<Department> departments = repository.findByOrganization(organizationId); for (int i = 0; i < departments.size(); i++) { departments.get(i).setEmployees(employeeClient.findByDepartment(departments.get(i).getId())); } return departments;
}

Перед выполнением нужного запросы нам следуем взглянуть на схему созданную для department-service. Каждый объект Department может содержать список назначенных сотрудников, и мы также определили тип Employee на который ссылается тип Department.

schema { query: DepartmentQueries mutation: DepartmentMutations
}
type DepartmentQueries { departments: [Department] department(id: ID!): Department! departmentsByOrganization(organizationId: Int!): [Department] departmentsByOrganizationWithEmployees(organizationId: Int!): [Department] }
type DepartmentMutations { newDepartment(department: DepartmentInput!): Department deleteDepartment(id: ID!) : Boolean updateDepartment(id: ID!, department: DepartmentInput!): Department
}
input DepartmentInput { organizationId: Int! name: String!
}
type Department { id: ID! organizationId: Int! name: String! employees: [Employee] }
type Employee { id: ID! name: String! position: String! salary: Int!
}

Теперь мы можем вызвать наш тестовый запрос с списком нужных полей используя GraphiQL. Приложение department-service по умолчанию доступно на порте 8091, то есть мы можем увидеть его по адресу http://localhost:8091/graphiql

Заключение

Возможно, GraphQL может быть интересной альтернативой стандартным REST API. Однако, нам не следует рассматривать его как замену REST. Существуют несколько случаев, где GraphQL может быть лучшим выбором, но те, где лучшим выбором будет REST. Если вашим клиентам не нужно иметь все поля, возвращаемые серверной частью, и более того у вас есть много клиентов с разными требованиями для одной точки входа, то GraphQL хороший выбор. Если посмотреть на то, что есть в микросервисном сообществе, то можно увидеть, что сейчас там нет решение основанных на Java, которые позволяют вам использовать GraphQL вместе с обнаружением сервисов, балансировщиком или API gateway из коробки. В этой статье я показал пример использования GQL и Unirest для создания GraphQL клиента вместе с Spring Cloud Eureka для микросервисной коммуникации. Пример кода автора статьи на английском на GitHub github.com/piomin/sample-graphql-microservices.git.

Пример моего когда с библиотекой GQL: github.com/lynx-r/sample-graphql-microservices


Оставить комментарий

Ваш email нигде не будет показан
Обязательные для заполнения поля помечены *

*

x

Ещё Hi-Tech Интересное!

Проксируем файлы из AWS S3 средствами nginx

Казалось бы, задача реализации фронтенда для AWS на nginx звучит как типовой кейс для StackOverflow — ведь проблем с проксированием файлов из S3 быть не может? На деле выяснилось, что готовое решение не так-то просто найти, и данная статья должна ...

Разработчики Windows 10 реализовали защиту от вирусов-вымогателей. Ее можно обойти с помощью инъекции DLL

Он предотвращает изменение файлов в указанных защищенных папках неизвестными программами. В Windows 10 появился механизм защиты от вирусов-вымогателей под названием Controlled Folder Access. обнаружил способ обхода этой защиты с помощью DLL-инъекций. Исследователь информационной безопасности из Fujitsu System Integration Laboratories Ltd. ...