零散专题27 BFF调研1

BFF调研报告。

前端开发中存在的难问题

  1. 多端应用,不同类型客户端对数据、API有个性化的需求
  2. 服务聚合,单一后端为多个前端团队提供接口,导致跨团队协作低效,资源协调困难

问题:服务端设计的接口究竟是面向UI,还是面向通用服务

BFF

解决方案: Backends For Frontends, 简称BFF。

BFF最适合的场景,为第三方提供定制API等差异化场景,每个用户体验(客户端)对应一个后端,

v2-50720e139878624f394dcf2d3de836fb_hd.jpg

BFF作为用户体验适配层,对后端接口进行组合、处理,对数据进行:裁剪、格式化、聚合、编排。

BFF理念中,最重要的一点是:服务自治,谁使用谁开发,所以一般由前端维护。

BFF实现不限制具体技术,可以自由选型:Java/Node/PHP/Python,但大部分前端团队都会选择Node.js。

BFF的演化历程

BFF和网关(API Gateway)是微服务架构中两个重要概念,可以通过下面的例子讲解BFF的出现过程

服务化架构V1

实现单块应用的解构拆分,微服务初步完成,前端用户体验层主要是传统的服务端Web应用,服务化架构如下所示:

service4.png

服务化架构V2

随着无线应用的流行,除了Web应用外,服务还需要为新的无线原生App来提供接口和数据,先采用下面的服务架构:

service5.png

这样的架构的问题是:

  1. 无线App与内部为微服务强耦合
  2. 无线App需要知道内部服务的地址等细节
  3. 无线App需要对接口数据进行大量的聚合剪裁和适配逻辑

服务化架构V2.1

为了解决这个问题,在用户体验层和内部微服务层之间引入了BFF层,将后端的微服务进行适配,想用户体验层暴露有号和统一的API,方便无线设备接入访问后端服务

service.png

这种架构解决了上面的问题:

  1. 无线App与内部为微服务解耦,两边可以独立变化
  2. 无线App只需要知道BFF的地址,并且服务接口统一,不需要关心内部复杂的微服务的地址和细节
  3. 无线App需要不再需要聚合剪裁和适配逻辑,这些步骤都在BFF完成

但是随着接入设备的增加,也会有一些问题:

  1. BFF只有一个,接入设备增加后导致要考虑匹配问题,团队之间沟通协调成本高
  2. BFF中不仅包括了各个业务线的聚合剪裁、适配和业务逻辑,还需要引入很多跨横切面逻辑,比如安全认证、日志监控、限流熔断等,这些代码混在一起,导致代码复杂度提高
  3. BFF如果出现错误,会导致所有应用都不可用

微服务架构V3

为了解决这个问题,引入API Gateway

serveice2.png

在这种架构下:

  1. BFF按团队和业务线进行解耦拆分,拆分成若干个BFF微服务,每个业务线并行开发和交付各自负责的BFF微服务
  2. 网关,一般由独立的团队负责运维,专注跨横切面的功能,包括
    • 路由,将来自无线设备的请求路由到后端的某个微服务BFF集群
    • 认证,对涉及敏感数据的API访问进行集中认证鉴权
    • 监控,对API调用进行性能监控
    • 限流熔断,网关能够进行限流熔断,保护后端服务
    • 安全防爬,收集访问日志,通过后台分析恶意行为,阻断恶意请求
  3. 网关在客户端与BFF之间又引入了一层,让两边可以独立变化

微服务架构V4

可以对V3架构进行优化,将传统的服务端Web应用模式改为前后分离架构,前端采用H5单页技术提供给用户更好体验。

同时增加了下手是哪个第三方应用开放API的能力

service3.png

拓展新的接入渠道,形成了一个完成的现代微服务架构,从外到内依次为:端用户体验层->网关层->BFF层->微服务层

具体实现

细分力度

要求把一个大后端拆分为多个小后端,有三种方式

  • 用户体验级(UI)
  • 端级
  • 团队级

一般建议按照用户体验级来拆分

对接下游服务(微服务)

(1)如何对接多个技术栈不同的下游服务

RPC协议调用

(2)如何管理、组合调用

借助RXJava、Finagle等事件机制来简化这些异步流程控制

(3)某个调用失败时,如何保障可用性

在BFF层容错,同时前端保证可接受不完整相应内容

复用问题

拆分后,多个BFF间会产生冗余代码,这是不可避免的。

可以添加API网关层,将通用的后端逻辑(授权、认证、限流)放进去

想消除冗余,又不想因为抽离可复用代码而导致BFF间紧耦合,所以就有了一种折衷的态度:容忍BFF间冗余、消除单BFF内冗余。也就是允许一定程度的BFF间冗余

