Kratos微服务框架下实现GraphQL服务

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: GraphQL 旨在让 API 变得快速、灵活并且为开发人员提供便利。它甚至可以部署在名为 GraphiQL 的集成开发环境(IDE)中。作为 REST 的替代方案,GraphQL 允许开发人员构建相应的请求,从而通过单个 API 调用从多个数据源中提取数据。

Kratos微服务框架下实现GraphQL服务

GraphQL 是一种用于应用编程接口(API)的查询语言和服务器端运行时,它可以使客户端准确地获得所需的数据,没有任何冗余。

GraphQL 由 Facebook 开发,并于 2012 年首次应用于移动应用。GraphQL 规范于 2015 年实现开源。现在,它受 GraphQL 基金会监管。

GraphQL有什么用?

GraphQL 旨在让 API 变得快速、灵活并且为开发人员提供便利。它甚至可以部署在名为 GraphiQL 的集成开发环境(IDE)中。作为 REST 的替代方案,GraphQL 允许开发人员构建相应的请求,从而通过单个 API 调用从多个数据源中提取数据。

此外,GraphQL 还可让 API 维护人员灵活地添加或弃用字段,而不会影响现有查询。开发人员可以使用自己喜欢的方法来构建 API,并且 GraphQL 规范将确保它们以可预测的方式在客户端发挥作用。

GraphQL 的优缺点

GraphQL 的优点

  • GraphQL 模式会在 GraphQL 应用中设置单一事实来源。它为企业提供了一种整合其整个 API 的方法。
  • 一次往返通讯可以处理多个 GraphQL 调用。客户端可得到自己所请求的内容,不会超量。
  • 严格定义的数据类型可减少客户端与服务器之间的通信错误。
  • GraphQL 具有自检功能。客户端可以请求一个可用数据类型的列表。这非常适合文档的自动生成。
  • GraphQL 允许应用 API 进行更新优化,而无需破坏现有查询。
  • 许多开源 GraphQL 扩展可提供 REST API 所不具备的功能。
  • GraphQL 不指定特定的应用架构。它能够以现有的 REST API 为基础,并与现有的 API 管理工具配合使用。

GraphQL 的缺点

  • 即便是熟悉 REST API 的开发人员,也需要一定时间才能掌握 GraphQL。
  • GraphQL 将数据查询的大部分工作都转移到服务器端,由此增加了服务器开发人员工作的复杂度。
  • 根据不同的实施方式,GraphQL 可能需要不同于 REST API 的 API 管理策略,尤其是在考虑速率限制和定价的情况下。
  • 缓存机制比 REST 更加复杂。
  • API 维护人员还会面临编写可维护 GraphQL 模式的额外任务。

GraphQL支持的数据类型以及关键字

标量类型

  • Int:带符号的32位整数,对应 JavaScript 的 Number
  • Float:带符号的双精度浮点数,对应 JavaScript 的 Number
  • String:UTF-8字符串,对应 JavaScript 的 String
  • Boolean:布尔值,对应 JavaScript 的 Boolean
  • ID:ID 值,是一个序列化后值唯一的字符串,可以视作对应 ES 2015 新增的 Symbol

高级类型

接口类型

Interface是包含一组确定字段的集合的抽象类型,实现该接口的类型必须包含interface定义的所有字段。比如:

interface Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
}

type Human implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  starships: [Starship]
  totalCredits: Int
}

type Droid implements Character {
  id: ID!
  name: String!
  friends: [Character]
  appearsIn: [Episode]!
  primaryFunction: String
}

联合类型

Union类型非常类似于interface,但是他们在类型之间不需要指定任何共同的字段。通常用于描述某个字段能够支持的所有返回类型以及具体请求真正的返回类型。比如定义:

union SearchResult = Human | Droid | Starship

枚举类型

又称Enums,这是一种特殊的标量类型,通过此类型,我们可以限制值为一组特殊的值。比如:

enum Episode {
  NEWHOPE
  EMPIRE
  JEDI
}

输入类型

input类型对mutations来说非常重要,在 GraphQL schema 语言中,它看起来和常规的对象类型非常类似,但是我们使用关键字input而非type,input类型按如下定义:

input CommentInput {
    body: String!
}

为什么不直接使用Object Type呢?因为 Object 的字段可能存在循环引用,或者字段引用了不能作为查询输入对象的接口和联合类型。

数组类型和非空类型

使用[]来表示数组,使用!来表示非空Non-Null强制类型的值不能为null,并且在请求出错时一定会报错。可以用于必须保证值不能为null的字段

对象类型

GraphQL schema最基本的类型就是Object Type。用于描述层级或者树形数据结构。比如:

type Character {
  name: String!
  appearsIn: [Episode!]!
}

GraphQL查询语法

GraphQL的一次操作请求被称为一份文档(document),即GraphQL服务能够解析验证并执行的一串请求字符串(Source Text)。完整的一次操作由操作(Operation)和片段(Fragments)组成。一次请求可以包含多个操作和片段。只有包含操作的请求才会被GraphQL服务执行。

只包含一个操作的请求可以不带OperationName,如果是operationType是query的话,可以全部省略掉,即:

{
  getMessage {
    # query也可以拥有注释,注释以#开头
    content
    author
  }
}

当query包含多个操作时,所有操作都必须带上名称。

GraphQL中,我们会有这样一个约定,Query和与之对应的Resolver是同名的,这样在GraphQL才能把它们对应起来。

Query

Query用做读操作,也就是从服务器获取数据。以上图的请求为例,其返回结果如下,可以看出一一对应,精准返回数据

{
  "data": {
    "single": [
      {
        "content": "test content 1",
        "author": "pp1"
      }
    ],
    "all": [
      {
        "content": "test content",
        "author": "pp",
        "id": "0"
      },
      {
        "content": "test content 1",
        "author": "pp1",
        "id": "1"
      }
    ]
  }
}

Field

Field是我们想从服务器获取的对象的基本组成部分。query是数据结构的顶层,其下属的allsingle都属于它的字段。

字段格式应该是这样的:alias:name(argument:value)

其中 alias 是字段的别名,即结果中显示的字段名称。

name 为字段名称,对应 schema 中定义的 fields 字段名。

argument 为参数名称,对应 schema 中定义的 fields 字段的参数名称。

value 为参数值,值的类型对应标量类型的值。

Argument

和普通的函数一样,query可以拥有参数,参数是可选的或必须的。参数使用方法如上图所示。

需要注意的是,GraphQL中的字符串需要包装在双引号中。

Variables

除了参数,query还允许你使用变量来让参数可动态变化,变量以$开头书写,使用方式如上图所示

变量还可以拥有默认值:

query gm($id: ID = 2) {
  # 查询数据
  single: getMessage(id: $id) {
    ...entity
  }
  all: getMessage {
    ...entity
    id
  }
}

Allases

别名,比如说,我们想分别获取全部消息和ID为1的消息,我们可以用下面的方法:

query gm($id: ID = 2) {
  # 查询数据
  getMessage(id: $id) {
    ...entity
  }
  getMessage {
    ...entity
    id
  }
}

由于存在相同的name,上述代码会报错,要解决这个问题就要用到别名了Allases。

query gm($id: ID = 2) {
  # 查询数据
  single: getMessage(id: $id) {
    ...entity
  }
  all: getMessage {
    ...entity
    id
  }
}

Fragments

Fragments是一套在queries中可复用的fields。比如说我们想获取Message,在没有使用fragment之前是这样的:

query gm($id: ID = 2) {
  # 查询数据
  single: getMessage(id: $id) {
    content
    author
  }
  all: getMessage {
    content
    author
    id
  }
}

但是如果fields过多,就会显得重复和冗余。Fragments在此时就可以起作用了。使用了Fragment之后的语法就如上图所示,简单清晰。

Fragment支持多层级地继承。

Directives

Directives提供了一种动态使用变量改变我们的queries的方法。如本例,我们会用到以下两个directive:

query gm($id: ID = 2, $isNotShowId: Boolean!, $showAuthor: Boolean!) {
  # 查询数据
  single: getMessage(id: $id) {
    ...entity
  }
  all: getMessage {
    ...entity
    id @skip(if: $isNotShowId)
  }
}

fragment entity on Message {
  content
  author @include(if: $showAuthor)
}

### 入参是:
{
  "id": 1,
  "isNotShowId": true,
  "showAuthor": false
}

@include: 只有当if中的参数为true时,才会包含对应fragment或field;

@skip:当if中的参数为true时,会跳过对应fragment或field;

