{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2020-10-25-serverless-order","result":{"data":{"currentBlog":{"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"]},"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>这是一个 JAVA 开发的订单后台应用（没错！就是那个让无数大学生痛不欲生的订单后台系统），结合 Serverless 这一无服务器思想，尝试通过云函数 + API 网关 + 云数据库的组合来部署 Springboot 的成功之作。</p>\n<p>该应用提供了完整的用户登录验证、接口数据验证、订单流 (CRUD) 等强大的功能，而且在本地开发调试时也能模拟 API 网关调用云函数（本地 Java 开发云端部署不是问题），还兼容了云消息队列 CMQ 的调用，以便后续开发引入云中间件。</p>\n<p>同时，这种部署方式也能让其他的 Springboot 很快地转换为云函数部署。</p>\n<p>为响应国家「十四五计划」的环保计划，特地的研究了一下传说中的 <strong>Serverless</strong> 方案（省服务器 😄），于是便有了这次尝试。</p>\n<h2 id=\"语言和框架\"><a href=\"#%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6\" 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<ul>\n<li>JAVA 天下第一**，当然 c/c++/c#/node/python/go/php/vb 这些也不错</li>\n<li>JAVA 的单体应用还能选什么呢？只能是 Springboot 啊</li>\n</ul>\n<h2 id=\"部署准备\"><a href=\"#%E9%83%A8%E7%BD%B2%E5%87%86%E5%A4%87\" 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<ol>\n<li>注册个腾讯云账号</li>\n<li>开通以下产品权限（云函数、API 网关、对象存储）</li>\n<li>财力允许的话还可以购买数据库服务（因为年少轻狂打折时我购买了这俩很长很长时间)</li>\n<li>mysql数据库</li>\n<li>redis数据库</li>\n</ol>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566278987-img1.png\"></p>\n<h2 id=\"部署方案\"><a href=\"#%E9%83%A8%E7%BD%B2%E6%96%B9%E6%A1%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>订单应用来说的话，必然是提供 restful 的接口，所以在统一 VPC 内采用了云函数 + API 网关的模式提供接口，于是就有了以下方案：</p>\n<ol>\n<li>应用主体部署在云函数</li>\n<li>使用 API 网关作为函数入口</li>\n<li>页面则是使用了对象存储部署</li>\n<li>数据库方面则使用了同一 vpc 下的云数据库(财力有限只尝试了 mysql、redis，理论上其他应该都可行)</li>\n</ol>\n<h2 id=\"尝试部署\"><a href=\"#%E5%B0%9D%E8%AF%95%E9%83%A8%E7%BD%B2\" 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>要让 JAVA 工程部署到云函数上，首先了解什么是云函数(以下摘自微信开放文档)</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">云函数即在云端（服务器端）运行的函数。在物理设计上，一个云函数可由多个文件组成，占用一定量的 CPU 内存等计算资源；各云函数完全独立；可分别部署在不同的地区。开发者无需购买、搭建服务器，只需编写函数代码并部署到云端即可在小程序端调用，同时云函数之间也可互相调用。</code></pre></div>\n<p>云函数其实就是将业务拆分成函数粒度部署在云上，那么就写了个简单的 demo 部署到云函数上，并且配上了 API 网关尝试调用。</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"12755787186976030000\"\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 * 纯javascf快速开发部署(不走springboot)\n *\n * @author Freeeeeedom\n * @date 2020/10/24 10:31\n */\npublic class Scf {\n    /**\n     * log Object\n     */\n    private static Logger log = LoggerFactory.getLogger(Scf.class);\n    private static DruidDataSource dataSource1 = new DruidDataSource();\n\n    static {\n        //此处加载或修改数据源 多数据源配置多个\n        dataSource1.setUsername(&quot;Freeeeeedom&quot;);\n        dataSource1.setUrl(&quot;jdbc:mysql://Freeeeeedom?autoReconnectForPools=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai&quot;);\n        dataSource1.setPassword(&quot;Freeeeeedom&quot;);\n        dataSource1.setMinIdle(1);\n        dataSource1.setMaxActive(5);\n        dataSource1.setMaxWait(10000);\n        dataSource1.setValidationQuery(&quot;SELECT 1 from dual&quot;);\n        log.info(&quot;数据源加载ok~&quot;);\n    }\n\n    /**\n     * 纯scf入口参数\n     *\n     * @param insertParam 入参\n     * @return java.lang.Object 执行结果\n     * @author Freeeeeedom\n     * @date 2020/10/24 10:31\n     */\n    public Object pure(Map<String, Object> insertParam) {\n        log.info(&quot;param:{}&quot;, gson.toJson(insertParam);\n        Gson gson = new GsonBuilder().disableHtmlEscaping().create();\n        try {\n            Class.forName(&quot;com.mysql.jdbc.Driver&quot;);\n        } catch (ClassNotFoundException e) {\n            log.error(&quot;内部处理异常&quot;, e);\n        }\n        Map response = new HashMap();\n        JdbcTemplate jdbcTemplate = new JdbcTemplate();\n        jdbcTemplate.setDataSource(dataSource1);\n        Map order = jdbcTemplate.queryForMap(&quot;select order_id,create_time from \\`order\\` limit 1&quot;);\n        log.info(order.toString());\n        return buildResponse(gson, gson.toJson(order), response);\n    }\n\n    private Object buildResponse(Gson gson, String json, Map response) {\n        Map<String, String> headers = new HashMap(1);\n        headers.put(&quot;Content-Type&quot;, &quot;application/json&quot;);\n        response.put(&quot;statusCode&quot;, HttpStatus.OK.value());\n        response.put(&quot;headers&quot;, headers);\n        response.put(&quot;body&quot;, json);\n        return gson.toJson(response);\n    }\n}`, `12755787186976030000`)\"\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=\"java\"><pre class=\"language-java\"><code class=\"language-java\"> <span class=\"token comment\">/**\n * 纯javascf快速开发部署(不走springboot)\n *\n * @author Freeeeeedom\n * @date 2020/10/24 10:31\n */</span>\n<span class=\"token keyword\">public</span> <span class=\"token keyword\">class</span> <span class=\"token class-name\">Scf</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token comment\">/**\n     * log Object\n     */</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">static</span> <span class=\"token class-name\">Logger</span> log <span class=\"token operator\">=</span> <span class=\"token class-name\">LoggerFactory</span><span class=\"token punctuation\">.</span><span class=\"token function\">getLogger</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Scf</span><span class=\"token punctuation\">.</span><span class=\"token keyword\">class</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">private</span> <span class=\"token keyword\">static</span> <span class=\"token class-name\">DruidDataSource</span> dataSource1 <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">DruidDataSource</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">static</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token comment\">//此处加载或修改数据源 多数据源配置多个</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setUsername</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Freeeeeedom\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setUrl</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"jdbc:mysql://Freeeeeedom?autoReconnectForPools=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;serverTimezone=Asia/Shanghai\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setPassword</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Freeeeeedom\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setMinIdle</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setMaxActive</span><span class=\"token punctuation\">(</span><span class=\"token number\">5</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setMaxWait</span><span class=\"token punctuation\">(</span><span class=\"token number\">10000</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        dataSource1<span class=\"token punctuation\">.</span><span class=\"token function\">setValidationQuery</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"SELECT 1 from dual\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        log<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"数据源加载ok~\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token comment\">/**\n     * 纯scf入口参数\n     *\n     * @param insertParam 入参\n     * @return java.lang.Object 执行结果\n     * @author Freeeeeedom\n     * @date 2020/10/24 10:31\n     */</span>\n    <span class=\"token keyword\">public</span> <span class=\"token class-name\">Object</span> <span class=\"token function\">pure</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Map</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">String</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">Object</span><span class=\"token punctuation\">></span></span> insertParam<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        log<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"param:{}\"</span><span class=\"token punctuation\">,</span> gson<span class=\"token punctuation\">.</span><span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span>insertParam<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token class-name\">Gson</span> gson <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">GsonBuilder</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">disableHtmlEscaping</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">create</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">try</span> <span class=\"token punctuation\">{</span>\n            <span class=\"token class-name\">Class</span><span class=\"token punctuation\">.</span><span class=\"token function\">forName</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"com.mysql.jdbc.Driver\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span> <span class=\"token keyword\">catch</span> <span class=\"token punctuation\">(</span><span class=\"token class-name\">ClassNotFoundException</span> e<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n            log<span class=\"token punctuation\">.</span><span class=\"token function\">error</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"内部处理异常\"</span><span class=\"token punctuation\">,</span> e<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token punctuation\">}</span>\n        <span class=\"token class-name\">Map</span> response <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HashMap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token class-name\">JdbcTemplate</span> jdbcTemplate <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">JdbcTemplate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        jdbcTemplate<span class=\"token punctuation\">.</span><span class=\"token function\">setDataSource</span><span class=\"token punctuation\">(</span>dataSource1<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token class-name\">Map</span> order <span class=\"token operator\">=</span> jdbcTemplate<span class=\"token punctuation\">.</span><span class=\"token function\">queryForMap</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"select order_id,create_time from `order` limit 1\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        log<span class=\"token punctuation\">.</span><span class=\"token function\">info</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">.</span><span class=\"token function\">toString</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> <span class=\"token function\">buildResponse</span><span class=\"token punctuation\">(</span>gson<span class=\"token punctuation\">,</span> gson<span class=\"token punctuation\">.</span><span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span>order<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n\n    <span class=\"token keyword\">private</span> <span class=\"token class-name\">Object</span> <span class=\"token function\">buildResponse</span><span class=\"token punctuation\">(</span><span class=\"token class-name\">Gson</span> gson<span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span> json<span class=\"token punctuation\">,</span> <span class=\"token class-name\">Map</span> response<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token class-name\">Map</span><span class=\"token generics\"><span class=\"token punctuation\">&lt;</span><span class=\"token class-name\">String</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">String</span><span class=\"token punctuation\">></span></span> headers <span class=\"token operator\">=</span> <span class=\"token keyword\">new</span> <span class=\"token class-name\">HashMap</span><span class=\"token punctuation\">(</span><span class=\"token number\">1</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        headers<span class=\"token punctuation\">.</span><span class=\"token function\">put</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Content-Type\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"application/json\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">put</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"statusCode\"</span><span class=\"token punctuation\">,</span> <span class=\"token class-name\">HttpStatus</span><span class=\"token punctuation\">.</span>OK<span class=\"token punctuation\">.</span><span class=\"token function\">value</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">put</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"headers\"</span><span class=\"token punctuation\">,</span> headers<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        response<span class=\"token punctuation\">.</span><span class=\"token function\">put</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"body\"</span><span class=\"token punctuation\">,</span> json<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n        <span class=\"token keyword\">return</span> gson<span class=\"token punctuation\">.</span><span class=\"token function\">toJson</span><span class=\"token punctuation\">(</span>response<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>只需要打包好代码，然后将入口函数设置为 <code class=\"language-text\">scf.Scf::pure</code> 就实现了接收数据，然后从数据库查询了第一个订单的 id 和创建时间并且返回的能力：</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566279001-img1.png\"></p>\n<p>每一次通过 API 网关触发云函数都会触发 pure 这个方法(调用者 > 调用 API 网关 > 云函数 --> pure)，但经测试发现 static 的数据源初始化并不会被重复加载，这也奠定了 springboot 可部署基础。</p>\n<p>其中通过 log 打印 API 网关带来的参数，直接将其复制为 json，然后通过 main 函数模拟调用，这样就实现了本地模拟 serverless 部署后的调用。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">log.info(&quot;param:{}&quot;, gson.toJson(insertParam);</code></pre></div>\n<p>有了这些基础，那么只需要有一个入口类模拟 springboot 启动的加载，然后再映射一下 API 网关过来入口参数，即可实现 springboot 在云函数上部署（其实就是上面 SCF 类的超级 plus 版本）。</p>\n<p>** API 网关配置**</p>\n<p>这里的路径参数对应 springboot 里的 mapping 路径</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566338083-apiconfig.png\"></p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566362752-apiconfigdetail.png\"></p>\n<h2 id=\"本地调试\"><a href=\"#%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95\" 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>有了上面那些 demo 后，可得知我们模拟云端部署运行已经不是问题。那么怎么在本地调试呢？答案很简单，直接启动 springboot 然后调正常就完事了。</p>\n<p>没错，就是直接用原生的 springboot 玩法即可。把 springboot 部署到云函数其实就是<strong>外挂了一个 springboot 的启动类</strong>（设计模式上叫适配器模式？(+_+)?</p>\n<h2 id=\"功能\"><a href=\"#%E5%8A%9F%E8%83%BD\" 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>完整的 springboot，能用 springboot 做的都能实现，我只是编写了一些小功能验证这个应用。</p>\n<ul>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 与本地服务器数据库连接</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 云数据库连接</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> vpc数据库连接</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 外部接口调用(发短信验证码)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 实现简单的订单流 (crud)</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 实现简单的登录能力</li>\n<li class=\"task-list-item\"><input type=\"checkbox\" checked disabled> 实现简单的数据验证能力</li>\n</ul>\n<p>整个项目功能简单但代码却不少。</p>\n<h2 id=\"安全\"><a href=\"#%E5%AE%89%E5%85%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>首先 \"serverless\"、\"腾讯\"、\"云服务\" 这几个词就足以代表安全了，但为了功能完整性我还是尝试加了点东西。</p>\n<p>在这个系统中，我选择了 header 中加签名的方式验证数据，原因是啥，操作简单，有效呗。加密手段和方案暂且不说，就从流程上来看，是很方便的:</p>\n<ol>\n<li>从 API 网关调用参数中获取到 header，body</li>\n<li>验证数据有效性</li>\n<li>请求转入业务模块</li>\n<li>验证数据有效性</li>\n<li>参数进入功能模块</li>\n<li>验证数据有效性</li>\n<li>………………</li>\n</ol>\n<p>其实只有 123 步骤是最有效的，后面的 45678 如果你想的话……更不用说 API 网关本身提供的鉴权功能了。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566390815-apiconfigaccess.png\"></p>\n<h2 id=\"性能\"><a href=\"#%E6%80%A7%E8%83%BD\" 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>内存的话对于订单系统来说单次请求加上 JVM 也才 300mb，而云函数单个函数执行内存能拉到 3GB，哪怕有点量的分布式计算应该问题也不大。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566438409-3gb.png\"></p>\n<p>并发的话云函数上的预置并发上限 200 个，订单系统嘛，QPS1000?10000?100000? ezpz了，再怎么也比自家机柜服务器强几百几千个量级了。</p>\n<p><img src=\"https://img.serverlesscloud.cn/2020115/1604566441354-predeploy.png\"></p>\n<p>内存算力不够服务器扩容？不存在的。</p>\n<h2 id=\"最后\"><a href=\"#%E6%9C%80%E5%90%8E\" 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>生成个 VUE 项目，改改链接调调页面，然后上传到存储桶上，一键打开 CDN ~(￣▽￣)~*完美！</p>\n<p>察觉到了到了科技的进步，时代的发展，Serverless 的强大。</p>\n<hr>\n<hr>\n<div id='scf-deploy-iframe-or-md'></div>\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-25-serverless-order/#%E5%89%8D%E8%A8%80\">前言</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E8%AF%AD%E8%A8%80%E5%92%8C%E6%A1%86%E6%9E%B6\">语言和框架</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E9%83%A8%E7%BD%B2%E5%87%86%E5%A4%87\">部署准备</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E9%83%A8%E7%BD%B2%E6%96%B9%E6%A1%88\">部署方案</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E5%B0%9D%E8%AF%95%E9%83%A8%E7%BD%B2\">尝试部署</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E6%9C%AC%E5%9C%B0%E8%B0%83%E8%AF%95\">本地调试</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E5%8A%9F%E8%83%BD\">功能</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E5%AE%89%E5%85%A8\">安全</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E6%80%A7%E8%83%BD\">性能</a></li>\n<li><a href=\"/blog/2020-10-25-serverless-order/#%E6%9C%80%E5%90%8E\">最后</a></li>\n</ul>"},"previousBlog":{"id":"2a450970-4d07-5115-9b7b-e0b9ae9ac4a7","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/20201125/1606302161462-QQ20201125-190101.jpg","authors":["兰浩"],"categories":["user-stories"],"date":"2020-10-26T00:00:00.000Z","title":"🤸‍♂️👩‍💻 使用腾讯云 Serverless 生成你的 GIT 代码年历！","description":"不要 Github profile 页的统计图！是好程序员就应该自己 Host！","authorslink":["https://github.com/LanHao0"],"translators":null,"translatorslink":null,"tags":["Serverless","GitHub"],"keywords":null,"outdated":null},"wordCount":{"words":125,"sentences":33,"paragraphs":33},"fileAbsolutePath":"/opt/build/repo/content/blog/2020-10-26-gitchartofyear.md","fields":{"slug":"/blog/2020-10-26-gitchartofyear/","keywords":["serverless","代码","Serverless","serverless","github","serverlesscloud"]}},"nextBlog":{"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"]}},"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":"bb1e667a-3b55-5ab5-b1d0-fc6480061def","previousBlogId":"2a450970-4d07-5115-9b7b-e0b9ae9ac4a7","nextBlogId":"5d75ad51-0cf8-5328-97bc-55494112b7b0","categories":["user-stories"]}}}