乐趣区

关于腾讯云:二次元看过来基于-Serverless-的舞萌音游查分器

前言

本文作者:远哥制作

一、什么是 Serverless Framework

Serverless Framework 是业界十分受欢迎的无服务器利用框架,开发者无需关怀底层资源即可部署残缺可用的 Serverless 利用架构。Serverless Framework 具备资源编排、主动伸缩、事件驱动等能力,笼罩编码、调试、测试、部署等全生命周期,帮忙开发者通过联动云资源,迅速构建 Serverless 利用

没错,就像几天前看到的《Serverless 之歌》外面所说 I'm gonna reduce your ops,它能大幅度加重运维压力,那就开始入手吧!留神开发环境需 Node.js 10.0+,一键全局装置:npm install -g serverless

二、腾讯云 Flask Serverless Component 简介

腾讯云 Flask Serverless Component,反对 Restful API 服务的部署

依照常规首先来部署 demo

  1. 本地 PyCharm 创立一个新的 Flask 我的项目

  1. 手动创立内容为 Flaskrequirements.txt
  2. 依照配置文档创立 serverless.yml,例如本我的项目理论应用的残缺内容,首次应用可自行酌情简化
  3. 将密匙写入 .env(当然,部署的时候也能够抉择微信扫码受权)
TENCENT_SECRET_ID=<rm>
TENCENT_SECRET_KEY=<rm>

这样基于 ServerlessFlask Demo 就部署实现了,接下来持续依照本人的形式写剩下的代码。

三、maimai_DX

maimai 是一款街机音游。

在这里放一张动图自行领会一下,原始素材来自「外录 maimai」QZKago Requiem Re:MASTER ALLPERFECT Player: Ruri*R

  • 日本官网
  • 海内官网

在国内,只能从微信公众号中查看问题,而且每次进页面都须要微信的受权登录,并且外面存储的记录有条数限度,相册 只存最新 10 条,游戏记录 只存最新 50 条(就是一个队列,先进先出的那种)。这就是本我的项目的初衷,本人打进去的每一次问题都应该保留好。

舞萌查分器

成绩展现了,前端 Fomantic-UI,后端 Flask+MySQLgh 开源地址:https://github.com/yuangezhizao/maimai_DX_CN_probe,欢送 watchstarfork & pr

目前实装了如下性能:

  1. wechat_archive 中蕴含 主页 游戏数据 相册 游戏记录:对原始网页进行了批改,并且增加了 Highcharts 库可视化曲线显示变动
  2. record 蕴含 记录(分页)差别(分页):即自写的疾速预览页面,是查看历史记录和问题变动的十分实用的性能
  3. info 蕴含 铺面列表:即全副铺面根底信息,输入到一个页面中,不便页面内搜寻

开发过程

接下来将依照工夫的程序,形容一下开发过程中遇到的问题以及如何解决

1. Serverless Framework Component 配置文件

Serverless Framework 当初是 V2 版本,也就是说不能因循之前版本的 serverless.yml 配置文件,须要从新对照文档批改。

a. 之前版本会依据 requirements.txt 主动下载第三方库到我的项目目录下的 .serverless 文件夹下的 requirements 文件夹以加入最终的依赖打包,压缩成 zip 文件再最终上传至云函数运行环境

b. 最新版本不再主动下载,须要自行处理。官网示例的参考用法:hook

  src:
    # TODO: 装置 python 我的项目依赖到我的项目当前目录
    hook: 'pip3 install -r requirements.txt -t ./requirements'
    dist: ./
    include:
      - source: ./requirements
        prefix: ../ # prefix, can make ./requirements files/dir to ./
    exclude:
      - .env
      - 'requirements/**'

正文写的很分明,应用 hook 去依据 requirements.txt 下载第三方库到我的项目目录下的 requirements 文件夹,防止第三方库导致本地文件夹管理混乱。而后 include 中指定了我的项目目录下的 requirements 文件夹在云端的 prefix,即对于云端的云函数运行环境,requirements 文件夹中的第三方库和我的项目目录是同级的,能够失常导入应用。当然了,本地运行应用的是全局的第三方库,并未用到我的项目目录下的 requirements 文件夹。

2. 层治理概述

前者(指 b)是一个很正当的设计,不过在理论环境中却发现了新的问题。完全一致的配置文件

  src:
    hook: 'pip3 install -r ./src/requirements.txt -t ./src/requirements'
    dist: ./src
    include:
      - source: ./requirements
        prefix: ../
    exclude:
      - .env

