资源发现(上篇)

首先来浅谈一下服务发现:即何为服务发现?

所有服务器(无论是某一种APP的多个服务器,还是不同APP的多个服务器)在启动时,都需要在“注册中心”进行注册;客户端需要从“注册中心”获取它所属APP的服务器(组)的地址信息;

从客户端角度看,注册中心起到的作用包含两个:
1、是否存在某个APP的服务器;
2、获取某个APP服务器(组)的地址信息。

所以,从客户端角度,这是一种“服务发现”的机制。

当然服务发现有很多妙用。此处就不进行阐述。回归正题,在学习完相关知识之后实现了一个“资源发现”。同样三种角色:资源注册中心,资源持有者,资源请求者。三者关系如下:

资源持有者 将自己持有的资源提交到注册中心。  资源请求者想要请求某个资源时必须先从“资源注册中心”获取 该资源对应的资源持有者地址集合。

实现思路及准备:

整个网络通信用的是短连接模式,利用之前编写的RMI工具,前面文章链接:

RMI学习

最基本的数据基础:

1.资源信息:ResourceInfo类(包含 app名称 + id(资源id)+ version(app版本号))。以三者的的hashCode作为该对象的相等条件。

2.网络节点:DefaultNetNode类(ip + port),首先一个网络节点能够被连接的条件是能够建立服务器(ip,port)。

3 资源节点映射表(ResourceNodePool类):首先它应该是一对多结构。因为一个资源可以由多个资源持有者提供。我使用的

是ConcurrentHashMap<Integer, List<DefaultNetNode>>。这里的  键:资源信息“ResourceInfo的hashCode”     值:值:网络节点集合

其所具有的基本功能如下:

              1>.存储注册者所注册资源与其网络地址(也即下面的registry方法)
              2>.注销资源持有者某一资源(也即下面的logout方法)
              3>.获取某一资源对应的网络节点集合(getAddressList方法)
              4>.删除某个资源持有者所注册过的所有资源(removeNode)
代码如下:

public class ResourceNodePool {
	private static final Map<Integer, List<DefaultNetNode>> rnPool = 
			new ConcurrentHashMap<Integer, List<DefaultNetNode>>();
	
	public static void registry(ResourceInfo resourceInfo, DefaultNetNode addr) {
		int resourceId = resourceInfo.hashCode();
		List<DefaultNetNode> nodeList = rnPool.get(resourceId);
		//若没有该资源信息的记录  则此处应该创建  且是线程安全的
		if(nodeList == null) {
			synchronized (rnPool) {
				if(nodeList == null) {
					//创建资源  CopyOnWriteArrayList 是线程安全的
					nodeList = new CopyOnWriteArrayList<DefaultNetNode>();
					rnPool.put(resourceId, nodeList);
				}
			}
		}
		nodeList.add(addr);
		//这里NodePool的作用后面会介绍到
		NodePool.add(addr);
	}
	
	/**
	 *	 此处注销的是一个网络节点对应的  某一个资源
	 * @param resourceInfo	要注销的资源信息
	 * @param addr			自己的资源地址
	 */
	public static void logout(ResourceInfo resourceInfo, DefaultNetNode addr) {
		int resourceId = resourceInfo.hashCode();
		List<DefaultNetNode> nodeList = null;
		synchronized (rnPool) {
			nodeList = rnPool.get(resourceId);
			if(nodeList == null) {
				//不存在该资源
				return;
			}
			nodeList.remove(addr);
			if(nodeList.isEmpty()) {
				rnPool.remove(resourceId);
			}
		}
		
	}
	
	//资源持有者宕机  应该移除其所有注册的资源
	public static synchronized void removeNode(DefaultNetNode node) {
		Iterator<List<DefaultNetNode>> nodeListList = rnPool.values().iterator();
		while(nodeListList.hasNext()) {
			List<DefaultNetNode> nodeList = nodeListList.next();
			nodeList.remove(node);
		}
		NodePool.remove(node);
	}
	
	/*
	 *	获取某个资源对应的网络节点集合 
	 */
	public static List<DefaultNetNode> getAddressList(ResourceInfo resourceInfo){
		int resourceId = resourceInfo.hashCode();
		List<DefaultNetNode> nodeList = null;
		synchronized(rnPool) {
			nodeList = rnPool.get(resourceId);
		}
		return nodeList;
	}
}

做了上述准备之后就需要考虑 资源注册中心ResourceRegistryCenter类 的实现了: 

首先资源注册中心要能响应如下几种最基本请求:
    1.资源持有者发起的资源注册请求。
    2.资源持有者发起的注销某一资源的请求。
    3.资源请求者发起的资源获取请求(获取该资源对应的网络节点地址)。

上述方法都是远程方法,即方法的本体只存在于“资源注册中心”一端。而对于资源请求者和资源持有者都是利用RMI远程调用这些方法。相关的RMI接口如下:

IResourceCenter:

public interface IResourceCenter {
	//资源注册方法(提供要注册的资源信息 + 自己的网络节点地址)
	boolean registry(ResourceInfo resourceInfo, DefaultNetNode node);
	//单个资源注销方法
	boolean logout(ResourceInfo resourceInfo, DefaultNetNode node);
	//根据某一资源信息可获取相关网络节点
	List<DefaultNetNode> getAddressList(ResourceInfo resourceInfo);
}

该接口的实现类。也即远程方法本体的存在:

ResourceCenterImpl类:

public class ResourceCenterImpl implements IResourceCenter {

	public ResourceCenterImpl() {
	}

	@Override
	public boolean registry(ResourceInfo resourceInfo, DefaultNetNode node) {
		ResourceNodePool.registry(resourceInfo, node);
		return true;
	}

	@Override
	public boolean logout(ResourceInfo resourceInfo, DefaultNetNode node) {
		ResourceNodePool.logout(resourceInfo, node);
		return true;
	}

	@Override
	public List<DefaultNetNode> getAddressList(ResourceInfo resourceInfo) {
		List<DefaultNetNode> result = new ArrayList<DefaultNetNode>();
		List<DefaultNetNode> nodeList = ResourceNodePool.getAddressList(resourceInfo);
		
		if(nodeList == null || nodeList.isEmpty()) {
			return result;
		}
		
		for(DefaultNetNode node : nodeList) {
			result.add(node);
		}
		
		return result;
	}

}


除此之外他还应该完成:若某一资源持有者宕机(下线)要从相应的资源(该网络节点提供的所有资源)中删除该节点信息(注意是找出对应资源删除该节点而非直接注销整个资源,因为可能该资源还有其他的资源持有者

ResourceRegistryCenter:

public class ResourceRegistryCenter {
	public static final int DEFAULT_PORT = 54200;
	
	//加载RMI配置文件
	static {
		RMIFactory.scanRmiMapping("/RMIMapping.xml");
	}

	private RMIServer rmiServer;
	private volatile boolean startUp;
	
	public ResourceRegistryCenter() {
		this(DEFAULT_PORT);
	}
	
	public ResourceRegistryCenter(int registryPort) {
		startUp = false;
		this.rmiServer = new RMIServer();
		this.rmiServer.setPort(registryPort);
	}
	
	public void setRegistryPort(int port) {
		this.rmiServer.setPort(port);
	}
	
	/**
	 * 	启动资源注册中心服务器
	 */
	public void startUp() {
		if(startUp) {
			return;
		}
		startUp = true;
		this.rmiServer.startUp();
		NodePool.startScanNode();
	}
	
	/**
	 * 关闭资源注册中心(宕机)
	 */
	public void shutdown() {
		if(!startUp) {
			return;
		}
		startUp = false;
		this.rmiServer.shutdown();
		NodePool.stopScanNode();
	}
	
}

接着再来分析  资源持有者 和 资源请求者:

这两者有一点共同的地方,即:

   1.首先它们两者都需要与“资源注册中心”主动通信。  即,他们两者都是“资源注册中心”的
    RMIClient。

    2.基于上述一点,故二者也都需要定义IResourceCenter接口。通过该接口来获取代理,
    执行远程方法。

基于这个相同点出发,首先定义出二者的共同父类:

Resourcer:

public class Resourcer {
	protected RMIClient rmiClient;
	protected IResourceCenter irc;
	
	public Resourcer() {
		this.rmiClient = new RMIClient();
		irc = rmiClient.getProxy(IResourceCenter.class);
	}
	
	public Resourcer(String registryIp, int registryPort) {
		this.rmiClient = new RMIClient(registryIp, registryPort);
		irc = rmiClient.getProxy(IResourceCenter.class);
	}
	
	public void setRegistryIp(String ip) {
		this.rmiClient.setRmiServerIp(ip);
	}
	
	public void setRegistryPort(int port) {
		this.rmiClient.setRmiServerPort(port);
	}
}

在分析 资源持有者 之前先要思考一个问题:

 由于资源持有者与资源注册中心没有保持长链接,而采用的是短连接模式,那么在该模式下如果资源持有者主动下线
还好,可以注销其所注册的资源。但是若它是异常宕机。资源注册中心是无法发现的。总不能给资源请求者响应一个
包含已经异常宕机的网络地址吧。出此考虑,想到解决的一个方法(不能说彻底避免该问题的发生,只能尽量降低该问题发生的概率)即:能不能将这些资源持有者的网络地址收集在一起。(封装在一个容器中存储起来)。然后每隔一段时间检测其是否异常宕机,我处理的方法是主动连接他们,如果超出一定时间没有连接上,则就将其视为异常宕机状态。而这个检测我是在启动资源注册中心服务器时候就开启的。在资源注册中心宕机时关闭。

因此才想到做了NodePool这个类。  而且这个容器的填充是资源持有者在注册资源时做的。

基于上述考虑基本可以确定 资源持有者(ResourceHolder)的基本功能了:

         1.如果要接受资源注册中心以上述方式进行健康检查。则他必须要建立RMIServer。
         2.它需要与“资源注册中心”主动通信。提交注册资源,注销资源的请求。故他需要继承Resourcer类
         3.提供资源的注册,注销方法。             
针对第一点的实现,先来定义“RMI接口”:

IResourceHolder:

public interface IResourceHolder {
	boolean isActive() throws Exception;
}

实现类ResourceHolderImpl:

public class ResourceHolderImpl implements IResourceHolder {
	public ResourceHolderImpl() {
	}

	/*
	 *	这个方法什么都不用做其实已经完成了他的功能了。	
	 *	他就是用来检测的。如果此方法执行不成功他会掉入连接异常。也就达到了检测的目的 
	 *	至于返回值其实就根本就不在乎
	 */
	@Override
	public boolean isActive() throws Exception {
		return true;
	}
}

为避免篇幅太长,后面的内容会在下篇中进行介绍。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值