{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2020-10-24-maimai","result":{"data":{"currentBlog":{"id":"5d75ad51-0cf8-5328-97bc-55494112b7b0","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020115/1604567602988-WX20201105-160934.jpg","authors":["远哥制造"],"categories":["user-stories"],"date":"2020-10-24T00:00:00.000Z","title":"二次元看过来！基于 Serverless 的舞萌音游查分器","description":"Serverless 和音乐游戏蹦出新火花🔥","authorslink":["https://www.yuangezhizao.cn/articles/python/flask/serverless/maimai_DX_CN_probe.html"],"translators":null,"translatorslink":null,"tags":["Serverless","二次元"],"keywords":null,"outdated":null},"wordCount":{"words":470,"sentences":98,"paragraphs":95},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-10-24-maimai.md","fields":{"slug":"/blog/2020-10-24-maimai/","keywords":["go","postgresql","python","serverless","无服务器","云函数","wsgi","maimai","requirements","Flask","serverless"]},"html":"<h2 id=\"前言\"><a href=\"#%E5%89%8D%E8%A8%80\" aria-label=\"前言 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>前言</h2>\n<p>社畜下班时刷微信时看到了<a href=\"https://web.archive.org/web/20201020122345/https://mp.weixin.qq.com/s/jaCLx6gn3aHbLLptXvqsLQ\">Serverless 有一百种玩法，比好玩更好玩</a>这篇推送，正巧自己最近断断续续在写音游的历史记录存档，趁着这个机会决定参加这次应用开发。</p>\n<p><strong>一、什么是 Serverless Framework</strong></p>\n<blockquote>\n<p><code class=\"language-text\">Serverless Framework</code> 是业界非常受欢迎的无服务器应用框架，开发者无需关心底层资源即可部署完整可用的 <code class=\"language-text\">Serverless</code> 应用架构。<code class=\"language-text\">Serverless Framework</code> 具有资源编排、自动伸缩、事件驱动等能力，覆盖编码、调试、测试、部署等全生命周期，帮助开发者通过联动云资源，迅速构建 <code class=\"language-text\">Serverless</code> 应用</p>\n</blockquote>\n<p>没错，就像几天前看到的《Serverless 之歌》里面所说 <code class=\"language-text\">I&#39;m gonna reduce your ops</code>，它能大幅度减轻运维压力，那就开始动手吧！注意开发环境需 <code class=\"language-text\">Node.js 10.0+</code>，一键全局安装：<code class=\"language-text\">npm install -g serverless</code></p>\n<p><strong>二、腾讯云 Flask Serverless Component 简介</strong></p>\n<blockquote>\n<p>腾讯云 <code class=\"language-text\">Flask Serverless Component</code>，支持 <code class=\"language-text\">Restful API</code> 服务的部署</p>\n</blockquote>\n<p>按照惯例首先来部署 <code class=\"language-text\">demo</code> 吧</p>\n<ol>\n<li>本地 <code class=\"language-text\">PyCharm</code> 创建一个新的 <code class=\"language-text\">Flask</code> 项目</li>\n</ol>\n<p><img src=\"https://img.serverlesscloud.cn/20201111/1605082790529-QQ20201020-212721%402x.png%21webp\" alt=\"Flask\"></p>\n<ol start=\"2\">\n<li>手动创建内容为 <code class=\"language-text\">Flask</code> 的 <code class=\"language-text\">requirements.txt</code></li>\n<li>按照<a href=\"https://github.com/serverless-components/tencent-flask/blob/master/docs/configure.md\">配置文档</a>创建 <code class=\"language-text\">serverless.yml</code>，例如本项目实际使用的完整内容，初次使用可自行酌情简化</li>\n<li>将密匙写入 <code class=\"language-text\">.env</code>（当然，部署的时候也可以选择微信扫码授权）</li>\n</ol>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"79567051964326510000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`TENCENT_SECRET_ID=<rm>\nTENCENT_SECRET_KEY=<rm>`, `79567051964326510000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token assign-left variable\">TENCENT_SECRET_ID</span><span class=\"token operator\">=</span><span class=\"token operator\">&lt;</span>rm<span class=\"token operator\">></span>\n<span class=\"token assign-left variable\">TENCENT_SECRET_KEY</span><span class=\"token operator\">=</span><span class=\"token operator\">&lt;</span>rm<span class=\"token operator\">></span></code></pre></div>\n<p><img src=\"https://img.serverlesscloud.cn/20201111/1605082869319-QQ20201020-225316%402x.png%21webp\" alt=\"成功部署\"></p>\n<p><img src=\"https://img.serverlesscloud.cn/20201111/1605082907209-QQ20201020-230006%402x.png%21webp\" alt=\"成功访问\"></p>\n<p>这样基于 <code class=\"language-text\">Serverless</code> 的 <code class=\"language-text\">Flask Demo</code> 就部署完成了，接下来继续按照自己的方式写剩下的代码。</p>\n<p><strong>三、maimai_DX</strong></p>\n<p><a href=\"https://mzh.moegirl.org.cn/zh-hans/Maimai%E7%B3%BB%E5%88%97\">maimai</a> 是一款街机音游。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604563824205-WX20201105-160934%402x.jpg\"></p>\n<p>在这里放一张动图自行体会一下，原始素材来自<a href=\"https://www.bilibili.com/video/av457219654\">「外录 maimai」QZKago Requiem Re:MASTER ALLPERFECT Player: Ruri*R</a></p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201024-213604-HD.gif!webp\" alt=\"QZKago\"></p>\n<blockquote>\n<ul>\n<li><a href=\"https://maimai.sega.jp/\">日本官网</a></li>\n<li><a href=\"https://maimai.sega.com/\">海外官网</a></li>\n</ul>\n</blockquote>\n<p>在国内，只能从微信公众号中查看成绩，而且每次进页面都需要微信的授权登录，并且里面存储的记录有条数限制，<code class=\"language-text\">相册</code>只存最新 10 条，<code class=\"language-text\">游戏记录</code>只存最新 50 条（就是一个队列，先进先出的那种）。这就是本项目的初衷，自己打出来的每一次成绩都应该保存好。</p>\n<h2 id=\"舞萌查分器\"><a href=\"#%E8%88%9E%E8%90%8C%E6%9F%A5%E5%88%86%E5%99%A8\" aria-label=\"舞萌查分器 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>舞萌查分器</h2>\n<p>成果展示了，前端 <code class=\"language-text\">Fomantic-UI</code>，后端 <code class=\"language-text\">Flask</code>+<code class=\"language-text\">MySQL</code>。<code class=\"language-text\">gh</code> 开源地址：<a href=\"https://github.com/yuangezhizao/maimai_DX_CN_probe\">https://github.com/yuangezhizao/maimai<em>DX</em>CN_probe</a>，欢迎 <code class=\"language-text\">watch</code>、<code class=\"language-text\">star</code>、<code class=\"language-text\">fork</code> &#x26; <code class=\"language-text\">pr</code>！</p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20200515-185154@2x.png!webp\" alt=\"https://maimai.yuangezhizao.cn\"></p>\n<p>目前实装了如下功能：</p>\n<ol>\n<li><a href=\"https://maimai.yuangezhizao.cn/wechat_archive\">wechat_archive</a>中包含 <code class=\"language-text\">主页</code>，<code class=\"language-text\">游戏数据</code>，<code class=\"language-text\">相册</code> 和 <code class=\"language-text\">游戏记录</code>：对原始网页进行了修改，并且添加了 <code class=\"language-text\">Highcharts</code> 库可视化曲线显示变化</li>\n<li><a href=\"https://maimai.yuangezhizao.cn/record\">record</a>包含 <code class=\"language-text\">记录（分页）</code> 和 <code class=\"language-text\">差异（分页）</code>：即自写的快速预览页面，是查看历史记录和成绩变化的非常实用的功能</li>\n<li><a href=\"https://maimai.yuangezhizao.cn/info\">info</a>包含 <code class=\"language-text\">铺面列表</code>：即全部铺面基础信息，输出到一个页面中，方便页面内搜索</li>\n</ol>\n<h2 id=\"开发过程\"><a href=\"#%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B\" aria-label=\"开发过程 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>开发过程</h2>\n<p>接下来将按照时间的顺序，描述一下开发过程中遇到的问题以及如何解决</p>\n<h3 id=\"1-serverless-framework-component-配置文件\"><a href=\"#1-serverless-framework-component-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6\" aria-label=\"1 serverless framework component 配置文件 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>1. <code class=\"language-text\">Serverless Framework Component</code> 配置文件</h3>\n<p><code class=\"language-text\">Serverless Framework</code> 现在是 <code class=\"language-text\">V2</code> 版本，也就是说不能沿袭之前版本的 <code class=\"language-text\">serverless.yml</code> 配置文件，需要重新对照文档修改。</p>\n<p>a. 之前版本会根据 <code class=\"language-text\">requirements.txt</code> 自动下载第三方库到项目目录下的 <code class=\"language-text\">.serverless</code> 文件夹下的 <code class=\"language-text\">requirements</code> 文件夹以参加最终的依赖打包，压缩成 <code class=\"language-text\">zip</code> 文件再最终上传至云函数运行环境</p>\n<p>b. 最新版本不再自动下载，需要自行处理。官方示例的参考用法：<a href=\"https://github.com/serverless-components/tencent-flask/blob/49bc914fad091bad9202ac481042760509342b3d/example/serverless.yml#L8-L17\">hook</a></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"14611488859623422000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  src:\n    # TODO: 安装python项目依赖到项目当前目录\n    hook: 'pip3 install -r requirements.txt -t ./requirements'\n    dist: ./\n    include:\n      - source: ./requirements\n        prefix: ../ # prefix, can make ./requirements files/dir to ./\n    exclude:\n      - .env\n      - 'requirements/**'`, `14611488859623422000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\">  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token comment\"># TODO: 安装python项目依赖到项目当前目录</span>\n    <span class=\"token key atrule\">hook</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'pip3 install -r requirements.txt -t ./requirements'</span>\n    <span class=\"token key atrule\">dist</span><span class=\"token punctuation\">:</span> ./\n    <span class=\"token key atrule\">include</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">source</span><span class=\"token punctuation\">:</span> ./requirements\n        <span class=\"token key atrule\">prefix</span><span class=\"token punctuation\">:</span> ../ <span class=\"token comment\"># prefix, can make ./requirements files/dir to ./</span>\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env\n      <span class=\"token punctuation\">-</span> <span class=\"token string\">'requirements/**'</span></code></pre></div>\n<p>注释写的很清楚，使用 <code class=\"language-text\">hook</code> 去根据 <code class=\"language-text\">requirements.txt</code> 下载第三方库到项目目录下的 <code class=\"language-text\">requirements</code> 文件夹，避免第三方库导致本地文件夹管理混乱。然后 <code class=\"language-text\">include</code> 中指定了项目目录下的 <code class=\"language-text\">requirements</code> 文件夹在云端的 <code class=\"language-text\">prefix</code>，即对于云端的云函数运行环境，<code class=\"language-text\">requirements</code> 文件夹中的第三方库和项目目录是同级的，可以正常导入使用。当然了，本地运行使用的是全局的第三方库，并未用到项目目录下的 <code class=\"language-text\">requirements</code> 文件夹。</p>\n<h3 id=\"2-层管理概述\"><a href=\"#2-%E5%B1%82%E7%AE%A1%E7%90%86%E6%A6%82%E8%BF%B0\" aria-label=\"2 层管理概述 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>2. 层管理概述</h3>\n<p>前者（指 b）是一个很合理的设计，不过在实际环境中却发现了新的问题。完全一致的配置文件</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"96551345459729700000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  src:\n    hook: 'pip3 install -r ./src/requirements.txt -t ./src/requirements'\n    dist: ./src\n    include:\n      - source: ./requirements\n        prefix: ../\n    exclude:\n      - .env`, `96551345459729700000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\">  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">hook</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'pip3 install -r ./src/requirements.txt -t ./src/requirements'</span>\n    <span class=\"token key atrule\">dist</span><span class=\"token punctuation\">:</span> ./src\n    <span class=\"token key atrule\">include</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">source</span><span class=\"token punctuation\">:</span> ./requirements\n        <span class=\"token key atrule\">prefix</span><span class=\"token punctuation\">:</span> ../\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env</code></pre></div>\n<p>在 macOS 下成功部署之后，云端的云函数编辑器中看到 <code class=\"language-text\">requirements</code> 文件夹不存在，第三方库和项目目录是同级的，的确没问题。</p>\n<p>不过在 Windows 下成功部署之后，云端的云函数编辑器中看到了 <code class=\"language-text\">requirements</code> 文件夹？也就是说第三方库和项目目录非同级，于是访问就会出现 <code class=\"language-text\">no module found</code> 的导入报错了……</p>\n<p>反复尝试修改 <code class=\"language-text\">prefix</code> 等配置项到最后也没有调试成功，因此在这里提出两种解决方法：</p>\n<p>a. 修改配置文件如下，让本地的第三方库和项目目录同级存在</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"92219625093509680000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  src:\n    hook: 'pip3 install -r ./src/requirements.txt -t ./src'\n    dist: ./src\n    exclude:\n      - .env`, `92219625093509680000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\">  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">hook</span><span class=\"token punctuation\">:</span> <span class=\"token string\">'pip3 install -r ./src/requirements.txt -t ./src'</span>\n    <span class=\"token key atrule\">dist</span><span class=\"token punctuation\">:</span> ./src\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env</code></pre></div>\n<p>不过随着项目和第三方库的扩大文件夹会越来越多，非常不便于管理</p>\n<p>b. 使用云函数提供的 <code class=\"language-text\">层</code></p>\n<p>虽然 <code class=\"language-text\">sls deploy</code> 部署的速度很快，但是如果可以在部署时只上传项目代码而不去处理依赖不就更好了嘛，这样跨终协作端开发只需要关心项目代码就 <code class=\"language-text\">ok</code> 了，再也不需要管理依赖！</p>\n<p>并且还有一点，想在 <code class=\"language-text\">SCF</code> 控制台中在线编辑函数代码需要将部署程序包保持在 <code class=\"language-text\">10MB</code> 以下，不要以为十兆很大，很快就用光也是可能的</p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201022-210610@2x.png!webp\" alt=\"仅显示入口文件\"></p>\n<p>具体如何操作呢？那就是要将第三方库文件夹直接打包并创建为层，则在函数代码中可直接通过 <code class=\"language-text\">import</code> 引用，毕竟有些特殊库比如 <code class=\"language-text\">Brotli</code>，Windows 下没有 <code class=\"language-text\">vc++</code> 的话就只能去<a href=\"https://lfd.uci.edu/~gohlke/pythonlibs\">https://lfd.uci.edu/~gohlke/pythonlibs</a>下载 <code class=\"language-text\">wheel</code> 安装。</p>\n<p>macOS 下正常安装之后会得到 <code class=\"language-text\">_brotli.cpython-39-darwin.so</code>，<code class=\"language-text\">brotli.py</code> 中再以 <code class=\"language-text\">import _brotli</code> 的形式导入，不过又出新问题了，云端会导入报错<code class=\"language-text\">ModuleNotFoundError: No module named &#39;_brotli&#39;&quot;</code></p>\n<blockquote>\n<p>当前 <code class=\"language-text\">SCF</code> 的执行环境建立在以下基础上：标准 <code class=\"language-text\">CentOS 7.2</code></p>\n</blockquote>\n<p>为了解决问题尝试在 linux 环境下打包，拿起手头的 <code class=\"language-text\">CentOS 8.2</code> 云主机开始操作</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"61268188627774010000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`pip3 install -r requirements.txt -t ./layer --upgrade\nzip -r layer.zip ./layer`, `61268188627774010000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">pip3 <span class=\"token function\">install</span> -r requirements.txt -t ./layer --upgrade\n<span class=\"token function\">zip</span> -r layer.zip ./layer</code></pre></div>\n<p>然后就可以把打包的 <code class=\"language-text\">layer.zip</code> 下载到本地再传上去了，暂时可以一劳永逸了。</p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201022-234808@2x.png!webp\" alt=\"层详细信息\"></p>\n<p>对了，配置文件可以移除 <code class=\"language-text\">hook</code> 并添加 <code class=\"language-text\">layers</code> 了</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"23185234407999220000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`  src:\n    src: ./src\n    exclude:\n      - .env\n      - '__pycache__/**'\n  layers:\n    - name: maimai_DX_CN_probe\n      version: 3`, `23185234407999220000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\">  <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">src</span><span class=\"token punctuation\">:</span> ./src\n    <span class=\"token key atrule\">exclude</span><span class=\"token punctuation\">:</span>\n      <span class=\"token punctuation\">-</span> .env\n      <span class=\"token punctuation\">-</span> <span class=\"token string\">'__pycache__/**'</span>\n  <span class=\"token key atrule\">layers</span><span class=\"token punctuation\">:</span>\n    <span class=\"token punctuation\">-</span> <span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> maimai_DX_CN_probe\n      <span class=\"token key atrule\">version</span><span class=\"token punctuation\">:</span> <span class=\"token number\">3</span></code></pre></div>\n<blockquote>\n<p>已绑定层的函数被触发运行，启动并发实例时，将会解压加载函数的运行代码至 <code class=\"language-text\">/var/user/</code> 目录下，同时会将层内容解压加载至 <code class=\"language-text\">/opt</code> 目录下。若需使用或访问的文件 <code class=\"language-text\">file</code>，放置在创建层时压缩文件的根目录下。则在解压加载后，可直接通过目录 <code class=\"language-text\">/opt/file</code> 访问到该文件。若在创建层时，通过文件夹进行压缩 <code class=\"language-text\">dir/file</code>，则在函数运行时需通过 <code class=\"language-text\">/opt/dir/file</code> 访问具体文件</p>\n</blockquote>\n<p>体验更快的部署速度吧！因为第三方库已经打包在“层”中了</p>\n<p>但是奇怪的是，在云端导入任意第三方库均会报错，于是调试着查看 <code class=\"language-text\">path</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"14638537152288400000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`for path in sys.path:\n    print(path)\n\n/var/runtime/python3\n/var/user\n/opt\n/var/lang/python3/lib/python36.zip\n/var/lang/python3/lib/python3.6\n/var/lang/python3/lib/python3.6/lib-dynload\n/var/lang/python3/lib/python3.6/site-packages\n/var/lang/python3/lib/python3.6/site-packages/pip-18.0-py3.6.egg`, `14638537152288400000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token keyword\">for</span> <span class=\"token for-or-select variable\">path</span> <span class=\"token keyword\">in</span> sys.path:\n    print<span class=\"token punctuation\">(</span>path<span class=\"token punctuation\">)</span>\n\n/var/runtime/python3\n/var/user\n/opt\n/var/lang/python3/lib/python36.zip\n/var/lang/python3/lib/python3.6\n/var/lang/python3/lib/python3.6/lib-dynload\n/var/lang/python3/lib/python3.6/site-packages\n/var/lang/python3/lib/python3.6/site-packages/pip-18.0-py3.6.egg</code></pre></div>\n<p>再查看 <code class=\"language-text\">opt</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"46911313177385460000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import os\ndirs = os.listdir('/opt')\n\nfor file in dirs:\n   print(file)\n\nlayer`, `46911313177385460000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\"><span class=\"token function\">import</span> os\n<span class=\"token function\">dirs</span> <span class=\"token operator\">=</span> os.listdir<span class=\"token punctuation\">(</span><span class=\"token string\">'/opt'</span><span class=\"token punctuation\">)</span>\n\n<span class=\"token keyword\">for</span> <span class=\"token for-or-select variable\">file</span> <span class=\"token keyword\">in</span> dirs:\n   print<span class=\"token punctuation\">(</span>file<span class=\"token punctuation\">)</span>\n\nlayer</code></pre></div>\n<p>这才恍然大悟，<strong>打包时需要在当前路径直接打包</strong>。上传之后“层”更新为版本 <code class=\"language-text\">2</code>，但是 <code class=\"language-text\">ModuleNotFoundError: No module named &#39;_brotli&#39;</code> 报错依旧，并且确认 <code class=\"language-text\">_brotli.cpython-38-x86_64-linux-gnu.so</code> 文件实际存在。</p>\n<p>而在 <code class=\"language-text\">CentOS</code> 和 <code class=\"language-text\">macOS</code> 上本地导入均没有问题，这可就犯难了，又想到很有可能是 <code class=\"language-text\">python</code> 版本的问题，于是去寻找现成 <code class=\"language-text\">3.6</code> 的环境，比如这里：</p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201022-233709@2x.png!webp\" alt=\"3.6.8\"></p>\n<p>再再次上传之后“层”更新为版本 <code class=\"language-text\">3</code>，访问成功！课题终于解决，原来是需要<strong>相同版本</strong>的 <code class=\"language-text\">Python 3.6</code> 运行环境</p>\n<h3 id=\"3自定义入口文件\"><a href=\"#3%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%A5%E5%8F%A3%E6%96%87%E4%BB%B6\" aria-label=\"3自定义入口文件 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>3.自定义入口文件</h3>\n<p><a href=\"https://github.com/serverless-components\">components</a>源码<a href=\"https://github.com/serverless-components/tencent-flask/tree/master/src/_shims\">tencent-flask/src/_shims/</a>中的文件每次都会被原封不动地重新打包上传到云端云函数中，目前有两个文件</p>\n<p>a. <code class=\"language-text\">severless_wsgi.py</code>，作用是 <code class=\"language-text\">converts an AWS API Gateway proxied request to a WSGI request.</code>\n<code class=\"language-text\">WSGI</code>的全称是<code class=\"language-text\">Python Web Server Gateway Interface</code>即<code class=\"language-text\">Web 服务器网关接口</code>，它是为<code class=\"language-text\">Python</code>语言定义的<code class=\"language-text\">Web</code>服务器和<code class=\"language-text\">Web</code>应用程序或框架之间的一种简单而通用的接口</p>\n<p>b. <code class=\"language-text\">sl_handler.py</code>，就是默认的入口文件</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"5012669628176503000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import app  # Replace with your actual application\nimport severless_wsgi\n\n# If you need to send additional content types as text, add then directly\n# to the whitelist:\n#\n# serverless_wsgi.TEXT_MIME_TYPES.append(&quot;application/custom+json&quot;)\n\ndef handler(event, context):\n    return severless_wsgi.handle_request(app.app, event, context)`, `5012669628176503000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> app  <span class=\"token comment\"># Replace with your actual application</span>\n<span class=\"token keyword\">import</span> severless_wsgi\n\n<span class=\"token comment\"># If you need to send additional content types as text, add then directly</span>\n<span class=\"token comment\"># to the whitelist:</span>\n<span class=\"token comment\">#</span>\n<span class=\"token comment\"># serverless_wsgi.TEXT_MIME_TYPES.append(\"application/custom+json\")</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> severless_wsgi<span class=\"token punctuation\">.</span>handle_request<span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span></code></pre></div>\n<p>针对于自己的项目，使用了 <code class=\"language-text\">Flask</code> 的 <code class=\"language-text\">工厂函数</code>，为了避免每次都要在云端云函数编辑器中重新修改，最好的方法是自定义入口文件：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"17397040436418742000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import severless_wsgi\n\nfrom maimai_DX_CN_probe import create_app  # Replace with your actual application\n\n\n# If you need to send additional content types as text, add then directly\n# to the whitelist:\n#\n# serverless_wsgi.TEXT_MIME_TYPES.append(&quot;application/custom+json&quot;)\n\ndef handler(event, context):\n    return severless_wsgi.handle_request(create_app(), event, context)`, `17397040436418742000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> severless_wsgi\n\n<span class=\"token keyword\">from</span> maimai_DX_CN_probe <span class=\"token keyword\">import</span> create_app  <span class=\"token comment\"># Replace with your actual application</span>\n\n\n<span class=\"token comment\"># If you need to send additional content types as text, add then directly</span>\n<span class=\"token comment\"># to the whitelist:</span>\n<span class=\"token comment\">#</span>\n<span class=\"token comment\"># serverless_wsgi.TEXT_MIME_TYPES.append(\"application/custom+json\")</span>\n\n<span class=\"token keyword\">def</span> <span class=\"token function\">handler</span><span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">return</span> severless_wsgi<span class=\"token punctuation\">.</span>handle_request<span class=\"token punctuation\">(</span>create_app<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> event<span class=\"token punctuation\">,</span> context<span class=\"token punctuation\">)</span></code></pre></div>\n<p>再指定 <code class=\"language-text\">执行方法</code> 为 <code class=\"language-text\">serverless_handler.handler</code>，就 ok 了</p>\n<h3 id=\"4-url_for-输出-http-而非-https-的-url\"><a href=\"#4-url_for-%E8%BE%93%E5%87%BA-http-%E8%80%8C%E9%9D%9E-https-%E7%9A%84-url\" aria-label=\"4 url_for 输出 http 而非 https 的 url permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>4. <code class=\"language-text\">url_for</code> 输出 <code class=\"language-text\">http</code> 而非 <code class=\"language-text\">https</code> 的 <code class=\"language-text\">URL</code></h3>\n<p>在视图函数中重定向到 <code class=\"language-text\">url_for</code> 所生成的链接都是 <code class=\"language-text\">http</code>，而不是 <code class=\"language-text\">https</code>……其实这个问题 <code class=\"language-text\">Flask</code> 的文档 <a href=\"https://flask.palletsprojects.com/en/1.1.x/deploying/wsgi-standalone/\">Standalone WSGI Containers</a>有描述到</p>\n<p>说到底这并不是 <code class=\"language-text\">Flask</code> 的问题，而是 <code class=\"language-text\">WSGI</code> 环境所导致的问题，推荐的方法是使用<strong>中间件</strong>，官方也给出了 <code class=\"language-text\">ProxyFix</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"8960114872143055000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`from werkzeug.middleware.proxy_fix import ProxyFix\napp.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1, x_host=1)`, `8960114872143055000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">from</span> werkzeug<span class=\"token punctuation\">.</span>middleware<span class=\"token punctuation\">.</span>proxy_fix <span class=\"token keyword\">import</span> ProxyFix\napp<span class=\"token punctuation\">.</span>wsgi_app <span class=\"token operator\">=</span> ProxyFix<span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">.</span>wsgi_app<span class=\"token punctuation\">,</span> x_proto<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> x_host<span class=\"token operator\">=</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span></code></pre></div>\n<p>但是是从<code class=\"language-text\">X-Forwarded-Proto</code>中取的值，<code class=\"language-text\">apigw</code>中其为<code class=\"language-text\">http</code>，因此并不能直接使用这个<code class=\"language-text\">ProxyFix</code>\n因为<code class=\"language-text\">Flask</code>的社区还算完善，参考资料很多前人都铺好了路，所以直接去<code class=\"language-text\">Stack Overflow</code>搜解决方法，<a href=\"https://stackoverflow.com/questions/14810795/flask-url-for-generating-http-url-instead-of-https\">Flask url_for generating http URL instead of https</a>\n问题出现的原因如图：<code class=\"language-text\">Browser ----- HTTPS ----&gt; Reverse proxy（apigw） ----- HTTP ----&gt; Flask</code>\n因为自己在<code class=\"language-text\">apigw</code>设置了<code class=\"language-text\">前端类型</code>仅<code class=\"language-text\">https</code>，也就是说<code class=\"language-text\">Browser</code>端是不可能使用<code class=\"language-text\">http</code>访问到的，通过打印<code class=\"language-text\">environ</code>可知</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"63775580098515490000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n  &quot;CONTENT_LENGTH&quot;: &quot;0&quot;,\n  &quot;CONTENT_TYPE&quot;: &quot;&quot;,\n  &quot;PATH_INFO&quot;: &quot;/&quot;,\n  &quot;QUERY_STRING&quot;: &quot;&quot;,\n  &quot;REMOTE_ADDR&quot;: &quot;&quot;,\n  &quot;REMOTE_USER&quot;: &quot;&quot;,\n  &quot;REQUEST_METHOD&quot;: &quot;GET&quot;,\n  &quot;SCRIPT_NAME&quot;: &quot;&quot;,\n  &quot;SERVER_NAME&quot;: &quot;maimai.yuangezhizao.cn&quot;,\n  &quot;SERVER_PORT&quot;: &quot;80&quot;,\n  &quot;SERVER_PROTOCOL&quot;: &quot;HTTP/1.1&quot;,\n  &quot;wsgi.errors&quot;: <__main__.CustomIO object at 0x7feda2224630>,\n  &quot;wsgi.input&quot;: <_io.BytesIO object at 0x7fed97093410>,\n  &quot;wsgi.multiprocess&quot;: False,\n  &quot;wsgi.multithread&quot;: False,\n  &quot;wsgi.run_once&quot;: False,\n  &quot;wsgi.url_scheme&quot;: &quot;http&quot;,\n  &quot;wsgi.version&quot;: (1, 0),\n  &quot;serverless.authorizer&quot;: None,\n  &quot;serverless.event&quot;: &quot;<rm>&quot;,\n  &quot;serverless.context&quot;: &quot;<rm>&quot;,\n  &quot;API_GATEWAY_AUTHORIZER&quot;: None,\n  &quot;event&quot;: &quot;<rm>&quot;,\n  &quot;context&quot;: &quot;<rm>&quot;,\n  &quot;HTTP_ACCEPT&quot;: &quot;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&quot;,\n  &quot;HTTP_ACCEPT_ENCODING&quot;: &quot;gzip, deflate, br&quot;,\n  &quot;HTTP_ACCEPT_LANGUAGE&quot;: &quot;zh-CN,zh;q=0.9,en;q=0.8&quot;,\n  &quot;HTTP_CONNECTION&quot;: &quot;keep-alive&quot;,\n  &quot;HTTP_COOKIE&quot;: &quot;<rm>&quot;,\n  &quot;HTTP_ENDPOINT_TIMEOUT&quot;: &quot;15&quot;,\n  &quot;HTTP_HOST&quot;: &quot;maimai.yuangezhizao.cn&quot;,\n  &quot;HTTP_SEC_FETCH_DEST&quot;: &quot;document&quot;,\n  &quot;HTTP_SEC_FETCH_MODE&quot;: &quot;navigate&quot;,\n  &quot;HTTP_SEC_FETCH_SITE&quot;: &quot;none&quot;,\n  &quot;HTTP_SEC_FETCH_USER&quot;: &quot;?1&quot;,\n  &quot;HTTP_UPGRADE_INSECURE_REQUESTS&quot;: &quot;1&quot;,\n  &quot;HTTP_USER_AGENT&quot;: &quot;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&quot;,\n  &quot;HTTP_X_ANONYMOUS_CONSUMER&quot;: &quot;true&quot;,\n  &quot;HTTP_X_API_REQUESTID&quot;: &quot;5bcb29af2ca18c1e6d7b1ec5ff7b5427&quot;,\n  &quot;HTTP_X_API_SCHEME&quot;: &quot;https&quot;,\n  &quot;HTTP_X_B3_TRACEID&quot;: &quot;5bcb29af2ca18c1e6d7b1ec5ff7b5427&quot;,\n  &quot;HTTP_X_QUALIFIER&quot;: &quot;\\$LATEST&quot;\n}`, `63775580098515490000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"json\"><pre class=\"language-json\"><code class=\"language-json\"><span class=\"token punctuation\">{</span>\n  <span class=\"token property\">\"CONTENT_LENGTH\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"0\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"CONTENT_TYPE\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"PATH_INFO\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"/\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"QUERY_STRING\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"REMOTE_ADDR\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"REMOTE_USER\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"REQUEST_METHOD\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"GET\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"SCRIPT_NAME\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"SERVER_NAME\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"maimai.yuangezhizao.cn\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"SERVER_PORT\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"80\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"SERVER_PROTOCOL\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"HTTP/1.1\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.errors\"</span><span class=\"token operator\">:</span> &lt;__main__.CustomIO object at <span class=\"token number\">0</span>x<span class=\"token number\">7</span>feda<span class=\"token number\">2224630</span>><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.input\"</span><span class=\"token operator\">:</span> &lt;_io.BytesIO object at <span class=\"token number\">0</span>x<span class=\"token number\">7</span>fed<span class=\"token number\">97093410</span>><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.multiprocess\"</span><span class=\"token operator\">:</span> False<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.multithread\"</span><span class=\"token operator\">:</span> False<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.run_once\"</span><span class=\"token operator\">:</span> False<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.url_scheme\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"http\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"wsgi.version\"</span><span class=\"token operator\">:</span> (<span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">0</span>)<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"serverless.authorizer\"</span><span class=\"token operator\">:</span> None<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"serverless.event\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;rm>\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"serverless.context\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;rm>\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"API_GATEWAY_AUTHORIZER\"</span><span class=\"token operator\">:</span> None<span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"event\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;rm>\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"context\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;rm>\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_ACCEPT\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"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\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_ACCEPT_ENCODING\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"gzip, deflate, br\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_ACCEPT_LANGUAGE\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"zh-CN,zh;q=0.9,en;q=0.8\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_CONNECTION\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"keep-alive\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_COOKIE\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"&lt;rm>\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_ENDPOINT_TIMEOUT\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"15\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_HOST\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"maimai.yuangezhizao.cn\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_SEC_FETCH_DEST\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"document\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_SEC_FETCH_MODE\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"navigate\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_SEC_FETCH_SITE\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"none\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_SEC_FETCH_USER\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"?1\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_UPGRADE_INSECURE_REQUESTS\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"1\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_USER_AGENT\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"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\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_X_ANONYMOUS_CONSUMER\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"true\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_X_API_REQUESTID\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"5bcb29af2ca18c1e6d7b1ec5ff7b5427\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_X_API_SCHEME\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"https\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_X_B3_TRACEID\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"5bcb29af2ca18c1e6d7b1ec5ff7b5427\"</span><span class=\"token punctuation\">,</span>\n  <span class=\"token property\">\"HTTP_X_QUALIFIER\"</span><span class=\"token operator\">:</span> <span class=\"token string\">\"$LATEST\"</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p><code class=\"language-text\">HTTP_X_FORWARDED_PROTO</code>对应<code class=\"language-text\">apigw</code>里的变量是<code class=\"language-text\">HTTP_X_API_SCHEME</code>，故解决方法如下：<a href=\"https://github.com/yuangezhizao/maimai_DX_CN_probe/blob/4600b3d8212777cb6184c796c1967b3ea9b05997/src/maimai_DX_CN_probe/__init__.py#L36\">app.wsgi<em>app = ReverseProxied(app.wsgi</em>app)</a></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"54289825187919270000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`class ReverseProxied(object):\n    def __init__(self, app):\n        self.app = app\n\n    def __call__(self, environ, start_response):\n        scheme = environ.get('HTTP_X_FORWARDED_PROTO')\n        if scheme:\n            environ['wsgi.url_scheme'] = scheme\n        return self.app(environ, start_response)\n\napp = Flask(__name__)\napp.wsgi_app = ReverseProxied(app.wsgi_app)`, `54289825187919270000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">class</span> <span class=\"token class-name\">ReverseProxied</span><span class=\"token punctuation\">(</span><span class=\"token builtin\">object</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__init__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> app<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        self<span class=\"token punctuation\">.</span>app <span class=\"token operator\">=</span> app\n\n    <span class=\"token keyword\">def</span> <span class=\"token function\">__call__</span><span class=\"token punctuation\">(</span>self<span class=\"token punctuation\">,</span> environ<span class=\"token punctuation\">,</span> start_response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n        scheme <span class=\"token operator\">=</span> environ<span class=\"token punctuation\">.</span>get<span class=\"token punctuation\">(</span><span class=\"token string\">'HTTP_X_FORWARDED_PROTO'</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">if</span> scheme<span class=\"token punctuation\">:</span>\n            environ<span class=\"token punctuation\">[</span><span class=\"token string\">'wsgi.url_scheme'</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> scheme\n        <span class=\"token keyword\">return</span> self<span class=\"token punctuation\">.</span>app<span class=\"token punctuation\">(</span>environ<span class=\"token punctuation\">,</span> start_response<span class=\"token punctuation\">)</span>\n\napp <span class=\"token operator\">=</span> Flask<span class=\"token punctuation\">(</span>__name__<span class=\"token punctuation\">)</span>\napp<span class=\"token punctuation\">.</span>wsgi_app <span class=\"token operator\">=</span> ReverseProxied<span class=\"token punctuation\">(</span>app<span class=\"token punctuation\">.</span>wsgi_app<span class=\"token punctuation\">)</span></code></pre></div>\n<h3 id=\"5-响应数据压缩\"><a href=\"#5-%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE%E5%8E%8B%E7%BC%A9\" aria-label=\"5 响应数据压缩 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>5. 响应数据压缩</h3>\n<p>不论是<code class=\"language-text\">IIS</code>、<code class=\"language-text\">Apache</code>还是<code class=\"language-text\">Nginx</code>，都提供有压缩功能。毕竟自己在用的云主机外网上行只有<code class=\"language-text\">1M</code>带宽，压缩后对于缩短首屏时间的效果提升极为显著。对于<code class=\"language-text\">Serverless</code>，响应数据是通过<code class=\"language-text\">API Gateway</code>传输到客户端，那么压缩也应该是它所具备的能力（虽然外网速度大幅度提高，但是该压缩还是得压缩），然而并没有找到……看到某些<code class=\"language-text\">js</code>框架原生有提供压缩功能，于是打算添加<code class=\"language-text\">Flask</code>自行压缩的功能。简单来讲，通过订阅<code class=\"language-text\">@app.after_request</code>信号并调用第三方库<code class=\"language-text\">brotli</code>的<code class=\"language-text\">compress</code>方法即可（\n在写之前去<code class=\"language-text\">gh</code>上看看有没有现成的轮子拓展，果然有……刚开始用的是<code class=\"language-text\">Flask-Zipper</code>，后来换成<code class=\"language-text\">Flask-Compress</code>解决了问题\n实测<code class=\"language-text\">3.1 MB</code>的数据采用<code class=\"language-text\">brotli</code>压缩算法减至<code class=\"language-text\">76.1 kB</code></p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201023-223613@2x.png!webp\" alt=\"压缩前\"></p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201023-224633@2x.png!webp\" alt=\"压缩后\"></p>\n<p><img src=\"https://i1.yuangezhizao.cn/macOS/QQ20201023-224722@2x.png!webp\" alt=\"br\"></p>\n<h3 id=\"6-apigw-三种环境不同路径所产生的影响\"><a href=\"#6-apigw-%E4%B8%89%E7%A7%8D%E7%8E%AF%E5%A2%83%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84%E6%89%80%E4%BA%A7%E7%94%9F%E7%9A%84%E5%BD%B1%E5%93%8D\" aria-label=\"6 apigw 三种环境不同路径所产生的影响 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>6. <code class=\"language-text\">apigw</code> 三种环境不同路径所产生的影响</h3>\n<p>默认的映射如下：</p>\n<table>\n<thead>\n<tr>\n<th align=\"center\">ID</th>\n<th align=\"center\">环境名</th>\n<th align=\"center\">访问路径</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\">1</td>\n<td align=\"center\">发布</td>\n<td align=\"center\">release</td>\n</tr>\n<tr>\n<td align=\"center\">2</td>\n<td align=\"center\">预发布</td>\n<td align=\"center\">prepub</td>\n</tr>\n<tr>\n<td align=\"center\">3</td>\n<td align=\"center\">测试</td>\n<td align=\"center\">test</td>\n</tr>\n</tbody>\n</table>\n<p>因为配置的<code class=\"language-text\">static_url_path</code>为<code class=\"language-text\">&quot;&quot;</code>，即<code class=\"language-text\">static</code>文件夹是映射到<code class=\"language-text\">/</code>路径下的，所以再加上<code class=\"language-text\">release</code>、<code class=\"language-text\">prepub</code>和<code class=\"language-text\">test</code>访问就自然<code class=\"language-text\">404</code>了\n因此绑定了<code class=\"language-text\">自定义域名</code>，<code class=\"language-text\">使用自定义路径映射</code>，并将<code class=\"language-text\">发布</code>环境的访问路径设置成<code class=\"language-text\">/</code>，这样再访问<code class=\"language-text\">发布</code>环境就没有问题了</p>\n<table>\n<thead>\n<tr>\n<th align=\"center\">ID</th>\n<th align=\"center\">环境名</th>\n<th align=\"center\">访问路径</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td align=\"center\">1</td>\n<td align=\"center\">发布</td>\n<td align=\"center\">/</td>\n</tr>\n<tr>\n<td align=\"center\">2</td>\n<td align=\"center\">预发布</td>\n<td align=\"center\">prepub</td>\n</tr>\n<tr>\n<td align=\"center\">3</td>\n<td align=\"center\">测试</td>\n<td align=\"center\">test</td>\n</tr>\n</tbody>\n</table>\n<h3 id=\"7-同时访问私有网络和外网\"><a href=\"#7-%E5%90%8C%E6%97%B6%E8%AE%BF%E9%97%AE%E7%A7%81%E6%9C%89%E7%BD%91%E7%BB%9C%E5%92%8C%E5%A4%96%E7%BD%91\" aria-label=\"7 同时访问私有网络和外网 permalink\" class=\"anchor\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>7. 同时访问<code class=\"language-text\">私有网络</code>和<code class=\"language-text\">外网</code></h3>\n<p><code class=\"language-text\">云函数</code>中可以利用到的云端数据库有如下几种</p>\n<ul>\n<li>云数据库<code class=\"language-text\">CDB</code>，需要<code class=\"language-text\">私有网络</code>访问，虽然可以通过外网访问但是能走内网就不走外网</li>\n<li><code class=\"language-text\">PostgreSQL for Serverless（ServerlessDB）</code>，这个是官方给<code class=\"language-text\">Serverless</code>配的<code class=\"language-text\">pg</code>数据库</li>\n<li>云开发<code class=\"language-text\">TCB</code>中的<code class=\"language-text\">MongoDB</code>，没记错的话需要开通内测权限访问</li>\n</ul>\n<p>因为自己是从旧网站迁移过来的，数据暂时还没有迁移，因此直接访问原始云数据库<code class=\"language-text\">CDB</code>，在<code class=\"language-text\">云函数</code>配置<code class=\"language-text\">所属网络</code>和<code class=\"language-text\">所属子网</code>即可。但是此时会无法访问外网，一种解决方法是开启<code class=\"language-text\">公网访问</code>和<code class=\"language-text\">公网固定IP</code>，就可以同时访问内网和外网资源了。关于配置文件，本项目是<code class=\"language-text\">单实例应用</code>也就是说<code class=\"language-text\">项目中只引入一个组件，部署时只生成一个组件实例</code>。但是如果想引入数据库的话，就得新增组件了，目前在<code class=\"language-text\">Flask Components</code>中并没有提供数据库相关的配置项，因此需要<code class=\"language-text\">项目中引入多个组件，部署时生成多个组件实例</code>。也很简单，创建一个含有<code class=\"language-text\">serverless.yml</code>的新文件夹，用来配置<code class=\"language-text\">postgresql</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"50997288596514360000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`component: postgresql # (必填) 组件名称，此处为 postgresql\nname: maimai_DX_CN_probe # (必选) 组件实例名称.\norg: yuangezhizao # (可选) 用于记录组织信息，默认值为您的腾讯云账户 appid，必须为字符串\napp: yuangezhizao # (可选) 用于记录组织信息. 默认与name相同，必须为字符串\nstage: dev # (可选) 用于区分环境信息，默认值是 dev\n\ninputs:\n  region: ap-beijing # 可选 ap-guangzhou, ap-shanghai, ap-beijing\n  zone: ap-beijing-3 # 可选 ap-guangzhou-2, ap-shanghai-2, ap-beijing-3\n  dBInstanceName: maimai_DX_CN_probe\n  #  projectId: 0\n  dBVersion: 10.4\n  dBCharset: UTF8\n  vpcConfig:\n    vpcId: vpc-mrg5ak88\n    subnetId: subnet-hqwa51dh\n  extranetAccess: false`, `50997288596514360000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"yml\"><pre class=\"language-yml\"><code class=\"language-yml\"><span class=\"token key atrule\">component</span><span class=\"token punctuation\">:</span> postgresql <span class=\"token comment\"># (必填) 组件名称，此处为 postgresql</span>\n<span class=\"token key atrule\">name</span><span class=\"token punctuation\">:</span> maimai_DX_CN_probe <span class=\"token comment\"># (必选) 组件实例名称.</span>\n<span class=\"token key atrule\">org</span><span class=\"token punctuation\">:</span> yuangezhizao <span class=\"token comment\"># (可选) 用于记录组织信息，默认值为您的腾讯云账户 appid，必须为字符串</span>\n<span class=\"token key atrule\">app</span><span class=\"token punctuation\">:</span> yuangezhizao <span class=\"token comment\"># (可选) 用于记录组织信息. 默认与name相同，必须为字符串</span>\n<span class=\"token key atrule\">stage</span><span class=\"token punctuation\">:</span> dev <span class=\"token comment\"># (可选) 用于区分环境信息，默认值是 dev</span>\n\n<span class=\"token key atrule\">inputs</span><span class=\"token punctuation\">:</span>\n  <span class=\"token key atrule\">region</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>beijing <span class=\"token comment\"># 可选 ap-guangzhou, ap-shanghai, ap-beijing</span>\n  <span class=\"token key atrule\">zone</span><span class=\"token punctuation\">:</span> ap<span class=\"token punctuation\">-</span>beijing<span class=\"token punctuation\">-</span><span class=\"token number\">3 </span><span class=\"token comment\"># 可选 ap-guangzhou-2, ap-shanghai-2, ap-beijing-3</span>\n  <span class=\"token key atrule\">dBInstanceName</span><span class=\"token punctuation\">:</span> maimai_DX_CN_probe\n  <span class=\"token comment\">#  projectId: 0</span>\n  <span class=\"token key atrule\">dBVersion</span><span class=\"token punctuation\">:</span> <span class=\"token number\">10.4</span>\n  <span class=\"token key atrule\">dBCharset</span><span class=\"token punctuation\">:</span> UTF8\n  <span class=\"token key atrule\">vpcConfig</span><span class=\"token punctuation\">:</span>\n    <span class=\"token key atrule\">vpcId</span><span class=\"token punctuation\">:</span> vpc<span class=\"token punctuation\">-</span>mrg5ak88\n    <span class=\"token key atrule\">subnetId</span><span class=\"token punctuation\">:</span> subnet<span class=\"token punctuation\">-</span>hqwa51dh\n  <span class=\"token key atrule\">extranetAccess</span><span class=\"token punctuation\">:</span> <span class=\"token boolean important\">false</span></code></pre></div>\n<p>然后在终端<code class=\"language-text\">cd</code>到这个目录再执行<code class=\"language-text\">sls deploy</code>即可成功部署<code class=\"language-text\">postgresql</code></p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"79486940427375120000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`yum install python3-devel postgresql-devel\npip install psycopg2`, `79486940427375120000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"bash\"><pre class=\"language-bash\"><code class=\"language-bash\">yum <span class=\"token function\">install</span> python3-devel postgresql-devel\npip <span class=\"token function\">install</span> psycopg2</code></pre></div>\n<p>结果</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"6010864468116916000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`import psycopg2\nFile &quot;/opt/psycopg2/__init__.py&quot;, line 51, in &lt;module&gt;\nfrom psycopg2._psycopg import (                     # noqa\nImportError: libpython3.6m.so.1.0: cannot open shared object file: No such file or directory`, `6010864468116916000`)\"\n            >\n              <div\n                class=\"gatsby-code-button\"\n                data-tooltip=\"\"\n              >\n                复制代码<svg class=\"gatsby-code-button-icon\" xmlns=\"http://www.w3.org/2000/svg\" width=\"24\" height=\"24\" viewBox=\"0 0 24 24\"><path fill=\"none\" d=\"M0 0h24v24H0V0z\"/><path d=\"M16 1H2v16h2V3h12V1zm-1 4l6 6v12H6V5h9zm-1 7h5.5L14 6.5V12z\"/></svg>\n              </div>\n            </div>\n<div class=\"gatsby-highlight\" data-language=\"python\"><pre class=\"language-python\"><code class=\"language-python\"><span class=\"token keyword\">import</span> psycopg2\nFile <span class=\"token string\">\"/opt/psycopg2/__init__.py\"</span><span class=\"token punctuation\">,</span> line <span class=\"token number\">51</span><span class=\"token punctuation\">,</span> <span class=\"token keyword\">in</span> <span class=\"token operator\">&amp;</span>lt<span class=\"token punctuation\">;</span>module<span class=\"token operator\">&amp;</span>gt<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">from</span> psycopg2<span class=\"token punctuation\">.</span>_psycopg <span class=\"token keyword\">import</span> <span class=\"token punctuation\">(</span>                     <span class=\"token comment\"># noqa</span>\nImportError<span class=\"token punctuation\">:</span> libpython3<span class=\"token punctuation\">.</span>6m<span class=\"token punctuation\">.</span>so<span class=\"token punctuation\">.</span><span class=\"token number\">1.0</span><span class=\"token punctuation\">:</span> cannot <span class=\"token builtin\">open</span> shared <span class=\"token builtin\">object</span> <span class=\"token builtin\">file</span><span class=\"token punctuation\">:</span> No such <span class=\"token builtin\">file</span> <span class=\"token keyword\">or</span> directory</code></pre></div>\n<p>下列问题处于解决之中：</p>\n<ul>\n<li><code class=\"language-text\">http</code> 强制跳转 <code class=\"language-text\">https</code></li>\n<li>测试环境推送至生产环境</li>\n</ul>\n<p>至此，本文就结束了，欢迎<a href=\"https://www.yuangezhizao.cn/articles/python/flask/serverless/maimai_DX_CN_probe.html\">交流</a>！</p>\n<hr>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li><a href=\"/blog/2020-10-24-maimai/#%E5%89%8D%E8%A8%80\">前言</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#%E8%88%9E%E8%90%8C%E6%9F%A5%E5%88%86%E5%99%A8\">舞萌查分器</a></li>\n<li>\n<p><a href=\"/blog/2020-10-24-maimai/#%E5%BC%80%E5%8F%91%E8%BF%87%E7%A8%8B\">开发过程</a></p>\n<ul>\n<li><a href=\"/blog/2020-10-24-maimai/#1-serverless-framework-component-%E9%85%8D%E7%BD%AE%E6%96%87%E4%BB%B6\">1. &#x3C;code class=\"language-text\">Serverless Framework Component&#x3C;/code> 配置文件</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#2-%E5%B1%82%E7%AE%A1%E7%90%86%E6%A6%82%E8%BF%B0\">2. 层管理概述</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#3%E8%87%AA%E5%AE%9A%E4%B9%89%E5%85%A5%E5%8F%A3%E6%96%87%E4%BB%B6\">3.自定义入口文件</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#4-url_for-%E8%BE%93%E5%87%BA-http-%E8%80%8C%E9%9D%9E-https-%E7%9A%84-url\">4. &#x3C;code class=\"language-text\">url_for&#x3C;/code> 输出 &#x3C;code class=\"language-text\">http&#x3C;/code> 而非 &#x3C;code class=\"language-text\">https&#x3C;/code> 的 &#x3C;code class=\"language-text\">URL&#x3C;/code></a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#5-%E5%93%8D%E5%BA%94%E6%95%B0%E6%8D%AE%E5%8E%8B%E7%BC%A9\">5. 响应数据压缩</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#6-apigw-%E4%B8%89%E7%A7%8D%E7%8E%AF%E5%A2%83%E4%B8%8D%E5%90%8C%E8%B7%AF%E5%BE%84%E6%89%80%E4%BA%A7%E7%94%9F%E7%9A%84%E5%BD%B1%E5%93%8D\">6. &#x3C;code class=\"language-text\">apigw&#x3C;/code> 三种环境不同路径所产生的影响</a></li>\n<li><a href=\"/blog/2020-10-24-maimai/#7-%E5%90%8C%E6%97%B6%E8%AE%BF%E9%97%AE%E7%A7%81%E6%9C%89%E7%BD%91%E7%BB%9C%E5%92%8C%E5%A4%96%E7%BD%91\">7. 同时访问&#x3C;code class=\"language-text\">私有网络&#x3C;/code>和&#x3C;code class=\"language-text\">外网&#x3C;/code></a></li>\n</ul>\n</li>\n</ul>"},"previousBlog":{"id":"bb1e667a-3b55-5ab5-b1d0-fc6480061def","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020115/1604567165601-spboot.jpg","authors":["Freeeeeedom"],"categories":["user-stories"],"date":"2020-10-25T00:00:00.000Z","title":"Springboot 之基于 Serverless 的订单应用","description":"Java 是最好的语言","authorslink":["https://freeeeeedom.github.io/"],"translators":null,"translatorslink":null,"tags":["Serverless","Springboot"],"keywords":null,"outdated":null},"wordCount":{"words":275,"sentences":65,"paragraphs":64},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-10-25-serverless-order.md","fields":{"slug":"/blog/2020-10-25-serverless-order/","keywords":["go","java","php","python","serverless","无服务器","云函数","函数","网关","部署","springboot","serverlesscloud","log"]}},"nextBlog":{"id":"8ce0f304-ef65-5bf8-91e6-64172fb9a8ac","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020927/1601192579846-1601190052484-ServerlessDays%20PPT%E6%A8%A1%E6%9D%BF001.jpg","authors":["Johann SchleierSmith"],"categories":["user-stories"],"date":"2020-09-27T00:00:00.000Z","title":"权威指南：Serverless 未来十年发展解读 — 伯克利分校实验室分享（下）","description":"Johann 在 ServerlessDays China 的演讲分享！","authorslink":["https://www.zhihu.com/people/Aceyclee"],"translators":null,"translatorslink":null,"tags":["Serverless","伯克利"],"keywords":"Serverless Framework,Serverless,SCF,Berkeley,加州大学,伯克利分校,云计算","outdated":null},"wordCount":{"words":431,"sentences":59,"paragraphs":59},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-09-27-slsdays-johann-2.md","fields":{"slug":"/blog/2020-09-27-slsdays-johann-2/","keywords":["serverless","云函数","Serverless","serverlesscloud","场景","存储","Cloudburst"]}},"recommendBlogs":{"edges":[{"node":{"id":"4300b21c-7209-5256-86ff-0d38e3daec9b","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/14f1c8eed372e76c1b139703b2f6d0fa.jpg","authors":["KieranMcCarthy"],"categories":["user-stories","engineering-culture"],"date":"2018-01-09T00:00:00.000Z","title":"我是如何在四年时间里，从厨师转行为 Serverless 应用开发者","description":"我是厨师出身，现在成为了一名 Serverless 应用开发者。","authorslink":["https://serverless.com/author/kieranmccarthy/"],"translators":["Aceyclee"],"translatorslink":["https://www.zhihu.com/people/Aceyclee"],"tags":["应用开发","Serverless"],"keywords":"Serverless 应用开发,Serverless 管理,厨师转行为 Serverless 应用开发者","outdated":null},"wordCount":{"words":285,"sentences":38,"paragraphs":36},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years.md","fields":{"slug":"/blog/2018-01-09-from-chef-to-serverless-developer-in-4-years/","keywords":["无服务器","无服务器开发","云函数","学习","Serverless","构建","Framework","开发者","服务器","应用","学位","简历"]}}},{"node":{"id":"713a0563-4bf9-5721-bacb-3b4ef609fe4a","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/camp-fire/camp-fire-housing-thumb.jpg","authors":["EricWyne"],"categories":["guides-and-tutorials","user-stories"],"date":"2018-12-05T00:00:00.000Z","title":"Serverless Twitter 机器人帮助为坎普山火受灾者安置住房","description":"加利福尼亚州的坎普山火致使数千人流离失所，为此，我构建了一个简单的 Serverless Twitter 机器人来帮助将受灾者安置在临时住房！","authorslink":["https://serverless.com/author/ericwyne/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":157,"sentences":26,"paragraphs":26},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-05-serverless-twitter-camp-fire.md","fields":{"slug":"/blog/2018-12-05-serverless-twitter-camp-fire/","keywords":["serverless","无服务器","云函数","Serverless","org","住房","Twitter","函数","受灾","机器人","山火"]}}},{"node":{"id":"98602143-b837-5f50-a24f-3b1ec76044d7","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/sqquid/sqquid-serverless-thumb.jpg","authors":["RonPeled"],"categories":["user-stories"],"date":"2018-12-17T00:00:00.000Z","title":"SQQUID：100% 无服务器初创公司","description":"SQQUID 将 AWS Lambda 和无服务器框架用于其核心产品和营销网站。我们来看看一个完全无服务器的初创公司是怎样的。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":266,"sentences":42,"paragraphs":42},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-12-17-sqquid-one-hundred-percent-serverless.md","fields":{"slug":"/blog/2018-12-17-sqquid-one-hundred-percent-serverless/","keywords":["go","serverless","无服务器","无服务器架构","服务器","架构","Lambda","集成","FaaS","串行","系统"]}}},{"node":{"id":"29dc2e58-d2ba-56f9-aee1-d21b0bc62e0e","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/ao-com-story/ao-serverless-thumbnail.png","authors":["NickGottlieb"],"categories":["user-stories"],"date":"2019-04-24T00:00:00.000Z","title":"AO.com：逐渐转向无服务器优先","description":"AO.com 的 SCV 团队率先尝试无服务器服务。折服于无服务器框架的快速周转时间和低维护成本，整个团队逐渐转向无服务器优先。","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":236,"sentences":42,"paragraphs":35},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-04-24-ao-serverless-first.md","fields":{"slug":"/blog/2019-04-24-ao-serverless-first/","keywords":["serverless","无服务器","服务器","团队","Lambda","功能","构建"]}}},{"node":{"id":"752d08d1-387a-5bde-acf3-98141baab294","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586871710979-%E5%85%AC%E5%85%B1%E7%94%A8.png","authors":["Anycodes"],"categories":["user-stories"],"date":"2019-06-20T00:00:00.000Z","title":"如何用 Serverless 为 Python 云函数打包依赖","description":"在使用无服务器云函数SCF时通常会遇到导入第三方库的问题，很多小伙伴比较头疼是：应该如何打包进去？这里，推荐几个不错的方法。","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["云函数","Serverless"],"keywords":"Serverless,Serverless应用,无服务器云函数","outdated":null},"wordCount":{"words":81,"sentences":43,"paragraphs":43},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-06-20-for-python-cloud-functions.md","fields":{"slug":"/blog/2019-06-20-for-python-cloud-functions/","keywords":["java","serverless","无服务器","无服务器云函数","云函数","serverlesscloud","安装","serverless","pillowtest"]}}},{"node":{"id":"2dc78814-9d77-555b-a1bb-ad202c8ec2d1","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.blog.serverless.com/cloudforecast/thumbnail.png","authors":["FrancoisLagier"],"categories":["user-stories"],"date":"2019-08-07T00:00:00.000Z","title":"Serverless：初创企业的理想选择？（CloudForecast 案例分析）","description":"CloudForecast 是 2018 年成立的一家独立初创企业，本文将介绍他们决定选择 Serverless 的原因。","authorslink":["https://serverless.com/author/francoislagier/"],"translators":["Aceyclee"],"translatorslink":["zhihu.com/people/Aceyclee"],"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":211,"sentences":29,"paragraphs":29},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-08-07-serverless-for-startups.md","fields":{"slug":"/blog/2019-08-07-serverless-for-startups/","keywords":["serverless","云函数","serverless","函数","Serverless","utm","Framework","blog","CloudForecast","cloudforecast"]}}},{"node":{"id":"97450b07-658b-5207-8216-1c7b9b51b115","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578988490344-v2-8b2cd2c5275aa2c5a3c5083a148a7a9f_1200x500.jpg","authors":["Anycodes"],"categories":["user-stories"],"date":"2019-09-01T00:00:00.000Z","title":"如何通过 Serverless 与自然语言处理，让搜索引擎「看」到你的博客","description":"Serverless 与自然语言处理结合的一个小应用","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["个人博客","serverless"],"keywords":"Serverless 自然语言处理","outdated":null},"wordCount":{"words":106,"sentences":34,"paragraphs":34},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-01-search-engine-blog.md","fields":{"slug":"/blog/2019-09-01-search-engine-blog/","keywords":["serverless","云函数","keywords","serverlesscloud","summary"]}}},{"node":{"id":"ae4fd2f8-515c-5aec-b584-38427ef33f7e","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578989800047-part-00492-780.jpg","authors":["Anycodes"],"categories":["guides-and-tutorials","user-stories"],"date":"2019-09-16T00:00:00.000Z","title":"突破传统 OJ 瓶颈，「判题姬」接入云函数","description":"通过 Serverless 实现在线编程","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["在线编程","云函数"],"keywords":"Serverless 在线编程,Serverless OJ","outdated":null},"wordCount":{"words":169,"sentences":30,"paragraphs":30},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-09-16-online-Judge.md","fields":{"slug":"/blog/2019-09-16-online-Judge/","keywords":["python","serverless","云函数","代码","函数","serverless"]}}}],"totalCount":64}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"5d75ad51-0cf8-5328-97bc-55494112b7b0","previousBlogId":"bb1e667a-3b55-5ab5-b1d0-fc6480061def","nextBlogId":"8ce0f304-ef65-5bf8-91e6-64172fb9a8ac","categories":["user-stories"]}}}