详解 资源发现技术 的基本实现
在本人之前的博文 《详解 服务发现 的基本实现》中,本人 详尽地 讲解了 服务发现 技术
的 基本概念 以及 重要意义 等诸多知识点
甚至在文章中 基本实现了 服务发现 技术
那么,本篇博文 名为 资源发现技术
,为什么本人要扯到 服务发现 技术
呢?
服务发现 与 资源发现:
- 所谓的 “发现”,在我们的技术中,也就是 注册、注销、获取提供者列表 等操作
因此,在很大程度上,两者的 实现思路 一致 - 资源,在 一定的程度上 来讲,就是 服务
例如:电影视频、游戏数据 等
上面的两个例子,都是 服务,
但是,在我们的角度上来分析,电影视频就是 视频资源,游戏数据就是 数据资源
由上面两个例子来分析,其实资源发现
就是服务发现
的 衍生体
实现 原理:
至于 实现原理,由于在 《详解 服务发现 的基本实现》中,本人已经 详尽地 解释过了,
因此,在本篇博文中,本人就来 展示下基本的功能:
至于 资源信息的 注册、注销、资源信息的获取 等功能,由于只是执行一次
因此,我们在此处采用 短链接模式,也就是 RMI技术 进行 网络通信
那么,在 服务发现
的基础上,本人就来实现下 资源发现
首先,本人来说明下 所需的 Jar包支持:
Jar包 支持:
- rmi-0.0.1.jar
(《详解 RMI技术 的基本实现》 所提供的接口与类)- balance-0.0.1.jar
(《详解 负载均衡技术 的基本实现》 所提供的接口与类)- Youzg-Mec-Utils-0.0.1.jar
(《【小工具】专栏总集篇》 所提供的接口与类)- gson-2.7.jar
(Youzg-Mec-Utils-0.0.1.jar 所需)- fastjson-1.2.62.jar
(Youzg-Mec-Utils-0.0.1.jar 所需)- cglib-nodep-2.1.3.jar
(Youzg-Mec-Utils-0.0.1.jar 所需)
那么,下面本人就来在上面Jar包的基础上,来实现下 资源发现
技术:
从技术的名称中,我们能够听得出:
资源 是
资源发现
技术 的核心
那么,本人就先来实现下 有关资源 的类:
资源:
首先,本人来给出一个 自定义异常类 —— FileDoesNotExistException:
目标路径不存在文件 异常 —— FileDoesNotExistException:
package edu.youzg.resource_founder.exception;
/**
* 目标路径不存在 文件 异常
*/
public class FileDoesNotExistException extends Exception {
private static final long serialVersionUID = -6217840007725641732L;
public FileDoesNotExistException() {
}
public FileDoesNotExistException(String message) {
super(message);
}
public FileDoesNotExistException(String message, Throwable cause) {
super(message, cause);
}
public FileDoesNotExistException(Throwable cause) {
super(cause);
}
}
本人再提供一个类,来表示 一个资源的身份:
资源 基本信息 实体类 —— ResourceBaseInfo:
实现 思路:
作为 标识资源 的类,最重要的是 能和其它资源区分开来
那么,在这里,本人 以三个属性,来描述一个资源:
- 服务器的名称
- 该资源 在 服务器端 的 id
- 该资源 的 版本号
实现 代码:
package edu.youzg.resource_founder.core;
/**
* 资源的基本信息<br/>
* 服务器的名称、当前资源的id、当前资源的version
*/
public class ResourceBaseInfo {
private String app; // 服务器的名称
private String id; // 当前资源的 id
private String version; // 当前资源的 版本号
public ResourceBaseInfo(String app, String id, String version) {
this.app = app;
this.id = id;
this.version = version;
}
public String getApp() {
return this.app;
}
public void setApp(String app) {
this.app = app;
}
public String getId() {
return this.id;
}
public void setId(String id) {
this.id = id;
}
public String getVersion() {
return this.version;
}
public void setVersion(String version) {
this.version = version;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((app == null) ? 0 : app.hashCode());
result = prime * result + ((id == null) ? 0 : id.hashCode());
result = prime * result + ((version == null) ? 0 : version.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
} else if (obj == null) {
return false;
} else if (this.getClass() != obj.getClass()) {
return false;
} else {
ResourceBaseInfo other = (ResourceBaseInfo) obj;
if (this.app == null) {
if (other.app != null) {
return false;
}
} else if (!this.app.equals(other.app)) {
return false;
}
if (this.id == null) {
if (other.id != null) {
return false;
}
} else if (!this.id.equals(other.id)) {
return false;
}
if (this.version == null) {
if (other.version != null) {
return false;
}
} else if (!this.version.equals(other.version)) {
return false;
}
return true;
}
}
@Override
public String toString() {
return "[" + this.app + "]" + this.id + ":" + this.version;
}
}
既然表明了 资源的身份,资源的内容信息 该如何获取呢?
本人再来提供一个类来 封装资源的详细信息:
资源 详细信息 实体类 —— ResourceSpecificInfo:
实现 思路:
由于一个资源可能是目标资源的 子资源,因此需要一个 编号属性
而且我们需要了解 该资源的地址,才能获取到该资源的内容信息
并且 我们在 接收/发送 时也需要了解 该资源的 大小
那么,依照上述思想,本人来给出实现代码:
实现 代码:
package edu.youzg.resource_founder.core;
import java.io.File;
import java.io.Serializable;
import java.util.Objects;
import edu.youzg.resource_founder.exception.FileDoesNotExistException;
/**
* (单)子资源详细信息<br/>
* 可能是目标文件的 子文件,也可能是目标文件<br/>
* 文件编号、文件所在本地位置、文件大小
*/
public class ResourceSpecificInfo implements Serializable { // 序列化,防止fastjson转换失败
private static final long serialVersionUID = 7198662964849667723L;
private int fileNo; // 文件的编号
private String filePath; // 文件所在本地相对位置(相对根目录 的路径)
private long fileSize; // 文件大小
public ResourceSpecificInfo() {
}
/**
* 设置文件路径,<br/>
* 并根据设置的文件路径,初始化成员属性
* @param fileNo 文件编号
* @param absoluteRoot 该文件 的根路径(可能是文件夹)
* @param filePath 当前(子)文件路径
* @throws FileDoesNotExistException
*/
public void setFilePath(int fileNo, String absoluteRoot, String filePath) throws FileDoesNotExistException {
this.fileNo = fileNo;
String absoluteFilePath = absoluteRoot + filePath; // 计算文件的 真实完整路径
File file = new File(absoluteFilePath);
if (!file.exists()) {
throw new FileDoesNotExistException("文件[" + absoluteFilePath + "]不存在!");
}
this.filePath = filePath;
this.fileSize = file.length();
}
public void setFileNo(int fileNo) {
this.fileNo = fileNo;
}
public int getFileNo() {
return fileNo;
}
public String getFilePath() {
return filePath;
}
public long getFileSize() {
return fileSize;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
ResourceSpecificInfo fileInfo = (ResourceSpecificInfo) o;
return fileNo == fileInfo.fileNo;
}
@Override
public int hashCode() {
return Objects.hash(fileNo);
}
@Override
public String toString() {
return fileNo + " : " + filePath + " : " + fileSize;
}
}
那么,有了资源的信息,我们现在就来思考下 如何去实现 注册中心:
注册 中心:
在上文中,本人讲过:
注册中心 会提供 资源拥有者 节点信息的 注册、注销,
以及 获取 目标资源的 拥有者节点信息列表、获取 注册的资源目录 的功能
那么,本人来给出一个 节点信息存储池,来方便我们管理 注册的资源拥有者节点
节点信息存储池 —— NodePool:
package edu.youzg.resource_founder.node;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;
import edu.youzg.balance.INetNode;
import edu.youzg.resource_founder.core.ResourceNodePool;
import edu.youzg.util.Didadida;
/**
* 存储/删除节点,并可以扫描每一个存储的节点的健康情况:<br/>
* 以当前节点 的hashcode为键,当前节点的节点信息为值,存储的map
*/
public class NodePool {
private static final long DEFAULT_DELAY_TIME = 3000L;
private static final Map<Integer, INetNode> nodePool
= new ConcurrentHashMap();
private static NodePool.ScanTimer scanTimer = new NodePool.ScanTimer(DEFAULT_DELAY_TIME);
private static Logger log = Logger.getLogger(NodePool.class);
public NodePool() {
}
/**
* 开始扫描每一个节点
*/
public static void startScanNode() {
scanTimer.start();
}
/**
* 停止扫描线程
*/
public static void stopScanNode() {
scanTimer.stop();
}
/**
* 新增一个 资源拥有者 节点
* @param node 资源拥有者 节点信息
*/
public static void addNode(INetNode node) {
int key = node.hashCode();
if (!nodePool.containsKey(key)) {
nodePool.put(key, new ResourceHolderNode(node));
}
}
/**
* 删除一个 资源拥有者 节点
* @param node 资源拥有者 节点信息
*/
public static void removeNode(INetNode node) {
int key = node.hashCode();
if (nodePool.containsKey(key)) {
nodePool.remove(key);
}
}
static class ScanTimer extends Didadida {
public ScanTimer() {
}
public ScanTimer(long delay) {
super(delay);
}
/**
* 心跳检测<br/>
* 保证 当前的列表 中 只保存 “活”的资源拥有者节点
*/
@Override
protected void doTask() {
if (!NodePool.nodePool.isEmpty()) {
Iterator nodeList = NodePool.nodePool.values().iterator();
while (nodeList.hasNext()) {
INetNode node = (INetNode) nodeList.next();
try {
((ResourceHolderNode) node).isActive();
} catch (Exception e) {
log.warn("节点[" + node.getIp() + ":" + node.getPort() + "]异常宕机!注册中心已将其 拥有资源信息 注销!");
ResourceNodePool.logout(node); // 当前节点 失活,注销该节点
}
}
}
}
}
}
接下来,本人根据上面的类,来提供一个 资源-拥有者节点信息 映射池:
资源-拥有者 映射池 —— ResourceNodePool:
package edu.youzg.resource_founder.core;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.log4j.Logger;
import edu.youzg.balance.INetNode;
import edu.youzg.resource_founder.node.NodePool;
/**
* 资源-拥有者 映射池:<br/>
* 注册/注销 资源拥有信息、查询拥有者信息
*/
public class ResourceNodePool {
private static final Map<Integer, List<INetNode>> rnPool
= new ConcurrentHashMap(); // 以 资源信息的hashcode 为键,该资源的拥有者list为值,存储map
private static final List<ResourceBaseInfo> resourceList
= new CopyOnWriteArrayList(); // 保存 已注册 的资源信息
private static Logger log = Logger.getLogger(ResourceNodePool.class);
/**
* 注册 一个资源的拥有者 的信息
* @param resourceInfo 目标资源
* @param netNode 持有者的节点信息
*/
public static void registry(ResourceBaseInfo resourceInfo, INetNode netNode) {
int key = resourceInfo.hashCode();
List<INetNode> addrList = null;
synchronized (rnPool) {
addrList = (List) rnPool.get(key);
if (addrList == null) {
addrList = new CopyOnWriteArrayList();
rnPool.put(key, addrList);
resourceList.add(resourceInfo);
}
}
addrList.add(netNode);
NodePool.addNode(netNode);
}
/**
* 注销 一个资源拥有者的 指定资源 信息
* @param resourceInfo 目标资源
* @param netNode 持有者的节点信息
*/
public static void logout(ResourceBaseInfo resourceInfo, INetNode netNode) {
int key = resourceInfo.hashCode();
List<INetNode> addrList = null;
synchronized (rnPool) {
addrList = rnPool.get(key);
if (addrList == null) {
// 日志:资源不存在异常!
log.error("资源["+ resourceInfo.toString() + "]不存在!");
return;
}
addrList.remove(netNode);
if (addrList.isEmpty()) {
rnPool.remove(key);
resourceList.remove(resourceInfo);
}
}
}
/**
* 注销 指定节点 的 所有资源
* @param netNode 要注销的 节点
*/
public static synchronized void logout(INetNode netNode) {
int key = 0;
List<INetNode> netNodes = null;
for (ResourceBaseInfo resourceInfo : resourceList) {
key = resourceInfo.hashCode();
netNodes = rnPool.get(key);
boolean remove = netNodes.remove(netNode);
if (remove) {
if (netNodes.isEmpty()) {
rnPool.remove(key);
resourceList.remove(resourceInfo);
}
}
}
NodePool.removeNode(netNode);
}
/**
* 获取 指定资源 的节点列表
* @param resourceInfo 目标资源
* @return 该资源的 拥有者节点列表
*/
public static synchronized List<INetNode> getAddressList(ResourceBaseInfo resourceInfo) {
return rnPool.get(resourceInfo.hashCode());
}
/**
* 获取 当前被注册的 资源列表
* @return 当前被注册的 资源列表
*/
public static List<ResourceBaseInfo> getResourceList() {
return resourceList;
}
}
那么,现在本人来给出一个 注册中心 功能接口:
注册中心 功能接口 —— IResourceCenter:
package edu.youzg.resource_founder.center;
import java.util.List;
import edu.youzg.balance.DefaultNetNode;
import edu.youzg.resource_founder.core.ResourceSpecificInfo;
import edu.youzg.resource_founder.core.ResourceBaseInfo;
/**
* 注册中心 基本功能接口
*/
public interface IResourceCenter {
void registry(ResourceBaseInfo info, List<ResourceSpecificInfo> fileInfoList, DefaultNetNode netNode);
void logout(ResourceBaseInfo res, DefaultNetNode addr);
List<DefaultNetNode> getTotalAddressList(String recieveIp, int receivePort, ResourceBaseInfo res);
List<ResourceSpecificInfo> getFileInfoListByResourceInfo(ResourceBaseInfo ri);
List<ResourceBaseInfo> getResourceList();
}
那么,根据上文所给的 资源-拥有者 映射池
本人现在来给出 注册中心 的 功能实现类:
注册中心 功能实现类 —— ResourceCenterImpl:
package edu.youzg.resource_founder.center;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.log4j.Logger;
import edu.youzg.balance.DefaultNetNode;
import edu.youzg.balance.INetNode;
import edu.youzg.resource_founder.core.ResourceSpecificInfo;
import edu.youzg.resource_founder.core.ResourceBaseInfo;
import edu.youzg.resource_founder.core.ResourceNodePool;
/**
* 注册中心 基本功能 实现类
*/
public class ResourceCenterImpl implements IResourceCenter {
private Map<ResourceBaseInfo, List<ResourceSpecificInfo>> resourcePool
= new ConcurrentHashMap<ResourceBaseInfo, List<ResourceSpecificInfo>>();
private Logger log = Logger.getLogger(ResourceCenterImpl.class);
public ResourceCenterImpl() {
}
@Override
public void registry(ResourceBaseInfo info, List<ResourceSpecificInfo> fileInfoList, DefaultNetNode netNode) {
ResourceNodePool.registry(info, netNode);
if (!this.resourcePool.containsKey(info)) {
this.resourcePool.put(info, fileInfoList);
}
log.info("节点" + netNode + ":资源[" + info + "]注册成功!");
}
@Override
public void logout(ResourceBaseInfo info, DefaultNetNode netNode) {
ResourceNodePool.logout(info, netNode);
if (resourcePool.get(info).size() <= 0) {
resourcePool.remove(info);
log.info("资源[" + info + "]拥有者已全部注销,该资源当前不存在!");
}
log.info("节点" + netNode + ":资源[" + info + "]注销成功!");
}
@Override
public List<DefaultNetNode> getTotalAddressList(String recieveIp, int receivePort, ResourceBaseInfo res) {
log.info("节点[" + recieveIp + ":" + receivePort + "]:请求资源[" + res + "]");
List<DefaultNetNode> result = new ArrayList<DefaultNetNode>();
List<INetNode> nodeList = ResourceNodePool.getAddressList(res);
if (nodeList == null || nodeList.isEmpty()) {
return result;
}
for (INetNode node : nodeList) {
result.add((DefaultNetNode) node);
}
return result;
}
@Override
public List<ResourceBaseInfo> getResourceList() {
return ResourceNodePool.getResourceList();
}
@Override
public List<ResourceSpecificInfo> getFileInfoListByResourceInfo(ResourceBaseInfo ri) {
return this.resourcePool.get(ri);
}
}
在有些情况下,注册中心 需要 监听 资源拥有者 的健康状况
因此,在这里,本人来提供一对 订阅者/发布者:
资源拥有者“健康状况” 发布者 —— IResourceSpeaker:
package edu.youzg.resource_founder.core;
import java.util.ArrayList;
import java.util.List;
/**
* 资源信息发布者 接口
*/
public interface IResourceSpeaker {
/**
* 增加 指定的订阅者
* @param listener 目标订阅者
*/
default void addListener(IResourceListener listener) {
List<IResourceListener> listenerList = getListenerList();
if (listenerList == null) {
synchronized (IResourceSpeaker.class) {
listenerList = getListenerList();
if (listenerList == null) {
listenerList = new ArrayList<>();
setListenerList(listenerList);
}
}
}
if (listenerList.contains(listener)) {
return;
}
listenerList.add(listener);
}
/**
* 移除 指定的订阅者
* @param listener 指定的订阅者
*/
default void removeListener(IResourceListener listener) {
List<IResourceListener> listenerList = getListenerList();
if (listenerList == null || !listenerList.contains(listener)) {
return;
}
listenerList.remove(listener);
}
/**
* 向所有订阅者 发布消息
* @param message 要发布的消息
*/
default void speakOut(String message) {
List<IResourceListener> listenerList = getListenerList();
if (listenerList == null || listenerList.isEmpty()) {
return;
}
for (IResourceListener listener : listenerList) {
listener.dealMessage(message);
}
}
/**
* 获取订阅者 列表
* @return 订阅者 列表
*/
List<IResourceListener> getListenerList();
/**
* 设置 订阅者 列表
* @param listenerList 订阅者 列表
*/
void setListenerList(List<IResourceListener> listenerList);
}
资源拥有者“健康状况” 订阅者 —— IResourceListener:
package edu.youzg.resource_founder.core;
/**
* 资源信息订阅者 接口
*/
public interface IResourceListener {
void dealMessage(String message);
}
那么,在以上诸类的基础上,本人来实现下 注册中心:
[核心]注册中心 —— ResourceRegistryCenter:
在上文 实现原理 处,本人就讲过:
对于 注册中心 的各种功能,均采用
短链接
模式 进行 网络通信
由 上述思想 以及 前面的铺垫,本人来给出 注册中心 的代码:
package edu.youzg.resource_founder.core;
import edu.youzg.resource_founder.node.NodePool;
import edu.youzg.rmi_impl.core.RMIFactory;
import edu.youzg.rmi_impl.server.RMIServer;
import java.util.List;
/**
* 资源注册中心:<br/>
* 默认端口:6666<br/>
* 配置文件名:ResourceRegistryCenter-RMI.xml
*/
public class ResourceRegistryCenter implements IResourceSpeaker {
private static final int DEFAULT_PORT = 6666;
private static final String DEFAULT_CONFIG_PATH = "/resource/ResourceRegistryCenter-RMI.xml";
private RMIServer rmiServer;
private volatile boolean startup;
private List<IResourceListener> listenerList;
public ResourceRegistryCenter() {
this(DEFAULT_PORT);
}
public ResourceRegistryCenter(int rmiServerPort) {
this.rmiServer = new RMIServer();
this.rmiServer.setRmiPort(rmiServerPort);
}
public void initRegistryCenter() {
initRegistryCenter(DEFAULT_CONFIG_PATH);
}
public void initRegistryCenter(String configFilePath) {
RMIFactory.scanRMIMapping(configFilePath);
}
public void setRmiServerPort(int rmiServerPort) {
this.rmiServer.setRmiPort(rmiServerPort);
}
public void startup() {
if (this.startup == true) {
speakOut("注册中心 已启动");
return;
}
this.startup = true;
this.rmiServer.startUp();
NodePool.startScanNode();
speakOut("注册中心 启动成功");
}
public void shutdown() {
if (this.startup == false) {
speakOut("注册中心 已关闭");
return;
}
this.startup = false;
this.rmiServer.shutdown();
NodePool.stopScanNode();
speakOut("注册中心 关闭成功");
}
@Override
public List<IResourceListener> getListenerList() {
return listenerList;
}
@Override
public void setListenerList(List<IResourceListener> listenerList) {
this.listenerList = listenerList;
}
}
注册中心设置好了,那么我们该如何去与注册中心通信呢?
资源请求者 和 资源拥有者 访问注册中心的流程 都是一样的
因此,在这里,本人提供一个 资源处理器,来简化 与注册中心通信 的流程:
资源处理器:
资源处理器 —— Resourcer:
package edu.youzg.resource_founder.resourcer;
import edu.youzg.resource_founder.center.IResourceCenter;
import edu.youzg.rmi_impl.client.RMIClient;
/**
* 封装 “访问 资源注册中心” 的基本属性
*/
public class Resourcer {
protected RMIClient rmiClient; // 请求 资源注册中心
protected IResourceCenter irc; // 所要请求执行的方法 执行接口
protected Resourcer() {
this.rmiClient = new RMIClient();
this.irc = this.rmiClient.getProxy(IResourceCenter.class);
}
public void setRmiServerIp(String rmiServerIp) {
this.rmiClient.setRmiServerIp(rmiServerIp);
}
public void setRmiServerPort(int rmiServerPort) {
this.rmiClient.setRmiServerPort(rmiServerPort);
}
}
资源拥有者:
作为一个 提供特殊功能 的 网络节点,必须要有 节点信息
但是,在给出 节点信息类 之前,本人要先来给出一个 具有 判断当前拥有者节点是否存活 功能 的接口 及其 实现类:
判断当前节点“是否存活” 功能接口 —— IResourceHolder:
package edu.youzg.resource_founder.core;
/**
* 判断 当前资源拥有者是否存活
*/
public interface IResourceHolder {
default boolean isActive() throws Exception {
return false;
}
}
接下来,是这个接口的实现类:
判断当前节点“是否存活” 功能实现类 —— ResourceHolderImpl:
package edu.youzg.resource_founder.core;
/**
* 此类并未完成,若有需要,请使用者自行实现!
* 判断 当前资源拥有者 是否 存活<br/>
* 此处可以添加 负载均衡策略
*/
public class ResourceHolderImpl implements IResourceHolder {
public ResourceHolderImpl() {
}
@Override
public boolean isActive() throws Exception {
return false;
}
}
在上面的接口和类的基础上,本人来给出 资源持有者 节点 封装类:
资源持有者 节点 —— ResourceHolderNode:
package edu.youzg.resource_founder.node;
import edu.youzg.balance.DefaultNetNode;
import edu.youzg.balance.INetNode;
import edu.youzg.resource_founder.core.IResourceHolder;
import edu.youzg.rmi_impl.client.RMIClient;
/**
* 资源持有者 节点:<br/>
* 1. ResourceHolderNode(INetNode node) 初始化成员属性
* 2. isActive()判断存活情况
*/
public class ResourceHolderNode extends DefaultNetNode {
private IResourceHolder resourceHolder;
public ResourceHolderNode(INetNode node) {
super(node.getIp(), node.getPort());
RMIClient rmiClient = new RMIClient();
rmiClient.setRmiServerIp(getIp());
rmiClient.setRmiServerPort(getPort());
this.resourceHolder = rmiClient.getProxy(IResourceHolder.class);
}
public boolean isActive() throws Exception {
return resourceHolder.isActive();
}
}
那么,本人在上面几个类的基础上,来实现下 资源拥有者:
[核心]资源拥有者 —— ResourceHolder:
package edu.youzg.resource_founder.resourcer;
import java.util.List;
import edu.youzg.balance.DefaultNetNode;
import edu.youzg.balance.INetNode;
import edu.youzg.resource_founder.core.ResourceSpecificInfo;
import edu.youzg.resource_founder.core.ResourceBaseInfo;
import edu.youzg.rmi_impl.core.RMIFactory;
import edu.youzg.rmi_impl.server.RMIServer;
/**
* 封装 资源持有者 的基本功能:<br/>
* 默认 配置文件 全路径名:/resource/ResourceHolder-RMI.xml
*/
public class ResourceHolder extends Resourcer {
private static final String DEFAULT_CONFIG_PATH = "/resource/ResourceHolder-RMI.xml";
private RMIServer rmiServer;
private INetNode netNode;
public ResourceHolder(String ip, int port) {
this.netNode = new DefaultNetNode(ip, port);
this.rmiServer = new RMIServer();
this.rmiServer.setRmiPort(port);
this.rmiServer.startUp();
}
/**
* 通过扫描 默认路径的配置文件,初始化RMI工厂
*/
public static void scanRMIMapping() {
scanRMIMapping(DEFAULT_CONFIG_PATH);
}
/**
* 通过扫描 指定路径的配置文件,初始化RMI工厂
* @param mappingFile 指定的 配置文件路径
*/
public static void scanRMIMapping(String mappingFile) {
RMIFactory.scanRMIMapping(mappingFile);
}
public void setHolderIp(String ip) {
this.netNode.setIp(ip);
}
public void setHolderPort(int serverPort) {
this.rmiServer.setRmiPort(serverPort);
this.netNode.setPort(serverPort);
}
/**
* 开启 RMI服务器
*/
public void startUp() {
this.rmiServer.startUp();
}
/**
* 注册一个资源 的持有信息
* @param info 目标资源的信息
*/
public void registry(ResourceBaseInfo info, List<ResourceSpecificInfo> fileInfoList) {
this.irc.registry(info, fileInfoList, (DefaultNetNode)netNode);
}
/**
* 注销一个资源 的持有信息
* @param info 目标资源的信息
*/
public void logout(ResourceBaseInfo info) {
this.irc.logout(info, (DefaultNetNode) this.netNode);
}
/**
* 关闭 RMI服务器
*/
public void shutdown() {
this.rmiServer.shutdown();
}
}
最后,就是 资源请求者 了:
资源请求者:
作为 资源请求者,并没有太多的逻辑
只需要 请求注册中心 所需要的信息 即可:
[核心]资源请求者 —— ResourceRequester:
package edu.youzg.resource_founder.resourcer;
import edu.youzg.balance.DefaultNetNode;
import edu.youzg.resource_founder.core.ResourceSpecificInfo;
import edu.youzg.resource_founder.core.ResourceBaseInfo;
import java.util.List;
/**
* 封装 资源请求者 的基本功能:<br/>
* 1. setRmiServerIp() 和 setRmiServerPort()<br/>
* 2. getAddressList(ResourceInfo res)<br/>
* 3. getResourceList()
*/
public class ResourceRequester extends Resourcer {
public ResourceRequester() {
super();
}
/**
* 获取 目标资源的 拥有者列表
* @param recieveIp 请求者ip
* @param receivePort 请求者port
* @param res 目标资源信息
* @return 目标资源的 拥有者列表
*/
public List<DefaultNetNode> getTotalAddressList(String recieveIp, int receivePort, ResourceBaseInfo res) {
return irc.getTotalAddressList(recieveIp, receivePort, res);
}
/**获取 资源中心 当前持有的 资源列表
*
* @return 资源中心 当前持有的 资源列表
*/
public List<ResourceBaseInfo> getResourceList() {
return irc.getResourceList();
}
/**
* 根据 目标资源信息,获取 该资源的 子文件相对路径列表
* @param ri 目标资源信息
* @return 该资源的 子文件相对路径列表
*/
public List<ResourceSpecificInfo> getFilePathListByResourceInfo(ResourceBaseInfo ri) {
return this.irc.getFileInfoListByResourceInfo(ri);
}
}
若有需要上述源码的同学,本人已将本文所讲解到的代码打成了Jar包:
工具 Jar包:
如有需要,请点击下方链接:
Resource-Discovery
心得体会:
那么,到这里,资源发现 技术
就基本实现了
我们在使用时,只需要将 资源拥有者 所拥有的 资源信息 注册
再通过 资源请求者 请求 注册中心 即可!
至于使用展示,将在本人之后的博文《【多文件自平衡云传输】专栏总集篇》中 进行巧妙地运用,
并在最后会有视频展示,有兴趣的同学请前往围观哦!
(最后,附上 本人《多文件自平衡云传输框架》专栏 展示视频的封面,希望大家多多支持哦!)