31

服务化改造实践(一):Dubbo + ZooKeeper

 5 years ago
source link: https://mp.weixin.qq.com/s/m0cdOpep15LrTL7h-qNM8g?amp%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

jiiUjiQ.jpg!web

“没有最好的技术,只有最合适的技术。”我想这句话也同样适用于微服务领域, 没有最好的服务框架,只有最适合自己的服务改造 。在 Dubbo 的未来规划中,除了保持自身技术上的领先性,关注性能、大流量、大规模集群领域的挑战外,围绕 Dubbo 核心来发展生态,将 Dubbo 打造成一个服务化改造的整体方案也是重点之一。 从本期开始,我们将推出“服务化改造”系列文章,通过在一些外围系统和服务化基础组件上的开发实践,分享Dubbo 生态下的服务化改造收获和总结。 

» 8月26日,Aliware Open Source 将首次在成都举办 Apache Dubbo 的meetup活动,Dubbo 、Sentinel和 Nacos 的小哥哥和小姐姐都会在现场进行技术分享,欢迎成都的朋友报名参加我们的活动喔,公众号后台发送“ 成都meetup ”,获取报名链接。

一、改造背景

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

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

nuIfAfa.jpg!web

O'Reilly的ebook-Zookeeper-Distributed Process Coordination

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

zIvaEfn.jpg!web

O'Reilly的ebook-Zookeeper-Distributed Process Coordination

二、基本用法

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

1、启动  ZooKeeper

执行命令将  ZooKeeper,运行在docker容器中

docker run --rm --name zookeeper -p 2181:2181 zookeeper


2、进入 Zookeeper 容器

docker exec -it zookeeper bash

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

bash-4.4# ls -l bintotal 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-4.4# psPID   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-4.4# bin/zkCli.sh -server 127.0.0.1:2181Connecting to 127.0.0.1:2181...
WATCHER::

WatchedEvent state:SyncConnected type:None path:null[zk: 127.0.0.1:2181(CONNECTED) 0] helpZooKeeper -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 节点:

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

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

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

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

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

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

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

三、在 Dubbo 中使用ZooKeeper

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

IjUBneN.jpg!web

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

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

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

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

四、准备示例代码

本文代码可以在以下链接中找到。

https://github.com/dubbo/dubbo-samples/tree/master/dubbo-samples-zookeeper

1、接口定义

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

public interface GreetingService {
    String sayHello(String name);}

2、服务端:服务实现

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

@Servicepublic class AnnotatedGreetingService implements GreetingService {
    public String sayHello(String name) {
        return "hello, " + name;
    }}

3、服务端:组装

定义 ProviderConfiguration 来组装Dubbo服务。

@Configuration@EnableDubbo(scanBasePackages = "com.alibaba.dubbo.samples.impl")@PropertySource("classpath:/spring/dubbo-provider.properties")static class ProviderConfiguration {}

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

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换成localhost即可。

4、服务端:启动服务

main 方法中通过启动一个Spring Context来对外提供Dubbo服务。

public class ProviderBootstrap {
    public static void main(String[] args) throws Exception {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ProviderConfiguration.class);
        context.start();
        System.in.read();
    }}

服务启动端的的 main 方法,将会看到下面的输出,代表服务端启动成功,并在注册中心( ZooKeeper Registry)注册上了 GreetingService 这个服务

