Happy Xmas!

0x00.前言

整个上文,见【1024,Serverless】maimai_DX 查分器,本文针对部分内容进行再补充
2020-12-24 00:19:31:意识到回望2020,你在技术之路上,有什么收获和成长么?对于未来,你有什么期待么?云+社区年度征文,各种定制好礼等你!征稿活动已经接近尾声
无奈工作遇到Deadline今晚爆肝jb到了21:00后才骑车回家,本来准备放弃去睡觉,后来想想还是快速给写个大概吧
2020-12-24 23:17:05:不知道是因为下午部门圣诞节分发的☕️一直喝到了晚上并爆肝995 jb,还是因为熊熊勇闯异世界12话剧情实在是太沉重了,现在整个人都不好了……
冥思苦想了几分钟才决定好本文到底该写些什么

0x01.WSGI

说起WSGI,只要是写过Python Web的人多多少少都应该会听说过Web服务器网关接口,先来看看维基的解释

Web服务器网关接口(Python Web Server Gateway Interface,缩写为WSGI)是为Python语言定义的Web服务器和Web应用程序或框架之间的一种简单而通用的接口。自从WSGI被开发出来以后,许多其它语言中也出现了类似接口

再来看官方文档,没错是在PEP 3333中引入的,这个在2010年创建的PEP详细描述了什么是Python Web Server Gateway Interface

WSGI[1] is not a server, a python module, a framework, an API or any kind of software. It is just an interface specification by which server and application communicate. Both server and application interface sides are specified in the PEP 3333. If an application (or framework or toolkit) is written to the WSGI spec then it will run on any server written to that spec.

简单来说,WSGI包含两个部分,服务器或网关端以及应用程序或框架端

