作者:刘宇 0duzhan.com

前言

Serverless架构是一个新的概念,也可以说是一个新的架构或者技术,但是无论他有多新,都不能一下子完成现有都开发习惯到Serverless架构的过渡,让现有的工程师放弃现有的Express、Koa、Flask、Django等框架直接在Serverless架构上开发项目,显然是不可能,就算可能,这也需要时间进行适应和过渡。


那么在这个过渡的期间我们是否可以考虑将现有的框架部署到Serverless架构上?接下来,我们以Flask框架进行一个简单的测试:

  • 测试四种接口:
    • Get请求(可能涉及到通过路径传递参数)
    • Post请求(通过Formdata传递参数)
    • Get请求(通过url参数进行参数传递)
    • Get请求(带有jieba等计算功能)
  • 测试两种情况:
    • 本地表现
    • 通过Flask-Component部署表现
  • 测试两种性能:
    • 传统云服务器上的性能表现
    • 云函数性能表现

首先是测试代码:

from flask import Flask, redirect, url_for, request
import jieba
import jieba.analyse

app = Flask(__name__)


@app.route('/hello/<name>')
def success(name):
    return 'hello %s' % name


@app.route('/welcome/post', methods=['POST'])
def welcome_post():
    user = request.form['name']
    return 'POST %s' % user


@app.route('/welcome/get', methods=['GET'])
def welcome_get():
    user = request.args.get('name')
    return 'GET %s' % user


@app.route('/jieba/', methods=['GET'])
def jieba_test():
    str = "Serverless Framework 是业界非常受欢迎的无服务器应用框架,开发者无需关心底层资源即可部署完整可用的 Serverless 应用架构。Serverless Framework 具有资源编排、自动伸缩、事件驱动等能力,覆盖编码、调试、测试、部署等全生命周期,帮助开发者通过联动云资源,迅速构建 Serverless 应用。"
    print(", ".join(jieba.cut(str)))
    print(jieba.analyse.extract_tags(str, topK=20, withWeight=False, allowPOS=()))
    print(jieba.analyse.textrank(str, topK=20, withWeight=False, allowPOS=('ns', 'n', 'vn', 'v')))
    return 'success'


if __name__ == '__main__':
    app.run(debug=True)

这段测试代码是比较有趣的,它包括了最常用的请求方法、传参方法,也包括简单的接口和稍微复杂的接口。

本地表现

本地运行之后,通过Postman进行三个接口简单测试:

  • Get通过路径传参:
  • Post参数传递:
  • Get参数传递:

通过Flask-Component部署表现

接下来,我们将这个代码部署到云函数中:

通过Flask-Component部署,可以参考Tencent给予的文档,Github地址https://github.com/serverless-components/tencent-flask

Yaml文档内容:

FlaskComponent:
  component: '@gosls/tencent-flask'
  inputs:
    region: ap-beijing
    functionName: Flask_Component
    code: ./flask_component
    functionConf:
      timeout: 10
      memorySize: 128
      environment:
        variables:
          TEST: vale
      vpcConfig:
        subnetId: ''
        vpcId: ''
    apigatewayConf:
      protocols:
        - http
      environment: release

接下来测试我们的目标三个接口

  • Get通过路径传参:
  • Post参数传递:
  • Get参数传递:

通过上面的测试,我们可以看出,通过Flask-Component部署的云函数,也是可以具备常用的几种请求形式和传参形式。

可以这样说,一般情况下,用户的Flask项目可以直接通过腾讯云提供的Flask-component快速部署到Serverless架构上,可以得到比较良好的运行。

简单的性能测试

接下来对性能进行一波简单的测试,首先购买一个云服务器,将这个部分代码部署到云服务器上。
在云上购买服务器,保守一点买了1核2G


然后配置环境,到服务可以跑起来:

通过Post设置一下简单的Tests:

然后对接口进行测试:

非常顺利完成了接口测试:

可以通过接口测试结果进行部分可视化:

同时对数据进行统计:

可以看到,通过上图和上表,服务器测的整体响应时间都快于云函数的响应时间。而且可以看到函数存在冷启动,一按出现冷启动,其响应时间会增长20余倍。在由于上述测试,仅仅是非常简单的接口,接下来我们来测试一下稍微复杂的接口,使用了jieba分词的接口,因为jieba分词接口存在:
测试结果:

