自主实现服务发现——实现过程与代码分析

上一篇博文中对服务发现作了简单的讲解。本篇会详细讲述代码的实现。

资源的基本信息的类(ResourceInfo)的代码以及ip与port的集合类:(INetNode,DefaultNetNode):这里就不占用篇幅了,可以在上一篇中查看。

代码及详解:

搬运上一篇各个类的说明:

   用作RMI调用的远程方法与实现类:(注:我的RMI框架采用的是XML配置)

   IResourceCenter 与ResourceCenterImpl,资源拥有者(服务提供者)与资源请求者(服务消费者),拥有IResourceCenter接口,注册中心拥有有实现类ResourceCenterImpl,里面有三个方法。注册,注销与获取资源节点列表。之所以将功能不分开的原因是:资源拥有者与资源请求者的身份不是一成不变的,有可能会有其身份的转变。

   IResourceHolder与ResourceHolderImpl,里面有一个方法isActive,用以心跳检测注册中心拥有IResourceHolder接口,服务提供者拥有实现类ResourceHolderImpl接口,节点池(注册中心拥有的服务器地址列表),通过其内部的定时器,定时执行接口方法,通过服务器发送结果验证其健壮性。

注册中心拥有的类:

    NodePool、ResourceNodePool。

    NodePool:以资源拥有者的综合信息(hashCode)为键,其ip与port的集合类(INetNode)为值的一张Map,用以管理所有的资源提供者,用心跳检测所有的资源拥有者进行检测。

    ResourceNodePool:以资源信息的实例为键,拥有该资源的资源拥有者信息(INetNode)的列表为值的一张Map,用以向资源请求者提供资源列表

   两个Map均采用ConcurrentHashMap,保证线程安全,注册与注销方法中会加锁(synchronized),保证多线程环境下的数据一致性。

    ResourceHolderNode,DefaultNetNode的实现类,里面有一个RMI客户端,NodePool就是通过将其map里的节点强转成这个类后执行远程方法isActive来检测每个节点的。

    IResourceListener与IResourceSpeaker,采用观察者模式,向APP层后序编写的功能框架,不是服务发现的重点,这里就不讲解了。

   IResourceHolder、ResourceCenterImpl,实现RMI功能,上面已讲到。

   基类Resourcer说明:资源拥有者与资源请求者的基类,有一个RMI客户端,用以实现IResourceCenter的方法。

资源拥有者拥有的类:

    ResourceHolder,继承于基类Resourcer。自身还拥有一个RMI服务器,用以接收注册中心的心跳检测请求。自身拥有注册与注销资源的方法

    ResourceHolderImpl,实现RMI功能,上面已讲到。

资源请求者拥有的类:

    ResourceRequester类:继承于基类Resourcer。自身拥有请求指定资源的服务器地址列表的方法。

    注册中心:

public interface IResourceListener {
	void dealMessage(String message);
}
public interface IResourceSpeaker {
	default void addListener(IResourceListener listener) {
		List<IResourceListener> listenerList = getListenerList();
		if (listenerList == null) {
			synchronized (IResourceSpeaker.class) {
				if (listenerList == null) {
					listenerList = new ArrayList<IResourceListener>();
					setListenerList(listenerList);
				}
			}
		}
		if (listenerList.contains(listener)) {
			return;
		}
		listenerList.add(listener);
	}
	default void removeListener(IResourceListener listener) {
		List<IResourceListener> listenerList = getListenerList();
		if (listenerList == null || listenerList.isEmpty()) {
			return;
		}
		listenerList.remove(listener);
	}

	default void speakOut(String message) {
		List<IResourceListener> listenerList = getListenerList();
		if (listenerList == null || listenerList.isEmpty()) {
			return;
		}
		for (IResourceListener listener : listenerList) {
			listener.dealMessage(message);
		}
	}

	
	List<IResourceListener> getListenerList();
	void setListenerList(List<IResourceListener> listenerList);
}

注册中心代码:

public class ResourceRegistryCenter implements IResourceSpeaker {
	private List<IResourceListener> listenerList;
	private volatile boolean startup;
	private RMIServer rmiServer;

	public ResourceRegistryCenter(int port) {
		this.rmiServer = new RMIServer(port);
	}
	
