<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>性能优化 on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/</link><description>Recent content in 性能优化 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 17 Jan 2025 04:35:49 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96/index.xml" rel="self" type="application/rss+xml"/><item><title>基于 Vue2 的文件预览解决方案，全部代码由 Cursor AI 助手生成</title><link>https://xiaobox.github.io/p/2025-01-17-ji-yu-vue2-de-wen-jian-yu-lan-jie-jue-fang-an-quan-bu-dai-ma/</link><pubDate>Fri, 17 Jan 2025 04:35:49 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-01-17-ji-yu-vue2-de-wen-jian-yu-lan-jie-jue-fang-an-quan-bu-dai-ma/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-01-17-ji-yu-vue2-de-wen-jian-yu-lan-jie-jue-fang-an-quan-bu-dai-ma/cover.jpg" alt="Featured image of post 基于 Vue2 的文件预览解决方案，全部代码由 Cursor AI 助手生成" /&gt;&lt;h2 id="项目介绍"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;项目介绍
&lt;/h2&gt;&lt;p&gt;这是一个基于 Vue 2 的文件预览解决方案，支持主流办公文件的在线预览，包括 Word、Excel、PPT 和 PDF 文件。本项目采用 Vue 2 技术栈开发，确保了更好的兼容性和稳定性。&lt;/p&gt;
&lt;p&gt;该项目目前已开源：https://github.com/xiaobox/file-preview-demo&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-01-17-ji-yu-vue2-de-wen-jian-yu-lan-jie-jue-fang-an-quan-bu-dai-ma/001-c71411d9.png"&gt;&lt;/p&gt;
&lt;h2 id="项目开发说明"&gt;&lt;a href="#%e9%a1%b9%e7%9b%ae%e5%bc%80%e5%8f%91%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;项目开发说明
&lt;/h2&gt;&lt;p&gt;本项目是一个特殊的实验性项目，完全通过与 Cursor（AI 驱动的智能 IDE）的交互来完成。从项目初始化到最终完成，所有的代码都是通过与 Cursor AI 助手的对话生成的，没有手动编写任何一行代码。这个开发过程展示了 AI 辅助编程的潜力，以及如何利用先进的 AI 工具来加速开发流程。&lt;/p&gt;
&lt;p&gt;主要特点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;零手写代码：所有代码均由 Cursor AI 生成&lt;/li&gt;
&lt;li&gt;完整对话驱动：通过自然语言描述需求，AI 理解并实现功能&lt;/li&gt;
&lt;li&gt;快速迭代：AI 能够根据反馈快速调整和优化代码&lt;/li&gt;
&lt;li&gt;高质量输出：生成的代码遵循最佳实践，包含完整的错误处理和用户体验考虑&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这种开发方式展示了 AI 在软件开发中的应用前景，以及如何利用 AI 工具来提高开发效率。&lt;/p&gt;
&lt;h2 id="功能特性"&gt;&lt;a href="#%e5%8a%9f%e8%83%bd%e7%89%b9%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;功能特性
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;支持文件类型：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Word 文档 (.docx)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Excel 表格 (.xlsx)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PowerPoint 演示文稿 (.pptx)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PDF 文档 (.pdf)&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持本地文件预览和远程 URL 文件预览&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;支持大文件分页加载&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完整的跨平台支持：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;完美适配 PC 端和移动端&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;iOS（iPhone/iPad）和 Android 设备上表现优异&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;响应式设计，自适应不同屏幕尺寸&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;针对移动端优化了触控体验和性能&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;优雅的加载状态和错误处理&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="技术栈"&gt;&lt;a href="#%e6%8a%80%e6%9c%af%e6%a0%88" class="header-anchor"&gt;&lt;/a&gt;技术栈
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;核心框架：Vue 2.7.x（使用 Vue 2 的最新稳定版本）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;路由管理：Vue Router 3.6.x（与 Vue 2 配套的路由版本）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;文件预览：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Word：@vue-office/docx&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Excel：@vue-office/excel&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PPT：@vue-office/pptx&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;PDF：pdfjs-dist（Mozilla PDF.js）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开发工具：&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Vue CLI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Babel&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ESLint&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="技术方案说明"&gt;&lt;a href="#%e6%8a%80%e6%9c%af%e6%96%b9%e6%a1%88%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;技术方案说明
&lt;/h2&gt;&lt;h3 id="pdf-预览方案"&gt;&lt;a href="#pdf-%e9%a2%84%e8%a7%88%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;PDF 预览方案
&lt;/h3&gt;&lt;p&gt;本项目选择使用 Mozilla 的 PDF.js（pdfjs-dist）而不是 @vue-office/pdf 的原因：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;功能完整性：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;PDF.js 是 Mozilla 维护的成熟方案，功能更加完整&lt;/li&gt;
&lt;li&gt;支持文档大纲、缩放、搜索等高级功能&lt;/li&gt;
&lt;li&gt;支持表单填写和注释&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;性能优势：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;支持分页按需加载，减少内存占用&lt;/li&gt;
&lt;li&gt;渲染性能更好，适合大型 PDF 文件&lt;/li&gt;
&lt;li&gt;支持流式加载，无需等待整个文件下载完成&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;兼容性：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;浏览器兼容性更好&lt;/li&gt;
&lt;li&gt;支持更多 PDF 特性和格式&lt;/li&gt;
&lt;li&gt;渲染效果更接近原生&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;社区支持：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;有庞大的社区支持&lt;/li&gt;
&lt;li&gt;问题修复和更新及时&lt;/li&gt;
&lt;li&gt;文档完善，示例丰富&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="9"&gt;
&lt;li&gt;实践经验：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;在测试中发现，使用 @vue-office/pdf 预览大型 PDF 文件时，在移动端存在严重问题&lt;/li&gt;
&lt;li&gt;具体表现为：在文件未完全加载完成时，页面会自动多次重新加载&lt;/li&gt;
&lt;li&gt;这可能是由移动设备内存限制或操作系统的内存管理机制导致&lt;/li&gt;
&lt;li&gt;而使用 PDF.js 的分页渲染机制可以很好地解决这个问题&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="渲染实现说明"&gt;&lt;a href="#%e6%b8%b2%e6%9f%93%e5%ae%9e%e7%8e%b0%e8%af%b4%e6%98%8e" class="header-anchor"&gt;&lt;/a&gt;渲染实现说明
&lt;/h3&gt;&lt;p&gt;PDF.js 的渲染流程：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;核心渲染过程：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;pdfDoc&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getPage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;viewport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getViewport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;scale&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;document&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="n"&gt;pdf&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;-$&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;pageNumber&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;getContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2d&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;height&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;canvas&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewport&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;renderContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;canvasContext&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;viewport&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;viewport&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;await&lt;/span&gt; &lt;span class="n"&gt;page&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;renderContext&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;promise&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;技术细节：&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;直接将 PDF 内容渲染到 Canvas，而不是转换成图片再加载&lt;/li&gt;
&lt;li&gt;每个页面使用独立的 Canvas 元素&lt;/li&gt;
&lt;li&gt;采用分页渲染机制，提升渲染性能&lt;/li&gt;
&lt;li&gt;支持缩放、旋转等视图操作&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="4"&gt;
&lt;li&gt;性能优化：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;实现分页渲染，避免一次性渲染所有页面&lt;/li&gt;
&lt;li&gt;使用 Canvas 直接渲染，减少内存占用&lt;/li&gt;
&lt;li&gt;支持流式加载，提升首屏加载速度&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="6"&gt;
&lt;li&gt;移动端适配：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;针对移动端内存限制进行优化&lt;/li&gt;
&lt;li&gt;避免了整个文件内容同时加载到内存的问题&lt;/li&gt;
&lt;li&gt;解决了大型 PDF 在移动端反复重载的问题&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="旧版-office-格式支持方案"&gt;&lt;a href="#%e6%97%a7%e7%89%88-office-%e6%a0%bc%e5%bc%8f%e6%94%af%e6%8c%81%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;旧版 Office 格式支持方案
&lt;/h3&gt;&lt;p&gt;如果需要支持旧版 Office 格式（.doc、.xls、.ppt），建议采用以下方案：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务器转换方案：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用 LibreOffice/OpenOffice 搭建文件转换服务&lt;/li&gt;
&lt;li&gt;将旧版格式转换为新版格式后再预览&lt;/li&gt;
&lt;li&gt;优点：可控性强，无需第三方服务&lt;/li&gt;
&lt;li&gt;缺点：需要自行维护服务器&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;商业转换服务：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;使用 Microsoft Office 365 API&lt;/li&gt;
&lt;li&gt;使用金山 WPS 开放平台&lt;/li&gt;
&lt;li&gt;优点：稳定性好，维护成本低&lt;/li&gt;
&lt;li&gt;缺点：需要付费，依赖第三方服务&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;客户端转换：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;提示用户使用 Office 或 WPS 另存为新版格式&lt;/li&gt;
&lt;li&gt;优点：实现简单，无需额外服务&lt;/li&gt;
&lt;li&gt;缺点：用户体验不够友好&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="最后"&gt;&lt;a href="#%e6%9c%80%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;最后
&lt;/h2&gt;&lt;h3 id="谈谈感受"&gt;&lt;a href="#%e8%b0%88%e8%b0%88%e6%84%9f%e5%8f%97" class="header-anchor"&gt;&lt;/a&gt;谈谈感受
&lt;/h3&gt;&lt;p&gt;最近团队在开发文件预览的功能，本来觉得是一个比较常规和普遍的功能就没多过问，后来前端同学反馈问题比较多，比如移动端的兼容问题、新旧 office 格式文件的兼容问题、pdf 大文件预览问题等等。于是参与想了几个方案，本文介绍的项目是一个&lt;strong&gt;纯前端&lt;/strong&gt; 解决方案。整体来看占用的资源比较小，是一个性价比很高的方案。当然，这是在解决了刚才提到的那些问题的前提下。&lt;/p&gt;
&lt;p&gt;这个项目从技术难度上并没有多高，但从&lt;strong&gt;过程和形式上&lt;/strong&gt;却很有意思。&lt;/p&gt;
&lt;p&gt;因为&lt;strong&gt;这是又一个我用 Cursor 在不手写任何一行代码的基础上完成的项目。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我本来预计半天的时间就能搞定，没想到整整搞了一天。&lt;/p&gt;
&lt;p&gt;无论是在技术社区还是在社交媒体，经常看到有人说：&lt;strong&gt;自己不会写代码，用 Cursor 在自己不写一行代码的情况下，用很短的时间完成了一个 app 或者一个项目。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;其实这样的项目，我已经写了好几个了，但区别是，我是一名具有 15 年研发经验的工程师。&lt;/p&gt;
&lt;p&gt;从我的角度来说，我觉得：&lt;strong&gt;在自己完全不会写代码的情况下，用 Cursor 完成一个项目是可能的，但可能性不大。除非这个项目非常简单。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;有过开发经验的朋友一定知道，一个项目在整个研发周期内多多少少会遇到一些问题，这些问题和挑战需要程序员们来解决，这些问题有大有小。我想表达的是，无论多少，你一定会遇到问题，而且很大概率是&lt;strong&gt;棘手的、不好解决的、针对你当前项目本身特定上下文的问题。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那么问题来了，遇到这种问题，你如果完全不懂开发，不会写代码，仅凭着使用 AI 工具开发项目的热情和很可能出现幻觉的模型是很可能搞不定的。即使能搞定也会相当浪费时间和资源。生产率极其低！&lt;/p&gt;
&lt;p&gt;这是我在使用 Cursor 开发了几个项目后的结论，我不是说 Cursor 不好，相反，我觉得它很好，但工具是需要配备给适合使用它的人，无脑的宣传它的万能是不对的。&lt;/p&gt;
&lt;p&gt;而对于我来讲，利用 Cursor 解决问题、开发项目，在理想的情况下真的是分分钟的事儿。别忘了，我有 15 年的研发经验啊。拥有这样的经验和基础知识再加上 Cursor 确实如虎添翼。&lt;/p&gt;
&lt;p&gt;现在，任何语言，任何技术栈在我面前都不是问题，计算机世界的大门从来没有像今天一样对我这样敞开。我的学习效率和解决问题的效率有了极大的提高，说 10 倍可能夸张，至少有 3-5 倍。&lt;/p&gt;
&lt;p&gt;所以，我认为 Cursor 这类优秀的 AI 编程工具就像一个你的结对编程的伙伴，你们是协作关系，他像一名高级工程师，你们互相引导、激发、创造！&lt;/p&gt;
&lt;p&gt;你想想什么人才能结对编程，一个什么都不懂的人和你结对你觉得靠谱吗？ 我想你明白我想表达的是什么了。&lt;/p&gt;
&lt;p&gt;最近在社交媒体看到知名博主 “宝玉” 总结的 AI 编程工具的使用原则，结合我自己的使用经验我觉得总结得很靠谱，分享给大家：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;准确的描述清楚需求&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;架构能力，合理的将复杂系统拆分成松耦合的模块，让 AI 可以在一次会话中处理&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;专业编程能力，能分辨 AI 生成的代码的好坏&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;调试能力，当 AI 生成的代码出现问题，能快速定位，自己或者借助 AI&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</description></item><item><title>打造互联网未来：边缘计算引领新趋势</title><link>https://xiaobox.github.io/p/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/</link><pubDate>Sat, 10 Aug 2024 05:18:45 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/cover.jpg" alt="Featured image of post 打造互联网未来：边缘计算引领新趋势" /&gt;&lt;p&gt;在科技飞速发展的今天，互联网正逐步迈向一个新的时代。在这个时代，边缘计算成为了一个不可忽视的重要趋势。它不仅能够提高网站和应用的性能，还能为全球用户提供更快、更安全的体验。本文将探讨边缘计算如何改变我们对互联网的理解，以及它对开发者和用户的深远影响。&lt;/p&gt;
&lt;h2 id="边缘计算的基本概念"&gt;&lt;a href="#%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e7%9a%84%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5" class="header-anchor"&gt;&lt;/a&gt;边缘计算的基本概念
&lt;/h2&gt;&lt;p&gt;首先，我们需要了解什么是边缘计算。简单来说，边缘计算指的是将计算和数据存储从传统的集中式数据中心转移到更靠近用户的地理位置。这意味着网站或应用程序不再只托管在单一服务器上，而是同时存在于全球多个服务器上。当用户请求访问网站或应用时，系统会将其指引到离他们最近的服务器上。这种方式不仅能减少延迟，还能提供更快的响应时间。&lt;/p&gt;
&lt;h3 id="边缘计算的技术优势"&gt;&lt;a href="#%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e7%9a%84%e6%8a%80%e6%9c%af%e4%bc%98%e5%8a%bf" class="header-anchor"&gt;&lt;/a&gt;边缘计算的技术优势
&lt;/h3&gt;&lt;p&gt;在传统的集中式服务器模型中，所有请求都需要经过中心服务器处理，这可能导致延迟增加，特别是对于距离服务器较远的用户。而通过边缘计算，服务器可以更靠近用户所在的地理位置，大大减少了数据传输的距离和时间。这种优化的物理布局能够显著提升页面加载速度，从而减少用户流失。&lt;/p&gt;
&lt;p&gt;根据谷歌的研究显示，当页面加载时间从 1 秒增加到 3 秒时，用户流失率增加了 32%；而当加载时间达到 5 秒时，流失率则增加到 90%。在边缘计算的帮助下，许多网站能够保持较低的延迟，提升用户体验。&lt;/p&gt;
&lt;h2 id="解决全球性能问题"&gt;&lt;a href="#%e8%a7%a3%e5%86%b3%e5%85%a8%e7%90%83%e6%80%a7%e8%83%bd%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;解决全球性能问题
&lt;/h2&gt;&lt;p&gt;举个例子，如果你在美国北弗吉尼亚的 AWS 数据中心托管一个应用程序，虽然这对美国东海岸的用户来说响应迅速，但对于其他地区的用户来说，响应时间可能就没那么理想了。对于位于德国法兰克福的用户，请求可能需要 339.95 毫秒才能得到响应；而对于新加坡和悉尼的用户，这一时间可能高达 944.14 毫秒和 889.85 毫秒。&lt;/p&gt;
&lt;p&gt;通过使用边缘计算，如 Deno Deploy，服务器可以更靠近用户。以 Deno 为例，除新加坡外，全球大部分地区的响应时间都在 100 毫秒以内。这是因为系统会自动选择离用户最近的边缘服务器，确保最快的响应时间。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/001-aafe74b6.png"&gt;&lt;/p&gt;
&lt;h2 id="边缘计算的历史演变"&gt;&lt;a href="#%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e7%9a%84%e5%8e%86%e5%8f%b2%e6%bc%94%e5%8f%98" class="header-anchor"&gt;&lt;/a&gt;边缘计算的历史演变
&lt;/h2&gt;&lt;p&gt;边缘计算并不是凭空产生的，它是互联网技术不断演进的结果。1969 年的 RFC 提出了服务器的概念，这为后来的网络发展奠定了基础。随着互联网的普及和网站流量的激增，CDN（内容分发网络）应运而生。Akamai 在 1998 年推出的首个 CDN，解决了“热点”问题，即服务器因流量过大而崩溃的问题。CDN 通过在全球范围内缓存静态内容，将用户请求引导至最近的服务器，提高了响应速度。&lt;/p&gt;
&lt;p&gt;而无服务器架构的出现，则为开发者提供了一种更加灵活和高效的方式来部署应用程序。AWS Lambda 是首个广泛使用的无服务器框架，通过事件驱动的方式，服务器仅在有请求时 才会启动，减少了资源的浪费。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/002-3bb2c285.png"&gt;&lt;/p&gt;
&lt;h2 id="边缘计算的优越性"&gt;&lt;a href="#%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e7%9a%84%e4%bc%98%e8%b6%8a%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;边缘计算的优越性
&lt;/h2&gt;&lt;p&gt;边缘计算结合了 CDN 的地理优势和无服务器架构的动态优势。通过在用户附近执行自定义代码，边缘计算带来了以下几大好处：&lt;/p&gt;
&lt;h3 id="提升性能"&gt;&lt;a href="#%e6%8f%90%e5%8d%87%e6%80%a7%e8%83%bd" class="header-anchor"&gt;&lt;/a&gt;提升性能
&lt;/h3&gt;&lt;p&gt;边缘计算的最大优势在于性能的提升。网站和应用程序在接近用户的边缘服务器上运行，响应速度更快。此外，计算在边缘服务器上进行，而不是在用户的浏览器中，这使得应用程序对用户设备的资源占用更少，减少了 CPU 和内存的使用，并降低了浏览器崩溃的风险。同时，发送给用户的数据量更小，带宽使用更少，确保了函数和 API 的一致性行为。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-10-da-zao-hu-lian-wang-wei-lai-bian-yuan-ji-suan-yin-ling-xin-q/003-552a4c23.png"&gt;&lt;/p&gt;
&lt;h3 id="增强安全性"&gt;&lt;a href="#%e5%a2%9e%e5%bc%ba%e5%ae%89%e5%85%a8%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;增强安全性
&lt;/h3&gt;&lt;p&gt;将计算从客户端转移到边缘服务器也减少了应用程序受到攻击的风险。正如 Deno 的 DX 工程负责人 Kitson Kelly 所说，“这意味着你立即减少了暴露给终端用户的攻击面。”在边缘服务器上执行计算，减少了与后端服务的 API 调用，从而提升了安全性。此外，边缘计算还能有效抵御 DDoS 攻击，因为攻击者需要同时攻击全球数十甚至数百个服务器才能奏效。&lt;/p&gt;
&lt;h3 id="改善开发者体验"&gt;&lt;a href="#%e6%94%b9%e5%96%84%e5%bc%80%e5%8f%91%e8%80%85%e4%bd%93%e9%aa%8c" class="header-anchor"&gt;&lt;/a&gt;改善开发者体验
&lt;/h3&gt;&lt;p&gt;尽管目前为边缘开发代码较为复杂，但这主要是因为边缘开发的混合特性。许多框架尚未完全支持边缘优先的开发模式，这迫使开发者在服务器端渲染和浏览器渲染之间进行选择。然而，新兴框架如 Fresh，则通过默认不向客户端发送 JavaScript，简化了边缘优化过程。开发者使用 Fresh 和 Deno Deploy 的全球分布式 JavaScript 无服务器边缘网络，能够实现如 Lighthouse 评分满分的性能优化。&lt;/p&gt;
&lt;h2 id="结论"&gt;&lt;a href="#%e7%bb%93%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;结论
&lt;/h2&gt;&lt;p&gt;边缘计算代表了互联网发展的下一阶段。从 IBM 360/91 到 Berners-Lee 的 NeXT 机器，再到 Akamai 的 CDN 和亚马逊的数据中心，无服务器架构以及如今的边缘计算。每一个阶段都是在前一阶段的基础上发展而来，汲取经验，修正错误。边缘计算将使互联网成为一个更快、更安全的场所，为用户和开发者带来更好的体验。&lt;/p&gt;
&lt;p&gt;希望本文能帮助大家更好地理解边缘计算的价值及其对未来互联网的影响。如果你有任何想法或疑问，欢迎在评论中与我们交流！&lt;/p&gt;</description></item><item><title>微服务架构：稳定性设计</title><link>https://xiaobox.github.io/p/2020-03-01-wei-fu-wu-jia-gou-wen-ding-xing-she-ji/</link><pubDate>Sun, 01 Mar 2020 11:18:22 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-03-01-wei-fu-wu-jia-gou-wen-ding-xing-she-ji/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-01-wei-fu-wu-jia-gou-wen-ding-xing-she-ji/cover.jpg" alt="Featured image of post 微服务架构：稳定性设计" /&gt;&lt;h3 id="服务分级与容量规划"&gt;&lt;a href="#%e6%9c%8d%e5%8a%a1%e5%88%86%e7%ba%a7%e4%b8%8e%e5%ae%b9%e9%87%8f%e8%a7%84%e5%88%92" class="header-anchor"&gt;&lt;/a&gt;服务分级与容量规划
&lt;/h3&gt;&lt;pre&gt;&lt;code&gt; 通过依赖的管理，我们能够知道，当前系统调用了哪些服务，被哪些服务调用。接下来，我们便可以根据当前系统所依赖的服务，以及系统的流程，判断依赖的服务是否影响应用的流程，以此来决定当前应用依赖的优先级。

 对于服务提供者来说，需要清楚了解当前的服务到底被多少人调用，并建立应用白名单机制，服务调用需要事先申请，以便将调用方增加到白名单当中进行管理和容量规划。为保障系统稳定，对于未知的调用者，最好的方式便是直接拒绝，以免给系统带来不确定风险。如果没有事先的容量规划，当未知的调用者流量突增，很可能将系统拖垮。

 **服务提供者也需要对服务消费者的优先级进行区分，哪些调用将影响核心链路，哪些调用是非核心链路。****当系统压力过大，无法承载的时候，必须 优先确保重要等级高的应用，核心的调用链路优先确保畅通，而对于重要性不那么高的应用，则可以暂时丢车保帅。**
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="优雅降级"&gt;&lt;a href="#%e4%bc%98%e9%9b%85%e9%99%8d%e7%ba%a7" class="header-anchor"&gt;&lt;/a&gt;优雅降级
&lt;/h3&gt;&lt;pre&gt;&lt;code&gt; 当依赖的服务出现不稳定，响应缓慢或者调用超时，或者依赖系统宕机，当前的系统需要能够及时感知到并进行相应处理，否则，大量超时的调用，有可能将当前系统的线程和可用连接数用完，导致新的请求进不来，服务僵死，这便是故障传递。如果处理不及时**，故障的传递可将一个非核心链路的问题扩大，引起核心节点故障，最终形成多米诺骨牌效应，使得整个集群都不能对外提供服务。**

 这样，服务调用优雅降级的重要性便体现出来了。对于调用超时的非核心服务，可以设定一个阀值，如果调用超时的次数超过这个阀值，便自动将该服务降级。此时，服务调用者跳过对该服务的调用，并指定一个休眠的时间点，当时间点过了以后，再次对该服务进行重试，如果服务恢复，则取消降级，否则，继续保持该服务的降级状态，直到所依赖的服务故障恢复。这样，便可以一定程度上避免故障传递的现象发生。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="开关"&gt;&lt;a href="#%e5%bc%80%e5%85%b3" class="header-anchor"&gt;&lt;/a&gt;开关
