学堂 学堂 学堂公众号手机端

Serverless开发流程及运行原理

lewis 1年前 (2024-03-27) 阅读数 6 #技术
Faas产品对比图

•Faas平台都支持Node.js、Python、Java等编程语言,也都支持Http触发器和定时触发器•各个厂商的Faas都支持与自己云产品相关的触发器 例如:函数计算支持阿里云表格存储触发器• Faas的计费都差不多,且每个月都提供一定的免费额度,每个月前100万次执行免费,前40万GB-s免费(函数每秒消耗的内存大小,例如1GB-s 函数在1GB内存的规格上执行一秒钟所消耗的内存大小),超过免费额度之后,基本上是执行一万次一分钱,执行1GB-s 0.0031617分钱。用Faas整体的费用会非常便宜,对于小应用来说,几乎是免费的•国外开发者会经常使用Lambda,相关的第三方产品和社区也会更加完善。国内经常使用函数计算,因为函数计算使用方式更符合国内开发者的习惯。怎么用Faas去开发一个Serverless应用?

编写一个所有人都可以访问的Helloword接口,并根据请求参数进行响应

客户端请求该接口的代码


接口开发流程一、传统应用开发流程

代码开发->初始化服务器(安装Node.js、Nginx)->启动Node.js Server(node index.js)->解析域名->配置Nginx

二、Serverless应用开发流程

代码开发->代码部署(Faas函数)->创建触发器

为函数配置http触发器,Faas自动帮你初始化运行环境,http触发器会自动为你提供一个测试域名

​1、函数计算方式:在函数计算控制台上新建一个函数​

注意:会用到Http触发器,函数计算的Http触发器要在创建函数时就指定

• request是请求信息,可以从中获取到请求参数•response是响应对象,可以用它来设置Http响应数据•context是函数上下文

​创建触发器​

​用该APi Endpoint对函数进行测试,通过curl命令测试一下​

如果用浏览器访问,response header中强制添加content-disposition: attachment字段,此字段会使得返回结果在浏览器中以附件的方式打开,此字段无法覆盖,使用自定义域名将不受影响。

​2、Lambda方式​

•在Lambda控制台上先创建一个函数,为函数添加api网关触发器,没有直接提供http触发器来实现通过http请求触发函数执行。Lambda也会默认提供一个测试域名•与http触发器不同的是,api网关触发器入参是event,event对象就是http请求信息

​http触发器和api网关触发器区别​

函数计算的http触发器请求处理方式和express.js框架处理请求更加类似,而api网关触发器是普通函数

开发Serverless应用的基础知识•Serverless应用是由一个个函数组成的,与main函数对应的就是Faas中的入口函数,一般名为handler即index.js文件中的handler函数•Faas函数可以包含多个源文件,然后按照编程语言的模块机制相互引入

​定义hello.js sayHello函数​

​引入sayHello函数​

将业务逻辑拆分到入口函数之外

​函数定义​

•函数定义本质上是由云厂商、触发器和编程语言等多个条件决定的。标准的函数定义是function(event,context),event是事件对象(Serverless中的触发器被称为事件源)•部分特殊函数触发器的定义可能与标准的也不一样,比如http触发器是对标准函数触发器的进一步封装,主要是为了方便对http请求进行处理

​函数上下文​

包含函数运行时的一些信息

不同编程语言开发函数

Node.js和python开发是最简洁的。Node.js开发人员也比较多,前后端都很容易上手,在Serverless中最受欢迎。

Node.js

​回调函数​

​异步函数​

在Node.js中,仅Lambda的入口函数支持异步async写法,其他Faas平台的入口函数只能是同步写法,对于异步函数只能使用回调函数实现,所以入口函数有第三个参数callback

Python

Java

​Faas平台入口函数是同步写法,对于异步操作只能使用回调函数实现​

