初探zookeeper与dubbo

前言

好几没有写博客了!!!

来鹅厂实习快两个月,忙,各种忙,最近接手一个任务,需要部署一个使用tomcat+dubbo+zookeeper框架组合的一个项目,作为一个一直在啃C++,java菜的一匹的初级程序员,伤不起啊西巴。这两天恶补各种框架知识。

这篇先作为开胃,简单论述下dubbo和zookeeper之间的关系。后面应该还会出两篇分别深入分析dubbo和zookeeper的博客。

zookeeper

组成

官方定义:主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等。 简单的说,zookeeper=文件系统+通知机制。

zookeeper维护一个类似文件系统的数据结构。有根目录“/”,根目录下有子目录,每个目录项都被称为节点,与Linux文件系统的不同之处在于节点可以存储数据,也就是说zk保存的数据是存放在节点中的。

有四种类型的节点:

1、PERSISTENT-持久化目录节点

客户端与zookeeper断开连接后,该节点依旧存在

2、 PERSISTENT_SEQUENTIAL-持久化顺序编号目录节点

客户端与zookeeper断开连接后,该节点依旧存在,只是Zookeeper给该节点名称进行顺序编号

3、EPHEMERAL-临时目录节点

客户端与zookeeper断开连接后,该节点被删除

4、EPHEMERAL_SEQUENTIAL-临时顺序编号目录节点

客户端与zookeeper断开连接后,该节点被删除,只是Zookeeper给该节点名称进行顺序编号

特性

zk文件系统还有一个很重要的特性,就是事件监听机制。客户端可以注册监听它关心的节点,当在zk中建立节点或者删除节点的时候都会产生事件,注册了事件监听的客户端,就可以获得这些事件。也就是说,只要zk的节点变化了,那么数据肯定变化,那么注册器通过监控节点的变化就可以知道数据的变化。与此特点相对应的就是zk对集群的管理,我们可以在一个目录下创建零时节点,每次新加入管理的机器就会新增一个节点,当有一台机器挂掉就销毁一个节点。那么同样的监控这个机器目录的客户端就可以知道这些事件,从而进行后续的调度。

zk有好多nb的特性,后面会深入分析,今天只说下它与dubbo搭档所用到的集群管理特性。

dubbo

dubbo是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案。

组成

其核心部分包含:

  • 远程通信(Remoting):提供对多种基于长连接的NIO框架抽象封装,包括多种线程模型,序列化,以及“请求-响应”模式的信息交换方式。

  • 集群容错(RPC): 提供基于接口方法的透明远程过程调用,包括多协议支持,以及软负载均衡,失败容错,地址路由,动态配置等集群支持。

  • 注册中心(Registry): 目录服务用于服务的注册和服务事件发布和订阅,使服务消费方能动态的查找服务提供方,使地址透明,使服务提供方可以平滑增加或减少机器。

dubbo原理图如上,其中:

  • provider(服务提供者):暴露服务方。

  • consumer(服务消费者):调用远程服务方。

  • Registry(服务注册中心):服务提供者在此注册服务,服务消费者在此发现服务。

  • Monitor(服务监控中心):统计服务的调用次数和调用时间。

工作原理

  • 注册中心负责服务地址的注册与查找,相当于目录服务,服务提供者和消费者只在启动时与注册中心交互,注册中心不转发请求,压力较小 。

  • 监控中心负责统计各服务调用次数,调用时间等,统计先在内存汇总后每分钟一次发送到监控中心服务器,并以报表展示。

  • 服务提供者向注册中心注册其提供的服务,并汇报调用时间到监控中心,此时间不包含网络开销。

  • 服务消费者向注册中心获取服务提供者地址列表,并根据软负载均衡算法直接调用提供者,同时汇报调用时间到监控中心,此时间包含网络开销。

  • 注册中心,服务提供者,服务消费者三者之间均为长连接,监控中心除外。

  • 注册中心通过长连接感知服务提供者的存在,服务提供者宕机,注册中心将立即推送事件通知消费者。

  • 注册中心和监控中心全部宕机,不影响已运行的服务提供者和消费者,消费者启动时在本地缓存了服务提供者列表。但此时服务提供者状态或数量的改变,服务消费者无从得知。

  • 通过监控中心反馈的数据(频率,时间),可以作为是否应该增加机器来扩容的依据。

  • 注册中心对等集群,任意一台宕掉将自动切换到另一台。

  • 服务提供者全部宕掉后,服务消费者应用将无法使用,并无限次重连等待服务提供者恢复 。

