微服务查询语言 - restQL 初识

原创作者:李贞

restQL is a microservice query language that makes easy to fetch information from multiple services in the most efficient manner.  http://restql.b2w.io/

介 绍

restQL的设计是用来简化客户端查询,特别是在微服务时代,对于一个复杂的页面,可能需要调用多个服务的接口,获得数据渲染页面。这对于客户端来说,这有着极大的消耗。而 restQL较好的解决的这个问题,并且使用restQL对原来API server无需改动。

restQL支持以下几种查询方式。

并行调用(Parallel calls)

他们所产生的查询,是并发进行的,等待所有的处理完成后将直接返回。

from hero

with

    name = "Restman"



from villain

with

    name = "SOAPLord"

链式调用(Chained invocations)

第二个查询依赖第一个查询的结果,所以需要等待第一个查询结束后,才会调用第二个查询,最终返回结果。

from hero

with

    name = "Restman"



from sidekick

with

    hero = hero.id

Multiplexed invocations

这个同样是第一个查询依赖第二个查询,但是第一个查询出来的是一个array,并对于array中的每个元素,调用第二个查询,处理完成后返回。

from search

with

    role = "hero"



from hero as heroes

with

    name = search.results.name

详细介绍见: http://restql.b2w.io/

安 装

restQL依赖一个restQL-http对外提供服务,由restQL-http解析restQL的查询语言并且调用资源服务(API server)

下载restQL-http到任意目录,并且解压,这里安装的是:

## 下载

curl -O https://github.com/B2W-BIT/restQL-http/releases/download/v3.5.5/restql-http-v3.5.5.zip

解压

unzip -d restql-http restql-http-v3.5.4.zip

运行

cd restql-http

planets=https://swapi.co/api/planets/:id ./bin/run.sh

入 门

对于入门最好的方式,莫过于官方文档了( http://docs.restql.b2w.io/#/restql/getting-started )。

官网是以swapi.co的API作为测试,这里也将使用这个服务作为简单的入门使用。如果需要配置更高级的使用方式( http://docs.restql.b2w.io/#/restql/config )。

运行

cd restql-http

planets=https://swapi.co/api/planets/:id ./bin/run.sh

启动成功后,将会默认监听9000端口, http://localhost:9000 ,也可以通过设置环境变量的方式,使其端口改变。

export PORT=1456

planets=https://swapi.co/api/planets/:id ./bin/run.sh

restQL提供的接口路径是 : http://localhost:9000/run-query

可以通过curl方式调用服务,检查是否启动正常

curl -d "from planets as allPlanets" -H "Content-Type: text/plain" localhost:9000/run-query

正常情况下将会得到类似以下的输出

{

"allPlanets": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": ...

}

}

查询一条数据

restQL 查询语句

from planets

with id = 1

调用接口,这里推荐使用Postman、Insomnia等工具测试

curl -d "from planets with id = 1" -H "Content-Type: text/plain" localhost:9000/run-query

将会得到以下数据

{

"planets": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": {

        "surface_water": "1",

        "climate": "arid",

        "residents": [

            ...

        ],

        "orbital_period": "304",

        "name": "Tatooine",

        "diameter": "10465",

        "created": "2014-12-09T13:50:49.641000Z",

        "gravity": "1 standard",

        "edited": "2014-12-21T20:48:04.175778Z",

        "films": [

            ...

        ],

        "population": "200000",

        "terrain": "desert",

        "url": "https://swapi.co/api/planets/1/",

        "rotation_period": "23"

    }

}

}

查询语言

语法

查询语句的顺序,对于 restQL 异常的重要

[ [ use modifier = value ] ]



METHOD resource-name [as some-alias] [in some-resource]

[ headers HEADERS ]

[ timeout INTEGER_VALUE ]

[ with WITH_CLAUSES ]

[ [only FILTERS] OR [hidden] ]

[ [ignore-errors] ]

Methods

restQL 提供了HTTP常用的几个 methods (GET,POST,PUT, PATCH, DELETE)

from - HTTP GET



to - HTTP POST



into - HTTP PUT



update - HTTP PATCH



delete - HTTP DELETE

from

planets = https://swapi.co/api/planets/:id

restQL 语句

from planets

with

    name = "Restman"

    id = 1

将会映射到以下调用

GET https://swapi.co/api/planets/1?name=Restman

to

