当前位置: 首页 > 后端技术 > Python

无服务器工程实践-Serverless应用开发理念的转变

时间:2023-03-26 13:05:05 Python

简介:Serverless架构带来的除了新的架构和新的编程范式之外,还包括思维上的转变,尤其是在开发过程中。有人说要把serverless架构看成是天生的分布式架构,就要用分布式架构的思想来开发serverless应用。诚然,这种说法是正确的。但是在某些情况下,Serverless还是有一些特性的,所以开发的理念需要转变。前言:在Serverless架构下,虽然更关注业务代码,但也需要关注一些配置和成本,必要时需要根据配置和成本对Serverless应用进行配置和代码优化。serverless应用开发理念的变化serverless架构除了带来新的架构和新的编程范式外,还包括思维上的变化,尤其是在开发过程中。有人说要把serverless架构看成是天生的分布式架构,就要用分布式架构的思想来开发serverless应用。诚然,这种说法是正确的。但是在某些情况下,Serverless还是有一些特性的,所以开发的理念需要转变。1、文件上传方式在传统的web框架中,上传文件是非常简单方便的,比如Python的Flask框架:f=request.files['file']f.save('my_file_path')但是在Serverless架构下,Files不能直接上传,原因如下:一般一些云平台的API网关触发器会将二进制文件转为字符串,不方便直接获取和存储;通常,API网关与FaaS平台之间传输的数据包的大小是有限制的,很多平台都将数据包的大小限制在6MB以内;大多数FaaS平台都是无状态的,即使保存在当前实例中,当实例释放时文件也会丢失。因此,传统Web框架中常用的文件上传方案并不适合在Serverless架构中直接使用。在Serverless架构中,上传文件通常有两种方式:一种是转换成Base64格式后上传,持久化到对象存储或NAS,但是API网关和API网关之间传输的数据包有大小限制。FaaS平台,所以这种方式通常适用于上传头像等小文件的业务场景。另一种上传方式是通过对象存储等平台上传。因为客户端直接通过密钥等方式将文件上传到对象存储,存在一定的风险。执行预签名操作,将预签名地址返回给客户端,客户端使用指定方法上传。上传完成后,通过对象存储触发器等方式更新上传结果,如下图所示。Serverless架构下上传文件的例子以阿里云函数计算为例,使用Bottle实现了以上两种常见的上传方式。在函数计算中,首先初始化对象存储相关的对象:初始化对象存储相关的对象:AccessKey={"id":'',"secret":''}OSSConf={'endPoint':'oss-cn-hangzhou.aliyuncs.com','bucketName':'bucketName','objectSignUrlTimeOut':60}#获取/上传文件到OSS临时地址auth=oss2.Auth(AccessKey['id'],AccessKey['secret'])bucket=oss2.Bucket(auth,OSSConf['endPoint'],OSSConf['bucketName'])#对象存储操作getUrl=lambdaobject,method:bucket.sign_url(method,object,OSSConf['objectSignUrlTimeOut'])getSignUrl=lambdaobject:getUrl(object,"GET")putSignUrl=lambdaobject:getUrl(object,"PUT")#GetrandomstringrandomStr=lambdalen:"".join(random.sample('abcdefghijklqrstuvwxyz123456789SAABCDEFGZ*100,len))第一种上传方式,Base64上传后,持久化文件到对象存储:#FileUpload#URI:/file/upload#Method:POST@bottle.route('/file/upload',"POST")defpostFileUpload():尝试:图片库64=bottle.request.GET.get('picture','').split("base64,")[1]object=randomStr(100)withopen('/tmp/%s'%object,'wb')作为f:f.write(base64.b64decode(pictureBase64))bucket.put_object_from_file(object,'/tmp/%s'%object)returnresponse({"status":'ok',})除了Exceptionase:print("Error:",e)returnresponse(ERROR['SystemError'],'SystemError')第二种上传方式是获取预签名对象存储地址,然后在客户端发起上传请求直接上传到对象存储:#Get文件上传地址#URI:/file/upload/url#Method:GET@bottle.route('/file/upload/url',"GET")defgetFileUploadUrl():try:object=randomStr(100)returnresponse({"upload":putSignUrl(object),"download":'https://download.xshu.cn/%s'%(object)})exceptExceptionase:print("Error:",e)返回响应(ERROR['SystemError'],'SystemError')HTML部分:

在网络上上传文件


方案一:上传到函数计算处理后再转入对象存储,这种方式比较直观,问题是FaaS平台和API网关对数据包的大小有上限,对二进制文件的处理不好


方案二:直接上传到对象存储。流程是先从函数计算获取临时地址并存储数据(比如将文件信息保存到Redis等),然后从客户端上传文件到对象存储,然后通过对象存储触发器,从存储系统(比如已经存储在Redis中)读取信息,然后对图像进行处理。

