rest-api与graphql在tango-control Web Applications中的应用

2024/9/26

一、概述

近十年来出现了通过web通用应用监测和控制tango系统中设备的研究工作,主要产生了两种技术路线,分别基于rest-apigraphqlrest-apigraphql是在web领域前后端分离开发中产生的产物,主要作用是作为前后端交互的桥梁,起到类似于tango系统中CAORB的作用。不管是哪一种技术路线,整体的系统架构可以概括为如下所示:

wa

整体的系统主要分为三个模块,一是tango-control-system,二是Web Server这个中间件,三是Web Client。tango-control-system承载设备;web-client是与用户交互的模块,用户可通过该模块监听和操作tango中的设备;而web-server则负责接收来自web-client的请求并解析该请求,再通过CAORB将该请求所对应的操作执行到tango系统中。

接下来,我将依次介绍rest-apigraphql以及两者所对应的已有的成熟tango-control Web Application


二、rest-api

2.1 rest-api基本概念

REST的全称是Representational State Transfer,中文为“表现层状态转化”。表现层其实指的是资源,对应到tango系统中就是device、database的attribute等信息。在web领域,我们利用URI(统一资源定位符)来定位资源,每种资源对应一个特定的URI。在前后端交互的过程中,必然涉及数据和状态的变化,而HTTP协议,是一个无状态协议。这意味着,所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生"状态转化"(State Transfer)。而这种转化是建立在表现层之上的,所以就是"表现层状态转化"。而客户端可以利用的手段就是通过HTTP中表示操作动作的四个动词GET、POST、PUT、DELETE来对服务器端的资源进行操作。

总结一下,rest-api定义了一系列api,前后端的开发都需要遵循这个api。基于此,客户端可以通过URI来定位服务器端的一系列“资源”,并且通过四个HTTP动词对服务器端的资源进行操作。

以访问sys/tg_test/1设备的ampli这个attribute为例,该资源所对应的URI为:

1
http://localhost:8080/tango/rest/v11/hosts/localhost/devices/sys/tg_test/1/attributes/ampli/value

客户端请求该URI返回的数据如下:

rr

如果是需要更改该value,则对应请求如下:(以curl来请求):

1
curl -X put http://localhost:8080/tango/rest/v11/hosts/localhost/devices/sys/tg_test/1/attributes/ampli/value?v=80

2.2 基于rest-api实现的rest-server

在gitlab上的rest-api仓库实际是rest-api版本迭代的文档仓库,具体的rest-server有一些实验室自己实现了,gitlab上给的参考有以下几个版本:

rs

c++版本和python版本都已经很老了且不做维护,因此以java版本为例。

java版本的rest-server是利用javax实现了一个servlet,依托Tomcat来部署和进行负载均衡、安全防护等工作,利用Jtango的tangorbtango-idl-java库来与tango系统通信,严格按照rest-api v1.1版本来处理网络请求,同时利用SSE(Server-Sent Events)来实现subscribe。

rs

SSE 与 WebSocket 作用相似,都是建立浏览器与服务器之间的通信渠道,然后服务器向浏览器推送
信息。但是SSE 是单向通道,只能服务器向浏览器发送。SSE发送的不是一次性的数据包,而是一个数据流,会连续不断地发送过来。这时,客户端不会关闭连接,会一直等着服务器发过来的新的数据流,视频播放就是这样的例子。本质上,这种通信就是以流信息的方式,完成一次用时很长的下载。

2.3 waltz

waltz是HZG实验室的一款基于rest-api的web应用,如下所示:

waltz主要有两个方面的功能,一个是类似于jive对设备的检测和控制;二是Dashboard,有着plot、table、list三种组件,可以选择不同属性组合到一起来监测。

waltz的实现主要依托于webix这个JS UI库,图表使用plotly.js进行绘制,项目利用webpack打包后压缩成.war文件,利用tomcat进行部署访问。


三、graphql

3.1 graphql基本概念

