车联网服务non-RESTful架构改造实践
导读
在构建面向企业项目、多端的内容聚合类在线服务API设计的过程中,由于其定制特点,采用常规的restful开发模式,通常会导致大量雷同API重复开发的窘境,本文介绍一种GraphQL查询语言+网关编排联合的实践,解决大量重复定制的问题。
早期与车厂合作过程中,基于高德已有的数据、引擎能力和一些较为重要的相关CP服务(如停车场、加油站、天气等),形成的在线服务协作模式是针对客户需求,采用REST API提供针对每个车厂、每个项目以及每个终端提供不同的API实现,然而数据核心独立服务实际上就有十余种,然而由于车线业务维护周期长,定制多,2-3年下来,API规模已达几百个,而且持续发散级增长,这给持续开发和维护带来不小挑战。
分解业务开发过程,无非两类工作,业务需求能力数据的获取和非业务诉求但是必不可少的如鉴权等通用化能力,当前来看,其实这两个问题是几乎所有业务团队都会遇到的问题,因此解决方案也基本类似,如服务聚合、流程编排、API网关等。
本文简要介绍下车联网在线服务改造旧架构的一些实践。
有关名词
GraphQL:GraphQL既是一种用于API的查询语言也是一个满足数据查询的运行时。GraphQL对API中的数据提供了一套易于理解的完整描述,使得客户端能够准确地获得它需要的数据,而且没有任何冗余,也让API更容易地随着时间推移而演进,还能用于构建强大的开发者工具。
DSL:指的是专注于某个应用程序领域的计算机语言。又译作领域专用语言。不同于普通的跨领域通用计算机语言(GPL),领域特定语言只用在某些特定的领域。 比如用来显示网页的HTML,以及Emacs所使用的Emac LISP语言。
API网关:API网关是一个服务器,是系统的唯一入口。从面向对象设计的角度看,它与外观模式类似。API网关封装了系统内部架构,为每个客户端提供一个定制的API。它可能还具有其它职责,如身份验证、监控、负载均衡、缓存、请求分片与管理、静态响应处理。
存在的问题
车线业务在线服务旧架构如下:

面临以下问题:

改进
针对上述问题,主要从以下几个方面思考改进:
服务能力原子化:目标是做稳,让上层通过组合实现业务需求;
构建查询引擎:支持强大的查询组合能力,实现原子服务能力任意聚合和定制;
API网关:对非业务数据能力需求进行抽象提供插件,实现插件编排。
下面分别介绍。
实现稳定、独立演进的原子能力服务
对已有的服务进行梳理,抽象出不同应该独立开发、部署演进的核心能力,对于引擎能力没有什么工作,重点是对于一些历史对接的外部CP,主要实现以下目标:
向上提供稳定接口,向下屏蔽底层复杂性(数据访问,多源差异);
以位置为中心有机整合,构建完备原子化能力集合。
这部分工作主要是解决历史遗留的一些服务组合不合理,跟随业务过度定制的问题。
定制代码开发转换为定义查询语句
这里主要目的就是将服务聚合、定制逻辑等原来需要的代码开发转换为编写查询语言的方式实现,只需要编写出声明式的查询语句即完成服务发布,特性如下:
向上提供标准化查询语言
向下实现原子能力组合
归纳业务共性,提炼定制模式,提升复用

本文选择GraphQL作为查询语言基础,然而,直接采用GraphQL有这样两个主要问题需要解决:
数据查询N+1放大问题,直接采用Fackbook提出的dataloader来解决,原理是批量加缓存;
GraphQL规范限制,一些定制难以实现,如:
入参定制:如参数关联,类型转换等;
输出格式:字段展现形式,如时间、经纬度等;
配置表定制:主要是部分业务逻辑需要根据配置表定制,如深度返回字段等;
模型连接:原子能力服务尽可能独立,同时也无法枚举定义模型关系,但是定制业务需求需要大量关联透出,减少业务请求降低延时,所以模型自由关联能力是必要的,由于本方案最终的查询控制在内部,对外暴露REST API,因此不会关联自由度造成的难理解性并不是一个问题。
需要通过嵌入简单的DSL实现:
内置和自定义函数功能;
模型动态关联查询,上下文参数获取;
可以方便扩展自定义函数。

这里嵌入DSL需要控制好度,因为DSL如果过于复杂,那么,使用者或者发布者无法快速写出查询的话,对比写代码提效就会打折扣,偏离本来的价值,所以基本原则是简单、可扩展。
业务无关功能通过API网关插件配置化
由于之前每个API的定制开发基本所有功能混合在一起,能复用部分就是鉴权提供装饰器,常规性的响应格式定制提供一些工具函数,任何需求变更都需要变更代码,走发布流程,有了上面第一步的改造,这个步骤期望将非业务数据部分的定制功能抽象出处理链,每个处理节点提供多实现(包含通用和定制),通过数据库存储插件链实现编排。

车线业务由于鉴权方式需要根据客户定制,因此存在多样性,实现上是通过Web中间件实现多种鉴权插件:
HTTP签名,参考这里:主要面向ToB(车厂后台、合作方)的请求;
JWT认证:主要面向车机、手机等终端;
API Key。
对于API网关来说,这些鉴权插件并没有什么不同之处,只是工程要处理一些定制场景,比如对于不同车厂的JWK管理刷新策略,JWT验证策略等,具体需要根据业务诉求抽象建模,通过插件属性来实现配置控制。
另外,网关还实现了一些变换器,主要用于将GraphQL的输出变换为REST API接口透出,这一方面由于一些旧接口要做兼容支持,另外,一些重点客户的全球化架构背景下自己已经完全定义好了接口式样,目前主要实现了:
入参变换:使用REST API参数填充GraphQL查询模板;
Header变换:主要用于适配不同客户规范;
JSON变换,使用场景如下:
可复用标准接口,但是不同客户的响应结构规范不一致
定制非标接口,需要对GraphQL输出进行转换
而插件的使用则通过控制台或API实现将插件配置信息存储于数据库中进行管理,使用时根据请求特征从DB中提取并缓存起来使用。
改造后的新架构如下:

