详解 资源发现技术 的基本实现

Youzg LOGO

在本人之前的博文 《详解 服务发现 的基本实现》中,本人 详尽地 讲解了 服务发现 技术基本概念 以及 重要意义 等诸多知识点
甚至在文章中 基本实现了 服务发现 技术

那么,本篇博文 名为 资源发现技术,为什么本人要扯到 服务发现 技术 呢?

服务发现 与 资源发现:

  1. 所谓的 “发现”,在我们的技术中,也就是 注册注销获取提供者列表 等操作
    因此,在很大程度上,两者的 实现思路 一致
  2. 资源,在 一定的程度上 来讲,就是 服务
    例如:电影视频、游戏数据 等
    上面的两个例子,都是 服务
    但是,在我们的角度上来分析,电影视频就是 视频资源,游戏数据就是 数据资源
    由上面两个例子来分析,其实 资源发现 就是 服务发现衍生体

实现 原理:

至于 实现原理,由于在 《详解 服务发现 的基本实现》中,本人已经 详尽地 解释过了,
因此,在本篇博文中,本人就来 展示下基本的功能
原理
至于 资源信息的 注册注销资源信息的获取 等功能,由于只是执行一次
因此,我们在此处采用 短链接模式,也就是 RMI技术 进行 网络通信


那么,在 服务发现 的基础上,本人就来实现下 资源发现

首先,本人来说明下 所需的 Jar包支持

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


心得体会:

那么,到这里,资源发现 技术 就基本实现了
我们在使用时,只需要将 资源拥有者 所拥有的 资源信息 注册
再通过 资源请求者 请求 注册中心 即可!
至于使用展示,将在本人之后的博文《【多文件自平衡云传输】专栏总集篇》中 进行巧妙地运用,
并在最后会有视频展示,有兴趣的同学请前往围观哦!

(最后,附上 本人《多文件自平衡云传输框架》专栏 展示视频的封面,希望大家多多支持哦!)
视频展示

原文地址:https://www.cnblogs.com/codderYouzg/p/13542855.html