	public static void scanRMIMapping(String path) {
		RMIFactory.scanRMIMapping(path);
	}
	
	public void setRmiServerPort(int port) {
		this.rmiServer.setPort(port);
	}
	
	public void startup() {
		if (this.startup == true) {
			speakOut("RMI服务器已启动,无需再次启动!");
			return;
		}
		this.startup = true;
		this.rmiServer.startup();
		speakOut("RMI服务器启动成功!");
		NodePool.startScanNode();
	}
	
	public void shutdown() {
		if (this.startup == false) {
			speakOut("RMI服务器未启动!");
			return;
		}
		this.startup = false;
		this.rmiServer.close();
		speakOut("RMI服务器已关闭!");
		NodePool.stopScanNode();
	}

	
	@Override
	public List<IResourceListener> getListenerList() {
		return this.listenerList;
	}

	@Override
	public void setListenerList(List<IResourceListener> listenerList) {
		this.listenerList = listenerList;
	}

}

注册中心拥有自己的RMI服务器,在开启后会让NodePool类对存储在里面的节点(资源拥有者服务器地址)进行扫描

NodePool类与ResourceHolderNode类,与IResourceHolder接口:

public class NodePool {
	public static final long DEFAULT_DELAY_TIME = 3000;
	private static final Map<Integer, INetNode> nodePool = 
			new ConcurrentHashMap<Integer, INetNode>();
	private static CheckNode checkNode = new CheckNode(DEFAULT_DELAY_TIME);
	
	public static void startScanNode() {
		checkNode.start();
	}
	
	public static void stopScanNode() {
		checkNode.stop();
	}
	
	public static void addNode(INetNode node) {
		int nodeId = node.hashCode();
		if(nodePool.containsKey(nodeId)) {
			return;
		}
		nodePool.put(nodeId,new ResourceHolderNode(node));
	}

	public static void removeNode(INetNode node) {
		int nodeId = node.hashCode();
		if(nodePool.containsKey(nodeId)) {
			nodePool.remove(nodeId);
		}
	}
	
	static class CheckNode extends MyTimer {
		
		public CheckNode() {
			super();
		}
		
		public CheckNode(long delay) {
			super(delay);
		}
		
		@Override
		public void doing() {
			if (nodePool.isEmpty()) {
				System.out.println("当前无资源拥有者");
				return;
			}
			Iterator<INetNode> nodeList = nodePool.values().iterator();
			
			while (nodeList.hasNext()) {
				INetNode node = nodeList.next();
				try {
					boolean alive = ((ResourceHolderNode) node).isActive();
					if (alive == false) {
						System.out.println("[资源拥有者: " + node.toString() + "状态异常!]");
						removeNode(node);
						ResourceNodePool.logout(node);
					} else {
						System.out.println("[资源拥有者: " + node.toString() + "状态正常!]");
					}
				} catch (Exception e) {
					System.out.println("[资源拥有者: " + node.toString() + "状态异常!]");
					removeNode(node);
					ResourceNodePool.logout(node);
				}
			}
		}	
	}

}
public class ResourceHolderNode extends DefaultNetNode{
	private IResourceHolder resourceHolder;
	
	public ResourceHolderNode(INetNode node) {
		super(node.getIp(), node.getPort());
		RMIClient client = new RMIClient();
		client.setRMIServerIp(getIp());
		client.setRMIServerPort(getPort());
                //远程接口赋值为代理对象
		this.resourceHolder = client.getProxy(IResourceHolder.class);
	}
	
        //调用远程方法
	public boolean isActive() throws Exception{
		return this.resourceHolder.isActive();
	}
}
public interface IResourceHolder {
	default boolean isActive() throws Exception {return true;}
}

定时器博文连接:定时器

  在NodePool类的doing方法(定时器定时执行的方法)中可以看到,我们会将Map里面的网络节点强转为DefaultNetNode的子类ResourceHolderNode,并执行其isActive方法。其isActive方法执行的是远程接口IResourceHolder的代理的isActive方法。这个操作是在定时器里写的,也就是说NodePool需要定时接收到来自资源拥有者的isAcive执行结果,以此来验证其是否存活吗,这就是心跳检测的实现过程。

  若节点已死(接收的isActive为false或执行isActive方法异常),则在自己的map里删除此节点。并且要将资源节点池(ResourceNodePool)里的所有该节点也删除掉。

  ResourceNodePool类与ResourceCenterImpl类:

