{"componentChunkName":"component---src-templates-best-practice-detail-tsx","path":"/best-practice/2020-04-22-coding-learing","result":{"data":{"currentBlog":{"id":"6e8a71a3-426e-5101-94db-bd5b1ef4d1ec","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020512/1589274422875-code.jpg","authors":["Anycodes"],"categories":["best-practice"],"date":"2020-04-22T00:00:00.000Z","title":"基于 Serverless 架构的编程学习小工具","description":"这是一个基于 Serverless 架构的编程学习 App，它不仅仅可以让你学习编程，还能实现代码的编写与运行","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Serverless","程序员"],"keywords":"Serverless 多环境配置,Serverless 管理环境,Serverless配置方案","outdated":null},"wordCount":{"words":281,"sentences":50,"paragraphs":50},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2020-04-22-coding-learing.md","fields":{"slug":"/best-practice/2020-04-22-coding-learing/","keywords":["go","python","serverless","函数计算","云函数","serverlesscloud","Serverless","req","函数","event"]},"html":"<p>之前我做过一个在线编程的软件，目前用户量大概有几十万，通过这个 App 不仅仅可以进行代码的编写、运行还可以进行编程的学习。自己一直对 Serverless 架构情有独钟，恰好赶到我的这个 App 学习板块被很多人吐槽难用，索性就对这个学习板块进行重构，并且打算在重构的时候，直接将这个学习板块搬上 Serverless 架构。</p>\n<p>基于 Serverless 架构重构是出于两个方面考虑 —— 一是 Serverless 架构能让个人开发者的运维工作变得简单，尤其是不用操心服务器，也不用关心流量洪峰（当然，对于我的个人项目而言，也没太多的洪峰），二是 Serverless 架构的按量付费，极大节约了成本。</p>\n<h2 id=\"整体设计\"><a href=\"#%E6%95%B4%E4%BD%93%E8%AE%BE%E8%AE%A1\" 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<h3 id=\"数据库设计\"><a href=\"#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1\" 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>数据库设计</h3>\n<p>这个部分在之前是若干个大模块，现在统一整理到一个模块中进行项目重构，所以这里继续复用之前的数据库：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-4-2.png\"></p>\n<p>在这个数据库中，四个模块分别是：新闻文章、开发文档、基础教程以及图书资源。其中开发文档包括大分类，子列表以及正文等内容，这里表关联并没有使用外键，而是直接用的 ID 进行表之间的关联。</p>\n<p>说实话，这个数据库设计的并不是很好，原因是因为初次构建这个数据部分，绝大部分数据都是在其他站点采集而来，当时由于模块快速上线，便直接按照原有格式存储，所以可以认为这个数据库中有很多表的字段其实是无效的，或者针对这个项目是未被使用的。</p>\n<h3 id=\"后端设计\"><a href=\"#%E5%90%8E%E7%AB%AF%E8%AE%BE%E8%AE%A1\" 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>后端设计</h3>\n<p>后端将会整体部署到一个函数上，功能整体结构：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-3.png\"></p>\n<p>整体功能就是云函数 SCF 绑定 API 网关触发器，用户访问 API 网关指定的地址，触发云函数，然后函数在入口处进行功能拆分，请求不同的方法获得对应的数据。</p>\n<p>这里要额外说明一下，后端整体接口部署在一个函数的原因，是因为我这个模块的使用量并不是非常频繁，所以部署到一个函数上也不会出现超过最大实例的限制，如果超出限制是可以申请扩容的；</p>\n<p>其次，所有的接口都是对数据库增删改查，放入到一个函数中，在一定程度上可以保证容器的活性，降低部分冷启动带来的问题，同时容器的复用，也可以在一定程度上降低后台数据库链接池的压力；除此之外，所有的接口功能，都是只需要最少的内存（64M）即可完整运行，不会因为个别接口的预估内存较大，进而影响影响整体的成本。</p>\n<p>所以这里评估之后，是可以将多个接口，放入到一个函数中，对外提供对应的服务。</p>\n<h3 id=\"前端设计\"><a href=\"#%E5%89%8D%E7%AB%AF%E8%AE%BE%E8%AE%A1\" 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>前端设计</h3>\n<p>前端设计，预计在学习资源部分需要有 8 个页面，主要就是科技类新闻、教程、文档、图书等相关功能，通过墨刀绘制的原型图如下：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-1.png\"></p>\n<p>前端项目开发将会采用 Vue.js，并且将其部署到对象存储中，通过腾讯云对象存储的静态网站功能对外提供服务。</p>\n<h2 id=\"项目开发\"><a href=\"#%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91\" 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<h3 id=\"后端函数开发\"><a href=\"#%E5%90%8E%E7%AB%AF%E5%87%BD%E6%95%B0%E5%BC%80%E5%8F%91\" 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>后端函数开发</h3>\n<p>后端函数开发主要包括三部分</p>\n<ul>\n<li>部分资源的初始化，部分资源初始化，需要在函数外进行，这样可以保证复用实例的时候不会再次建立链接，防止数据库连接池出现问题：</li>\n</ul>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"79542058023911770000\"\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(`def getConnection(dbName):\n    conn = pymysql.connect(host=&quot;&quot;,\n                           user=&quot;root&quot;,\n                           password=&quot;&quot;,\n                           port=3306,\n                           db=dbName,\n                           charset='utf8',\n                           cursorclass=pymysql.cursors.DictCursor,\n                           )\n    conn.autocommit(1)\n    return conn\n\n\nconnectionArticle = getConnection(&quot;anycodes_article&quot;)`, `79542058023911770000`)\"\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\">def</span> <span class=\"token function\">getConnection</span><span class=\"token punctuation\">(</span>dbName<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    conn <span class=\"token operator\">=</span> pymysql<span class=\"token punctuation\">.</span>connect<span class=\"token punctuation\">(</span>host<span class=\"token operator\">=</span><span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n                           user<span class=\"token operator\">=</span><span class=\"token string\">\"root\"</span><span class=\"token punctuation\">,</span>\n                           password<span class=\"token operator\">=</span><span class=\"token string\">\"\"</span><span class=\"token punctuation\">,</span>\n                           port<span class=\"token operator\">=</span><span class=\"token number\">3306</span><span class=\"token punctuation\">,</span>\n                           db<span class=\"token operator\">=</span>dbName<span class=\"token punctuation\">,</span>\n                           charset<span class=\"token operator\">=</span><span class=\"token string\">'utf8'</span><span class=\"token punctuation\">,</span>\n                           cursorclass<span class=\"token operator\">=</span>pymysql<span class=\"token punctuation\">.</span>cursors<span class=\"token punctuation\">.</span>DictCursor<span class=\"token punctuation\">,</span>\n                           <span class=\"token punctuation\">)</span>\n    conn<span class=\"token punctuation\">.</span>autocommit<span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> conn\n\n\nconnectionArticle <span class=\"token operator\">=</span> getConnection<span class=\"token punctuation\">(</span><span class=\"token string\">\"anycodes_article\"</span><span class=\"token punctuation\">)</span></code></pre></div>\n<ul>\n<li>数据库查询操作</li>\n</ul>\n<p>这一部分主要就是针对不同接口查询数据库，例如获取文章分类：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"74928731119664230000\"\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(`def getArticleCategory():\n    connectionArticle.ping(reconnect=True)\n    cursor = connectionArticle.cursor()\n    search_stmt = ('SELECT * FROM \\`category\\` ORDER BY \\`sort\\`')\n    cursor.execute(search_stmt, ())\n    data = cursor.fetchall()\n    cursor.close()\n    result = {}\n    for eve_data in data:\n        if eve_data['pre_name'] not in result:\n            result[eve_data['pre_name']] = []\n        result[eve_data['pre_name']].append({\n            &quot;id&quot;: eve_data[&quot;sort&quot;],\n            &quot;name&quot;: eve_data[&quot;name&quot;]\n        })\n    return result`, `74928731119664230000`)\"\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\">def</span> <span class=\"token function\">getArticleCategory</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    connectionArticle<span class=\"token punctuation\">.</span>ping<span class=\"token punctuation\">(</span>reconnect<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n    cursor <span class=\"token operator\">=</span> connectionArticle<span class=\"token punctuation\">.</span>cursor<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    search_stmt <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token string\">'SELECT * FROM `category` ORDER BY `sort`'</span><span class=\"token punctuation\">)</span>\n    cursor<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>search_stmt<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    data <span class=\"token operator\">=</span> cursor<span class=\"token punctuation\">.</span>fetchall<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    cursor<span class=\"token punctuation\">.</span>close<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    result <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span><span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">for</span> eve_data <span class=\"token keyword\">in</span> data<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">if</span> eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">'pre_name'</span><span class=\"token punctuation\">]</span> <span class=\"token keyword\">not</span> <span class=\"token keyword\">in</span> result<span class=\"token punctuation\">:</span>\n            result<span class=\"token punctuation\">[</span>eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">'pre_name'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">]</span>\n        result<span class=\"token punctuation\">[</span>eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">'pre_name'</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span>append<span class=\"token punctuation\">(</span><span class=\"token punctuation\">{</span>\n            <span class=\"token string\">\"id\"</span><span class=\"token punctuation\">:</span> eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"sort\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n            <span class=\"token string\">\"name\"</span><span class=\"token punctuation\">:</span> eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"name\"</span><span class=\"token punctuation\">]</span>\n        <span class=\"token punctuation\">}</span><span class=\"token punctuation\">)</span>\n    <span class=\"token keyword\">return</span> result</code></pre></div>\n<p>例如获取文章列表：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"85647965593157800000\"\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(`def getArticleList(cid):\n    connectionArticle.ping(reconnect=True)\n    cursor = connectionArticle.cursor()\n    search_stmt = ('SELECT * FROM \\`article\\` WHERE \\`category\\` = %s ORDER BY \\`sort\\`')\n    cursor.execute(search_stmt, (cid,))\n    data = cursor.fetchall()\n    cursor.close()\n    result = [{\n                &quot;id&quot;: eve_data[&quot;aid&quot;],\n                &quot;title&quot;: eve_data[&quot;title&quot;]\n            } for eve_data in data]\n    return result`, `85647965593157800000`)\"\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\">def</span> <span class=\"token function\">getArticleList</span><span class=\"token punctuation\">(</span>cid<span class=\"token punctuation\">)</span><span class=\"token punctuation\">:</span>\n    connectionArticle<span class=\"token punctuation\">.</span>ping<span class=\"token punctuation\">(</span>reconnect<span class=\"token operator\">=</span><span class=\"token boolean\">True</span><span class=\"token punctuation\">)</span>\n    cursor <span class=\"token operator\">=</span> connectionArticle<span class=\"token punctuation\">.</span>cursor<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    search_stmt <span class=\"token operator\">=</span> <span class=\"token punctuation\">(</span><span class=\"token string\">'SELECT * FROM `article` WHERE `category` = %s ORDER BY `sort`'</span><span class=\"token punctuation\">)</span>\n    cursor<span class=\"token punctuation\">.</span>execute<span class=\"token punctuation\">(</span>search_stmt<span class=\"token punctuation\">,</span> <span class=\"token punctuation\">(</span>cid<span class=\"token punctuation\">,</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span>\n    data <span class=\"token operator\">=</span> cursor<span class=\"token punctuation\">.</span>fetchall<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    cursor<span class=\"token punctuation\">.</span>close<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n    result <span class=\"token operator\">=</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n                <span class=\"token string\">\"id\"</span><span class=\"token punctuation\">:</span> eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"aid\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">,</span>\n                <span class=\"token string\">\"title\"</span><span class=\"token punctuation\">:</span> eve_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"title\"</span><span class=\"token punctuation\">]</span>\n            <span class=\"token punctuation\">}</span> <span class=\"token keyword\">for</span> eve_data <span class=\"token keyword\">in</span> data<span class=\"token punctuation\">]</span>\n    <span class=\"token keyword\">return</span> result</code></pre></div>\n<ul>\n<li>最后一部分就是函数的入口，函数入口部分就是做功能分发和接口识别：</li>\n</ul>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"60251170198515140000\"\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(`def main_handler(event, context):\n    try:\n        result_data = {\n            &quot;error&quot;: False\n        }\n        req_type = event[&quot;pathParameters&quot;][&quot;type&quot;]\n        if req_type == &quot;get_book_list&quot;:\n            result_data[&quot;data&quot;] = getBookList()\n        elif req_type == &quot;get_book_info&quot;:\n            result_data[&quot;data&quot;] = getBookContent(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_daily_content&quot;:\n            result_data[&quot;data&quot;] = getDailyContent(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_daily_list&quot;:\n            result_data[&quot;data&quot;] = getDailyList(event[&quot;queryString&quot;][&quot;category&quot;])\n        elif req_type == &quot;get_dictionary_result&quot;:\n            result_data[&quot;data&quot;] = getDictionaryResult(event[&quot;queryString&quot;][&quot;word&quot;])\n        elif req_type == &quot;get_dev_content&quot;:\n            result_data[&quot;data&quot;] = getDevContent(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_dev_section&quot;:\n            result_data[&quot;data&quot;] = getDevSection(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_dev_chapter&quot;:\n            result_data[&quot;data&quot;] = getDevChapter(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_dev_list&quot;:\n            result_data[&quot;data&quot;] = getDevList()\n        elif req_type == &quot;get_article_content&quot;:\n            result_data[&quot;data&quot;] = getArticle(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_article_list&quot;:\n            result_data[&quot;data&quot;] = getArticleList(event[&quot;queryString&quot;][&quot;id&quot;])\n        elif req_type == &quot;get_article_category&quot;:\n            result_data[&quot;data&quot;] = getArticleCategory()\n        return result_data\n    except Exception as e:\n        print(e)\n        return {&quot;error&quot;: True}`, `60251170198515140000`)\"\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\">def</span> <span class=\"token function\">main_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\">try</span><span class=\"token punctuation\">:</span>\n        result_data <span class=\"token operator\">=</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token string\">\"error\"</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">False</span>\n        <span class=\"token punctuation\">}</span>\n        req_type <span class=\"token operator\">=</span> event<span class=\"token punctuation\">[</span><span class=\"token string\">\"pathParameters\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"type\"</span><span class=\"token punctuation\">]</span>\n        <span class=\"token keyword\">if</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_book_list\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getBookList<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_book_info\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getBookContent<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_daily_content\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDailyContent<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_daily_list\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDailyList<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"category\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_dictionary_result\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDictionaryResult<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"word\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_dev_content\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDevContent<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_dev_section\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDevSection<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_dev_chapter\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDevChapter<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_dev_list\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getDevList<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_article_content\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getArticle<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_article_list\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getArticleList<span class=\"token punctuation\">(</span>event<span class=\"token punctuation\">[</span><span class=\"token string\">\"queryString\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">[</span><span class=\"token string\">\"id\"</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">elif</span> req_type <span class=\"token operator\">==</span> <span class=\"token string\">\"get_article_category\"</span><span class=\"token punctuation\">:</span>\n            result_data<span class=\"token punctuation\">[</span><span class=\"token string\">\"data\"</span><span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> getArticleCategory<span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> result_data\n    <span class=\"token keyword\">except</span> Exception <span class=\"token keyword\">as</span> e<span class=\"token punctuation\">:</span>\n        <span class=\"token keyword\">print</span><span class=\"token punctuation\">(</span>e<span class=\"token punctuation\">)</span>\n        <span class=\"token keyword\">return</span> <span class=\"token punctuation\">{</span><span class=\"token string\">\"error\"</span><span class=\"token punctuation\">:</span> <span class=\"token boolean\">True</span><span class=\"token punctuation\">}</span></code></pre></div>\n<p>函数部分完成之后，可以配置 API 网关部分：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-4.png\"></p>\n<p>在整个后端接口开发过程中，其实并没有遇到什么太大的问题，因为这个学习功能的模块基本上就是对数据库进行查询的操作，所以相对来说非常顺利。</p>\n<h2 id=\"效果预览\"><a href=\"#%E6%95%88%E6%9E%9C%E9%A2%84%E8%A7%88\" 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>整体预览结果：一共包括十几个页面，这里取其中8个主要的页面进行效果展示：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-5.png\"></p>\n<p>整个页面基本上是还原了设计稿的样子，并且和原有项目进行了部分的整合，无论是列表页面还是图书页面等，数据加载速度表现良好。</p>\n<p>通过 PostMan 进行基本测试：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-9.png\"></p>\n<p>对接口进行 1000 次访问测试：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-10.png\"></p>\n<p>可以看到，接口表现良好，并未出现失败的情况，对该测试结果进行耗时的可视化：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-11.png\"></p>\n<p>其中最大的时间消耗是 219 毫秒，最小是 27 毫秒，平均值 35 毫秒，可以看到整体的效果还是非常不错。</p>\n<p>这样一个项目开发完成，上线之后，前端部分被放到对象存储 COS 中，后端业务被放到云函数 SCF 中，触发器使用的是 API 网关，在监控层面，函数计算有着比较不错的监控纬度：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-6.png\"></p>\n<p>同时函数并发，弹性伸缩等问题都由云厂商来解决，可以这样说，自从这个组件部署到了 Serverless 架构上，我所做的操作就是如果业务代码有问题，进行简单修复和简单维护。讲真，整个效果还是不错的。</p>\n<p>通过按量付费，可以看到我后端服务产生的费用：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-7.png\"></p>\n<p>由于云函数没办法看到单个资源的费用，所以整个函数我有几十个，一共花费的费用也远远比服务器的一个月便宜很多。</p>\n<p>当然虽然说在计算服务这里整体费用只有几元钱相对来说非常便宜，但是其还有 API 网关的费用和对象存储的费用，例如 API 网关费用：</p>\n<p><img src=\"https://img.serverlesscloud.cn/202058/3-3-12.png\"></p>\n<p>同样，我这里的 API 网关也是有很多服务的，不仅仅是 Anycodes 这样一个服务产生的，但是整体加一起 2 月份只有 1 元钱，相对来说也是蛮低的。</p>\n<h2 id=\"总结\"><a href=\"#%E6%80%BB%E7%BB%93\" 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>通过个人项目中的一个子模块重构过程，将该项目部署到 Serverless 架构上：</p>\n<ul>\n<li>开发过程中非常方便，一方面自己不需要在服务器中安装各类软件，也不需要搭建 web 服务，不需要对 web 服务进行优化，做的只是读取数据库，按照一定的格式进行 return，至于 web 服务等相关模块交给 API 网关来实现，整个一个后端开发大概耗时大约是一个多小时；前端开发是比较耗时的，因为我个人不是专业做前端的，所以无论是布局还是逻辑开发，都是有点障碍的，但是也只用了 2 天时间；所以这个模块从开发到上线只用了 2 天时间；</li>\n<li>项目在部署的时候非常流畅，基于 Serverless Framework 的开发者工具一键部署，后期更新维护，只需要重新部署即可，线上也是无缝切换，不会出现更新服务造成的服务中断，也不用为更新服务可能造成服务中断而做额外的操作，整体后期更新过程快速且简单易用；</li>\n<li>资源消耗部分就是使用按量付费，通过一个月的观察，整个资源消耗是蛮低的，整体性能保证的同时，成本也逐渐的被压低，对于个人开发者来说，确实是一个福音。</li>\n</ul>\n<p>通过这样一个简单上 Serverless 架构的过程，也让我对 Serverless 架构有了更深入的了解和认识，作为一种新技术或者说新的架构，Serverless 的成长还需要一段时间。但是我相信，他的成长，会很快速。</p>\n<hr>\n<div id='scf-deploy-iframe-or-md'></div>\n<hr>\n<blockquote>\n<p><strong>传送门：</strong></p>\n<ul>\n<li>GitHub: <a href=\"https://github.com/serverless/serverless/blob/master/README_CN.md\">github.com/serverless</a></li>\n<li>官网：<a href=\"https://serverless.com/\">serverless.com</a></li>\n</ul>\n</blockquote>\n<p>欢迎访问：<a href=\"https://serverlesscloud.cn/\">Serverless 中文网</a>，您可以在 <a href=\"https://serverlesscloud.cn/best-practice\">最佳实践</a> 里体验更多关于 Serverless 应用的开发！</p>","tableOfContents":"<ul>\n<li>\n<p><a href=\"/best-practice/2020-04-22-coding-learing/#%E6%95%B4%E4%BD%93%E8%AE%BE%E8%AE%A1\">整体设计</a></p>\n<ul>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E6%95%B0%E6%8D%AE%E5%BA%93%E8%AE%BE%E8%AE%A1\">数据库设计</a></li>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E5%90%8E%E7%AB%AF%E8%AE%BE%E8%AE%A1\">后端设计</a></li>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E5%89%8D%E7%AB%AF%E8%AE%BE%E8%AE%A1\">前端设计</a></li>\n</ul>\n</li>\n<li>\n<p><a href=\"/best-practice/2020-04-22-coding-learing/#%E9%A1%B9%E7%9B%AE%E5%BC%80%E5%8F%91\">项目开发</a></p>\n<ul>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E5%90%8E%E7%AB%AF%E5%87%BD%E6%95%B0%E5%BC%80%E5%8F%91\">后端函数开发</a></li>\n</ul>\n</li>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E6%95%88%E6%9E%9C%E9%A2%84%E8%A7%88\">效果预览</a></li>\n<li><a href=\"/best-practice/2020-04-22-coding-learing/#%E6%80%BB%E7%BB%93\">总结</a></li>\n</ul>"},"previousBlog":{"id":"a93bc1e0-585b-5ae5-be5c-4f850ce95080","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020512/1589276332053-Python-Programming-Course-The-Complete-Bootcamp.jpg","authors":["Anycodes"],"categories":["best-practice"],"date":"2020-04-23T00:00:00.000Z","title":"Serverless 实战：用 20 行 Python 代码轻松搞定图像分类和预测","description":"本文将尝试通过一个有趣的 Python 库，快速将图像分类的功能搭建在云函数上，并且和 API 网关结合，对外提供 API 功能，实现一个 Serverless 架构的「图像分类 API」","authorslink":["https://www.zhihu.com/people/liuyu-43-97"],"translators":null,"translatorslink":null,"tags":["Serverless","Python"],"keywords":"Serverless 全局变量组件,Serverless 单独部署组件,Serverless Component","outdated":true},"wordCount":{"words":298,"sentences":65,"paragraphs":65},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2020-04-23-python-pics.md","fields":{"slug":"/best-practice/2020-04-23-python-pics/","keywords":["python","serverless","云函数","prediction","Serverless","图像","Python","serverlesscloud","分类"]}},"nextBlog":{"id":"ebea695d-436a-5cb9-955a-2be16fa5102f","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020511/1589207417716-ZalNtxgQAC_small.jpg","authors":["Anycodes"],"categories":["best-practice"],"date":"2020-04-20T00:00:00.000Z","title":"Serverless 架构下，3 分钟实现文本敏感词过滤","description":"随着各种社交平台等的日益火爆，敏感词过滤逐渐成了非常重要的也是值得重视的功能。那么在 Serverless 架构下，敏感词过滤又有那些新的实现呢？","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["Python","文本处理"],"keywords":"Serverless 多环境配置,Serverless 管理环境,Serverless配置方案","outdated":true},"wordCount":{"words":167,"sentences":44,"paragraphs":44},"fileAbsolutePath":"/opt/build/repo/content/best-practice/2020-04-20-filter-sensitive-words.md","fields":{"slug":"/best-practice/2020-04-20-filter-sensitive-words/","keywords":["python","serverless","函数计算","self","root","关键词","敏感","time","level","word"]}}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"6e8a71a3-426e-5101-94db-bd5b1ef4d1ec","previousBlogId":"a93bc1e0-585b-5ae5-be5c-4f850ce95080","nextBlogId":"ebea695d-436a-5cb9-955a-2be16fa5102f"}}}