蚂蚁财富的BFF实践

2016年时,汤尧对BFF在蚂蚁财富项目中的落地实践进行了介绍。

在引入BFF之前,蚂蚁财富的业务也遇到了上面提到的问题,主要是体验层API经常变化,导致开发效率低下。

于是引入了BFF,主要的目的是对数据进行:裁剪、格式化、聚合、编排。

一个理想的模型如下:

BFF-11.png

在实际落地过程中,形成了如下的架构:

BFF-1.png

他们在实战过程中,主要解决了一下几个问题:

(1)Node.js与Java通信

  • 数据:跨语言序列化协议hessian
  • 服务:Node弱类型,Java强类型,如何调用

BFF-2.png

这个调用过程一般是通过RPC协议完成的,而非通过HTTP协议。

由于我对Java没有接触,而且资料只是他当时的PPT,没有详细的讲解,没有办法理解具体是如何实现的。而且恐怕对于大多数前端开发工程师来说,与Java的通信也是BFF落地过程中一个比较严峻的挑战,而且这是在前端开发人员有比较好的Node开发能力的基础上。

(2)多App适配

在API网关层处理多个App的通用的逻辑,比如错误码管理、数据一致性、免登、业务日志等。

(3)聚合

BFF-3.png

  • 传给客户端需要的数据,简化客户端逻辑,减少网络开销
  • 避免无意义的透传
  • 敏感信息过滤

(4)接口设计准则

基础服务接口:(微服务?)

  • 细粒度
  • 通用的功能,可能会被多个BFF用到
  • 提供含各种状态的mock真实数据,易于同步开发(如何实现?)

BFF API设计

  • 合理设计接口数量,太多不易维护
  • 提供含各种状态的mock真实数据,页面不依赖server开发(如何实现?)
  • 多协议发布(如何实现?)
  • 规范数据格式

在落地BFF过程中,从技术角度来看:

  • 前端和BFF由同一人完成
  • 前端需要具备服务端技能
  • 快速的应用发布能力(docker?)

TWA的理念与实践

在2018年的SEE Conf,蚂蚁金服的不四(知乎ID:死马)对在BFF基础上发展出来的TWA(Techless Web Application)开发体系进行了介绍(视频PPT)。

传统的分层:

BFF10.png

BFF负责聚合底层业务数据,给客户端提供接口,秉承谁使用谁维护的理念,一般由前端团队维护。

v2-d245bbeae2b0245da8858bd237b344f7_r.jpg

业务实际上分为三层:前端(HTML/CSS/JS)+ BFF(Node.js接口聚合层)+ 后端服务(Java)

BFF-6.png

BFF on Chari是蚂蚁自研的,基于Egg.js的BFF框架,打通了Node到Java的RPC通信链路

Egg.js已经开源(Egg.js是以Koa作为基础框架),需要基于Egg.js去打造属于自己的BFF框架

RPC的意思是不在一个内存空间的两个应用,借助网络来实现,像调用本地的函数一样去调用远程函数

BFF不是银弹,有着自己的问题:

BFF-7.png

  • 研发成本上升:前端团队既要开发客户端,又要开发BFF,人手不够
  • 流程繁琐:BFF引入后,要同时走前端、服务端的研发流程,多端发布、互相依赖,导致流程繁琐
  • 运维经验不足:主要是前端工程师运维经验不足(给业务团队带来了很大困扰)

尽管职责划分越来越清晰,但是由于前后端发布系统不一致,前端团队仍需要在基于不同的代码仓库进行研发,走不同的发布流程

理想的开发流程:

TWA.png

蚂蚁金服推出的解决方案TWA(Techless Web Application),是一个全栈的研发框架

TWA是为了提升开发者研发体验而推出的渐进式解决方案,开发者在一个代码仓库下,基于TWA的框架,完成客户端和BFF层的研发,通过Basement研发平台提供的流程支持,不用再关注应用、构建、部署、流程等细节,可以一键将应用部署到各个运行终端,同时在研发平台上完成应用的自主运维和监控

将TWA拆解开来,分为了三个大的方向:框架、研发平台、运行时

  • 框架:提供了前后端合一的框架,通过定制化网关,抹平多个终端和接入形式的差异,让一套代码可以运行在不同的终端下(?)
  • 研发平台:Basement,提供TWA的研发迭代、自主运维和监控,精简规范了H5 APP的研发流程
  • 运行时:通过基础服务和Docker,给客户端代码和服务端代码提供稳定可靠的运行环境

TWA2.png

框架

是一个渐进式框架,可以选择部分能力使用

