一、ZooKeeper的配置管理( Configuration Management)使用场景
配置的管理在分布式应用环境中很常见,例如同一个应用系统需要多台 PC Server 运行,但是它们运行的应用系统的某些配置项是相同的,如果要修改这些相同的配置项,那么就必须同时修改每台运行这个应用系统的 PC Server ,这样非常麻烦而且容易出错。 像这样的配置信息完全可以交给 Zookeeper 来管理,将配置信息保存在 Zookeeper 的某个目录节点中,然后将所有需要修改的应用机器监控配置信息的状态,一旦配置信息发生变化,每台应用机器就会收到 Zookeeper 的通知,然后从 Zookeeper 获取新的配置信息应用到系统中。
二、配置管理结构图
三、实战案例
3.1 工作中有这样的一个场景: 数据库用户名和密码信息放在一个配置文件中,应用读取该配置文件,配置文件信息放入缓存。
若数据库的用户名和密码改变时候,还需要重新加载缓存,比较麻烦,通过ZooKeeper可以轻松完成,当数据库发生变化时自动完成
缓存同步
3.2 模拟程序代码
创建SetConfig.java 写配置文件
创建MyClient.java 读取配置文件和注册watcher到zk上
package
example;
import
org.apache.zookeeper.CreateMode;
import
org.apache.zookeeper.WatchedEvent;
import
org.apache.zookeeper.Watcher;
import
org.apache.zookeeper.ZooKeeper;
import
org.apache.zookeeper.ZooDefs.Ids;
/**
*
<p>
* write data to zookeeper
*
</p>
*
*
@author
shenfl
*
*/
public
class
SetConfig {
// zk的连接串
private
final
static
String
CONNECT_STR
=
"192.168.2.35:2181"
;
// 连接zk的超时时间
private
static
final
int
SESSION_TIMEOUT
= 30000;
// 数据库连接
private
final
static
String
uRLNode
=
"10.12.1.1"
;
private
final
static
String
userNameNode
=
"admin"
;
private
final
static
String
passwdNode
=
"admin123"
;
// Auth认证
public
static
String
authType
=
"digest"
;
public
static
String
authPasswd
=
"*ik1234"
;
public
static
void
main(String[]
args
) {
try
{
ZooKeeper
zk
=
new
ZooKeeper(
CONNECT_STR
,
SESSION_TIMEOUT
,
new
Watcher() {
@Override
public
void
process(WatchedEvent
e
) {
System.
out
.println(
" type : "
+
e
.getType() +
",path:"
+
e
.getPath());
}
});
zk
.addAuthInfo(
authType
,
authPasswd
.getBytes());
if
(
zk
.exists(
"/jfconf"
,
true
)==
null
){
zk
.create(
"/jfconf"
,
uRLNode
.getBytes(), Ids.
OPEN_ACL_UNSAFE
, CreateMode.
PERSISTENT
);
}
if
(
zk
.exists(
"/jfconf/uRLNode"
,
true
) ==
null
) {
zk
.create(
"/jfconf/uRLNode"
,
uRLNode
.getBytes(), Ids.
OPEN_ACL_UNSAFE
, CreateMode.
PERSISTENT
);
}
if
(
zk
.exists(
"/jfconf/userNameNode"
,
true
) ==
null
) {
zk
.create(
"/jfconf/userNameNode"
,
userNameNode
.getBytes(), Ids.
OPEN_ACL_UNSAFE
,
CreateMode.
PERSISTENT
);
}
if
(
zk
.exists(
"/jfconf/passwdNode"
,
true
) ==
null
) {
zk
.create(
"/jfconf/passwdNode"
,
passwdNode
.getBytes(), Ids.
OPEN_ACL_UNSAFE
, CreateMode.
PERSISTENT
);
}
zk
.close();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
}
package
example;
import
java.io.IOException;
import
org.apache.zookeeper.WatchedEvent;
import
org.apache.zookeeper.Watcher;
import
org.apache.zookeeper.Watcher.Event.EventType;
import
org.apache.zookeeper.ZooKeeper;
/**
*
<p>
* 创建属于自己的Watcher,该Watcher监控 zk上的/jfconf 节点, 当该节点下的文件发生变化重新加载缓存
*
</p>
*
*
<p>
* ZooKeeper通过 Auth和ACL完成节点的权限控制
*
</p>
*
*
<p>
* Auth表示某种认证,由于一个ZooKeeper集群可能被多个项目使用,各个项目属于不同的项目组,
* 他们在进行开发时肯定不想其他项目访问与自己相关的节点,这时可以通过为每个项目组分配一个 Auth,
* 然后每个项目组先通过 Auth认证以后再继续相关的操作,这样甲 Auth认证的用户就不能操作其他
* Auth认证后创建的节点,从而实现各个项目之间的隔离。ZooKeeper提供了如下方法完成认证
*
</p>
*
*
*
@author
shenfl
*
*/
public
class
MyClient
implements
Watcher {
// zk的连接串
private
final
static
String
CONNECT_STR
=
"192.168.2.35:2181"
;
// 连接zk的超时时间
private
static
final
int
SESSION_TIMEOUT
= 30000;
// client获取的数据库信息
private
String
uRL
;
private
String
userName
;
private
String
passwd
;
// Auth认证
public
static
String
authType
=
"digest"
;
public
static
String
authPasswd
=
"*ik1234"
;
public
MyClient() {
initValue();
}
public
static
void
main(String[]
args
) {
MyClient
client
=
new
MyClient();
int
i
= 0;
try
{
ZooKeeper
zk
=
null
;
// 连接zk
zk
=
new
ZooKeeper(
CONNECT_STR
,
SESSION_TIMEOUT
,
client
);
// 判断权限是否能访问/ jfconf目录
zk
.addAuthInfo(
authType
,
authPasswd
.getBytes());
while
(
true
) {
System.
out
.println(
"url["
+
i
+
"]="
+
client
.getuRL());
System.
out
.println(
"userName["
+
i
+
"]="
+
client
.getUserName());
System.
out
.println(
"passwd["
+
i
+
"]="
+
client
.getPasswd());
Thread. sleep(10 * 1000);
i
++;
if
(
i
== 10) {
break
;
}
}
zk
.close();
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
/**
* 获取 zk中相关信息
*
*
@throws
Exception
*/
private
void
initValue() {
try
{
ZooKeeper
zk
= getZk();
/**
* return true: znode happened changed,uRL update.
*/
this
.
uRL
=
new
String(
zk
.getData(
"/jfconf/uRLNode"
,
true
,
null
));
this
.
userName
=
new
String(
zk
.getData(
"/jfconf/userNameNode"
,
true
,
null
));
this
.
passwd
=
new
String(
zk
.getData(
"/jfconf/passwdNode"
,
true
,
null
));
}
catch
(Exception
e
) {
e
.printStackTrace();
}
}
/**
* 获取 zk连接
*
*
@return
*
@throws
IOException
*/
public
ZooKeeper getZk() {
ZooKeeper
zk
=
null
;
try
{
zk
=
new
ZooKeeper(
CONNECT_STR
,
SESSION_TIMEOUT
,
this
);
}
catch
(IOException
e
) {
e
.printStackTrace();
}
// 判断权限是否能访问/ jfconf目录
zk
.addAuthInfo(
authType
,
authPasswd
.getBytes());
return
zk
;
}
/**
*
<p>
* 如何服务器中的配置信息发生变化,通知process方法,把 zk中的数据重新获取,然后放到缓存中
*
</p>
*/
@Override
public
void
process(WatchedEvent
event
) {
String
message
=
""
;
EventType
type
=
event
.getType();
if
(
type
.equals(Watcher.Event.EventType.
None
)) {
message
=
"connect zk sucess!!!"
;
}
else
if
(
type
.equals(Watcher.Event.EventType.
NodeCreated
)) {
message
=
"znode create sucess!!!"
;
}
else
if
(
type
.equals(Watcher.Event.EventType.
NodeChildrenChanged
)) {
message
=
"child of znode create sucess!!!"
;
}
else
if
(
type
.equals(Watcher.Event.EventType.
NodeDataChanged
)) {
message
=
"znode update success!!!,reload db's information"
;
initValue();
}
else
if
(
type
.equals(Watcher.Event.EventType.
NodeDeleted
)) {
message
=
"znode delete success!!!"
;
}
System.
out
.println(
message
);
}
public
String getuRL() {
return
uRL
;
}
public
void
setuRL(String
uRL
) {
this
.
uRL
=
uRL
;
}
public
String getUserName() {
return
userName
;
}
public
void
setUserName(String
userName
) {
this
.
userName
=
userName
;
}
public
String getPasswd() {
return
passwd
;
}
public
void
setPasswd(String
passwd
) {
this
.
passwd
=
passwd
;
}
}
3.3 验证Watcher
通过run as 运行MyClient.java,并查看结果
connect zk sucess!!!
url[0]=10.12.1.1
userName[0]=admin
passwd[0]=admin123
url[1]=10.12.1.1
userName[1]=admin
passwd[1]=admin123
url[2]=10.12.1.1
userName[2]=admin
passwd[2]=admin123
connect zk sucess!!!
url[3]=10.12.1.1
userName[3]=admin
passwd[3]=admin123
connect zk sucess!!!
然后修改/jfconf/
userNameNode下的数据,再次检查结果
znode update success!!!,reload db's information
url[4]=10.12.1.1
userName[4]=adminadmin
passwd[4]=admin123
url[5]=10.12.1.1
userName[5]=adminadmin
passwd[5]=admin123
url[6]=10.12.1.1
userName[6]=adminadmin
passwd[6]=admin123
url[7]=10.12.1.1
userName[7]=adminadmin
passwd[7]=admin123
url[8]=10.12.1.1
userName[8]=adminadmin
passwd[8]=admin123
url[9]=10.12.1.1
userName[9]=adminadmin
passwd[9]=admin123
总结: 创建一个Watcher,然后注册到zk上,通过下面的代码完成注册
zk
=
new
ZooKeeper(
CONNECT_STR
,
SESSION_TIMEOUT
,
client
);