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

本文涉及的产品
云原生网关 MSE Higress,422元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
注册配置 MSE Nacos/ZooKeeper,118元/月
简介: 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,我用着挺爽的。

参考文档

目录
相关文章
|
1月前
|
XML JSON API
ServiceStack:不仅仅是一个高性能Web API和微服务框架,更是一站式解决方案——深入解析其多协议支持及简便开发流程,带您体验前所未有的.NET开发效率革命
【10月更文挑战第9天】ServiceStack 是一个高性能的 Web API 和微服务框架,支持 JSON、XML、CSV 等多种数据格式。它简化了 .NET 应用的开发流程,提供了直观的 RESTful 服务构建方式。ServiceStack 支持高并发请求和复杂业务逻辑,安装简单,通过 NuGet 包管理器即可快速集成。示例代码展示了如何创建一个返回当前日期的简单服务,包括定义请求和响应 DTO、实现服务逻辑、配置路由和宿主。ServiceStack 还支持 WebSocket、SignalR 等实时通信协议,具备自动验证、自动过滤器等丰富功能,适合快速搭建高性能、可扩展的服务端应用。
107 3
|
1月前
|
Cloud Native Java API
聊聊从单体到微服务架构服务演化过程
本文介绍了从单体应用到微服务再到云原生架构的演进过程。单体应用虽易于搭建和部署,但难以局部更新;面向服务架构(SOA)通过模块化和服务总线提升了组件复用性和分布式部署能力;微服务则进一步实现了服务的独立开发与部署,提高了灵活性;云原生架构则利用容器化、微服务和自动化工具,实现了应用在动态环境中的弹性扩展与高效管理。这一演进体现了软件架构向着更灵活、更高效的方向发展。
|
25天前
|
Kubernetes 负载均衡 Docker
构建高效后端服务:微服务架构的探索与实践
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于任何在线业务的成功至关重要。本文将深入探讨微服务架构的概念、优势以及如何在实际项目中有效实施。我们将从微服务的基本理念出发,逐步解析其在提高系统可维护性、扩展性和敏捷性方面的作用。通过实际案例分析,揭示微服务架构在不同场景下的应用策略和最佳实践。无论你是后端开发新手还是经验丰富的工程师,本文都将为你提供宝贵的见解和实用的指导。
|
24天前
|
监控 API 持续交付
构建高效后端服务:微服务架构的深度探索
【10月更文挑战第20天】 在数字化时代,后端服务的构建对于支撑复杂的业务逻辑和海量数据处理至关重要。本文深入探讨了微服务架构的核心理念、实施策略以及面临的挑战,旨在为开发者提供一套构建高效、可扩展后端服务的方法论。通过案例分析,揭示微服务如何帮助企业应对快速变化的业务需求,同时保持系统的稳定性和灵活性。
46 9
|
26天前
|
监控 安全 Java
构建高效后端服务:微服务架构深度解析与最佳实践###
【10月更文挑战第19天】 在数字化转型加速的今天,企业对后端服务的响应速度、可扩展性和灵活性提出了更高要求。本文探讨了微服务架构作为解决方案,通过分析传统单体架构面临的挑战,深入剖析微服务的核心优势、关键组件及设计原则。我们将从实际案例入手,揭示成功实施微服务的策略与常见陷阱,为开发者和企业提供可操作的指导建议。本文目的是帮助读者理解如何利用微服务架构提升后端服务的整体效能,实现业务快速迭代与创新。 ###
60 2
|
1月前
|
消息中间件 Kafka 数据库
微服务架构中,如何确保服务之间的数据一致性?
微服务架构中,如何确保服务之间的数据一致性?
|
1月前
|
Dubbo Java 应用服务中间件
Dubbo学习圣经:从入门到精通 Dubbo3.0 + SpringCloud Alibaba 微服务基础框架
尼恩团队的15大技术圣经,旨在帮助开发者系统化、体系化地掌握核心技术,提升技术实力,从而在面试和工作中脱颖而出。本文介绍了如何使用Dubbo3.0与Spring Cloud Gateway进行整合,解决传统Dubbo架构缺乏HTTP入口的问题,实现高性能的微服务网关。
|
28天前
|
运维 Kubernetes 开发者
构建高效后端服务:微服务架构与容器化技术的结合
【10月更文挑战第18天】 在数字化转型的浪潮中,企业对后端服务的要求日益提高,追求更高的效率、更强的可伸缩性和更易于维护的系统。本文将探讨微服务架构与容器化技术如何结合,以构建一个既灵活又高效的后端服务体系。通过分析当前后端服务面临的挑战,介绍微服务和容器化的基本概念,以及它们如何相互配合来优化后端服务的性能和管理。本文旨在为开发者提供一种实现后端服务现代化的方法,从而帮助企业在竞争激烈的市场中脱颖而出。
25 0
|
2月前
|
消息中间件 Kafka 数据库
微服务架构中,如何确保服务之间的数据一致性
微服务架构中,如何确保服务之间的数据一致性
|
2月前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
56 1