「大数据」(三十四)ZooKeeper之服务简介
【导读:数据是二十一世纪的石油,蕴含巨大价值,这是·情报通·大数据技术系列第[34]篇文章,欢迎阅读和收藏】
1 基本概念
ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,提供的功能包括命名服务、配置管理、负载均衡、组服务等。
2 术语解释
JNDI :Java Naming and Directory Interface ,即 Java 命名和目录接口。JNDI 包含了一些标准 API 接口, Java 程序可以通过这些接口来访问命名目录服务。JNDI 不依赖于任何独立的命名目录服务器,不管采用哪种命名目录服务器,应用程序都可以通过统一的 JNDI 接口来调用。要使用 JNDI ,必须要安装 jdk 1.3 以上版本。
UUID :Universally Unique Identifier ,即通用唯一识别码。让分布式系统中的所有元素都能有唯一的辨识信息,而不要要通过中央控制端来做辨识信息的指定。
3 详细说明
3.1 ZooKeeper 实现命名服务
Zookeeper 的命名服务,有两个应用方向:
1. 提供类似 JNDI 的功能:
2 .利用 zookeeper 中的顺序节点的特性,制作分布式的序列号生成器( ID 生成器)
在往数据库中插入数据,通常是要有一个 ID 号,在单机环境下,可以利用数据库的主键自动生成 id 号,但是这种在分布式环境下就无法使用了,可以使用 UUID ,但是 UUID 有一个缺点,就是没有什么规律很难理解。使用 zookeeper 的命名服务可以生成有顺序的容易理解的,支持分布式的编号。
算法流程如下:
生成 ID 方法如下:
public class IdMaker {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private ZkClient client = null;
private final String server;
// id 生成器根节点
private final String root;
// id 节点
private final String nodeName;
// 启动状态 : true: 启动 ;false: 没有启动,默认没有启动
private volatile boolean running = false;
private ExecutorService cleanExector = null;
public enum RemoveMethod {
// 不,立即,延期
NONE, IMMEDIATELY, DELAY
}
public IdMaker(String zkServer, String root, String nodeName) {
this.server = zkServer;
this.root = root;
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) {
logger.info(" 节点已经存在 , 节点路径 :" + root);
}
}
// 资源释放
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);// 20
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);
// 创建顺序节点每个父节点会为他的第一级子节点维护一份时序,会记录每个子节点创建的先后顺序。
// 基于这个特性,在创建子节点的时候,可以设置这个属性,那么在创建节点过程中,
// ZooKeeper 会自动为给定节点名加上一个数字后缀,作为新的节点名
final String ourPath = client.createPersistentSequential(fullNodePath, null);
if (removeMethod.equals(RemoveMethod.IMMEDIATELY)) {// 立即删除
client.delete(ourPath);
}
else if (removeMethod.equals(RemoveMethod.DELAY)) {// 延期删除
cleanExector.execute(new Runnable() {
public void run() {
client.delete(ourPath);
}
});
}
return ExtractId(ourPath);
}
}
测试:
public class TestIdMaker {
public static void main(String[] args) throws Exception {
IdMaker idMaker = new IdMaker("127.0.0.1:2181",
"/NameService/IdGen", "ID-");
idMaker.start();
try {
for (int i = 0; i < 2; i++) {
String id = idMaker.generateId(RemoveMethod.DELAY);
System.out.println(id);
}
} finally {
idMaker.stop();
}
}
}
3.2 ZooKeeper 实现负载均衡
负载均衡是一种手段,用来把对某种资源的访问分摊给不同的设备,从而减轻单点的压力。
架构图如下:
图中左侧为 ZooKeeper 集群,右侧上方为工作服务器,下面为客户端。每台工作服务器在启动时都会去 zookeeper 的 servers 节点下注册临时节点,每台客户端在启动时都会去 servers 节点下取得所有可用的工作服务器列表,并通过一定的负载均衡算法计算得出一台工作服务器,并与之建立网络连接。网络连接我们采用开源框架 netty 。