TWA3.png

客户端、服务端目录、依赖都是相互独立的。

复杂的终端环境,有不同的接入链路(可能通过网关走TCP长连接通信)、不同的鉴权方案。如果每个业务的BFF系统要对接到每个系统完成接入、鉴权是非常复杂的。

TWA网关2.png

解决方案是:TWA网关层 + 客户端RPC Client,

TWA网关2.png

TWA网关层统一完成所有终端的接入、鉴权,BFF不需要对接到每个终端,只需要对接到网关,让BFF接口统一

客户端的RPC Clinet是一个请求库,对接到网关,将客户端的鉴权、接入的细节隐藏,并且可以自动选择接入链路

这就可以在client目录下,像调用本地方法一样,调用server中目录下的服务

TWA-4.png

研发平台

Basement.png

Basement,一站式运维平台,基于Docker,支撑整个应用的研发流程,完成自动化的测试和部署

TWA-6.png

Node与Java的通信

与使用的微服务框架有关,可以采用HTTP的方式,也可以通过RPC调用。

蚂蚁金服的实践

  • 数据:跨语言序列化协议hessian
  • 服务:Node PRC

开源框架:

  1. sofa-bolt-node:蚂蚁通讯协议Bolt的Nodejs实现
  2. sofa-rpc-node:一个通用的Nodejs RPC模块

有赞的实践 – HTTP

可以采用服务注册中心:

youzan1.png

首先,Java应用服务启动的时候,会往服务注册中心注册服务,这里的服务注册中心可能是ETCD或者Zookeeper,然后,Node应用在启动的时候,会先从服务注册中心拉取服务列表,接着Node会跟Java服务建立一条TCP长链接,除此之外,Node还需要负责Hession协议解析以及负载均衡等。

上面的方式Node职责比较重,对Node开发的要求高,有赞在此基础上做了改进:

youzan2.png

在Node和Java之间添加了一层中间代理层Tether,Tether是用Go语言写的一个本地代理,Tether会对外暴露一个HTTP的服务,对Node来说,只需要通过HTTP方式调用本地的服务即可,其他服务化相关的服务发现、协议解析、负载均衡、长链建立维护都交由Tether来处理。这样,Node这一层就非常轻量了,Node是调用Java服务时:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const Service = require('../base/BaseService');

class GoodsService extends Service {
  /**
   * 根据商品 alias 获取商品详情
   * @param  {String} alias 商品 alias
   */
  async getGoodsDetailByAlias(alias) {
    const result = this.invoke(
      'com.youzan.ic.service.GoodsService',
      'getGoodsDetailByAlias',
      [alias]
    );
    return result;
  }
}
module.exports = GoodsService;

这种方式的优点:

  1. 使用简单,对前端开发非常友好,只需要通过HTTP方式调用本地的Tether服务即可;
  2. 多语言接入成本低,后期如果有其他语言(Python、Ruby)也需要接入整个服务化体系,也像Node一样,它们都只需要调用本地Tether暴露的HTTP服务即可,没有额外的开发成本了。
  3. 后期更方便做协议层的优化,因为这种方式Tether其实就是一个代理,后期如果需要做协议层性能上的优化,那只需要优化Tether的性能就可以了。

总结

关于TWA

TWA是蚂蚁金服自研的,整合了BFF的一整套渐进式的前端+Node聚合层的开发体系,它不仅包括了BFF的引入,还包括了前后端代码管理、部署、运维等功能框架。

但是很遗憾的是,TWA没有开源,对于想要从0开始引入BFF,想实现TWA的功能,还有一个努力的过程。

实际上我们引入BFF,不是要完整的开发TWA,而是首先实现Chari BFF的功能。

接入BFF的好处

对于团队来说:

  • 业务支持变多
  • 沟通协作变少
  • 解决问题变快

对于个人来说:

  • 更合理分工
  • 做BFF可以拓展知识面

接入BFF的坏处:

  • 组织决定了架构的复杂度
  • 前期学习成本高,短期成为资源瓶颈

可能遇到的问题和可能的解决方法

  1. Node与Java通信 – Hessian(数据),RPC(服务)(sofa-rpc-node)/ (grpc-node
  2. 前端代码和BFF段代码管理 – Egg.js(同一个仓库,不同目录,不同依赖,分别打包、分别配置)
  3. 前端的应用发布、运维能力 – Docker
  4. 服务端的配合 – 服务:微服务(?),人员: (?)
  5. 前端能力提高 – 前端能力(JavaScript + HTML + CSS) + Node(服务端技能)+ Java(如何与Java通信)+ 运维能力(Docker)

参考