&lt;/h3&gt;&lt;pre&gt;&lt;code&gt; 当系统负载较高，即将突破警戒水位的时候，如何通过实时地屏蔽一些非核心链路的调用，降低系统的负载呢？这个时候，需要系统预先定义一些开关，来控制程序的服务提供策略。开关通过修改一些预先定义好的全局变量，来控制系统的关键路径和逻辑，比如，可以定义一个是否允许某一个级别的应用调用当前服务的开关，当系统处于流量高峰期的时候，将非核心链路的调用屏蔽，等高峰期过去之后，再将相应的开关打开。

 当然 ，同一个应用，可能也会对外提供多个服务，如果服务耗费系统资源较多，且又不影响系统核心链路，这时，也可以将一些非核心的服务关闭掉，以减轻系统的负担，有效的提高系统对核心应用的服务能力。
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id="建立服务监控统计报表"&gt;&lt;a href="#%e5%bb%ba%e7%ab%8b%e6%9c%8d%e5%8a%a1%e7%9b%91%e6%8e%a7%e7%bb%9f%e8%ae%a1%e6%8a%a5%e8%a1%a8" class="header-anchor"&gt;&lt;/a&gt;建立服务监控、统计、报表
&lt;/h3&gt;&lt;pre&gt;&lt;code&gt; 服务运行期间，需要对服务器相应指标进行监控，如系统load、磁盘利用率、内存占用率、网络流量、QPS/TPS 等。

 对于服务的调用，需要有统计的报表，按照小时/日/周/月 展示 ，并且通过设置异常情况监控，如流量突增突降，系统要能够及时报警。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;应用预案&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 紧急情况（如双十一、双十二、热点事件等）并不是时时刻刻都发生，大部分人在第一次面对突发事件时，难免会显得手足无措。因此，要想在系统出现故障的情况下能够处变不惊，沉着应对，将损失降到最低，首先得准备一份应急预案，并且，得进行经常性的故障演练，以熟悉各种情况下对应的应急预案的操作流程和规范，避免紧急情况下错误的决策致使损失扩大，并且在实际操作中也能够积累经验。

 **应急预案中需要明确的规定服务的级别，梳理清楚核心应用的调用链路，对于每一种故障，都做出合理的假设和有针对性的处理方法，对于级别低的调用和功能，事先准备好屏蔽的开关和接口。**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;场景和问题&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; 以下问题供大家思考
&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;场景一：假设某服务最大承受10 000TPS，如果超出是排队等待还是保证10 000TPS范围内请求正常，其他请求直接拒绝？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;场景二：非核心服务流量洪峰导致核心服务受到影响，如何解决？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;场景三：某服务有几百个消费者，如果其中一个消费者要求升级，满足一个临时活动，是否要求所有服务同步升级？&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-03-01-wei-fu-wu-jia-gou-wen-ding-xing-she-ji/001-bf98d7fd.jpg"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>库存服务优化思路</title><link>https://xiaobox.github.io/p/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/</link><pubDate>Fri, 28 Feb 2020 14:12:54 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/cover.jpg" alt="Featured image of post 库存服务优化思路" /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/001-0505c464.jpg"&gt;&lt;/p&gt;
&lt;p&gt;很多业务系统构架中都有库存服务&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;库存可以以数量为单元，假如有5台手机，那么库存就是 5 ，卖掉一台，库存就减 1 变成 4 。这种以数量为单位进行的库存计算相对比较简单，在架构设计中无论你优化DB中的SQL，还是存储结构，或者引入Redis做缓存都比较好设计和实现。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;有些库存不是以数量为单位的，比如酒店房间，这是可以重复利用的，是以天为单位的，行话叫“间夜”，理论上如果某间房间一年365天都可用，那么一年就有365个库存，一天就一个。A客人1月1号占了，B客人1月1号就不能占。当然，还有钟点房，时间粒度更细，这个先不讨论，后面会涉及。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;还有些业务的库存是以更细粒度为单位的，比如小时。租车业务就是以小时为单位进行库存占用的，假设我们只有一辆车，A客户8:00-10:00租用，B客户可以 11:00-15:00租用。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;以下的库存优化思路是针对租车这种细粒度的占用单位的&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;笔者几年前参与过租车业务中库存系统的改造升级，当时的库存服务主要的问题有两个&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;没有形成独立的服务，与其他服务耦合在一个“全家桶”式的系统中&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;与库存相关的业务提供的RPC服务响应较慢，而又由于库存是核心服务，所以库存一慢，各相关子系统都会拖慢&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对于第一个问题，相对比较好解决，我们将库存服务独立出来，包括数据库。&lt;strong&gt;问题在于第二个&lt;/strong&gt;，其实慢的原因也很明显，就是对于库存的计算完全依赖数据库，利用SQL与java代码进行计算，当然重头戏在DB，当时采用的数据库是SQL Server，幸亏是SQL Server，如果是My SQL的话可能性能撑不到我们对它进行改造的时候。随着数据量的增加，整个库存服务在完全依赖DB的情况下，服务的查询速度较慢，是秒的量级。&lt;/p&gt;
&lt;p&gt;当时的改造先进行了一轮谨慎的尝试，即对业务进行整体重新梳理、对现存SQL，java代码进行全面优化。优化的结果并不理想，虽然这个过程让业务更清晰，也发现了些遗留问题和BUG，但是并没有解决慢的根本问题。&lt;/p&gt;
&lt;p&gt;在第一轮的基础上想过用缓存进行处理的方案，但由于单位粒度太细，缓存的设计方案复杂又不好落地，并没有采用。在QPS和TPS趋于平稳的情况下，库存系统的问题并没有被逐渐放大，反而似乎没有到大家忍受不了的地步。于是就不了了之了。&lt;/p&gt;
&lt;p&gt;而实际上，这件事真的就结束了吗？我想不一定&lt;/p&gt;
&lt;p&gt;由于当时公司的系统越来越多，并且旧系统也会升级改造，随着一些业务的新增或升级，如果有突发的流量，比如做个营销活动，系统能不能撑得住？即使撑得住，用户体验会不会好？我不知道，但我知道如果一个核心服务慢了，整体的体验都不会好。&lt;/p&gt;
&lt;p&gt;其实对于我个人来讲，那次的改造不彻底也是我的一个心结，首先当时没有明确的标准和目标，没有特别清晰的量化下来，比如库存服务优化到300-500ms。其实当时我很清楚它慢，就是没有很好的解决了那个问题，心有不甘。&lt;/p&gt;
&lt;p&gt;于是今天想起来，提出一个方案，看能不能解决它！&lt;/p&gt;
&lt;p&gt;整个库存服务的重点就是如何准确的计算出来它的值，如果算的不对，可能导致有车租不出去（算少了），或没车租出去了（算多了）。而计算这个值的公式特别简单：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; **可预定车辆数 = 现有运营车辆数 - 被占用车辆数**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1 我们将现有运营车辆数以 门店_车型_amount 为 key ，车辆数为 value 存在Redis中。 当现有运营车辆数变化时，在修改DB的同时同步Redis，当然无论是DB还是Redis都是要做高可用架构设计的，以防止单点故障的发生。&lt;/p&gt;
&lt;p&gt;2 被占用车辆原先是以数据库表的方式存储的记录，之前由于时间粒度太细，所以用SQL计算，所以慢，这里我们&lt;strong&gt;采用贪心算法的思路&lt;/strong&gt;，具体是这样的：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;首先按门店+车型 一对一的将占车记录取出来。如A门店C1车型，A门店C2车型。将每组每条占车记录的开始和结束时间转为Unix时间戳，最小单位是秒，如：1546315932。

再将开始和结束时间组成一个闭区间如[1546315932,1546316000]，再把每组的这些闭区间组成一个二维数组。int[][]intervals。
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例如：&lt;/p&gt;
&lt;p&gt;[[1546315932,1546316000],[1546315942,1546317000],[1546315932,1546315943],[1546315901,1546315907]]&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;最后每一个门店+车型都对应一个占车二维数组。

将这些数组以 门店\_车型\_occupy 为 key ，二维数组为 value 序列化到redis中

**当要计算某门店某车型的库存时，先将上面的以门店\_车型\_occupy为key的值取出，然后利用贪心算法计算出时间段重合的占用数量，再用门店\_车型\_amount为key的总量减去占用数量就得到了可用库存数。如果时间段不重合就更新Redis二维数组添加新的元素。Redis在操作期间是需要LOCK的。**
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;**贪心算法
**&lt;/p&gt;
&lt;p&gt;贪心算法，特别适合解决 Interval Scheduling（区间调度问题），**比如算出多个区间中最多有几个互不相交的区间；**&lt;strong&gt;或给定一个区间的集合，找到需要移除区间的最小数量，使剩余区间互不重叠。&lt;strong&gt;如果我们请求库存的参数中的时间与现有占车记录的有重叠，那么返回的&lt;/strong&gt;需要移除区间的最小数量就是 &amp;gt;=1 的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;代码实现如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-cs" data-lang="cs"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;eraseOverlapIntervals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[][]&lt;/span&gt; &lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;Arrays&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Comparator&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&amp;gt;(){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;compare&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;o1&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]-&lt;/span&gt;&lt;span class="n"&gt;o2&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;++)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;end&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;end&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;++;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;intervals&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;length&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="n"&gt;cnt&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/002-80380443.gif"&gt;&lt;/p&gt;
&lt;p&gt;由于贪心算法是线性时间复杂度，又是在内存中计算，所以效率会比数据库高很多。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;这里你可能会关心Reids存储的数据量，由于车辆的开放预定周期不会太久，比如半年或一年，那么分到每个门店和车型其实预定的人并不那么多，即使在未来的预定周期内所有车都被预定了数据量也不很大。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于过期的数据，比如一个月以前或一周以前的不再参与库存计算的Redis数据，用定时任务清掉就可以了，或者也可以再行持久化备份一份。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这里讲的是单车型的，如果要算多车型的，就起多线程并行计算。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上就是利用了缓存和贪心算法进行的缓存优化，在此基础上还应该注意：不要对原服务（依赖数据库版本）进行删除，可做为系统服务降级时使用。Redis也要做好高可用和数据库降级处理，如果Redis挂了，还可以用DB顶一下，如果数据没有了，就将整个服务降级到老版本。&lt;/p&gt;
&lt;p&gt;需要注意的是，上面的方案我并没有在生产环境实施过，只是一种单纯的设计，如有不严谨和不对的地方，欢迎指正&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/003-35076111.png"&gt;。如你需要解决相似的问题，请谨慎参考！&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/004-80504576.png"&gt;&lt;/p&gt;
&lt;p&gt;End&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/005-67882503.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-02-28-ku-cun-fu-wu-you-hua-si-lu/006-57ddc455.jpg"&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;关注公众号 获取更多精彩内容&lt;/strong&gt;&lt;/p&gt;</description></item></channel></rss>