通过Base64上传的客户端JavaScript实现:functionUpladFileFC(){constoFReader=newFileReader();oFReader.readAsDataURL(document.getElementById("fileFc").files[0]);oFReader.onload=function(oFREvent){constxmlhttp=window.XMLHttpRequest?(newXMLHttpRequest()):(newActiveXObject("Microsoft.XMLHTTP"))xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){alert(xmlhttp.responseText)}}consturl="https://domain.com/file/upload"xmlhttp.open("POST",url,true);xmlhttp.setRequestHeader("内容类型","application/json");xmlhttp.send(JSON.stringify({图片:oFREvent.target.result}));}}客户端直接将预签名地址传输给对象存储的客户端JavaScript实现:functiondoUpload(bodyUrl){constxmlhttp=window.XMLHttpRequest?(新的XMLHttpRequest()):(新的ActiveXObject("Microsoft.XMLHTTP"));xmlhttp.open("PUT",bodyUrl,true);xmlhttp.onload=function(){alert(xmlhttp.responseText)};xmlhttp。发送(document.getElementById("fileOss").files[0]);}functionUpladFileOSS(){constxmlhttp=window.XMLHttpRequest?(newXMLHttpRequest()):(newActiveXObject("Microsoft.XMLHTTP"))xmlhttp.onreadystatechange=function(){if(xmlhttp.readyState==4&&xmlhttp.status==200){constbody=JSON.parse(xmlhttp.responseText)if(body['url']){doUpload(body['url'])}}}constgetUploadUrl='https://domain.com/file/upload/url'xmlhttp.open("POST",getUploadUrl,true);xmlhttp.setRequestHeader("内容类型","application/json");xmlhttp.send();}整体效果如下图Serverless架构下文件上传实验web端效果图此时,我们可以在当前页面进行不同类型的文件上传方案实验。2、文件读写及持久化方法应用程序可能会涉及到文件的读写操作,或者执行过程中的一些文件持久化操作。在传统的云主机模式下,可以直接读写文件,或者将文件持久化到某个目录下,但是在Serverless架构下就不是这样了。由于FaaS平台是无状态的,使用后会销毁,所以文件不能直接持久化到实例中,可以持久化到其他服务中,比如对象存储,NAS等。同时,在没有配置NAS的情况下,FaaS平台通常只有/tmp目录的可写权限,所以可以在/tmp文件夹下缓存一些临时文件。3.慎用一些web框架的特性(1)异步函数计算在请求层面是隔离的,所以当请求结束时可以认为实例处于静默状态。在函数计算中,API网关的触发器通常是同步调用的(以阿里云函数计算为例,通常只有定时触发器、OSS事件触发器、MNS主题触发器、物联网触发器等几种情况才会异步触发)。这意味着当API网关返回结果给客户端时,整个函数将进入安静状态或被销毁,而不是继续执行异步方法。所以一般情况下,Tornado等框架在Serverless架构下是很难发挥异步作用的。当然,如果用户需要异步能力,可以参考云厂商提供的异步方法。以阿里云函数计算为例,阿里云函数计算为用户提供异步调用能力。当函数的异步调用被触发时,函数计算会将触发事件放入内部队列,并返回请求ID,而不是具体的调用状态和函数执行状态。如果用户想获取异步调用的结果,可以通过配置异步调用的target来实现,如图。功能异步功能原理简图(二)定时任务在serverless架构下,一旦应用完成当前请求,就会进入静默状态,甚至会销毁实例,这使得一些内置了-在计划任务中正常执行计划任务。函数计算通常由事件触发,不会在预定的时间自动启动。比如在Egg项目中设置了一个定时任务,但是如果在实际的函数计算中没有通过trigger触发该函数,则该函数不会被触发,也不会从内部自动启动执行定时任务.这时候就可以使用Timedtrigger,它可以通过定时触发器触发指定的方法来代替定时任务。4.关注应用组成结构(1)静态资源和业务逻辑Serverless架构下,静态资源要在对象存储和CDN的支持下对外提供服务,否则所有资源都在函数中。通过函数计算对外暴露,不仅会降低函数业务逻辑的并发度,还会带来更多的成本。尤其是将一些已有的程序迁移到Serverless架构时,比如Wordpress等,更要注意拆分静态资源和业务逻辑,否则在高并发条件下性能和成本会受到严峻考验。(2)业务逻辑的拆分在很多云厂商中,功能的收费标准是根据运行时间、配置的内存、产生的流量。如果某个函数的内存设置不合理,成本会成倍增加。要想保证内存设置合理,还必须保证业务逻辑结构的可靠性。以阿里云函数计算为例,一个应用有两个对外接口,其中一个接口占用内存不到128MB,另一个接口内存占用稳定在3000MB左右。这两个接口平均每天触发10000次,耗时100毫秒。如果一个函数中写了两个接口,那么这个函数可能需要设置内存为3072MB,同时用户要求使用内存消耗少的接口,以获得冷启动条件下更好的性能;如果两个接口分别写入函数,则两个函数的内存可以分别设置为128MB和3072MB,如表所示。从上表可以明显看出,合理、适当的业务拆分会在一定程度上节约成本。上例中的成本节省将近50%。作者简介:刘宇(江宇)国防科技大学电子信息专业博士生,阿里云Serverless产品经理,阿里云Serverless云布道者,CIO学院特聘讲师。新书推荐本书将通过多个开源项目,多个云厂商的多个云产品,向读者介绍什么是Serverless架构,如何入门Serverless架构,Serverless架构在不同领域的应用以及如何从零开始开发,以及各种方式AServerless应用等。本书可以帮助读者将Serverless架构融入到自己的领域,实现Serverless项目,获得Serverless架构带来的技术红利。原文链接本文为阿里云原创内容,未经许可不得转载。