<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://www.higlowx.com/blog</id>
    <title>Higlowx Blog</title>
    <updated>2021-07-14T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://www.higlowx.com/blog"/>
    <subtitle>Higlowx Blog</subtitle>
    <icon>https://www.higlowx.com/img/favicon.png</icon>
    <entry>
        <title type="html"><![CDATA[Spring Cloud Alibaba 微服务解决方案如何落地 | 初探]]></title>
        <id>how-implement-spring-cloud-alibaba-microservices-solution-1</id>
        <link href="https://www.higlowx.com/blog/how-implement-spring-cloud-alibaba-microservices-solution-1"/>
        <updated>2021-07-14T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[概述]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_LWe7" id="概述">概述<a class="hash-link" href="#概述" title="标题的直接链接">​</a></h2><p>Spring Cloud Alibaba 衍生自 Spring Cloud Community， 其已经通过 Spring 认证并被收录在 <a href="https://spring.io/projects/spring-cloud-alibaba" target="_blank" rel="noopener noreferrer">官方网站</a> 中。根据阿里的介绍，这套微服务解决方案中包括了一部分开源组件和一部分阿里云商业化产品。这次我们要上手的将只包含开源部分，对于一些未提供的能力，比如网关服务，将结合 Spring Cloud Community 提供补充，上手过程中会基于本人目前的经验，对其中相关知识点做出简单总结。</p><p>围绕开源生态，探究如何将这套微服务方案落地到生产中，是本篇的核心目的。作为初探篇，将着重于应用代码侧的接入，很多设计是结合现有比较常见的开发生产习惯决定的。对于其中的各类中间件环境如何实现 HA，本篇将暂不探讨，有兴趣的可以从本篇末尾获得查看官方建议的链接。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="主要功能">主要功能<a class="hash-link" href="#主要功能" title="标题的直接链接">​</a></h3><ul><li><strong>流控与服务降级（Flow Control and service degradation）</strong>：默认支持 WebServlet、WebFlux, OpenFeign、RestTemplate、Spring Cloud Gateway, Zuul, Dubbo 和 RocketMQ 限流降级功能的接入，可以在运行时通过控制台实时修改限流降级规则，还支持查看限流降级 Metrics 监控。</li><li><strong>服务注册与发现（Service registration and discovery）</strong>：适配 Spring Cloud 服务注册与发现标准，默认集成了 Ribbon 负载均衡的支持。</li><li><strong>分布式配置管理（Distributed configuration）</strong>：支持分布式系统中的外部化配置，配置更改时自动刷新。</li><li><strong>事件驱动（Event-driven）</strong>：基于分布式消息实现构建高性能事件驱动的微服务体系。</li><li><strong>分布式事务（Distributed Transaction）</strong>：高效并且对业务零侵入地解决分布式事务问题。</li><li><strong>微服务网关（Microservices Gateway）</strong>：为整套内部微服务链路提供一个可靠且高性能的对外网关，并配套流控、路由、鉴权等能力。</li><li><strong>服务间远程过程调用（Remote Procedure Call）</strong>：提供可靠方便的RPC框架，实现各个系统间就像在调用本地程序一样地调用远程服务。</li></ul><h3 class="anchor anchorWithStickyNavbar_LWe7" id="工程简介">工程简介<a class="hash-link" href="#工程简介" title="标题的直接链接">​</a></h3><p>在这次上手实践过程中，我们临时不会接入基于消息的异步事件驱动功能。同时我们需要引入一种常见的业务场景，以达到辅助理解的作用。这里将以交易场景为例构建工程雏形，我们假设交易系统基于第三方支付服务商、银行等聚合，并主要有以下两个模块：</p><ul><li>Trade：交易模块，是整个交易系统的入口，为每一次交易动作的发起提供支撑，无论这笔交易的最终结果如何</li><li>Bill：账单模块，是整个交易系统的后置节点，为每一笔成功的交易生成流水凭证，并将和Trade资源有机的关联起来</li></ul><p>基于以上两个模块，我们还需要对基础的支付流程有一个大致的了解，假设我们的系统只有简单的线性同步调用模型，以某个用户的某次成功付款为例，流程如下：</p><p><img loading="lazy" alt="one_trade.jpg" src="/assets/images/one_trade-5886fd64c1c45a0564744365e08a17cd.jpg" width="721" height="391" class="img_ev3q"></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="组件版本">组件版本<a class="hash-link" href="#组件版本" title="标题的直接链接">​</a></h3><table><thead><tr><th>组件</th><th>版本</th><th>功能</th></tr></thead><tbody><tr><td>Spring Cloud Alibaba</td><td>2.2.6.RC1</td><td>以pom方式接入，用于各类client中的依赖引入和版本控制</td></tr><tr><td>Nacos server</td><td>1.4.2</td><td>服务注册发现与分布式配置中心</td></tr><tr><td>Sentinel dashboard</td><td>1.8.1</td><td>流控、降级与流量指标监控控制台</td></tr><tr><td>Seata server</td><td>1.3.0</td><td>分布式事务TC</td></tr><tr><td>Spring Cloud Community</td><td>Spring Cloud Hoxton.SR9</td><td>以pom方式接入，用于各类社区版组件client中的依赖引入和版本控制</td></tr><tr><td>Spring Cloud Gateway Server</td><td>2.2.6.RELEASE</td><td>微服务网关</td></tr><tr><td>Spring Cloud OpenFeign</td><td>2.2.6.RELEASE</td><td>RPC调用组件，未使用官方推荐的Dubbo</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="代码模块">代码模块<a class="hash-link" href="#代码模块" title="标题的直接链接">​</a></h3><p>基于上述最简单的交易场景模型，并结合要使用的微服务组件，我们将实际的代码划分成了以下模块：</p><ul><li><strong>ebpp-common</strong>： 公共包，用于存储URI、常量、工具等。</li><li><strong>ebpp-gateway</strong> ：网关服务，使用 Spring Cloud Gateway 完成统一鉴权、负载均衡等。</li><li><strong>ebpp-nacos-server</strong>： Nacos服务，用作服务注册发现、服务管理、服务路由、配置中心等。</li><li><strong>ebpp-sentinel-dashboard</strong>： Sentinel 控制台服务。</li><li><strong>ebpp-seata-server</strong>：Seata TC 服务，进行分布式事务协调。</li><li><strong>ebpp-xxx-route</strong>： 路径路由，用于各服务API接口的定义，携带URI、出入参等，便于其他服务引用，以及解决URI变化造成其他调用者无法获知导致调用失败的问题。</li><li><strong>ebpp-service-trade</strong>： 交易服务。</li><li><strong>ebpp-service-bill</strong>： 账单服务，与支付系统配合模拟强一致性分布式事务，使用seata，基于全局事务管理理论（两阶段提交）变种出的AT模式。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="细节">细节<a class="hash-link" href="#细节" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="依赖管理">依赖管理<a class="hash-link" href="#依赖管理" title="标题的直接链接">​</a></h3><p>出于对依赖版本的规范化，我们推荐使用下面的的结构：</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">— project root</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">— — module A</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">— — — pom.xml of module A</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">— — module B</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">— — — pom.xml of module B</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">—  pom.xml of full project</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>相应的，需要在 pom.xml of full project 的 <strong>dependencyManagement</strong> 标签中引入以下配置，进而规范 module 内部各种 Spring Cloud Alibaba、Spring Cloud Community 依赖的版本：</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- spring cloud alibaba--&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-alibaba-dependencies</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">2.2.6.RC1</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">pom</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">import</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- spring cloud community --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.springframework.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-dependencies</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">Hoxton.SR9</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">version</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">pom</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">type</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">import</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">scope</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>另外，需要提前说明一下业务代码包的核心依赖关系，以免存在误解：</p><div class="codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">ebpp-service-xxx 依赖于 ebpp-xxx-route 依赖于 ebpp-common</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="nacos">Nacos<a class="hash-link" href="#nacos" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="名词理解">名词理解<a class="hash-link" href="#名词理解" title="标题的直接链接">​</a></h4><p>Nacos 中定义了一些专用概念，来规范其技术使用，我们可以试着从 Nacos 的两个核心功能（服务管理、配置管理）分别去理解以下的几个名词概念：</p><ul><li><strong>命名空间（Namespace）</strong>：用于进行租户粒度的配置隔离。不同的命名空间下，可以存在相同的Group或DataID的配置。Namespace的常用场景之一是不同环境的配置的区分隔离，例如开发测试环境和生产环境的资源（如配置、服务）隔离等。</li><li><strong>服务分组（Group）</strong>：不同的服务可以归类到同一分组。本人理解的是，可以用来定义同多种服务同属于一个项目集的标识，比如订单微服务、库存微服务同属于电商系统，前者可以拥有相同的服务分组。</li><li><strong>配置项</strong>：一个具体的可配置参数与其值域，通常以 param-key=param-value 的形式存在。例如我们常配置系统的日志输出级别（logLevel=INFO|WARN|ERROR）就是一个配置项。</li><li><strong>配置集</strong>：一组相关或者不相关的配置项的集合称为配置集。在系统中，一个配置文件通常就是一个配置集，包含了系统各个方面的配置。例如，一个配置集可能包含了数据源、线程池、日志级别等配置项。</li><li><strong>配置集ID（DataId）</strong>：Nacos 中的某个配置集的 ID。配置集 ID 是组织划分配置的维度之一。Data ID 通常用于组织划分系统的配置集。一个系统或者应用可以包含多个配置集，每个配置集都可以被一个有意义的名称标识。Data ID 通常采用类 Java 包（如 com.taobao.tc.refund.log.level）的命名规则保证全局唯一性。此命名规则非强制。</li><li><strong>配置分组</strong>：区别于服务分组，配置分组是 Nacos 中的一组配置集，是组织配置的维度之一，每个配置分组都有一个唯一的 Group Id。一个 Group Id 下可以有若干个 Data Id，这样做不仅对配置集进行了分组，同时也区分了 Data Id 相同的配置集。举个例子，订单服务和商品服务内部都要配置有自己的数据源链接（datasource-url），按照配置集ID的命名建议，如果它们的 Data Id 都是 spring.datasource.druid.url，在配置进行集中管理时就会存在冲突，这时候如果让它们分别从属于 ORDER_GROUP 和 PRODUCT_GROUP 这两个 Group Id 下，就可以避免冲突问题。</li></ul><h4 class="anchor anchorWithStickyNavbar_LWe7" id="nacos-client">Nacos Client<a class="hash-link" href="#nacos-client" title="标题的直接链接">​</a></h4><p>在官方介绍中我们了解到，Nacos 提供了<strong>服务注册发现</strong>和<strong>配置中心</strong>两大功能，所以 Nacos 的客户端也被分为了 <strong>nacos-discovery</strong> 和 <strong>nacos-config</strong> 两种。我们只需要在接入的服务中，引入对应的 Spring Cloud Starter 依赖并在启动所需配置文件中进行简单配置，就可完成集成。</p><p>以 ebpp-service-trade 为例，先要在 pom.xml of ebpp-service-trade 引入 stater 依赖：</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-nacos-discovery</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-nacos-config</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后在 bootstrap.properties 中加入如下配置就可以了：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 最好配置应用名</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.application.name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-service-trade</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># nacos-discovery</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># 这里必须配置命名空间的空间ID，不可是名称</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># spring.cloud.nacos.discovery.namespace=f1140e5e-0608-4f9c-b578-d34820f32068</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.discovery.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.discovery.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">EBPP_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># nacos-config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.file-extension</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">properties</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">DEFAULT_GROUP</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="在日常编码工作中怎样的配置架构更合理">在日常编码工作中，怎样的配置架构更合理<a class="hash-link" href="#在日常编码工作中怎样的配置架构更合理" title="标题的直接链接">​</a></h4><ul><li><strong>关于多环境配置（以 dev、test、prod 三种环境为例）</strong>：dev 环境下，我们的目的是更直观、更方便的读写配置文件；test 和 prod 环境下，出于安全运维的考虑，一部分重要的或者全部的应用配置一般都会由配置中心统一管理</li><li><strong>关于配置文件的加载优先级</strong>：一般的情况下（指没有进行特殊指定时），bootstrap 优先于 application，properties 优先于 yml，resources/config 优先于 resources/</li><li><strong>关于服务配置的实时动态更新</strong>：目前以我的认知，更倾向于大部分配置启动时加载，小部分配置实时更新的策略。</li><li><strong>关于相同环境下配置文件的拆分</strong>：我的观点是尽量少拆分直至不拆分。因为现在很少会出现一个微服务应用（或一个可执行 JAR 包）由多个团队（或许多许多人）开发维护的情形，所以就很少出现相同环境下有许多个配置文件（配置集）的情况。 由于很少出现，如果继续将原来1~2个配置文件拆分成若干个分工明确的小型配置文件，反而会增加日常的维护成本，增大编码的复杂度。同样的，如果不需要特别细分的配置权限管理，依然去拆分成许多个小的配置子集，其实也是不合理的。那么有人会质疑，之所以做拆分，是因为要实现修改中心配置时，微服务应用内部可以实时地动态更新，如果拆分的话，会减轻应用每次更新配置集时的资源消耗。其实并非如此，之所以会有这样的顾虑，是因为还没有理解接入 Nacos 配置中心的工作原理，假如我们在 Nacos 端修改了某一个 key 的配置，对应的微服务应用其实只会更新这一条配置，以及依赖它的某几个 Bean （如果需要）。配置的更新并非是文件维度的，而是 key 维度，所以说拆分可以降低资源消耗这点也许就是个伪命题。下图也可以简单看出配置动态更新中的端倪。</li></ul><p><img loading="lazy" alt="nacos-1-1.png" src="/assets/images/nacos-1-1-f041573bbe6fee3ffff2c46d03c56f1f.png" width="1540" height="82" class="img_ev3q"></p><p>综上观点，我们的工程可以采用的配置架构可以是下面这个样子的</p><p><img loading="lazy" alt="nacos-1-2.png" src="/assets/images/nacos-1-2-5a68272d199d7f7a6933bafb24d931df.png" width="500" height="148" class="img_ev3q"></p><p>bootstrap.properties 文件负责多环境、应用的基础信息、默认启动端口等配置。例如：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key attr-name" style="color:rgb(241, 250, 140)">server.port</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">9901</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.profiles.active</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">dev</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.application.name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-service-trade</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>bootstrap-dev.properties 文件负责日常开发环境的所有配置，注意是在本地，并非远程配置中心。</p><p>bootstrap-prod.properties 文件负责设置生产环境的远程配置中心，生产的所有配置在远端（test 环境同理）。例如：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.file-extension</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">properties</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.config.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">DEFAULT_GROUP</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="nacos-配置中的-data-id-如何与某个微服务应用关联起来">Nacos 配置中的 Data Id 如何与某个微服务应用关联起来<a class="hash-link" href="#nacos-配置中的-data-id-如何与某个微服务应用关联起来" title="标题的直接链接">​</a></h4><p>在 Nacos Spring Cloud 中，Data Id 的完整格式如下：</p><div class="language-txt codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-txt codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">${prefix}-${spring.profiles.active}.${file-extension}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>prefix 默认为 spring.application.name 的值，也可以通过配置项 spring.cloud.nacos.config.prefix 来配置。</p><p>spring.profiles.active 即为当前环境对应的 profile。注意：当 spring.profiles.active 为空时，对应的连接符 - 也将不存在，Data Id 的拼接格式变成 ${prefix}.${file-extension}。</p><p>file-exetension 为配置内容的数据格式，可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型。</p><p>以上一个问题中的配置为例，Nacos 端生产环境的 Data Id 应该是 ebpp-service-trade-prod.properties（.properties 必须要有）</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="sentinel">Sentinel<a class="hash-link" href="#sentinel" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="sentinel-client">Sentinel Client<a class="hash-link" href="#sentinel-client" title="标题的直接链接">​</a></h4><p>关于 Sentinel 客户端在各个服务中的集成，以 ebpp-service-bill 为例，概括来看，主要有这么几个部分：</p><ul><li><strong>Sentinel Client 与 Sentinel Dashboard 的连接配置</strong></li></ul><p>首先是依旧是依赖的引入，即在需要接入限流、熔断、降级的服务中引入 Spring Cloud Starter 依赖：</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-sentinel</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>其次需要在 bill 服务的 bootstrap.properties 中做如下简单配置，这样在应用启动后，客户端与控制台就会建立其连接：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#是否提前触发sentinel初始化(sentinel会在客户端首次调用的时候进行初始化，开始向控制台发送心跳包)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.eager</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#应用与sentinel控制台交互的端口，应用本地会起一个该端口占用的HttpServer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.port</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">8719</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#sentinel控制台地址</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.dashboard</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">localhost:8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#应用与sentinel控制台的心跳间隔时间</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.heartbeat-interval-ms</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">5000</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li><strong>Sentinel 限流熔断和降级规则的持久化配置</strong></li></ul><p>默认情况下 Sentinel Client 的各种限流降级规则是只被保存在客户端所在的 JVM 实例中（即内存中），这样的后果就是，一旦 JVM 重启，所有配置好的规则都将丢失，这种情况在生产环境是不可能被允许的，必须要有对应的持久化措施，官方推荐<strong>通过控制台设置规则后将规则推送到统一的规则中心，客户端实现 ReadableDataSource 接口端监听规则中心实时获取变更</strong>，流程如下：</p><p><img loading="lazy" alt="sentinel-1-1" src="/assets/images/sentinel-1-1-97fdd6a07489ac138ded68614bee83f3.png" width="1564" height="926" class="img_ev3q"></p><p>DataSource 扩展常见的实现方式有:</p><ol><li><strong>拉模式</strong>：客户端主动向某个规则管理中心定期轮询拉取规则，这个规则中心可以是 RDBMS、文件，甚至是 VCS 等。这样做的方式是简单，缺点是无法及时获取变更；</li><li><strong>推模式</strong>：规则中心统一推送，客户端通过注册监听器的方式时刻监听变化，比如使用 <a href="https://github.com/alibaba/nacos" target="_blank" rel="noopener noreferrer">Nacos</a>、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。</li></ol><p>我们这里将要采用官方推荐的 Nacos Push 模式，与官方网站上描述的不同，我们不会手动去实现客户端的 ReadableDataSource 接口，而是使用更简单快捷的 Spring Cloud Starter 方案。</p><p>先在 pom.xml of ebpp-service-bill 中引入依赖：</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- 这里为什么依然说是 Spring Cloud Starter，是因为</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">在 spring-cloud-starter-alibaba-sentinel 依赖中有针对本依赖的 &lt;optional&gt;true&lt;/optional&gt; 标注，</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">表示其为可选择引入的，至于为什么要选择，因为有很多持久化方案。没错，你要选一个，总不能默认全引进来吧 --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.csp</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">sentinel-datasource-nacos</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>接着就是 bootstrap.properties 的配置，注意多种限流类型是可以分开持久化的，可以使用 <strong>spring.cloud.sentinel.datasource.${name}.nacos.${property}</strong> 这种方式隔离开，其中的 name 可以自定义指定，最好与 rule-type 属性有一定的关联性，方便开发维护（只是本人建议）。</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#sentinel持久化至nacos config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.flow.nacos.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.flow.nacos.group-id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">DEFAULT_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.flow.nacos.data-id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">com.higlowx.scal.ebpp.service.bill.sentinel.flow</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.flow.nacos.rule-type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">flow</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.degrade.nacos.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.degrade.nacos.group-id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">DEFAULT_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.degrade.nacos.data-id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">com.higlowx.scal.ebpp.service.bill.sentinel.degrade</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.datasource.degrade.nacos.rule-type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">degrade</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>最后，还有一个值得我们以后去思考如何实现的问题，在控制台界面手动配置 Sentinel 规则的方式很难在生产场景大规模应用，人们需要更加高效的方式去完成配置集的初始化和动态更新。</p><ul><li><strong>配置 RPC 框架 Feign 对Sentinel 的支持</strong></li></ul><p>关于这个支持项的配置，只需要在引入 Feign 依赖并完成基本配置后，加入下边这行这只即可 ：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key attr-name" style="color:rgb(241, 250, 140)">feign.sentinel.enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">true</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="feign-client-在开启-sentinel-支持后是怎样做到在消费者一方处理生产者返回的-sentinel-异常的">Feign Client 在开启 Sentinel 支持后，是怎样做到在消费者一方处理生产者返回的 Sentinel 异常的<a class="hash-link" href="#feign-client-在开启-sentinel-支持后是怎样做到在消费者一方处理生产者返回的-sentinel-异常的" title="标题的直接链接">​</a></h4><p>我们都知道，健壮的系统架构一定会有良好的异常处理逻辑，是针对特定异常做出特定处理的逻辑。首先我们要明确 Sentinel 的异常应在生产者抛出，并被消费者捕获处理（或直接不处理），整个异常处理是跨进程的。</p><p>我们先看生产者一方，Sentinel Client 之所以可以工作，其限流、熔断和降级等，这些特殊处理一定是在进入代码业务逻辑之前进行，否则就没有限制请求的意义了，所以一定会有请求被拦截的逻辑，同样的异常的抛出也需要在这个层面。</p><p>基于这个初始判断，我们继续思考。基于 Spring Cloud 体系，怎样的方法可以做到拦截请求？如果是我，首先会想到以下两种：</p><ul><li>基于 Spring 的 Interceptor 拦截；</li><li>基于默认 Tomcat 容器的 Filter 拦截；</li></ul><p>如果是你，你会选哪种呢？好吧，我们现在只是大略的追一下 Sentinel 源码，希望可以看出其中的端倪。</p><p>我们通过 Client 中的 Sentinel 配置项，可以直接反追出 SentinelProperties 配置属性实例被 SentinelWebAutoConfiguration 配置类实例所持有使用。SentinelWebAutoConfiguration 类实现了 WebMvcConfigurer 接口并重写了 addInterceptors 方法，看到这里我们基本可以确定 Sentinel Client 实现拦截请求是基于 Spring 的 Interceptor，并且可以看到，在向 Spring 注入 SentinelWebInterceptor 拦截器时，为其绑定了一个高级别的 Order 属性，以保证该拦截器在所有拦截器的最外层工作。我们再简单看一下 SentinelWebAutoConfiguration 持有的对象、属性：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Autowired</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">Optional</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">UrlCleaner</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> urlCleanerOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Autowired</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">Optional</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">BlockExceptionHandler</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> blockExceptionHandlerOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Autowired</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">Optional</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">RequestOriginParser</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> requestOriginParserOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Autowired</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">Optional</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">SentinelWebInterceptor</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> sentinelWebInterceptorOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>urlCleanerOptional 和 requestOriginParserOptional 从字面意思上应该分别负责 Spring 容器内 URL 的清洗整理 和 请求源解析处理，我们暂时先不研究它们。blockExceptionHandlerOptional 是为开发者提供的自定义处理 BlockException（Sentinel 默认异常） 的机制，我们暂不使用，直接走默认处理。关注点锁定 sentinelWebInterceptorOptional ，其内部是SentinelWebInterceptor 拦截器，该拦截器是所有规则处理的关键所在，其核心配置是 SentinelWebMvcConfig 类，在自动装配 SentinelWebMvcConfig 实例时的源代码是下面这样的：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Bean</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@ConditionalOnProperty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"spring.cloud.sentinel.filter.enabled"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        matchIfMissing </span><span class="token operator">=</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">SentinelWebMvcConfig</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">sentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">SentinelWebMvcConfig</span><span class="token plain"> sentinelWebMvcConfig </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">SentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setHttpMethodSpecify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getHttpMethodSpecify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    sentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setWebContextUnify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getWebContextUnify</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">blockExceptionHandlerOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">isPresent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        blockExceptionHandlerOptional</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ifPresent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">sentinelWebMvcConfig</span><span class="token operator">::</span><span class="token function" style="color:rgb(80, 250, 123)">setBlockExceptionHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">else</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">StringUtils</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">hasText</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getBlockPage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            sentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setBlockExceptionHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                    e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token operator">-&gt;</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">sendRedirect</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">properties</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getBlockPage</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">else</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            sentinelWebMvcConfig</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setBlockExceptionHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">DefaultBlockExceptionHandler</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    urlCleanerOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ifPresent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">sentinelWebMvcConfig</span><span class="token operator">::</span><span class="token function" style="color:rgb(80, 250, 123)">setUrlCleaner</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    requestOriginParserOptional</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">ifPresent</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">sentinelWebMvcConfig</span><span class="token operator">::</span><span class="token function" style="color:rgb(80, 250, 123)">setOriginParser</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> sentinelWebMvcConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>可以看出，如果不设置自定异常处理和自定义 Block Page 的情况下，BlockException 的处理将交由 DefaultBlockExceptionHandler 负责。其源码如下：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">DefaultBlockExceptionHandler</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">BlockExceptionHandler</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">handle</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">HttpServletRequest</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">HttpServletResponse</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">BlockException</span><span class="token plain"> e</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name">Exception</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">// Return 429 (Too Many Requests) by default.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">setStatus</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token number">429</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">PrintWriter</span><span class="token plain"> out </span><span class="token operator">=</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getWriter</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">print</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Blocked by Sentinel (flow limiting)"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">flush</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">close</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>情况一目了然，默认情况下，生产者向消费者返回 429 状态码，以及 Blocked by Sentinel (flow limiting) 字样的描述信息。</p><p>看完生产者，我们在看一下消费者一方。消费者通过 Feign Client 调用服务，而 Feign Client 又需要兼容 Sentinel ， 我们知道将 feign.sentinel.enabled 属性设置为 true 后，会开启兼容，该属性的描述是这样的：</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">If true, an OpenFeign client will be wrapped with a Sentinel circuit breaker.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>原来，消费者端的 Sentinel Client 将 Feign Client 又包装了一层，这样就可以针对生产者返回的 429 状态码做出特定的处理，并适配 Feign Client 的异常逻辑或者 fallback 逻辑了，相关源码可以以后再详细探讨。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="sentinel-官方提供了基于-spi-的-datasource-自动注册工具类-initfunc-其应用场景是什么">Sentinel 官方提供了基于 SPI 的 Datasource 自动注册工具类 InitFunc ，其应用场景是什么<a class="hash-link" href="#sentinel-官方提供了基于-spi-的-datasource-自动注册工具类-initfunc-其应用场景是什么" title="标题的直接链接">​</a></h4><p>根据<a href="https://sentinelguard.io/zh-cn/docs/dynamic-rule-configuration.html" target="_blank" rel="noopener noreferrer">官方说明</a> ，我们做了以下实验（源代码已被删除）。</p><p>在 com.higlowx.scal.ebpp.service.bill.config.spi 包下创建自定义的SPI实现类。</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">SentinelDataSourceInitFunc</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">InitFunc</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">//尝试使用国际化资源读取，获取配置文件中的属性值，出现报错，SentinelDataSourceInitFunc无法被实例化</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">//public static final ResourceBundle R = ResourceBundle.getBundle("application.properties", Locale.CHINA);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">//private static final String SERVER_ADDR = R.getString("spring.cloud.sentinel.datasource.flow.nacos.server-addr");</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">//private static final String GROUP_ID = R.getString("spring.cloud.sentinel.datasource.flow.nacos.group-id");</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token comment" style="color:rgb(98, 114, 164)">//private static final String DATA_ID = R.getString("spring.cloud.sentinel.datasource.flow.nacos.data-id");</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">SERVER_ADDR</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"localhost:8488"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">GROUP_ID</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"DEFAULT_GROUP"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">DATA_ID</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"com.higlowx.scal.ebpp.service.bill.sentinel.flow"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">init</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name">Exception</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//误以为该init方法的作用是，根据每台部署机器的特性组装各种限流降级规则，然后向Nacos传输配置，</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//以免去初次生产部署应用，都要先在Nacos发布配置集，然后再启动应用的问题，现在想来有些误入歧途了，而且有报错。</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//误入歧途 开始</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//以下暂时仅对flow类型（流量控制）的init进行演示</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//List&lt;FlowRule&gt; flowRules = new ArrayList&lt;&gt;();</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//FlowRule flowRule = new FlowRule();</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//理论上resource的配置可以采用扫描出资源，并进行规模化或特例化配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setResource("/bill/main/create");</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//count值理论上可以根据每台机器的特性计算得出，这样更加合理</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setCount(5);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setStrategy(RuleConstant.STRATEGY_DIRECT);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRule.setClusterMode(false);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRules.add(flowRule);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//JSONObject source = new JSONObject();</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//source.put("flow", flowRules);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//ReadableDataSource&lt;String, List&lt;FlowRule&gt;&gt; flowRuleDataSource;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//flowRuleDataSource = new NacosDataSource&lt;List&lt;FlowRule&gt;&gt;(SERVER_ADDR, GROUP_ID, DATA_ID,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//        source.getObject("flow", new TypeReference&lt;List&lt;FlowRule&gt;&gt;() {})</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//);</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//误入歧途结束</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ReadableDataSource</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">FlowRule</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> flowRuleDataSource </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">NacosDataSource</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">FlowRule</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token constant" style="color:rgb(189, 147, 249)">SERVER_ADDR</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">GROUP_ID</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">DATA_ID</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Converter</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">String</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token generics"> </span><span class="token generics class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">FlowRule</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">FlowRule</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">convert</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">parseObject</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">source</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">TypeReference</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">List</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">FlowRule</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">FlowRuleManager</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">register2Property</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">flowRuleDataSource</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getProperty</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>然后在 resources 目录下创建 META-INF/services 目录，并在该目录中新建 com.alibaba.csp.sentinel.init.InitFunc 文件，文件内容如下：</p><div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">com.higlowx.scal.ebpp.service.bill.config.spi.SentinelDataSourceInitFunc</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>运行后得到的结论：</p><p>官方提供的该方法只可以做到应用从 Nacos 端获取 Sentinel 配置，且内部会监听 Nacos push 过来的配置更新，后续会自动刷到本实例内存中。</p><p>如果我们想要把 Nacos 配置集的更新集成到自有的 DevOps 中，且不是通过最原始的手动配置方式，官方也给出了一些简易示例：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">NacosConfigSender</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">throws</span><span class="token plain"> </span><span class="token class-name">Exception</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> remoteAddress </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"localhost:8848"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> groupId </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"Sentinel_Demo"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> dataId </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"com.alibaba.csp.sentinel.demo.flow.rule"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">String</span><span class="token plain"> rule </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"[\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"  {\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"resource\": \"TestResource\",\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"controlBehavior\": 0,\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"count\": 5.0,\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"grade\": 1,\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"limitApp\": \"default\",\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"    \"strategy\": 0\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"  }\n"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"]"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">ConfigService</span><span class="token plain"> configService </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">NacosFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">createConfigService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">remoteAddress</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">configService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">publishConfig</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">dataId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> groupId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> rule</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h3 class="anchor anchorWithStickyNavbar_LWe7" id="seata">Seata<a class="hash-link" href="#seata" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="seata-clienttm-与-rm">Seata Client（TM 与 RM）<a class="hash-link" href="#seata-clienttm-与-rm" title="标题的直接链接">​</a></h4><p>在 Seata 的架构中，有三个重要的组成部分：</p><ul><li><strong>TM（Transaction Manager）</strong>：事务管理器，定义全局事务的范围，开始、提交或回滚全局事务；</li><li><strong>TC（Transaction Coordinator）</strong>：事务协调者，是 Seata Server 端，维护全局和分支事务的状态，驱动全局事务提交或回滚；</li><li><strong>RM（Resource Manager）</strong>：资源管理器，管理分支事务处理的资源，与 TC 交谈以注册分支事务和报告分支事务的状态，并驱动分支事务提交或回滚。</li></ul><p>其中，除了 TC 需要以独立进程单独部署集群外，TM 和 RM 会以 JAR 包依赖的形式被嵌入进 APP 中，无论是 Server 还是 Client 都支持配置中心与服务中心，我们依旧还是会选用官方推荐的自家组件 Nacos 来完成。</p><p>在 TC 集群搭建完成之后，我们就可以开展业务代码端的接入工作了。Seata Client 的接入需要引入统一的客户端依赖，该依赖对于 TM 和 RM 是相同的。</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-seata</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在引入依赖后需要进行 bootstrap.properties 参数的设置，以 bill 服务为例，可以分为以下几个部分：</p><ul><li><strong>用于 TM、RM 路由发现 TC 集群的服务注册中心配置</strong>：</li></ul><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#seata registry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.registry.type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">nacos</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.registry.nacos.application</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-seata-server</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.registry.nacos.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.registry.nacos.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">EBPP_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.registry.nacos.cluster</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">default</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li><strong>用于从配置中心获取 Client 自身通用基础配置项的配置</strong>：</li></ul><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#seata config</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.config.type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">nacos</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.config.nacos.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">SEATA_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.config.nacos.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ul><li><strong>用于设置每个 Client 自身特有配置项的配置</strong>：</li></ul><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.service.disable-global-transaction</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.alibaba.seata.tx-service-group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-service-bill-tx-group</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">seata.service.vgroup-mapping.ebpp-service-bill-tx-group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">default</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上配置项的详细说明可以在 <a href="http://seata.io/zh-cn/docs/user/configurations.html" target="_blank" rel="noopener noreferrer">这里</a> 找到，本篇不再单独说明。</p><p>另外，有一个尤其需要注意的事前工作，在我们启动 Server 和 Client 之前，Nacos 配置中心中必须要有它们的基础启动配置，否则将无法启动。官方也提供给了我们方便的初始化工具，由 <strong><a href="https://github.com/seata/seata/blob/develop/script/config-center/config.txt" target="_blank" rel="noopener noreferrer">配置集txt</a></strong> 和 <strong><a href="https://github.com/seata/seata/blob/develop/script/config-center/nacos/nacos-config.sh" target="_blank" rel="noopener noreferrer">向配置中心请求初始化配置的脚本</a></strong> 组成。脚本的执行指令如下：</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">sh</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">${SCRIPTPATH}</span><span class="token plain">/nacos-config.sh -h localhost -p </span><span class="token number">8848</span><span class="token plain"> -g SEATA_GROUP -t 5a3c7d6c-f497-4d68-a71a-2e5e3340b3ca -u username -w password</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -h: host, the default value is localhost.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -p: port, the default value is 8848.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -g: Configure grouping, the default value is 'SEATA_GROUP'.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -t: Tenant information, corresponding to the namespace ID field of Nacos, the default value is ''.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -u: username, nacos 1.2.0+ on permission control, the default value is ''.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)"># -w: password, nacos 1.2.0+ on permission control, the default value is ''.</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>有的时候，脚本会找不到配置集 config.txt ，这时候需要简单修改下 nacos-config.sh 脚本中的文件路径即可。</p><p>最后的工作，就是根据业务应用要用到的 Seata 分布式事务模式，完成一些业务数据库的表建造，不同的模式对应的表是不太一样的，可以在这里找到：<a href="https://github.com/seata/seata/tree/develop/script/client/at/db" target="_blank" rel="noopener noreferrer">AT</a> 、<a href="https://github.com/seata/seata/tree/develop/script/client/tcc/db" target="_blank" rel="noopener noreferrer">TCC</a> 、<a href="https://github.com/seata/seata/tree/develop/script/client/saga/db" target="_blank" rel="noopener noreferrer">SAGA</a> 。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="在具体业务中服务的调用顺序对分布式事务的影响">在具体业务中，服务的调用顺序对分布式事务的影响<a class="hash-link" href="#在具体业务中服务的调用顺序对分布式事务的影响" title="标题的直接链接">​</a></h4><p>我们可以从一个具体的业务情景带入叙述，一方面可以重温一下为何会产生分布式事务问题，另一方面可以研究下 Seata 应该在哪些部位发挥作用。同样以一次成功付款为例，系统可以使用A和B两种调用顺序完成处理。先看顺序A：</p><p><img loading="lazy" alt="seata-1-1" src="/assets/images/seata-1-1-5aa7b52fefddbfc1f9b83768d8c0e989.png" width="1032" height="950" class="img_ev3q"></p><p>如果 trade 服务内部所有的本地事务全部执行成功，之后再调用bill服务去创建账单（注意这个调用处理是同步的）。如果 bill 服务在创建账单的时候因为某些异常导致执行失败，在有本地事务保障的情况下，bill 服务本身不会产生脏数据。后续的，在同步响应回 trade 服务后，trade 服务也能迅速感知到创建账单失败，并回滚掉自己本地的事务，最终在调用 ROOT 处返回处理失败，整个过程结束。</p><p>我们发现在刚才的流程中，如果不出现其他问题的话，是不存在分布式事务问题的。这个过程可以形象地概括为“做好自己的事再去请求别人”，整个线性调用有机地弥补了分布式系统带来的事务问题。然而要实现如此美好的愿景，需要一个大前提，即在整个调用链路的各个 JVM 实例外部，所有环节要素必须是正常且迅速的，这其中就包括了使用到的中间件、网络状况、数据库资源等，这么多外部要素一直保持正常很明显是不现实的。 在 trade 服务调用 bill 服务的过程中，如果由于上述的某种原因导致响应超时，这时候 trade 服务可以感知到并进行回滚，但是 bill 服务就不一定了：可能处理是成功的，仅仅是返回超时了；也有可能是失败的。 一般的，微服务之间还会接入限流、降级和熔断这样的调用保障，如此一来，消费方能够更加迅速地应对调用生产方时出现的异常，并做出反馈，更加加剧了上述问题的产生，这时候就需要选用适当的分布式事务解决方案来尽量避免这种情况的发生。</p><p>顺序B：</p><p><img loading="lazy" alt="seata-1-1" src="/assets/images/seata-1-1-5aa7b52fefddbfc1f9b83768d8c0e989.png" width="1032" height="950" class="img_ev3q"></p><p>如果 trade 服务在执行完本地所有的事务性逻辑之前，调用了 bill 服务去创建账单。假设 bill 服务的处理响应是成功且快速的。后续如果 trade 服务内部再出现异常，导致本地事务回滚，这种情况下，bill 本地事务已经提交了，不可能再进行回滚，其服务数据如何补偿就成了极其重要的一环。比较容易想到的是单独再去调用删除脏数据的 API（不论是同步还是异步的），这样不仅增加了开发成本，还增大了调用链路的复杂度，很难保证不会再次发生问题，而且事务的 ACID 特性已被打破，脏读也有发生的可能性。 上述这种情况也是十分典型的分布式事务问题。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="openfeign">OpenFeign<a class="hash-link" href="#openfeign" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="feign-client">Feign Client<a class="hash-link" href="#feign-client" title="标题的直接链接">​</a></h4><p>本篇中使用的 RPC Client 是 Spring Cloud 生态中 OpenFeign，它是一种基于 http 协议的声明式的消费客户端，内置了 Ribbon 负载均衡组件，只需要简单的配置，就可以帮助我们实现简单快捷的生产消费体验。</p><p>以 trade 服务调用消费 bill 服务为例，我们需要在消费端（trade）引入依赖：</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.springframework.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-openfeign</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>之后在启动配置中加入下面的简单配置，详细配置可以根据实际需要去增加删减：</p><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#feign客户端统一配置：连接超时时间，单位毫秒，指建立连接最多花费的时间，超过则抛出connect timeout异常</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">feign.client.config.default.connect-timeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">3000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#feign客户端统一配置：读取超时间，单位毫秒，指建立连接之后，读取网络资源最多花费的时间，超出则抛出read timeout异常</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">feign.client.config.default.read-timeout</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">5000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#开启feign对sentinel的支持</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#feign.sentinel.enabled=true</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>值得注意的是，要启用 Feign Client，必须要在 APP 启动类上加上 <strong>@EnableFeignClients</strong> 注解，这样在进程启动的时候，Spring 才可以扫描注入我们编写的 Feign Client。</p><h4 class="anchor anchorWithStickyNavbar_LWe7" id="feign-client-如何编写与维护">Feign Client 如何编写与维护<a class="hash-link" href="#feign-client-如何编写与维护" title="标题的直接链接">​</a></h4><p>以消费 bill 服务的账单类接口为例，一个规范的消费客户端长这个样子：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">cloud</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">openfeign</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">FeignClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">service</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bill</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">route</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BillMainRoute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@FeignClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">name </span><span class="token operator">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"ebpp-service-bill"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain">fallbackFactory </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">BillClientFallbackFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">BillClient</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">extends</span><span class="token plain"> </span><span class="token class-name">BillMainRoute</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>其中所继承的 BillMainRoute 可以是这样的：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">consts</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UriConsts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">res</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UnifiedResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">web</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bind</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">annotation</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">GetMapping</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">web</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bind</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">annotation</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">RequestMapping</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">web</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bind</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">annotation</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">RequestParam</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">java</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">math</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BigDecimal</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@RequestMapping</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UriConsts</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">BILL_PREFIX</span><span class="token plain"> </span><span class="token operator">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"/main"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">interface</span><span class="token plain"> </span><span class="token class-name">BillMainRoute</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@GetMapping</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"/create"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token class-name">UnifiedResponse</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@RequestParam</span><span class="token plain"> </span><span class="token class-name">BigDecimal</span><span class="token plain"> amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@RequestParam</span><span class="token plain"> </span><span class="token class-name">Integer</span><span class="token plain"> tradeId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>BillClientFallbackFactory 负责消费端的消费失败后处理，可用于补救、重试或快速失败，：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">res</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UnifiedResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">res</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UnifiedResponseCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">feign</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">hystrix</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">FallbackFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">slf4j</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">Logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">slf4j</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">LoggerFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">stereotype</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">Component</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">java</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">math</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BigDecimal</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * 本实例必须被spring管理！！</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Component</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">BillClientFallbackFactory</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">FallbackFactory</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">BillClient</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Logger</span><span class="token plain"> log </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">LoggerFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getLogger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">BillClientFallbackFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">BillClient</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">Throwable</span><span class="token plain"> throwable</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        log</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">warn</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"bill服务触发sentinel的熔断限流机制，trade侧调用fallback逻辑兼容rpc故障"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> throwable</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">BillClient</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">UnifiedResponse</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">BigDecimal</span><span class="token plain"> amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Integer</span><span class="token plain"> tradeId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token class-name">UnifiedResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UnifiedResponseCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">UNIFIED_FAIL</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"bill接口被限流、熔断或降级"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>这里，如果你对这种继承与依赖关系感兴趣的话，也可以看一下 Bill 对应的 Restful 接口是什么样子的：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">alibaba</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">fastjson</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">res</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UnifiedResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">common</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">res</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">UnifiedResponseCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">service</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bill</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">entity</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">Bill</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">service</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bill</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">route</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BillMainRoute</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">com</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">higlowx</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">scal</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">ebpp</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">service</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bill</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">service</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BillService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">slf4j</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">Logger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">slf4j</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">LoggerFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">beans</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">factory</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">annotation</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">Autowired</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">web</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">bind</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">annotation</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">RestController</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">java</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">math</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">BigDecimal</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@RestController</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">MainController</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">implements</span><span class="token plain"> </span><span class="token class-name">BillMainRoute</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">final</span><span class="token plain"> </span><span class="token class-name">Logger</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">LOG</span><span class="token plain"> </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">LoggerFactory</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getLogger</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">MainController</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Autowired</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token class-name">BillService</span><span class="token plain"> billService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">UnifiedResponse</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">BigDecimal</span><span class="token plain"> amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token class-name">Integer</span><span class="token plain"> tradeId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Bill</span><span class="token plain"> bill </span><span class="token operator">=</span><span class="token plain"> billService</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">create</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">amount</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> tradeId</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">UnifiedResponse</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token generics class-name">Object</span><span class="token generics punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"> out </span><span class="token operator">=</span><span class="token plain"> </span><span class="token class-name">UnifiedResponse</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">UnifiedResponseCode</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token constant" style="color:rgb(189, 147, 249)">OK</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> bill</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token constant" style="color:rgb(189, 147, 249)">LOG</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">info</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"out: {}"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token constant" style="color:rgb(189, 147, 249)">JSON</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">toJSONString</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>以上各个组件的关系可以总结为：<strong>基于 Route 接口声明，生产者实现，消费者继承</strong>。这样的结构在实际的开发维护中，会大大增加我们的工作效率。有兴趣的也可思考一下，Feign Client 可以直接交由生产者开发维护，并向消费者提供 JAR 包依赖吗，对应的原因是怎样的。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="spring-cloud-gateway">Spring Cloud Gateway<a class="hash-link" href="#spring-cloud-gateway" title="标题的直接链接">​</a></h3><h4 class="anchor anchorWithStickyNavbar_LWe7" id="部署启动">部署启动<a class="hash-link" href="#部署启动" title="标题的直接链接">​</a></h4><p>该网关的部署比较简单，其本质上基于 webflux 和 loadbalancer，是 Spring Cloud 开源生态中的一员，部署 Spring Cloud Gateway 就像部署一个 Spring Cloud 业务应用一样简单。生产环境中，集群部署以及高性能的云原声环境可以为微服务网关提供HA支撑。</p><p>此次将仅介绍单机该如何部署。</p><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">org.springframework.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-gateway</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在引入上面的依赖后，分别创建启动类与 bootstrap.properties 配置文件，输入命令运行启动类便可开启我们的网关服务。</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">boot</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">SpringApplication</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">import</span><span class="token plain"> </span><span class="token import namespace">org</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">springframework</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">boot</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import namespace">autoconfigure</span><span class="token import namespace punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token import class-name">SpringBootApplication</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">* 启动类</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">**/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@SpringBootApplication</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">AppStart</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">SpringApplication</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">AppStart</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"># 简单的配置文件</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">server.port</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">9900</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.application.name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-gateway</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.profiles.active</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">dev</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><h4 class="anchor anchorWithStickyNavbar_LWe7" id="接入服务">接入服务<a class="hash-link" href="#接入服务" title="标题的直接链接">​</a></h4><p>仅仅启动服务是远远不够的，作为一个合格的网关，它应该具有路由分发、统一鉴权、限流等能力，其中前两者可以使用 Spring Cloud Gateway 自带的机制实现，此外，网关的限流可以通过接入 Sentinel 实现。</p><p>关于路由分发的配置示例（基于 Nacos 注册中心寻址业务应用的方式）：</p><ol><li>引入 nacos-discovery 依赖</li></ol><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-nacos-discovery</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol><li>配置文件示例</li></ol><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#注册中心配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.discovery.group</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">EBPP_GROUP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.nacos.discovery.server-addr</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">127.0.0.1:8848</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#让gateway从nacos中获取服务信息</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.discovery.locator.enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#路由配置</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[0].id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-service-trade-route</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[0].uri</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">lb://ebpp-service-trade</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[0].predicates[0].name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">Path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[0].predicates[0].args.Path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">/trade/**</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[1].id</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">ebpp-service-bill-route</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[1].uri</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">lb://ebpp-service-bill</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[1].predicates[0].name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">Path</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.gateway.routes[1].predicates[0].args.Path</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">/bill/**</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>关于实现网关自身限流的示例：</p><ol><li>引入 Sentinel 依赖</li></ol><div class="language-xml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-xml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- sentinel dependence for service instance itself --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-starter-alibaba-sentinel</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">&lt;!-- sentinel dependence for adapting spring cloud gateway --&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">com.alibaba.cloud</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">groupId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain">spring-cloud-alibaba-sentinel-gateway</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">artifactId</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&lt;/</span><span class="token tag" style="color:rgb(255, 121, 198)">dependency</span><span class="token tag punctuation" style="color:rgb(248, 248, 242)">&gt;</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><ol><li>完成基本的配置</li></ol><div class="language-properties codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-properties codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">#是否提前触发sentinel初始化(sentinel会在客户端首次调用的时候进行初始化，开始向控制台发送心跳包)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.eager</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#应用与sentinel控制台交互的端口，应用本地会起一个该端口占用的HttpServer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.port</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">8719</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#sentinel控制台地址</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.dashboard</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">localhost:8080</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#应用与sentinel控制台的心跳间隔时间</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token key attr-name" style="color:rgb(241, 250, 140)">spring.cloud.sentinel.transport.heartbeat-interval-ms</span><span class="token punctuation" style="color:rgb(248, 248, 242)">=</span><span class="token value attr-value">5000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token comment" style="color:rgb(98, 114, 164)">#限流规则的设置与本篇中提到的相同，这里不再赘述</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>在实际生产中，网关中的路由规则是会不断变化的，每次有新应用加入，对应的路由规则也必须要进行修改。如果频繁地去修改路由规则并重启 gateway，势必会严重影响整个系统的可用性。可喜的是，Nacos Config 为 Spring Cloud Gateway 提供了支持，让它得以在持续运行的状态下动态更新路由规则和其他必要的参数，而且接入方式与接入普通业务系统是相同的，有兴趣的可以参考下本文中曾经描述的 Nacos Config 使用部分，这里同样不再赘述。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="总结">总结<a class="hash-link" href="#总结" title="标题的直接链接">​</a></h2><p>随着应用系统复杂度和业务量的增多，对系统进行微服务化改造是必须要经历的重要一步。Spring Cloud Alibaba 为我们提供了更加适用于国内业务场景的完整解决方案。本篇仅仅只是介绍了比较初级的使用，对于一些可能存在的问题无法有效地排查出去。而在其真正落地到生产中去后，这套方案可能会给我们开发者带来更多的惊喜，当然也有可能是各种问题不断。但技术总是有两面性，解决了痛点往往伴随着新的痛点的产生，也正是这种两面性，使得技术得以不断进步。最后，我想说，没有真正完美的解决方案，只有最适合的解决方案，我们程序员的工作就是在各种权衡利弊中找到问题的最优解。</p><blockquote><p><a href="https://nacos.io/zh-cn/docs/what-is-nacos.html" target="_blank" rel="noopener noreferrer">Nacos 官方文档</a>；</p><p><a href="https://sentinelguard.io/zh-cn/docs/introduction.html" target="_blank" rel="noopener noreferrer">Sentinel 官方文档</a>；</p><p><a href="http://seata.io/zh-cn/docs/overview/what-is-seata.html" target="_blank" rel="noopener noreferrer">Seata 官方文档</a>；</p><p><a href="https://spring.io/projects/spring-cloud" target="_blank" rel="noopener noreferrer">Spring Cloud</a>；</p><p><a href="https://spring.io/projects/spring-cloud-gateway" target="_blank" rel="noopener noreferrer">Spring Cloud Gateway</a>；</p><p><a href="https://github.com/higlowx/spring-cloud-alibaba-learning" target="_blank" rel="noopener noreferrer">项目源码</a>；</p><p>HA 汇总：<a href="https://nacos.io/zh-cn/docs/cluster-mode-quick-start.html" target="_blank" rel="noopener noreferrer">Nacos</a>； <a href="http://seata.io/zh-cn/docs/ops/deploy-ha.html" target="_blank" rel="noopener noreferrer">Seata</a>； <a href="https://github.com/spring-cloud/spring-cloud-gateway/issues/566" target="_blank" rel="noopener noreferrer">Spring Cloud Gateway</a>；</p></blockquote>]]></content>
        <author>
            <name>Dylan Li</name>
            <email>higlowx@gmail.com</email>
            <uri>https://github.com/higlowx</uri>
        </author>
        <category label="Spring Cloud Alibaba" term="Spring Cloud Alibaba"/>
        <category label="Spring Cloud" term="Spring Cloud"/>
        <category label="Microservices" term="Microservices"/>
        <category label="微服务" term="微服务"/>
        <category label="Java" term="Java"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Filebeat 日志采集环境中如何获取主机 IP]]></title>
        <id>filebeat-hostip-acquisition</id>
        <link href="https://www.higlowx.com/blog/filebeat-hostip-acquisition"/>
        <updated>2019-01-19T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[在使用 Elastic Stack v7.4 实现统一的日志系统时，我们希望在 Filebeat 增加像 hostip 这样的参数来将宿主机的 IP 带到日志采集流中。比如这样配置：]]></summary>
        <content type="html"><![CDATA[<p>在使用 Elastic Stack v7.4 实现统一的日志系统时，我们希望在 Filebeat 增加像 <code>hostip</code> 这样的参数来将宿主机的 IP 带到日志采集流中。比如这样配置：</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> log</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">paths</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> /path/to/your/logs/your</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">serviceid.log</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-serviceid-log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.pattern</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'^[[:space:]]|^Caused by:|^&lt;|^{|^}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.negate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.match</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> after</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">ignore_older</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 24h</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fields_under_root</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">serviceid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> your</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">serviceid</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">hostip</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> your</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">hostip</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>将 <code>your-hostip</code> 替换为当前宿主机的 IP，然后启动 filebeat 服务即可实现最初的目的。但是这样静态配置的方式还不够优雅，如果每台宿主机的配置文件中存在多组上述配置，在每一组配置中都去手动指定 IP 将会成一个繁琐而无味的工作。</p><p>换种思路，如果我们在 <code>/etc/profile</code> 文件中添加 <code>HOST_IP</code> 环境变量，并将值设置为宿主机的 IP，然后尝试重启获取一下，效果如何，我们将配置变更一下：</p><div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> log</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">enabled</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">paths</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain"> /path/to/your/logs/your</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">serviceid.log</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"your-serviceid-log"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.pattern</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">'^[[:space:]]|^Caused by:|^&lt;|^{|^}'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.negate</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">false</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">multiline.match</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> after</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">ignore_older</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> 24h</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fields_under_root</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token boolean important">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token key atrule">fields</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">serviceid</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> your</span><span class="token punctuation" style="color:rgb(248, 248, 242)">-</span><span class="token plain">serviceid</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">      </span><span class="token key atrule">hostip</span><span class="token punctuation" style="color:rgb(248, 248, 242)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(255, 121, 198)">"${HOST_IP}"</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>令人失落的是，我们发现使用这种方案会导致 filebeat 进程无法重启，原因出在它是通过 <code>systemd</code> 启动的，默认情况下系统的环境变量只有通过 <code>pam</code> 方式登录的用户才能读取到，但是 <code>systemd</code> 是不会进行登录的，所以就不能直接读取到系统的环境变量。该如何解决呢？</p><p>再换种思路，是否可以使用某种方法向 <code>systemd</code> 注入环境变量呢？答案是可以的，我们可以创建一个前置服务并在该服务中执行动态获取宿主机 IP 的脚本命令 <code>ipecho</code>，脚本的返回值就是宿主机的 IP 地址，像这种：</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">tee</span><span class="token plain"> /usr/local/bin/ipecho </span><span class="token operator">&lt;&lt;-</span><span class="token string" style="color:rgb(255, 121, 198)">'EOF'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">#!/bin/bash</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="display:inline-block;color:rgb(255, 121, 198)"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">ip -o -4  address show  | awk ' NR==2 { gsub(/\/.*/, "", $4); print $4 } '</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">EOF</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>之后为此命令设置可执行权限：<code>chmod u+x /usr/local/bin/ipecho</code>，然后使用 <code>systemctl</code> 的 <code>set-environment</code> 指令直接执行 <code>ipecho</code> 并将返回值赋值给 <code>HOST_IP</code> 变量再注入到 <code>systemd</code> 运行时环境中，最后向 <code>systemd</code> 提交一个名为 <code>systemd-setenv.service</code> 的新服务：</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token function" style="color:rgb(80, 250, 123)">tee</span><span class="token plain"> /usr/lib/systemd/system/systemd-setenv.service </span><span class="token operator">&lt;&lt;-</span><span class="token string" style="color:rgb(255, 121, 198)">'EOF'</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">[Unit]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">Description=Transfers /etc/environment to systemd</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">Requires=network-online.target</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">After=network-online.target</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="display:inline-block;color:rgb(255, 121, 198)"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">[Service]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">ExecStart=/usr/bin/bash -c "/usr/bin/systemctl set-environment HOST_IP=`ipecho`"</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="display:inline-block;color:rgb(255, 121, 198)"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">[Install]</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">WantedBy=multi-user.target</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token string" style="color:rgb(255, 121, 198)">EOF</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>初次创建后需要设置开机自启并启动它。</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain">systemctl </span><span class="token builtin class-name" style="color:rgb(189, 147, 249)">enable</span><span class="token plain"> systemd-setenv.service</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">systemctl start systemd-setenv.service</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>我们将该前置服务设置给 filebeat 服务。通过 <code>vim /usr/lib/systemd/system/filebeat.service</code> 来进行设置，重点是 <code>After=systemd-setenv.service</code> 这一行:</p><div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">Unit</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Description</span><span class="token operator">=</span><span class="token plain">Filebeat sends log files to Logstash or directly to Elasticsearch.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Documentation</span><span class="token operator">=</span><span class="token plain">https://www.elastic.co/products/beats/filebeat</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Wants</span><span class="token operator">=</span><span class="token plain">network-online.target</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">After</span><span class="token operator">=</span><span class="token plain">network-online.target</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">After</span><span class="token operator">=</span><span class="token plain">systemd-setenv.service</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">Service</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Environment</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"BEAT_LOG_OPTS=-e"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Environment</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"BEAT_CONFIG_OPTS=-c /etc/filebeat/filebeat.yml"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Environment</span><span class="token operator">=</span><span class="token string" style="color:rgb(255, 121, 198)">"BEAT_PATH_OPTS=-path.home /usr/share/filebeat -path.config /etc/filebeat -path.data /var/lib/filebeat -path.logs /var/log/filebeat"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">ExecStart</span><span class="token operator">=</span><span class="token plain">/usr/share/filebeat/bin/filebeat </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$BEAT_LOG_OPTS</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$BEAT_CONFIG_OPTS</span><span class="token plain"> </span><span class="token variable" style="color:rgb(189, 147, 249);font-style:italic">$BEAT_PATH_OPTS</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">Restart</span><span class="token operator">=</span><span class="token plain">always</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token plain">Install</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token assign-left variable" style="color:rgb(189, 147, 249);font-style:italic">WantedBy</span><span class="token operator">=</span><span class="token plain">multi-user.target</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>运行 <code>systemctl daemon-reload</code>、<code>systemctl restart filebeat</code> 命令重载和重启 filebeat 服务即可实现我们的目的，相比最原始的方式要优雅和便捷的多～</p>]]></content>
        <author>
            <name>Dylan Li</name>
            <email>higlowx@gmail.com</email>
            <uri>https://github.com/higlowx</uri>
        </author>
        <category label="Filebeat" term="Filebeat"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[如何实现 Spring Boot 服务的优雅关闭]]></title>
        <id>spring-boot-shutdown-gracefully</id>
        <link href="https://www.higlowx.com/blog/spring-boot-shutdown-gracefully"/>
        <updated>2019-01-13T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[原生 JVM 进程的优雅退出 - ShutdownHook]]></summary>
        <content type="html"><![CDATA[<h2 class="anchor anchorWithStickyNavbar_LWe7" id="原生-jvm-进程的优雅退出---shutdownhook">原生 JVM 进程的优雅退出 - ShutdownHook<a class="hash-link" href="#原生-jvm-进程的优雅退出---shutdownhook" title="标题的直接链接">​</a></h2><p>下面一段代码是最常用的一种优雅关闭实现，使用 <code>Runtime.getRuntime().addShutdownHook()</code> 可以向 JVM Runtime 注册一条 Hook Thread 实例，用于执行我们的优雅关闭逻辑，
值得注意的是，这个 Thread 实例并不会立刻被调度执行。 当我们从 OS 层面向 JVM 进程传递某些 Signal（信号）后，这个 Hook 线程才会被运行。</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">class</span><span class="token plain"> </span><span class="token class-name">ShutdownHookExample</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">volatile</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">boolean</span><span class="token plain"> running </span><span class="token operator">=</span><span class="token plain"> </span><span class="token boolean">true</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">main</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">[</span><span class="token punctuation" style="color:rgb(248, 248, 242)">]</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">Runtime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">getRuntime</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">addShutdownHook</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Thread</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Runnable</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token annotation punctuation" style="color:rgb(248, 248, 242)">@Override</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">void</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"Shutdown Hook is activated"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">//close resources...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                </span><span class="token comment" style="color:rgb(98, 114, 164)">//change running signal to false.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">                running </span><span class="token operator">=</span><span class="token plain"> </span><span class="token boolean">false</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"The Java Virtual Machine is running!"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">running</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token comment" style="color:rgb(98, 114, 164)">//jvm is running.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token comment" style="color:rgb(98, 114, 164)">//when running signal is false, main thread will println this line.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">System</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain">out</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">println</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token string" style="color:rgb(255, 121, 198)">"The Java Virtual Machine shut down gracefully"</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>那么前面所说的 Signal 到底是什么呢？</p><p>Signal 是 OS 与进程间进行沟通的一种机制，这种机制常常用来触发进程的某些动作。在 Linux 中，Signal 通过 <code>kill</code> 命令或其他方式来向进程传递。
运行 <code>kill -l</code> 可以看到标准 Signal 在 Linux OS 中是怎样的。</p><p><img loading="lazy" alt="linux-kill-l" src="/assets/images/linux-kill-l-4d532bde6b59cfc0caac6408e42e87e4.png" width="640" height="253" class="img_ev3q"></p><p>以 <code>1) SIGHUB</code> 为例，<code>SIGHUB</code> 是一种命名为 <strong>HUB</strong> 的信号，前面的 <code>1</code> 则是使用 <code>kill</code> 命令时要传入的值（命令格式为 <code>kill -[Signal Num] [Process ID]</code> ）。</p><p>大部分场景下会用到的 Signal 并不是很多：</p><table><thead><tr><th>Signal</th><th>Describe</th><th>Linux Command</th></tr></thead><tbody><tr><td>HUB</td><td>在进程运行时通知其重载配置</td><td>kill -1</td></tr><tr><td>INT</td><td>终止信号（正常退出，允许进程自行清除状态后退出）</td><td>^C</td></tr><tr><td>KILL</td><td>终止信号（强制退出，立刻回收内存并剥离CPU）</td><td>kill -9</td></tr><tr><td>TERM</td><td>终止信号（优雅退出，允许进程自行清除状态后退出）</td><td>kill -15</td></tr></tbody></table><p>Java 默认对 <strong>HUB</strong> 、 <strong>INT</strong> 、 <strong>TERM</strong> 三种信号提供了支持，开发者可以有效地利用这些机制完成我们的某些需求。至于 JVM 进程是如何做到捕获这些信号并执行所需逻辑的，
我们可以从 JDK 源代码中找到其出处，核心的类有 <code>java.lang.System</code> 、 <code>java.lang.Runtime</code> 、 <code>java.lang.Shutdown</code> 、 <code>java.lang.Terminator</code> 、
<code>java.lang.ApplicationShutdownHooks</code> 、 <code>sun.misc.Signal</code> 以及 <code>sun.misc.SignalHandler</code> ，感兴趣的可以看一下源代码，重点的方法有：</p><ul><li><code>Terminator.setup()</code>：设置 Java 对 <strong>HUB</strong>、<strong>INT</strong> 和 <strong>TERM</strong> 信号的捕获处理实例。</li><li><code>Shutdown.add(int slot, boolean registerShutdownInProgress, Runnable hook)</code>：向 hooks 集合中追加 Hook 线程。</li><li><code>Shutdown.exit(int status)</code>：在 Runtime.exit 时调用，执行内置的、用户自定义的 Shutdown 逻辑，其中包括执行 hooks 集合中的 Hook 线程。</li><li><code>Shutdown.runHooks()</code>：运行所有已注册的 Shutdown Hook。</li></ul><h2 class="anchor anchorWithStickyNavbar_LWe7" id="spring-boot-如何做到">Spring Boot 如何做到<a class="hash-link" href="#spring-boot-如何做到" title="标题的直接链接">​</a></h2><p>从 Spring Boot 2.3 版本起，开发者可以通过 <strong>Stater</strong> 方式在配置文件中设置，之后通过向其发送 <code>SIGINT</code> 或 <code>SIGTERM</code> 信号便可触发服务的优雅关闭。重点有两个属性：</p><ul><li><code>server.shutdown=graceful</code>：设置 Web Server 的 shutdown 方式，graceful 为优雅关闭，immediate 为立即关闭。</li><li><code>spring.lifecycle.timeout-per-shutdown-phase=${duration}</code>：在触发优雅关闭后，留给 Spring 容器关闭资源的超时时间，
超过这个时间，即使 Spring 中还有没关闭的资源，进程也会立刻终止并输出相应日志</li></ul><p>能实现这样的优雅关闭，内部依然是依赖 Java 的原生支持。Spring Boot 中对于 Web Server、Application Context 和 Bean 都提供了基于 Shutdown Hook 的关闭机制。</p><p>在实际编码中，我们通过<code>SpringApplication.run(Class&lt;?&gt; primarySource, String... args)</code>静态方法来运行我们的入口类，其内部最终
调用了<code>ConfigurableApplicationContext run(String... args)</code>：</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token class-name">ConfigurableApplicationContext</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">run</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token class-name">StopWatch</span><span class="token plain"> stopWatch </span><span class="token operator">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">StopWatch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        stopWatch</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token function" style="color:rgb(80, 250, 123)">start</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">            </span><span class="token function" style="color:rgb(80, 250, 123)">refreshContext</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token comment" style="color:rgb(98, 114, 164)">//关于Spring容器ShutdownHook的注册逻辑在这里</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token punctuation" style="color:rgb(248, 248, 242)">.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>Spring 容器默认情况下会开启 Shutdown Hook 用于容器和 Bean 的销毁，而<code>server.shutdown=graceful</code>则是在 Web Server 层面设置了关闭方式，在触发关闭的同时，
Web Server 会关闭新链接的建立并等待已经接受的请求响应完毕，在达到设定的超时时间后进而完成整个进程的主动退出。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="常见问题">常见问题<a class="hash-link" href="#常见问题" title="标题的直接链接">​</a></h2><p>在向进程传递 <strong>INT</strong>、<strong>TERM</strong> 终止信号前，正在运行的 jar 文件不允许被修改、迁移或删除，否则会导致退出过程中出现部分 Bean 无法关闭的情况。</p>]]></content>
        <author>
            <name>Dylan Li</name>
            <email>higlowx@gmail.com</email>
            <uri>https://github.com/higlowx</uri>
        </author>
        <category label="Spring Boot" term="Spring Boot"/>
        <category label="Spring" term="Spring"/>
        <category label="DevOps" term="DevOps"/>
        <category label="Java" term="Java"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[随时随地手写正则表达式]]></title>
        <id>hold-regex</id>
        <link href="https://www.higlowx.com/blog/hold-regex"/>
        <updated>2018-11-20T00:00:00.000Z</updated>
        <summary type="html"><![CDATA[正则表达式常常被认为是一种现用现取的工具，开发者会忽视其潜在的场景和应用。实际上，几乎所有的开发语言均对其进行了差异化的实现，基于这一点，它被广泛应用于数据校验、 数据处理等场景。]]></summary>
        <content type="html"><![CDATA[<p>正则表达式常常被认为是一种现用现取的工具，开发者会忽视其潜在的场景和应用。实际上，几乎所有的开发语言均对其进行了差异化的实现，基于这一点，它被广泛应用于数据校验、 数据处理等场景。
从某种意义上说，正则表达式已经不是一种现用现取的工具，而是每个开发者必须要具备的一项基础素质。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="应用场景">应用场景<a class="hash-link" href="#应用场景" title="标题的直接链接">​</a></h2><p>在开发API时判断请求参数中的手机号是否合法，使用数据爬虫爬取网页中某个标签下的数据，对敏感数据进行检测与处理，采集日志时根据格式提取关键信息等，这些场景都需要使用正则表达式来提高
处理的效率。概括来说主要分为两方面：<strong>字符串格式校验</strong>、<strong>字符串提取</strong></p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="字符串格式校验">字符串格式校验<a class="hash-link" href="#字符串格式校验" title="标题的直接链接">​</a></h3><p>一次性判断一整个字符串是否符合某种格式。比如用<code>^[0-9]+$</code>来校验字符串是否只包含数字，用<code>^[a-zA-Z0-9]+@gmail\.com$</code>来校验是否是谷歌邮箱等。</p><h3 class="anchor anchorWithStickyNavbar_LWe7" id="字符串提取">字符串提取<a class="hash-link" href="#字符串提取" title="标题的直接链接">​</a></h3><p>根据某种格式从一段字符串中提取出符合要求的字串。比如使用<code>[a-z]+</code>从<code>hey, welcome to 中国</code>里提取出所有的英文单词：hey、welcome和to。</p><h2 class="anchor anchorWithStickyNavbar_LWe7" id="细节">细节<a class="hash-link" href="#细节" title="标题的直接链接">​</a></h2><h3 class="anchor anchorWithStickyNavbar_LWe7" id="元字符meta-characters">元字符（Meta Characters）<a class="hash-link" href="#元字符meta-characters" title="标题的直接链接">​</a></h3><p>正则表达式主要依赖于元字符。元字符不代表他们本身的字面意思，他们都有特殊的含义，可以类比开发语言中的关键字。一些元字符写在方括号<code>[]</code>中的时候有一些特殊的意思。</p><table><thead><tr><th>元字符</th><th>描述</th></tr></thead><tbody><tr><td>.</td><td>匹配任意字符，除了换行符</td></tr><tr><td>[ ]</td><td>匹配方括号内的任意字符</td></tr><tr><td>[^ ]</td><td>匹配除了方括号里的任意字符</td></tr><tr><td>*</td><td>连续匹配 &gt;=0 个重复的在 * 号之前的字符</td></tr><tr><td>+</td><td>连续匹配 &gt;=1 个重复的在 + 号之前的字符</td></tr><tr><td>{ }</td><td>连续匹配 num 个大括号之前的字符或字符集，写法包括{n,m}、{n}、{n,}，分别匹配 <!-- -->[n,m]<!-- -->、n、[n,+∞) 个</td></tr><tr><td>?</td><td>标记 ? 之前的字符为可选，或者惰性匹配时使用</td></tr><tr><td>( )</td><td>分组，按组匹配符合括号内正则表达式的子字符集</td></tr><tr><td>|</td><td>或运算符，匹配符号前或后的字符</td></tr><tr><td>\ </td><td>转义字符，用于匹配一些保留的字符 <!-- -->[ ]<!-- --> ( ) { } . * + ? ^ $ \ <!-- -->|</td></tr><tr><td>^</td><td>前定界符，从第一个字符开始匹配</td></tr><tr><td>$</td><td>后定界符，匹配到最后一个字符为止</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="简写字符集shorthand-character-sets">简写字符集（Shorthand Character Sets）<a class="hash-link" href="#简写字符集shorthand-character-sets" title="标题的直接链接">​</a></h3><p>一些常用的字符集可以通过简写的方式进行匹配。</p><table><thead><tr><th>简写</th><th>描述</th></tr></thead><tbody><tr><td>\w</td><td>匹配字母、数字和下划线，等价于：<!-- -->[a-zA-Z0-9_]</td></tr><tr><td>\W</td><td>匹配非字母、非数字和非下划线的字符，即符号，等价于：<sup id="fnref-a-za-z0-9_-30bddc"><a href="#fn-a-za-z0-9_-30bddc" class="footnote-ref">a-zA-Z0-9_</a></sup>、<sup id="fnref-\w-30bddc"><a href="#fn-\w-30bddc" class="footnote-ref">\w</a></sup></td></tr><tr><td>\s</td><td>匹配空格字符，包括制表符、换行符、换页符、回车符号等，等价于：<!-- -->[\t\n\f\r\p{Z}]</td></tr><tr><td>\S</td><td>匹配非空格字符，等价于：<sup id="fnref-\t\n\f\r\p{z}-30bddc"><a href="#fn-\t\n\f\r\p{z}-30bddc" class="footnote-ref">\t\n\f\r\p{Z}</a></sup>、<sup id="fnref-\s-30bddc"><a href="#fn-\s-30bddc" class="footnote-ref">\s</a></sup></td></tr><tr><td>\d</td><td>匹配数字，等价于：<!-- -->[0-9]</td></tr><tr><td>\D</td><td>匹配非数字：<sup id="fnref-0-9-30bddc"><a href="#fn-0-9-30bddc" class="footnote-ref">0-9</a></sup>、<sup id="fnref-\d-30bddc"><a href="#fn-\d-30bddc" class="footnote-ref">\d</a></sup></td></tr><tr><td>\f</td><td>匹配换页符</td></tr><tr><td>\n</td><td>匹配换行符</td></tr><tr><td>\r</td><td>匹配回车符</td></tr><tr><td>\t</td><td>匹配制表符</td></tr><tr><td>\v</td><td>匹配垂直制表符</td></tr><tr><td>\p</td><td>匹配 CR/LF（等价于 \r\n），用来匹配 DOS 行终止符</td></tr><tr><td>.</td><td>虽然属于元字符，但严格意义上也属于一种简写字符集</td></tr></tbody></table><blockquote><p>补充：\p{Z} 或 \p{Separator}，指任何类型的空格或不可见的分隔符，极少使用，了解即可</p></blockquote><h3 class="anchor anchorWithStickyNavbar_LWe7" id="断言lookarounds">断言（Lookarounds）<a class="hash-link" href="#断言lookarounds" title="标题的直接链接">​</a></h3><p><strong>断言主要分为两种：</strong></p><ul><li><strong>先行断言</strong>：断言部分位于某个表达式<strong>之前</strong>，用来修饰后者的判断条件，在某个子字符串满足正则部分的前提下，该子字符串<strong>紧邻</strong>的<strong>前面</strong>的字符另需满足断言部分。</li><li><strong>后发断言</strong>：断言部分位于某个表达式<strong>之后</strong>，用来修饰后者的判断条件，在某个子字符串满足正则部分的前提下，该子字符串<strong>紧邻</strong>的<strong>后面</strong>的字符另需满足断言部分。</li></ul><p><strong>捕获与非捕获：</strong></p><ul><li><strong>捕获</strong>：捕获文本，且对组合（同"分组"概念）进行计数。形象点说，就是会提取出匹配某个正则表达式的字符串，该表达式既可以是普通表达式，也可以是断言表达式。</li><li><strong>非捕获</strong>：不捕获文本，也不针对组合进行计数。形象解释，指不会提取出匹配某个正则表达式的字符串，这个正则表达式往往属于断言表达式。</li></ul><p>常用断言一览：</p><table><thead><tr><th>断言</th><th>类型</th><th>断言匹配的字符是否被捕获</th><th>举例</th><th>描述</th></tr></thead><tbody><tr><td>?=</td><td>后发断言</td><td>否</td><td>a(?=bc)</td><td>字符a后紧邻的字符串是bc</td></tr><tr><td>?!</td><td>后发断言</td><td>否</td><td>a(?!bc)</td><td>字符a后紧邻的字符串不是bc</td></tr><tr><td>?&lt;=</td><td>先行断言</td><td>否</td><td>(?&lt;=bc)a</td><td>字符a前紧邻的字符串是bc</td></tr><tr><td>?&lt;!</td><td>先行断言</td><td>否</td><td>(?&lt;!bc)a</td><td>字符a前紧邻的字符串不是bc</td></tr><tr><td>\b</td><td>后发断言、先行断言</td><td>否</td><td>a\b、\ba</td><td>边界断言，指断言位置存在边界，边界：各类空格、整个待匹配字符串的头尾边界</td></tr><tr><td>\B</td><td>后发断言、先行断言</td><td>否</td><td>a\B、\Ba</td><td>非边界断言，与 \b 相反，指断言位置不存在边界，而是其他字符</td></tr><tr><td>?:</td><td>后发断言、先行断言</td><td>是</td><td>a(?:bc)、(?:bc)a</td><td>约等价于 ?= 和 ?&lt;= ，不同是断言匹配的字符会被捕获</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="标志flags">标志（Flags）<a class="hash-link" href="#标志flags" title="标题的直接链接">​</a></h3><p>标志也叫模式修饰符，因为它可以用来修饰表达式，进而影响匹配结果。这些标志可以任意地互相组合起来使用，也可以单独使用，
在格式上遵循 <code>/{regex}/{flags}</code> ，例如 <code>/[abc]+/gm</code>。一般情况下，不需要开发者在 regex 串中设置，而是在方法的调用入参或者调用实例的属性中设置，
例如 Java 中 java.util.regex.Pattern 类的 compile 方法，就有一种支持 flags 参数的多态化实现。</p><div class="language-java codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#F8F8F2;--prism-background-color:#282A36"><div class="codeBlockContent_biex"><pre tabindex="0" class="prism-code language-java codeBlock_bY9V thin-scrollbar"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)">/**</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * Compiles the given regular expression into a pattern with the given</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * flags.</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * @param  regex</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         The expression to be compiled</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * @param  flags</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         Match flags, a bit mask that may include</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         {@link #CASE_INSENSITIVE}, {@link #MULTILINE}, {@link #DOTALL},</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         {@link #UNICODE_CASE}, {@link #CANON_EQ}, {@link #UNIX_LINES},</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         {@link #LITERAL}, {@link #UNICODE_CHARACTER_CLASS}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *         and {@link #COMMENTS}</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> *</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * @return the given regular expression compiled into a pattern with the given flags</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> * .......</span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token comment" style="color:rgb(98, 114, 164)"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain"></span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">static</span><span class="token plain"> </span><span class="token class-name">Pattern</span><span class="token plain"> </span><span class="token function" style="color:rgb(80, 250, 123)">compile</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token class-name">String</span><span class="token plain"> regex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">int</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(248, 248, 242)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(189, 147, 249);font-style:italic">new</span><span class="token plain"> </span><span class="token class-name">Pattern</span><span class="token punctuation" style="color:rgb(248, 248, 242)">(</span><span class="token plain">regex</span><span class="token punctuation" style="color:rgb(248, 248, 242)">,</span><span class="token plain"> flags</span><span class="token punctuation" style="color:rgb(248, 248, 242)">)</span><span class="token punctuation" style="color:rgb(248, 248, 242)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#F8F8F2"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(248, 248, 242)">}</span><br></span></code></pre><div class="buttonGroup__atx"><button type="button" aria-label="复制代码到剪贴板" title="复制" class="clean-btn"><span class="copyButtonIcons_eSgA" aria-hidden="true"><svg class="copyButtonIcon_y97N" viewBox="0 0 24 24"><path d="M19,21H8V7H19M19,5H8A2,2 0 0,0 6,7V21A2,2 0 0,0 8,23H19A2,2 0 0,0 21,21V7A2,2 0 0,0 19,5M16,1H4A2,2 0 0,0 2,3V17H4V3H16V1Z"></path></svg><svg class="copyButtonSuccessIcon_LjdS" viewBox="0 0 24 24"><path d="M21,7L9,19L3.5,13.5L4.91,12.09L9,16.17L19.59,5.59L21,7Z"></path></svg></span></button></div></div></div><p>常见的标志有这些：</p><table><thead><tr><th>标志</th><th>描述</th></tr></thead><tbody><tr><td>i</td><td>忽略大小写</td></tr><tr><td>g</td><td>全局搜索</td></tr><tr><td>m</td><td>多行修饰符：元字符 ^ $ 工作范围在每行的起始</td></tr><tr><td>s</td><td>单行修饰符</td></tr><tr><td>x</td><td>忽律空格符</td></tr><tr><td>u</td><td>忽律大小写，同时对 Unicode 编码字符集生效。不使用该标志时，默认只匹配 US-ASCII 字符集中的字符</td></tr><tr><td>U</td><td>启用对预定义类的 Unicode 支持</td></tr></tbody></table><h3 class="anchor anchorWithStickyNavbar_LWe7" id="贪婪匹配与惰性匹配greedy-matching--lazy-matching">贪婪匹配与惰性匹配（Greedy Matching &amp; Lazy Matching）<a class="hash-link" href="#贪婪匹配与惰性匹配greedy-matching--lazy-matching" title="标题的直接链接">​</a></h3><p>正则表达式默认采用贪婪匹配模式，在该模式下意味着会匹配尽可能长的子串。我们也可以使用 <code>?</code> 将贪婪匹配模式转化为惰性匹配模式。两者的不同可以通过下面的例子了解：</p><table><thead><tr><th>输入字符串</th><th>正则表达式</th><th>捕获到的子串</th></tr></thead><tbody><tr><td>hahahaha</td><td>(ha)+</td><td>hahahaha</td></tr><tr><td>hahahaha</td><td>(ha)+?</td><td>ha、ha、ha、ha</td></tr></tbody></table><h2 class="anchor anchorWithStickyNavbar_LWe7" id="java-中的差异化使用">Java 中的差异化使用<a class="hash-link" href="#java-中的差异化使用" title="标题的直接链接">​</a></h2><ul><li><code>\</code>转义字符必须替换为<code>\\</code>使用。</li><li>在支持匿名捕获的基础上，还支持命名式捕获，格式如 <code>(?&lt;name&gt;:asd)</code>，可以通过指定 name 的方式提取数据，类似于 K-V 数据中的 K。</li></ul>]]></content>
        <author>
            <name>Dylan Li</name>
            <email>higlowx@gmail.com</email>
            <uri>https://github.com/higlowx</uri>
        </author>
        <category label="正则表达式" term="正则表达式"/>
    </entry>
</feed>