# Dubbo分布式治理

官方网站：[apache dubbo](http://dubbo.apache.org/zh-cn/) Github: [incubator-dubbo](https://github.com/apache/incubator-dubbo)

许多公司采用 dubbo + spring boot + docker 的架构方式。

## dubbo 是什么？

dubbo是一个分布式服务框架，提供高性能的以及透明化的RPC远程服务调用解决方案，以及SOA服务治理方案。

**dubbo可以解决的问题（解决了不好治理的问题）**：

1. url维护，订单系统会调用用户系统，用户系统也会调用订单系统，调用过多，url无法统一管理 解决方案：注册中心（zookeeper，redis，memcached..） 将对外暴露的服务url全部保存在第三方中间件上
2. F5负载均衡压力较大，成本较高 解决方案：软负载均衡（zookeeper...）
3. 服务架构依赖混乱，如果理出架构关系? 需要自动整理架构关系的方式
4. 服务器调用量越来越大，服务器的容量问题如何评估？（评估用于扩容指标） 需要监控平台，监控调用量，响应事件

**dubbo的核心功能**：

1. 远程通信
2. 集群容错
3. 服务自动发现
4. 负载均衡
5. 基于url的总线驱动(所有信息都是通过url传递的)

## dubbo的架构

核心角色：

```
Provider
Consumer
Registry
Monitor
Container
```

![](/files/iANm3x3ZvQhbePObmN9U)

* Provider 提供服务，比如tomcat
* Registry 注册中心，Provider将启动后的url注册到Registry
* Consumer订阅Registry，Registry在发生变化时，将通知Consumer地址发生变化。
* Consumer通过Registry提供的url 远程访问Provider
* Monitor负责监控Consumer和Provider，解决调用关系以及调用量的监控问题

## dubbo的简单使用

dubbo相关的demo位于： [Github](https://github.com/xf616510229/dubbo-demo)

**构建provider**：

1. 创建order-api模块，用于向其他模块提供接口。其中，此模块中只包含Java业务Server接口和输入输出对象。
2. 创建order-provider模块，此模块依赖order-api模块，是order服务的具体实现；需要依赖dubbo与zkclient

   ```xml
   <dependency>
       <groupId>com.alibaba</groupId>
       <artifactId>dubbo</artifactId>
   </dependency>
   <dependency>
       <groupId>com.github.sgroschupf</groupId>
       <artifactId>zkclient</artifactId>
   </dependency>
   ```
3. 在 `resources/META-INF/spring/`目录下创建 `order-consumer.xml` 文件，用于定义dubbo服务

   ```xml
   <?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
          xsi:schemaLocation="http://www.springframework.org/schema/beans        
              http://www.springframework.org/schema/beans/spring-beans.xsd        
              http://code.alibabatech.com/schema/dubbo        
              http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

       <!--当前项目在整个分布式架构里面的唯一名称，计算依赖关系的标签-->
       <dubbo:application name="order-provider" owner="feathers"/>
       
       <!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
       <dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183"/>
       <!--或者这样写-->
       <!--<dubbo:registry address="zookeeper://127.0.0.1:2181?backup=127.0.0.1:2181,127.0.0.1:2181"/>-->
       <!--或者不使用注册中心, 这样就不会将提供的接口地址存入到zookeeper中，需要消费者使用url消费-->
       <!--<dubbo:registry address="N/A"/>-->

       <!--当前服务发布所依赖的协议；webserovice、Thrift、Hessain、http、dubbo-->
       <dubbo:protocol name="dubbo" port="20880"/>

       <!--服务发布的配置，需要暴露的服务接口-->
       <dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService"/>
       
       <!--暴露的接口的实现-->
       <bean id="orderService" class="me.feathers.demo.order.impl.OrderServiceImpl"/>
       
       <!--<dubbo:monitor protocol="registry"/>-->

   </beans>
   ```
4. 编写启动类，注册服务到注册中心zookeeper

   ```java
    import com.alibaba.dubbo.container.Main;

    public static void main(String[] args) throws IOException {
       Main.main(args);
       System.in.read();
   }
   ```
5. 查看zookeeper，发现dubbo节点下已经注册了一个url地址，使用此地址，可以直接调用dubbo服务: !\[]\(../../../../../../OneDrive/note/Java/Distributed/images/20190313205831318\_24519.png =687x)

**创建consumer：**

1. 创建项目client，并在`resources`目录下创建配置文件`order-consumer.xml`，此配置文件用于配置远程代理对象：

   ```xml
   <?xml version="1.0" encoding="UTF-8"?>
   <beans xmlns="http://www.springframework.org/schema/beans"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
          xsi:schemaLocation="http://www.springframework.org/schema/beans        
              http://www.springframework.org/schema/beans/spring-beans.xsd        
              http://code.alibabatech.com/schema/dubbo        
              http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
       <!--当前项目在整个分布式架构里面的唯一名称，计算依赖关系的标签-->
       <dubbo:application name="order-provider" owner="feathers"/>

       <!--dubbo这个服务所要暴露的服务地址所对应的注册中心-->
       <!--<dubbo:registry address="zookeeper://127.0.0.1:2181?backup=127.0.0.1:2182,127.0.0.1:2183"/>-->

       <!--生成一个远程服务的调用代理, 使用注册中心获取url调用-->
       <!--<dubbo:reference id="orderServices" interface="me.feathers.demo.order.OrderService"/>-->
       <!--生成一个远程服务的调用代理, 使用地址直连（测试使用）-->
       <dubbo:reference id="orderServices" interface="me.feathers.demo.order.OrderService"
           url="dubbo://10.0.75.1:20880/me.feathers.demo.order.OrderService"/>
   </beans>
   ```
2. 使用Main启动consumer，并且利用创建好的服务代理对象调用服务：

   ```java
   public class App {
       public static void main(String[] args) throws IOException {

           // 调用服务

           // 1. 从spring容器中获取远程服务代理对象
           ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("order-consumer.xml");

           // 2. 调用下单接口
           OrderService services = (OrderService) context.getBean("orderServices");

           Order order = new Order();
           order.setProductName("哈哈");
           order.setUserId("u112000");
           order.setAmount("$1");
           order.setOrderId(123L);
           BaseOrderResponse response = services.addOrder(order);
           
           // 3. 展示响应
           System.out.println(response);
           System.in.read();
       }
   }
   ```

### dubbo-admin

`dubbo-admin`用来做服务治理，比如控制服务权重、服务路由等。

`dubbo-admin`在`dubbo2.7`时，被单独列入了一个仓库：[Dubbo Admin Github](https://github.com/apache/incubator-dubbo-admin.git)；如果是2.6版本，则是在`dubbo-admin`模块中([地址](https://github.com/apache/incubator-dubbo/tree/dubbo-2.6.0))

将项目clone下来，可以依照[README](https://github.com/apache/incubator-dubbo-admin/blob/develop/README_ZH.md)进行配置。

新的`dubbo-admin`项目结构如下： ![](/files/YATQ2wtqSxC7ulBq7zp8)

其中，`dubbo-admin-ui` 是 前端项目，`dubbo-admin-server` 是接口服务。

1. 修改 `dubbo-admin-server\src\main\resources\application.properties`文件的相关内容，配置注册中心地址，用户名密码等信息
2. 在`dubbo-admin-server` 执行`mvn clean package -Dmaven.test.skip=true` 将项目打包为jar
3. 执行jar文件，启动接口服务
4. 进入`dubbo-admin-ui` 前端项目，执行 `npm install`
5. 执行完毕后，使用`npm run dev` 或者 `npm run build` 运行项目，并打开网址测试

新的dubbo控制台界面如下：

![image-20220307201955194](/files/seFBlg23fCBqWtn0fAtf)

### dubbo-monitor

`dubbo-monitor`用来监控服务的调用次数,调用关系、响应时间等。

dubbo-monitor-simple 是一个简单的dubbo监控平台，具体安装方式如下：

文件： [dubbo-monitor-simple-2.5.3.zip](https://github.com/yangsx95/notes/blob/master/distributed/服务治理/attachments/20190315200217886_6841/dubbo-monitor-simple-2.5.3.zip)

1. 修改每个dubbo应用程序的xml文件，加入如下配置: `<dubbo:monitor protocol="registry"/>`，添加monitor监控
2. 修改配置文件，如下：

   ```properties
   dubbo.container=log4j,spring,registry,jetty
   dubbo.application.name=simple-monitor
   dubbo.application.owner=
   # 配置注册中心
   # dubbo.registry.address=multicast://224.5.6.7:1234
   dubbo.registry.address=zookeeper://127.0.0.1:2181
   #dubbo.registry.address=redis://127.0.0.1:6379
   #dubbo.registry.address=dubbo://127.0.0.1:9090
   dubbo.protocol.port=7070
   # jetty容器端口，防止端口占用
   dubbo.jetty.port=8099
   # jetty存储路径，需要手动创建，否则图表不展示
   dubbo.jetty.directory=${user.home}/monitor
   # 图表路径,需要手动创建
   dubbo.charts.directory=${dubbo.jetty.directory}/charts
   # 统计信息路径,需要手动创建
   dubbo.statistics.directory=${user.home}/monitor/statistics
   # 日志存储路径
   dubbo.log4j.file=logs/dubbo-monitor-simple.log
   # 日志级别
   dubbo.log4j.level=WARN
   ```
3. 执行`bin/start.sh`或者`start.bat`
4. 访问`localhost:8099`: ![](/files/FFzIHJk3YIAKdxmVFs0h)

### 启动服务检查

启动Consumer时，默认回去检测所依赖的服务是否正常提供服务。

```xml
<dubbo:reference id="orderServices" interface="me.feathers.demo.order.OrderService"
            url="dubbo://10.0.75.1:20880/me.feathers.demo.order.OrderService" check="true"/>
```

即默认选项为 `check=true`；如果将check设置为false，则即使服务不可用，Consumer也可以正常启动。

除此之外，一下标签也拥有`check`属性：

* `dubbo:consumer, check=true/false`: true,没有服务提供者的时候，报错
* `dubbo:registry, check=true/false`: true,注册订阅失败时报错

## telnet命令

```
telnet localhost 20880

# 进入dubbo控制台，提供了一些命令可以对服务进行操作，类似linux命令
# 查看该dubbo应用程序提供的所有服务
dubbo>ls
me.feathers.demo.order.OrderService

# 进入OrderServer类级别服务
dubbo>cd me.feathers.demo.order.OrderService
Used the me.feathers.demo.order.OrderService as default.
You can cancel default service by command: cd /

# 查看类级别服务下的所有方法级别服务
dubbo>ls
Use default service me.feathers.demo.order.OrderService.
addOrder
deleteOrder
getOrderById

# 查看当前所在的路径
dubbo>pwd
me.feathers.demo.order.OrderService

# 使用invoke调用服务
dubbo>invoke me.feathers.demo.order.OrderService.deleteOrder('oid123')
Use default service me.feathers.demo.order.OrderService.
{"msg":"删除订单成功","code":"00-00"}
elapsed: 2 ms.

# 可以传入对象参数，对象参数是一个json
dubbo>invoke me.feathers.demo.order.OrderService.addOrder({userId:'123', orderId:111, productName:'food', amount:'18.5'})
Use default service me.feathers.demo.order.OrderService.
{"msg":"添加订单成功","code":"00-00"}
elapsed: 0 ms.

# 清屏
clear
```

### Qos

**参考**：[Dubbo启动时qos-server can not bind localhost:22222错误解决](https://blog.csdn.net/u012988901/article/details/84503672)

telnet 默认不可以远程调用，如需配置远程调用，需要加入如下参数：

```xml
<dubbo:application name="simple-provider">
	<dubbo:parameter key="qos.enable" value="true" />
	<dubbo:parameter key="qos.accept.foreign.ip" value="true" />
	<dubbo:parameter key="qos.port" value="22223" />
</dubbo:application>
```

其中，qos是指Quality of Service，是dubbo的在线运维命令，`qos.enable`代表是否开启在线运维命令，`qos.accept.foreign.ip` 是否可以远程连接，`qos.port`qos的端口(默认端口22222，如果被占用，则会抛出qos-server can not bind localhost:22222的错误)

## 多协议的支持

dubbo本身使用的RPC协议是dubbo协议（服务之间的通信协议），同时，dubbo也支持其他协议，比如：

* dubbo
* RMI
* Hessian
* WebService
* Http
* Thrift

### Hessian 协议

Dubbo支持使用[Hessian](http://hessian.caucho.com)作为RPC协议，Hessian协议是[Caucho](https://caucho.com/)开源的一个RPC框架，Hessian协议底层采用Http作为传输协议，序列化采用Hessian二进制序列化。

Hessian协议底层采用Http通讯，并使用JavaServlet暴露服务，缺省内嵌Jetty作为服务器实现。所以使用Hessian，需要添加两个包，Hessian和Jetty：

```xml
    <dependency>
    　　<groupId>com.caucho</groupId>
    　　<artifactId>hessian</artifactId>
    　　<version>4.0.7</version>
    </dependency>

　　<dependency>
　　　　<groupId>org.mortbay.jetty</groupId>
　　　　<artifactId>jetty</artifactId>
　　　　<version>6.1.26</version>
　　</dependency>
```

在Provider中添加对Hessian协议的支持：

```xml
<!--dubbo支持多协议同时配置，此处增加hessian协议-->
<dubbo:protocol name="hessian" port="8090" server="jetty"/>
```

此时查看注册中心，就会发现，在providers节点下，多出一个子节点：

![](/files/JF2xSx8QfXMOCspCJ77h)

客户端需要在调用服务时，指定所使用的RPC协议：

```xml
<!--使用protocol指定调用协议-->
<dubbo:reference id="orderServices" interface="me.feathers.demo.order.OrderService" check="true" protocol="hessian"/>
```

## 多注册中心支持（不常用）

针对部分服务发布到指定的注册中心，通常用于**注册中心分组**、**注册中心热备份**等功能，实现很简单，只要在需要进行多注册中心的provider与consumer中添加多个不同的registry即可：

```xml
<!--多注册中心支持, 因为是多个注册中心，所以，需要为每个注册中心指定id    -->
<dubbo:registry id="reg1" protocol="zookeeper" address="xxx"/>
```

为指定的服务指定注册中心，只需要在`<dubbo:service` 上指定注册中心即可：

```xml
<dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService" registry="reg1"/>
```

## 服务多版本支持

现在，针对OrderService，系统需要发布一个新的2.0版本，新增一个OrderService实现：OrderServiceImpl2

![](/files/ciBQxbpWUwaLHGBq9u67)

为了保证旧版用户正常使用，需要配置dubbo服务版本：

```xml
<!--服务发布的配置，需要暴露的服务接口-->
<dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService" version="1.0"/> 
<!--新版OrderService-->
<dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService2" version="2.0"/>

<!--暴露的接口的实现-->
<bean id="orderService" class="me.feathers.demo.order.impl.OrderServiceImpl"/>
<bean id="orderService2" class="me.feathers.demo.order.impl.OrderServiceImpl2"/>
```

更新provider，在注册中心就会提供两个OrderService服务，一个版本1.0一个版本2.0：

!\[]\(../../../../../../OneDrive/note/Java/Distributed/images/20190318101350399\_21345.png =730x)

comsumer在使用时，同样需要指定version：

```xml
 <dubbo:reference id="orderServices" interface="me.feathers.demo.order.OrderService" protocol="hessian" version="2.0"/>
```

## 异步调用

> 注意：异步调用只支持dubbo协议

调用接口时，调用过程可能较慢或者无需同步等待调用的返回结果，此时就需要异步调用。异步调用，服务端无需变动，只需要在客户端方面添加如下配置即可：

```xml
<dubbo:reference id="orderServices" 
                     interface="me.feathers.demo.order.OrderService" 
                     async="true"/> <!--异步调用-->
<!--指定某个方法异步调用-->
<dubbo:reference id="orderServices" 
                 interface="me.feathers.demo.order.OrderService" 
                 protocol="dubbo" >
    <dubbo:method name="addOrder" async="true"/>
</dubbo:reference>
```

并使用`RpcContext`对象获取调用的返回结果：

```java
//异步调用
services.addOrder(order);
Future<BaseOrderResponse> future = RpcContext.getContext().getFuture();
BaseOrderResponse response = null;
try {
    response = future.get(); // 阻塞等待
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}
// 3. 展示响应
System.out.println(response);
```

## 主机绑定

**provider发布服务时，ip地址是从何而来的？**

```java
###com.alibaba.dubbo.config.ServiceConfig###

// 1. ProtocolConfig中如果绑定了host，则使用绑定的host
String host = protocolConfig.getHost();
if (provider != null && (host == null || host.length() == 0)) {
// 2. 如果protocolConfig中没有配置，则从providerConfig中获取host
    host = provider.getHost();
}

boolean anyhost = false;
if (NetUtils.isInvalidLocalHost(host)) {
    anyhost = true;
    try {
        // 3. 如果上述两个都没有配置host或者都是不正确的host
        // 则从InetAddress中获取host
        host = InetAddress.getLocalHost().getHostAddress();
    } catch (UnknownHostException e) {
        logger.warn(e.getMessage(), e);
    }
    // 4. 如果仍然获取不到，则使用
    if (NetUtils.isInvalidLocalHost(host)) {
        if (registryURLs != null && registryURLs.size() > 0) {
            for (URL registryURL : registryURLs) {
                try {
                    Socket socket = new Socket();
                    try {
                        // 如果仍然获取不到host，则发起连接到注册中心，再获取连接过去后本地的host
                        SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                        socket.connect(addr, 1000);
                        host = socket.getLocalAddress().getHostAddress();
                        break;
                    } finally {
                        try {
                            socket.close();
                        } catch (Throwable e) {}
                    }
                } catch (Exception e) {
                    logger.warn(e.getMessage(), e);
                }
            }
        }
        if (NetUtils.isInvalidLocalHost(host)) {
            // 6. 如果仍然获取不到，使用NetUtils获取
            host = NetUtils.getLocalHost();
        }
    }
}
```

**绑定主机地址：**

```xml
<!--使用protocolConfig绑定host-->
<dubbo:protocol name="dubbo" port="20880" host="10.35.10.170"/>

<!--使用providerConfig绑定host-->
<dubbo:provider host="10.35.10.170"/>
```

**绑定后的ip：**

!\[]\(../../../../../../OneDrive/note/Java/Distributed/images/20190318110512123\_17445.png =834x)

## 服务只订阅

服务只订阅是指，在测试环境下，有可能需要调用注册中心的服务，但是又不能将当前服务注册到注册中心的情况，处理也很简单：

```xml
<dubbo:registry protocol="zookeeper" address="127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183" subscribe="true" register="false"/>
<!-- 订阅注册中心的服务，但是不注册当前服务，通常用于测试联调-->
```

## 服务只注册

```xml
<!--注册中心只注册，常用于多个注册中心的情况-->
<dubbo:registry subscribe="false"/>
```

## 连接超时 timeout

一般情况下，一个服务允许的超时时间为 5\~10 s。

```xml
服务端配置：
<dubbo:provider timeout="5000"/>

客户端配置：
<dubbo:reference id="orderServices" 
                     interface="me.feathers.demo.order.OrderService" 
                     timeout="5000"/>
```

## 服务集群和负载均衡

dubbo集群配置比较简单，不需要进行任何配置，只需将服务在其他机器上运行再向注册中心注册一次即可，代码就不再延时。

### 负载均衡策略

Dubbo共有以下几种负载均衡策略：

* Random, 随机分配到某个服务，这是Dubbo默认的负载均衡策略
* RoundRobin，轮询：按照公约后的权重设置轮询比率
* LeastActive LoadBalance，最少活跃用数，即响应时间较短的服务优先
* Consistent LoadBalance，一致性hash

四种负载均衡策略，位于源代码模块`dubbo-cluster`的`org.apache.dubbo.rpc.cluster.loadbalance`包下。

### 配置负载均衡策略

```xml
<dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService" loadbalance="xxx"/>

支持的值： 
random
roundrobin
leastactive
consistent
```

## 集群容错

**dubbo提供了以下几种集群容错策略**：

* failover cluster：失败时自动切换其他服务器。通过 `retries=2` 来设置重试次数；这是dubbo的默认策略
* failfast cluster：快速失败，只发起一次调用。比如写操作，如新增数据，避免重复插入，这是非幂等请求(发起一次请求与发起多次请求所得的结果时不变的)
* failsafe cluster：失败安全，即使失败也不抛异常，比如操作日志
* failback cluster：失败自动恢复，后台记录失败请求，定时重发
* forking cluster：并行调用多个服务器，只要有一个成功就返回，只能用于读请求（会浪费服务器资源）
* broadcast cluster：广播调用所有提供者，其中一台报错就会抛出异常

**配置集群容错：**

```xml
服务端配置： 
<dubbo:service interface="me.feathers.demo.order.OrderService" ref="orderService2" version="2.0" cluster="failback"/>

客户端配置：
<dubbo:reference id="orderServices" 
                     interface="me.feathers.demo.order.OrderService" 
                     protocol="dubbo" 
                     version="2.0"
                     cluster="failback"/>
```

## 服务配置优先级

```
客户端>服务端
服务方法 > 服务

reference.method > service.method > reference > service > consumer > provider
```

## dubbo 企业应用配置

[代码展示](https://github.com/xf616510229/dubbo-example)

### 分包

```
web-xxx  web模块，提供controller
dubbo-user 用户模块，提供用户相关业务的service
    user-api  service接口定义，dubbo consumer消费xml配置(放入META-INFO/client下,调用者使用spring import标签引入配置文件，从而注入接口)、dto
    user-provider server具体实现
dubbo-order
    order-api
    order=provider
```

1. 将order-api与order-provider放入单一项目，其他user也放入单一项目，而不是全部采用多模块的方式放在一起。可以方便项目进行团队管理
2. 服务接口、请求服务模型、异常信息都放在api模块下，复合重用等价原则，共同重用原则
3. api模块中放入spring的引用配置，也可以放在模块的包目录下。`com.xxx.xxx/xx-references.xml`

### 粒度

1. 尽可能将接口设置为粗粒度，每个服务方法代表一个独立的功能，而不是某个功能的步骤。否则会涉及到分布式事务
2. 服务接口建议以业务场景为单位划分。并对相近业务进行抽象，防止接口暴增
3. 不建议使用过于抽象的通用接口，比如泛型，这样的接口没有明确的语义，会使后期维护困难

### 版本

1. 每个接口都应该定义版本，为后续的兼容性提供前瞻性考虑 (version)，注意maven快照版本与发布版本的管理
2. 建议使用两位版本号，第三位版本号则用于表示兼容性升级，只有不兼容时才需要变更服务版本
3. 当接口做到不兼容升级的时候，先升级一半或者一台提供者为新版本，再将消费者全部升级为新版本，最后将所有提供者升级为新版本

### 推荐配置

* 再provider端尽可能consumer端中需要配置的属性，比如timeout、retires、loadBalance、线程池大小
* 配置管理员信息，即在`application`中配置的`owner`属性，建议配置多个，出问题可以找到相关人员

### 配置缓存文件

当注册中心集群出现问题，将会造成整个服务瘫痪，可以使用缓存文件，记录注册中心中的状态；当注册中心出现问题，将会从缓存文件中获取服务地址进行使用，进一步保证了服务的可用性。

```xml
<dubbo:registry protocol="zookeeper" file="/data/dubbo/cache/zookeeper.cache" address="xxx"/>
```

缓存文件将会缓存：

* 注册中心列表
* 服务提供者列表

### 配置异常处理

尽量不要使用返回码作为返回信息，推荐使用异常代替。


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://yangsx95.gitbook.io/notes/distributed/fu-wu-zhi-li/dubbo-fen-bu-shi-zhi-li.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