在 macOS 下胜利部署之后,云端的云函数编辑器中看到 requirements 文件夹不存在,第三方库和我的项目目录是同级的,确实没问题。

不过在 Windows 下胜利部署之后,云端的云函数编辑器中看到了 requirements 文件夹?也就是说第三方库和我的项目目录非同级,于是拜访就会呈现 no module found 的导入报错了……

重复尝试批改 prefix 等配置项到最初也没有调试胜利,因而在这里提出两种解决办法:

a. 批改配置文件如下,让本地的第三方库和我的项目目录同级存在

  src:
    hook: 'pip3 install -r ./src/requirements.txt -t ./src'
    dist: ./src
    exclude:
      - .env

不过随着我的项目和第三方库的扩充文件夹会越来越多,十分不便于管理

b. 应用云函数提供的

尽管 sls deploy 部署的速度很快,然而如果能够在部署时只上传我的项目代码而不去解决依赖不就更好了嘛,这样跨终合作端开发只须要关怀我的项目代码就 ok 了,再也不须要治理依赖!

并且还有一点,想在 SCF 控制台中在线编辑函数代码须要将部署程序包放弃在 10MB 以下,不要认为十兆很大,很快就用光也是可能的

具体如何操作呢?那就是要将第三方库文件夹间接打包并创立为层,则在函数代码中可间接通过 import 援用,毕竟有些非凡库比方 Brotli,Windows 下没有 vc++ 的话就只能去 https://lfd.uci.edu/~gohlke/pythonlibs 下载 wheel 装置。

macOS 下失常装置之后会失去 _brotli.cpython-39-darwin.sobrotli.py 中再以 import _brotli 的模式导入,不过又出新问题了,云端会导入报错ModuleNotFoundError: No module named '_brotli'"

以后 SCF 的执行环境建设在以下根底上:规范 CentOS 7.2

为了解决问题尝试在 linux 环境下打包,拿起手头的 CentOS 8.2 云主机开始操作

pip3 install -r requirements.txt -t ./layer --upgrade
zip -r layer.zip ./layer

而后就能够把打包的 layer.zip 下载到本地再传上去了,临时能够一劳永逸了。

对了,配置文件能够移除 hook 并增加 layers

  src:
    src: ./src
    exclude:
      - .env
      - '__pycache__/**'
  layers:
    - name: maimai_DX_CN_probe
      version: 3

已绑定层的函数被触发运行,启动并发实例时,将会解压加载函数的运行代码至 /var/user/ 目录下,同时会将层内容解压加载至 /opt 目录下。若需应用或拜访的文件 file,搁置在创立层时压缩文件的根目录下。则在解压加载后,可间接通过目录 /opt/file 拜访到该文件。若在创立层时,通过文件夹进行压缩 dir/file,则在函数运行时需通过 /opt/dir/file 拜访具体文件

体验更快的部署速度吧!因为第三方库曾经打包在“层”中了

然而奇怪的是,在云端导入任意第三方库均会报错,于是调试着查看 path

for path in sys.path:
    print(path)

/var/runtime/python3
/var/user
/opt
/var/lang/python3/lib/python36.zip
/var/lang/python3/lib/python3.6
/var/lang/python3/lib/python3.6/lib-dynload
/var/lang/python3/lib/python3.6/site-packages
/var/lang/python3/lib/python3.6/site-packages/pip-18.0-py3.6.egg

再查看 opt

import os
dirs = os.listdir('/opt')

for file in dirs:
   print(file)

layer

这才豁然开朗,打包时须要在以后门路间接打包。上传之后“层”更新为版本 2,然而 ModuleNotFoundError: No module named '_brotli' 报错仍旧,并且确认 _brotli.cpython-38-x86_64-linux-gnu.so 文件理论存在。

而在 CentOSmacOS 上本地导入均没有问题,这可就犯难了,又想到很有可能是 python 版本的问题,于是去寻找现成 3.6 的环境,比方这里:

再再次上传之后“层”更新为版本 3,拜访胜利!课题终于解决,原来是须要 雷同版本 Python 3.6 运行环境

3. 自定义入口文件

components 源码 tencent-flask/src/_shims/ 中的文件每次都会被一成不变地从新打包上传到云端云函数中,目前有两个文件

a. severless_wsgi.py,作用是 converts an AWS API Gateway proxied request to a WSGI request.
WSGI的全称是 Python Web Server Gateway InterfaceWeb 服务器网关接口 ,它是为Python 语言定义的 Web 服务器和 Web 应用程序或框架之间的一种简略而通用的接口