public class ResourceNodePool {
	private static final Map<Integer, List<INetNode>> rnPool =
			new ConcurrentHashMap<Integer, List<INetNode>>();
	
	public ResourceNodePool() {
	}
	
	public static void registry(ResourceInfo res, INetNode address) {
		int resId = res.hashCode();
		List<INetNode> addressList = null;
		
		synchronized (rnPool) {
			addressList = rnPool.get(resId);
			if (addressList == null) {
				addressList = new CopyOnWriteArrayList<INetNode>();
			}
			addressList.add(address);
			rnPool.put(resId, addressList);
			NodePool.addNode(address);
			System.out.println("注册成功一个资源");
		}
	}
	
	public static void logout(ResourceInfo res, INetNode address) {
		int resId = res.hashCode();
		List<INetNode> addressList = null;
		
		synchronized (rnPool) {
			addressList = rnPool.get(resId);
//			if (addressList == null) {
//				return;
//			}
			addressList.remove(address);
			if (addressList.isEmpty()) {
				rnPool.remove(resId);
			}
		}
	}
	
	public static void logout(INetNode address) {
		Iterator<List<INetNode>> nodeListList = rnPool.values().iterator();
		
		while (nodeListList.hasNext()) {
			List<INetNode> nodeList = nodeListList.next();
			boolean isRemoved = nodeList.remove(address);
			if(isRemoved) { System.out.println("已删除:" + address.toString());}
		}
		NodePool.removeNode(address);
	}
	
	public static List<INetNode> getAddressList(ResourceInfo res) {
		int resId = res.hashCode();
		List<INetNode> nodeList = null;
		
		synchronized (rnPool) {
			nodeList = rnPool.get(resId);
		}
		
		return nodeList;
	}
}
public class ResourceCenterImpl implements IResourceCenter {

	@Override
	public void registry(ResourceInfo res, DefaultNetNode address) {
		ResourceNodePool.registry(res, address);
	}

	@Override
	public void logout(ResourceInfo res, DefaultNetNode address) {
		ResourceNodePool.logout(res, address);
	}

	@Override
	public List<DefaultNetNode> getAddressList(ResourceInfo res) {
		List<DefaultNetNode> result = new ArrayList<DefaultNetNode>();
		
		List<INetNode> nodeList = ResourceNodePool.getAddressList(res);
		if (nodeList == null || nodeList.isEmpty()) {
			return result;
		}
		System.out.println("一位客户请求了资源!");
		for (INetNode node : nodeList) {
			result.add((DefaultNetNode) node);
		}
		
		return result;
	}

}

    ResourceCenterImpl类是以后资源拥有者和资源请求者调用的远程类。其三个方法分别调用了ResourceNodePool对应的三个方法。

   资源拥有者(服务提供者):

   首先我们看其与资源请求者的基类Resourcer类,其拥有一个RMI客户端,其有一个IResourceCenter远程接口成员,我们在构造时对其赋值为这个接口的代理,就可以用这个成员调用远程方法(ResourceCenterImpl里的方法)了:

public class Resourcer {
	protected RMIClient rmiClient;
	protected IResourceCenter irc;
	
	public Resourcer() {
		this.rmiClient = new RMIClient();
                //远程接口赋值为代理对象
		this.irc = rmiClient.getProxy(IResourceCenter.class);
	}
	
	public void setRMIServerIp(String ip) {
		this.rmiClient.setRMIServerIp(ip);
	}
	
	public void setRMIServerPort(int port) {
		this.rmiClient.setRMIServerPort(port);
	}
}

  下面看资源拥有者的两个类ResourceHolder和ResourceHolderImpl:

public class ResourceHolder extends Resourcer {
	private RMIServer rmiServer;
	private INetNode address;
	
	public ResourceHolder() {	
	}
	
	public ResourceHolder(String ip, int port) {
		super();
		this.address = new DefaultNetNode(ip, port);
		this.rmiServer = new RMIServer(port);
	}
	
	public static void scanRMIMapping(String filePath) {
		RMIFactory.scanRMIMapping(filePath);
		System.out.println(RMIFactory.getDefinition("com.mec.resource_discovery.center.IResourceHolder"));
	}
	
