回顾

大家好,我是老马。

最近 dubbo3.x 在公司内部分享,于是想系统梳理一下。

总体思路是官方文档入门+一些场景的问题思考+源码解析学习。


在 Dubbo 应用中使用 Zookeeper

本文介绍了 Zookeeper 的基本概念、用法,以及如何在 Dubbo 应用中使用 Zookeeper 作为注册中心。

Zookeeper 介绍

基本概念

在现代的分布式应用中,往往会出现节点和节点之间的协调问题,其中就包括了:选主、集群管理、分布式锁、分布式配置管理、统一命名服务、状态同步等诉求。Apache Zookeeper,正如它的名字所暗示的那样,动物园管理员,就是为了解决这些诉求的一个分布式协调服务框架。

为了保证高可用,ZooKeeper 本身也可以部署成集群模式,称之为 ZooKeeper ensemble。ZooKeeper 集群中始终确保其中的一台为 leader 的角色,并通过 ZAB (Zookeeper Atomic Broadcast Protocol) 协议确保所有节点上的信息的一致。客户端可以访问集群中的任何一台进行读写操作,而不用担心数据出现不一致的现象。

Diagram shows client-server architecture of ZooKeeper Image Credit : ebook -Zookeeper-Distributed Process Coordination from O’Reilly

Zookeeper 中的数据存储方式与传统的 UNIX 文件系统相似,节点按照树状结构来组织,其中,节点被称之为 znodes (ZooKeeper data nodes)

https://cn.dubbo.apache.org/imgs/blog/zk-emsemble.png

zk-tree Image Credit : ebook -Zookeeper-Distributed Process Coordination from O’Reilly

基本用法

可以通过直接下载的方式 安装并运行 Zookeeper ,在 Mac 上也可以通过 Homebrew brew install zookeeper 来安装,考虑到通用性,本文采用 docker 的方式来运行 Zookeeper。如果没有安装 docker,请先准备好 docker 环境 。

1. 启动 Zookeeper

执行命令将 Zookeeper 运行在 docker 容器中

  [bash]
1
docker run --rm --name zookeeper -p 2181:2181 zookeeper

2. 进入 Zookeeper 容器

  [bash]
1
docker exec -it zookeeper bash

bin 目录下有启动 Zookeeper 的命令 zkServer 以及管理控制台 zkCli

  [bash]
1
2
3
4
5
6
7
8
9
10
bash-4.4# ls -l bin total 36 -rwxr-xr-x 1 zookeepe zookeepe 232 Mar 27 04:32 README.txt -rwxr-xr-x 1 zookeepe zookeepe 1937 Mar 27 04:32 zkCleanup.sh -rwxr-xr-x 1 zookeepe zookeepe 1056 Mar 27 04:32 zkCli.cmd -rwxr-xr-x 1 zookeepe zookeepe 1534 Mar 27 04:32 zkCli.sh -rwxr-xr-x 1 zookeepe zookeepe 1759 Mar 27 04:32 zkEnv.cmd -rwxr-xr-x 1 zookeepe zookeepe 2696 Mar 27 04:32 zkEnv.sh -rwxr-xr-x 1 zookeepe zookeepe 1089 Mar 27 04:32 zkServer.cmd -rwxr-xr-x 1 zookeepe zookeepe 6773 Mar 27 04:32 zkServer.sh

3. 通过 zkCli 进入 Zookeeper 管理界面

由于是通过 Docker 启动,Zookeeper 进程已经启动,并通过 2181 端口对外提供服务。

  [bash]
1
2
3
4
5
bash-4.4# ps PID USER TIME COMMAND 1 zookeepe 0:02 /usr/lib/jvm/java-1.8-openjdk/jre/bin/java -Dzookeeper.log.dir=. -Dzookeeper.root 32 root 0:00 bash 42 root 0:00 ps

因此可以直接通过 zkCli 来访问 Zookeeper 的控制台来进行管理。

  [bash]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
bash-4.4# bin/zkCli.sh -server 127.0.0.1:2181 Connecting to 127.0.0.1:2181 ... WATCHER:: WatchedEvent state:SyncConnected type:None path null [zk: 127.0.0.1:2181(CONNECTED) 0] help ZooKeeper -server host:port cmd args stat path [watch] set path data [version] ls path [watch] delquota [-n|-b] path ls2 path [watch] setAcl path acl setquota -n|-b val path history redo cmdno printwatches on|off delete path [version] sync path listquota path rmr path get path [watch] create [-s] [-e] path data acl addauth scheme auth quit getAcl path close connect host:port

4. zkCli 上的一些基本操作

创建 /hello-zone 节点:

  [bash]
1
2
[zk: 127.0.0.1:2181(CONNECTED) 19] create /hello-zone 'world' Created /hello-zone

列出 / 下的子节点,确认 hello-zone 被创建:

  [bash]
1
2
[zk: 127.0.0.1:2181(CONNECTED) 20] ls / [zookeeper, hello-zone]

列出 /hello-zone 的子节点,确认为空:

  [bash]
1
2
[zk: 127.0.0.1:2181(CONNECTED) 21] ls /hello-zone []

