vlambda博客
学习文章列表

「大数据」(三十四)ZooKeeper之服务简介

【导读:数据是二十一世纪的石油,蕴含巨大价值,这是·情报通·大数据技术系列第[34]篇文章,欢迎阅读和收藏】

基本概念

ZooKeeper 是一个分布式的,开放源码的分布式应用程序协调服务,提供的功能包括命名服务、配置管理、负载均衡、组服务等。

术语解释

JNDI :Java Naming and Directory Interface ,即 Java 命名和目录接口。JNDI 包含了一些标准 API 接口, Java 程序可以通过这些接口来访问命名目录服务。JNDI 不依赖于任何独立的命名目录服务器,不管采用哪种命名目录服务器,应用程序都可以通过统一的 JNDI 接口来调用。要使用 JNDI ,必须要安装 jdk 1.3 以上版本。

UUID :Universally Unique Identifier ,即通用唯一识别码。让分布式系统中的所有元素都能有唯一的辨识信息,而不要要通过中央控制端来做辨识信息的指定。

详细说明

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 。