小结
通过上述改造,将车联网在线服务开发模式进行了升级,实现API控制台动态发布,大幅提升定制开发效率:
- 提效开发:正交化原子能力编排,通过轻量级定义取代定制化代码开发:
定制化开发占比下降60%;
单接口开发从2-3人日→2-3人时。
- 协议兼容:混合REST方案,对外提供标准协议、支持既有适配协议。

关注高德技术,找到更多出行技术领域专业内容
车联网服务non-RESTful架构改造实践的更多相关文章
- 第三模块 :微服务网关Zuul架构和实践
52.Zuul网关架构剖析~1.mp4 请求过来之后首先会通过前置过滤器,然后到路由过滤器,路由过滤器是真正访问后台远程服务的,经过路由器之后,最后会传递给后置过滤器 在三个过滤器处理的过程中任何一个 ...
- Nodejs RESTFul架构实践之api篇(转)
why token based auth? 此段摘自 http://zhuanlan.zhihu.com/FrontendMagazine/19920223 英文原文 http://code.tuts ...
- 基于 springMVC 的 RESTful HTTP API 实践(服务端)
理解 REST REST(Representational State Transfer),中文翻译叫"表述性状态转移".是 Roy Thomas Fielding 在他2000年 ...
- vivo 服务端监控架构设计与实践
一.业务背景 当今时代处在信息大爆发的时代,信息借助互联网的潮流在全球自由的流动,产生了各式各样的平台系统和软件系统,越来越多的业务也会导致系统的复杂性. 当核心业务出现了问题影响用户体验,开发人员没 ...
- 基于AWS的云服务架构最佳实践
ZZ from: http://blog.csdn.net/wireless_com/article/details/43305701 近年来,对于打造高度可扩展的应用程序,软件架构师们挖掘了若干相关 ...
- SpringBoot系列十一:SpringBoot整合Restful架构(使用 RestTemplate 模版实现 Rest 服务调用、Swagger 集成、动态修改日志级别)
声明:本文来源于MLDN培训视频的课堂笔记,写在这里只是为了方便查阅. 1.概念:SpringBoot整合Restful架构 2.背景 Spring 与 Restful 整合才是微架构的核心,虽然在整 ...
- RESTful 架构与 RESTful 服务
风格 ⇒ 标准 ⇒ 协议 ⇒ 实现 Representational State Transfer,是一种软件架构风格,既然是风格,就非标准或协议,而是定义了一组设计原则和约束条件.具有如下特点: 适 ...
- 用于构建 RESTful Web 服务的多层架构
作者:Bruce Sun, Java 架构师, IBM 出处:http://www.ibm.com/developerworks/cn/web/wa-aj-multitier/ 用于构建 RESTfu ...
- 【EBook】-NO.161.微服务.1 -【微服务架构与实践】
Style:Mac Series:Java Since:2018-09-10 End:2018-09-10 Total Hours:1 Degree Of Diffculty:5 Degree Of ...
随机推荐
- [小米OJ] 3. 大数相减
题目链接 思路: 利用两个string保存相减的数,其他模拟即可. 参考了别人的一个处理减的步骤,很简洁好看. string substract(string str1, string str2) { ...
- iOS 类知乎”分页”效果的实现?
我们先看张gif图看一下效果(LICEcap录制的有点卡, 凑合看) 好像还是卡, 怼个视频演示链接吧: https://m.weibo.cn/1990517135/4398431764047996 ...
- .net持续集成sonarqube篇之 sonarqube集成单元测试
系列目录 通过前面章节我们可以看到,不论怎么构建,单元测试覆盖率一栏总是0% 这是因为单元测试覆盖率报告需要额外集成.这一节我们就讲解如何在sonarqube里集成单元测试覆盖率报告. 这里需要借助O ...
- 《Scalable IO in Java》译文
<Scalable IO in Java> 是java.util.concurrent包的作者,大师Doug Lea关于分析与构建可伸缩的高性能IO服务的一篇经典文章,在文章中Doug L ...
- python基础——字符串(string)
字符串是 Python 中最常用的数据类型.我们可以使用引号('或")来创建字符串. 创建字符串很简单,只要为变量分配一个值即可. str1 = 'python' str2 = " ...
- PHP与ECMAScript_7_流程控制
PHP ECMAScript 顺序结构 默认从上到下依次执行 默认从上到下依次执行 分支结构 if / switch if / switch 循环结构 for / while / do-w ...
- jQuery通过id和name获取值的区别
$(#'id').函数 $("input[name='name']")
- 关于HTML的引入CSS文件问题
一 html代码引用外部css文件时若css文件在本文件的父目录下的其他目录下,可使用绝对路径.此时路径要写为 “ ../ ”形式,如在tomcat下建立一个test文件,在该文件中建立两个文件 夹 ...
- spring-boot-plus集成Spring Boot Admin管理和监控应用
Spring Boot Admin Spring Boot Admin用来管理和监控Spring Boot应用程序 应用程序向我们的Spring Boot Admin Client注册(通过HTTP) ...
- 常用GDB命令行调试命令
po po是print-object的简写,可用来打印所有NSObject对象.使用举例如下: (gdb) po self <LauncherViewController: 0x552c570& ...