graphql_如何在GraphQL中包装流I / O接口

graphql

This post will be about using GraphQL to handle a service that uses an I/O stream for interaction between client and server. In a previous post, I mocked up a GraphQL API to the Universal Chess Interface (UCI). The UCI uses stdio to communicate, accepting commands from an input stream and sending responses via an output stream. I’ll be using UCI as an illustration, but I won’t be describing UCI in great detail.

这篇文章将关于使用GraphQL处理使用I / O流进行客户端和服务器之间交互的服务。 在上一篇文章中,我为通用国际象棋界面 (UCI)模拟了GraphQL API。 UCI使用stdio进行通信,从输入流接受命令并通过输出流发送响应。 我将以UCI为例,但是不会详细描述UCI。

fish鱼 (Stockfish)

Stockfish is as well-known chess engine that supports UCI. Using NodeJS and the module stockfish.js (a JavaScript transpilation of the original), it is easy to setup a running engine that implements UCI via stdio:

Stockfish是支持UCI的著名国际象棋引擎。 使用NodeJS和模块stockfish.js(原始JavaScript翻译),可以很容易地设置一个运行的引擎,该引擎通过stdio实现UCI:

  • create and cd into a folder

    创建并CD进入文件夹
  • npm init

    npm init

  • npm install stockfish

    npm install stockfish

  • node node_modules/stockfish/src/stockfish.js

    node node_modules/stockfish/src/stockfish.js

And from there you can type in UCI commands in terminal window and see the results.

从那里,您可以在终端窗口中键入UCI命令并查看结果。

查询与变异回顾 (A review of Query vs Mutation)

Queries are executed in parallel. That is not a problem for a stateless API where each query will return the same result regardless of the order in which results are returned. UCI is not stateless, so commands and results have to operate in sequence. Here’s an example of interaction between the command line ‘client’ and chess engine:

查询是并行执行的。 对于无状态API而言,这不是问题,因为无论查询返回的顺序如何,每个查询都将返回相同的结果。 UCI不是无状态的 ,因此命令和结果必须按顺序操作。 这是命令行“客户端”和国际象棋引擎之间交互的示例:

Engine responses to client commands are indented. The first state transition is to initiate the UCI protocol, where the engine responds with default option settings and a uciok signal indicating it is finished. At this point, the client can configure options. These will only take effect when the command isready is issued. The engine responds with readyok when all options are set. Later state transitions will occur during game set up and analysis (not shown).

引擎对客户端命令的响应会缩进。 第一个状态转换是启动UCI协议,在该协议中,引擎以默认选项设置和uciok信号进行响应以指示其已完成。 此时,客户端可以配置选项。 这些仅在发出命令readyy时生效。 设置所有选项后,引擎会以readyok响应。 稍后的状态转换将在游戏设置和分析过程中发生(未显示)。

Running several queries in parallel may issue commands prematurely, since no query waits for the response of another query. The problem can be illustrated with a simple GraphQL API to an mock asynchronous service:

并行运行多个查询可能会过早发出命令,因为没有查询会等待另一个查询的响应。 可以通过模拟异步服务的简单GraphQL API来说明此问题:

import {makeExecutableSchema} from 'graphql-tools';

const typeDefs = `
type Query {
  message(id: ID!): String!
}
type Mutation {
  message(id: ID!): String!
}
`

const resolvers = {
  Query: {
    message: (_, {id}) => new Promise(resolve => {
      setTimeout(function() {
        let message = `response to message ${id}`;
        console.log(message)
        resolve(message);
      }, Math.random() * 10000)
    })
  },
  Mutation: {
    message: (_, {id}) => new Promise(resolve => {
      setTimeout(function() {
        let message = `response to message ${id}`;
        console.log(message)
        resolve(message);
      }, Math.random() * 10000)
    })
  }
}

const schema = makeExecutableSchema({typeDefs, resolvers});
export {
  schema
};

The results are:

结果是:

In the console windows (bottom half), you can see when responses were returned. Now execute the same requests via Mutation:

在控制台窗口(下半部分)中,您可以看到何时返回响应。 现在通过Mutation执行相同的请求:

Getting a response takes longer because each operation must finish before the next is invoked.

获得响应需要更长的时间,因为每个操作必须在调用下一个操作之前完成。

这对于GraphQL UCI包装器意味着什么 (What this means for a GraphQL UCI wrapper)

In a previous post, I gave arguments for why GraphQL might be used to wrap UCI. Perhaps the easiest way to do this is to use GraphQL’s subscription service. This will send events back to the client via a web socket. Commands are sent via Queries or Mutations, and the responses come back as subscribed-to events.

在上一篇文章中 ,我给出了为什么可以使用GraphQL包装UCI的论点。 可能最简单的方法是使用GraphQL的订阅服务。 这将通过Web套接字将事件发送回客户端。 通过查询或突变发送命令,并且响应作为预订事件返回。

In the case of UCI interaction, mutations would be used to ensure that commands are executed in the expected sequence. Before executing a command, you would first set up a subscription to receive the response. By using GraphQL, subscription responses are type-safe, much like return values of a Query or Mutation request.

在UCI交互的情况下,将使用突变来确保命令按预期顺序执行。 在执行命令之前,您首先需要设置一个订阅以接收响应。 通过使用GraphQL,订阅响应是类型安全的,非常类似于Query或Mutation请求的返回值。