•上图代码中入口函数handler是回调函数,其他函数都是async函数• callback的第一个参数是错误信息,第二个参数是返回值,如果函数运行正常没有报错,callback第一个参数返回空值。触发器及事件对象

Faas接收到触发器产生的事件之后会根据触发器信息生成event对象,以event为参数调用函数

Http触发器

http触发器会根据http请求和请求参数生成事件,然后以参数形式传递给函数。那么http函数触发器的入口函数中的request和response参数具体由哪些属性组成?

​http触发器request参数​

​通过以下这种方式获取request中的body信息​

API网关触发器

用API网关接收http请求,然后产生事件,将事件传递给Faas,Faas将函数执行完毕之后,将函数返回值传递给API网关,API网关再将返回值包装为Http响应返回给用户

​Lambda API网关触发器event参数示例​

http触发器使用起来更简单,API网关触发器功能更加丰富比如ip黑名单,流量控制等

定时触发器

​event参数示例​

​小结​

•每个云厂商都实现与自己相关的触发器,比如文件存储触发器、消息触发器、数据库触发器•触发器决定了一个Serverless应用如何提供服务也决定了代码应该如何编写•丰富的触发器可以让应用的功能更加强大,不同的触发器的额外属性不同也给编程带来了麻烦•将业务逻辑拆分到入口函数之外,保持入口函数的简洁,这样业务代码就与触发器无关了日志输出

在Serverless中,日志输出和传统应用的日志输出没有太大差别,只是日志的存储和查询方式变了

•默认使用日志服务存储日志,日志服务包含日志采集和分析产品并设置报警项。•各个云厂商的Faas使用自己的日志服务来存储函数日志。•Faas平台也提供了基本的函数监控,包括函数的运行时长,内存使用异常处理

•虽然在Serverless中一个函数的异常,只会影响这一次函数的运行,不会使得整个系统崩溃,但是在编写代码时,也需要充分考虑程序的异常,保证代码的健壮性,进一步提升系统稳定性。•代码编辑可以在云端进行,而不仅仅在本地进行。•应用的组成是单个独立的函数而不是所有功能的集合体。•除了具体基本的函数能力之外,还需要提供便利的开发工具,丰富的触发器,完善的日志监控等其他服务集成,这些是在做技术选型的时候需要考虑的问题。执行上下文重用问题案例分析

​定义变量​

​定义函数​

​定义入口函数​

为函数设置一个定时触发器,每分钟执行一次。这段代码在本地执行是没有问题的,每次执行函数都会获取一个新的时间,然后查询数据。

但在Serverless中就存在两个比较严重的问题:

•函数并发限制•执行上下文重用

这个案例的现象是每次查询的结果都是一样的。

要想深刻的理解问题原因呢需要先了解Serverless的运行原理

Serverless运行原理

Serverless运行原理本质上就是函数的运行原理

接下来从三个角度来叙述下。

函数调用链路:事件驱动函数执行

在案例中设置了一个定时触发器,函数每分钟都会执行一次,这是因为定时触发器会产生一个事件,Faas平台会接收各种事件,当时间来临的时候会根据事件属性来执行函数,这个过程叫事件驱动。

浏览器和Nodejs也是事件驱动的,本质上都是将用户的操作抽象为事件,由事件是监听器监听事件,然后驱动程序执行,只是不同技术的驱动模型实现不同。

•对于Faas平台来说一方面可以通过事件来触发,另一方面可以直接调用API来执行(Faas平台都提供了执行函数的API)• Faas函数的两种调用方式:同步调用和异步调用调用方式同步调用

Faas平台收到函数调用之后会立即给函数分配运行环境来执行函数。

​使用函数计算Node.js SDK来同步调用函数​

这是一个函数同步调用的实例

•其中handler中的x-fc-invocation-type用来表示同步和异步•event是事件对象,使用sdk的时候可以自定义事件对象

​同步执行的结果​

异步调用

