{"componentChunkName":"component---src-templates-blog-detail-tsx","path":"/blog/2019-11-13-serverless-ssr-now","result":{"data":{"currentBlog":{"id":"82d71850-8dc8-5a6a-8f90-5a66609dae1c","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/images/YHl6UWa9s61qzticncvwNpZulMqH7uWLsJ37JictvIBCibiaU5sDYicicxtKP9bu8PEWPCZyIlpfymbzmbw2geGvPaHw.jpg","authors":["Serverless 中文网"],"categories":["news"],"date":"2019-11-13T00:00:00.000Z","title":"Node 部署和运维工作量降低 80%，腾讯 NOW 直播是怎么做到的？","description":"本篇文章，将分享腾讯NOW直播在Serverless技术的探索实践。","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["NGW","云函数"],"keywords":"Serverless, Serverless实践, Serverless SSR","outdated":null},"wordCount":{"words":505,"sentences":90,"paragraphs":90},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-11-13-serverless-ssr-now.md","fields":{"slug":"/blog/2019-11-13-serverless-ssr-now/","keywords":["go","java","serverless","云函数","Serverless","分发","服务","Node","插件","Application","接入","配置"]},"html":"<p>本篇文章，将分享腾讯NOW直播在Serverless技术的探索实践。</p>\n<p><strong>一、背景简介</strong></p>\n<p>首先解释一下几个名词：</p>\n<ul>\n<li><strong>NGW</strong>（Node Gateway，Node 前端接入层）：IVWEB 团队开发的内部前端业务接入层方案，目前处于腾讯内部开源状态。</li>\n<li><strong>BFF</strong> (Backend For Frontend) ，通常指服务于前端的后端。</li>\n<li><strong>SFF</strong> (Severless For Frontend) ， BFF 架构的升华，落地到 Serverless 中。</li>\n</ul>\n<p>目前，NOW 直播团队正在逐步接入 NGW，完成 BFF 向 SFF 架构的演进。</p>\n<p>从前后端分离到 BFF，前端的能力不断扩大，逐渐涉猎到后端和运维，对前端人员的技术能力要求也越来越高。NOW 直播 IVWEB 团队从提升研发效率的角度，在调研了腾讯云Serverless之后，团队决定接入云函数SCF产品，对 BFF 架构结合 Serverless 进行了新的尝试。</p>\n<p>在 SFF 架构下，Node 服务落地到 Serverless，<strong>最大的收益者是前端开发人员</strong>：</p>\n<ul>\n<li>前端同学不再需要关注服务器的申请/维护/扩容；</li>\n<li>无需关心生产环境的搭建；</li>\n<li>真正做到专注业务逻辑的开发；</li>\n<li>自动扩容，零运维；</li>\n<li>接入工作量降低了 80%。</li>\n</ul>\n<p>此前，团队已经通过开发 Nest 系统的聚合服务（Node 服务的可视化管理）完成了 BFF 架构的演进。NGW 作为新项目，响应腾讯自研上云的号召，抛开历史包袱，全面拥抱上云，并且有幸成为公司内首个和腾讯云 SCF 大规模合作的前端项目，结合 SCF 实现了同构直出和 Node 服务的无服务化，做到自动部署和更新。在这里<strong>感谢腾讯云Serverless团队</strong>和<strong>QQ团队</strong>的大力支持。</p>\n<p><strong>二、从一个故事说起</strong></p>\n<p>阿特是个特别认真的人，但也有汉子柔情的一面，他攒了很久的假期，准备在情人节和小美来一场说走就走的旅行。不巧，就在情人节前一天，新上线的项目被产品挑战了，说“首屏速度不够快”。</p>\n<p>作为高级工程师的阿特，有 100 种可以让页面提升首屏的方法；他马上用团队的同构直出框架，半天就撸出来了一个直出服务，部署完之后就可以和心爱的小美去旅行了。</p>\n<p>阿特打开运维系统，看了一下业务机的承载，需要扩容才能满足需求。为了不影响线上业务，阿特选了新申请一批机器。半天过去了，新机器终于下来了，这时距离下班时间还有 5 分钟。为了小美的旅行，今天无论如何都要上线。</p>\n<p>面对新下来的机器，阿特一把梭装好了所有基础组件，配置和部署好直出服务，现在只需要将原来的静态请求接入到直出服务就可以了；但事情并没有这么简单，现网的域名竟然要经过好几层接入机才能到直出服务。</p>\n<p>凭借着高级工程师的一双能手，阿特一步步地终于将业务成功上线了。他松了一口气，看了一下时间，沉默了许久。现在是北京时间 2 月 14 日上午 9:55，距离飞机起飞时间还有 5 分钟，阿特终究还是错过了和小美的旅行。</p>\n<p>故事虽然听起来比较夸张，但反映的事实却无比真实：</p>\n<ul>\n<li>业务不断增多，服务混布导致新老业务的<strong>变更越来越复杂</strong>，全部独立部署则会造成资源浪费；</li>\n<li>运维系统鱼龙混杂概念繁多，不是老司机很难理清楚，<strong>对小白不友好</strong>；</li>\n<li>业务模块多、机器多、配置多，<strong>变更全靠配置系统下发</strong>；</li>\n<li>前后端共用接入机，<strong>转发逻辑复杂</strong>。</li>\n</ul>\n<p>我们发现，即使完成了 BFF 架构的演进，但难免会存在服务混布，而且仍需关注复杂的运维，效率的瓶颈始终在运维上。<strong>有没有一种方法可以既能使服务独立部署，并且能有效降低运维成本呢？</strong></p>\n<p>答案：<strong>Serverless</strong>。</p>\n<p><strong>三、NGW 的架构</strong></p>\n<p><strong>NGW</strong>（Node Gateway，Node 前端接入层）是介于 STGW 和真实业务的中间层，用户的请求会通过 统一网关先进入到 NGW 层，再由 NGW 来分发请求到对应业务上。</p>\n<p>这里的业务可以是：</p>\n<ul>\n<li>静态页面</li>\n<li>BFF 架构下的 Node 服务</li>\n<li>SFF 架构下的 Serverless 直出或 Node 服务</li>\n</ul>\n<p>咦，这不是 Nginx 做的工作吗？——<strong>Nginx 虽然强大，但缺点是无法做到强逻辑的分发，也不容易进行扩展，而且每次更改配置之后都需要重启服务，查看日志也很困难。</strong></p>\n<p>NGW 使用 TypeScript + Node 开发，对前端同学更友好，能更方便地进行功能的迭代和扩展，转发逻辑可以动态下发，无需重启服务。</p>\n<p>整个 NGW 方案主要分为三个模块：</p>\n<ul>\n<li>NGW 分发层：主要专注于业务的分发；</li>\n<li>NGW-Service 层：负责周边服务，如配置管理、Serverless 服务的部署和更新，日志旁路等；</li>\n<li>NGW Platform：管理平台，用于配置管理与日志管理等。</li>\n</ul>\n<p><strong>四、容器化</strong></p>\n<p>作为 9102 年的新项目，响应自研上云的号召，NGW 的全部服务均使用 Orange-CI（自研内部 CI 工具） + TKE 腾讯云容器服务 进行构建和部署，并利用容器编排对 Docker 镜像的构建部署进行了优化。（优化后构建镜像时间缩短为 20秒，大小缩小至 10M 以内）</p>\n<p>NGW 分发层作为业务分发的基础服务，性能和稳定性非常重要，有了 Orange-CI 和 STKE 的加持，大大提高了服务部署的敏捷程度，并且在高并发场景下也能做到灵活自动扩容，再也不用提前评估业务量。</p>\n<p>TKE 单机满负载运行资源占用（8 核 16G，数据来源 TKE）：</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/images/6aVaON9Kibf4tGcQJIlibvXKlsfS0oZUVTkFDia15PGTSLp0R9hgkTemPQictI0Bich22BzicXDJEWJzMJhre3aFTXqA.jpg\" alt=\"Serverless\"></p>\n<p>高并发场景压测下自动扩容至 8 个实例，峰值接近 8000 TPS（数据来源于 WeTest 压测大师）：</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/images/TN05MmJLxMqpZPPtdbqykRRWoicibX5BokYEEHtvCWqcgPHtjvLYaG8IYiaJCCtaIEeGu3urvOlq25EDib7gM0TKFQ.jpg\" alt=\"Serverless\"></p>\n<p>为什么峰值只压到 8000 TPS 呢？主要是因为峰值已经达到下层静态服务器的最大承载了，导致转发效率变慢，并不代表 8000 TPS就是 NGW 层的最大承载（逃）。在 STKE 资源充足的前提下，NGW 是可以无限扩容的，无需担心高负载导致服务器宕机。</p>\n<p><strong>五、配置下发</strong></p>\n<p>Nginx 每次更改转发配置，都需要重启 Nginx 服务。</p>\n<p>NGW 开发了 NGW-Service 层，结合管理平台对配置进行动态管理，配置更改后通过云 Redis 下发到 NGW 分发层中。配置热重载，平滑过渡。</p>\n<p>配置下发流程：</p>\n<p>目前已完成分发逻辑的动态下发，后续将陆续支持操作记录和版本管理。</p>\n<p><strong>六、Serverless 无服务</strong></p>\n<p>在 NGW 分发层的基础上，结合腾讯云 SCF，就能更方便灵活地对现有 Node 服务进行 BFF 到 SFF 的演进。</p>\n<ul>\n<li>NGW-Service 负责 Serverless 服务的管理，如部署、更新等；</li>\n<li>NGW 分发层进行业务的转发。</li>\n</ul>\n<p>架构：</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/images/TN05MmJLxMqpZPPtdbqykRRWoicibX5BokyopCc2iaibchknNq0bDP53O0l0qiaUlo9tnz5NVS6Ep32vJFRKXZR8BcA.jpg\" alt=\"Serverless\"></p>\n<p>目前 NGW 已经支持同构 SSR 的 Serverless 化方案，并且已接入多个项目在线上通过验证，<strong>成功率接近 99.99%</strong>（数据来自内部打点统计系统）。</p>\n<p>NGW 目前处于腾讯内部开源状态，待方案完善脱敏后将会逐步进行外部开源。</p>\n<p>关于 Serverless 化方案的实现技术细节，后续将会有专门的文章来介绍，本文先不赘述了。</p>\n<p><strong>七、插件体系</strong></p>\n<p>前文说到 Nginx 虽然强大，但缺点是不容易进行扩展。NGW 的分发功能扩展是怎么实现的呢？</p>\n<p>除了 Koa 中间件，NGW 还引入了插件的概念来对专用分发逻辑进行模块化：</p>\n<ul>\n<li>Koa 中间件：用于处理统一逻辑；</li>\n<li>插件：与单次分发过程的生命周期强关联，用于处理专用逻辑。</li>\n</ul>\n<p>插件的实现基于 Tapable，通过生命周期钩子来对分发过程进行逻辑干预。</p>\n<p>生命周期钩子：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"84591254934228940000\"\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(`// 分为同步和异步钩子两种，以同步钩子为例\ninterface ApplicationSyncHooks {\n   // 收到请求\n   didReceiveReq: AsyncSeriesHook<[Application, ProxyConfig]>;\n   // 即将转发代理请求\n   willSendProxyReq: AsyncSeriesHook<[Application, ProxyConfig]>;\n   // 代理请求成功\n   proxyReqSucc: AsyncSeriesHook<[Application, TswAjaxResult]>;\n   // 代理请求失败\n   proxyReqFail: AsyncParallelHook<[Application, TswAjaxResult]>;\n   // 接收到代理请求响应\n   didReceiveProxyRes: AsyncParallelHook<[Application, IncomingMessage]>;\n   // 即将返回代理层响应结果\n   willReturnRes: AsyncSeriesHook<[Application, IncomingMessage]>;\n   // 返回响应完成\n   didReturnRes: AsyncSeriesHook<[Application, TswAjaxResult]>;\n}`, `84591254934228940000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// 分为同步和异步钩子两种，以同步钩子为例</span>\n<span class=\"token keyword\">interface</span> <span class=\"token class-name\">ApplicationSyncHooks</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token comment\">// 收到请求</span>\n   didReceiveReq<span class=\"token punctuation\">:</span> AsyncSeriesHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> ProxyConfig<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 即将转发代理请求</span>\n   willSendProxyReq<span class=\"token punctuation\">:</span> AsyncSeriesHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> ProxyConfig<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 代理请求成功</span>\n   proxyReqSucc<span class=\"token punctuation\">:</span> AsyncSeriesHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> TswAjaxResult<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 代理请求失败</span>\n   proxyReqFail<span class=\"token punctuation\">:</span> AsyncParallelHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> TswAjaxResult<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 接收到代理请求响应</span>\n   didReceiveProxyRes<span class=\"token punctuation\">:</span> AsyncParallelHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> IncomingMessage<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 即将返回代理层响应结果</span>\n   willReturnRes<span class=\"token punctuation\">:</span> AsyncSeriesHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> IncomingMessage<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n   <span class=\"token comment\">// 返回响应完成</span>\n   didReturnRes<span class=\"token punctuation\">:</span> AsyncSeriesHook<span class=\"token operator\">&lt;</span><span class=\"token punctuation\">[</span>Application<span class=\"token punctuation\">,</span> TswAjaxResult<span class=\"token punctuation\">]</span><span class=\"token operator\">></span><span class=\"token punctuation\">;</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>利用插件，可以在基础的分发逻辑上进行一些专用的逻辑处理，比如灰度转发、白名单等。以灰度插件为例，可以在分发配置中添加插件：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"59583914414334550000\"\n              data-toaster-class=\"gatsby-code-button-toaster\"\n              data-toaster-text-class=\"gatsby-code-button-toaster-text\"\n              data-toaster-text=\"代码复制成功\"\n              data-toaster-duration=\"3500\"\n              onClick=\"copyToClipboard(`{\n   &quot;origin&quot;: &quot;/ngw/index.html&quot;,\n   &quot;proxyPass&quot;: &quot;http://the-path-where-you-want-to-go.com&quot;,\n   // 添加灰度插件，Application 会在分发前进行插件实例化，并将 args 传入构造函数中\n   &quot;plugins&quot;: [{\n       &quot;name&quot;: &quot;AlphaPlugin&quot;, &quot;args&quot;: [&quot;plan-A&quot;]\n   }]\n}`, `59583914414334550000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token punctuation\">{</span>\n   <span class=\"token string\">\"origin\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"/ngw/index.html\"</span><span class=\"token punctuation\">,</span>\n   <span class=\"token string\">\"proxyPass\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"http://the-path-where-you-want-to-go.com\"</span><span class=\"token punctuation\">,</span>\n   <span class=\"token comment\">// 添加灰度插件，Application 会在分发前进行插件实例化，并将 args 传入构造函数中</span>\n   <span class=\"token string\">\"plugins\"</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token punctuation\">{</span>\n       <span class=\"token string\">\"name\"</span><span class=\"token punctuation\">:</span> <span class=\"token string\">\"AlphaPlugin\"</span><span class=\"token punctuation\">,</span> <span class=\"token string\">\"args\"</span><span class=\"token punctuation\">:</span> <span class=\"token punctuation\">[</span><span class=\"token string\">\"plan-A\"</span><span class=\"token punctuation\">]</span>\n   <span class=\"token punctuation\">}</span><span class=\"token punctuation\">]</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>插件逻辑处理：</p>\n<div\n              class=\"gatsby-code-button-container\"\n              data-toaster-id=\"3954224437920706000\"\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\nclass AlphaPlugin extends AbstractPlugin {\n   public name = 'AlphaPlugin';\n   public config: AlphaPluginConfig;\n   public constructor(args: AlphaPluginConfig) {\n       super();\n       this.config = args; // 接收配置中的 args 参数\n   }\n   public apply(application: Application) {\n       application.syncHooks.didReceiveReq.tapPromise(this.name, this.didReceiveReq.bind(this));\n   }\n   public async didReceiveReq(application: Application, proxyConfig: ProxyConfig) {\n       const alphaConfig = getAlphaConfig(this.config[0]); // 获取灰度逻辑，入参是配置的 &quot;plan-A&quot;\n       proxyConfig.proxyPass = alphaConfig.url; // 修改分发的地址\n   }\n}`, `3954224437920706000`)\"\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=\"javascript\"><pre class=\"language-javascript\"><code class=\"language-javascript\"><span class=\"token comment\">// 以下为简化代码...</span>\n\n<span class=\"token keyword\">class</span> <span class=\"token class-name\">AlphaPlugin</span> <span class=\"token keyword\">extends</span> <span class=\"token class-name\">AbstractPlugin</span> <span class=\"token punctuation\">{</span>\n   <span class=\"token keyword\">public</span> name <span class=\"token operator\">=</span> <span class=\"token string\">'AlphaPlugin'</span><span class=\"token punctuation\">;</span>\n   <span class=\"token keyword\">public</span> config<span class=\"token punctuation\">:</span> AlphaPluginConfig<span class=\"token punctuation\">;</span>\n   <span class=\"token keyword\">public</span> <span class=\"token function\">constructor</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">args<span class=\"token punctuation\">:</span> AlphaPluginConfig</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       <span class=\"token keyword\">super</span><span class=\"token punctuation\">(</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span>\n       <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>config <span class=\"token operator\">=</span> args<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 接收配置中的 args 参数</span>\n   <span class=\"token punctuation\">}</span>\n   <span class=\"token keyword\">public</span> <span class=\"token function\">apply</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">application<span class=\"token punctuation\">:</span> Application</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       application<span class=\"token punctuation\">.</span>syncHooks<span class=\"token punctuation\">.</span>didReceiveReq<span class=\"token punctuation\">.</span><span class=\"token function\">tapPromise</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>name<span class=\"token punctuation\">,</span> <span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span><span class=\"token function\">didReceiveReq</span><span class=\"token punctuation\">.</span><span class=\"token function\">bind</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</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 keyword\">public</span> <span class=\"token keyword\">async</span> <span class=\"token function\">didReceiveReq</span><span class=\"token punctuation\">(</span><span class=\"token parameter\">application<span class=\"token punctuation\">:</span> Application<span class=\"token punctuation\">,</span> proxyConfig<span class=\"token punctuation\">:</span> ProxyConfig</span><span class=\"token punctuation\">)</span> <span class=\"token punctuation\">{</span>\n       <span class=\"token keyword\">const</span> alphaConfig <span class=\"token operator\">=</span> <span class=\"token function\">getAlphaConfig</span><span class=\"token punctuation\">(</span><span class=\"token keyword\">this</span><span class=\"token punctuation\">.</span>config<span class=\"token punctuation\">[</span><span class=\"token number\">0</span><span class=\"token punctuation\">]</span><span class=\"token punctuation\">)</span><span class=\"token punctuation\">;</span> <span class=\"token comment\">// 获取灰度逻辑，入参是配置的 \"plan-A\"</span>\n       proxyConfig<span class=\"token punctuation\">.</span>proxyPass <span class=\"token operator\">=</span> alphaConfig<span class=\"token punctuation\">.</span>url<span class=\"token punctuation\">;</span> <span class=\"token comment\">// 修改分发的地址</span>\n   <span class=\"token punctuation\">}</span>\n<span class=\"token punctuation\">}</span></code></pre></div>\n<p>这样一来，就可以通过灰度插件实现对现网业务进行灰度上线或 ABTest，比如现网投放的 A 页面，通过 NGW 可以动态分发到 B 或 C 等页面。</p>\n<p><strong>八、日志查看</strong></p>\n<p>NGW 基于 TSW，得益于 TSW 的染色体系，可以轻松地查看实时染色日志，秒杀 Nginx。后续也会将 SCF 中的日志收归到 NGW-Platform 中。</p>\n<p><strong>九、现状</strong></p>\n<h3 id=\"serverless-ssr\"><a href=\"#serverless-ssr\" aria-label=\"serverless ssr 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><strong>Serverless SSR</strong></h3>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/images/6aVaON9Kibf4tGcQJIlibvXKlsfS0oZUVTicBUlL4cTFqdSr1awmXTgfXic2wVkqkNVoxeW8NyeTHBVc9RsBg4Pa4A.png\" alt=\"Serverless\"></p>\n<p>目前 NGW + Serverless SSR 已经应用到 NOW 直播、手 Q 附近、浏览器直播和手 Q 群送礼等多个项目中。实际业务开发中，<strong>Node 业务的部署和运维工作量降低了 80% 以上</strong>。</p>\n<p>以前的业务上线路径：</p>\n<p>申请机器 -> 申请 CL5 -> 织云包下发部署服务 -> 更改 Nginx 配置</p>\n<p>接入 NGW + Serverless SSR 只需要两步，后续接入团队脚手架后，连这两部都可以省掉了，工作量 down down down：</p>\n<ul>\n<li>更改 oci 配置，将 CI 制品推送到 SCF</li>\n<li>NGW-Platform 添加分发配置</li>\n</ul>\n<h3 id=\"稳定性\"><a href=\"#%E7%A8%B3%E5%AE%9A%E6%80%A7\" 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><strong>稳定性</strong></h3>\n<p>NGW 目前的分发成功率稳定在 3 个 9 以上，单元测试覆盖率已超过 85%。</p>\n<p><img src=\"https://img.serverlesscloud.cn/qianyi/images/TN05MmJLxMqpZPPtdbqykRRWoicibX5BokJJZ38r7Rw00yr7douEWSNJNnDL4YvmPcI8ibjGfeqPHt00ZUvCxH6tg.png\" alt=\"Serverless\"></p>\n<p><strong>十、接下来的计划</strong></p>\n<ul>\n<li>和腾讯云 SCF 展开深度合作，提升性能；</li>\n<li>React Chunked 流式渲染方案，结合 SCF 进一步提升首屏速度；</li>\n<li>接入 Aegis 前端监控体系，实现前端全息日志；</li>\n</ul>","tableOfContents":"<ul>\n<li><a href=\"/blog/2019-11-13-serverless-ssr-now/#serverless-ssr\">Serverless SSR</a></li>\n<li><a href=\"/blog/2019-11-13-serverless-ssr-now/#%E7%A8%B3%E5%AE%9A%E6%80%A7\">稳定性</a></li>\n</ul>"},"previousBlog":{"id":"8fb3dfc3-b43e-5c5d-82d2-b4c0809761e3","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020114/1578992366948-16e6e6a4c7f54ffb.png","authors":["腾讯 IVWEB 团队"],"categories":["guides-and-tutorials"],"date":"2019-11-15T00:00:00.000Z","title":"NGW，前端新技术赛场：Serverless SSR 技术内幕","description":"腾讯 IVWeb team（负责腾讯互动视频 NOW 直播、花样直播等产品） 经验分享~","authorslink":["https://juejin.im/user/5a9f77666fb9a028c14a01eb"],"translators":null,"translatorslink":null,"tags":["NGW","Serverless SSR"],"keywords":"Serverless SSR,Serverless 新技术","outdated":null},"wordCount":{"words":582,"sentences":109,"paragraphs":109},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-11-15-serverless-ssr.md","fields":{"slug":"/blog/2019-11-15-serverless-ssr/","keywords":["php","serverless","webpack","服务端渲染","同构渲染","无服务器","云函数","函数","直出","Serverless","serverlesscloud"]}},"nextBlog":{"id":"37200d26-7acc-58c7-aeac-89e58bb15f19","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/images/YHl6UWa9s62zicSFHQTm5RAchm5pOdynuj5PhOKpDQ5x70nXbiaR2JImuNRcZmKALEhOAXyxXUIiaQwUWaDoxN3Rg.jpg","authors":["Serverless 中文网"],"categories":["news"],"date":"2019-11-11T00:00:00.000Z","title":"《世界争霸》聊天 API 迁移至 Serverless 过程中踩过的坑和趟平的路","description":"本文整理自董文强在Techo开发者大会「Serverless Summit」专场的演讲。","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["API","云函数"],"keywords":"Serverless, Serverless游戏, Serverless API","outdated":null},"wordCount":{"words":277,"sentences":63,"paragraphs":63},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-11-11-serverless-api-games.md","fields":{"slug":"/blog/2019-11-11-serverless-api-games/","keywords":["serverless","无服务器","云函数","函数","网关","serverless","serverlesscloud"]}},"recommendBlogs":{"edges":[{"node":{"id":"73576d26-e0ce-5f26-9330-64b4f3889157","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/3cb7b20955d78ced738e0279bb3f6f41.jpg","authors":["AndreaPasswater"],"categories":["news","engineering-culture"],"date":"2018-03-09T00:00:00.000Z","title":"Serverless 数据解读：2018 报告","description":"Serverless Framework 使用统计数据：事件源、服务结构、运行时长等等。","authorslink":["https://serverless.com/author/andreapasswater/"],"translators":["Aceyclee"],"translatorslink":["https://www.zhihu.com/people/Aceyclee"],"tags":["事件源","服务结构"],"keywords":"Serverless 统计数据,Serverless 事件源,Serverless 服务结构","outdated":null},"wordCount":{"words":212,"sentences":45,"paragraphs":45},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-03-09-serverless-by-the-numbers-2018-data-report.md","fields":{"slug":"/blog/2018-03-09-serverless-by-the-numbers-2018-data-report/","keywords":["go","serverless","无服务器","云函数","服务","使用率","Go","部署"]}}},{"node":{"id":"84876745-cbfb-5c1d-9f6c-7c74338d5d28","frontmatter":{"thumbnail":"https://s3-us-west-2.amazonaws.com/assets.site.serverless.com/images/champions/champions_banner.jpg","authors":["RupakGanguly"],"categories":["news","engineering-culture"],"date":"2018-04-10T00:00:00.000Z","title":"2018 年首批无服务器社区冠军介绍","description":"无服务器社区冠军是无服务器社区的领导者。快来认识一下我们 2018 年度的社区英雄吧！","authorslink":null,"translators":null,"translatorslink":null,"tags":null,"keywords":null,"outdated":null},"wordCount":{"words":194,"sentences":32,"paragraphs":32},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-04-10-announcing-new-2018-serverless-champions.md","fields":{"slug":"/blog/2018-04-10-announcing-new-2018-serverless-champions/","keywords":["serverless","webpack","无服务器","无服务器架构","服务器","serverless","champions","架构","blog","github","框架"]}}},{"node":{"id":"1e1a90fc-3b65-540c-b726-755999e7912a","frontmatter":{"thumbnail":"https://main.qcloudimg.com/raw/8a0db1c9fd8b51c15d0b006291d52bf5.jpg","authors":["AndreaPasswater"],"categories":["news","engineering-culture"],"date":"2018-07-19T00:00:00.000Z","title":"2018 年社区调查：Serverless 使用率大幅增长","description":"我们曾在开发社区进行问卷调查，询问 Serverless 的使用情况。它的使用率增长连我们自己都惊讶不已，下面来看看数据。","authorslink":["https://serverless.com/author/andreapasswater/"],"translators":["Aceyclee"],"translatorslink":["https://www.zhihu.com/people/Aceyclee"],"tags":["Component","Serverless"],"keywords":"Serverless 社区调查,Serverless 使用情况,Serverless 数据","outdated":null},"wordCount":{"words":267,"sentences":50,"paragraphs":49},"fileAbsolutePath":"/opt/build/repo/content/blog/2018-07-19-2018-serverless-community-survey-huge-growth-usage.md","fields":{"slug":"/blog/2018-07-19-2018-serverless-community-survey-huge-growth-usage/","keywords":["go","serverless","无服务器","Serverless","serverless"]}}},{"node":{"id":"8311b008-2b15-5225-8adc-9b75e484177b","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586880819419-%E5%B0%81%E9%9D%A2%E5%9B%BE%20%286%29.png","authors":["serverless 社区"],"categories":["news"],"date":"2019-04-08T00:00:00.000Z","title":"邀您参加 | K8S&云原生技术开放日-北京站","description":"K8S&云原生技术开放日，将围绕K8S、Serverless等云原生相关技术，从最佳落地实践、不同场景技术改造、应用优化，到开源领域深度技术研究，和技术爱好者们一起探讨。","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["云原生","Serverless"],"keywords":"serverless 入门,serverless 框架,腾讯云 serverless","outdated":null},"wordCount":{"words":36,"sentences":9,"paragraphs":9},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-04-08-k8s.md","fields":{"slug":"/blog/2019-04-08-k8s/","keywords":["serverless","云原生","serverless","Serverless","技术","原生","serverlesscloud","github"]}}},{"node":{"id":"25f709db-cde0-59d7-81f4-944c46a6dd8a","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586880571672-%E5%B0%81%E9%9D%A2%E5%9B%BE%20%285%29.png","authors":["serverless 社区"],"categories":["news"],"date":"2019-04-04T00:00:00.000Z","title":"Serverless 技术风暴来袭，开发者该如何应对？","description":"Hello Serverless技术沙龙北京站，将围绕Serverless的应用场景，客户案例，FaaS+BaaS架构的实现方案等，针对未来的无服务形态进行交流和讨论，释放技术想象！","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["应用场景","Serverless"],"keywords":"serverless 入门,serverless 框架,腾讯云 serverless","outdated":null},"wordCount":{"words":37,"sentences":8,"paragraphs":8},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-04-04-technology-storm.md","fields":{"slug":"/blog/2019-04-04-technology-storm/","keywords":["serverless","Serverless","serverless","serverlesscloud","github","技术","架构","围绕"]}}},{"node":{"id":"4bc4d946-6187-5c3d-91c6-d98c4c4c1314","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020414/1586880172144-%E5%B0%81%E9%9D%A2%E5%9B%BE%20%284%29.png","authors":["serverless 社区"],"categories":["news"],"date":"2019-06-11T00:00:00.000Z","title":"一图读懂无服务器云函数","description":"本图将重要信息进行梳理和提炼，帮助读者更好的理解无服务器云函数的理念和作用","authorslink":["https://zhuanlan.zhihu.com/ServerlessGo"],"translators":null,"translatorslink":null,"tags":["云函数","Serverless"],"keywords":"serverless 入门,serverless 框架,腾讯云 serverless","outdated":null},"wordCount":{"words":31,"sentences":6,"paragraphs":6},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-06-11-one-picture-serverless-cloud-function.md","fields":{"slug":"/blog/2019-06-11-one-picture-serverless-cloud-function/","keywords":["serverless","无服务器","无服务器云函数","云函数","serverless","serverlesscloud","Serverless","github","开发者","函数"]}}},{"node":{"id":"1cd76f95-14a9-5fd5-926b-456af189b7ed","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/2020326/1585217744291-%E6%91%84%E5%9B%BE%E7%BD%91_400730082_wx.jpg","authors":["朱峰 Ben"],"categories":["news"],"date":"2019-10-14T00:00:00.000Z","title":"由浅入深说 Serverless 之云函数的生命周期","description":"希望通过文章分享帮助大家更深入的了解 Serverless 背后的机制并掌握相关的最佳实践。","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["云函数"],"keywords":"Serverless, Serverless前端开发, Serverless云函数","outdated":null},"wordCount":{"words":74,"sentences":29,"paragraphs":29},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-14-serverless-cloud-function.md","fields":{"slug":"/blog/2019-10-14-serverless-cloud-function/","keywords":["faas","java","云函数","函数","触发","实例","事件","数据库","启动","连接","sql","代码"]}}},{"node":{"id":"4f16f8bc-1a35-5394-9029-f7abc2ad6137","frontmatter":{"thumbnail":"https://img.serverlesscloud.cn/qianyi/images/YHl6UWa9s629ucl0iaVcic8rL06uEBM5go4LSXiaHnibDJSBjLGAhGlWz3QZ1RZzd3NeCibjJxOyUApDO7TaPYcwxsw.jpg","authors":["Serverless 中文网"],"categories":["news"],"date":"2019-10-26T00:00:00.000Z","title":"Hello Serverless 实战沙龙回顾","description":"2019 年 10 月 26 日，Hello Serverless 沙龙活动在广州市海珠区腾讯众创空间成功举办，一起看看我们都为大家准备了哪些精彩的演讲吧！","authorslink":["https://github.com/jiangliu5267"],"translators":null,"translatorslink":null,"tags":["Meetup"],"keywords":"Serverless, Serverless技术沙龙,Hello Serverless","outdated":null},"wordCount":{"words":64,"sentences":13,"paragraphs":13},"fileAbsolutePath":"/opt/build/repo/content/blog/2019-10-26-hello-serverless-meetup.md","fields":{"slug":"/blog/2019-10-26-hello-serverless-meetup/","keywords":["go","serverless","云函数","云原生","Serverless","存储","腾讯","serverlesscloud"]}}}],"totalCount":46}},"pageContext":{"isCreatedByStatefulCreatePages":false,"blogId":"82d71850-8dc8-5a6a-8f90-5a66609dae1c","previousBlogId":"8fb3dfc3-b43e-5c5d-82d2-b4c0809761e3","nextBlogId":"37200d26-7acc-58c7-aeac-89e58bb15f19","categories":["news"]}}}