什么是分布式协调技术

概述

分布式协调技术主要用来解决分布式环境当中多个进程之间的同步控制,让他们有序的去访问某种临界资源,防止造成”脏数据”的后果。

img

在这图中有三台机器,每台机器各跑一个应用程序。然后我们将这三台机器通过网络将其连接起来,构成一个系统来为用户提供服务,对用户来说这个系统的架构是透明的,他感觉不到我这个系统是一个什么样的架构。那么我们就可以把这种系统称作一个分布式系统

阅读更多

什么是分布式锁

概述

为了防止分布式系统中的多个进程之间相互干扰,我们需要一种分布式协调技术来对这些进程进行调度。而这个分布式协调技术的核心就是来实现这个分布式锁

为什么要使用分布式锁

img

阅读更多

Zookeeper 运维

zookeeper配置详解

一、ZK的最小配置
最小配置是指Zookeeper运行所需的最小配置,Zookeeper只需要配置这些项就可以正常的运行Zookeeper。

  • clientPort
    配置ZK监听客户端连接的端口
  • dataDir
    内存数据库快照存放地址,如果没有指定事务日志存放地址(dataLogDir),默认也是存放在这个路径下,建议两个地址分开存放到不同的设备上。
  • tickTime
    心跳基本时间单位,毫秒级,ZK基本上所有的时间都是这个时间的整数倍。
阅读更多

Apache Dubbo Zookeeper

概述

目前市场上主流的 第二套微服务架构解决方案:Spring Boot + Dubbo + Zookeeper

  • Apache Dubbo (incubating) |ˈdʌbəʊ| 是一款高性能、轻量级的开源 Java RPC 框架。
  • ZooKeeper 是一种分布式协调服务,用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。

分布式协调框架 Zookeeper

ZooKeeper 是一种分布式协调服务,用于管理大型主机。在分布式环境中协调和管理服务是一个复杂的过程。ZooKeeper 通过其简单的架构和 API 解决了这个问题。ZooKeeper 允许开发人员专注于核心应用程序逻辑,而不必担心应用程序的分布式特性。

阅读更多

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 数据发布订阅

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

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

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

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

阅读更多