zookeeper和dubbo的关系

Dubbo的将注册中心进行抽象,使得它可以外接不同的存储媒介给注册中心提供服务,有ZooKeeper,Memcached,Redis等。

引入了ZooKeeper作为存储媒介,也就把ZooKeeper的特性引进来。首先是负载均衡,单注册中心的承载能力是有限的,在流量达到一定程度的时候就需要分流,负载均衡就是为了分流而存在的,一个ZooKeeper群配合相应的Web应用就可以很容易达到负载均衡;资源同步,单单有负载均衡还不够,节点之间的数据和资源需要同步,ZooKeeper集群就天然具备有这样的功能;命名服务,将树状结构用于维护全局的服务地址列表,服务提供者在启动的时候,向ZK上的指定节点/dubbo/${serviceName}/providers目录下写入自己的URL地址,这个操作就完成了服务的发布。

示例

看了很多大牛的博客,挑了一个我认为很通俗易懂的,在这里做一个分享:

定义服务接口

1
2
3
4
5
6
7
8
9
10
package com.ricky.dubbo.api;

import com.ricky.dubbo.api.model.User;

public interface DemoService {

public String sayHello(String name);

public User findUserById(long id);
}
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
31
32
33
34
35
36
37
//User.java
package com.ricky.dubbo.api.model;

import java.io.Serializable;

public class User implements Serializable {

private static final long serialVersionUID = 1L;

private long id;
private String name;
private int age;

public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User [id=" + id + ", name=" + name + ", age=" + age + "]";
}

}

服务提供者(实现接口)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package com.ricky.dubbo.provider.impl;

import com.ricky.dubbo.api.DemoService;
import com.ricky.dubbo.api.model.User;

public class DemoServiceImpl implements DemoService {

@Override
public String sayHello(String name) {
return "Hello " + name;
}

@Override
public User findUserById(long id) {

User user = new User();
user.setId(id);
user.setName("Ricky");
user.setAge(26);

return user;
}

}

使用Spring配置生命暴露服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//provider.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="dubbo-provider-app" />

<!-- 使用zookeeper注册中心暴露服务地址 -->
<!-- 使用multicast广播注册中心暴露服务地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />

<!-- 用dubbo协议在20880端口暴露服务 -->
<dubbo:protocol name="dubbo" port="20880" />

<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.ricky.dubbo.api.DemoService" ref="demoService" />

<!-- 和本地bean一样实现服务 -->
<bean id="demoService" class="com.ricky.dubbo.provider.impl.DemoServiceImpl" />

</beans>

加载Spring配置

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
package com.ricky.dubbo.provider;

import java.io.IOException;

import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
* Dubbo provider
*
*/
public class App {

public static void main(String[] args) {

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "provider.xml" });
context.start();

System.out.println("Dubbo provider start...");

try {
System.in.read(); // 按任意键退出
} catch (IOException e) {
e.printStackTrace();
}

}
}

服务消费者,通过Spring配置引用远程服务

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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="consumer-of-helloworld-app" />

<!-- 使用zookeeper注册中心暴露服务地址 -->
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry address="multicast://224.5.6.7:1234" />

<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="com.ricky.dubbo.api.DemoService" />

</beans>

加载Spring配置,并调用远程服务

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
package com.ricky.dubbo.consumer;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.ricky.dubbo.api.DemoService;
import com.ricky.dubbo.api.model.User;

/**
* Dubbo Consumer client
*
*/
public class App {

public static void main(String[] args) {

ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
new String[] { "consumer.xml" });
context.start();

DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理
String hello = demoService.sayHello("ricky"); // 执行远程方法
System.out.println(hello); // 显示调用结果

User user = demoService.findUserById(15);
System.out.println(user); // 显示调用结果

}
}

zookeeper简介

百度百科

Dubbo实战(一)快速入门

dubbo官网文档