planets = https://swapi.co/api/planets

restQL 语句

to planets

with

    name = "Restman"

将会映射到以下调用

POST https://swapi.co/api/planets

BODY {"name": "Restman"

}

into

planets = https://swapi.co/api/planets/:id

restQL 语句

into planets

with

    id = 1

    name = "xxxx"

将会映射到以下调用

PUT https://swapi.co/api/planets/1

BODY {"name": "xxxx"

}

update

planets = https://swapi.co/api/planets/:id

restQL 语句

update planets

with

    id = 1

    name = "abc"

将会映射到以下调用

PATCH https://swapi.co/api/planets/1

BODY {"name": "abc"

}

delete

planets = https://swapi.co/api/planets/:id

restQL 语句

delete planets

with

    id = 1

将会映射到以下调用

DELETE https://swapi.co/api/planets/1?name=Restman

实 践

这里将以  https://api.spacexdata.com/ 作为数据服务,接下来的所有数据,都是由它来提供。( https://docs.spacexdata.com/?version=latest

restQL 支持多种资源映射方式,可以通过环境变量,也可以通过restql.yml的方式亦或者通过restQL-manager( http://docs.restql.b2w.io/#/restql/manager )前面演示了通过环境变量的方式,这里将通过restql.yml,在restql-http目录创建restql.yml文件。

restql.yml

mappings:

launches: "https://api.spacexdata.com/v3/launches"

执行

./bin/run.sh

启动成功后,将可以看到类似这样的输出

2019-11-08 14:46:03,239 INFO restql.http.plugin.core [main] No plugins to load

2019-11-08 14:46:03,244 INFO restql.http.server.core [main] Server starting with {:port 1456, :executor-utilization 0.5, :executor-max-threads 512, :executor-control-period 1000}

2019-11-08 14:46:03,465 INFO restql.http.server.core [main] Server started!

简单查询

restql 语句

from launches

查询结果

{

"launches": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": [...]

}

}

如果想要查询 2017-06-22 到 2017-06-25 之间的数据,只需要给查询语句加上 with

from launches

with

    start = "2017-06-22"

    end = "2017-06-25"

其他参数也按照这种方式。如果需要指定的获取某一条数据,调用的接口是

https://api.spacexdata.com/v3/launches/{{flight_number

}}

可以添加一条资源映射规则写入到restql.yml,

mappings:

launches: "https://api.spacexdata.com/v3/launches"

oneLaunches: "https://api.spacexdata.com/v3/launches/:flight_number"

例如查询的是 42

from oneLaunches

with

    flight_number = 42

响应结果

{

"oneLaunches": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": {

        ...

        "flight_number": 42,

        ...

    }

}

}

过滤器(Filter)

restQL还支持一个filter特性,可以指定某个资源获取的数据,这样可以减少payload大小,对于移动客户端优化非常重要。

这里以 launches 作为演示。

from launches

with

    start = "2017-06-22"

    end = "2017-06-25"

only

    mission_name,

    flight_number,

    launch_site.site_id,

    ships,

    rocket.first_stage.cores.land_success

响应结果

{

"launches": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": [

        {

            "mission_name": "BulgariaSat-1",

            "flight_number": 42,

            "launch_site": {

                "site_id": "ksc_lc_39a"

            },

            "ships": [

                "ELSBETH3",

                "GOQUEST",

                "GOSEARCHER",

                "OCISLY"

            ],

            "rocket": {

                "first_stage": {

                    "cores": [

                        {

                            "land_success": true

                        }

                    ]

                }

            }

        }

    ]

}

}

并行查询

并行调用,正是restQL需要处理的问题,能够有效减少客户端和服务器的连接数,对于微服务架构而言,可能一个页面所呈现需要的数个服务来提供。这就意味着需要创建数个连接获取资源,这对于客户端来讲,是非常糟糕的。

这里添加一个新的资源映射地址,作为一个测试

mappings:

launches: "https://api.spacexdata.com/v3/launches"

oneLaunches: "https://api.spacexdata.com/v3/launches/:flight_number"

oneHistory: "https://api.spacexdata.com/v3/history/:id"
from launches

with

    start = "2017-06-22"

    end = "2017-06-25"

only

    mission_name,

    flight_number,

    launch_site.site_id,

    ships,

    rocket.first_stage.cores.land_success

from oneHistory

with

    id = 1
{

"launches": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": [...]

},