异步调用无法直接获取返回结果,适用于运行时间比较长的场景。对于函数计算来说,定时触发器就是异步调用的。

​异步调用的结果​

​异步调用怎么重试?​

Faas平台会默认帮你有限次数的重试,但大部分情况下不能只靠Faas所提供的功能。

对于异步调用,如果你关心调用结果的正确性,可以为函数配置“异步调用目标”,将调用结果发送到消息队列或其他服务中,通过监听消息判断得到异步执行结果。

不管函数是同步还是异步执行的,都会有一个默认超时时间60s,否则对于Faas平台持续运行的函数会一直占用资源无法释放。

如果1分钟内的日志量很大,导致查询时间很长,函数执行时间比如3分钟或者更长

那么设置超时时间为10分钟,但是运行函数会越来越多。Faas默认只会存在100个运行中的实例,超过之后,事件队列就会等待其他实例执行完毕之后再生成新的函数实例。

对于案例由于函数并发的限制,如果函数执行时间过长,那么使用new Date()获取时间就会有问题

你以为在12点的时候执行,可能实际在12点10分的时候执行,所以就不能通过new Date来获取当前代码执行时间,而是应用从函数触发器对象当中获取函数被触发执行的时间。

因为定时触发器是异步调用的,需要为函数设置调用目标,对于异常的调用结果进行处理。不过由于这个问题需要函数并发超过限制的时候才会出现,所以没有第一时间发现问题,为未来埋下隐患。如果这个问题不能解决,那么很有可能处理的数据是不准确的。

那现在知道了函数并发限制是怎么造成的,函数上下文启用是怎么回事呢?这就涉及到函数的生命周期了

生命周期函数启动过程

整个函数的运行过程分为4个阶段:下载代码、启动容器、初始化运行环境、运行代码。只有当Faas接收到触发器事件之后才会启动并运行函数。

​下载代码​

Faas平台本身不存储代码,而是放在对象存储中,需要执行函数的时候从对象存储中将函数代码下载并解压,所以Faas平台对代码包的大小进行限制,通常代码包不会超过50M。

​启动容器​

Faas会根据函数的配置启动对应的容器,然后使用容器。

​初始化运行环境​

初始化运行环境,分析代码依赖,执行用户初始化逻辑。初始化入口函数之外的代码等,最后才是运行代码,调用入口函数执行代码。当函数第一次执行的时候会经过完整的四个步骤,前三个过程称为冷启动,最后一个过程称为热启动。整个冷启动流程耗时达到百毫秒级别。函数运行完毕之后,运行环境保留2-5分钟(和具体云厂商有关)。

如果这段时间内函数需要再次执行,那么Faas平台就会使用上次的运行环境,这就是执行上下文重用。

函数的这个过程也被称为热启动,热启动的耗时完全是启动函数的耗时。

当一段时间内没有请求的时候,函数运行环境就会被释放,直到下次执行到来,直接从冷启动开始初始化。

​函数的请求示意图​

请求1和请求3是冷启动,请求2是热启动。函数执行完毕之后销毁运行环境,虽然对首次函数执行的性能有损耗,但却极大的提高了资源利用效率,只有在需要执行代码的时候需要初始化运行环境,消耗硬件资源。

如果函数每分钟都执行,则函数几乎都是热启动的,也就是会重复使用之前的执行上下文。

执行上下文就包括函数的容器环境和入口函数之外的代码,但是在实时处理日志的案例中就会出现问题了,由于执行上下文重用所以代码中除入口函数handler之外的代码都会在函数第一次运行的冷启动中执行,后面函数执行的时候都会使用第一次函数执行时已经初始化完毕的值。

这也就是为什么函数每次处理得到的都是同一份数据。

解决这个问题的方法就是让处理时间不被初始化就可以了,将处理时间的代码放在入口函数当中。


版权声明

本文仅代表作者观点,不代表博信信息网立场。

热门