> For the complete documentation index, see [llms.txt](https://yangsx95.gitbook.io/notes/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://yangsx95.gitbook.io/notes/distributed/fu-wu-zhi-li/dubbo-fen-bu-shi-zhi-li.md).

# 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"/>
```

缓存文件将会缓存：

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

### 配置异常处理

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