The client calls GraphQL Mutations to send requests via HTTP, then receives responses (if any) via web socket. Though simple to implement on the server, a socket-based interface is awkward for the client because it is multi-stepped:

客户端调用GraphQL Mutations通过HTTP发送请求,然后通过Web套接字接收响应(如果有)。 尽管在服务器上实现起来很简单,但是基于套接字的接口对于客户端却很尴尬,因为它是多步骤的:

  1. subscribe to the expected response event

    订阅预期的响应事件
  2. send a command via HTTP

    通过HTTP发送命令
  3. receive an HTTP response (an acknowledgment that the request was received, not the actual result)

    收到HTTP响应(确认已收到请求,而不是实际结果)
  4. await the real response to arrive via the web socket.

    等待真正的响应通过Web套接字到达。
  5. act on the response

    根据回应采取行动
简化客户端-服务器交互 (Simplifying the client-server interaction)

Let’s categorize the types of responses UCI sends:

让我们对UCI发送的响应类型进行分类:

  1. single line response

    单线响应
  2. no response

    没有React
  3. multi-line, multi-value response, with terminator

    多行,多值响应,带终止符

(Aside: It is possible to start analysis without a definite time limit (“infinite go”). This would fall under category 2 because analysis will arrive at a best move termination point, either by exhaustion or by the stop command.)

(此外:可以在没有明确的时间限制(“无限前进 ”)的情况下开始分析。这将归为类别2,因为分析将通过精疲力尽或通过stop命令到达最佳移动终止点。)

Category 1 is simple call and response, and these can be handled as plain old GraphQL HTTP requests. No need to subscribe for a response: the resolver can just return it when it arrives.

类别1是简单的调用和响应,可以将它们作为普通的旧GraphQL HTTP请求进行处理。 无需订阅响应:解析程序可以在响应到达时将其返回。

Category 2 receives no response from the engine, but a response is required by HTTP. All that is needed in this case is to acknowledge the request.

类别2没有收到来自引擎的响应,但是HTTP要求响应。 在这种情况下,所需要做的就是确认请求。

Category 3 has two subtypes: requests with multi-line but fixed responses (e.g. option), and requests with streaming, intermediate responses (go). The former can again be handled through HTTP because the response will be predictable and timely. The latter has a varying (possibly long) completion time, and may be sending a series of intermediate responses of interest to the client, which it would like to receive in real time. Since we can’t send back multiple responses to an HTTP request, this latter case cannot be handled by HTTP alone, so the subscription interface as described above is still appropriate.

类别3有两个子类型:具有多行但固定响应的请求(例如option )和具有流媒体,中间响应的请求( go )。 前者可以再次通过HTTP处理,因为响应是可预测的且及时的。 后者具有变化的(可能很长的)完成时间,并且可能正在向客户端发送一系列感兴趣的中间响应,它希望实时接收 由于我们无法将对HTTP请求的多个响应发送回去,因此后一种情况不能仅由HTTP处理,因此上述预订接口仍然适用。

Despite UCI being a streaming interface, it turns out that for most cases, an HTTP response/request can be used for interaction via GraphQL.

尽管UCI是流接口,但事实证明,在大多数情况下,HTTP响应/请求可用于通过GraphQL进行交互。

结论 (Conclusions)

  1. The GraphQL schema should consist of Mutations because UCI is stateful and commands must execute in sequence

    GraphQL模式应该由Mutations组成,因为UCI是有状态的,命令必须按顺序执行
  2. For Category 1 & 2 commands, HTTP request/response is simplest. There is still streaming going on in the back end, but GraphQL resolvers will instantiate a UCI stream listener specific to the expected UCI command response before sending the command to the engine. That listener will resolve the GraphQL request via HTTP when the response arrives from the engine. This makes lighter work for the client.

    对于1类和2类命令,HTTP请求/响应是最简单的。 后端仍在进行流传输,但是GraphQL解析器会在将命令发送到引擎之前实例化特定于预期UCI命令响应的UCI流侦听器。 当响应从引擎到达时,该侦听器将通过HTTP解析GraphQL请求。 这使客户工作更轻松。
  3. The server will also track UCI state to ensure that commands are executed in the proper context. If the client tries to execute a command before the engine can handle it, an HTTP status error will be returned

    服务器还将跟踪UCI状态,以确保在正确的上下文中执行命令。 如果客户端尝试在引擎处理命令之前执行命令,则会返回HTTP状态错误
  4. For those cases where there is no expected response from UCI, the GraphQL resolver will just acknowledge the command was received.

    对于那些没有预期的UCI响应的情况,GraphQL解析器将仅确认已收到命令。
  5. The determinate case for Category 3 (where there’s a sure and quick response) can be handled by HTTP.

    可以通过HTTP处理类别3(确定可靠且响应Swift)的确定情况。
  6. The indeterminate case, where there are intermediate responses before termination, can be handled via web socket. This, in turn, can be wrapped in a GraphpQL subscription service.

    不确定的情况(在终止之前会有中间响应)可以通过Web套接字处理。 这又可以包装在GraphpQL订阅服务中。

The mock implementation pretty much covered the essentials, but this short analysis provides a blueprint for going forward with an implementation.

模拟实现几乎涵盖了要点,但是这段简短的分析为实现实现提供了一个蓝图。

Code for this article can be found here.

可以在此处找到本文的代码。

翻译自: https://www.freecodecamp.org/news/wrapping-an-streaming-i-o-interface-in-graphql-931650dafd3b/

graphql

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值