        //下面两个方法内部都是调用的远程方法
	public void registry(ResourceInfo res) {
		irc.registry(res, (DefaultNetNode) address);
	}
	
	public void logout(ResourceInfo res) {
		irc.logout(res, (DefaultNetNode) address);
	}
	
	public void startup() {
		rmiServer.startup();
	}
	
	public void shutdown() {
		rmiServer.close();
	}
	
}
public class ResourceHolderImpl implements IResourceHolder {
	public ResourceHolderImpl() {
	}

        //调用接口默认方法,回返回true,当然这里仅仅是示例,真实如何编写交给App层
	@Override
	public boolean isActive() throws Exception {
		System.out.println("向注册中心发送心跳信息!");
		return IResourceHolder.super.isActive();
	}
}

资源请求者:

  ResourceRequester类:

public class ResourceRequester extends Resourcer {
	public ResourceRequester() {
		super();
	}
	
        //调用的远程方法
	public List<DefaultNetNode> getAddressList(ResourceInfo res) {
		return irc.getAddressList(res);
	}
}

以上就是我编写的服务发现的所有代码,其也是一个框架。

测试部分:

  注册中心测试:

public class TestForRegietryCenter {
	public static void main(String[] args) {
		ResourceRegistryCenter.scanRMIMapping("/RegistryCenterMapping.xml");
		ResourceRegistryCenter rrc = new ResourceRegistryCenter(54188);
		rrc.startup();
	}
}

配置文件:

<?xml version="1.0" encoding="UTF-8"?>
<RMIMapping>
	<mapping interface="com.mec.resource_discovery.resourcer.IResourceCenter" 
	         class="com.mec.resource_discovery.center.ResourceCenterImpl"></mapping>
</RMIMapping>

资源拥有者1:

public class ResourceHolder1 {
	
	public static void main(String[] args) {
		ResourceHolder.scanRMIMapping("/ResourceHolderMapping.xml");
		ResourceHolder rh = new ResourceHolder("127.0.0.1", 54200);
		rh.setRMIServerIp("127.0.0.1");
		rh.setRMIServerPort(54188);
		rh.startup();
		rh.registry(new ResourceInfo("app1", "01", "v0.0.1"));
	}
}

资源拥有者2:

public class ResourceHolder2 {
	
	public static void main(String[] args) {
		ResourceHolder.scanRMIMapping("/ResourceHolderMapping.xml");
		ResourceHolder rh = new ResourceHolder("127.0.0.1", 54201);
		rh.setRMIServerIp("127.0.0.1");
		rh.setRMIServerPort(54188);
		rh.startup();
		rh.registry(new ResourceInfo("app1", "01", "v0.0.1"));
	}
}

配置文件:

<RMIMapping>
	<mapping interface="com.mec.resource_discovery.center.IResourceHolder"
			 class="com.mec.resource_discovery.holder.ResourceHolderImpl"></mapping>
</RMIMapping>

资源请求者:

public class ResourceRequester1 {

	public static void main(String[] args) {
		ResourceRequester rr = new ResourceRequester();
		rr.setRMIServerIp("127.0.0.1");
		rr.setRMIServerPort(54188);
		List<DefaultNetNode> l1 = rr.getAddressList(new ResourceInfo("app1", "01", "v0.0.1"));
		System.out.println(l1.toString());
		
		try {
			Thread.sleep(10000);

			List<DefaultNetNode> l2 = rr.getAddressList(new ResourceInfo("app1", "01", "v0.0.1"));
			System.out.println(l2.toString());
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
	}
}

场景:我们先注册两个资源,然后启动资源请求者请求资源,然后强行停止资源拥有者2,(资源请求者过10s后再请求一次资源,代码中有体现)。最后再停止资源拥有者1.

注册中心测试结果:

资源拥有者1:

资源拥有者2:

资源请求者:

结果分析:

    可以看到,我们的框架可以做到动态的管理资源请求者并对其进行心跳检测。检测的结果也正如我们所料。资源请求者两次请求到的信息也是准确地,及时更新的。

服务发现是一个非常强大的分布式框架,阿里巴巴的dubbo框架就是拥有服务发现功能的框架,不过要强大得多。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值