可视化结果:

通过对Jieba接口的测试,可以看到虽然服务器也会有因分词组件进行初始化而产生比较慢的响应时间,但是整体而言,速度依旧是远远低于云函数。
那么问题来了,是函数本身的性能有问题,还是增加了Flask框架+APIGW响应集成之后才有问题?
接下来,做一组新的接口测试,在函数中,直接返回内容,而不进行额外处理,看看函数+API网关性能和正常情况下的服务器性能对比

可以看出虽然最小和平均耗时的区别不是很大,但是最大耗时基本上是持平。可以看出来,框架的加载会导致函数冷启动时间长度变得异常可怕。
接下来通过Python代码,对Flask框架进行并发测试:
对函数进行3次压测,每次并发300:

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 1.2727971077
response mintime 0.573610067368

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 1.1745698452
response mintime 0.172255039215

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 1.2857568264
response mintime 0.157210826874

对服务器进行3次压测,同样是每次并发300:

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 3.41151213646
response mintime 0.255661010742

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 3.37784004211
response mintime 0.212490081787

===========task end===========
total:301,succ:301,fail:0,except:0
response maxtime: 3.39548277855
response mintime 0.439364910126

通过这一波压测,我们可以看到这样一个奇怪现象,那就是在函数和服务器预热完成之后,连续三次并发301个请求。函数的整体表现,反而比服务器的要好。这也说明了在Serverless架构下,弹性伸缩的一个非常重要的表现。传统服务器,我们如果出现了高并发现象,很容易会导致整体服务受到严重影响,例如响应时间变长,无响应,甚至是服务器直接挂掉,但是在Serverless架构下,这个弹性伸缩的能力是云厂商帮助我们做的,所以在并发量达到一定的时候,其实Serverless架构的优势变得会更加明显。

总结:

  • Flask是可以通过很简单的方法上Serverless架构,用户基本上可以按照原生Flask开发习惯来开发Flask项目,尤其是使用Flask开发接口服务的项目,更是可以比较容易的迁移到Serverless架构。
  • 整体框架迁移上Serverless架构可能要要注意几个额外的点:
  1. 如果接口比较多,可能要按照资源消耗比较大的那个接口来设置内存大小,以我例子中的情况,非jieba接口使用的时候,可以使用最小内存(64M),jieba接口使用的时候,需要256M的内存,而整个项目是一体的,只能设置一个内存,所以为了保证项目可用性,就会整体设置为256M的内存,这样一来如果另外三个接口访问比较多的前提下,可能资源消耗会相对增加比较大,所以,如果有条件的话,可考虑将资源消耗比较大的接口额外提取出来;
  2. 云函数+API网关的组合对静态资源以及文件上传等的支持可能并不是十分友好,尤其是云函数+API网关的双重收费,所以这里建议将Flask中的一些静态资源统一放在对象存储中,同时将文件上传逻辑修改成优先上传到对象存储中,可以参考之前的文章:【实践与踩坑】用Serverless怎么上传文件?
  • 框架越大,或者框架内的资源越多函数冷启动的时间可能会越大。这一点是非常值得重视的。在刚才测试过程中,非框架下,最高耗时是平均耗时的3倍,而在加载Flask框架和Jieba的前提下,最高耗时是平均的10+倍!如果可以保证函数都是热启动还好,一旦出现冷启动,可能会有一定的影响。
  • 由于用户发起请求是客户端到API网关再到函数,然后从函数回到API网关,再回到客户端,这个过程相对直接访问服务器获得结果的链路明显长了一些,所以在实际测试过程中小用户量对的表现发而不是很好,几次测试,基本上1核2G的服务器都是优于函数表现。但是当并发量上来的之后可以看到函数的表现实现了大超车,一度超越这台1核2G的服务器。那么这里有一个有趣的结论:对于极小规模请求,函数是按量付费,虽然性能上有一定的劣势,但是按量付费在价格上有一定的优势;当流量逐渐变大之后,函数在性能上的优势也逐渐凸显。

我相信,Serverless架构会随着时间的发展,越发的成熟,目前可能还有或多或少的问题,但是不久的将来,一定不负众望。


Sunny one so true