获取存储在 /hello-zone 节点上的数据:

  [bash]
1
2
[zk: 127.0.0.1:2181(CONNECTED) 22] get /hello-zone world

在 Dubbo 中使用 Zookeeper

Dubbo 使用 Zookeeper 用于服务的注册发现和配置管理,在 Zookeeper 中数据的组织由下图所示:

dubbo-in-zk

首先,所有 Dubbo 相关的数据都组织在 /dubbo 的根节点下。

二级目录是服务名,如 com.foo.BarService

三级目录有两个子节点,分别是 providersconsumers,表示该服务的提供者和消费者。

四级目录记录了与该服务相关的每一个应用实例的 URL 信息,在 providers 下的表示该服务的所有提供者,而在 consumers 下的表示该服务的所有消费者。举例说明,com.foo.BarService 的服务提供者在启动时将自己的 URL 信息注册到 /dubbo/com.foo.BarService/providers 下;同样的,服务消费者将自己的信息注册到相应的 consumers 下,同时,服务消费者会订阅其所对应的 providers 节点,以便能够感知到服务提供方地址列表的变化。

准备示例代码

本文的代码可以在 https://github.com/apache/dubbo-samples/tree/master/3-extensions/registry/dubbo-samples-zookeeper 中找到。

1. 接口定义

定义一个简单的 GreetingService 接口,里面只有一个简单的方法 sayHello 向调用者问好。

  [java]
1
2
3
public interface GreetingService { String sayHello(String name); }

2. 服务端:服务实现

实现 GreetingService 接口,并通过 @Service 来标注其为 Dubbo 的一个服务。

  [java]
1
2
3
4
5
6
@Service public class AnnotatedGreetingService implements GreetingService { public String sayHello(String name) { return "hello, " + name; } }

3. 服务端:组装

定义 ProviderConfiguration 来组装 Dubbo 服务。

  [java]
1
2
3
4
@Configuration @EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.impl") @PropertySource("classpath:/spring/dubbo-provider.properties") static class ProviderConfiguration {}

dubbo-provider.properties 是在 Spring 应用中外置配置的方式,内容如下:

  [properties]
1
2
3
4
dubbo.application.name =demo-provider dubbo.registry.address=zookeeper://$DOCKER_HOST:2181 dubbo.protocol.name =dubbo dubbo.protocol.port=20880

由于 Zookeeper 运行在 Docker 容器中,需要注意的是:

  • 本文假定 Dubbo 应用运行在宿主机上,也就是 Docker 容器外,需要将 Zookeeper 的地址替换成环境变量 ${DOCKER_HOST} 所指定的 IP 地址,相关信息请查阅 Docker 官方文档
  • 如果 Dubbo 应用也是 Docker 化的应用,只需要用 Zookeeper 的容器名,在本文中容器名是 zookeeper
  • 当然,如果不用容器方式启动 Zookeeper,只需要简单的将这里的 $DOCKER_HOST 替换为 Zookeeper 的实际 IP 地址即可

3. 客户端:服务引用

在客户端,通过 @Reference 注解来引用远程的 GreetingService 服务。

  [java]
1
2
3
4
5
6
7
8
9
10
@Component public class GreetingServiceConsumer { @Reference private GreetingService greetingService; public String doSayHello(String name) { return greetingService.sayHello(name); } }

4. 客户端:组装

定义 ConsumerConfiguration 来组装 Dubbo 客户端。

  [java]
1
2
3
4
5
@Configuration @EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.action") @PropertySource("classpath:/spring/dubbo-consumer.properties") @ComponentScan(value = {"com.alibaba.dubbo.samples.action"}) static class ConsumerConfiguration {}

dubbo-consumer.properties 是在 Spring 应用中外置配置的方式,内容如下:

  [properties]
1
2
dubbo.application.name =demo-consumer dubbo.registry.address=zookeeper://$DOCKER_HOST:2181

运行示例

1. 启动 Zookeeper

确保 Zookeeper 已经启动并运行在 2181 端口。

  [bash]
1
docker run --rm --name zookeeper -p 2181:2181 zookeeper

2. 启动服务端

dubbo-samples-zookeeper 目录下,运行服务端应用。

  [bash]
1
2
3
mvn clean package cd dubbo-samples-zookeeper-provider mvn exec:java -Dexec.mainClass="com.alibaba.dubbo.samples.provider.ProviderBootstrap"

3. 启动客户端

dubbo-samples-zookeeper 目录下,运行客户端应用。

  [bash]
1
2
cd dubbo-samples-zookeeper-consumer mvn exec:java -Dexec.mainClass="com.alibaba.dubbo.samples.consumer.ConsumerBootstrap"

4. 查看结果

客户端启动后,会调用远程的 GreetingService 服务,并输出结果。

  [bash]
1
hello, world

总结

本文介绍了如何在 Dubbo 应用中使用 Zookeeper 作为注册中心,并通过示例代码演示了如何配置和运行 Dubbo 服务。

通过 Zookeeper,Dubbo 能够实现服务的自动发现和动态扩展,从而构建高可用的分布式系统。

更多关于 Dubbo 和 Zookeeper 的详细信息,请参考官方文档:

参考资料