1. 应用程序或框架端

  • 它仅仅是一个接收两个参数的可调用(callable对象,函数、方法、类等具有__call__方法的object都属于前句话中对象的范畴内,并且这个对象必须可以多次调用
    虽然名字叫做应用程序,但写web app的开发人员并不会直接拿WSGI作为api使用,而是会去用上层更高级的框架(比如Flask等),写框架和服务器开发者才会面对WSGI编程(
  • The Application/Framework Side的示例代码中可以看到接收的两个对象分别是:environstart_response。这俩位置参数是必须的,虽然命名可以不这么命
    environ参数是一个字典对象,必须包含某些WSGI所需的变量(除非值为空时可省略),如下面的CGI变量:
  1. REQUEST_METHOD
  2. SCRIPT_NAME
  3. PATH_INFO
  4. QUERY_STRING
  5. CONTENT_TYPE
  6. CONTENT_LENGTH
  7. SERVER_NAME, SERVER_PORT
  8. SERVER_PROTOCOL
  9. HTTP_ Variables

另外,还必须包含以下WSGI定义的变量

  1. wsgi.version
  2. wsgi.url_scheme
  3. wsgi.input
  4. wsgi.errors
  5. wsgi.multithread
  6. wsgi.multiprocess
  7. wsgi.run_once
  • 通过调用start_response(status, response_headers, exc_info = None)发送状态码(HTTP Status Code)和头部信息(HTTP Header
    整个函数的返回值为响应内容(HTTP Body)是一个write(body_data)可调用对象

2. 服务器或网关端

客户端发起了一个请求之后,它就会去调一次前文中的可调用对象。参照The Server/Gateway Side示例代码即result = application(environ,start_response)
正好对应上前文中的接收的两个对象

3.中间件

本来仅有12就足够了,这里的中间件是可选功能(

0x02.传统Web服务与Serverless

Serverless通常翻译为「无服务架构」,是一种软件系统设计架构思想和方法,并不是一个开发框架或者工具。它的出现是为了让开发者更加关注业务的开发,而将繁杂的运维和部署交给云厂商。ServerlessFaasBaas组成,Faas为开发者提供业务运算环境,然后与Baas提供的数据和存储服务,进行交互,从而提供与传统服务一致的体验。但是由于Faas是无状态的,并且其运行环境是有读写限制的,最重要的是它是基于事件触发的。因此如果传统Web服务想迁移到Serverless上,是需要进行相关改造和特殊处理的,为此迁移成本是必不可少的

腾讯云 Serverless 产品家族
腾讯云 Serverless 产品家族

  1. 传统Web服务:日常生活中接触最多的就是HTTP服务,客户端发起请求,服务端接受请求后进行处理最后返回响应。部署流程是需要将项目代码扔到云主机上
    根据不用的语言启用不同的Web服务器常驻进程,并监听云主机相关接口,等待客户端的到来(
  2. Serverless:客户端发起请求至网关,网关触发事件至云函数云函数返回响应至网关最终到达客户端
    应用场景
    应用场景
  • Serverless Framework的基础组件不仅包含SCF 组件目前还包含:Website 组件API 网关组件VPC 组件COS 组件PostgreSQL 组件CynosDB 组件CDN 组件Layer 组件调度其他云上资源
  • 云函数基于网关触发事件被调用,传统Web服务Serverless化的核心就是可以将Event对象转化为HTTP请求,也就是AdapterServerless Components中的高阶组件就适配了常用框架
    serverless cli相关脚手架工具是基于node的,但是不必担心并不是只能部署node相关的服务……
  • 想知道它到底干了些什么?最直接的方法就是翻源码鸭!拿tencent-flask举个栗子:
    src
    src

直接看serverless.js,整个部署流程一共分三步:①标准化 Inputs②部署云函数③部署网关(把大象装进冰箱

async deploy(inputs)
async deploy(inputs)

再来深♂入看一下

1.②部署云函数

async deployFunction(credentials, inputs, regionList)
async deployFunction(credentials, inputs, regionList)

首先上来进行校验凭据,之后将本地代码打包的压缩包传到COS上,通过COS再加载到云函数之中并配置区域、版本和流量分配
备注:其中关于打包,会判断是否是由模板创建,如果不是则会将src下的_shims文件夹中的全部内容注入,实际部署时会原封不动地被放到云函数环境的~路径下

shimFiles
shimFiles

对于这个栗子则会将sl_handler.pyserverless_wsgi.py这两个文件上传

  1. sl_handler.py,这个文件的handler函数就是默认云函数的配置的入口文件,根据自己项目的需要可以自定义入口文件。比如使用了工厂模式就可以改写如下并指定云函数的入口为severless_handle.pyhandler函数即可
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    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)
  2. severless_wsgi.py,这个文件中的handle_request(app, event, context)函数则会将来自网关的eventcontext会转换为environ并调用app生成response,并可根据网关的需要进行base64编码

2.③部署网关

在这个步骤之前还插入了一步尝试添加自定义域名的CNAME的操作。然而之后并没有看的很明白,不过应该是根据serviceId来对应更新状态

async deployApigateway(credentials, inputs, regionList)
async deployApigateway(credentials, inputs, regionList)

0x03.再谈查分器

  • 毕竟数据都是来自于爬虫,半年前基本写完的maimai_DX_CN_saver也一直处于停滞状态中。
    页面适配中大方面的排行榜是不打算去做了,实时性太强的数据存到本地基本上没有太大意义,参考MuseDash空间过大被发邮件,然后现在放到了MuseDash-backup,排行榜数据是以json格式存储的,后来用转化为LFS对象另存了一份MuseDash
  • 针对于查分器公开使用?目前已经实装了前端手动存储,不过是提交一个含有原页面html的表单到后台再进行提取数据存入数据库的。因为华立封禁了云主机的ip也就是说云端爬虫肯定是做不了了,然而这是在已经写完自动存储功能之后云端上线之后才意识到……所以自动存储的功能只能在本地跑了(
  • 趣事:当初使用API 网关时并不支持响应内容压缩,于是自己去找了个Flask的库实装上,其实这本来应该是网关所支持的功能,然后最近发现API 网关现在支持了(

0x04.后记

有了这次的开发经验,针对wacca音游可以写出一个类似的查分器了(

0x05.引用

如何将 Web 框架迁移到 Serverless
如何为Serverless架构做了一个Django的Component