上一篇博文中对服务发现作了简单的讲解。本篇会详细讲述代码的实现。
资源的基本信息的类(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框架就是拥有服务发现功能的框架,不过要强大得多。