[03/08/18 10:50:33:033 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Register: dubbo://192.168.99.1:20880/com.alibaba.dubbo.samples.api.GreetingService?anyhost=true&application=demo-provider&dubbo=2.6.2&generic=false&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=12938&side=provider&timestamp=1533264631849, dubbo version: 2.6.2, current host: 192.168.99.1

通过 ZooKeeper 管理终端观察服务提供方的注册信息:

$ docker exec -it zookeeper bash
bash-4.4# bin/zkCli.sh -server localhost:218
Connecting to localhost:2181
...
Welcome to ZooKeeper!
JLine support is enabled
...
[zk: localhost:2181(CONNECTED) 0] ls /dubbo/com.alibaba.dubbo.samples.api.GreetingService/providers
[dubbo%3A%2F%2F192.168.99.1%3A20880%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.6.2%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D12938%26side%3Dprovider%26timestamp%3D1533264631849]

可以看到刚刚启动的Dubbo的服务在 providers 节点下注册了自己的URL地址:dubbo://192.168.99.1:20880 /com.alibaba.dubbo.samples.api.GreetingService?anyhost = true&application = demo-provider&dubbo =2.6 0.2&通用=假接口=com.alibaba.dubbo.samples.api.GreetingService&方法= sayHello的&PID = 12938&侧=提供商时间戳=1533264631849

5、客户端:引用服务

通过 @Reference 来在客户端声明服务的引用,运行时将会通过该引用发起全程调用,而服务的目标地址将会从 ZooKeeper provider 节点下查询。

@Component("annotatedConsumer")public class GreetingServiceConsumer {
    @Reference
    private GreetingService greetingService;
    
    public String doSayHello(String name) {
        return greetingService.sayHello(name);
    }}


6、客户端:组装

定义ConsumerConfiguration来组装Dubbo服务。

@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应用中外置配置的方式,内容如下:

dubbo.application.name=demo-consumer
dubbo.registry.address=zookeeper://$DOCKER_HOST:2181
dubbo.consumer.timeout=3000

与服务端:组装相同,需要根据自己的运行环境来修改dubbo.registry.address中定义的$ DOCKER_HOST。请参阅步骤3的说明部分。

7、客户端:发起远程调用

运行 main 向已经启动的服务提供方发起一次远程调用。Dubbo会先向 ZooKeeper 订阅服务地址,然后从返回的地址列表中选取一个,向对端发起调用:

public class ConsumerBootstrap {
    public static void main(String[] args) {public class ConsumerBootstrap {

    public static void main(String[] args) throws IOException {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ConsumerConfiguration.class);
        context.start();
        GreetingServiceConsumer greetingServiceConsumer = context.getBean(GreetingServiceConsumer.class);
        String hello = greetingServiceConsumer.doSayHello("zookeeper");
        System.out.println("result: " + hello);
        System.in.read();
    }

}

运行结果如下:

[03/08/18 01:42:31:031 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Register: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&category=consumers✓=false&default.timeout=3000&dubbo=2.6.2&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=82406&side=consumer&timestamp=1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 #1[03/08/18 01:42:31:031 CST] main  INFO zookeeper.ZookeeperRegistry:  [DUBBO] Subscribe: consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application=demo-consumer&category=providers,configurators,routers&default.timeout=3000&dubbo=2.6.2&interface=com.alibaba.dubbo.samples.api.GreetingService&methods=sayHello&pid=82406&side=consumer&timestamp=1533274951195, dubbo version: 2.6.2, current host: 192.168.99.1 #2...
result: hello, zookeeper

#说明:

a. 注册:消费者://192.168.99.1/...& category= consumers &:消费者向 ZooKeeper 注册自己的信息,并放在 consumers 节点下

b. 订阅:消费者://192.168.99.1/...& 类别=提供商,配置器,路由器:消费者同时向动物园管理员订阅了 providers configurators routers 节点,其中 configurations   与多宝配置相关, routers 与路由规则相关,值得注意的英文 providers 节点的订阅,当有新的服务提供方加入后,由于订阅的关系,新的地址列表会推送给订阅方,服务的消费者也因此动态感知到了地址列表的变化。

通过 ZooKeeper 管理终端观察服务提供方的注册信息:

$ docker exec -it zookeeper bash
bash-4.4# bin/zkCli.sh -server localhost:218
Connecting to localhost:2181
...
Welcome to ZooKeeper!
JLine support is enabled
...
[zk: localhost:2181(CONNECTED) 4] ls /dubbo/com.alibaba.dubbo.samples.api.GreetingService/consumers
[consumer%3A%2F%2F192.168.99.1%2Fcom.alibaba.dubbo.samples.api.GreetingService%3Fapplication%3Ddemo-consumer%26category%3Dconsumers%26check%3Dfalse%26default.timeout%3D3000%26dubbo%3D2.6.2%26interface%3Dcom.alibaba.dubbo.samples.api.GreetingService%26methods%3DsayHello%26pid%3D82406%26side%3Dconsumer%26timestamp%3D1533274951195]

可以看到Dubbo的服务消费者在 consumers 节点下注册了自己的URL地址:consumer://192.168.99.1/com.alibaba.dubbo.samples.api.GreetingService?application= demo-consumer&category= provider,configurators,routers&default。超时= 3000&达博= 2.6.2&接口= com.alibaba.dubbo.samples.api.GreetingService&方法=sayHello的&PID =82406&侧=消费者的时间戳=1533274951195

五、总结

本文侧重介绍了如何在Dubbo应用中使用Zookeeper做为注册中心,当然,本文也提到了Zookeeper在Dubbo的应用场景下还承担了配置中心和服务治理的职责。本文中的Zookeeper是单节点,Standalone的模式,在生产环境中为了高可用的诉求,往往会组件Zookeeper集群,也就是Zookeeper ensemble模式。

通过本文的学习,读者可以掌握到:

  • ZooKeeper 的基本概念和基本用法

  • ZooKeeper 在Dubbo应用中的作用

  • 通过实战了解 ZooKeeper 与Dubbo的交互

  • Dubbo在 ZooKeeper 中服务注册,消费信息的存储方式

当然,自从阿里巴巴开源  Nacos  后, Dubbo生态中又多了一项动态服务发现的选项, Nacos +  Dubbo的组合正进一步释放  Dubbo  在云原生及ServiceMesh时代中,在大规模微服务治理、流量治理、服务集成与服务共享等服务平台能力建设上的威力。详情请见文末 “今日推文”。

参考链接:

[1] https://www.ixiacom.com/company/blog/apache-zab-zookeeper-atomic-broadcast-protocol

[2] https://www.apache.org/dyn/closer.cgi/zookeeper/

[3] https: //brew.sh

[4] https://www.docker.com/community-edition

今日推文

点击下方图片即可阅读

» 阿里巴巴发布 Nacos v0.1.0版本,回顾自研 10 年路

fueiey6.jpg!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK