LVPHP

试试zookeeper

简介

Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目,它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务、状态同步服务、集群管理、分布式应用配置项的管理等

安装和配置详解

Zookeeper的安装和配置十分简单, 既可以配置成单机模式, 也可以配置成集群模式
1.单机模式
官方网站下载zookeeper的安装包之后, 解压到合适目录. 进入zookeeper目录下的conf子目录,复制一份zoo_sample.cfg为zoo.cfg,根据情况修改配置:

1
2
3
4
tickTime=2000
dataDir=/home/apple/zookeeper/data
dataLogDir=/home/apple/zookeeper/logs
clientPort=4180

参数说明:

  • tickTime: zookeeper中使用的基本时间单位, 毫秒值.
  • dataDir: 数据目录. 可以是任意目录.
  • dataLogDir: log目录, 同样可以是任意目录. 如果没有设置该参数, 将使用和dataDir相同的设置.
  • clientPort: 监听client连接的端口号.
    至此, zookeeper的单机模式已经配置好了. 启动server只需运行脚本:
    1
    ./bin/zkServer.sh start

Server启动之后, 就可以启动client连接server了, 执行脚本:

1
./bin/zkCli.sh -server localhost:4180

2.伪集群模式
所谓伪集群, 是指在单台机器中启动多个zookeeper进程, 并组成一个集群. 以启动3个zookeeper进程为例.
将zookeeper的目录拷贝2份:

1
2
3
|--zookeeper1
|--zookeeper2
|--zookeeper3

更改各目录下的conf/zoo.cfg的配置,例如下:

1
2
3
4
5
6
7
8
9
tickTime=2000
initLimit=5
syncLimit=2
dataDir=/home/apple/zookeeper0/data
dataLogDir=/home/apple/zookeeper0/logs
clientPort=4180
server.1=127.0.0.1:8880:7770
server.2=127.0.0.1:8881:7771
server.3=127.0.0.1:8882:7772

新增了几个参数, 其含义如下:

  • initLimit: zookeeper集群中的包含多台server, 其中一台为leader, 集群中其余的server为follower. initLimit参数配置初始化连接时, follower和leader之间的最长心跳时间. 此时该参数设置为5, 说明时间限制为5倍tickTime, 即5*2000=10000ms=10s.
  • syncLimit: 该参数配置leader和follower之间发送消息, 请求和应答的最大时间长度. 此时该参数设置为2, 说明时间限制为2倍tickTime, 即4000ms.
  • server.X=A:B:C 其中X是一个数字, 表示这是第几号server. A是该server所在的IP地址. B配置该server和集群中的leader交换消息所使用的端口. C配置选举leader时所使用的端口. 由于配置的是伪集群模式, 所以各个server的B, C参数必须不同.
    在之前设置的dataDir中新建myid文件, 写入一个数字, 该数字表示这是第几号server. 该数字必须和zoo.cfg文件中的server.X中的X一一对应。
    /home/apple/zookeeper1/data/myid文件中写入1, /home/apple/zookeeper2/data/myid文件中写入2, /home/apple/zookeeper3/data/myid文件中写入3.
    3.集群模式
    集群模式的配置和伪集群基本一致.由于集群模式下, 各server部署在不同的机器上, 因此各server的conf/zoo.cfg文件可以完全一样.
    下面是一个示例:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    tickTime=2000
    initLimit=5
    syncLimit=2
    dataDir=/home/zookeeper/data
    dataLogDir=/home/zookeeper/logs
    clientPort=4180
    server.43=10.1.39.43:2888:3888
    server.47=10.1.39.47:2888:3888
    server.48=10.1.39.48:2888:3888

示例中部署了3台zookeeper server, 分别部署在10.1.39.43, 10.1.39.47, 10.1.39.48上. 需要注意的是, 各server的dataDir目录下的myid文件中的数字必须不同.
10.1.39.43 server的myid为43, 10.1.39.47 server的myid为47, 10.1.39.48 server的myid为48.

node客户端链接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
"use strict";
const zookeeper = require('node-zookeeper-client');
const client = zookeeper.createClient("127.0.0.1:4180,127.0.0.1:4181,127.0.0.1:4182");
function createPath(path){
client.create(
path,
new Buffer('aaaaaaaa'),
zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL,
function (error, path) {
if (error) {
console.log(error.stack);
return;
}
console.log('Node: %s is created.', path);
}
);
}
function getData(path){
client.getData(
path,
function (event) {
console.log('Got event: %s.', event);
getData(path);
},
function (error, data, stat) {
if (error) {
console.log(error.stack);
return;
}
console.log('Got data: %s', data.toString('utf8'));
}
);
}
function listChildren(path) {
client.getChildren(
path,
function (event) {
console.log('Got watcher event: %s', event.getPath());
listChildren(path);
},
function (error, children, stat) {
if (error) {
console.log(
'Failed to list children of %s due to: %s.',
path,
error
);
return;
}
console.log('Children of %s are: %j.', path, children);
}
);
}
client.once('connected', function () {
console.log('Connected to ZooKeeper.');
createPath("/test/node4");
listChildren("/test");
});
client.connect();

node server使用zookeeper

local_address
node-zookeeper-client

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
"use strict";
const program = require("commander"),
events = require('events'),
fs = require("fs"),
util = require("util"),
debug = require('debug')("server"),
local_address = require("./lib/local_address"),
zookeeper = require('node-zookeeper-client'),
net = require('net');
program
.version('0.0.1')
.usage('[options] <file ...>')
.option('-e, --env [develop|test|bjac]', 'Run ENV')
.parse(process.argv);
if(typeof program.env == "undefined" ){
console.log("argv error");
process.exit();
}
let _env = program.env;
let configfile = __dirname + "/config/config_" + _env + ".js";
if(!fs.existsSync(configfile)){
console.log("no config file");
process.exit();
}
var Config = require(configfile);
let nodename = local_address();
var Server = function(){
let self = this;
self.ready = false;
self.name = nodename;
self.connectZK();
self.once("ready", function(){
self.ready = true;
self.init();
});
};
util.inherits(Server, events.EventEmitter);
Server.prototype.connectZK = function () {
let self = this;
let zk = self.zk = zookeeper.createClient(Config.connectionString, Config.zkOptions);
zk.once('connected', function () {
console.log('Connected to ZooKeeper.');
createPath("/test/"+self.name, new Buffer(self.name+":"+Config.port));
});
zk.on("error", function(error){
console.log(error);
self.emit("error", error);
});
zk.connect();
function createPath(path, data){
self.zk.create(
path,
data,
zookeeper.CreateMode.EPHEMERAL_SEQUENTIAL,
function (error, path) {
if (error) {
console.log(error.stack);
self.emit("error", error);
return;
}
self.emit("ready");
}
);
}
};
Server.prototype.init = function(){
let self = this;
self.server = net.createServer((c) => {
// 'connection' listener
console.log('client connected');
c.on('end', () => {
console.log('client disconnected');
});
c.write('hello\r\n');
c.pipe(c);
});
self.server.on('error', (err) => {
throw err;
});
self.server.listen(Config.port, () => {
console.log('server bound');
});
}
var sv = new Server();

列出所有节点

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
"use strict";
const zookeeper = require('node-zookeeper-client');
const client = zookeeper.createClient("127.0.0.1:4180,127.0.0.1:4181,127.0.0.1:4182");
function listChildren(path) {
client.getChildren(
path,
function (event) {
console.log('Got watcher event: %s', event.getPath());
listChildren(path);
},
function (error, children, stat) {
if (error) {
console.log(
'Failed to list children of %s due to: %s.',
path,
error
);
return;
}
console.log('Children of %s are: %j.', path, children);
}
);
}
client.once('connected', function () {
console.log('Connected to ZooKeeper.');
listChildren("/test");
});
client.connect();

说明

zookeeper创建目录节点的时候有四种模式:

  • PERSISTENT:持久化目录节点,存储的数据不会丢失。
  • PERSISTENT_SEQUENTIAL:顺序自动编号的持久化目录节点,存储的数据不会丢失,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。
  • EPHEMERAL:临时目录节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除。
  • EPHEMERAL_SEQUENTIAL:临时自动编号节点,一旦创建这个节点的客户端与服务器端口也就是session 超时,这种节点会被自动删除,并且根据当前已近存在的节点数自动加 1,然后返回给客户端已经成功创建的目录节点名。
    ZooKeeper中一共由三种方法可以实现Watch,分别为getData、exists和getChildren,但这三种方法仅仅监控一次数据变化,要想永久监控,需要在对应的事件回调中继续增加watch。