<?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/%E5%BE%AE%E6%9C%8D%E5%8A%A1/</link><description>Recent content in 微服务 on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 12 Apr 2026 08:38:12 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/%E5%BE%AE%E6%9C%8D%E5%8A%A1/index.xml" rel="self" type="application/rss+xml"/><item><title>我用两句中文，让 Claude Code 帮我画了10张出版级技术图</title><link>https://xiaobox.github.io/p/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/</link><pubDate>Sun, 12 Apr 2026 08:38:12 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/cover.jpg" alt="Featured image of post 我用两句中文，让 Claude Code 帮我画了10张出版级技术图" /&gt;&lt;p&gt;前两天在写一篇技术文章，写到一半需要配一张微服务架构图。我打开了某在线画图工具，对着空白画布发了十分钟的呆，拖了两个方块，连了一条线，觉得丑，删掉，再拖两个方块。&lt;/p&gt;
&lt;p&gt;半小时过去了，图还没画完，文章的灵感已经凉透了。&lt;/p&gt;
&lt;p&gt;我相信很多搞技术内容的朋友都有过这种体验。你脑子里其实很清楚这张图应该长什么样，但你就是得花一两个小时在画图工具里对齐、配色、调字号。明明内容才是核心，结果时间全花在了排版上。&lt;/p&gt;
&lt;p&gt;然后我发现了一个东西，彻底解决了这个问题。&lt;/p&gt;
&lt;p&gt;它叫 fireworks-tech-graph，是一个 Claude Code 的 skill。装上之后，你跟 Claude Code 说一句中文，它就能给你吐出一张出版级别的技术图。SVG 矢量源文件加 1920px 高清 PNG，直接能往文章里塞。&lt;/p&gt;
&lt;p&gt;我用它画了10张不同类型的图，从架构图到 ER 图到状态机，从白底极简到暗色霓虹到工程蓝图。每张图从下指令到拿到成品 PNG，平均不超过30秒。&lt;/p&gt;
&lt;p&gt;30秒。&lt;/p&gt;
&lt;p&gt;我之前在画图工具里对齐一个箭头的时间都不止30秒。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;怎么装呢，你甚至不需要记任何命令。&lt;/p&gt;
&lt;p&gt;打开 Claude Code，直接跟它说「帮我安装 fireworks-tech-graph 这个 skill」，它自己就把活干了。装完之后你说「画一个 xxx 图」，它就自动触发。&lt;/p&gt;
&lt;p&gt;如果你喜欢手动装也行，就一句 claude skills install fireworks-tech-graph，完事。&lt;/p&gt;
&lt;p&gt;触发词非常宽泛，「画图」「帮我画」「做个架构图」「生成一个流程图」「可视化一下」，随便怎么说都行，它都能识别。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;它能画什么？&lt;/p&gt;
&lt;p&gt;这个 skill 支持10种有模板的图表类型，外加4种无模板但有规则定义的类型。我挑几个最实用的说一下。&lt;/p&gt;
&lt;p&gt;1，架构图。这是用得最多的，画微服务分层、系统组件关系。你告诉它有哪些服务、怎么分层、哪些组件之间有调用关系，它自动帮你排好。我画的那张微服务架构图有5层，右侧还挂了一个观测性旁路，出来的效果跟正经架构文档里的图一模一样。&lt;/p&gt;
&lt;p&gt;2，流程图。CI/CD 流水线、审批流、业务决策流。菱形判断节点、圆角矩形处理步骤、失败回环，全都有。你只需要描述「从提交代码到部署生产」中间经过哪些步骤和判断就行。&lt;/p&gt;
&lt;p&gt;3，时序图。微服务之间谁先调谁，消息怎么传递。标准的 UML 时序图，有生命线、激活框、alt 分组框。你列出参与者和消息序列，它帮你排好。&lt;/p&gt;
&lt;p&gt;4，ER 图。数据库表之间的关系。支持鸦脚记法，PK 自动下划线，FK 标注。你把实体和属性列出来，告诉它哪些是一对多、哪些是多对多，它画出来的东西可以直接放进数据库设计文档。&lt;/p&gt;
&lt;p&gt;5，状态机。订单生命周期、工单状态流转这种。每个状态是一个圆角矩形，转换线上标事件名，有初始态的实心圆和终态的同心圆。&lt;/p&gt;
&lt;p&gt;6，对比矩阵。横评几个模型、几个方案的时候特别好使。我画了一张 LLM 模型对比表，5个模型7个维度，绿色打勾红色打叉，交替行填充，出来就是一张可以直接发朋友圈的表。&lt;/p&gt;
&lt;p&gt;7，时间线。项目路线图、版本规划。甘特图样式，彩色横条加菱形里程碑。&lt;/p&gt;
&lt;p&gt;除了这些，还有 Agent 架构图、用例图、数据流图。反正你在技术写作里能用到的图，它基本都覆盖了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;比较骚的是它有7种视觉风格，每种味道完全不一样。&lt;/p&gt;
&lt;p&gt;默认的 Flat Icon 是白底彩色，适合博客和文档。Dark Terminal 是暗色霓虹风，发 GitHub 和技术社区特别帅。Blueprint 是工程蓝图风，深蓝色背景加网格线加角标，那种 CAD 图纸的感觉。Notion Clean 是极简白，一根线一个色。Glassmorphism 是毛玻璃卡片，适合产品官网和 Keynote。最近还加了 Claude Official 和 OpenAI Official 两种风格，分别是 Anthropic 和 OpenAI 的品牌调性。&lt;/p&gt;
&lt;p&gt;你指定风格的方式就是在 prompt 里加一句「用蓝图风」或者「Style 3」，就这么简单。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;我觉得这个 skill 最打动我的点，不是它画得多漂亮，而是它把「画图」这件事的心理门槛降到了零。以前我写文章需要配图的时候，经常会想「算了这里用文字描述一下也行吧」，因为打开画图工具、画完、导出、插入这一套流程太重了。现在不一样了，我在 Claude Code 里写着文章，写到需要配图的地方，直接说一句「帮我画一个 xxx」，30秒后图就在本地了。&lt;/p&gt;
&lt;p&gt;这种体验就像是，你本来在用文本编辑器写代码，突然有人给你装了一个实时预览插件。功能上没变，但那个「随时能看到效果」的即时反馈感，会让你更愿意去做这件事。&lt;/p&gt;
&lt;p&gt;画图也是一样。当成本足够低的时候，你会发现你开始「想画就画」了。&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;想试的朋友，打开 Claude Code，说一句「帮我安装 fireworks-tech-graph」，等它装完，再说一句「画一个 xxx 图」。&lt;/p&gt;
&lt;p&gt;就这么简单。两句话的事。&lt;/p&gt;
&lt;p&gt;下面附一些 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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/001-a45422ed.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/002-ae30d407.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/003-08fbb303.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/004-4787f3ce.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/005-fc59df47.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/006-5f1611fe.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/007-6052529a.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/008-475c5a2d.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/009-f8934911.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/2026-04-12-wo-yong-liang-ju-zhong-wen-rang-claude-code-bang-wo-hua-le-1/010-2706d92e.png"&gt;&lt;/p&gt;</description></item><item><title>Java老兵的十字路口：坚守还是突围？</title><link>https://xiaobox.github.io/p/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/</link><pubDate>Sat, 29 Mar 2025 05:33:55 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/cover.jpg" alt="Featured image of post Java老兵的十字路口：坚守还是突围？" /&gt;&lt;h2 id="java在技术江湖中的现状稳固基石还是夕阳西下"&gt;&lt;a href="#java%e5%9c%a8%e6%8a%80%e6%9c%af%e6%b1%9f%e6%b9%96%e4%b8%ad%e7%9a%84%e7%8e%b0%e7%8a%b6%e7%a8%b3%e5%9b%ba%e5%9f%ba%e7%9f%b3%e8%bf%98%e6%98%af%e5%a4%95%e9%98%b3%e8%a5%bf%e4%b8%8b" class="header-anchor"&gt;&lt;/a&gt;Java在技术江湖中的现状：稳固基石还是夕阳西下？
&lt;/h2&gt;&lt;p&gt;Java 作为企业级开发的常青树，至今在大量核心系统中扮演着中流砥柱的角色。然而，资深 Java 开发者也明显感受到技术环境的变化。一方面，在银行、政府、互联网巨头等复杂业务场景中，Java 的地位依然稳固；大量遗留系统和核心业务仍运行在 Java 上，短期内很难被完全替换。以优酷的版权管理系统为例，这套长达10年的老系统采用了过时的技术框架，积累了 81万行 Java 代码和大量“没人敢动”的if-else逻辑，可谓技术债累累。像这样的遗留系统，全面重构风险极高，只能在业务推动下逐步演进。因此，在许多传统领域，Java 作为“老码农”的看家本领，仍是不可或缺的基石。&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/001-40dd5948.png"&gt;&lt;/p&gt;
&lt;p&gt;另一方面，新兴业务和初创项目对技术栈的选择更加多元。近年Go语言等后起之秀在性能、并发和开发效率上表现出色，成为云原生时代的“宠儿”。不少互联网大厂开始在核心服务中引入 Go：例如谷歌、滴滴、Uber、腾讯等都用 Go 开发高并发、高性能的服务。业界一度流传着“Java 老旧笨重、Go 崭新酷炫”的声音。面对这种冲击，不少资深 Java 工程师难免焦虑：Java 会不会像当年的 COBOL 一样淡出主流舞台？&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/002-28c85ce1.png"&gt;&lt;/p&gt;
&lt;p&gt;事实证明，这种担忧有些过度。Java 拥有数百万开发者和完整生态，并非轻易就能被取代的工具。正如有分析指出，Go 的崛起为行业提供了新选择，但并不是对 Java 的简单替代；两种语言各有优势，未来将长期共存。换言之，Java 依旧是技术江湖里的定海神针，只是江湖规矩变了——老兵们需要适应新玩法。&lt;/p&gt;
&lt;h2 id="java-面临的核心挑战笨重背后的突围"&gt;&lt;a href="#java-%e9%9d%a2%e4%b8%b4%e7%9a%84%e6%a0%b8%e5%bf%83%e6%8c%91%e6%88%98%e7%ac%a8%e9%87%8d%e8%83%8c%e5%90%8e%e7%9a%84%e7%aa%81%e5%9b%b4" class="header-anchor"&gt;&lt;/a&gt;Java 面临的核心挑战：笨重背后的突围
&lt;/h2&gt;&lt;p&gt;资深 Java 开发者在项目实践中遇到的挑战，往往并非语言本身跑不动，而是架构转型带来的“不适感”。近年来微服务、云原生风潮兴起，Java 传统的开发模式在新环境下面临诸多掣肘。&lt;/p&gt;
&lt;h3 id="微服务部署压力"&gt;&lt;a href="#%e5%be%ae%e6%9c%8d%e5%8a%a1%e9%83%a8%e7%bd%b2%e5%8e%8b%e5%8a%9b" class="header-anchor"&gt;&lt;/a&gt;微服务部署压力
&lt;/h3&gt;&lt;p&gt;将单体应用拆成数十上百的微服务后，每个服务都需要独立部署运行。Java 应用启动慢、内存占用高的问题被放大。在同样功能下，一个简单的 Go 容器镜像可能只有十几MB，而等价的 Java（如采用 Helidon 框架）镜像初始体积高达 1.4GB！即使使用模块化手段缩减JDK体积（JLink可降至150MB左右），Java应用的容器仍显得臃肿。如此高的基础开销让追求极致弹性的微服务架构如临大敌：部署50个 Java 微服务可能需要远超预期的硬件资源，这正是很多团队转向更轻量语言的原因之一。有开发者调侃：“即便Java性能再好，内存占用是别人的 2～10倍，成本账算下来也让人犹豫。”而且在无服务器（Serverless）场景下，Java 冷启动延迟更是令人头疼——函数实例首次启动可能耗时数秒到十几秒。这一问题长期无法回避，直到 AWS 推出 SnapStart 等黑科技，把 Java 函数冷启动时间缩短到毫秒级，才算勉强止血。但需要注意，这类优化是云厂商额外提供的特殊支持，侧面说明 Java 在边缘计算、FaaS 等领域的先天劣势依然存在。&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/003-c6bc61f8.png"&gt;&lt;/p&gt;
&lt;h3 id="启动速度与样板代码"&gt;&lt;a href="#%e5%90%af%e5%8a%a8%e9%80%9f%e5%ba%a6%e4%b8%8e%e6%a0%b7%e6%9d%bf%e4%bb%a3%e7%a0%81" class="header-anchor"&gt;&lt;/a&gt;启动速度与样板代码
&lt;/h3&gt;&lt;p&gt;“万事开头难”在 Java 世界格外贴切。传统 Java Web 应用往往需要预热 JVM、加载大量类和配置，启动一个服务动辄数十秒甚至几分钟。这在强调弹性伸缩的云环境下难以接受。此外，Java 以模板代码多著称，同样的业务逻辑，用 Java 写可能需要冗长的类定义和 Getter/Setter，而用 Go/Kotlin 等语言则简洁得多。例如，Go 以极简的语法实现高并发，让开发者专注于业务；相比之下，过去的 Java 代码显得繁琐累赘，不少老兵自己也调侃天天在写“体力活”。虽然 Java 近年通过 Lambda、Streams、Records 等特性在不断精简，但是遗留项目中的大量样板代码依旧是维护负担。缺乏某些现代语言特性（如模式匹配、代数数据类型）也使Java在表达某些逻辑时不够优雅。这些痛点促使部分团队尝试用 Kotlin 等 JVM 语言替换 Java，以期获得更简洁的语法和更少的冗余。&lt;/p&gt;
&lt;h3 id="并发与性能困境"&gt;&lt;a href="#%e5%b9%b6%e5%8f%91%e4%b8%8e%e6%80%a7%e8%83%bd%e5%9b%b0%e5%a2%83" class="header-anchor"&gt;&lt;/a&gt;并发与性能困境
&lt;/h3&gt;&lt;p&gt;高并发一直是 Java 的强项，但实现方式却日益受到挑战。传统 Java 使用操作系统线程实现并发，每个线程都对应一定的内存和调度开销，在大规模场景下显得“沉重”。为绕过这个瓶颈，过去几年兴起了基于 Reactive 异步编程的微服务框架，通过单线程事件循环避免线程阻塞。然而异步风格代码复杂度高、调试困难，让许多工程师“叫苦不迭”。好消息是，JDK 19/20 引入了虚拟线程（Project Loom）并在 JDK 21 成为正式特性。虚拟线程是由 JVM 管理的超轻量线程，实现了 Go 协程式的并发模型。这意味着开发者可以用以往同步阻塞的简单代码，实现过去需要复杂回调/响应式才能处理的高并发任务。正如 Java 架构师 Brian Goetz 所言：Loom 的出现有望“一举终结Reactive编程的必要”——因为过去Reactive是为解决线程不足的权宜之计。虚拟线程让我们能够创建成千上万个并发任务而不必担心线程耗尽，大幅降低了编写和维护高并发代码的心智负担。然而，新事物也伴随不确定性：老项目若迁移到虚拟线程模型，需要评估线程本地变量、同步机制等是否还能正常工作；调优方式也与传统线程有所不同。目前虚拟线程虽强大，但毕竟是新特性，在大型生产环境中的考验还不充分。Java 老兵们既期待它带来性能飞跃，也需要保持一份观望和谨慎。&lt;/p&gt;
&lt;h3 id="graalvm-与原生执行"&gt;&lt;a href="#graalvm-%e4%b8%8e%e5%8e%9f%e7%94%9f%e6%89%a7%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;GraalVM 与原生执行
&lt;/h3&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/004-3147eb4e.png"&gt;&lt;/p&gt;
&lt;p&gt;为了解决启动慢、内存高的问题，Java 社区近年另一“大招”是 GraalVM 原生镜像。通过提前编译(AOT)，可以将 Java 应用直接打包成本地可执行文件，启动时间和内存占用都有数量级的优化。这项技术被视为让 Java 重返边缘计算和 Serverless 舞台的希望：原生镜像下，一个 Spring Boot 微服务的“Hello World”容器镜像可小到 ~几十MB；运行时不需要JVM，冷启动延迟大幅降低。实际测试中，采用 GraalVM 原生镜像的 Java 服务在某些基准下性能甚至超越 Go：平均延迟仅0.25毫秒，每秒处理事务达到82426次，吞吐率是 Go 实现的两倍多！这种结果令人振奋，仿佛看到了 Java 打了一场漂亮的翻身仗。&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/005-846f3af1.png"&gt;&lt;/p&gt;
&lt;p&gt;然而，理想很丰满，现实有时比较骨感。将复杂应用迁移到 GraalVM 原生镜像并非易事。例如，反射、动态代理等机制需要额外配置支持，许多成熟库在AOT编译下可能行为异常。构建原生镜像的过程也比较繁琐，往往需要调整代码、引入特定插件，并忍受较长的编译时间。调试诊断也更具挑战——原生应用无法使用 JVM 的丰富调试工具，需要新的手段排查问题。此外，引入 GraalVM 还意味着团队需要掌握一套新的知识体系。在的总结中作者就指出：“将 Spring Boot 应用打包为 Native 镜像并非没有挑战”，直接迁移复杂项目可能遇到种种坑，需要开发者充分评估和测试。因此，GraalVM 不是万能灵药，而是有门槛的新武器：用得好，Java 如虎添翼；用不好，反而可能引入新的不稳定因素。&lt;/p&gt;
&lt;p&gt;综上，资深 Java 开发者面对的不是“Java 不行了”，而是如何让 Java 行得更轻、更快、更优雅。JVM 社区显然没有躺在功劳簿上吃老本，而是在微服务、云原生时代积极求变。从 Spring Boot 3 对原生镜像的支持，到 JDK 连续的功能升级（如Records、Pattern Matching等），Java 正在努力破除“笨重”的刻板印象。老兵们需要做的，是与时俱进地拥抱这些变化，用新工具、新思路来武装自己的Java技能库。&lt;/p&gt;
&lt;h2 id="用舍之道哪些场景放弃-java哪些场景坚持-java"&gt;&lt;a href="#%e7%94%a8%e8%88%8d%e4%b9%8b%e9%81%93%e5%93%aa%e4%ba%9b%e5%9c%ba%e6%99%af%e6%94%be%e5%bc%83-java%e5%93%aa%e4%ba%9b%e5%9c%ba%e6%99%af%e5%9d%9a%e6%8c%81-java" class="header-anchor"&gt;&lt;/a&gt;用舍之道：哪些场景放弃 Java，哪些场景坚持 Java？
&lt;/h2&gt;&lt;p&gt;技术选型从来都不是非黑即白，对于 Java 的去留更是如此。究竟在哪些业务场景下应该考虑放弃 Java？哪些场景下 Java 仍是首选？结合真实案例和趋势，我们可以做出以下判断：&lt;/p&gt;
&lt;h3 id="场景一边缘计算与serverless--谨慎使用-java"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%b8%80%e8%be%b9%e7%bc%98%e8%ae%a1%e7%ae%97%e4%b8%8eserverless--%e8%b0%a8%e6%85%8e%e4%bd%bf%e7%94%a8-java" class="header-anchor"&gt;&lt;/a&gt;场景一：边缘计算与Serverless – 谨慎使用 Java。
&lt;/h3&gt;&lt;p&gt;对于运行环境受限、对启动延迟敏感的场景，选择 Java 需要非常慎重。典型如物联网设备、边缘网关、函数计算等，这些环境往往内存有限且要求冷启动极快。在这些场合，运行一个庞大的 JVM 显然不如直接使用原生语言（C/C++、Rust）或轻量脚本语言（JavaScript、Python）来得高效。过去不少团队在实现事件驱动的小型服务时，就倾向于用 Node.js 或 Python 编写——不是因为Java不能实现功能，而是因为Java在每次调用都要“热身”的开销让人难以忍受。虽然有 GraalVM Native Image 可以大幅优化，但对小型团队而言，引入它的复杂度可能得不偿失。因此，对于边缘和无服务函数等应用，除非有充足理由和相应优化手段，否则倾向于选择更轻便的技术栈。当然，规则也非绝对：如果业务逻辑需要调用大量现有的 Java 类库（例如进行某种算法运算，而相关库只有Java实现），那么即便在 Serverless 环境下通过原生镜像等方式使用 Java 也是可以考虑的。总的来说，在这些场景，“能不用Java就不用”是较为实际的指导原则。&lt;/p&gt;
&lt;h3 id="场景二高性能微服务--视情况取舍"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%ba%8c%e9%ab%98%e6%80%a7%e8%83%bd%e5%be%ae%e6%9c%8d%e5%8a%a1--%e8%a7%86%e6%83%85%e5%86%b5%e5%8f%96%e8%88%8d" class="header-anchor"&gt;&lt;/a&gt;场景二：高性能微服务 – 视情况取舍
&lt;/h3&gt;&lt;p&gt;在互联网分布式系统中，每种语言都有用武之地。如果团队主要目标是极致的性能和资源利用率，并且成员对 Go/Rust 等语言驾轻就熟，那么把部分微服务用新语言实现未尝不可。例如某些网关服务、实时通信服务，行业里确有用 Rust 或 Go 重写后延迟降低、内存减半的成功案例。特别是低延迟、高并发的基础设施组件（消息队列、代理服务器等），很多开源项目早就避开 Java 转投 Go/Rust 怀抱，这是技术基因所致（Java 更擅长业务逻辑，系统编程领域C系语言传统更强）。但是，对于业务逻辑复杂的微服务，Java 仍然具有难以替代的优势：强大的生态提供了各种中间件客户端、成熟的 ORM 和事务框架、安全完备的验证和监控工具等等。这些“全家桶”式的支持使Java在开发业务系统时如鱼得水，大大减少了造轮子的成本。如果纯粹为了追新把此类服务改用另一种语言，可能会发现需要重建许多Java自带的轮子，得不偿失。因此，我们的立场是：对性能极限有追求的核心组件，可以考虑非Java实现以挖掘潜力；但大多数微服务尤其是业务导向的微服务，Java 依然是稳妥且高效的选择。况且随着Quarkus、Micronaut等专为云环境优化的Java框架出现，以及JVM自身的持续优化，Java 微服务的“笨重”正在被削平。正如某次测试所示，在较大的机器上，Java 的吞吐甚至可与 Go 持平甚至略胜一筹——只要用对了方式，Java 完全能胜任高性能微服务。&lt;/p&gt;
&lt;h3 id="场景三大型核心系统--坚定拥抱-java"&gt;&lt;a href="#%e5%9c%ba%e6%99%af%e4%b8%89%e5%a4%a7%e5%9e%8b%e6%a0%b8%e5%bf%83%e7%b3%bb%e7%bb%9f--%e5%9d%9a%e5%ae%9a%e6%8b%a5%e6%8a%b1-java" class="header-anchor"&gt;&lt;/a&gt;场景三：大型核心系统 – 坚定拥抱 Java
&lt;/h3&gt;&lt;p&gt;对于那些 复杂度高、生命周期长、需要强一致性和可靠性的核心业务，Java 无疑仍是值得长期信赖的编程语言。例如银行的核心账务系统、航空公司的订票系统、阿里的电商交易中台等，这些系统往往经历多年演化，业务规则繁多且严谨，需要大量业内验证过的中间件支撑。Java 的严格类型体系和成熟框架在这里如鱼得水。特别是在金融、政府等对稳定性要求极高的领域，“Java + 大型商用中间件”的组合几乎是默认标配。从技术债的角度考虑，这类系统虽然也面临老旧架构的问题，但重构时通常还是在 Java 体系内升级（比如从 Struts 升级到 Spring Boot，或引入分布式事务框架等），而不会轻易迁移到一门全新的语言上。这不仅因为重写成本高，更因为 Java 多年沉淀的安全性和可靠性难以替代。可以说，在复杂业务长跑中，Java 是一匹稳健的“长途马”，跑得也许不算最快，但足够稳当，生态中现成的工具能够覆盖方方面面，让架构师和开发者更安心。基于这些原因，我们坚定认为：在复杂业务和核心系统场景下，坚持使用 Java 是明智之举。即便引入新的技术插件，也是作为补充而非颠覆，比如用 Python 做小部分AI预测，再把结果喂给Java主系统等等。Java 老兵在这些战场上大可发挥深厚经验，将系统设计得健壮且易于维护，为业务保驾护航。&lt;/p&gt;
&lt;p&gt;归纳来说，用舍有道，视需而定：Java 并非万能，同样也远未过时。关键在于根据项目需求选择最合适的工具。在前沿领域不妨多尝试新语言新架构，以保持竞争力；而在关系到企业命脉的长线工程上，Java 依然值得我们托付。&lt;/p&gt;
&lt;h2 id="java老兵的自我进化坚守阵地or华丽转型"&gt;&lt;a href="#java%e8%80%81%e5%85%b5%e7%9a%84%e8%87%aa%e6%88%91%e8%bf%9b%e5%8c%96%e5%9d%9a%e5%ae%88%e9%98%b5%e5%9c%b0or%e5%8d%8e%e4%b8%bd%e8%bd%ac%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Java老兵的自我进化：坚守阵地or华丽转型？
&lt;/h2&gt;&lt;p&gt;面对风起云涌的技术浪潮，10年以上经验的 Java 老将们该何去何从？是固守舒适圈，还是勇敢拓展边界？以下几点建议或许对处在十字路口的你有所启发：&lt;/p&gt;
&lt;h3 id="拥抱java新特性跟上生态演进"&gt;&lt;a href="#%e6%8b%a5%e6%8a%b1java%e6%96%b0%e7%89%b9%e6%80%a7%e8%b7%9f%e4%b8%8a%e7%94%9f%e6%80%81%e6%bc%94%e8%bf%9b" class="header-anchor"&gt;&lt;/a&gt;拥抱Java新特性，跟上生态演进
&lt;/h3&gt;&lt;p&gt;不要认为“学了十几年Java就没有新东西可学”。相反，Java生态在飞速更新，每年两个版本迭代。老兵们应该主动学习 JDK近几版的新功能（如Records、Sealed Class、Pattern Matching、虚拟线程等），这些特性能显著改善代码质量和性能，使你的技能焕发新生。例如，试着用 Loom 虚拟线程改造一个老的并发模块，体会一下开发模式的简化；或者研究 GraalVM 如何将现有服务无缝打包为原生镜像，了解其中的限制和调优手段。拥抱新技术不仅能提升生产力，也向团队展示了你与时俱进的技术热情。作为Java老兵，切忌固步自封——持续学习是对抗职业倦怠和时代冲击的最好武器。&lt;/p&gt;
&lt;h3 id="拓展多元技能栈成为t型人才"&gt;&lt;a href="#%e6%8b%93%e5%b1%95%e5%a4%9a%e5%85%83%e6%8a%80%e8%83%bd%e6%a0%88%e6%88%90%e4%b8%bat%e5%9e%8b%e4%ba%ba%e6%89%8d" class="header-anchor"&gt;&lt;/a&gt;拓展多元技能栈，成为“T型”人才
&lt;/h3&gt;&lt;p&gt;在保持Java优势的同时，建议横向拓展一到两门其它语言或领域技能。比如，可以尝试学习 Go 或 Python，用它们做些小项目，体会不同语言在思维模型上的差异。再比如，深入了解一下前端技术或移动开发，哪怕不做前端，也能与前端同事更高效协作。这种“T”字型的技能结构（既有一门精深的主力技术，又对相关技术有所涉猎）将使你在团队中更具价值。很多架构师在成为架构师前，都曾是精通数门语言、熟悉多种数据库和中间件的全能型工程师。对Java老兵来说，学习一门新语言还能帮助跳出现有思维框架，把新理念反哺到Java日常开发中。例如，借鉴函数式编程思想优化Java代码，或者用脚本语言编写自动化工具提升开发效率。多元化的技能还为你提供了职业备胎：万一某天真的不想写Java了，你在其他领域的积累也足以支撑转型，不至于手足无措。&lt;/p&gt;
&lt;h3 id="深入业务和架构提升不可替代性"&gt;&lt;a href="#%e6%b7%b1%e5%85%a5%e4%b8%9a%e5%8a%a1%e5%92%8c%e6%9e%b6%e6%9e%84%e6%8f%90%e5%8d%87%e4%b8%8d%e5%8f%af%e6%9b%bf%e4%bb%a3%e6%80%a7" class="header-anchor"&gt;&lt;/a&gt;深入业务和架构，提升不可替代性
&lt;/h3&gt;&lt;p&gt;随着工作年限增长，“懂业务、能设计”往往比单纯的编码能力更重要。Java老兵应该充分利用在一个行业浸润多年的经验，去深入理解业务领域 的本质问题，把握业务发展方向。将业务洞察与技术方案相结合，主动参与系统架构设计和重大技术选型，这会让你成为团队中不可或缺的核心人物。很多时候，业务专家+技术专家的复合型人才，比仅仅精通某种语法的程序员更有竞争力。如果你已经是某核心系统的Owner，不妨尝试推进架构优化和性能提升项目，展示自己在宏观层面的掌控力。同时，培养自己的系统设计能力，多研究业界大型系统的架构案例，学习它们如何权衡取舍。当你能从容驾驭分布式事务、异地多活、CQRS 等架构模式时，你的价值早已超越“Java 工程师”的范畴，而成为真正的技术专家。这种升级，无论未来Java的热度如何，都能让你的职业生涯保持上升。&lt;/p&gt;
&lt;h3 id="考虑转型技术管理或其他新领域"&gt;&lt;a href="#%e8%80%83%e8%99%91%e8%bd%ac%e5%9e%8b%e6%8a%80%e6%9c%af%e7%ae%a1%e7%90%86%e6%88%96%e5%85%b6%e4%bb%96%e6%96%b0%e9%a2%86%e5%9f%9f" class="header-anchor"&gt;&lt;/a&gt;考虑转型技术管理或其他新领域
&lt;/h3&gt;&lt;p&gt;并非每个人都要永远写代码。工作十年以上后，你也可以根据兴趣转型，选择最适合自己的道路。如果你热衷带团队和项目把控，可以逐步走向 技术管理 岗位，担任Team Leader、技术经理甚至CTO，把多年经验用于培养新人和决策把关。很多Java老兵在这一阶段选择带领团队，既可传承自己的开发哲学，又能获得管理成就感。又或者，你对某些新兴领域情有独钟，例如 人工智能、大数据、安全 等，不妨利用业余时间学习相关知识，寻求内部调岗或外部机会。资深程序员转做产品经理、解决方案架构师的例子也屡见不鲜——只要有心，完全可以跳出演员阵容，转到幕后编剧或导演的位置上。当然，做出转型决定前需要评估清楚：你的核心竞争力是什么，新领域是否真心喜欢，从头开始是否有心理准备。转型不是逃避，而是为了更长远的发展。无论选择深耕Java栈还是开拓新跑道，持续的学习和热情都是关键驱动力。&lt;/p&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;p&gt;想对每一位焦虑中的Java老兵说：技术江湖瞬息万变，但真正的资深工程师价值从不局限于某种语法。Java 之父 James Gosling 曾打比方说：“Java就像一辆可靠的卡车”，或许它没有跑车那样光鲜，但能载着重载货物稳稳前行。这辆卡车如今也在不断改装升级，动力和油耗都在改进。我们作为司机，要做的不是弃车而逃，而是练就更高超的驾驶技巧，并且学会在不同道路上换合适的交通工具。坚守初心并不代表故步自封，拥抱变化也不意味着全盘否定过去。当我们既掌握了Java这门老牌利器，又勇于学习新招式、新套路，在变化的浪潮中依然能找到自己的方向和节奏。&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-03-29-java-lao-bing-de-shi-zi-lu-kou-jian-shou-hai-shi-tu-wei/006-0c6fa2fa.png"&gt;&lt;/p&gt;
&lt;p&gt;10+年开发生涯沉淀下来的经验与智慧，是宝贵的财富。无论Java的流行曲线如何波动，真正优秀的工程师都会不断进化，拓展自己的边界。在这个过程中，我们既要有克制冷静的思考，看清技术演进的本质；也要保持对编程的热爱，不忘初心地享受技术创造的乐趣。愿每一位Java老兵都能在时代洪流中找到属于自己的位置：该出手时果断出手，该坚守时稳如磐石，在新的十年里续写属于你的传奇。&lt;/p&gt;</description></item><item><title>云原生，你真的懂了吗？</title><link>https://xiaobox.github.io/p/2025-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/</link><pubDate>Sat, 22 Feb 2025 04:00:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/cover.jpg" alt="Featured image of post 云原生，你真的懂了吗？" /&gt;&lt;h2 id="啥是云"&gt;&lt;a href="#%e5%95%a5%e6%98%af%e4%ba%91" class="header-anchor"&gt;&lt;/a&gt;啥是云？
&lt;/h2&gt;&lt;p&gt;说起云原生（cloud native），就不得不说明一下 “云” 是啥。&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-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/001-79f74f23.png"&gt;&lt;/p&gt;
&lt;p&gt;简单理解，云就是 “云计算”，如果给它下个定义的话是这样的：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;云&amp;quot; 特指以云计算模型构建的现代化动态环境&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;为什么叫 &lt;strong&gt;云&lt;/strong&gt; 呢？它怎么不叫其他的什么呢？&lt;/p&gt;
&lt;p&gt;这个吧，就像你问马云为什么叫马云一样，那是因为给他起名的家长就是这么定的。对，云计算也是如此，计算机领域的专家就是这么定的。后来成了专业术语，大家都这么说。那你说叫 “云” 有没有道理呢？ 有道理。&lt;/p&gt;
&lt;p&gt;我们类比地看，如果你将 “资源” 看成水的话，那么在天空上自由自在、飘来飘去的云就是资源的载体，在你有需要的时候，“呼风唤雨🌧️” ，一朵带着雨水（资源）的积雨云就能为你带来一场甘霖（你需要的资源）。&lt;/p&gt;
&lt;p&gt;云在天上，不在地下，不像以前（过去的架构），你想吃水了得自己挖井，现在你可以 “呼风唤雨” 叫云给你水。而且很好管理和维护。所以你看，用 “云” 这个字还是有道理的。&lt;/p&gt;
&lt;h3 id="云-可以有多种形态"&gt;&lt;a href="#%e4%ba%91-%e5%8f%af%e4%bb%a5%e6%9c%89%e5%a4%9a%e7%a7%8d%e5%bd%a2%e6%80%81" class="header-anchor"&gt;&lt;/a&gt;“云” 可以有多种形态
&lt;/h3&gt;&lt;p&gt;云还有多种形态，一般来说有以下三种：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;公有云（Public Cloud）： 由第三方云计算提供商（如 AWS、Azure、Google Cloud、阿里云、腾讯云等）拥有和运营，面向公众提供服务。&lt;/li&gt;
&lt;li&gt;私有云（Private Cloud）： 由企业或组织自己拥有和运营，仅供内部使用。私有云可以部署在企业自己的数据中心，也可以托管给第三方提供商。&lt;/li&gt;
&lt;li&gt;混合云（Hybrid Cloud）： 结合了公有云和私有云的优势，允许应用程序和数据在两者之间迁移和共享。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;还是用吃水比喻，公有云就像 “自来水”，打开水管就能喝。私有云就像自己找水源，自己挖井，自己舀水喝。混合云自然就是这两种的混合。&lt;/p&gt;
&lt;h3 id="资源有啥"&gt;&lt;a href="#%e8%b5%84%e6%ba%90%e6%9c%89%e5%95%a5" class="header-anchor"&gt;&lt;/a&gt;资源有啥？
&lt;/h3&gt;&lt;p&gt;说完了云，我们该说水了，云是提供资源的，那么具体有哪些资源呢？什么东西是云可以提供给我们的呢？你可能想到了：“嗨，不就是服务器嘛”。&lt;/p&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;计算资源（CPU/GPU/FPGA）&lt;/li&gt;
&lt;li&gt;存储资源（磁盘 / SSD / 对象存储）&lt;/li&gt;
&lt;li&gt;网络资源（带宽 / IP / 负载均衡）&lt;/li&gt;
&lt;li&gt;内存资源（RAM）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;然后是抽象服务，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;容器编排（Kubernetes（EKS/GKE/ACK）、Nomad）&lt;/li&gt;
&lt;li&gt;数据库服务（AWS RDS、阿里云 RDS）&lt;/li&gt;
&lt;li&gt;消息队列（Kafka 云托管、AWS SQS、RabbitMQ）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;接着是管理与安全，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;身份权限（AWS IAM、阿里云 RAM、Azure AD）&lt;/li&gt;
&lt;li&gt;监控告警（Prometheus 云托管、Datadog、阿里云 ARMS）&lt;/li&gt;
&lt;li&gt;日志分析（ELK Stack 云服务、Splunk）&lt;/li&gt;
&lt;li&gt;安全防御（阿里云 WAF）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最后是高阶能力，比如：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Serverless（AWS Lambda、阿里云 FC）&lt;/li&gt;
&lt;li&gt;服务网格（Istio、Linkerd）&lt;/li&gt;
&lt;li&gt;边缘计算（AWS Wavelength、阿里云 ENS）&lt;/li&gt;
&lt;li&gt;量子计算（AWS Braket、Azure Quantum）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;我上面举的只是一些常见的例子，其实每一个分类下的资源条目是非常多的。所以你看，“资源” 可不止服务器那么简单，它是多维度的。现在几乎你能用的到的东西都上云了，都是资源。就算没有，经不住人家云厂商包装啊，只要你有需求，人家就有产品卖你，哈哈。&lt;/p&gt;
&lt;h2 id="云原生"&gt;&lt;a href="#%e4%ba%91%e5%8e%9f%e7%94%9f" class="header-anchor"&gt;&lt;/a&gt;云原生
&lt;/h2&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-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/002-7897a5f7.png"&gt;&lt;/p&gt;
&lt;p&gt;云其实还是比较好理解的，但是云原生（cloud native）就比较抽象了，在我刚接触这个概念的时候是一头雾水，无论别人给我讲，还是我给别人讲总是说不清楚，或者人家听不懂。&lt;/p&gt;
&lt;p&gt;我们还是先来说说定义吧，我找到 CNCF （https://github.com/cncf/toc/blob/main/DEFINITION.md）的一个定义，算比较权威了。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;云原生技术有利于各组织在公有云、私有云和混合云等新型动态环境中，构建和运行可弹性扩展的应用。云原生的代表技术包括容器、服务网格、微服务、不可变基础设施和声明式 API。&lt;/p&gt;
&lt;p&gt;这些技术能够构建容错性好、易于管理和便于观察的松耦合系统。结合可靠的自动化手段，云原生技术使工程师能够轻松地对系统作出频繁和可预测的重大变更。&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;Cloud native practices empower organizations to develop, build, and deploy workloads in computing environments (public, private, hybrid cloud) to meet their organizational needs at scale in a programmatic and repeatable manner. It is characterized by loosely coupled systems that interoperate in a manner that is secure, resilient, manageable, sustainable, and observable.&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;说实话，如果你没有在这个领域干过，光看定义是不容易理解的。甚至就算在云原生环境下从事开发的工程师，如果不认真思考、理解，也不能把什么是“云原生” 讲的很明白。&lt;/p&gt;
&lt;p&gt;原因是这个概念本来就比较抽象。所以我不用 CNCF 的定义，换一种说法来描述一下什么是 “云原生”&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;云原生是一种以云计算基础设施为基石，通过容器化封装、微服务拆分、声明式 API 和自动化运维，将应用的非功能需求（如弹性扩缩、故障自愈、安全策略）交由云平台管理的架构范式。其本质是通过技术手段让业务代码与底层资源解耦，使开发者只需关注业务逻辑，而由云平台自动处理部署、监控、容灾等复杂性。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;你看，我这样说是不是清晰一些了？ 更好理解一些了？还没有？接着往下看。&lt;/p&gt;
&lt;h3 id="本质"&gt;&lt;a href="#%e6%9c%ac%e8%b4%a8" class="header-anchor"&gt;&lt;/a&gt;本质
&lt;/h3&gt;&lt;p&gt;透过现象看本质，概念怎么说都行，重要的是要理解这个东西的本质。&lt;/p&gt;
&lt;p&gt;那我们就来看一看 “云原生” 的本质 ：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;云原生并非单纯的技术集合，而是一种 “以应用为中心” 的思维模式。它通过标准化技术栈（容器 / K8s / 微服务），将云计算从 “资源层” 提升到 “应用层”，让企业能像使用水电一样按需使用计算能力。它的目标是让技术复杂性对业务透明化。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;说到这里，我们就不得不说一下为什么要使用或推崇“云原生”了。&lt;/p&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;/li&gt;
&lt;li&gt;扩展性差：单体架构难以应对流量突增，扩容需要数小时甚至数天。&lt;/li&gt;
&lt;li&gt;运维成本高：故障恢复、监控、日志收集等运维工作需人工干预。&lt;/li&gt;
&lt;li&gt;环境不一致：开发、测试、生产环境差异导致 “在我机器上能跑” 的问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;采用 “云原生” 技术栈及应用架构可以有效的解决上面的所有问题。&lt;/p&gt;
&lt;h3 id="什么是不可替代的"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%98%af%e4%b8%8d%e5%8f%af%e6%9b%bf%e4%bb%a3%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;什么是不可替代的？
&lt;/h3&gt;&lt;p&gt;不得不说，Docker 和 Kubernetes 技术的出现给了 “云原生” 极大的助力。那如果没有 Docker 和 Kubernetes 呢？ 云原生这个概念还立得住吗？&lt;/p&gt;
&lt;p&gt;其实，云原生概念的出现（2010 年 Netflix 提出）比 Docker（2013）和 Kubernetes（2014）更早。核心思想在容器技术普及前就已萌芽。所以：&lt;strong&gt;云原生本质是一种架构哲学，而非特定技术捆绑&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;那么我们紧接着下一个问题就是：什么是云原生的核心内容，有什么是缺一不可的呢？或者说缺了什么就不能算是云原生了呢？&lt;/p&gt;
&lt;p&gt;总结来说，有四大支柱：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;弹性伸缩（Elasticity）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;必须能力：根据负载自动扩缩资源&lt;/li&gt;
&lt;li&gt;替代方案：AWS Lambda（Serverless）、Nomad（非 K8s 编排器）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="3"&gt;
&lt;li&gt;故障自愈（Resilience）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;必须能力：服务熔断、重试、健康检查&lt;/li&gt;
&lt;li&gt;替代方案：Hystrix（微服务容错库）+ Consul（服务发现）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;声明式管理（Declarative）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;必须能力：通过 YAML/JSON 描述目标状态，而非手动操作&lt;/li&gt;
&lt;li&gt;替代方案：Terraform（基础设施即代码）+ Ansible（配置管理）&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="7"&gt;
&lt;li&gt;自动化交付（Automation）&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;必须能力：CI/CD 流水线、蓝绿部署&lt;/li&gt;
&lt;li&gt;替代方案：GitLab CI + Spinnaker（持续交付平台）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="云原生的-复杂性悖论"&gt;&lt;a href="#%e4%ba%91%e5%8e%9f%e7%94%9f%e7%9a%84-%e5%a4%8d%e6%9d%82%e6%80%a7%e6%82%96%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;云原生的 “复杂性悖论”
&lt;/h3&gt;&lt;p&gt;云原生看起来真好呀，但事实是这样吗？对于软件开发，我们都知道一句著名的话：“没有银弹”，是的。云原生也不是哪儿哪儿都好，它也有它的问题。&lt;/p&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;/li&gt;
&lt;li&gt;云原生的复杂性下沉到基础设施层（如 K8s 集群运维、Istio 配置）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;对企业而言，这相当于用 “可控的工程复杂度” 替代 “不可控的业务阻塞风险”。但关键在于，这种转移是否真正带来净收益。&lt;/p&gt;
&lt;p&gt;说白了，你要会“算账”，在成本一定的情况下，要有所取舍，到底要不要上“云原生”，不是技术本身决定的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;云原生有它的显性与隐性成本&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;技术债务：K8s 版本升级兼容性问题、Helm Chart 维护&lt;/li&gt;
&lt;li&gt;人力成本：需要 DevOps 工程师（平均薪资比开发高 30%+）&lt;/li&gt;
&lt;li&gt;认知负担：团队需掌握容器、服务网格、声明式 API 等新范式&lt;/li&gt;
&lt;li&gt;工具链成本 ：监控（Prometheus+AlertManager）、日志（EFK）、追踪（Jaeger）的集成和维护&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;有成本当然也有收益&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;部署效率：从手动部署 1 小时 → 全自动 CI/CD 5 分钟（效率提升 12 倍）&lt;/li&gt;
&lt;li&gt;资源利用率 ：虚拟机静态分配 → 容器动态调度（CPU 利用率从 15% 提升至 60%+）&lt;/li&gt;
&lt;li&gt;故障恢复速度：人工排查 1 小时 → 自动熔断 + 滚动更新（MTTR 从 60 分钟降至 5 分钟）&lt;/li&gt;
&lt;li&gt;业务敏捷性：新功能上线周期从 1 个月 → 1 周（迭代速度提升 4 倍）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，关键是要根据你的实际情况 “算账”，一般来说，当企业规模超过临界点（通常≥50 个微服务 / 日部署次数≥10 次），云原生的收益将显著超越成本。&lt;/p&gt;
&lt;h3 id="结论"&gt;&lt;a href="#%e7%bb%93%e8%ae%ba" class="header-anchor"&gt;&lt;/a&gt;结论
&lt;/h3&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-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/003-05d14457.png"&gt;&lt;/p&gt;
&lt;p&gt;所以说了半天云原生到底是什么？ 我不知道你明白了没有，我们上面林林总总写了那么多技术点，如果初次接触确实很头大。但这不重要。&lt;/p&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;/li&gt;
&lt;li&gt;云原生交付 ≈ 汽车生产线（标准化零件 + 自动化流水线 + 质量检测体系）&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果你面试的时候，面试官让你谈谈对云原生的理解，我希望你能够把这篇文章的精华吸收了，从一个比较高的层次来谈，&lt;strong&gt;我不希望给你一个中规中矩的回答模板，虽然它是正确的&lt;/strong&gt;，比如以下这样：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“云原生是云计算时代的一种架构范式，旨在通过标准化技术栈（如容器、K8s）和自动化运维体系，将非业务功能（如弹性扩缩、故障自愈）从应用代码中剥离，由云平台接管。其核心驱动力是解决传统单体应用部署慢、环境不一致、扩展难的问题。&lt;/p&gt;
&lt;p&gt;从技术分层看，云原生包含：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础设施层：Docker 实现环境一致性，K8s 完成资源调度，比如我们通过 Deployment 的滚动更新实现零停机发布；&lt;/li&gt;
&lt;li&gt;应用架构层：微服务拆分业务能力，Istio 服务网格处理服务间通信的熔断、监控，例如在订单服务中配置超时自动重试；&lt;/li&gt;
&lt;li&gt;交付运维层：GitOps 工具链（如 Argo CD）确保声明式配置的版本可控，配合 Prometheus+Grafana 实现实时监控。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;关键设计原则包括不可变基础设施（容器镜像一次构建多次运行）、声明式 API（通过 YAML 描述目标状态而非手动操作）。但引入云原生也带来挑战，比如团队需要掌握复杂的 K8s 生态，我们通过建设内部开发者平台（IDP）封装底层细节，让开发者只需关注业务代码。”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="云原生应用"&gt;&lt;a href="#%e4%ba%91%e5%8e%9f%e7%94%9f%e5%ba%94%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;云原生应用
&lt;/h2&gt;&lt;h3 id="什么样的应用是云原生的"&gt;&lt;a href="#%e4%bb%80%e4%b9%88%e6%a0%b7%e7%9a%84%e5%ba%94%e7%94%a8%e6%98%af%e4%ba%91%e5%8e%9f%e7%94%9f%e7%9a%84" class="header-anchor"&gt;&lt;/a&gt;什么样的应用是云原生的？
&lt;/h3&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-02-22-yun-yuan-sheng-ni-zhen-de-dong-le-ma/004-d345c5b7.png"&gt;&lt;/p&gt;
&lt;p&gt;一个应用是否云原生，而要看是否具备以下特征：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;容器化部署：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;应用打包为 Docker 镜像，在 Kubernetes 集群中运行。&lt;/li&gt;
&lt;li&gt;示例：电商大促时，通过 HPA（水平扩缩）自动增加订单处理服务的 Pod 数量。&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;业务拆分为独立服务（如用户服务、支付服务），通过 API 通信。&lt;/li&gt;
&lt;li&gt;示例：视频网站将视频转码服务独立部署，利用 GPU 节点加速，不影响主站稳定性。&lt;/li&gt;
&lt;/ul&gt;
&lt;ol start="5"&gt;
&lt;li&gt;DevOps 流水线：&lt;/li&gt;
&lt;/ol&gt;
&lt;ul&gt;
&lt;li&gt;代码提交后自动触发 CI/CD，完成测试、镜像构建、灰度发布。&lt;/li&gt;
&lt;li&gt;示例：金融 App 通过蓝绿部署实现零停机更新，降低发布风险。&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;使用云托管的数据库（如 AWS RDS）、消息队列（如 Kafka on K8s）、缓存（如 Redis Cluster）。&lt;/li&gt;
&lt;li&gt;示例：社交平台用云原生数据库 TiDB 处理海量关系数据，自动分片扩容。&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;应用设计不绑定特定云厂商，可在 AWS、Azure、私有云间迁移。&lt;/li&gt;
&lt;li&gt;示例：跨国企业采用 Anthos 实现跨公有云和本地数据中心的统一管理。&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;p&gt;云原生不是终点，而是通往智能软件时代的必由之路。当我们将视角从工具本身移开，看到的是一场关于效率、可靠性与创新速度的认知革命。正如 Linux 之父 Linus Torvalds 所说：“技术终将老去，但优秀的架构思想永存。” 在这场变革中，比掌握某个工具更重要的，是建立持续进化的云原生思维体系。&lt;/p&gt;</description></item><item><title>nginx 如何做针对 ip 的限流</title><link>https://xiaobox.github.io/p/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/</link><pubDate>Tue, 23 Jul 2024 14:10:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/cover.jpg" alt="Featured image of post nginx 如何做针对 ip 的限流" /&gt;&lt;h2 id="闲白"&gt;&lt;a href="#%e9%97%b2%e7%99%bd" class="header-anchor"&gt;&lt;/a&gt;闲白
&lt;/h2&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-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/001-c3921c64.jpg"&gt;&lt;/p&gt;
&lt;p&gt;说到限流，大家一定能想到很多算法，比如 &lt;code&gt;令牌桶&lt;/code&gt; 、&lt;code&gt;漏桶&lt;/code&gt; 、&lt;code&gt;计数器限流&lt;/code&gt;、 &lt;code&gt;信号量&lt;/code&gt; 等等。解决方案也有很多，以 java 为例，&lt;code&gt;Guava&lt;/code&gt; 库中的 RateLimiter 类 可以实现，&lt;code&gt;Semaphore&lt;/code&gt; 类也可以实现。再复杂点儿，比如你是一个分布式微服务系统，可以上 &lt;code&gt;Hystrix&lt;/code&gt;、&lt;code&gt;Resilience4j&lt;/code&gt; 这种现成的方案。&lt;/p&gt;
&lt;p&gt;从系统架构上来说，无非是在单体应用的当前进程中实现，还是分布式应用的非当前进程中之实现。当然还有另一种方案，就是不在业务应用中实现，而是把这种跟业务不那么紧耦合的功能抽象出去，&lt;strong&gt;在网络层面对所有进入系统的请求进行统一的限流控制，这种方式的好处是可以避免每个微服务都实现自己的限流逻辑。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;现在很多 API 网关，尤其是新晋的 “云原生” 网关都具备这个功能（基本是标配），比如：Zuul、Kong、Ambassador、APISIX 等。&lt;/p&gt;
&lt;p&gt;我们先不论系统是不是分布式微服务的，就单说限流这个事儿，其实也完全可以用 API 网关的思路来实现。就是我不用非要把代码写在应用中，如果我就是不想改代码呢？我想随时调整个限流策略还得重启应用？应用那么重，生效时间那么长，我可不想重启！&lt;/p&gt;
&lt;p&gt;所以我们回头看看自己架构中的这些软件，一定能想到这位老朋友 &lt;strong&gt;Nginx&lt;/strong&gt; 。当然无论是原味的 Nginx 还是跟它有血缘关系的 &lt;strong&gt;openResty&lt;/strong&gt; 都一样。&lt;/p&gt;
&lt;p&gt;想像一下，用 nginx 配置一下然后 &lt;code&gt;nginx -s reload&lt;/code&gt; 就能搞定了，岂不痛快 ？!&lt;/p&gt;
&lt;h2 id="正题"&gt;&lt;a href="#%e6%ad%a3%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;正题
&lt;/h2&gt;&lt;p&gt;下文我们开始介绍在 nginx 怎么配置能实现针对某些（讨厌的）ip 进行限流，且不影响系统正常运行。（感叹：nginx 是个好东西！！！）&lt;/p&gt;
&lt;p&gt;可能有些朋友看到标题就已经开始写 prompt 了，喝着 coffee 等着 &lt;strong&gt;AI&lt;/strong&gt; 给你一行行输出答案，然后心里想：“什么年代了，大哥，还用写个文章专门说这事儿吗？你得学会用工具呀” 。&lt;/p&gt;
&lt;p&gt;我想说的是，关于这个问题 AI 能给你回答对 90% 的内容，剩下的 10% 你得自己改。开发同学都知道 ，别说 10% 了，0.1% 不对，程序也不 work 呀。我是不会告诉你我花了一下午时间跟 AI 都聊了什么的。因为那显得我很弱智。&lt;/p&gt;
&lt;p&gt;你也别抬杠说我用的工具不对，市面上但凡有的我都用了，真不行，所以我觉得还是值得写一下的。&lt;/p&gt;
&lt;h3 id="配置详解"&gt;&lt;a href="#%e9%85%8d%e7%bd%ae%e8%af%a6%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;配置详解
&lt;/h3&gt;&lt;p&gt;其实改的地方不多，首先我们要在 nginx 默认配置文件的 &lt;code&gt;http&lt;/code&gt; 下面配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; geo $limit_ip {
&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; default 0; # 默认为 0，表示不受限制
&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; 1.2.3.4 1; # 需要被限制的 IP
&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; # 添加更多需要限制的 IP 地址
&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&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; map $limit_ip $limit_key {
&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; 0 &amp;#34;&amp;#34;;
&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; 1 $binary_remote_addr;
&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&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&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; limit_req_zone $limit_key zone=mylimit:10m rate=2r/s;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们解释一下。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;geo 指令：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;geo 名字来源于“geographic”，意指地理位置。但是值得注意的是，geo 指令实际上只基于 IP 地址进行匹配，而 IP 地址与地理位置之间的映射需要额外的数据库或服务来提供。许多第三方服务和数据库（如 MaxMind GeoIP、GeoLite2 等）可以用来更精确地将 IP 地址转换为地理位置信息。&lt;/p&gt;
&lt;p&gt;解释一下我们上文中中 geo 的配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;geo $limit_ip { ... }&lt;/code&gt;：定义了一个名为 &lt;code&gt;$limit_ip&lt;/code&gt;的变量，用于根据客户端 IP 地址设置不同的值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default 0;&lt;/code&gt;：默认情况下，如果客户端 IP 地址不在列表中，&lt;code&gt;$limit_ip&lt;/code&gt; 的值为 0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1.2.3.4 1;&lt;/code&gt;：如果客户端 IP 地址是 1.2.3.4，则 &lt;code&gt;$limit_ip&lt;/code&gt; 的值为 1。这里的 1 是一个标记，表示这个 IP 地址需要被限制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结来说就是用 geo 指令标记需要限制的 IP 地址&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;map 指令：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;map $limit_ip $limit_key { ... }&lt;/code&gt;：根据&lt;code&gt;$limit_ip&lt;/code&gt;的值来设置另一个变量&lt;code&gt;$limit_key&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;quot;&amp;quot;;&lt;/code&gt;：如果&lt;code&gt;$limit_ip&lt;/code&gt;的值为 0（即默认情况），则&lt;code&gt;$limit_key&lt;/code&gt;的值为空字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 $binary_remote_addr;&lt;/code&gt;：如果&lt;code&gt;$limit_ip&lt;/code&gt;的值为 1（即被标记的 IP 地址），则&lt;code&gt;$limit_key&lt;/code&gt;的值为客户端 IP 地址的二进制形式（&lt;code&gt;$binary_remote_addr&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不知道聪明的你看出来没有，&lt;strong&gt;我们这里其实设置的是 “黑名单” （即我想限制哪些 ip 我就配置哪些，剩下的不限制）&lt;/strong&gt;，在 geo 配置的 ip 到了 map 这里以后，将这些 IP 地址映射到了一个变量上，即 limit_key 。&lt;strong&gt;如果你想设置白名单（即我想让哪些 ip 不被限制我就配置哪些，剩下的都限制）不就是反过来操作嘛。&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-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;geo $limit {
&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; default 1;
&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; 10.0.0.0/8 0;
&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; 192.168.0.0/24 0;
&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; 172.20.0.35 0;
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;map $limit $limit_key {
&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; 0 &amp;#34;&amp;#34;;
&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; 1 $binary_remote_addr;
&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="limit_req_zone"&gt;&lt;a href="#limit_req_zone" class="header-anchor"&gt;&lt;/a&gt;limit_req_zone
&lt;/h3&gt;&lt;p&gt;接着是整块配置的最后一行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;limit_req_zone $limit_key zone=mylimit:10m rate=2r/s;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用 limit_req_zone 指令定义了一个限流区域，对标记的 IP 地址进行请求速率限制。如果一个 IP 地址不在 geo 指令中定义，则不受限制。如果一个 IP 地址被标记，则它的请求速率会被限制在每秒 2 个请求。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$limit_key&lt;/code&gt;：使用$limit_key 变量作为限流的键。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zone=mylimit:10m&lt;/code&gt;：设置共享内存区域的大小为 10MB，用于存储限流信息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rate=2r/s&lt;/code&gt;：设置每个键值（即每个 IP 地址）的请求速率限制为每秒 2 个请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实这些指令都有一些详细参数，简单起见，我就不介绍了，都有 AI 了，需要的话自己查吧。我们说点儿重点。&lt;/p&gt;
&lt;p&gt;我猜你可能关心 &lt;code&gt;zone=mylimit&lt;/code&gt; 里面到底是什么样的，里面到底有啥 。是的，这很重要，了解清楚 zone 的结构很关键，关于 zone 的数据我没细看过，但结构大致类似这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&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; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;mylimit&amp;#34;&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="nt"&gt;&amp;#34;123.124.210.242&amp;#34;&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; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&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="nt"&gt;&amp;#34;last&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1618305483&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&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="nt"&gt;&amp;#34;tokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 当前令牌桶中的令牌数
&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="nt"&gt;&amp;#34;delay&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&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="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="c1"&gt;// ... 其他被限流的 IP 地址信息
&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="nt"&gt;&amp;#34;192.168.1.100&amp;#34;&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;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&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="nt"&gt;&amp;#34;last&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1618305495&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="nt"&gt;&amp;#34;tokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&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;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delay&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&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="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="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;好了，到这里我们第一部分的配置就结束了，是不很简单？然后我们进行第二部分的配置，也很简单。&lt;/p&gt;
&lt;p&gt;前文我们第一部分的配置只是定义了一个限流的策略，我们还没应用呢呀。所以我们要在需要的地方把它用起来。&lt;/p&gt;
&lt;p&gt;很简单，在需要限流的 location 中这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; location /abc/api {
&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; limit_req zone=mylimit;
&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;没了？就一句？&lt;/p&gt;
&lt;p&gt;对，没了。是不很简单？简单到我都不想解释，如果你理解了前文你就懂了，我就不解释了。毕竟你会用 AI 不是。&lt;/p&gt;
&lt;p&gt;然后你就可以重新加载配置，或重启 nginx 了。再然后你就要耐心等待和观察，等待之前那些讨厌的恶意 ip 再次造访，顺利地话你会在 nginx 的 error 日志中看到类似这样的信息 ：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;... [error] ..limiting requests,excess:0.996 by zone &amp;#34;mylimit&amp;#34;, client:1.2.3.4 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>Make Jar, Not War.</title><link>https://xiaobox.github.io/p/2022-07-05-make-jar-not-war/</link><pubDate>Tue, 05 Jul 2022 09:51:29 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-07-05-make-jar-not-war/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-07-05-make-jar-not-war/cover.jpg" alt="Featured image of post Make Jar, Not War." /&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/2022-07-05-make-jar-not-war/001-dd69b2c2.jpg"&gt;&lt;/p&gt;
&lt;p&gt;大约在 4 年前，关于 java 应用最终打成 &lt;code&gt;jar&lt;/code&gt; 包还是 &lt;code&gt;war&lt;/code&gt; 包的选择令我比较疑惑。&lt;/p&gt;
&lt;p&gt;那时候更多的应用是打成 &lt;code&gt;war&lt;/code&gt; 包的，即使我们知道可以打成 &lt;code&gt;jar&lt;/code&gt; 包，但之前都是打成 &lt;code&gt;war&lt;/code&gt; 包，并且好像打成 &lt;code&gt;jar&lt;/code&gt; 包并没有什么特别明显的好处。&lt;/p&gt;
&lt;p&gt;但当时令我困惑的是越来越多的实践正在不怎么说明理由的情况下转而打 &lt;code&gt;jar&lt;/code&gt; 包，于是我开始思考&amp;hellip;&amp;hellip;&lt;/p&gt;
&lt;h2 id="war-包的理由"&gt;&lt;a href="#war-%e5%8c%85%e7%9a%84%e7%90%86%e7%94%b1" class="header-anchor"&gt;&lt;/a&gt;war 包的理由
&lt;/h2&gt;&lt;p&gt;在某大型 OTA 企业内部，应用仍然打成 &lt;code&gt;war&lt;/code&gt; 包, PaaS 平台会自动安装并配置好 tomcat，我知道这对于 &lt;strong&gt;web server 的统一配置和运维来说是有好处的&lt;/strong&gt;。基于 &lt;code&gt;war&lt;/code&gt; 背后的一系列 CI/CD 、DevOps 流程都一定有相应的适配，且就算 &lt;code&gt;jar&lt;/code&gt; 包有我不知道的某些优势也不可能一夜之间在大型企业内部使用，需要平台和系统做出调整。&lt;/p&gt;
&lt;p&gt;SpringBoot 在当时并未像现在这样流行，这并不意味着大家不用它，我的意思是相对新的项目来说，企业内部会有非常多 “老系统” 需要维护，我们不能指望一下子把这些老系统都用新的技术栈替换掉，就像你知道现在 &lt;code&gt;web application&lt;/code&gt; 一般是前后端分离开发，但如果接手一个使用 &lt;code&gt;jsp&lt;/code&gt; 的老家伙，你还得维护不是？&lt;/p&gt;
&lt;h2 id="jar-的时代"&gt;&lt;a href="#jar-%e7%9a%84%e6%97%b6%e4%bb%a3" class="header-anchor"&gt;&lt;/a&gt;jar 的时代
&lt;/h2&gt;&lt;p&gt;时代不同了，说得好像过了几十年的样子，但其实也就几年光阴而已。不过仅仅是这几年的光阴却足以改变一些事情的面貌。&lt;/p&gt;
&lt;p&gt;如今，云原生、微服务大行其道，大家好像已经非常适应这种开发模式，没有人纠结要不要用 SpringBoot，只会讨论使用的版本高还是低。更不用说打包的事情，很自然的会使用 &lt;code&gt;jar&lt;/code&gt;,虽然这种看起来的 “最佳实践”，在长期开发的过程中会形成 “肌肉记忆”，但我们还是要讨论一下为什么。&lt;/p&gt;
&lt;h3 id="方便"&gt;&lt;a href="#%e6%96%b9%e4%be%bf" class="header-anchor"&gt;&lt;/a&gt;方便
&lt;/h3&gt;&lt;p&gt;可运行 Jar 是打包自包含可运行应用程序的便捷方法。这样，我们可以最大限度地减少依赖关系。可以通过 Spring boot Maven 和 Gradle plugin 来管理依赖。&lt;/p&gt;
&lt;h3 id="云原生友好"&gt;&lt;a href="#%e4%ba%91%e5%8e%9f%e7%94%9f%e5%8f%8b%e5%a5%bd" class="header-anchor"&gt;&lt;/a&gt;云原生友好
&lt;/h3&gt;&lt;p&gt;在自备容器的情况下（docker,k8s), &lt;code&gt;jar&lt;/code&gt; 包可以直接作为一个 &lt;code&gt;single application&lt;/code&gt; 来管理。&lt;/p&gt;
&lt;p&gt;在过去我们使用 &lt;code&gt;war&lt;/code&gt; 是为了让多应用共享 web server，现在是容器的天下，在容器内，一般情况只跑一个应用进程。由于只有一个进程，我们就可以轻松地管理它，比如重启（不会影响其他的应用，因为没有其他应用）。&lt;/p&gt;
&lt;h3 id="版本控制"&gt;&lt;a href="#%e7%89%88%e6%9c%ac%e6%8e%a7%e5%88%b6" class="header-anchor"&gt;&lt;/a&gt;版本控制
&lt;/h3&gt;&lt;p&gt;利用 &lt;code&gt;git&lt;/code&gt; 等版本控制软件，可以控制程序运行所需要的一切（比如配置文件）&lt;/p&gt;
&lt;h3 id="易于扩展"&gt;&lt;a href="#%e6%98%93%e4%ba%8e%e6%89%a9%e5%b1%95" class="header-anchor"&gt;&lt;/a&gt;易于扩展
&lt;/h3&gt;&lt;p&gt;例如，将其复制到另一台服务器，然后“ just run it!” 无需安装和/或配置容器&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://medium.com/@satyajit.nalavade/make-jar-not-war-josh-long-d6ce5fbb8a23" target="_blank" rel="noopener"
 &gt;https://medium.com/@satyajit.nalavade/make-jar-not-war-josh-long-d6ce5fbb8a23&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>面试不会问的Hystrix实现资源隔离</title><link>https://xiaobox.github.io/p/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/</link><pubDate>Mon, 25 Apr 2022 03:37:31 +0000</pubDate><guid>https://xiaobox.github.io/p/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/cover.jpg" alt="Featured image of post 面试不会问的Hystrix实现资源隔离" /&gt;&lt;h2 id="资源是什么又为什么要隔离"&gt;&lt;a href="#%e8%b5%84%e6%ba%90%e6%98%af%e4%bb%80%e4%b9%88%e5%8f%88%e4%b8%ba%e4%bb%80%e4%b9%88%e8%a6%81%e9%9a%94%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;资源是什么？又为什么要隔离？
&lt;/h2&gt;&lt;p&gt;我们设想一个这样的场景：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;在某分布式微服务系统中的某个服务 A ，它依赖外部的 B 、C、D 三个服务，通过 RPC 远程调用它们。&lt;/li&gt;
&lt;li&gt;假设服务 A 是一个 springboot 启动的 java 进程，内部 wrap 的 web server 是 tomcat。&lt;/li&gt;
&lt;li&gt;假设 tomcat 采用的线程模型是 NIO 模式，它默认的最大连接数是 &lt;code&gt;10000&lt;/code&gt; ，也就是最多同时接收处理&lt;code&gt;10000&lt;/code&gt;个用户请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在正常情况下，只要同时请求数不超过&lt;code&gt;10000&lt;/code&gt; 且服务 A 及内外部服务都正常运行就没有问题。（理论上虽然是这样，但实际情况可能不太严谨）&lt;/p&gt;
&lt;p&gt;考虑这样一种情况：&lt;/p&gt;
&lt;p&gt;假设依赖的 B 服务由于各种原因不正常了，比如出现了超时，而且 B 服务是一个业务核心依赖（基本所有请求都要过它）。那么这时候用户从 A 服务入口进来的正常请求线程将不能正常 &lt;code&gt;终止（terminated）&lt;/code&gt;，而会 &lt;code&gt;阻塞（Blocked）&lt;/code&gt; 或者 &lt;code&gt;等待 (waiting)&lt;/code&gt; 在 B 服务这里。&lt;/p&gt;
&lt;p&gt;这时 tomcat 的可用线程数将下降，也就会导致用户对 A 服务的正常请求受到影响，如果 B 服务的情况不能得到改善，那么 A 服务将有可能面临&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;线程资源不足，A 服务的非核心请求也受到影响 （不走 B 服务的）&lt;/li&gt;
&lt;li&gt;&lt;code&gt;雪崩&lt;/code&gt; 的风险，有可能会因为线程资源不足而 hang 死，产生连锁反应，导致 A 也不可用。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;可见，我们并不想产生这样的影响，我们希望无论 B 服务是不是核心依赖，它出了问题，都尽量不要或最小范围影响我本服务。&lt;/p&gt;
&lt;p&gt;所以，总结来看，资源具体来说就是&lt;code&gt;线程&lt;/code&gt; ，而隔离的目的，是为了在依赖服务出问题的情况下，影响范围最小化。&lt;/p&gt;
&lt;h2 id="hystrix"&gt;&lt;a href="#hystrix" class="header-anchor"&gt;&lt;/a&gt;Hystrix
&lt;/h2&gt;&lt;h3 id="hystrix-模型"&gt;&lt;a href="#hystrix-%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;Hystrix 模型
&lt;/h3&gt;&lt;p&gt;Hystrix 将远程服务的请求托管在一个线程池中。即默认情况下，所有 Hystrix 命令 (@HystrixCommand) 共享同一个线程池来处理这些请求。该线程池中持有 10 个线程来处理各种远程服务请求，可以是 REST 服务调用、数据库访问等。如下图所示：&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/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/001-3a14cda6.jpg"&gt;&lt;/p&gt;
&lt;h3 id="如何隔离"&gt;&lt;a href="#%e5%a6%82%e4%bd%95%e9%9a%94%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;如何隔离
&lt;/h3&gt;&lt;p&gt;有两种策略分别是&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;线程池隔离&lt;/li&gt;
&lt;li&gt;信号量隔离&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;两种隔离方式都是限制对共享资源的并发访问量，线程在就绪状态、运行状态、阻塞状态、终止状态间转变时需要由操作系统调度，占用很大的性能消耗；而信号量是在访问共享资源时，进行 tryAcquire，tryAcquire 成功才允许访问共享资源。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;HystrixCommandProperties.Setter().withExecutionisolationStrategy(ExecutionlsolationStrategy.THREAD)
&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;HystrixCommandProperties.Setter().withExecutionisolationStrategy(ExecutionisolationStrategy.SEMAPHORE)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="线程池隔离"&gt;&lt;a href="#%e7%ba%bf%e7%a8%8b%e6%b1%a0%e9%9a%94%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;线程池隔离
&lt;/h3&gt;&lt;p&gt;@HystrixCommand 的默认配置适用于只有少量远程调用的应用。幸运的是，Hystrix 提供了简单易用的方法实现舱壁来隔离不同的远程资源调用。下图说明了 Hystrix 将不同的远程调用隔离在不同的“舱室”（线程池）中：&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/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/002-f2889115.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Hystrix 可以为每一个依赖建立一个线程池，使之和其他依赖的使用资源隔离，同时限制他们的并发访问和阻塞扩张。每个依赖可以根据权重分配资源（这里主要是线程），每一部分的依赖出现了问题，也不会影响其他依赖的使用资源。&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/2022-04-25-mian-shi-bu-hui-wen-de-hystrix-shi-xian-zi-yuan-ge-li/003-9e729873.jpg"&gt;&lt;/p&gt;
&lt;p&gt;如果简单的使用异步线程来实现依赖调用会有如下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;线程的创建和销毁&lt;/li&gt;
&lt;li&gt;线程上下文空间的切换，用户态和内核态的切换带来的性能损耗。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;使用线程池的方式可以解决第一种问题，但是第二个问题计算开销是不能避免的。&lt;/p&gt;
&lt;p&gt;Netflix 在使用过程中详细评估了使用异步线程和同步线程带来的性能差异，结果表明在 99%的情况下，异步线程带来的几毫秒延迟的完全可以接受的。&lt;/p&gt;
&lt;p&gt;优点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;一个依赖可以给予一个线程池，这个依赖的异常不会影响其他的依赖。&lt;/li&gt;
&lt;li&gt;使用线程可以完全隔离第三方代码，请求线程可以快速放回。&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;p&gt;缺点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;使用线程池的缺点主要是增加了计算的开销。每一个依赖调用都会涉及到队列，调度，上下文切换，而这些操作都有可能在不同的线程中执行。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;线程池的创建和管理&lt;/p&gt;
&lt;p&gt;虽然 Hystrix 可以为每个依赖建立一个线程池，但是如果依赖成千上万，建立那么多线程池肯定是不可能的。所以默认情况下，Hystrix 会为每一个 Command Group 建立一个线程池。Hystrix 的线程池在 HystrixConcurrencyStrategy 初始化，线程池是由 ThreadPoolExecutor 实现的。每个线程池默认初始化 10 个线程。Hystrix 有个静态类 Factory，创建的线程池会被存储在 Factory 中的 ConcurrentHashMap 中。ConcurrentHashMap 的 Key 则是上文说到的 CommandGroupKey 或者指定的 ThreadPoolKey。每次命令执行的时候，都会根据 ThreadPoolKey 去找到对应的线程池。线程池拥有一个继承于 rxjava 中 Scheduler 的 HystrixContextScheduler，用于在执行命令的时候，把命令在这个线程池上调度执行。&lt;/p&gt;
&lt;h3 id="信号量隔离"&gt;&lt;a href="#%e4%bf%a1%e5%8f%b7%e9%87%8f%e9%9a%94%e7%a6%bb" class="header-anchor"&gt;&lt;/a&gt;信号量隔离
&lt;/h3&gt;
 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;每次调用线程，当前请求通过计数信号量进行限制，当信号大于了最大请求数（maxConcurrentRequests）时，进行限制，调用 fallback 接口快速返回。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;信号量的资源隔离只是起到一个开关的作用，例如，服务 X 的信号量大小为 10，那么同时只允许 10 个 tomcat 的线程（此处是 tomcat 的线程，而不是服务 X 的独立线程池里面的线程）来访问服务 X，其他的请求就会被拒绝，从而达到限流保护的作用。&lt;/p&gt;
&lt;p&gt;信号量的调用是同步的，也就是说，每次调用都得阻塞调用方的线程，直到结果返回。这样就导致了无法对访问做超时（只能依靠调用协议超时，无法主动释放）&lt;/p&gt;
&lt;p&gt;什么时候适合用信号量隔离而不是用线程池？&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;隔离的细粒度太高，数百个实例需要隔离，此时用线程池做隔离开销过大&lt;/li&gt;
&lt;li&gt;通常这种都是非网络调用的情况下&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="选用"&gt;&lt;a href="#%e9%80%89%e7%94%a8" class="header-anchor"&gt;&lt;/a&gt;选用
&lt;/h3&gt;&lt;p&gt;两种策略对比&lt;/p&gt;
&lt;p&gt;|&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;线程池隔离&lt;/th&gt;
 &lt;th&gt;信号量隔离&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;线程&lt;/td&gt;
 &lt;td&gt;与调用线程非相同线程&lt;/td&gt;
 &lt;td&gt;与调用线程相同&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;开销&lt;/td&gt;
 &lt;td&gt;排队、调度、上下文开销等&lt;/td&gt;
 &lt;td&gt;无线程切换，开销低&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;异步&lt;/td&gt;
 &lt;td&gt;支持&lt;/td&gt;
 &lt;td&gt;不支持&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;并发支持&lt;/td&gt;
 &lt;td&gt;支持（最大线程池大小）&lt;/td&gt;
 &lt;td&gt;支持（最大信号量上限）&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;对于那些本来延迟就比较小的请求（例如访问本地缓存成功率很高的请求）来说，线程池带来的开销是非常高的，这时，你可以考虑采用其他方法，例如非阻塞信号量（不支持超时），来实现依赖服务的隔离，使用信号量的开销很小。但绝大多数情况下，Netflix 更偏向于使用线程池来隔离依赖服务，因为其带来的额外开销可以接受，并且能支持包括超时在内的所有功能。&lt;/li&gt;
&lt;li&gt;当请求的服务网络开销比较大的时候，或者是请求比较耗时的时候，我们最好是使用线程隔离策略，这样的话，可以保证大量的容器 (tomcat) 线程可用，不会由于服务原因，一直处于阻塞或等待状态，快速失败返回。而当我们请求缓存这些服务的时候，我们可以使用信号量隔离策略，因为这类服务的返回通常会非常的快，不会占用容器线程太长时间，而且也减少了线程切换的一些开销，提高了缓存服务的效率。&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;@HystrixCommand(
&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; commandProperties = { //利用 commandProperties 更改线程池的一些默认配置
&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; //选择“线程池”模式、&amp;#34;信号量&amp;#34;模式
&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; @HystrixProperty(name=&amp;#34;execution.isolation.strategy&amp;#34;,value = &amp;#34;THREAD&amp;#34;/&amp;#34;SEMAPHORE&amp;#34;), 
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; @HystrixProperty(name=&amp;#34;execution.isolation.thread.timeoutInMilliseconds&amp;#34;,value = &amp;#34;3000&amp;#34;),
&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; //信号量大小为 10，那么同时只允许 10 个 tomcat 的线程（此处是 tomcat 的线程，而不是服务的独立线程池里面的线程）来访问服务，其他的请求就会被拒绝，从而达到限流保护的作用
&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; @HystrixProperty(name=&amp;#34;execution.isolation.semaphore.maxConcurrentRequests&amp;#34;,value = &amp;#34;10&amp;#34;),
&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&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;public List&amp;lt;License&amp;gt; getLicensesByOrg(String organizationId){
&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&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&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意，如果选用线程池，要合理的设置线程池的大小，和超时时间。&lt;/p&gt;
&lt;p&gt;spring cloud 的 yaml 配置可以参考：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;feign&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;hystrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 启用熔断降级策略&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&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="w"&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="nt"&gt;ribbon&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 请求建立连接超时时间，单位：毫秒，默认：2000&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ConnectTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3000&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 读取数据超时时间长，单位：毫秒，默认：5000&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 这里设置为 10 秒，表示请求发出后，超过 10 秒没有读取到数据时请求超时&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ReadTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;10000&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对所有操作都进⾏重试，默认：false&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OkToRetryOnAllOperations&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 对同一个实例的最大重试次数，默认：0&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MaxAutoRetries&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 切换实例重试的最大次数，默认：1&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MaxAutoRetriesNextServer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="w"&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="w"&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="nt"&gt;hystrix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;execution&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 配置 HystrixCommand 命令执行是否开启超时，默认：true。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;isolation&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 隔离策略，分为 THREAD 和 SEMAPHORE，默认为 THREAD。&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;strategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;THREAD&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;semaphore&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 信号量大小，当隔离策略为信号量时，最大并发请求达到该设置值，后续的请求将被拒绝，默认：10&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;maxConcurrentRequests&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;100&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 表示设置是否在执行超时时，中断 HystrixCommand.run() 的执行，默认：true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;interruptOnTimeout&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# hystrixCommand 命令执行超时时间，单位：毫秒，默认：1000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 首先要考虑接口的响应时间，其次要考虑 ribbon 的超时时间和重试次数&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;timeoutInMilliseconds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;30000&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;更多配置请 参考：https://github.com/Netflix/Hystrix/wiki/Configuration&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/Netflix/Hystrix/wiki/Configuration" target="_blank" rel="noopener"
 &gt;https://github.com/Netflix/Hystrix/wiki/Configuration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="http://www.baoguoding.com/2019/08/471-hystrix08.html" target="_blank" rel="noopener"
 &gt;http://www.baoguoding.com/2019/08/471-hystrix08.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.csdn.net/shiyong1949/article/details/119201924" target="_blank" rel="noopener"
 &gt;https://blog.csdn.net/shiyong1949/article/details/119201924&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>自顶向下学习 RocketMQ（二）SpringCloud 整合 RocketMQ</title><link>https://xiaobox.github.io/p/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/</link><pubDate>Mon, 06 Dec 2021 10:49:07 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/cover.jpg" alt="Featured image of post 自顶向下学习 RocketMQ（二）SpringCloud 整合 RocketMQ" /&gt;&lt;h2 id="why"&gt;&lt;a href="#why" class="header-anchor"&gt;&lt;/a&gt;why
&lt;/h2&gt;&lt;p&gt;上文中我们讨论了 RocketMQ 的安装问题，有些重要的问题忘了说，即：&lt;/p&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;p&gt;来讨论为什么之前，先来看一下消息模型，即 “是什么？”，我们引用 RocketMQ 的消息模型：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;RocketMQ 主要由 Producer、Broker、Consumer 三部分组成，其中 Producer 负责生产消息，Consumer 负责消费消息，Broker 负责存储消息。Broker 在实际部署过程中对应一台服务器，每个 Broker 可以存储多个 Topic 的消息，每个 Topic 的消息也可以分片存储于不同的 Broker。Message Queue 用于存储消息的物理地址，每个 Topic 中的消息地址存储于多个 Message Queue 中。ConsumerGroup 由多个 Consumer 实例构成。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;可以看到，主要是有 Producer、Broker、Consumer 这三部分。利用这样的模型，一般来说我们可以做到几点应用：&lt;/p&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;h3 id="异步"&gt;&lt;a href="#%e5%bc%82%e6%ad%a5" class="header-anchor"&gt;&lt;/a&gt;异步
&lt;/h3&gt;&lt;p&gt;相对同步，异步理论上执行的速度更快，效率更高，主线程执行完自己的逻辑然后发一条消息给消息队列就结束了，另外一个异步线程去订阅然后消费这条消息。&lt;/p&gt;
&lt;h3 id="解耦"&gt;&lt;a href="#%e8%a7%a3%e8%80%a6" class="header-anchor"&gt;&lt;/a&gt;解耦
&lt;/h3&gt;&lt;p&gt;其实异步和解耦关系很密切，如果你把一个业务从同步的改成异步的了，实际上它从业务上就已经解耦了，至于形式上，无论你是用多个微服务进行拆分解耦，还是多个线程进行拆分解耦，都是解耦的。举个比较常见的例子，也是异步场景的，比如电商的用户下单购买后，增加消费积分场景：订单服务主业务逻辑结束后会给消息队列发一条增加消费积分的消息，下游营销服务会去订阅这条消息消费它，异步地执行增加积分的逻辑。&lt;/p&gt;
&lt;p&gt;当然解耦后，系统间调用关系从大的业务上是同步的还是异步的，完全是由业务自己决定的。&lt;/p&gt;
&lt;h3 id="削峰填谷"&gt;&lt;a href="#%e5%89%8a%e5%b3%b0%e5%a1%ab%e8%b0%b7" class="header-anchor"&gt;&lt;/a&gt;削峰填谷
&lt;/h3&gt;&lt;p&gt;说白了，就是根据系统的处理能力来处理信息。在我们没有用消息队列之前，系统接收多少请求就都要处理完响应回去，处理的能力是根据单机或集群的处理能力而定，当然这是有上限的，虽然可以扩展，但可扩展的粒度比较粗：scale out 或 scale up，要么增加机器，要么扩展单机性能。如果我只是有某一类请求有问题处理不过来，其他的没啥事儿，这种扩展粒度就不太合适了。&lt;/p&gt;
&lt;p&gt;利用消息队列，我们完全可以实现灵活的扩展，即将更细粒度的请求，解耦抽象为消息，发送到消息队列，让下游服务根据自身的能力去进消费。这里还涉及一个“流控”的问题，即像控制水流的流速一样，我们也要根据上下游系统及消息队列的处理能力来控制消息的流量，以 RocketMQ 为例：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;生产者流控，因为 broker 处理能力达到瓶颈&lt;/li&gt;
&lt;li&gt;消费者流控，因为消费能力达到瓶颈。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所谓削峰填谷，讲究的是一个平衡，这个平衡是系统和消息队列处理能力与消息流量之间的平衡。峰值的时候控一控让它下来，低谷的时候填一填让它上来，仅此而已。&lt;/p&gt;
&lt;h2 id="spring-cloud-stream"&gt;&lt;a href="#spring-cloud-stream" class="header-anchor"&gt;&lt;/a&gt;Spring Cloud Stream
&lt;/h2&gt;&lt;p&gt;聊完 why, 我们回到本文的正题，既然是 SpringCloud 的整合，那我们先聊一下 SpringCloud 吧。&lt;/p&gt;
&lt;p&gt;Spring Cloud 体系内本身是有基于消息驱动的微服务框架的，即 Spring Cloud Stream。&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/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/001-4616aa6b.jpg"&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems.&lt;/p&gt;
&lt;p&gt;The framework provides a flexible programming model built on already established and familiar Spring idioms and best practices, including support for persistent pub/sub semantics, consumer groups, and stateful partitions.&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;Spring Cloud Stream 提供了消息中间件配置的统一抽象，推出了 publish-subscribe、consumer groups、partition 这些统一的概念，有效的简化了上层研发人员对 MQ 使用的复杂度，让开发人员更多的精力投入到核心业务的处理。&lt;/p&gt;
&lt;p&gt;Spring Cloud Stream 解决什么问题？&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;无感知的使用消息中间件 Stream 解决了开发人员无感知的使用消息中间件的问题，因为 Stream 对消息中间件的进一步封装，可以做到代码层面对中间件的无感知。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;中间件和服务的高度解耦 Spring Cloud Stream 进行了配置隔离，只需要调整配置，开发中可以动态的切换中间件（如 rabbitmq 切换为 kafka)，使得微服务开发的高度解耦，服务可以关注更多自己的业务流程。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="应用模型"&gt;&lt;a href="#%e5%ba%94%e7%94%a8%e6%a8%a1%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;应用模型
&lt;/h3&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/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/002-3cb708a0.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Spring Cloud Stream 由一个中立的中间件内核组成。Spring Cloud Stream 会注入输入和输出的 channels，应用程序通过这些 channels 与外界通信，而 channels 则是通过一个明确的中间件 Binder 与外部 brokers 连接。&lt;/p&gt;
&lt;p&gt;列举下 Binder 的实现：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RabbitMQ&lt;/li&gt;
&lt;li&gt;Apache Kafka&lt;/li&gt;
&lt;li&gt;Amazon Kinesis&lt;/li&gt;
&lt;li&gt;Google PubSub (partner maintained)&lt;/li&gt;
&lt;li&gt;Solace PubSub+ (partner maintained)&lt;/li&gt;
&lt;li&gt;Azure Event Hubs (partner maintained)&lt;/li&gt;
&lt;li&gt;Apache RocketMQ (partner maintained)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;另外还有一个概念叫 Binding,Binding 在消息中间件与应用程序提供的 Provider 和 Consumer 之间提供了一个桥梁，实现了开发者只需使用应用程序的 Provider 或 Consumer 生产或消费数据即可，屏蔽了开发者与底层消息中间件的接触。Binding 包括 Input Binding 和 Output Binding。&lt;/p&gt;
&lt;p&gt;注意这里的 input 和 output 是站在生产者和消费者的角度，而不是 broker 的角度，如果你生产消息，那么对应使用的应该是 output, 如果你消费消息，那么对应使用的应该是 input。&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/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/003-f58cf852.jpg"&gt;&lt;/p&gt;
&lt;h2 id="接入-rocketmq"&gt;&lt;a href="#%e6%8e%a5%e5%85%a5-rocketmq" class="header-anchor"&gt;&lt;/a&gt;接入 RocketMQ
&lt;/h2&gt;&lt;h3 id="依赖"&gt;&lt;a href="#%e4%be%9d%e8%b5%96" class="header-anchor"&gt;&lt;/a&gt;依赖
&lt;/h3&gt;&lt;p&gt;使用 SpringCloud SpringBoot SpringCloud Alibaba 的组合，版本信息如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;spring.boot.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2.3.2.RELEASE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;spring.boot.version&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;2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;spring.cloud.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Hoxton.SR9&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;spring.cloud.version&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;3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;spring.cloud.alibaba.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2.2.6.RELEASE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;spring.cloud.alibaba.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;依赖包&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&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; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&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; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-web&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&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; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&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; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&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="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;com.alibaba.cloud&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&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; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-cloud-starter-stream-rocketmq&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&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; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&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; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&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;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&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;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-actuator&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&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;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="配置"&gt;&lt;a href="#%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;配置
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;spring&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;mvc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;throw-exception-if-no-handler-found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 处理 404 问题&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;resources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;add-mappings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 关闭 404 资源映射&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;application&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mq-example&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cloud&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;bindings&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 定义 name 为 input 的 binding&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;application/json&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test-topic&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test-group&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 定义 name 为 output 的 binding&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;output&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;content-type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;application/json&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;test-topic&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;rocketmq&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;binder&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# 配置 rocketmq 的 nameserver 地址&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;name-server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;127.0.0.1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;9876&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="生产消费"&gt;&lt;a href="#%e7%94%9f%e4%ba%a7%e6%b6%88%e8%b4%b9" class="header-anchor"&gt;&lt;/a&gt;生产&amp;amp;消费
&lt;/h3&gt;&lt;p&gt;生产者 controller&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@GetMapping&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/produce&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;produceMsg&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&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="w"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Object&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MessageConst&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;PROPERTY_TAGS&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;test&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MessageBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;createMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;Hello RocketMQ!&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MessageHeaders&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;&lt;span class="w"&gt;
&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;发送了消息 &amp;#34;&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&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="w"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;消费者订阅&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nd"&gt;@Service&lt;/span&gt;&lt;span class="w"&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="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ReceiveService&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&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="w"&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="w"&gt; &lt;/span&gt;&lt;span class="cm"&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="cm"&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="cm"&gt; * @param receiveMsg
&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="cm"&gt; */&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nd"&gt;@StreamListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;receiveInput1&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;receiveMsg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34; 接受到消息 input receive: &amp;#34;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;receiveMsg&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&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="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="测试"&gt;&lt;a href="#%e6%b5%8b%e8%af%95" class="header-anchor"&gt;&lt;/a&gt;测试
&lt;/h3&gt;&lt;p&gt;首先启动 RocketMQ(nameserver 和 broker) ，然后启动服务，调用 controller 接口 发送消息，查看接收到的消息内容。&lt;/p&gt;
&lt;p&gt;也可以通过 dashboard 查看消息的接收状态。&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/2021-12-06-zi-ding-xiang-xia-xue-xi-rocketmq-er-springcloud-zheng-he-ro/004-1d0e0832.jpg"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://spring.io/projects/spring-cloud-stream" target="_blank" rel="noopener"
 &gt;https://spring.io/projects/spring-cloud-stream&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md" target="_blank" rel="noopener"
 &gt;https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/rocketmq-example/readme-zh.md&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://docs.spring.io/spring-cloud-stream/docs/3.1.5/reference/html/spring-cloud-stream.html#spring-cloud-stream-reference" target="_blank" rel="noopener"
 &gt;https://docs.spring.io/spring-cloud-stream/docs/3.1.5/reference/html/spring-cloud-stream.html#spring-cloud-stream-reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/binyue/p/12222198.html" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/binyue/p/12222198.html&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>上了 istio 的贼船之 API Gateway</title><link>https://xiaobox.github.io/p/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/</link><pubDate>Mon, 21 Jun 2021 05:02:18 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/cover.jpg" alt="Featured image of post 上了 istio 的贼船之 API Gateway" /&gt;&lt;h1 id="上了-istio-的贼船之-api-gateway"&gt;&lt;a href="#%e4%b8%8a%e4%ba%86-istio-%e7%9a%84%e8%b4%bc%e8%88%b9%e4%b9%8b-api-gateway" class="header-anchor"&gt;&lt;/a&gt;上了 istio 的贼船之 API Gateway
&lt;/h1&gt;&lt;h2 id="现状"&gt;&lt;a href="#%e7%8e%b0%e7%8a%b6" class="header-anchor"&gt;&lt;/a&gt;现状
&lt;/h2&gt;&lt;p&gt;下图是我们系统的架构现状，大致介绍一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础设施在华为云上&lt;/li&gt;
&lt;li&gt;基本上是基于 &lt;code&gt;istio on k8s&lt;/code&gt; 架构。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;istio&lt;/code&gt; 版本为 1.3，所以组件较多（&lt;code&gt;galley、pilot、citadel、telemetry&lt;/code&gt;&amp;hellip;&amp;hellip;）&lt;/li&gt;
&lt;li&gt;微服务后端用 &lt;code&gt;spring boot&lt;/code&gt; 单体，前端有 &lt;code&gt;nodejs、vue&lt;/code&gt;等&lt;/li&gt;
&lt;li&gt;应用的链路监控主要基于 &lt;code&gt;skywalking&lt;/code&gt;, &lt;code&gt;istio&lt;/code&gt; 的通讯层面利用 &lt;code&gt;kiali&lt;/code&gt;可视化调用链&lt;/li&gt;
&lt;li&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/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/001-abd5152c.jpg"&gt;&lt;/p&gt;
&lt;p&gt;历史架构&lt;/p&gt;
&lt;p&gt;主要介绍下作为服务通讯基础设施的 &lt;code&gt;istio&lt;/code&gt; 在这里的作用&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务间通讯，依赖 &lt;code&gt;envoy sidecar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;注册中心，依赖 &lt;code&gt;istio&lt;/code&gt; 控制面&lt;/li&gt;
&lt;li&gt;服务治理（熔断、超时、重试）&lt;em&gt;这部分还没有完完全全切干净，还有些 &lt;code&gt;spring boot&lt;/code&gt; 应用依赖 &lt;code&gt;hystrix&lt;/code&gt;，后面会全部改成利用 &lt;code&gt;istio&lt;/code&gt;&lt;/em&gt;。&lt;/li&gt;
&lt;li&gt;流量管理（&lt;code&gt;ingress gateway&lt;/code&gt;、&lt;code&gt;egress gateway&lt;/code&gt;、负载均衡）&lt;/li&gt;
&lt;li&gt;测试（错误注入）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过将传统微服务架构的这些控制面功能解耦到 istio,可以让微服务应用本身专注于业务开发，是一个比较简的单体 springboot 应用。再结合 k8s 的高扩展性，研发整体的迭代速度和运维效率还是比较高的，缺点是无论是 k8s 还是 istio ，学习成本偏高，需要团队至少 2 人具有专业知识，对于招聘成本、系统升级都有风险。&lt;/p&gt;
&lt;h2 id="上了贼船"&gt;&lt;a href="#%e4%b8%8a%e4%ba%86%e8%b4%bc%e8%88%b9" class="header-anchor"&gt;&lt;/a&gt;上了贼船
&lt;/h2&gt;&lt;p&gt;坦白讲，如果系统最初的设计是我来做，是不会用如此“激进”的方案的，会转而用 &lt;code&gt;spring cloud&lt;/code&gt; 为基础的架构，当然，&lt;code&gt;k8s&lt;/code&gt; 是非常可能会使用的。但可惜我是中间接手，也想过换架构，但迫于公司业务和迭代的压力，再加上人手有限，再换架构的风险会非常高，所以既然上了 &lt;code&gt;istio&lt;/code&gt; 的贼船，就只能走下去了，等什么时候时机成熟再并行其他架构，或切换回合适的架构。这里我要强调的是，做架构选择或者选型不是为了技术而技术，一定是要非常适合当时公司的发展现状、业务场景、团队能力、招聘成本等等，综合多种条件而得出的结论。 巧合的是 istio 的 logo 也是一个帆船的样子，果真是上了贼船 &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/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/002-5ae55d6c.png"&gt;&lt;/p&gt;
&lt;h2 id="api-gateway"&gt;&lt;a href="#api-gateway" class="header-anchor"&gt;&lt;/a&gt;API Gateway
&lt;/h2&gt;&lt;p&gt;终于说到本文的重点 &lt;code&gt;API Gateway&lt;/code&gt; 了，对于这个话题，之前写过一篇文章，读者可以先脱离现在的架构从功能层面来了解下 &lt;code&gt;API Gateway&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; [![Image](https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/003-65589640.png)API 网关选型及包含 BFF 的架构设计](http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247484717&amp;amp;idx=1&amp;amp;sn=8162d7957b29504162490780e8d3763d&amp;amp;chksm=eb6dbaabdc1a33bd14c244069e44e3b84dfea8bae546c09facfa42bb5dbf90c1c70421efa6dc&amp;amp;scene=21#wechat_redirect)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;回到我们现在的架构，你会发现，虽然我们有前置的 &lt;code&gt;openResty&lt;/code&gt;,但应用层这边并没有一个担当 &lt;code&gt;API Gateway&lt;/code&gt; 角色的服务。而无论是 &lt;code&gt;openResty&lt;/code&gt; 或者是 &lt;code&gt;nginx&lt;/code&gt; 对于云原生 &lt;code&gt;API Gateway&lt;/code&gt; 的需求是不能完全满足的。&lt;/p&gt;
&lt;p&gt;当然了解 &lt;code&gt;istio&lt;/code&gt; 的读者可能会问：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;istio ingress gateway&lt;/code&gt; 不也具有网关的功能吗 ？&lt;/li&gt;
&lt;li&gt;为什么没有用 &lt;code&gt;nginx ingress controller&lt;/code&gt; ？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;首先，我承认基于 &lt;code&gt;k8s ingress&lt;/code&gt; 实现的各种 &lt;code&gt;ingress controller&lt;/code&gt; 功能越来越完善，如果我们没有用 &lt;code&gt;istio&lt;/code&gt; 可能会采用这种方案，但我们使用了，那么再结合 &lt;code&gt;k8s ingress&lt;/code&gt; 就会有如如何为服务网格选择入口网关[1]中说的如下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;K8s Ingress&lt;/code&gt; 是独立在 &lt;code&gt;Istio&lt;/code&gt; 体系之外的，需要单独采用 &lt;code&gt;Ingress rule&lt;/code&gt; 进行配置，导致系统入口和内部存在两套互相独立的路由规则配置，运维和管理较为复杂。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;K8s Ingress rule&lt;/code&gt; 的功能较弱，不能在入口处实现和网格内部类似的路由规则，也不具备网格 &lt;code&gt;sidecar&lt;/code&gt; 的其它能力，导致难以从整体上为应用系统实现灰度发布、分布式跟踪等服务管控功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其次，没错 &lt;code&gt;istio ingress gateway&lt;/code&gt; 除了基础的通讯功能之外，还有一些其他的应用层功能。但我们综合来比较下 &lt;code&gt;k8s ingress&lt;/code&gt;、&lt;code&gt;istio ingress gateway&lt;/code&gt; 和我们理想中的 &lt;code&gt;API Gateway&lt;/code&gt;，就会发现它还不够完善,主要是对于 &lt;code&gt;API&lt;/code&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/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/004-920af72e.jpg"&gt;&lt;/p&gt;
&lt;h2 id="未来"&gt;&lt;a href="#%e6%9c%aa%e6%9d%a5" class="header-anchor"&gt;&lt;/a&gt;未来
&lt;/h2&gt;&lt;p&gt;前文已经对各种 &lt;code&gt;API Gateway&lt;/code&gt;的实现方案进行了讨论，结论是在目前难以找到一个同时具备&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Isito Ingress&lt;/code&gt;能力的网关。那么再回顾一下我们的需求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们使用 istio&lt;/li&gt;
&lt;li&gt;我们需要 API Gateway&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/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/005-af72412a.jpg"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;仍然利用 &lt;code&gt;istio ingress gateway&lt;/code&gt;作为入口&lt;/li&gt;
&lt;li&gt;将 &lt;code&gt;istio ingress gateway&lt;/code&gt;接到 &lt;code&gt;LB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;API Gateway&lt;/code&gt;纳入到&lt;code&gt;istio cluster&lt;/code&gt;管理的范畴当中，即拥有&lt;code&gt;sidecar proxy&lt;/code&gt;，可被&lt;code&gt;istio&lt;/code&gt;控制面控制。&lt;code&gt;API Gateway&lt;/code&gt;的选型很有可能使用云原生应用网关，如 &lt;code&gt;API SIX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;应用层微服务不会利用如 spring cloud gateway 编码一个服务网关&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结：使用&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Sidecar Proxy&lt;/code&gt;一起为服务网格提供外部流量入口。&lt;/p&gt;
&lt;h3 id="还有没有问题"&gt;&lt;a href="#%e8%bf%98%e6%9c%89%e6%b2%a1%e6%9c%89%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;还有没有问题？
&lt;/h3&gt;&lt;p&gt;有的，根据 Service Mesh 和 API Gateway关系深度探讨[2]上述方案的优势在于&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Sidecar&lt;/code&gt;独立部署，职责明确，架构清晰。但是，和&lt;code&gt;Service Mesh&lt;/code&gt;使用&lt;code&gt;sidecar&lt;/code&gt;被质疑多一跳会造成性能开销影响效率一样，&lt;code&gt;API Gateway&lt;/code&gt;使用&lt;code&gt;Sidecar&lt;/code&gt;也被同样的质疑：多了一跳……&lt;/p&gt;
&lt;p&gt;对了多了这一跳，从整个架构每一段的网络耗时及其作用来看，这一跳多出的时间，几乎可以忽略不计。&lt;/p&gt;
&lt;h3 id="性能如何"&gt;&lt;a href="#%e6%80%a7%e8%83%bd%e5%a6%82%e4%bd%95" class="header-anchor"&gt;&lt;/a&gt;性能如何？
&lt;/h3&gt;&lt;p&gt;作为 &lt;code&gt;sidecar&lt;/code&gt; 的 &lt;code&gt;envoy&lt;/code&gt;的性能应该是毋庸置疑了。至于&lt;code&gt;istio ingress gateway&lt;/code&gt;虽然官方给出的数据也不错，但还是要在实践中观察。而作为 &lt;code&gt;Cloud Native API Gateway&lt;/code&gt; 比如 &lt;code&gt;API SIX&lt;/code&gt; 我对它有足够的信心，至少在我司现阶段业务体量以及未来百倍增长规模下都不会担心性能问题。&lt;/p&gt;
&lt;h3 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h3&gt;&lt;p&gt;[1]&lt;/p&gt;
&lt;p&gt;如何为服务网格选择入口网关？: &lt;em&gt;&lt;a class="link" href="https://zhaohuabing.com/post/2019-03-29-how-to-choose-ingress-for-service-mesh/#k8s-ingress" target="_blank" rel="noopener"
 &gt;https://zhaohuabing.com/post/2019-03-29-how-to-choose-ingress-for-service-mesh/#k8s-ingress&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[2]&lt;/p&gt;
&lt;p&gt;Service Mesh和API Gateway关系深度探讨: &lt;em&gt;&lt;a class="link" href="https://www.servicemesher.com/blog/service-mesh-and-api-gateway/" target="_blank" rel="noopener"
 &gt;https://www.servicemesher.com/blog/service-mesh-and-api-gateway/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>API 网关选型及包含 BFF 的架构设计</title><link>https://xiaobox.github.io/p/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/</link><pubDate>Tue, 13 Oct 2020 04:14:35 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/cover.jpg" alt="Featured image of post API 网关选型及包含 BFF 的架构设计" /&gt;&lt;h1 id="api-网关选型及包含-bff-的架构设计"&gt;&lt;a href="#api-%e7%bd%91%e5%85%b3%e9%80%89%e5%9e%8b%e5%8f%8a%e5%8c%85%e5%90%ab-bff-%e7%9a%84%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;API 网关选型及包含 BFF 的架构设计
&lt;/h1&gt;&lt;h2 id="一-背景介绍"&gt;&lt;a href="#%e4%b8%80-%e8%83%8c%e6%99%af%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;一 背景介绍
&lt;/h2&gt;&lt;p&gt;下图是我从网络上找到的一个微服务架构的简单架构图，如图可见 API Gateway 在其中起到一个承上启下的作用，是关键组件。&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/001-7e801992.png"&gt;&lt;/p&gt;
&lt;p&gt;图片来源于网络&lt;/p&gt;
&lt;p&gt;在更通用的场景下我们会使用 NGINX 这样的软件做前置，用来处理SLB负载均衡过来的流量，作用是反向代理、集群负载均衡、转发、日志收集等功能。&lt;/p&gt;
&lt;p&gt;然后再将 NGINX 的请求 proxy 到 API Gateway 做统一网关处理。&lt;/p&gt;
&lt;p&gt;在上面的这个场景下 API Gateway 可以包含以下功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安全&lt;/li&gt;
&lt;li&gt;限流&lt;/li&gt;
&lt;li&gt;缓存&lt;/li&gt;
&lt;li&gt;熔断&lt;/li&gt;
&lt;li&gt;重试&lt;/li&gt;
&lt;li&gt;负载&lt;/li&gt;
&lt;li&gt;反向路由&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;p&gt;熟悉 NGINX 的朋友应该可以看出来，上面列出的这些功能和 NGINX 的部分功能是重合的，不过由于架构结构不同，在上面我提到的场景中，即 NGINX 在前 API gateway 在后的结构中，他们两者关注的维度也不一样，所以即使有重合也正常。&lt;/p&gt;
&lt;h2 id="二-架构调整"&gt;&lt;a href="#%e4%ba%8c-%e6%9e%b6%e6%9e%84%e8%b0%83%e6%95%b4" class="header-anchor"&gt;&lt;/a&gt;二 架构调整
&lt;/h2&gt;&lt;p&gt;下图是我基于云原生微服务架构设计的架构图其中前端流量是通过 SLB -&amp;gt; NGINX -&amp;gt; API Gateway 再到具体服务。&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/002-1d7f0cab.jpg"&gt;&lt;/p&gt;
&lt;h2 id="三-java技术栈的-api-gateway-选型"&gt;&lt;a href="#%e4%b8%89-java%e6%8a%80%e6%9c%af%e6%a0%88%e7%9a%84-api-gateway-%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;三 java技术栈的 API Gateway 选型
&lt;/h2&gt;&lt;p&gt;由于后端采用java 的 spring cloud 开发的，所以在语言一致性上更倾向 java 语言开发的组件。如上图虽然在 API Gateway 的位置上写的是 spring cloud gateway，然而也可以采用像 zuul、zuul2 这些同样是 java 语言开发的组件。对于具体 zuul 和 spring gateway的选型，是这样考虑的：&lt;/p&gt;
&lt;p&gt;|&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;spring cloud gateway&lt;/th&gt;
 &lt;th&gt;zuul&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;性能&lt;/td&gt;
 &lt;td&gt;性能比 Netflix Zuul 好将近一倍&lt;/td&gt;
 &lt;td&gt;Zuul1 的性能较差 Zuul2 较 Zuul1 有较大的提升&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;社区和文档&lt;/td&gt;
 &lt;td&gt;spring社区非常活跃&lt;/td&gt;
 &lt;td&gt;一般&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;可维护性&lt;/td&gt;
 &lt;td&gt;基于spring官方维护性强&lt;/td&gt;
 &lt;td&gt;经常跳票、Spring Cloud暂时还没有对Zuul2.0的整合计划&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;亮点&lt;/td&gt;
 &lt;td&gt;异步、配置灵活&lt;/td&gt;
 &lt;td&gt;成熟、简单门槛低&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;不足&lt;/td&gt;
 &lt;td&gt;早期产品、新版本踩坑&lt;/td&gt;
 &lt;td&gt;性能一般、可编程一般&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Spring Cloud Gateway 的性能比 Zuul 好基本上已经是业界公认的了，实际上，Spring Cloud Gateway 官方也发布过一个性能测试，这里节选如下数据：&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/003-8c6955ec.png"&gt;&lt;/p&gt;
&lt;p&gt;Spring Cloud Gateway 构建于 Spring 5+，基于 Spring Boot 2.x 响应式的、非阻塞式的 API。同时，它支持 websockets，和 Spring 框架紧密集成。从目前来看，gateway替代zuul是趋势。基于以上这些，综合考虑在架构中使用Spring Cloud Gateway。&lt;/p&gt;
&lt;h2 id="四-非java技术栈的-api-gateway-选型"&gt;&lt;a href="#%e5%9b%9b-%e9%9d%9ejava%e6%8a%80%e6%9c%af%e6%a0%88%e7%9a%84-api-gateway-%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;四 非java技术栈的 API Gateway 选型
&lt;/h2&gt;&lt;p&gt;现代 API Gateway 越来越需要或者流行可编程网关了。上面介绍的都是基于 java 语言开发的可编程的 API Gateway。下面我们来聊聊非 java 语言开发的网关。从前面的架构图上看，我们完全可以将 NGINX 和 API Gateway 合并起来，他们的功能的重合点自然消除了，也能降低架构的复杂性和运维成本。&lt;/p&gt;
&lt;p&gt;NGINX 是一款优秀的软件，然而它在动态性方面的不足导致不太灵活，后面出现的 OpenResty、tengine 这些基于NGINX 和 Lua 的软件在动态性、灵活方面有本质上的改善，加上基于Lua脚本和插件，可以实现所谓的可编程。&lt;/p&gt;
&lt;p&gt;市面上基于OpenResty 以 API Gateway 为应用场景的应用软件有 Kong、APISIX、tyk 等。以下是CNCFland scape 的一个概览&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/004-4dffcf41.png"&gt;&lt;/p&gt;
&lt;p&gt;比较了一下 NGING 和 KONG&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/005-0c00debf.png"&gt;&lt;/p&gt;
&lt;p&gt;经过考虑，在架构上，后期有可能将 NGINX、Spring Cloud Gateway 替换成KONG 或其他软件。&lt;/p&gt;
&lt;p&gt;比较了一下，目前最火的应用是Kong，另一个国产的 APISIX 趋势也是很猛，且他们的技术栈雷同，所以我在选型上找到了APISIX的作者做的对比：&lt;/p&gt;
&lt;p&gt;从 API 网关核心功能点来看，两者均已覆盖：&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/006-82d62ded.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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/007-817eae70.png"&gt;&lt;/p&gt;
&lt;p&gt;通过性能测试可以看到，在不开启插件的情况下，Apache APISIX 的性能（QPS 和延迟）是 Kong 的2倍，但开启了两个常用插件后，性能就是 Kong 的十倍了。&lt;/p&gt;
&lt;p&gt;无论从性能、可用性、可编程代码量等各个维度APISIX都是非常优秀的，目前唯一担心的就是这种早期项目没有太多大规模应用实践，如果上生产还是有风险，可在测试环境调研，并等待有更多生产实践作为依据。 当然如果架构师认为风险并不大，且经过了测试调研也是可以上的。😁&lt;/p&gt;
&lt;h2 id="五-bff-层建设迭代"&gt;&lt;a href="#%e4%ba%94-bff-%e5%b1%82%e5%bb%ba%e8%ae%be%e8%bf%ad%e4%bb%a3" class="header-anchor"&gt;&lt;/a&gt;五 BFF 层建设迭代
&lt;/h2&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/008-ee15f4db.png"&gt;&lt;/p&gt;
&lt;p&gt;前面我们将 API Gateway 的网关选型介绍了一下，请求通过网关后一般不会直接打到具体微服务上的，而是会通过BFF层，所谓的BFF，即 backend for frontend 面向前端的后端。具体来说它的职能包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;api数据裁剪&lt;/li&gt;
&lt;li&gt;接口编排&lt;/li&gt;
&lt;li&gt;接口调用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这层有的公司会按业务进行多个BFF的建设，在BFF中又有可能拆成多个服务，比如支撑首页的，支持列表页的，或者只有一个服务，支撑某个应用的所有请求的。&lt;/p&gt;
&lt;p&gt;有了BFF层，前后端就会更好的解耦，前端不用再调用多个接口，然后再组织数据，微服务后端也只需要关心自己服务边界内的事情。&lt;/p&gt;
&lt;p&gt;然而在实践的过程中会出现一些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大量业务逻辑从前后端集中在了BFF层&lt;/li&gt;
&lt;li&gt;BFF层逻辑复杂，代码量越来越大，难以维护&lt;/li&gt;
&lt;li&gt;BFF API版本维护复杂&lt;/li&gt;
&lt;li&gt;前端端接口职责不清，扯皮的结果就是放在BFF层&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上是我真实遇到过的场景。所以在后面的架构设计和实施中，这些情况会尽量避免，但没有从技术上解决根本问题。直到 GraphQL 的出现，让我眼前一亮，给了我一个很好的解决方案。关于GraphQL的搭建，数据交换等细节这里就不展开说了，感兴趣的可以从网上找到很多资料。&lt;/p&gt;
&lt;p&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/009-06053c35.png"&gt;&lt;/p&gt;
&lt;p&gt;图片来源于网络&lt;/p&gt;
&lt;p&gt;说起来简单，做起来没那么容易 ，细节是魔鬼，每利用一个新的技术都会经历一波打怪升级的过程。不过总体来说利用GraphQL确实能从理论上解决上面所说的问题。而重点是如何将它结合进你的系统架构中，并且发挥出它的优势。架构很多时候是在做权衡和选择&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-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/010-ae0d6502.gif"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>如何使用skywalking 进行全链路监控</title><link>https://xiaobox.github.io/p/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/</link><pubDate>Tue, 29 Sep 2020 06:41:07 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/cover.jpg" alt="Featured image of post 如何使用skywalking 进行全链路监控" /&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/001-9446e222.png"&gt;&lt;/p&gt;
&lt;h1 id="如何使用skywalking-进行全链路监控"&gt;&lt;a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8skywalking-%e8%bf%9b%e8%a1%8c%e5%85%a8%e9%93%be%e8%b7%af%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;如何使用skywalking 进行全链路监控
&lt;/h1&gt;&lt;h2 id="本文涉及内容"&gt;&lt;a href="#%e6%9c%ac%e6%96%87%e6%b6%89%e5%8f%8a%e5%86%85%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;本文涉及内容
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;skywalking 全链路监控&lt;/li&gt;
&lt;li&gt;skywalking 的参数配置&lt;/li&gt;
&lt;li&gt;skywalking UI 监控视角与指标介绍&lt;/li&gt;
&lt;li&gt;一些很有用的点&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="skywalking-全链路监控"&gt;&lt;a href="#skywalking-%e5%85%a8%e9%93%be%e8%b7%af%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;skywalking 全链路监控
&lt;/h2&gt;&lt;p&gt;下图是我从网上找到的一个比较常见的微服务架构，看的出来使用的是 spring cloud 框架组件，后端服务是 java。我所谓的全链路监控是 从 Nginx 到数据库 这个链路的监控。&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/002-d1c5d4dc.jpg"&gt;&lt;/p&gt;
&lt;p&gt;我们知道 skywalking 可以通过 agent 比较方便的监控到后端的 java 应用。有关 skywalking 的安装请参考官方文档[1]&lt;/p&gt;
&lt;p&gt;以下是几个界面截图：通过 skywalking , 我们可以从服务入口开始一直监控到数据库，甚至是数据库的 sql 以及参数都可以一览无余（&lt;em&gt;sql 参数显示需要单独配置，后面会讲&lt;/em&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/003-02ac1ae2.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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/004-a176d122.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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/005-d168e03c.png"&gt;&lt;/p&gt;
&lt;p&gt;然而我们并没有监控到请求的上游源头，即 Nginx 入口，如果我们将从 Nginx 入口来的并且经由 java 服务最终到数据库的请求全部监控起来，就完成了请求的全链路监控。上面我们处理了下半段，现在我们来处理上半段。&lt;/p&gt;
&lt;p&gt;skywalking-nginx-lua[2] 这是 skywalking 的另一个项目，可以通过它来对nginx进行监控。skywalking-nginx-lua 是使用lua来织入 agent 的。所以要求你的 nginx 要么有 lua 模块，要么用 openResty 这样的自带 Lua 功能模块的软件。&lt;/p&gt;
&lt;p&gt;我使用的是openResty，只需要加以下配置就可以实现监控（注意中文注释部分）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&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;http&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="kn"&gt;lua_package_path&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/Path/to/.../skywalking-nginx-lua/lib/skywalking/?.lua&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;;&lt;span class="kn"&gt;&amp;#34;&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&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="c1"&gt;# Buffer represents the register inform and the queue of the finished segment
&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="kn"&gt;lua_shared_dict&lt;/span&gt; &lt;span class="s"&gt;tracing_buffer&lt;/span&gt; &lt;span class="mi"&gt;100m&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; 6&lt;/span&gt;&lt;span class="cl"&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="c1"&gt;# Init is the timer setter and keeper
&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="c1"&gt;# Setup an infinite loop timer to do register and trace report.
&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="kn"&gt;init_worker_by_lua_block&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;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;local&lt;/span&gt; &lt;span class="s"&gt;metadata_buffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ngx.shared.tracing_buffer&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&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="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Set&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt; &lt;span class="s"&gt;name&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="s"&gt;metadata_buffer:set(&amp;#39;serviceName&amp;#39;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;User&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt; &lt;span class="s"&gt;Name&amp;#39;)&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="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Instance&lt;/span&gt; &lt;span class="s"&gt;means&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt; &lt;span class="s"&gt;of&lt;/span&gt; &lt;span class="s"&gt;Nginx&lt;/span&gt; &lt;span class="s"&gt;deployment,&lt;/span&gt; &lt;span class="s"&gt;does&lt;/span&gt; &lt;span class="s"&gt;not&lt;/span&gt; &lt;span class="s"&gt;mean&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt; &lt;span class="s"&gt;instances&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="s"&gt;metadata_buffer:set(&amp;#39;serviceInstanceName&amp;#39;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;User&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt; &lt;span class="s"&gt;Instance&lt;/span&gt; &lt;span class="s"&gt;Name&amp;#39;)&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="c1"&gt;#这是你的skywalking server地址
&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="s"&gt;require(&amp;#34;client&amp;#34;):startBackendTimer(&amp;#34;http://127.0.0.1:12800&amp;#34;)&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="err"&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&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="s"&gt;server&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="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8080&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;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/ingress&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;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="s"&gt;text/html&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;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;rewrite_by_lua_block&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;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;NOTICE,&lt;/span&gt; &lt;span class="s"&gt;this&lt;/span&gt; &lt;span class="s"&gt;should&lt;/span&gt; &lt;span class="s"&gt;be&lt;/span&gt; &lt;span class="s"&gt;changed&lt;/span&gt; &lt;span class="s"&gt;manually&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;This&lt;/span&gt; &lt;span class="s"&gt;variable&lt;/span&gt; &lt;span class="s"&gt;represents&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;logic&lt;/span&gt; &lt;span class="s"&gt;address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Please&lt;/span&gt; &lt;span class="s"&gt;set&lt;/span&gt; &lt;span class="s"&gt;them&lt;/span&gt; &lt;span class="s"&gt;as&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt; &lt;span class="s"&gt;logic&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="s"&gt;or&lt;/span&gt; &lt;span class="s"&gt;DNS&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Currently,&lt;/span&gt; &lt;span class="s"&gt;we&lt;/span&gt; &lt;span class="s"&gt;can&lt;/span&gt; &lt;span class="s"&gt;not&lt;/span&gt; &lt;span class="s"&gt;have&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;real&lt;/span&gt; &lt;span class="s"&gt;network&lt;/span&gt; &lt;span class="s"&gt;address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):start(&amp;#34;upstream&lt;/span&gt; &lt;span class="s"&gt;service&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;If&lt;/span&gt; &lt;span class="s"&gt;you&lt;/span&gt; &lt;span class="s"&gt;want&lt;/span&gt; &lt;span class="s"&gt;correlation&lt;/span&gt; &lt;span class="s"&gt;custom&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt; &lt;span class="s"&gt;to&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;downstream&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):start(&amp;#34;upstream&lt;/span&gt; &lt;span class="s"&gt;service&amp;#34;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;custom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;custom_value&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 这是你的目标下游服务，比如java的微服务网关
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8080/backend&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;41&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;body_filter_by_lua_block&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;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;ngx.arg[2]&lt;/span&gt; &lt;span class="s"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):finish()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;log_by_lua_block&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;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;require(&amp;#34;tracer&amp;#34;):prepareForReport()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是几个监控到的nginx数据的截图&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/006-b2997720.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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/007-6e313138.png"&gt;&lt;/p&gt;
&lt;p&gt;至此我们就完成了整个链路的监控。&lt;/p&gt;
&lt;h2 id="skywalking-的参数配置"&gt;&lt;a href="#skywalking-%e7%9a%84%e5%8f%82%e6%95%b0%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;skywalking 的参数配置
&lt;/h2&gt;&lt;h3 id="一些中文文档"&gt;&lt;a href="#%e4%b8%80%e4%ba%9b%e4%b8%ad%e6%96%87%e6%96%87%e6%a1%a3" class="header-anchor"&gt;&lt;/a&gt;一些中文文档
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;agent的文档[3]&lt;/li&gt;
&lt;li&gt;ui的文档[4]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="通过修改agentconfigagenetconfig-文件得到的能力"&gt;&lt;a href="#%e9%80%9a%e8%bf%87%e4%bf%ae%e6%94%b9agentconfigagenetconfig-%e6%96%87%e4%bb%b6%e5%be%97%e5%88%b0%e7%9a%84%e8%83%bd%e5%8a%9b" class="header-anchor"&gt;&lt;/a&gt;通过修改agent/config/agenet.config 文件得到的能力
&lt;/h3&gt;&lt;p&gt;根据文档 &lt;code&gt;https://github.com/apache/skywalking/blob/v8.0.0/docs/en/setup/service-agent/java-agent/README.md&lt;/code&gt; 得知&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 可以获取 sql中的参数，默认是获取不到的。当然还要设置参数最大长度。但获取参数有可能引起性能问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;property key&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;plugin.mysql.trace_sql_parameters&lt;/td&gt;
 &lt;td&gt;If set to true, the parameters of the sql (typically java.sql.PreparedStatement) would be collected.&lt;/td&gt;
 &lt;td&gt;false&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;plugin.mysql.sql_parameters_max_length&lt;/td&gt;
 &lt;td&gt;If set to positive number, the db.sql.parameters would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.&lt;/td&gt;
 &lt;td&gt;512&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;2 收集http参数&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;#收集SpringMVC plugin插件请求参，在tomcat上时这俩设置一个即可plugin.tomcat.collect_http_params or plugin.springmvc.collect_http_params
&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; plugin.springmvc.collect_http_params=true
&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&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; plugin.http.http_params_length_threshold=1024
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;3 skywalking-oap 的配置文件中关于数据存储时长的配置&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE:default}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Mixed: Receive agent data, Level 1 aggregate, Level 2 aggregate&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Receiver: Receive agent data, Level 1 aggregate&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Aggregator: Level 2 aggregate&lt;/span&gt;&lt;span class="w"&gt;
&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_ROLE:Mixed}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Mixed/Receiver/Aggregator&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_HOST:0.0.0.0}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_PORT:12800}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restContextPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_CONTEXT_PATH:/}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_HOST:0.0.0.0}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_PORT:11800}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_ENABLED:false}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslKeyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_KEY_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslCertChainPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_CERT_CHAIN_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslTrustedCAPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_TRUSTED_CA_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;downsampling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Hour&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Day&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Month&lt;/span&gt;&lt;span class="w"&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="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Set a timeout on metrics data. After the timeout has expired, the metrics data will automatically be deleted.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enableDataKeeperExecutor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Turn it off then automatically metrics data delete will be close.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dataKeeperExecutePeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# How often the data keeper executor runs periodically, unit is minute&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;recordDataTTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_RECORD_DATA_TTL:3}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Unit is day&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;metricsDataTTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_RECORD_DATA_TTL:7}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Unit is day&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;主要是这四行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # Turn it off then automatically metrics data delete will be close.
&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;dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # How often the data keeper executor runs periodically, unit is minute
&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;recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3} # Unit is day
&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;metricsDataTTL: ${SW_CORE_RECORD_DATA_TTL:7} # Unit is day
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="skywalking-ui-监控视角与指标介绍"&gt;&lt;a href="#skywalking-ui-%e7%9b%91%e6%8e%a7%e8%a7%86%e8%a7%92%e4%b8%8e%e6%8c%87%e6%a0%87%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;skywalking UI 监控视角与指标介绍
&lt;/h2&gt;&lt;h3 id="cpm-每分钟请求数"&gt;&lt;a href="#cpm-%e6%af%8f%e5%88%86%e9%92%9f%e8%af%b7%e6%b1%82%e6%95%b0" class="header-anchor"&gt;&lt;/a&gt;cpm 每分钟请求数
&lt;/h3&gt;&lt;p&gt;cpm 全称 call per minutes，是吞吐量(Throughput)指标。下图是拼接的全局、服务、实例和接口的吞吐量及平均吞吐量。&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/008-10d14c3d.png"&gt;&lt;/p&gt;
&lt;p&gt;第一条185cpm=185/60=3.08个请求/秒。&lt;/p&gt;
&lt;h3 id="sla-服务等级协议"&gt;&lt;a href="#sla-%e6%9c%8d%e5%8a%a1%e7%ad%89%e7%ba%a7%e5%8d%8f%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;SLA 服务等级协议
&lt;/h3&gt;&lt;p&gt;SLA 全称 Service-Level Agreement，直译为 “服务等级协议”，用来表示提供服务的水平。在IT中，SLA可以衡量平台的可用性，下面是N个9的计算：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1年 = 365天 = 8760小时&lt;/li&gt;
&lt;li&gt;99 = 8760 * 1% =&amp;gt; 3.65天&lt;/li&gt;
&lt;li&gt;99.9 = 8760 * 0.1% =&amp;gt; 8.76小时&lt;/li&gt;
&lt;li&gt;99.99 = 8760 * 0.01% =&amp;gt; 52.6分钟&lt;/li&gt;
&lt;li&gt;99.999 = 8760 * 0.001% =&amp;gt; 5.26分钟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，全年只要发生一次较大规模宕机事故，4个9肯定没戏，一般平台3个9差不多。但2个9就基本不可用了，相当于全年有87.6小时不可用，每周(一个月按4周算)有1.825小时不可用。下图是服务、实例、接口的SLA，一般看年度、月度即可。&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/009-27e40c81.png"&gt;&lt;/p&gt;
&lt;h3 id="percent-response-百分位数统计"&gt;&lt;a href="#percent-response-%e7%99%be%e5%88%86%e4%bd%8d%e6%95%b0%e7%bb%9f%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;Percent Response 百分位数统计
&lt;/h3&gt;&lt;p&gt;表示采集样本中某些值的占比，Skywalking 有 &lt;code&gt;p50、p75、p90、p95、p99&lt;/code&gt; 一些列值。其中的 “p99:390” 表示 99% 请求的响应时间在390ms以内。而99%一般用于抛掉一些极端值，表示绝大多数请求。&lt;/p&gt;
&lt;h3 id="slow-endpoint-慢端点"&gt;&lt;a href="#slow-endpoint-%e6%85%a2%e7%ab%af%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;Slow Endpoint 慢端点
&lt;/h3&gt;&lt;p&gt;Endpoint 表示具体的服务，例如一个接口。下面是全局Top N的数据，通过这个可以观测平台性能情况。&lt;/p&gt;
&lt;h3 id="heatmap-热力图"&gt;&lt;a href="#heatmap-%e7%83%ad%e5%8a%9b%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;Heatmap 热力图
&lt;/h3&gt;&lt;p&gt;Heapmap 可译为热力图、热度图都可以，其中颜色越深，表示请求数越多，这和GitHub Contributions很像，commit越多，颜色越深。横坐标是响应时间，鼠标放上去，可以看到具体的数量。通过热力图，一方面可以直观感受平台的整体流量，另一方面也可以感受整体性能。&lt;/p&gt;
&lt;h3 id="apdex"&gt;&lt;a href="#apdex" class="header-anchor"&gt;&lt;/a&gt;apdex
&lt;/h3&gt;&lt;p&gt;是一个衡量服务器性能的标准。apdex有三个指标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;满意：请求响应时间小于等于T。&lt;/li&gt;
&lt;li&gt;可容忍：请求响应时间大于T，小于等于4T。&lt;/li&gt;
&lt;li&gt;失望：请求响应时间大于4T。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;T：自定义的一个时间值，比如：500ms。apdex = （满意数 + 可容忍数/2）/ 总数。例如：服务A定义T=200ms，在100个采样中，有20个请求小于200ms，有60个请求在200ms到800ms之间，有20个请求大于800ms。计算apdex = (20 + 60/2)/100 = 0.5。&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/010-d79d1a09.png"&gt;&lt;/p&gt;
&lt;h2 id="一些很有用的点"&gt;&lt;a href="#%e4%b8%80%e4%ba%9b%e5%be%88%e6%9c%89%e7%94%a8%e7%9a%84%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;一些很有用的点
&lt;/h2&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/011-c660509c.png"&gt;&lt;/p&gt;
&lt;p&gt;在拓扑图中&lt;/p&gt;
&lt;p&gt;红色代表当前节点的请求有一段时间内是响应异常的。当节点全部变红的时候证明服务现阶段内就彻底不可用了。我们可以通过Topology迅速发现某一个服务潜在的问题，并进行下一步的排查并做到预防。&lt;/p&gt;
&lt;p&gt;仔细看线是有流向的，有单向和双向的，单向有从左至右的或从右至左的，这样你就知道你的服务是谁依赖了谁。双向的就证明你的服务有循环引用依赖问题。&lt;/p&gt;
&lt;p&gt;在最新版本8.1中有endpoint端口依赖的分析，可以分析出接口级别的依赖关系，可以知道某接口是被谁调用，它又调用了谁。&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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/012-b97ccb7d.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-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/013-daacb3be.gif"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;
&lt;h3 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h3&gt;&lt;p&gt;[1]&lt;/p&gt;
&lt;p&gt;skywalking官方文档: &lt;em&gt;&lt;a class="link" href="https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md" target="_blank" rel="noopener"
 &gt;https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[2]&lt;/p&gt;
&lt;p&gt;skywalking-nginx-lua项目地址: &lt;em&gt;&lt;a class="link" href="https://github.com/apache/skywalking-nginx-lua/" target="_blank" rel="noopener"
 &gt;https://github.com/apache/skywalking-nginx-lua/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[3]&lt;/p&gt;
&lt;p&gt;skywalking-agent文档: &lt;em&gt;&lt;a class="link" href="https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/setup/service-agent/java-agent/" target="_blank" rel="noopener"
 &gt;https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/setup/service-agent/java-agent/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[4&lt;/p&gt;
&lt;p&gt;skywalking-ui 文档: &lt;em&gt;&lt;a class="link" href="https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/ui/" target="_blank" rel="noopener"
 &gt;https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/ui/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>