9月7日-8日,相约 2023 腾讯全球数字生态大会!聚焦产业未来发展新趋势! 了解详情
写点什么

二次元看过来!基于 Serverless 的舞萌音游查分器

  • 2021-03-17
  • 本文字数:6847 字

    阅读完需:约 22 分钟

二次元看过来!基于 Serverless 的舞萌音游查分器

前言


社畜下班时刷微信时看到了《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. 手动创建内容为 Flask 的 requirements.txt

  2. 按照配置文档创建 serverless.yml,例如本项目实际使用的完整内容,初次使用可自行酌情简化

  3. 将密匙写入 .env(当然,部署的时候也可以选择微信扫码授权)


TENCENT_SECRET_ID=<rm>TENCENT_SECRET_KEY=<rm>
复制代码




这样基于 Serverless 的 Flask Demo 就部署完成了,接下来继续按照自己的方式写剩下的代码。


三、maimai_DX


maimai 是一款街机音游:



日本官网:https://maimai.sega.jp海外官网:https://maimai.sega.com
复制代码


在国内,只能从微信公众号中查看成绩,而且每次进页面都需要微信的授权登录,并且里面存储的记录有条数限制,相册只存最新 10 条,游戏记录只存最新 50 条(就是一个队列,先进先出的那种)。这就是本项目的初衷,自己打出来的每一次成绩都应该保存好。


舞萌查分器


成果展示了,前端 Fomantic-UI,后端 Flask+MySQL


gh 开源地址:https://github.com/yuangezhizao/maimai_DX_CN_probe,欢迎 watch、star、fork & pr!
复制代码


https://maimai.yuangezhizao.cn


目前实装了如下功能:

  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 --upgradezip -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 osdirs = os.listdir('/opt')
for file indirs: print(file)
layer
复制代码

这才恍然大悟,打包时需要在当前路径直接打包。上传之后“层”更新为版本 2,但是 ModuleNotFoundError: No module named '_brotli' 报错依旧,并且确认 _brotli.cpython-38-x86_64-linux-gnu.so 文件实际存在。


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


3.6.8

再再次上传之后“层”更新为版本 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 applicationimport 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 而非 https 的 URL


在视图函数中重定向到 url_for 所生成的链接都是 http,而不是 https……其实这个问题 Flask 的文档 Standalone WSGI Containers 有描述到说到底这并不是 Flask 的问题,而是 WSGI 环境所导致的问题,推荐的方法是使用中间件,官方也给出了 ProxyFix


from werkzeug.middleware.proxy_fix import ProxyFixapp.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# (必填) 组件名称,此处为 postgresqlname: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-develpip install psycopg2
复制代码

结果

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


下列问题处于解决之中:


  • http 强制跳转 https

  • 测试环境推送至生产环境


至此,本文就结束了,欢迎交流!


头图:Unsplash

作者:远哥制造

原文:https://mp.weixin.qq.com/s/7ScZQcYp2UT9aOG_sMXYqA

原文:二次元看过来!基于 Serverless 的舞萌音游查分器

来源:TencentServerless - 微信公众号 [ID:ServerlessGo]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

活动推荐:

2023年9月3-5日,「QCon全球软件开发大会·北京站」 将在北京•富力万丽酒店举办。此次大会以「启航·AIGC软件工程变革」为主题,策划了大前端融合提效、大模型应用落地、面向 AI 的存储、AIGC 浪潮下的研发效能提升、LLMOps、异构算力、微服务架构治理、业务安全技术、构建未来软件的编程语言、FinOps 等近30个精彩专题。咨询购票可联系票务经理 18514549229(微信同手机号)。

2021-03-17 00:251541

评论

发布
暂无评论
发现更多内容

吃串串,数签签,这个AI神器一秒搞定

百度大脑

人工智能 EasyDL

Linux之常见的存储架构

在即

9月日更

天壤完成新一轮战略融资 加速构建数字化转型通用智能平台

InfoQ 天津

华为云顾炯炯:云原生应用传送网络AND的实现架构与核心技术分享

华为云开发者联盟

网络 华为云 应用传送网络 ADN 东数西算

iPhone13全线机型上线WeTest云手机平台

WeTest

Elasticsearch IK 分词扩展词典(qbit)

qbit

elastic 扩展词 分词

一文带你了解经典的Java垃圾回收机制

华为云开发者联盟

Java JVM 对象 垃圾回收机制 垃圾收集器

考试试卷存储设计

guangbao

JavaScript “上层”语言

Augus

JavaScript 9月日更

p3c 插件,是怎么检查出你那屎山的代码?

小傅哥

小傅哥 代码规范 p3m pmd 开发手册

HUAWEI雄起!顶级网络工程师总结出了这份网络协议开源笔记

Java 架构 面试 程序人生 编程语言

数据安全与隐私系列08:大数据与电影《少数派报告》

数据与智能

人工智能

回帖抽大奖——5分钟极速体验AI技术能力

百度大脑

人工智能

什么是低代码自动化以及它如何使你受益?

低代码小观

程序员 自动化 工具 低代码 低代码开发平台

两个剪辑透明化融合视频特效处理

老猿Python

Python 音视频 视频剪辑 视频特效 引航计划

百度信息流和搜索业务中的KV存储实践

百度Geek说

后端 搜索

OceanBase 源码解读(五):租户的一生

OceanBase 数据库

数据开发 oceanbase OceanBase 开源 OceanBase 社区版 OceanBase 数据库大赛

2021年9月数据库流行度排行解读:聊聊国产数据库可以从哪方面做到以用户为中心

墨天轮

数据库 TiDB oceanbase 国产数据库 达梦

linux之秘钥登录

入门小站

Linux

在线985,211高校查询

入门小站

工具

MLOps生产中的机器学习:为什么你应该关心数据和概念漂移 易筋 ARTS 打卡 Week 67

John(易筋)

ARTS 打卡计划

带你读论文丨基于视觉匹配的自适应文本识别

华为云开发者联盟

损失函数 视觉 文本识别 文档识别 视觉匹配

大厂面试喜欢考算法,该怎么破?

博文视点Broadview

低代码平台的功能及其用处

低代码小观

程序员 低代码 开发工具 低代码开发平台 无代码

MPU:鸿蒙轻内核的任务栈的溢出检察官

华为云开发者联盟

鸿蒙 内核 任务栈 MPU 内存保护单元

密码学系列之:1Password的加密基础PBKDF2

程序那些事

算法 加密解密 密码学 程序那些事

maven如何忽略指定的远程仓库

小江

maven nexus 迁移 java;

[微服务] You built it.You fix it.

baiyutang

微服务 9月日更

2B 销售系统设计需要考虑的 3 个层面

boshi

团队管理 销售管理

深入理解Netty-从偶现宕机看Netty流量控制

vivo互联网技术

Java、 框架 netty

卷王本卷

FunTester

内卷 FunTester

  • 扫码添加小助手
    领取最新资料包
二次元看过来!基于 Serverless 的舞萌音游查分器_开源_TencentServerless_InfoQ精选文章