zookeeper应用:分布式服务器感知
更新时间:2018-08-19 | 阅读量(459)
现在我们项目开发一般会使用maven的聚合工程,把项目的service层和web层进行分离,然后打包为两个应用部署分别部署到服务器,这里我们就以一个订单模块为例,假设我们打包的应用名称分别叫 orderServer.jar(service层业务)和orderClient.war(web层处理逻辑),这样他们的部署应该是这样的:

对于上面这个简单的一个RPC调用过程,那么我们的orderClient这个应用肯定是需要知道我们的orderServer.jar是部署在哪个服务器上的,这样我们才可以通过具体的主机地址+端口号找到对应的应用程序调用并且返回结果,所以在我们的orderClient应该是有一个**配置或者说是在程序启动的时候传递orderServer的相关信息**去找到我们对应的OrderServer.jar这个应用
但是系统业务的增长以及对系统的可靠性的要求,我们会面临下面两个问题
- 请求数量增多,现在一个orderServer.jar已经不能支撑业务系统的要求
- 如果服务器A或者说应用OrderServer.jar程序内存溢出,整个系统都会出现无法访问的情况
为了解决上面两个问题,我们需要使用分布式应用的部署模式,即对于orderServer.jar和orderClient.war都需要在多个服务器上部署应用,那么这个时候我们的应用部署应该是这样的:

对于上述这个部署环境来说,对于所有的orderClient.war的应用程序,我们需要获取到orderServer.jar的相关信息(主机地址和端口)不能再写死,我们需要动态的去获取所有的服务端的应用程序列表
- 当我们在增加orderServer.jar的部署机器的时候,对于orderClient可以实时的添加新增加的orderServer.jar的地址信息
- 当我们某一个服务器,比如说服务器A宕机的时候,不能再提供服务的情况下,我们的orderClient可以实时的获取到相关信息,在orderClient.war的程序中及时的剔除服务器A的地址信息
如果要实现对于客户端orderClient.war实时的获取orderServer.jar的地址信息,我们可以使用zookeeper完成我们这个简单的需求,其中主要的流程图如下

如果要实现上面流程图的这个效果,我们在orderServer.jar和orderClient.war的程序中需要做如下的操作
orderServer.jar
- 创建一个zkClient 连接到zookeeper集群
- 启动orderServer.jar的时候往zookeeper集群中的/Servers目录下创建**序列的临时节点**
- 在创建节点的时候设置值为当前服务器的地址信息(主机名:端口)
orderClient.war
- 创建一个zkClient连接到zookeeper集群
- 启动orderClient.war的时候,需要获取/Servers目录下面所有的节点信息
- 在创建zkClient的时候绑定一个监听NodeChildrenChanged类型的事件,当/Servers目录下的节点信息发生改变的时候,可以及时的做出响应,获取最新的地址列表
**下面是具体的实现代码,如果可以比较好的理解这些内容,说明对于zookeeper的应用基本上已经掌握**
服务端 OrderServerRegister
```java
package cn.wolfcode.zookeeper.server;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.ZooDefs;
import org.apache.zookeeper.ZooKeeper;
import org.apache.zookeeper.data.Stat;
//服务器启动注册
public class OrderServerRegister {
//zkServer的连接地址信息,多个连接地址之间使用逗号隔开
private String connectStr="lab171:2181,lab172:2181,lab173:2181";
//客户端连接zkServer的超时时间
private int sessionTimeOut=2000;
//zk客户端 实例对象,用户连接zk集群
private ZooKeeper client=null;
//指定服务器地址信息存放的目录
private String serverAddr="/servers";
//指定节点名称,因为使用的是EPHEMERAL_SEQUENTIAL,所以不会存在节点重复的情况
//并且在断开连接后,该节点会自动删除
private String serverName="orderServer";
public OrderServerRegister() throws Exception {
//实例化zk客户端
client=new ZooKeeper(connectStr,sessionTimeOut,null);
//判断存放的地址是否存在,如果不存在,创建一个对应的目录
Stat stat = client.exists(serverAddr,null);
if(stat ==null){
client.create(serverAddr,"".getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.PERSISTENT);
}
}
// 注册地址信息的方法
public void register(String host,String port) throws Exception{
//创建一个临时的序列节点
client.create(serverAddr+"/"+serverName,(host+":"+port).getBytes(),
ZooDefs.Ids.OPEN_ACL_UNSAFE,CreateMode.EPHEMERAL_SEQUENTIAL);
}
public static void main(String[] args) throws Exception {
//设置ip地址和端口 通过启动参数传递进来
String host=args[0];
String port=args[1];
OrderServerRegister server = new OrderServerRegister();
server.register(host,port);
System.out.println("服务端"+host+":"+port+"启动成功");
//模拟服务程序一直处于运行状态
Thread.sleep(Long.MAX_VALUE);
}
}
```
客户端 OrderClientDiscovery
```java
package cn.wolfcode.zookeeper.server;
import org.apache.zookeeper.WatchedEvent;
import org.apache.zookeeper.Watcher;
import org.apache.zookeeper.ZooKeeper;
import java.util.ArrayList;
import java.util.List;
//客户端发现服务端信息
public class OrderClientDiscovery {
//zkServer的连接地址信息
private String connectStr="lab171:2181,lab172:2181,lab173:2181";
//客户端连接zkServer的超时时间
private int sessionTimeOut=2000;
//zk客户端
private ZooKeeper client=null;
//服务器信息存放地址
private String serverAddr="/servers";
//存放服务器信息地址列表的信息
private List orderServerList=new ArrayList<>();
public OrderClientDiscovery() throws Exception {
//实例化zk客户端,这里需要指定事件监听
client=new ZooKeeper(connectStr, sessionTimeOut, new Watcher() {
@Override
public void process(WatchedEvent event) {
//连接成功状态
if(event.getState()==Event.KeeperState.SyncConnected){
//对于指定路径的事件监听
if(serverAddr.equals(event.getPath())){
//监听子节点改变状态,子节点的数量增加或者减少
if(event.getType()==Event.EventType.NodeChildrenChanged){
try {
//调用业务处理方法
getOrderServerList();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
});
}
public void getOrderServerList() throws Exception{
//获取到所有的子节点列表, 其中true是保持监听状态,使用创建zkClient的默认事件监听程序
List nodeList = client.getChildren(serverAddr,true);
//临时列表,用于存放对应的服务器信息
List temp=new ArrayList<>();
for (String node : nodeList) {
//获取节点的数据, 地址+端口
byte[] data = client.getData(serverAddr+"/"+node,null,null);
temp.add(new String(data));
}
//更新成员变量服务器列表信息
orderServerList=temp;
System.out.println("更新后的服务器列表为:"+orderServerList);
}
public static void main(String[] args) throws Exception {
OrderClientDiscovery orderClient = new OrderClientDiscovery();
orderClient.getOrderServerList();
System.out.println("客户端启动完成,随时监听服务器列表的改变信息");
Thread.sleep(Long.MAX_VALUE);
}
}
```
完整代码下载地址:https://gitee.com/heshengjun/demo_zookeeper001.git