{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2021-03-23-openfass","result":{"data":{"currentBlog":{"id":"1b19a47c-5dca-50a4-8f84-86a084f544f7","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/36f1c6e908049ad64bde09330b1f9bf0.png","authors":["林加德"],"categories":["user-stories"],"date":"2021-03-23T00:00:00.000Z","title":"用户体验测评笔记：OpenFaaS 和腾讯云 Serverless","description":"本文通过 OpenFaaS 和腾讯云 Serverless 两种服务，讲述如何将业务部署到云平台的过程","authorslink":null,"translators":null,"translatorslink":null,"tags":["Serverless","OpenFaaS"],"keywords":null,"outdated":null},"wordCount":{"words":176,"sentences":32,"paragraphs":32},"fileAbsolutePath":"/opt/build/repo/content/blog/2021-03-23-openfass.md","fields":{"slug":"/blog/2021-03-23-openfass/","keywords":["faas","python","serverless","vec","tensorflow","scf","ssvm"]},"html":"<p>最近看到<a href=\"https://mp.weixin.qq.com/s/KdMRNiOhTy2aR_RN8b37cw\">《用 Serverless 架构部署 TensorFlow 模型推理函数》</a>的活动，对 serverless 非常感兴趣，本着学习的心态初步探索两个 serverless 框架，一个是开源的 OpenFaaS，一个是腾讯云，通过实际使用和对比初步入门 Serverless。</p>\n<h2 id=\"openfaas\"><a href=\"#openfaas\" aria-label=\"openfaas 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>OpenFaaS</h2>\n<p>按文档说明在 Ubuntu 20.04 上部署这个框架。    </p>\n<p>然后创建 Python 函数：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">def handle(req):\n    print(&quot;Hello! You said: &quot; + req)</code></pre></div>\n<p>修改配置，这里需要写入 docker hub 的帐号。</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">version: 1.0\nprovider:\n  name: openfaas\n  gateway: http://127.0.0.1:8080\nfunctions:\n  pycon:\n    lang: python3\n    handler: ./pycon\n    image: &gt;&gt;&gt; dockerhub 用户名&lt;&lt;&lt;/pycon</code></pre></div>\n<p>OpenFaaS 提供一个叫 faas-cli 的部署工具，faas-cli 会先将镜像上传到相应的 docker hub 帐号名下，然后再下拉到 OpenFaaS 服务。</p>\n<p>开始部署成功后在 Web 界面 127.0.0.1:8080/ui/ 看到刚才创建的函数。</p>\n<p>测试：</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">╰─➤  curl localhost:8080/function/pycon -d &quot;Hi&quot;\nHello! You said: Hi</code></pre></div>\n<p>从上面的例子可以看出：</p>\n<ol>\n<li>开发者只需要写好事件处理的函数，修改配置文件，确认部署即可，而不需要了解服务器的基础架构，甚至也不需要了解代码实际部署在哪个 Web 框架。</li>\n<li>FaaS 服务返回调用接口。</li>\n</ol>\n<h2 id=\"将图像识别服务部署到腾讯云\"><a href=\"#%E5%B0%86%E5%9B%BE%E5%83%8F%E8%AF%86%E5%88%AB%E6%9C%8D%E5%8A%A1%E9%83%A8%E7%BD%B2%E5%88%B0%E8%85%BE%E8%AE%AF%E4%BA%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<p>除了将 Serverless 业务构建在硬件和容器（比如，OpenFaaS 使用 docker）之外，还有一种新兴的方法: 使用特定于应用程序的虚拟机，比如 WebAssembly（Wasm）。</p>\n<p>这个例子通过 Second State 的  Serverless Wasm 虚拟机 (SSVM), 把用 Rust 编写的图像识别业务代码最终编译成 .so 文件，通过 <a href=\"https://www.serverless.com/\">serverless 工具</a> 上传到腾讯云的 FaaS 中。</p>\n<p>根据 Second State 的 demo 部署之后，在项目根目录输入 <code class=\"language-text\">sls deploy</code>, 验证腾讯云帐号，100 秒左右就部署成功，查看腾讯云的控制台，可以看到刚才部署的功能。</p>\n<p><img src=\"https://main.qcloudimg.com/raw/228909074a982356c337e318891b77a8.png\"></p>\n<p>测试：</p>\n<p><img src=\"https://main.qcloudimg.com/raw/adc54555e9d448fefaf5b91a11f94ea6.png\"></p>\n<h2 id=\"魔改\"><a href=\"#%E9%AD%94%E6%94%B9\" 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>通过魔改 Second State 的例子学习腾讯云 Serverless 的用法。</p>\n<p>先了解 <a href=\"https://github.com/second-state/tencent-tensorflow-scf\">tencent-tensorflow-scf</a> 的结构：</p>\n<p><img src=\"https://main.qcloudimg.com/raw/0a2fb6a05982dd05fa28ee179330a7dc.png\"></p>\n<p>cos, layer, scf 三个目录都有 serveress.yml，执行 <code class=\"language-text\">sls deploy</code> 的时候，可以看到这三个目录的文件被打包上传。</p>\n<p><img src=\"https://main.qcloudimg.com/raw/725df2064901719f5ca2dbaa3f5d70ef.png\"></p>\n<p>执行 <code class=\"language-text\">ssvmup build --enable-ext --enable-aot</code> 生成 <code class=\"language-text\">pkg/scf.so</code>, 需要将它拷贝至 <code class=\"language-text\">scf/</code> 目录。</p>\n<p><code class=\"language-text\">scf/bootstrap</code> 是一个脚本，运行后是一个服务进程。</p>\n<p>核心命令如下，其中 \"$_HANDLER\" 是 scf.so</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\">RESPONSE=$(LD_LIBRARY_PATH=/opt /opt/ssvm-tensorflow &quot;$_HANDLER&quot; &lt;&lt;&lt; &quot;$EVENT_DATA&quot;)</code></pre></div>\n<p>这就说明，我们可以在本地运行 \"$_HANDLER\"。我们可以先在本地调试业务功能。</p>\n<p>首先需要编译 <a href=\"https://github.com/second-state/ssvm-tensorflow\">ssvm-tensorflow</a> , 也可以直接下载二进制运行。</p>\n<p>编译完之后，将 <a href=\"https://github.com/second-state/wasm-learning/tree/master/faas/mobilenet\">这个 demo</a> 的代码迁移到 tencent-tensorflow-scf/src/main.rs, 实现魔改：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"13650707629926416000\"\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(`use std::io::{self, Read};\nuse ssvm_tensorflow_interface;\nuse serde::Deserialize;\n\nfn search_vec(vector: &Vec<f32>, labels: &Vec<&str>, value: &f32) -> (i32, String) {\n    for (i, f) in vector.iter().enumerate() {\n        if f == value {\n            return (i as i32, labels[i].to_owned());\n        }\n    }\n    return (-1, &quot;Unclassified&quot;.to_owned());\n}\n\nfn main() {\n    let model_data: &[u8] = include_bytes!(&quot;mobilenet_v2_1.4_224_frozen.pb&quot;);\n    let labels = include_str!(&quot;imagenet_slim_labels.txt&quot;);\n    let label_lines : Vec<&str> = labels.lines().collect();\n\n\n    let mut buffer = String::new();\n    io::stdin().read_to_string(&mut buffer).expect(&quot;Error reading from STDIN&quot;);\n    let obj: FaasInput = serde_json::from_str(&buffer).unwrap();\n    let img_buf = base64::decode_config(&(obj.body), base64::STANDARD).unwrap();\n\n    let flat_img = ssvm_tensorflow_interface::load_jpg_image_to_rgb32f(&img_buf, 224, 224);\n\n    let mut session = ssvm_tensorflow_interface::Session::new(model_data, ssvm_tensorflow_interface::ModelType::TensorFlow);\n    session.add_input(&quot;input&quot;, &flat_img, &[1, 224, 224, 3])\n           .add_output(&quot;MobilenetV2/Predictions/Softmax&quot;)\n           .run();\n    let res_vec: Vec<f32> = session.get_output(&quot;MobilenetV2/Predictions/Softmax&quot;);\n    let mut sorted_vec = res_vec.clone(); \n    sorted_vec.sort_by(|a, b| b.partial_cmp(a).unwrap());\n\n    let top1 = sorted_vec[0];\n    let top2 = sorted_vec[1];\n    let top3 = sorted_vec[2];\n\n    let r1 = search_vec(&res_vec, &label_lines, &top1);\n    let r2 = search_vec(&res_vec, &label_lines, &top2);\n    let r3 = search_vec(&res_vec, &label_lines, &top3);\n\n    println!(&quot;{}: {:.2}%\\n{}: {:.2}%\\n{}: {:.2}%&quot;\n        , r1.1, top1 * 100.0\n        , r2.1, top2 * 100.0   \n        , r3.1, top3 * 100.0   \n        );\n}\n\n#[derive(Deserialize, Debug)]\nstruct FaasInput {\n    body: String\n}`, `13650707629926416000`)\"\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=\"rust\"><pre class=\"language-rust\"><code class=\"language-rust\"><span class=\"token keyword\">use</span> std<span class=\"token punctuation\">::</span>io<span class=\"token punctuation\">::</span><span class=\"token punctuation\">{</span><span class=\"token keyword\">self</span><span class=\"token punctuation\">,</span> Read<span class=\"token punctuation\">}</span><span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> ssvm_tensorflow_interface<span class=\"token punctuation\">;</span>\n<span class=\"token keyword\">use</span> serde<span class=\"token punctuation\">::</span>Deserialize<span class=\"token punctuation\">;</span>\n\n<span class=\"token keyword\">fn</span> <span class=\"token function\">search_vec</span><span class=\"token punctuation\">(</span>vector<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span>Vec<span class=\"token operator\">&lt;</span>f32<span class=\"token operator\">></span><span class=\"token punctuation\">,</span> labels<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span>Vec<span class=\"token operator\">&lt;</span><span class=\"token operator\">&amp;</span>str<span class=\"token operator\">></span><span class=\"token punctuation\">,</span> value<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span>f32<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">-></span> <span class=\"token punctuation\">(</span>i32<span class=\"token punctuation\">,</span> String<span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">for</span> <span class=\"token punctuation\">(</span>i<span class=\"token punctuation\">,</span> f<span class=\"token punctuation\">)</span> <span class=\"token keyword\">in</span> vector<span class=\"token punctuation\">.</span><span class=\"token function\">iter</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">enumerate</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n        <span class=\"token keyword\">if</span> f <span class=\"token operator\">==</span> value <span class=\"token punctuation\">{</span>\n            <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span>i <span class=\"token keyword\">as</span> i32<span class=\"token punctuation\">,</span> labels<span class=\"token punctuation\">[</span>i<span class=\"token punctuation\">]</span><span class=\"token punctuation\">.</span><span class=\"token function\">to_owned</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 punctuation\">}</span>\n    <span class=\"token punctuation\">}</span>\n    <span class=\"token keyword\">return</span> <span class=\"token punctuation\">(</span><span class=\"token operator\">-</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"Unclassified\"</span><span class=\"token punctuation\">.</span><span class=\"token function\">to_owned</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 punctuation\">}</span>\n\n<span class=\"token keyword\">fn</span> <span class=\"token function\">main</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n    <span class=\"token keyword\">let</span> model_data<span class=\"token punctuation\">:</span> <span class=\"token operator\">&amp;</span><span class=\"token punctuation\">[</span>u8<span class=\"token punctuation\">]</span> <span class=\"token operator\">=</span> <span class=\"token function\">include_bytes!</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"mobilenet_v2_1.4_224_frozen.pb\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> labels <span class=\"token operator\">=</span> <span class=\"token function\">include_str!</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"imagenet_slim_labels.txt\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> label_lines <span class=\"token punctuation\">:</span> Vec<span class=\"token operator\">&lt;</span><span class=\"token operator\">&amp;</span>str<span class=\"token operator\">></span> <span class=\"token operator\">=</span> labels<span class=\"token punctuation\">.</span><span class=\"token function\">lines</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">collect</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n\n    <span class=\"token keyword\">let</span> <span class=\"token keyword\">mut</span> buffer <span class=\"token operator\">=</span> String<span class=\"token punctuation\">::</span><span class=\"token function\">new</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    io<span class=\"token punctuation\">::</span><span class=\"token function\">stdin</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">read_to_string</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span><span class=\"token keyword\">mut</span> buffer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">expect</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"Error reading from STDIN\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> obj<span class=\"token punctuation\">:</span> FaasInput <span class=\"token operator\">=</span> serde_json<span class=\"token punctuation\">::</span><span class=\"token function\">from_str</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span>buffer<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">unwrap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> img_buf <span class=\"token operator\">=</span> base64<span class=\"token punctuation\">::</span><span class=\"token function\">decode_config</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span><span class=\"token punctuation\">(</span>obj<span class=\"token punctuation\">.</span>body<span class=\"token punctuation\">)</span><span class=\"token punctuation\">,</span> base64<span class=\"token punctuation\">::</span>STANDARD<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">unwrap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> flat_img <span class=\"token operator\">=</span> ssvm_tensorflow_interface<span class=\"token punctuation\">::</span><span class=\"token function\">load_jpg_image_to_rgb32f</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span>img_buf<span class=\"token punctuation\">,</span> <span class=\"token number\">224</span><span class=\"token punctuation\">,</span> <span class=\"token number\">224</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> <span class=\"token keyword\">mut</span> session <span class=\"token operator\">=</span> ssvm_tensorflow_interface<span class=\"token punctuation\">::</span>Session<span class=\"token punctuation\">::</span><span class=\"token function\">new</span><span class=\"token punctuation\">(</span>model_data<span class=\"token punctuation\">,</span> ssvm_tensorflow_interface<span class=\"token punctuation\">::</span>ModelType<span class=\"token punctuation\">::</span>TensorFlow<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    session<span class=\"token punctuation\">.</span><span class=\"token function\">add_input</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"input\"</span><span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>flat_img<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span><span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">,</span> <span class=\"token number\">224</span><span class=\"token punctuation\">,</span> <span class=\"token number\">224</span><span class=\"token punctuation\">,</span> <span class=\"token number\">3</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span>\n           <span class=\"token punctuation\">.</span><span class=\"token function\">add_output</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"MobilenetV2/Predictions/Softmax\"</span><span class=\"token punctuation\">)</span>\n           <span class=\"token punctuation\">.</span><span class=\"token function\">run</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> res_vec<span class=\"token punctuation\">:</span> Vec<span class=\"token operator\">&lt;</span>f32<span class=\"token operator\">></span> <span class=\"token operator\">=</span> session<span class=\"token punctuation\">.</span><span class=\"token function\">get_output</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"MobilenetV2/Predictions/Softmax\"</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> <span class=\"token keyword\">mut</span> sorted_vec <span class=\"token operator\">=</span> res_vec<span class=\"token punctuation\">.</span><span class=\"token function\">clone</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> \n    sorted_vec<span class=\"token punctuation\">.</span><span class=\"token function\">sort_by</span><span class=\"token punctuation\">(</span><span class=\"token operator\">|</span>a<span class=\"token punctuation\">,</span> b<span class=\"token operator\">|</span> b<span class=\"token punctuation\">.</span><span class=\"token function\">partial_cmp</span><span class=\"token punctuation\">(</span>a<span class=\"token punctuation\">)</span><span class=\"token punctuation\">.</span><span class=\"token function\">unwrap</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> top1 <span class=\"token operator\">=</span> sorted_vec<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> top2 <span class=\"token operator\">=</span> sorted_vec<span class=\"token punctuation\">[</span><span class=\"token number\">1</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> top3 <span class=\"token operator\">=</span> sorted_vec<span class=\"token punctuation\">[</span><span class=\"token number\">2</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token keyword\">let</span> r1 <span class=\"token operator\">=</span> <span class=\"token function\">search_vec</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span>res_vec<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>label_lines<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>top1<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> r2 <span class=\"token operator\">=</span> <span class=\"token function\">search_vec</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span>res_vec<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>label_lines<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>top2<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n    <span class=\"token keyword\">let</span> r3 <span class=\"token operator\">=</span> <span class=\"token function\">search_vec</span><span class=\"token punctuation\">(</span><span class=\"token operator\">&amp;</span>res_vec<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>label_lines<span class=\"token punctuation\">,</span> <span class=\"token operator\">&amp;</span>top3<span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n\n    <span class=\"token function\">println!</span><span class=\"token punctuation\">(</span><span class=\"token string\">\"{}: {:.2}%\\n{}: {:.2}%\\n{}: {:.2}%\"</span>\n        <span class=\"token punctuation\">,</span> r1<span class=\"token number\">.1</span><span class=\"token punctuation\">,</span> top1 <span class=\"token operator\">*</span> <span class=\"token number\">100.0</span>\n        <span class=\"token punctuation\">,</span> r2<span class=\"token number\">.1</span><span class=\"token punctuation\">,</span> top2 <span class=\"token operator\">*</span> <span class=\"token number\">100.0</span>   \n        <span class=\"token punctuation\">,</span> r3<span class=\"token number\">.1</span><span class=\"token punctuation\">,</span> top3 <span class=\"token operator\">*</span> <span class=\"token number\">100.0</span>   \n        <span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span>\n\n<span class=\"token attribute attr-name\">#[derive(Deserialize, Debug)]</span>\n<span class=\"token keyword\">struct</span> FaasInput <span class=\"token punctuation\">{</span>\n    body<span class=\"token punctuation\">:</span> String\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>测试：</p>\n<p><img src=\"https://main.qcloudimg.com/raw/cc06e3c809472f686dfac49a6fe57762.png\"></p>\n<p>输出排名前三的可能结果。</p>\n<p>tomato.json 用于模拟请求数据，将图片数据 base64 之后放在 \"body\" 后面。</p>\n<p><img src=\"https://main.qcloudimg.com/raw/b48dcd21e994c5a3310af541e599e711.png\"></p>\n<p>最后重新 <code class=\"language-text\">sls deploy</code> 部署上线：</p>\n<p><img src=\"https://main.qcloudimg.com/raw/f5440eedec6ed88029f63a2aa22cebde.png\"></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>本文通过 OpenFaaS 和腾讯云 Serverless 两种服务，初步了解了将业务部署到云平台的过程。通过 FaaS 服务商提供的工具，用户可以避免直接操作 docker, 或设置脚本运行环境变量等不重要的细节，从而将注意力集中在业务开发上。</p>","tableOfContents":"<ul>\n<li><a href=\"/blog/2021-03-23-openfass/#openfaas\">OpenFaaS</a></li>\n<li><a href=\"/blog/2021-03-23-openfass/#%E5%B0%86%E5%9B%BE%E5%83%8F%E8%AF%86%E5%88%AB%E6%9C%8D%E5%8A%A1%E9%83%A8%E7%BD%B2%E5%88%B0%E8%85%BE%E8%AE%AF%E4%BA%91\">将图像识别服务部署到腾讯云</a></li>\n<li><a href=\"/blog/2021-03-23-openfass/#%E9%AD%94%E6%94%B9\">魔改</a></li>\n<li><a href=\"/blog/2021-03-23-openfass/#%E6%80%BB%E7%BB%93\">总结</a></li>\n</ul>"},"previousBlog":{"id":"662f1f6b-b303-58d5-a5b8-4397aad7f710","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/f843d90243a9a8261afd6c1db5aa22c6.jpg","authors":["Serverless 社区"],"categories":["meetup"],"date":"2021-03-25T00:00:00.000Z","title":"大咖直播预告｜云函数实践：使用 KonaJDK11 加速 Spring 云函数启动","description":"本周四晚 7:00，线上直播","authorslink":null,"translators":null,"translatorslink":null,"tags":["Serverless","Meetup"],"keywords":null,"outdated":null},"wordCount":{"words":null,"sentences":null,"paragraphs":null},"fileAbsolutePath":"/opt/build/repo/content/blog/2021-03-25-jdk-meetup.md","fields":{"slug":"/blog/2021-03-25-jdk-meetup/","keywords":[]}},"nextBlog":{"id":"d59c1f6e-5e81-5963-9f11-abef21d6e939","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/5be06958204a527b4d5dfa5e87283e6b.jpg","authors":["Serverless 社区"],"categories":["meetup"],"date":"2021-03-16T00:00:00.000Z","title":"高校呼声最高的开发实践直播预告：Serverless 快速建站","description":"本周四晚 7:00，线上直播","authorslink":null,"translators":null,"translatorslink":null,"tags":["Serverless","Meetup"],"keywords":null,"outdated":null},"wordCount":{"words":44,"sentences":10,"paragraphs":10},"fileAbsolutePath":"/opt/build/repo/content/blog/2021-03-16-techo-youth.md","fields":{"slug":"/blog/2021-03-16-techo-youth/","keywords":["serverless","serverless","Serverless","直播","serverlesscloud","github"]}},"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":"1b19a47c-5dca-50a4-8f84-86a084f544f7","previousBlogId":"662f1f6b-b303-58d5-a5b8-4397aad7f710","nextBlogId":"d59c1f6e-5e81-5963-9f11-abef21d6e939","categories":["user-stories"]}}}