使用 Spring Cloud Gateway 和 GraphQL Mesh,可以轻松构建现有微服务的 BFF (REST+GraphQL)
KINTO Technologies 2021 年 Qiita 的「圣诞快乐」系列的第 24 篇文章。
首先
本文介绍了在考虑实现微服务的BFF(前端后端)时,我们在PoC中进行的GraphQL Mesh构建示例。
我想在这篇文章中传达的是
如果我们使用基于REST接口的微服务架构,再加上Spring Cloud Gateway和GraphQL Mesh,就可以很容易地构建一个BFF,可以同时支持现有的REST接口和GraphQL接口。希望能够在这方面进行分享。
系统概要图
为什么选择Spring Cloud Gateway?
- Spring Boot
目前,我们的微服务后端使用Spring Boot,并计划使用Spring生态系统来构建BFF,因此我们决定使用Spring Cloud系列框架。
- マルチクラウド問題
作为BFF的角色,我们考虑了使用AWS的管理服务。然而,由于我们需要在服务的海外扩展中支持多云环境,我们决定采用基于容器的方式来构建API Gateway。
- 認証・認可のカスタマイズ
计划使用BFF实施认证和授权,并根据海外扩展需求进行系统定制化,希望构建一种标准且灵活的解决方案。我认为能够利用Spring Security等生态系统也是一个优势。
为什么选择GraphQL Mesh?
- API Composition問題
系统正在转变为微服务,并且预计存在将各个API响应进行合并的使用情景,我们考虑了API组合策略,但在现有的REST API中,需要为每个使用情景创建新的API。因此,我们决定考虑GraphQL接口,它可以灵活地处理请求和响应,当然也包括API组合,我们决定使用GraphQL Mesh来实现该目标。
- 構築が一番簡単
我认为有很多方法可以使用GraphQL接口包装现有的微服务,但在这些方法中,使用GraphQL Mesh仅通过openapi配置来构建是最简单的。
实施
微服务
本文描述了使用OpenAPI Generator从API定义的YAML文件中输出源代码的微服务。在本次验证中,我们决定只实现订阅(Subscription)和车辆(Vehicle)这两个微服务的GET方法。
请注意,由于本文主要讨论BFF的实现,因此省略了微服务的详细实现内容。
- OpenAPI設定
openapi: 3.0.3
info:
title: 订阅API
description: 这是一个用于测试的订阅API。
version: 1.0.0
servers:
– url: ‘http://localhost:8091/’
description: 本地开发环境
tags:
– name: 订阅
description: 测试订阅API
paths:
/subscriptions:
get:
tags:
– 订阅
summary: 获取订阅列表
description: |
- 按创建时间排序返回订阅表中的订阅列表。
operationId: listSubscription
parameters:
– in: query
name: count
description: 最大数量
required: true
schema:
type: integer
– in: query
name: customerId
description: 客户ID
required: false
schema:
type: string
responses:
‘200’:
description: 成功
content:
application/json:
schema:
type: array
items:
$ref: ‘#/components/schemas/订阅响应’
‘400’:
$ref: ‘#/components/responses/错误请求’
default:
$ref: ‘#/components/responses/默认错误’
security:
– BearerAuth: []components:
responses:
错误请求:
description: 错误请求
content:
application/json:
schema:
$ref: ‘#/components/schemas/Api错误’
默认错误:
description: 非预期错误
content:
application/json:
schema:
$ref: ‘#/components/schemas/Api错误’
schemas:
Api错误:
type: object
properties:
errors:
type: array
items:
type: object
properties:
code:
type: string
description: 错误代码
message:
type: string
description: 错误消息
订阅响应:
type: object
properties:
id:
type: string
customerId:
type: string
mailAddress:
type: string
name:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
modifiedAt:
type: string
format: date-time
modifiedBy:
type: string
version:
type: integer
format: int32securitySchemes:
BearerAuth:
type: http
scheme: bearer
openapi: 3.0.3
info:
title: 车辆API
description: 这是用于测试的车辆API。
version: 1.0.0
servers:
– url: ‘http://localhost:8092/’
description: 本地开发环境
tags:
– name: 车辆
description: 测试车辆API
paths:
/vehicles:
get:
tags:
– 车辆
summary: 获取车辆列表
description: |
- 按创建日期排序返回车辆列表。
operationId: 获取车辆列表
parameters:
– in: query
name: count
description: 最大数量
required: true
schema:
type: integer
– in: query
name: customerId
description: 客户ID
required: false
schema:
type: string
responses:
‘200’:
description: 请求成功
content:
application/json:
schema:
type: array
items:
$ref: ‘#/components/schemas/车辆响应’
‘400’:
$ref: ‘#/components/responses/请求错误’
default:
$ref: ‘#/components/responses/默认响应’
security:
– BearerAuth: []components:
responses:
请求错误:
description: 请求错误
content:
application/json:
schema:
$ref: ‘#/components/schemas/Api错误’
默认响应:
description: 意外错误
content:
application/json:
schema:
$ref: ‘#/components/schemas/Api错误’
schemas:
Api错误:
type: object
properties:
errors:
type: array
items:
type: object
properties:
code:
type: string
description: 错误代码
message:
type: string
description: 错误消息
车辆响应:
type: object
properties:
id:
type: string
customerId:
type: string
model:
type: string
color:
type: string
createdAt:
type: string
format: date-time
createdBy:
type: string
modifiedAt:
type: string
format: date-time
modifiedBy:
type: string
version:
type: integer
format: int32securitySchemes:
BearerAuth:
type: http
scheme: bearer
春季云网关
在Spring Initializr的默认源代码基础上,只需更改以下设置。
- 設定
dependencies {
implementation 'org.springframework.cloud:spring-cloud-starter-gateway:3.0.5'
}
spring:
cloud:
gateway:
routes:
- id: graphql
uri: http://localhost:4000
predicates:
- Path=/graphql
filters:
- SetPath=/graphql
- id: subscription
uri: http://localhost:8091
predicates:
- Path=/subscriptions
filters:
- SetPath=/subscriptions
- id: vehicle
uri: http://localhost:8092
predicates:
- Path=/vehicles
filters:
- SetPath=/vehicles
GraphQL Mesh 可以进行本地中文求情重新解释为:图灵网格
GraphQL Mesh的Docker镜像是基于以下内容创建的。
- GraphQL Mesh設定
sources:
- name: Fake API
handler:
openapi:
source: ./openapi.yml
baseUrl: http://host.docker.internal:8080
serve:
hostname: 0.0.0.0
※GraphQL Mesh运行在本地PC的Docker上,而Spring Cloud Gate运行在本地PC上,因此将host设置为host.docker.internal。
- Dockerfile
FROM hiroyukiosaki/graphql-mesh:latest-all-alpine
COPY .meshrc.yaml ./.meshrc.yaml
COPY openapi.yml ./openapi.yml
- OpenAPI設定
openapi:3.0.3
info:
title:微服务API
description:这是一个用于测试的微服务API。
version:1.0.0
servers:
– url:’http://localhost:8080/’
description:本地开发环境
tags:
– name:订阅
description:测试订阅API
– name:车辆
description:测试车辆API
路径:
/subscriptions:
get:
tags:
– 订阅
summary:获取订阅列表
description:|
- 按创建日期对订阅表进行排序。
operationId:listSubscription
parameters:
– in:query
name:count
description:最大数量
required:true
schema:
type:integer
– in:query
name:customerId
description:客户ID
required:false
schema:
type:string
responses:
‘200’:
description:成功
content:
application/json:
schema:
type:array
items:
$ref:’#/components/schemas/SubscriptionResponse’
‘400’:
$ref:’#/components/responses/BadRequest’
default:
$ref:’#/components/responses/Default’
security:
– BearerAuth:[]/vehicles:
get:
tags:
– 车辆
summary:获取车辆列表
description:| - 按创建日期对车辆表进行排序。
operationId:listVehicles
parameters:
– in:query
name:count
description:最大数量
required:true
schema:
type:integer
– in:query
name:customerId
description:客户ID
required:false
schema:
type:string
responses:
‘200’:
description:成功
content:
application/json:
schema:
type:array
items:
$ref:’#/components/schemas/VehicleResponse’
‘400’:
$ref:’#/components/responses/BadRequest’
default:
$ref:’#/components/responses/Default’
security:
– BearerAuth:[]components:
responses:
BadRequest:
description:错误请求
content:
application/json:
schema:
$ref:’#/components/schemas/ApiError’
Default:
description:意外错误
content:
application/json:
schema:
$ref:’#/components/schemas/ApiError’
schemas:
ApiError:
type:object
properties:
errors:
type:array
items:
type:object
properties:
code:
type:string
description:错误代码
message:
type:string
description:错误信息
SubscriptionResponse:
type:object
properties:
id:
type:string
customerId:
type:string
mailAddress:
type:string
name:
type:string
createdAt:
type:string
format:date-time
createdBy:
type:string
modifiedAt:
type:string
format:date-time
modifiedBy:
type:string
version:
type:integer
format:int32
VehicleResponse:
type:object
properties:
id:
type:string
customerId:
type:string
model:
type:string
color:
type:string
createdAt:
type:string
format:date-time
createdBy:
type:string
modifiedAt:
type:string
format:date-time
modifiedBy:
type:string
version:
type:integer
format:int32
securitySchemes:
BearerAuth:
type:http
scheme:bearer
※openapi.yml是将微服务的subscription.yml和vehicle.yml合并而成的文件。
- Docker image作成
PS C:\dooboo\test\custom-images\graphql-mesh> docker build . --tag graphql-mesh
[+] Building 0.2s (8/8) FINISHED
=> [internal] load build definition from Dockerfile 0.0s
=> => transferring dockerfile: 156B 0.0s
=> [internal] load .dockerignore 0.0s
=> => transferring context: 2B 0.0s
=> [internal] load metadata for docker.io/hiroyukiosaki/graphql-mesh:latest-all-alpine 0.0s
=> [1/3] FROM docker.io/hiroyukiosaki/graphql-mesh:latest-all-alpine 0.0s
=> [internal] load build context 0.0s
=> => transferring context: 225B 0.0s
=> CACHED [2/3] COPY .meshrc.yaml ./.meshrc.yaml 0.0s
=> [3/3] COPY openapi.yml ./openapi.yml 0.0s
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => writing image sha256:b9e2f32d6c20cb5340de6cafa9e2f72b663fc95150809002fe1b08bd463a2acf 0.0s
=> => naming to docker.io/library/graphql-mesh 0.0s
Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
- GraphQL Mesh 実行
PS C:\dooboo\test\custom-images\graphql-mesh> docker run --name graphql-mesh -p 4000:4000 graphql-mesh
yarn run v1.22.5
$ mesh dev
?️ - Server: Generating Mesh schema...
?️ - Server: Serving GraphQL Mesh: http://0.0.0.0:4000
- GraphQL 起動確認
最终成果
执行环境
休息
在swagger editor的网站上,通过对上述openapi.yml定义进行REST(Representational State Transfer)验证,直接运行了基于http://localhost:8080的Spring Cloud Gateway。
GraphQL是一种用于API的查询语言。
我确认也能成功执行这个。
结束时。
使用Spring Cloud Gateway和GraphQL Mesh,通过为现有的微服务添加BFF(Backend For Frontend)来实现对REST接口和GraphQL接口的双重支持。实际上,如果要运营BFF,请假设在BFF端执行认证和授权,并且可以使用Spring Security + Spring Session进行支持,这样可以实现通用的用法。
我们公司正在进行丰田汽车的订阅服务计划和开发工作,目前正在招聘工程师。请访问KINTO Technologies公司官方网站。