阿里妹导读:FaaS—Function as a service,函数即服务。它是2014年由于亚马逊的AWS Lambda的兴起,而被大家广泛认知。FaaS能力是NBF中的一项非常重要的能力,NBF是一个非典型的FaaS架构,但是具备了典型的FaaS能力。文章将详细介绍NBF的FaaS容器架构、服务发布、服务路由和强大的Serverless能力以及NBF-FaaS在阿里大促期间的实践心得。
NBF (New-Retail Business Framework) 是供应链中台基础技术团队研发的新零售服务开放框架,提供了标准化业务定义、快捷服务开发和生态开放的能力,旨在为生态伙伴提供一整套完整的新零售PaaS和SaaS的解决方案。
2.FaaS
FaaS是Serverless的一种典型形式,由 Serverless平台提供负载均衡、高可用、自动扩缩容、服务治理等最佳实践,将这些最佳实践对 Developer 透明化,进一步缩短 Developer 从想法到产品的时间,降低开发成本,同时保障 Developer 开发的服务的可靠性。通过事件驱动的方式,开发者的Function通过Event有效触发,比如 HTTP 请求、消息事件等。Event Sources Function 事件驱动的集合;
Function Instances 提供服务的Function或微服务;
FaaS Controller 管理Function的控制服务,比如典型的API Gateway或者BFF(Backend For Front)等;
Platform Services Function依赖的平台服务,如权限管理 API、对象存储服务等。
3.NBF-FaaS架构
3.1 NBF-Platform Services(1)Serverless平台—CSE(Cloud Service Engine):Serverless是FaaS平台依赖的基础能力,这一块NBF与中间件CSE团队深度合作,CSE提供快速的扩缩容的能力,可以在毫秒级别支持并行水平扩容和动态缩容,满足业务的错峰场景。基础技术团队与CSE共建容器冷热启动的性能优化以及Serverless运维工具(日志,监控报警,链路跟踪等)开发。(2)NBF容器:NBF容器采用OSGI架构,提供了Bundle完整的生命周期管理,包括Bundle的加载,启动,卸载和注销等等,以及容器和Bundle的隔离和通信能力。服务发布:把Function/Bundle快速发布成服务的能力。
服务路由:服务多态,降级和Mock的路由能力。
服务管理:基于SPI和Bundle的版本管理和服务启停能力。
服务运维:服务的Serverless能力,混部能力,灰度能力和容灾降级能力等 。
3.2 NBF-Function InstancesNBF的函数实例对应的就是长在NBF服务市场的一个个服务背后的Bundle实现:NBF的Event Sources是由流程中心提供的基于EventMap的服务编排能力:NBF的FaaS Controller包括三种类型:(1)流程中心调度服务 流程中心提供了调度SPI服务的事件驱动能力,包括流程事件,消息事件,定时事件等等,目前基本可以覆盖所有的事件驱动的业务场景。(2)Broker调用RPC服务 Broker支持多模式调用Bundle发布的RPC服务,包括服务的多态路由,降级路由和Mock路由等 。(3)NBF-Rest调用HTTP服务,NBF-Rest支持调用Bundle发布的HTTP服务。NBF容器管理了Bundle的加载,启动,卸载和注销的完整周期,并采用OSGI机制实现了容器和Bundle之间的隔离和通信能力。这一层NBF和CSE团队共建,CSE负责实现Fast Auto-Scaling,目前已经在双十二和女王节等大促活动得到了充分验证。NBF实现了Fast Cold Start和Fast Hot Start Fast Cold Start—优化Bundle服务发布的冷启动时间 Fast Hot Start—优化Bundle服务Scaling up后的服务可用时间 底层依赖的CaaS服务目前也在跟随CSE的节奏从Sigma3.0向ACK-EE迁移,未来将全面支持阿里云单元。NBF-OSGI Framework是采用OSGI机制实现了Bundle加载,启动,卸载和注销完整生命周期托管,目前在集团内部绝对多数都是Pandora应用,集团中间件都是通过ModuleClassLoader插件式加载,因此目前NBF容器的Bundle加载方式也是建立在Pandora的加载机制之上的NBF-OSGI Framework提供了一整套Bundle的隔离机制,Bundle与容器的通信机制以及Bundle之间的通信机制。<imported>
<packages>
<package>org.springframework</package>
<package>org.apache.commons.logging</package>
<package>org.aopalliance</package>
<package>org.aspectj</package>
</packages>
</imported>
Bundle无需多言,是业务方编写的业务逻辑代码 Plugin是NBF引擎提供的增值能力,采用插件化的方式进行加载,比如NBF-FaaS能力中最核心的服务发布能力。前面提到了由于目前集团内部绝对多数都是Pandora应用,因此目前NBF的容器架构是建立Pandora的加载机制上的,本质上是Run在Pandora的容器内的。而未来的NBF容器架构是由NBF-OSGI Framework来托管外部容器,这些外部容器可以是Pandora容器,也可以是非Pandora容器,这样就实现了NBF容器对于Pandora容器的依赖倒置。而对于Run在NBF-FaaS平台的Bundle而言就具有更丰富的可变性。服务发布的核心原理如下图比较详细的介绍了NBF容器把Bundle发布成RPC服务的完整链路,核心主要包括三步:Broker Agent实现了Broker的SPI和Implement的分离,通过BrokerBundleLoader动态加载implement,这样Broker的版本升级对于使用方而言是不用做代码变更和重新发布。想想某些重型的二方库版本升级,每个业务方都需要深度感知,是不是觉得会舒爽很多。SPI Proxy则实现了采用注解的方式来实现无侵入的服务调用,从传统的服务调用方式迁移到NBF的服务调用方式易如反掌。举个栗子:@Autowired
ServiceA serviceA;
serviceA.invoke(params);
@Autowired
BundleBroker bundleBroker;
bundleBroker.get(ServiceA.class).invoke(params);
@DynamicInject
ServiceA serviceA;
serviceA.invoke(params);
有了@DynamicInject,是不是觉得NBF服务调用跟原有的传统调用方式没啥区别, 对于@DynamicInject支持的几种方式。对于Broker Bundle核心功能包括以下几块核心功能:4.3.1.1 BundleProxy
BundleProxy可以简单理解成是对Bundle运行的代理机制,比如Bundle的主动熔断和被动降级这些能力都是通过BundleProxy实现的,因为这些特性对于每个运行Bundle都是统一的机制。
4.3.1.2 服务发现
服务发现主要职责怎么找到需要调用的服务,怎么生成调用服务的URI,拿HSF的服务寻址策略来对比,整个机制就简单了 HSF的寻址URI: Proxy://IP:port/service/version/method 对于Broker服务发现 IP和port对应的容器本身的网络信息,这些可以通过APPName或者Armory分组以及未来serverless后的GroupId来获取 serviceName,version这些数据就是第三层Broker数据层提供的spi和bundle元数据信息 有了这些基本信息以后,我们就可以生成NBF服务发现的URI了。
4.3.1.3 路由计算
在介绍路由计算之前,先介绍下先前提到@DynamicInject支持的几种方式:默认模式,规则模式和动态模式。
(1)默认模式: 默认模式就是服务调用不需要指定任何路由参数,这种调用方式适合单Bundle实现的SPI,Bundle实现就是默认实现
@DynamicInject
private ConfigReadService configReadService;
ResultDO<List<ConfigDTO>> result = configReadService.queryConfig(new ConfigQuery);
(2)规则模式: 规则模式支持三种方式指定路由参数:Id(业务身份),Expression(正则表达式),Rule(规则表达式)。
// 指定bundleId方式, type默认为ID
@DynamicInject(pattern = "drf")
// 指定正则方式
@DynamicInject(pattern = "^drf-hz.*$", type = "REG")
// 指定Rule方式
@DynamicInject(pattern = "{\"wareHouseId\":\"2001\"}", type = "RULE")
(3)动态模式: 动态模式指的是编码时无法确定调用参数的场景,路由参数需要调用时传入。
@DynamicInject
private DynamicInvoker<ConfigReadService> configReadServiceDynamic;
ResultDO<List<ConfigDTO>> result;
// 动态传入bundleId
result = configReadServiceDynamic.getService(bundleId).queryConfig(new ConfigQuery);
// 动态传入规则参数
Map<String, Object> params = new HashMap<>();
params.put("merchant", merchant);
result = configReadServiceDynamic.getService(params).queryConfig(new ConfigQuery);
路由计算又要再次提到我们先前提到过的SpiProxy了,SpiProxy的职能主要有两个:a.获取SPIInfo,包括SPI的ClassName,SpiVersion和SpiCode等等b.根据路由参数计算需要调用BundleId 然后再根据我们在服务发现中提到的寻址策略,不难发现我们已经可以生成NBF服务调用的URI,这就是NBF多态路由的核心原理 。4.1.3.4 熔断降级
熔断降级包括两个核心能力:被动降级和主动熔断。
(1)被动降级:被动降级会在三种情况下触发:服务找不到,服务返回异常和服务超时,这个时候服务调用会自动路由到Bundle对应的降级Bundle。用个简单的表格解释下降级的含义 :(2)主动熔断:主动熔断是通过NBF设置基线指标来实现的,如果超过服务的基线指标,则路由到降级Bundle 。在截图的栗子中我们选用Bundle(供应链-批发服务-大润发实现)作为Bundle(供应链-批发服务-盒马实现)的降级Bundle,在超过基线指标100ms就会路由到降级Bundle。对于熔断降级实现的核心原理就是我们先前提到过的BundleProxy,这些特性对于每个运行Bundle都是统一的机制,通过BundleProxy识别是否满足主动熔断和被动降级的条件,然后再代理执行真正的Bundle 。4.1.3.5 流量管控
流量管控提供了一种软负载的能力,支持设置Bundle和降级Bundle之间的流量配比,我们仍以Bundle:供应链-批发服务-盒马实现为例,以图为证:
在先前提到的NBF容器架构中,NBF-Serverless能力是NBF容器架构的重要基石,只有在Serverless实现毫秒级弹性扩缩容前提下,才能真正支撑错峰场景,才能最大程度的节约机器资源。只有在Serverless实现服务资源统一弹性调度的前提下,才能真正实现NBF的服务部署隔离,而不是目前通过定制容器规格(1Core2G,2Core4G,4Core8G等等)和Bundle混部的方式来实现Bundle部署隔离和机器资源之间的平衡。在这里一定要为NBF的深度合作伙伴——CSE团队鼓个掌,他们已经具备了毫秒级Auto-Scaling能力,为我们提供了可靠的基础设施。当然对于Serverless配套运维设施(日志,监控报警,链路跟踪等)和Serverless迁移到ACK-EE云单元这些事情,NBF和CSE都还在路上。那在NBF-Serverless能力的建设过程中,NBF又扮演什么角色呢?用一张图来简单表述下Serverless的实现原理以及CSE与NBF的职责划分。 ★ 5.1.1 Fast Auto-ScalingFast Auto-Scaling是CSE提供的核心基础设施能力,毫秒级的弹性扩容主要包括几个步骤:(1)种子机器的启动 种子机器的启动就是冷启动的过程,这个过程跟当前集团APP启动的方式无异,就是容器启动,镜像加载和服务暴露的几个步骤,因此冷启动的时间普遍来说是分钟级别的。(2)种子分发 通过Fork2的技术实现了种子机器的内存复制,而把内存复制到扩容机器上的时间是极短的,因此CSE的Auto-Scaling可以毫秒级实现并行水平扩容。(3)服务注册 这个过程实际上就是在ConfigServer完成服务注册,从而可以保障复制出来的Service Bean是可被调用的。NBF在NBF-Serverless能力构建中第一个重要事项就是实现冷启动优化,我们期望把冷启动的启动从分钟级别优化到秒级,因此调整了NBF Bundle的冷启动机制:(1)在Bundle创建机器分组和扩容分组的时候提前部署Engine。(2)通过NBF的FaaS能力动态加载Bundle,原来,冷启动时间=Pandora容器的启动时间+Engine的启动时间+Bundle的install和start时间,经过优化以后,冷启动时间=Bundle的install和start时间。
★ 5.1.3 Fast Hot Start
由于当前的扩容机制是通过内存复制实现的,而类似于UUID这种与机器有关的内存变量的复制是不合适的,因此NBF的热启动优化主要是提供了refresh内存变量的机制。NBF的Framework托管了Bundle生命周期管理,也提供相应的Hook能力,通过这些Hook就能解决UUID这种问题。虽然目前Serverless运维配套能力还不够完善,但是我们仍然在去年双十二和今年女王节上线了几个P0级服务,验证在大促场景下Serverless的稳定性和毫秒级的Auto-Scaling能力。当然我们敢在S级的大促中验证P0级服务也是有所依仗的,那就是NBF的熔断降级和流量管控能力。文描服务在女王节当天的QPS流量从4000+飙升到12万,Serverless非常迅速的扩容到10台,妥妥的支撑了业务峰值。而对于机器资源的节约就显而易见了,原来文描服务根据业务体量常态部署的10台,而Serverless目前只需要常态部署2台(其实可以只部署1台,2台可以认为是容灾),而终态Serverless将解决长尾服务的问题,最终可以缩容到0台,这样对机器资源是更大程度的节约。下图是女王节期间的Serverless前后的指标体系对比 :从图中的数据可以看出,整个文描服务在大促期间表现出来的系统稳定性和服务稳定性是完全可靠的,这也就充分验证NBF-Serverless的可行性。极速回滚是NBF服务高可用运维一种非常有效的手段。传统的APP回滚方式是重新编译、构建、打包和部署,而NBF具备典型的FaaS能力,对于Bundle回滚只需要重新load指定回滚版本的Jar包而已,而NBF Engine又是常驻容器,因此Bundle回滚速度是非常之快的。 文章比较详细的介绍了NBF的FaaS能力,一句话总结:NBF是非典型的FaaS架构,但是具备典型的FaaS能力。开篇介绍了业界对于FaaS的广泛定义,然后对比了FaaS典型架构和NBF-FaaS的非典型架构之间的关系,之后重点介绍NBF的FaaS能力,包括NBF的容器架构,Bundle的服务发布和Bundle路由与管控的核心实现原理。最后表述了NBF的高可用运维能力,重点表述了NBF-Serverless的实现原理和具体实践心得。现在NBF从生长的盒马回归到供应链中台,为包括盒马在内的25个BU和合作伙伴提供生态开放能力。