"oneHistory": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": {

        "event_date_unix": 1222643700,

        "title": "Falcon 1 Makes History",

        "details": "Falcon 1 becomes the first privately developed liquid fuel rocket to reach Earth orbit.",

        "id": 1,

        "event_date_utc": "2008-09-28T23:15:00Z",

        "flight_number": 4,

        "links": {

            "wikipedia": "https://en.wikipedia.org/wiki/Falcon_1",

            "article": "http://www.spacex.com/news/2013/02/11/flight-4-launch-update-0",

            "reddit": null

        }

    }

}

}

链式查询

链式调用也是restQL的一个亮点,也简单做一下演示

from oneHistory

with

    id = 1

from oneLaunches

with

    flight_number = oneHistory.flight_number

only

    mission_name,

    flight_number,

    launch_site.site_id,

    rocket.first_stage.cores.land_success

响应结果

{

"oneHistory": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": {

        ...

        "flight_number": 4,

        ...

    }

},

"oneLaunches": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": {

        "mission_name": "RatSat",

        "flight_number": 4,

        "launch_site": {

            "site_id": "kwajalein_atoll"

        },

        "rocket": {

            "first_stage": {

                "cores": [

                    {}

                ]

            }

        }

    }

}

}

这个的应用场景也是极为广泛的,特别在于页面信息极其复杂的情况下,在一次请求就可以获取到关联的数据。

多路调用

为了避免歧义,这里将添加一个新的映射地址,作为本次演示,编辑 restql.yaml文件

mappings:

launches: "https://api.spacexdata.com/v3/launches"

oneLaunches: "https://api.spacexdata.com/v3/launches/:flight_number"

history: "https://api.spacexdata.com/v3/history"

oneHistory: "https://api.spacexdata.com/v3/history/:id"
from history

with

    start = "2016-06-22"

    end = "2017-06-25"

only

    flight_number

from oneLaunches

with

    flight_number = history.flight_number

only

    mission_name,

    flight_number,

响应结果

{

"history": {

    "details": {

        "success": true,

        "status": 200,

        "metadata": {}

    },

    "result": [

        {

            "flight_number": 38

        },

        {

            "flight_number": 41

        }

    ]

},

"oneLaunches": {

    "details": [

        {

            "success": true,

            "status": 200,

            "metadata": {}

        },

        {

            "success": true,

            "status": 200,

            "metadata": {}

        }

    ],

    "result": [

        {

            "mission_name": "SES-10",

            "flight_number": 38

        },

        {

            "mission_name": "CRS-11",

            "flight_number": 41

        }

    ]

}

}

完 结

restQL 还有几个高级特性,详情请见官网

http://docs.restql.b2w.io/#/restql/manager

http://docs.restql.b2w.io/#/restql/saved-queries

http://docs.restql.b2w.io/#/restql/plugins

参考链接

http://restql.b2w.io/

https://medium.com/b2w-enginee ... 09b40

https://github.com/B2W-BIT/restQL-http

原文链接: https://mp.weixin.qq.com/s/4fF19jc4Dgs92Q67MwN-Lw

关于睿云智合

深圳睿云智合科技有限公司成立于2012年,总部位于深圳,并分别在成都、深圳设立了研发中心,北京、上海设立了分支机构,核心骨干人员全部为来自金融、科技行业知名企业资深业务专家、技术专家。早期专注于为中国金融保险等大型企业提供创新技术、电子商务、CRM等领域专业咨询服务。

自2016年始,在率先将容器技术引进到中国保险行业客户后,公司组建了专业的容器技术产品研发和实施服务团队,旨在帮助中国金融行业客户将容器创新技术应用于企业信息技术支持业务发展的基础能力改善与提升,成为中国金融保险行业容器技术服务领导品牌。

此外,凭借多年来在呼叫中心领域的业务经验与技术积累,睿云智合率先在业界推出基于开源软交换平台FreeSwitch的微服务架构多媒体数字化业务平台,将语音、视频、webchat、微信、微博等多种客户接触渠道集成,实现客户统一接入、精准识别、智能路由的CRM策略,并以容器化治理来支持平台的全应用生命周期管理,显著提升了数字化业务处理的灵活、高效、弹性、稳定等特性,为帮助传统企业向“以客户为中心”的数字化业务转型提供完美的一站式整体解决方案。

我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章