承接上篇:资源注册中心要实现对资源持有者的健康检查,那么他必须有所有已提交过注册的资源持有者节点地址。
此处的健康检查是采用定时的方式,去连接请求 资源持有者节点。
下面是自己做的一个定时器小工具:
public abstract class DiDaDiDa implements Runnable{
private static final long DEFAULT_DELAY = 3000;
private long delay;
private Object lock;
private volatile boolean goon;
public DiDaDiDa() {
this(DEFAULT_DELAY);
}
public DiDaDiDa(long delay) {
this.delay = delay;
this.lock = new Object();
}
public long getDelay() {
return delay;
}
public abstract void doing();
//启动定时器
public void startUp() {
if(goon == true) {
return;
}
goon = true;
new Thread(this, "定时器").start();
}
//关闭定时器
public void stop() {
if(goon == false) {
return;
}
goon = false;
}
@Override
public void run() {
while(goon) {
synchronized (lock) {
try {
lock.wait(delay);
/*
* 注意此处是开一个线程执行doing,若直接执行那么定时是非常不准的,他会加上doing的
* 执行时间
*/
new InnerWoker();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
//开启线程执行doing()方法
private class InnerWoker implements Runnable{
public InnerWoker() {
new Thread(this).start();
}
@Override
public void run() {
doing();
}
}
}
说一下NodePool的数据结构:一对一结构。
ConcurrentHashMap<Integer, DefaultNetNode> 键: DefaultNetNode对象的hashCode 值:DefaultNetNode对象
NodePool代码如下:
public class NodePool {
private static final long DEFAULT_DELAY = 3000;
private static final Map<Integer, DefaultNetNode> nodePool =
new ConcurrentHashMap<Integer, DefaultNetNode>();
private static ScanTimer scanTimer = new ScanTimer(DEFAULT_DELAY);
//开启定时扫面
public static void startScanNode() {
scanTimer.startUp();
}
public static void stopScanNode() {
scanTimer.stop();
}
public static void add(DefaultNetNode node) {
int key = node.hashCode();
if(nodePool.containsKey(key)) {
return;
}
nodePool.put(key, new ResourceHolderNode(node));
}
public static void remove(DefaultNetNode node) {
int key = node.hashCode();
if(nodePool.containsKey(key)) {
nodePool.remove(key);
}
}
static class ScanTimer extends DiDaDiDa{
public ScanTimer() {
super();
}
public ScanTimer(long delay) {
super(delay);
}
@Override
public void doing() {
//扫描检测
if(nodePool.isEmpty()) {
return;
}
Iterator<DefaultNetNode> nodeList = nodePool.values().iterator();
while(nodeList.hasNext()) {
DefaultNetNode node = nodeList.next();
//进行活跃检测
ResourceHolderNode rhNode = (ResourceHolderNode) node;
try {
rhNode.isActive();
System.out.println("正在检测 [" + rhNode + "]的活跃情况");
} catch (Exception e) {
//发现了非活跃资源持有者 将其删除
ResourceNodePool.removeNode(node);
}
}
}
}
}
要想成功进行健康检测,那么资源注册中心必须能够主动连接资源持有者,即要利用RMIClient。此处的健康监测是在NodePool中进行的。故NodePool中存储的节点用DefaultNetNode(只有ip和port)是不行的。而是需要一个能够实在进行通信的网络节点,故在填充NodePool的时候存储的应该是ResourceHolderNode。
ResourceHolderNode:
public class ResourceHolderNode extends DefaultNetNode {
private IResourceHolder resourceHolder;
/**
* @param node 资源持有者的地址
*/
public ResourceHolderNode(DefaultNetNode node) {
super(node.getIp(), node.getPort());
RMIClient rmiClient = new RMIClient(node.getIp(), node.getPort());
//设置连接时长
rmiClient.setConnectTime(10);
this.resourceHolder = rmiClient.getProxy(IResourceHolder.class);
}
//检测健康情况
public boolean isActive() throws Exception {
return resourceHolder.isActive();
}
}
接下来是真正的资源持有者ResourceHolder:
public class ResourceHolder extends Resourcer {
private RMIServer rmiServer;
private INetNode addr;
public static void scanfRmiMapping(String filePath) {
RMIFactory.scanRmiMapping(filePath);
}
public ResourceHolder(String ip, int port) {
super();
this.addr = new DefaultNetNode(ip, port);
this.rmiServer = new RMIServer();
this.rmiServer.setPort(port);
this.rmiServer.startUp();
}
/**
* 执行远程方法 注册资源
*/
public void registry(ResourceInfo resourceInfo) {
irc.registry(resourceInfo, (DefaultNetNode)addr);
}
public void logout(ResourceInfo resourceInfo) {
irc.logout(resourceInfo, (DefaultNetNode)addr);
}
public void shutdown() {
this.rmiServer.shutdown();
}
}
对于资源请求者 其实它的实现很简单,他只需要向资源注册中心发送 “获取某资源对应的资源持有者集合”即可。
ResourceRequester:
public class ResourceRequester extends Resourcer{
public ResourceRequester() {
super();
}
public List<DefaultNetNode> getAddressList(ResourceInfo resourceInfo){
return irc.getAddressList(resourceInfo);
}
}
至此基本上就完成了整个“资源发现工具”的所有内容。
测试是必要的。利用两个测试类:
TestHolder:
public class TestHolder {
/**
* 此处的做法是定义两个资源 ri1和ri2 建立三个资源持有者 holder1(持有ri1) holder2(持有ri1) holder3(持有ri2)
*/
public static void main(String[] args) {
ResourceInfo ri1 = new ResourceInfo("app", "1234567", "1.0");
ResourceInfo ri2 = new ResourceInfo("app", "45757", "1.0");
//建立资源注册中心---此处他会使用默认端口号54200
ResourceRegistryCenter rrc = new ResourceRegistryCenter();
rrc.startUp();
ResourceHolder.scanfRmiMapping("/ResourceHolder.rmi.map.xml");
ResourceHolder holder1 = new ResourceHolder("127.0.0.1", 54195);
holder1.setRegistryIp("127.0.0.1");
holder1.setRegistryPort(ResourceRegistryCenter.DEFAULT_PORT);
holder1.registry(ri1);
ResourceHolder holder2 = new ResourceHolder("127.0.0.1", 54190);
holder2.setRegistryIp("127.0.0.1");
holder2.setRegistryPort(ResourceRegistryCenter.DEFAULT_PORT);
holder2.registry(ri1);
ResourceHolder holder3 = new ResourceHolder("127.0.0.1", 54185);
holder3.setRegistryIp("127.0.0.1");
holder3.setRegistryPort(ResourceRegistryCenter.DEFAULT_PORT);
holder3.registry(ri2);
}
}
TestRequester:
public class TestRequester {
public static void main(String[] args) {
ResourceInfo ri1 = new ResourceInfo("app", "1234567", "1.0");
//建立资源请求者并设置注册中心地址
ResourceRequester requester = new ResourceRequester();
requester.setRegistryIp("127.0.0.1");
requester.setRegistryPort(ResourceRegistryCenter.DEFAULT_PORT);
//资源请求者请求资源
List<DefaultNetNode> nodeList = requester.getAddressList(ri1);
if(nodeList.isEmpty()) {
System.out.println("未发现该资源用有者");
}else {
System.out.println("发现资源拥有者如下:");
for(DefaultNetNode node : nodeList) {
System.out.println(node);
}
}
}
}
先执行TestHolder可以看到输出结果:
再执行TestRequester结果: