转-BFF调研-1

转自:  https://blog.csdn.net/duola8789/article/details/89332064 

 

前端开发中存在的难问题

  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服务时:

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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

这种方式的优点:

  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)

参考

  • Pattern: Backends For Frontends@samnewman
  • 为什么互联网公司开始用node.js做web服务的中间件?有什么好处吗?@知乎
  • Backend For Frontend@黯羽轻扬
  • 蚂蚁财富的BFF实践.pdf@alipayobjects
  • 微服务架构:BFF和网关是如何演化出来的@10条
  • Developer Experience First —— TWA 的理念与实践@知乎专栏
  • Techless Web Application 的理念与实践 — 不四@优酷
  • 02_SEEConf_TWA的理念与实践_不四.pdf@语雀
  • Node 在有赞的实践@掘金
  • 聊聊 Node.js RPC(一)— 协议@语雀
  • 聊聊 Node.js RPC(二)— 服务发现@语雀

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/330360.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JDK8新特性之Stream流

转载自 JDK8新特性之Stream流 是什么是Stream流 java.util.stream.Stream Stream流和传统的IO流,它们都叫流,却是两个完全不一样的概念和东西。 流可以简单的说是处理数据集合的东西,可以申明式流式API来处理集合,而不是写一个…

(转)贫血和富血

转自: https://blog.csdn.net/t0404/article/details/51865174 贫血vs富血 我们来回顾一下。在企业架构模式中,业务层的实现一般有两种模式:一种是事务角本模式(Transaction script),另一种是领域模型模式…

JDK8的排序大法!!

转载自 屌炸天,JDK8的排序大法!! 今天总结了下JDK中排序的方法,包括JDK8中强大的lambda表达式及函数式接口运用,不废话,请看下面示例。 public class Test {public static void main(String[] args) {Lis…

康复题11

定义一个二维数组: int maze[5][5] {0, 1, 0, 0, 0,0, 1, 0, 1, 0,0, 0, 0, 0, 0,0, 1, 1, 1, 0,0, 0, 0, 1, 0,}; 它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求…

JDK8新特性之函数式接口

转载自 JDK8新特性之函数式接口 什么是函数式接口 先来看看传统的创建线程是怎么写的 Thread t1 new Thread(new Runnable() {Overridepublic void run() {System.out.println("t1");} }); t1.start(); 再来看看使用了函数式接口是怎么写的 Thread t2 new Thre…

hashCode和identityHashCode的区别你知道吗?

转载自 hashCode和identityHashCode的区别你知道吗?hashCode 关于hashCode参考之前的文章,点击参考之前文章。 identityHashCode identityHashCode是System里面提供的本地方法,java.lang.System#identityHashCode。 /*** Returns the same ha…

转:drop、truncate和delete的区别与选择

转自: https://blog.csdn.net/shadow_zed/article/details/78252494 (1)日志与事务 1.delete语句执行删除的过程是每次从表中删除一行,并且同时将该行的删除操作作为事务记录在日志中保存以便进行进行回滚操作。 2.truncate tab…

Java高级篇——深入浅出Java类加载机制

转载自 Java高级篇——深入浅出Java类加载机制类加载器简单讲,类加载器ClassLoader的功能就是负责将class文件加载到jvm内存。类加载器分类从虚拟机层面讲分为两大类型的类加载器,一是Bootstrap Classloader即启动类加载器(C实现)…

康复题21

For a positive integer n lets define a function f: f(n)   - 1  2 - 3  ..  ( - 1)nn Your task is to calculate f(n) for a given integer n. Input The single line contains the positive integer n (1 ≤ n ≤ 1015). Output Print f(n) in a …

转: databasemetadata 无法获取数据库表备注的解决方法

转自: https://blog.csdn.net/10km/article/details/77389038 mysql/jdbc:设置useInformationSchematrue读取表注释信息(table_comment) 问题描述 今天在读取表的注释信息(COMMENT)时,发现返回的REMARKS字段返回居然是null. 以下是代码示例: DatabaseMetaData me…

IntegerCache的妙用和陷阱

转载自 IntegerCache的妙用和陷阱 考虑下面的小程序,你认为会输出为什么结果? public class Test {public static void main(String[] args) {Integer n1 123;Integer n2 123;Integer n3 128;Integer n4 128;System.out.println(n1 n2);System.ou…

转:线性代数知识汇总

https://blog.csdn.net/MyArrow/article/details/53365048 1. 线性代数知识图谱 线性代数是代数学的一个分支,主要处理线性关系问题。线性关系意即数学对象之间的关系是以一次形式来表达的。例如,在解析几何里,平面上直线的方程是二元一次…

康复题25

都说天上不会掉馅饼,但有一天gameboy正走在回家的小径上,忽然天上掉下大把大把的馅饼。说来gameboy的人品实在是太好了,这馅饼别处都不掉,就掉落在他身旁的10米范围内。馅饼如果掉在了地上当然就不能吃了,所以gameboy马…

2020-09-17

https://www.zhihu.com/question/32729130

高级JAVA码农必须搞清楚它们的区别:instanceof、isInstance、isAssignableFrom

转载自 高级JAVA码农必须搞清楚它们的区别:instanceof、isInstance、isAssignableFrominstanceof、isInstance、isAssignableFrom这几个有没有接触过,或者接触过部分?定义a instanceof B a是B的实例,B是类或者接口、父类或父接口&…

康复题12

An elephant decided to visit his friend. It turned out that the elephants house is located at point 0 and his friends house is located at point x(x > 0) of the coordinate line. In one step the elephant can move 1, 2, 3, 4 or 5 positions forward. Deter…

Java架构师必看的10本书

转载自 Java架构师必看的10本书1、大型网站系统与JAVA中间件实践 本书围绕大型网站和支撑大型网站架构的Java中间件的实践展开介绍。 从分布式系统的知识切入,让读者对分布式系统有基本的了解;然后介绍大型网站随着数据量、访问量增长而发生的架构变迁&a…

转: Spark 的核心概念 RDD

转自: https://juejin.im/post/6844903826953076750 1.RDD 概述 1.1 什么是 RDD ? RDD(Resilient Distributed Dataset) 叫着 弹性分布式数据集 ,是Spark 中最基本的抽象,它代表一个不可变、可分区、里面元素可以并行计算的集合。 RDD …

SpringMVC @Transactional的陷井大坑引发No Session found for current thread

一、TransactionManager事务配置 &#xff08;1&#xff09;注解配置 配置spring的xml加Transactional<tx:annotation-driven transaction-manager"transactionManager" proxy-target-class"true" /> &#xff08;2&#xff09;xml配置 <tx:adv…

POJ3904(BFS算法)

Problem Descrption Promble Description 定义一个二维数组&#xff1a; int maze[5][5] { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, }; 它表示一个迷宫&#xff0c;其中的1表示墙壁&#xff0c;0表示可以走的路&…