graphql是一种用于 API 的查询语言,是一个使用基于类型系统来执行查询的服务端运行时(类型系统由自己的数据定义。graphql全称为graph query language,为什么说是graph,则在于其依托的类型系统的结构是一个图的结构,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
schema {
query: Query
mutation: Mutations
subscription: Subscription
}

type Query {
version: String
info: String
devices(pattern: String): [Device]
device(name: String!): Device
domains(pattern: String): [Domain]
families(domain: String, pattern: String): [Family]
members(domain: String, family: String, pattern: String): [Member]
servers(pattern: String): [Server]
instances(server: String, pattern: String): [ServerInstance]
classes(pattern: String): [DeviceClass]
attributes(fullNames: [String]!): [DeviceAttribute]
commands(fullNames: [String]!): [DeviceCommand]
}

type DeviceAttribute {
name: String
device: String
datatype: String
dataformat: String
format: String
writable: String
label: String
unit: String
description: String
value: ScalarTypes
...
}
...

还是以查询sys/tg_test/1设备的ampli这个attribute为例

graghql与rest相比最大的特点有两个,一是请求数据更少更精准,二是可以合并请求,以下面这个例子为例:

同时查询两个设备实例的属性以及property,我们只需要一次网络请求,并且想要的数据也是我们指定所需要的,而rest的话至少需要发起三次请求,并且返回的数据是固定且冗余的

1
2
3
http://localhost:8080/tango/rest/v11/hosts/localhost/devices/sys/tg_test/1/attributes/ampli/value
http://localhost:8080/tango/rest/v11/hosts/localhost/devices/sys/tg_test/2/attributes/ampli/value
http://localhost:8080/tango/rest/v11/hosts/localhost/devices/sys/tg_test/1/properties

tangogql的具体实现就不赘述了,可以看另外一个文档《TangoGQL的技术验证》

接下来讲一下两个比较重要的库graphql.jsgraphql-ws.js,以及一款基于tangogql的web应用taranta

3.2 graphql.js

graphql.js为 GraphQL 规范提供了一个参考实现,它包含了用于定义和执行 GraphQL 操作的核心库。可以使用它在 Node.js 服务器上构建 GraphQL 服务,或者在客户端发起 GraphQL 查询。

如下图所示,graphql.js所提供的功能主要有以下四种:

graphql.js在服务器端的表现很强大,构建类型系统,解析、验证、执行graphql语句都可以依靠它。在客户端层面,其主要应用场景是作为apollo-client(react)、relay、vue-apollo等构建graphql客户端工具的依赖使用。相比于自己利用原生 JavaScript Fetch API 进行graphql请求,利用上述工具有助于帮助我们构建查询、处理响应,更好的与前端框架进行互动。

以vue-apollo为例,依托于graphql.js的支持,我们可以更方便的发起query请求,帮助我们构建完整的查询语句,同时提供了客户端缓冲、防抖、节流、错误处理等特性,还将结果和可能的错误包装成易于使用的钩子函数配合框架进行使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
const input = ref(`query {
device(name:"sys/tg_test/2") {
read_attributes(names:["ampli"]) {
name
value
data_format
}
}
}`);

const options = ref({
fetchPolicy: cache-and-network,
// debounce、enabled、errorPolicy.....
});
const { result, error, refetch } = useQuery(()=>{gql`${input.value}`},null,options);
onResult(result => {
output.value = result.value;
})
onError(error => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error);
}
})

3.3 graphql-ws

graphql-ws是依托于websocket协议来实现graphql中订阅功能的协议,其主要工作流程如下图所示:

gws

首先客户端与服务器之间通过connection_initconnection_ack完成握手,之后客户端发送一个subscribe(旧版为start)类型的报文,该报文会携带一个唯一的id以及graphql语句。服务器收到该subscribe报文后,会解析其中的graphql语句并订阅相应的事件,当事件发生时,服务器会返回next类型的报文,该报文携带着之前唯一的id以及返回的数据。直到任意一方提出终止连接则订阅结束。

测试效果如下:

taranta项目中,对于动态变化的图像,其使用subscribe来实现该功能:

而在基于rest-api的waltz中,其使用轮询网络请求的方式,可以看到网络请求的负担明显更重:

3.4 taranta

taranta是MAX IV实验室的产品,主要使用react框架以及apollo-client等库来实现,如下所示:

Taranta具有和waltz类似的两个功能,一是类似于jive对设备的监控与操作、二是dashboard,有着许多小组件可供用户使用,方便自定义对于设备不同属性的监测。

Taranta还有一个功能名为Synoptics。Synopitcs是一个控制系统的可控制概要图,为用户提供可视且灵活的操作。一个Synopitcs图有许多svg小组件,每个svg都可以对应一个device进行相应的控制和监测,不同的颜色也代表着设备不同的state,比如下图中(文档中的例子)蓝色表示RUNNING state,绿色表示ON state

四、测试

使用webbench模拟高并发的场景对TangoGQL(cpp)、rest-server、tangogql(pytango)、tangogql(ariadne)进行压力测试

结果表现
TangoGQL(cpp)在高于1000用户的情况下就会崩溃,由于测试用的虚拟机最多开4500个用户,不能测出剩余3个server的上限,但从结果可以看出,抗压水平从高到低依次为:rest-server > tangogql(pytango) > tangogql(ariadne)

下图测试结果中,由于TangoGQL(cpp)服务器端在http发送完成后不会主动断开连接,所以webbench不能识别成功与失败的包,但从流量可以看出性能并不好。