Zookeeper 分布式队列

在传统的单进程编程中,我们使用队列来存储一些数据结构,用来在多线程之间共享或传递数据。

分布式环境下,我们同样需要一个类似单进程队列的组件,用来实现跨进程、跨主机、跨网络的数据共享和数据传递,这就是我们的分布式队列。

zookeeper可以通过顺序节点实现分布式队列。

架构图

clipboard.png

图中左侧代表zookeeper集群,右侧代表消费者和生产者。
生产者通过在queue节点下创建顺序节点来存放数据,消费者通过读取顺序节点来消费数据。

阅读更多

Zookeeper 分布式命名服务

zookeeper的命名服务有两个应用方向,一个是提供类似JNDI的功能,利用zookeepeer的树型分层结构,可以把系统中各种服务的名称、地址以及目录信息存放在zookeeper,需要的时候去zookeeper中读取。

另一个,是利用zookeeper顺序节点的特性,制作分布式的ID生成器,写过数据库应用的朋友都知道,我们在往数据库表中插入记录时,通常需要为该记录创建唯一的ID,在单机环境中我们可以利用数据库的主键自增功能。但在分布式环境则无法使用,有一种方式可以使用UUID,但是它的缺陷是没有规律,很难理解。利用zookeeper顺序节点的特性,我们可以生成有顺序的,容易理解的,同时支持分布式环境的序列号。

代码实现

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
public class IdMaker {

private ZkClient client = null;
private final String server;
// zookeeper顺序节点的父节点
private final String root;
// 顺序节点的名称
private final String nodeName;
// 标识当前服务是否正在运行
private volatile boolean running = false;
private ExecutorService cleanExector = null;

public enum RemoveMethod{
NONE,IMMEDIATELY,DELAY

}

public IdMaker(String zkServer,String root,String nodeName){

this.root = root;
this.server = zkServer;
this.nodeName = nodeName;

}

// 启动服务
public void start() throws Exception {

if (running)
throw new Exception("server has stated...");
running = true;

init();

}

// 停止服务
public void stop() throws Exception {

if (!running)
throw new Exception("server has stopped...");
running = false;

freeResource();

}

// 初始化服务资源
private void init(){

client = new ZkClient(server,5000,5000,new BytesPushThroughSerializer());
cleanExector = Executors.newFixedThreadPool(10);
try{
client.createPersistent(root,true);
}catch (ZkNodeExistsException e){
//ignore;
}

}

// 释放服务器资源
private void freeResource(){

// 释放线程池
cleanExector.shutdown();
try{
cleanExector.awaitTermination(2, TimeUnit.SECONDS);
}catch(InterruptedException e){
e.printStackTrace();
}finally{
cleanExector = null;
}

if (client!=null){
client.close();
client=null;

}
}

// 检测当前服务是否正在运行
private void checkRunning() throws Exception {
if (!running)
throw new Exception("请先调用start");

}

// 从顺序节点名中提取我们要的ID值
private String ExtractId(String str){
int index = str.lastIndexOf(nodeName);
if (index >= 0){
index+=nodeName.length();
return index <= str.length()?str.substring(index):"";
}
return str;

}

// 生成ID
public String generateId(RemoveMethod removeMethod) throws Exception{
checkRunning();

// 构造顺序节点的完整路径
final String fullNodePath = root.concat("/").concat(nodeName);
// 创建持久化顺序节点
final String ourPath = client.createPersistentSequential(fullNodePath, null);

// 避免zookeeper的顺序节点暴增,直接删除掉刚创建的顺序节点
if (removeMethod.equals(RemoveMethod.IMMEDIATELY)){ // 立即删除
client.delete(ourPath);
}else if (removeMethod.equals(RemoveMethod.DELAY)){ // 延迟删除
cleanExector.execute(new Runnable() { // 用线程池执行删除,让generateId()方法尽快返回
public void run() {
client.delete(ourPath);
}
});

}
//node-0000000000, node-0000000001
return ExtractId(ourPath);
}

}
public class TestIdMaker {

public static void main(String[] args) throws Exception {

IdMaker idMaker = new IdMaker("192.168.1.105:2181",
"/NameService/IdGen", "ID");
idMaker.start();

try {
for (int i = 0; i < 10; i++) {
String id = idMaker.generateId(RemoveMethod.DELAY);
System.out.println(id);
}
} finally {
idMaker.stop();

}
}

}
阅读更多

Zookeeper 负载均衡

负载均衡是一种手段,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。

架构图

clipboard.png

