Serverless架构下的AI应用开发:入门、实战与性能优化
上QQ阅读APP看书,第一时间看更新

2.3.2 其他方案

相对于阿里云的HTTP函数以及HTTP触发器,AWS、华为云、腾讯云等FaaS平台则需要借助API网关以及一个转换层来实现传统Web框架到FaaS平台的部署。

如图2-14所示,以Python Web框架为例,在通常情况下,使用Flask等框架时实际上要通过Web Server才能进入下一个环节,而云函数是一个函数,本不需要启动Web Server,所以可以直接调用wsgi_app方法。

图2-14 传统WSGI Web Server工作原理示例

这里的environ就是对event/context等处理后的对象,也就是所说的转换层要做的工作;start_response可以认为是一种特殊的数据结构,例如response结构形态等。以Flask项目为例,在腾讯云云函数上,这个所谓的转换层代码示例如下:


import sys
import json
from urllib.parse import urlencode
from flask import Flask
try:
    from cStringIO import StringIO
except ImportError:
    try:
        from StringIO import StringIO
    except ImportError:
        from io import StringIO
from werkzeug.wrappers import BaseRequest
def make_environ(event):
    environ = {}
    for hdr_name, hdr_value in event['headers'].items():
        hdr_name = hdr_name.replace('-', '_').upper()
        if hdr_name in ['CONTENT_TYPE', 'CONTENT_LENGTH']:
            environ[hdr_name] = hdr_value
            continue
        http_hdr_name = 'HTTP_%s' % hdr_name
        environ[http_hdr_name] = hdr_value
    apigateway_qs = event['queryStringParameters']
    request_qs = event['queryString']
    qs = apigateway_qs.copy()
    qs.update(request_qs)
    body = ''
    if 'body' in event:
        body = event['body']
    environ['REQUEST_METHOD'] = event['httpMethod']
    environ['PATH_INFO'] = event['path']
    environ['QUERY_STRING'] = urlencode(qs) if qs else ''
    environ['REMOTE_ADDR'] = 80
    environ['HOST'] = event['headers']['host']
    environ['SCRIPT_NAME'] = ''
    environ['SERVER_PORT'] = 80
    environ['SERVER_PROTOCOL'] = 'HTTP/1.1'
    environ['CONTENT_LENGTH'] = str(len(body))
    environ['wsgi.url_scheme'] = ''
    environ['wsgi.input'] = StringIO(body)
    environ['wsgi.version'] = (1, 0)
    environ['wsgi.errors'] = sys.stderr
    environ['wsgi.multithread'] = False
    environ['wsgi.run_once'] = True
    environ['wsgi.multiprocess'] = False
    BaseRequest(environ)
    return environ
class LambdaResponse(object):
    def __init__(self):
        self.status = None
        self.response_headers = None
    def start_response(self, status, response_headers, exc_info=None):
        self.status = int(status[:3])
        self.response_headers = dict(response_headers)
class FlaskLambda(Flask):
    def __call__(self, event, context):
        if 'httpMethod' not in event:
            return super(FlaskLambda, self).__call__(event, context)
        response = LambdaResponse()
        body = next(self.wsgi_app(
            make_environ(event),
            response.start_response
        ))
        return {
            'statusCode': response.status,
            'headers': response.response_headers,
            'body': body
        }

当然,转换工作在某些情况下还是比较麻烦的,所以很多时候我们可以借助常见的开发者工具进行传统Web框架的部署,例如借助开源的开发者工具Serverless Devs、Serverless Framework等。