结果如下:

{
  "data": {
    "single": [
      {
        "content": "test content 1"
      }
    ],
    "all": [
      {
        "content": "test content"
      },
      {
        "content": "test content 1"
      }
    ]
  }
}

Mutation

传统的API使用场景中,我们会有需要修改服务器上数据的场景,mutations就是应这种场景而生。mutations被用以执行写操作,通过mutations我们会给服务器发送请求来修改和更新数据,并且会接收到包含更新数据的反馈。mutations和queries具有类似的语法,仅有些许的差别。

  1. operationType为mutation
  2. 为了保证数据的完整性mutations是串形执行,而queries可以并行执行。

Subscription

Subscription是GraphQL最后一个操作类型,它被称为订阅。当另外两个由客户端通过HTTP请求发送,订阅是服务器在某个事件发生时将数据本身推送给感兴趣的客户端的一种方式。这就是GraphQL 处理实时通信的方式。

编译GraphQL文件

在graphql同级目录下创建一个配置文件,命名为:gqlgen.yml

schema:
  - "*.graphql"

或者是指定导出models项:

models:
  Todo:
    model: github.com/Laisky/laisky-blog-graphql.Todo

然后在graphql文件同级目录下,使用命令行执行以下命令,即可生成go代码:

# 直接运行
go run github.com/99designs/gqlgen

# 安装
go get github.com/99designs/gqlgen
go install github.com/99designs/gqlgen
# 然后运行
gqlgen

开始在Kratos微服务框架下使用GraphQL

我基于gqlgen实现的GraphQL服务封装,它可以在Kratos微服务框架下直接使用:https://github.com/tx7do/kratos-transport/tree/main/transport/graphql

实例程序的目标是从服务器获取温湿度信息,然后将温湿度信息发送给客户端。示例代码可以在单元测试里面找到。

编写GraphQL协议

type Hygrothermograph {
    humidity: Float!
    temperature: Float!
}

type Query {
    hygrothermograph: Hygrothermograph!
}

编写Graphql服务器

首先需要编写解析器,在Kratos里面,可以写成Service。

type resolver struct{}

func (r *resolver) Query() api.QueryResolver {
    return &queryResolver{}
}

type queryResolver struct{}

func (r *queryResolver) Hygrothermograph(ctx context.Context) (*api.Hygrothermograph, error) {
    ret := &api.Hygrothermograph{
        Humidity:    float64(rand.Intn(100)),
        Temperature: float64(rand.Intn(100)),
    }
    fmt.Println("Humidity:", ret.Humidity, "Temperature:", ret.Temperature)
    return ret, nil
}

编写服务器

ctx := context.Background()

srv := NewServer(
  WithAddress(":8800"),
)

srv.Handle("/query", api.NewExecutableSchema(api.Config{Resolvers: &resolver{}}))

if err := srv.Start(ctx); err != nil {
  panic(err)
}

defer func() {
  if err := srv.Stop(ctx); err != nil {
    t.Errorf("expected nil got %v", err)
  }
}()

服务器本地访问地址为:http://localhost:8800/query

测试

如果要测试的话,推荐使用客户端:Altair。

点击Docs按钮,可以查询到API文档,文档内容会显示在右侧编辑框,如下图所示:

graphql_check_docs.png

鼠标放到API上面会显示ADD QUERY按钮,点击就可以添加一个新的查询到最左边的编辑框中。

graphql_add_query.png

点击右上角的Send Request按钮,可以发送请求。在中间的编辑框就可以看到请求的结果了。

graphql_send_request.png

客户端工具

客户端推荐使用Altair,我用着挺爽的。

参考文档

目录
相关文章
|
28天前
|
弹性计算 API 持续交付
后端服务架构的微服务化转型
本文旨在探讨后端服务从单体架构向微服务架构转型的过程,分析微服务架构的优势和面临的挑战。文章首先介绍单体架构的局限性,然后详细阐述微服务架构的核心概念及其在现代软件开发中的应用。通过对比两种架构,指出微服务化转型的必要性和实施策略。最后,讨论了微服务架构实施过程中可能遇到的问题及解决方案。
|
14天前
|
NoSQL 前端开发 测试技术
👀探秘微服务:从零开启网关 SSO 服务搭建之旅
单点登录(Single Sign-On,简称SSO)是一种认证机制,它允许用户只需一次登录就可以访问多个应用程序或系统。本文结合网关和SaToken快速搭建可用的Session管理服务。
64 8
|
2月前
|
弹性计算 持续交付 API
构建高效后端服务:微服务架构的深度解析与实践
在当今快速发展的软件行业中,构建高效、可扩展且易于维护的后端服务是每个技术团队的追求。本文将深入探讨微服务架构的核心概念、设计原则及其在实际项目中的应用,通过具体案例分析,展示如何利用微服务架构解决传统单体应用面临的挑战,提升系统的灵活性和响应速度。我们将从微服务的拆分策略、通信机制、服务发现、配置管理、以及持续集成/持续部署(CI/CD)等方面进行全面剖析,旨在为读者提供一套实用的微服务实施指南。
|
30天前
|
弹性计算 Kubernetes API
构建高效后端服务:微服务架构的深度剖析与实践####
本文深入探讨了微服务架构的核心理念、设计原则及实现策略,旨在为开发者提供一套系统化的方法论,助力其构建灵活、可扩展且易于维护的后端服务体系。通过案例分析与实战经验分享,揭示了微服务在提升开发效率、优化资源利用及增强系统稳定性方面的关键作用。文章首先概述了微服务架构的基本概念,随后详细阐述了其在后端开发中的应用优势与面临的挑战,最后结合具体实例,展示了如何从零开始规划并实施一个基于微服务的后端项目。 ####
|
2月前
|
监控 持续交付 数据库
构建高效的后端服务:微服务架构的深度解析
在现代软件开发中,微服务架构已成为提升系统可扩展性、灵活性和维护性的关键。本文深入探讨了微服务架构的核心概念、设计原则和最佳实践,通过案例分析展示了如何在实际项目中有效地实施微服务策略,以及面临的挑战和解决方案。文章旨在为开发者提供一套完整的指导框架,帮助他们构建出更加高效、稳定的后端服务。
|
3月前
|
Kubernetes 负载均衡 Docker
构建高效后端服务:微服务架构的探索与实践
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于任何在线业务的成功至关重要。本文将深入探讨微服务架构的概念、优势以及如何在实际项目中有效实施。我们将从微服务的基本理念出发,逐步解析其在提高系统可维护性、扩展性和敏捷性方面的作用。通过实际案例分析,揭示微服务架构在不同场景下的应用策略和最佳实践。无论你是后端开发新手还是经验丰富的工程师,本文都将为你提供宝贵的见解和实用的指导。
|
2月前
|
Kubernetes API Docker
构建高效后端服务:微服务架构的深度实践与优化####
本文深入探讨了微服务架构在现代后端开发中的应用,通过剖析其核心概念、设计原则及实施策略,结合具体案例分析,展示了如何有效提升系统的可扩展性、可靠性和维护性。文章还详细阐述了微服务拆分的方法论、服务间通信的最佳实践、以及容器化与编排工具(如Docker和Kubernetes)的应用技巧,为读者提供了一份全面的微服务架构落地指南。 ####
|
3月前
|
监控 API 持续交付
构建高效后端服务:微服务架构的深度探索
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于支撑复杂的业务逻辑和海量数据处理至关重要。本文深入探讨了微服务架构的核心理念、实施策略以及面临的挑战,旨在为开发者提供一套构建高效、可扩展后端服务的方法论。通过案例分析,揭示微服务如何帮助企业应对快速变化的业务需求,同时保持系统的稳定性和灵活性。
51 9
|
2月前
|
分布式计算 Java 持续交付
如何选择合适的微服务框架
如何选择合适的微服务框架
35 0
|
3月前
|
监控 安全 Java
构建高效后端服务:微服务架构深度解析与最佳实践###
【10月更文挑战第19天】 在数字化转型加速的今天,企业对后端服务的响应速度、可扩展性和灵活性提出了更高要求。本文探讨了微服务架构作为解决方案,通过分析传统单体架构面临的挑战,深入剖析微服务的核心优势、关键组件及设计原则。我们将从实际案例入手,揭示成功实施微服务的策略与常见陷阱,为开发者和企业提供可操作的指导建议。本文目的是帮助读者理解如何利用微服务架构提升后端服务的整体效能,实现业务快速迭代与创新。 ###
71 2