图中左侧为ZooKeeper集群,右侧上方为工作服务器,下面为客户端。每台工作服务器在启动时都会去zookeeper的servers节点下注册临时节点,每台客户端在启动时都会去servers节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接。网络连接我们采用开源框架netty。

阅读更多

Zookeeper 分布式锁

我们常说的锁是单进程多线程锁,在多线程并发编程中,用于线程之间的数据同步,保护共享资源的访问。而分布式锁,指在分布式环境下,保护跨进程、跨主机、跨网络的共享资源,实现互斥访问,保证一致性。

架构图

clipboard.png

左侧是zookeeper集群,locker是数据节点,node_1到node_n代表一系列的顺序节点。

右侧client_1至client_n代表客户端,Service代表需要互斥访问的服务。

阅读更多

ZooKeeper 概述

什么是ZooKeeper

Apache ZooKeeper 是一个开源的实现高可用的分布式协调服务器。ZooKeeper是一种集中式服务,用于维护配置信息,域名服务,提供分布式同步和集群管理。所有这些服务的种类都被应用在分布式环境中,每一次实施这些都会做很多工作来避免出现bug和竞争条件。

ZooKeeper 设计原则

ZooKeeper 很简单

ZooKeeper 允许分布式进程通过共享的分层命名空间相互协调,ZooKeeper命名空间与文件系统很相似,每个命名空间填充了数据节点的注册信息 - 叫做Znode,这是在 ZooKeeper 中的叫法,Znode 很像我们文件系统中的文件和目录。ZooKeeper 与典型的文件系统不同,ZooKeeper 数据保存在内存中,这意味着 ZooKeeper 可以实现高吞吐量和低时延。

阅读更多

Zookeeper 数据发布订阅

多个订阅者对象同时监听同一主题对象,主题对象状态变化时通知所有订阅者对象更新自身状态。发布方和订阅方独立封装、独立改变,当一个对象的改变需要同时改变其他对象,并且它不知道有多少个对象需要改变时,可以使用发布订阅模式。

在分布式系统中的顶级应用有配置管理和服务发现。

配置管理:指集群中的机器拥有某些配置,并且这些配置信息需要动态地改变,那么我们就可以使用发布订阅模式把配置做统一的管理,让这些机器订阅配置信息的改变,但是配置改变时这些机器得到通知并更新自己的配置。

服务发现:指对集群中的服务上下线做统一管理,每个工作服务器都可以作为数据的发布方,向集群注册自己的基本信息,而让某些监控服务器作为订阅方,订阅工作服务器的基本信息。当工作服务器的基本信息改变时,如服务上下线、服务器的角色或服务范围变更,那么监控服务器可以得到通知并响应这些变化。

阅读更多

Zookeeper 基础

zookeeper集群配置

准备3台服务器:
n1:192.168.1.101
n2:192.168.1.102
n3:192.168.1.103

安装zookeeper

1
2
3
4
cd /opt
wget http://mirror.bit.edu.cn/apache/zookeeper/stable/zookeeper-3.4.10.tar.gz
tar -zxvf zookeeper-3.4.10.tar.gz
mv zookeeper-3.4.10 zookeeper
阅读更多

Zookeeper master 选举

考虑7*24小时向外提供服务的系统,不能有单点故障,于是我们使用集群,采用的是Master+Slave。集群中有一台主机和多台备机,由主机向外提供服务,备机监听主机状态,一旦主机宕机,备机必需迅速接管主机继续向外提供服务。在这个过程中,从备机选出一台机作为主机的过程,就是Master选举。

架构图

clipboard.png

左边是ZooKeeper集群,右边是3台工作服务器。工作服务器启动时,会去ZooKeeper的Servers节点下创建临时节点,并把基本信息写入临时节点。这个过程叫服务注册,系统中的其他服务可以通过获取Servers节点的子节点列表,来了解当前系统哪些服务器可用,这该过程叫做服务发现。接着这些服务器会尝试创建Master临时节点,谁创建成功谁就是Master,其他的两台就作为Slave。所有的Work Server必需关注Master节点的删除事件。通过监听Master节点的删除事件,来了解Master服务器是否宕机(创建临时节点的服务器一旦宕机,它所创建的临时节点即会自动删除)。一旦Master服务器宕机,必需开始新一轮的Master选举。

阅读更多

ZooKeeper 概述

一、什么是ZooKeeper

从上面我们也可以发现,好像哪都有ZooKeeper的身影,那什么是ZooKeeper呢?我们先去官网看看介绍:

官网对ZooKeeper的介绍

官网还有另一段话:

ZooKeeper: A Distributed Coordination Service for Distributed Applications

阅读更多