b. sl_handler.py,就是默认的入口文件

import app  # Replace with your actual application
import severless_wsgi

# If you need to send additional content types as text, add then directly
# to the whitelist:
#
# serverless_wsgi.TEXT_MIME_TYPES.append("application/custom+json")

def handler(event, context):
    return severless_wsgi.handle_request(app.app, event, context)

针对于本人的我的项目,应用了 Flask工厂函数,为了防止每次都要在云端云函数编辑器中从新批改,最好的办法是自定义入口文件:

import severless_wsgi

from maimai_DX_CN_probe import create_app  # Replace with your actual application


# If you need to send additional content types as text, add then directly
# to the whitelist:
#
# serverless_wsgi.TEXT_MIME_TYPES.append("application/custom+json")

def handler(event, context):
    return severless_wsgi.handle_request(create_app(), event, context)

再指定 执行办法serverless_handler.handler,就 ok 了

4. url_for 输入 http 而非 httpsURL

在视图函数中重定向到 url_for 所生成的链接都是 http,而不是 https……其实这个问题 Flask 的文档 Standalone WSGI Containers 有形容到

说到底这并不是 Flask 的问题,而是 WSGI 环境所导致的问题,举荐的办法是应用 中间件,官网也给出了 ProxyFix

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)

然而是从 X-Forwarded-Proto 中取的值,apigw中其为 http,因而并不能间接应用这个ProxyFix
因为 Flask 的社区还算欠缺,参考资料很多前人都铺好了路,所以间接去 Stack Overflow 搜解决办法,Flask url_for generating http URL instead of https
问题呈现的起因如图:Browser ----- HTTPS ----> Reverse proxy(apigw)----- HTTP ----> Flask
因为本人在 apigw 设置了 前端类型 https,也就是说 Browser 端是不可能应用 http 拜访到的,通过打印 environ 可知

{
  "CONTENT_LENGTH": "0",
  "CONTENT_TYPE": "","PATH_INFO":"/","QUERY_STRING":"",
  "REMOTE_ADDR": "","REMOTE_USER":"",
  "REQUEST_METHOD": "GET",
  "SCRIPT_NAME": "","SERVER_NAME":"maimai.yuangezhizao.cn","SERVER_PORT":"80","SERVER_PROTOCOL":"HTTP/1.1","wsgi.errors": <__main__.CustomIO object at 0x7feda2224630>,"wsgi.input": <_io.BytesIO object at 0x7fed97093410>,"wsgi.multiprocess": False,"wsgi.multithread": False,"wsgi.run_once": False,"wsgi.url_scheme":"http","wsgi.version": (1, 0),"serverless.authorizer": None,"serverless.event":"<rm>","serverless.context":"<rm>","API_GATEWAY_AUTHORIZER": None,"event":"<rm>","context":"<rm>","HTTP_ACCEPT":"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9","HTTP_ACCEPT_ENCODING":"gzip, deflate, br","HTTP_ACCEPT_LANGUAGE":"zh-CN,zh;q=0.9,en;q=0.8","HTTP_CONNECTION":"keep-alive","HTTP_COOKIE":"<rm>","HTTP_ENDPOINT_TIMEOUT":"15","HTTP_HOST":"maimai.yuangezhizao.cn","HTTP_SEC_FETCH_DEST":"document","HTTP_SEC_FETCH_MODE":"navigate","HTTP_SEC_FETCH_SITE":"none","HTTP_SEC_FETCH_USER":"?1","HTTP_UPGRADE_INSECURE_REQUESTS":"1","HTTP_USER_AGENT":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.111 Safari/537.36","HTTP_X_ANONYMOUS_CONSUMER":"true","HTTP_X_API_REQUESTID":"5bcb29af2ca18c1e6d7b1ec5ff7b5427","HTTP_X_API_SCHEME":"https","HTTP_X_B3_TRACEID":"5bcb29af2ca18c1e6d7b1ec5ff7b5427","HTTP_X_QUALIFIER":"$LATEST"
}

HTTP_X_FORWARDED_PROTO对应 apigw 里的变量是HTTP_X_API_SCHEME,故解决办法如下:app.wsgi_app = ReverseProxied(app.wsgi_app)

class ReverseProxied(object):
    def __init__(self, app):
        self.app = app

    def __call__(self, environ, start_response):
        scheme = environ.get('HTTP_X_FORWARDED_PROTO')
        if scheme:
            environ['wsgi.url_scheme'] = scheme
        return self.app(environ, start_response)

app = Flask(__name__)
app.wsgi_app = ReverseProxied(app.wsgi_app)

5. 响应数据压缩

不论是 IISApache 还是 Nginx,都提供有压缩性能。毕竟本人在用的云主机外网上行只有1M 带宽,压缩后对于缩短首屏工夫的成果晋升极为显著。对于 Serverless,响应数据是通过API Gateway 传输到客户端,那么压缩也应该是它所具备的能力(尽管外网速度大幅度提高,然而该压缩还是得压缩),然而并没有找到……看到某些 js 框架原生有提供压缩性能,于是打算增加 Flask 自行压缩的性能。简略来讲,通过订阅 @app.after_request 信号并调用第三方库 brotlicompress办法即可(
在写之前去 gh 上看看有没有现成的轮子拓展,果然有……刚开始用的是 Flask-Zipper,起初换成Flask-Compress 解决了问题
实测 3.1 MB 的数据采纳 brotli 压缩算法减至76.1 kB

6. apigw 三种环境不同门路所产生的影响

默认的映射如下:

ID 环境名 拜访门路
1 公布 release
2 预公布 prepub
3 测试 test

因为配置的 static_url_path"",即 static 文件夹是映射到 / 门路下的,所以再加上 releaseprepubtest拜访就天然 404
因而绑定了 自定义域名 应用自定义门路映射 ,并将 公布 环境的拜访门路设置成 /,这样再拜访 公布 环境就没有问题了

ID 环境名 拜访门路
1 公布 /
2 预公布 prepub
3 测试 test

7. 同时拜访 公有网络 外网

云函数 中能够利用到的云端数据库有如下几种

  • 云数据库 CDB,须要 公有网络 拜访,尽管能够通过外网拜访然而能走内网就不走外网
  • PostgreSQL for Serverless(ServerlessDB),这个是官网给 Serverless 配的 pg 数据库
  • 云开发 TCB 中的MongoDB,没记错的话须要开明内测权限拜访

因为本人是从旧网站迁徙过去的,数据临时还没有迁徙,因而间接拜访原始云数据库 CDB,在 云函数 配置 所属网络 所属子网 即可。然而此时会无法访问外网,一种解决办法是开启 公网拜访 公网固定 IP,就能够同时拜访内网和外网资源了。对于配置文件,本我的项目是 单实例利用 也就是说 我的项目中只引入一个组件,部署时只生成一个组件实例 。然而如果想引入数据库的话,就得新增组件了,目前在Flask Components 中并没有提供数据库相干的配置项,因而须要 我的项目中引入多个组件,部署时生成多个组件实例 。也很简略,创立一个含有serverless.yml 的新文件夹,用来配置postgresql

component: postgresql # (必填) 组件名称,此处为 postgresql
name: maimai_DX_CN_probe # (必选) 组件实例名称.
org: yuangezhizao # (可选) 用于记录组织信息,默认值为您的腾讯云账户 appid,必须为字符串
app: yuangezhizao # (可选) 用于记录组织信息. 默认与 name 雷同,必须为字符串
stage: dev # (可选) 用于辨别环境信息,默认值是 dev

inputs:
  region: ap-beijing # 可选 ap-guangzhou, ap-shanghai, ap-beijing
  zone: ap-beijing-3 # 可选 ap-guangzhou-2, ap-shanghai-2, ap-beijing-3
  dBInstanceName: maimai_DX_CN_probe
  #  projectId: 0
  dBVersion: 10.4
  dBCharset: UTF8
  vpcConfig:
    vpcId: vpc-mrg5ak88
    subnetId: subnet-hqwa51dh
  extranetAccess: false

而后在终端 cd 到这个目录再执行 sls deploy 即可胜利部署postgresql

yum install python3-devel postgresql-devel
pip install psycopg2

后果

import psycopg2
File "/opt/psycopg2/__init__.py", line 51, in &lt;module&gt;
from psycopg2._psycopg import (                     # noqa
ImportError: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory

下列问题处于解决之中:

  • http 强制跳转 https
  • 测试环境推送至生产环境

至此,本文就完结了,欢送交换!

One More Thing

立刻体验腾讯云 Serverless Demo,支付 Serverless 新用户礼包 ???? serverless/start

欢送拜访:Serverless 中文网!

退出移动版