(二)模拟实现 《资源发现》框架

news2024/10/7 7:27:16

文章目录

  • 前言
  • 资源发现
    • 《资源发现》概述
      • 技术难点
    • 《资源发现》基本思想
    • 《资源发现》框架思考
      • 需求分析
      • 技术选择
  • 《资源发现》技术难点实现
  • 《资源发现》框架实现
    • 资源发现基础类实现
    • 资源注册中心的实现
    • 资源持有者和资源请求者
    • 资源持有者和资源请求者功能具体实现

前言

《资源发现》框架的实现是本人的一个编程训练项目,为了提升本人的编程能力、JAVA 编程思想,基于框架的角度出发,完成资源请求端可以从资源注册中心得到资源拥有者的地址列表信息,所以从客户端角度看,这是一种“服务发现”的机制,基于此机制之下,完成《资源发现》框架的编程。

此《资源发现》框架**“不重复造方轮子”**,只是自我的编程训练的项目。

资源发现

《资源发现》概述

资源发现是对《服务发现》的一种模拟,也是下一个项目《多文件云传输系统》的框架基础,资源发现是指使用一个资源注册中心来记录分布式系统中的全部资源的信息,以便其他请求资源的客户端能够快速的找到已注册的资源,基于多服务器、多 APP 资源处理的资源共享框架。

在如今的网络社会中,资源的传递需求极大,由此会产生不同的资源传输功能的 APP 和技术,而庞大的数据传输需要多个服务器进行,但是多个服务器需要解决服务器地址分发、负载均衡等问题,在这种情况下,为了解决上述多服务器系统的要求,并尽可能降低系统实现代价,《服务发现》的思路诞生。其拥有很好的容错性能,有服务器热插拔的优点,可以支持跨应用服务!

《资源发现》是基于《服务发现》的思路,实现对其的一种模拟,其独立于某个 APP 而产生的框架,其强大之处在于适用于不同的 APP,不被约束。他处理的是不同 APP 中以资源编号产生的资源,而不关心具体的资源形式和内容是什么。

资源发现由:

  • 1、资源持有者;
  • 2、资源请求者;
  • 3、资源注册中心。

三大部分组成。

技术难点

  • 基于个人开发的《MecRMI》框架(详见HB个人博客多文件云传输框架之模拟实现RMI)

  • 两池一表的线程安全问题(多线程安全问题)

  • 节点选择策略实现

  • 单例模式和适配器模式应用

  • 僵尸地址的处理
    僵尸地址:资源拥有者下线或异常掉线时对它的资源节点地址

《资源发现》基本思想

《资源发现》基于《服务发现》,它是使用一个资源注册中心来记录分布式系统中的全部资源的信息,资源请求者发出资源请求,资源注册中心提供拥有该资源的资源拥有者节点列表,资源请求者可以在资源注册中心注册和注销资源,三者各司其职实现资源发现。

《资源发现》框架思考

需求分析

服务发现

《服务发现》的架构中有主要的三者组成:注册中心、服务提供者、服务消费者,如下:

注册中心是《服务发现》的核心,注册中心的基本功能为:

1、服务注册,其核心任务是:提交服务提供者的地址(服务器地址);

2、服务注销;

3、提供(服务)地址,即其本质是,向客户机提供服务器地址。

资源发现

基于《服务发现》,我们的资源发现也就有的雏形,也就有了核心的三大角色:资源注册中心,资源请求者,资源拥有者

根据需求分析,可初步明确他们的功能如下:

  • 资源注册中心:

它是《资源发现》的核心,它要为资源请求者和资源拥有者提供服务,主要的功能是资源注册、资源注销、提供资源节点地址列表、并且对僵尸地址(资源拥有者下线或异常掉线时对它的资源节点地址)进行处理。

  • 资源拥有者:

资源拥有者可以拥有多个资源,且,这些资源可能分属不同的APP。它的主要功能就是注册资源和注销资源。

  • 资源请求者:

资源请求者向资源注册中心发出获取所需要的资源的资源节点地址列表,它的主要功能就是请求资源地址、僵尸地址汇报(汇报给资源注册中心)。

如下:

所以我们《资源发现》所希望实现的功能也就如下:

当一个资源请求者客户端请求资源时,会从资源注册中心得到拥有该资源的所有网络节点地址列表,该资源请求者会根据我们所提供的健康值属性,选出当前压力最小的K个发送者,对他们请求这个资源的不同部分,并将有下线或异常掉线的资源节点地址汇报给资源注册中心,资源注册中心将该资源地址注销,最终资源请求者得到他所请求的资源。

技术选择

  • 使用长链接(一对多的 C/S 模式),系统实时性更强,但,容量更差。
  • 使用短连接,有两种实现方案:
    1、NIO模式;(严格地说,NIO是逻辑长连接)
    优点:注册中心能发现资源拥有者的异常掉线。
    缺点:相对复杂,稳定性较差。
    2、RMI模式。
    优点:简单、效率高。
    缺点:注册中心对资源拥有者的异常掉线,几乎没有可能发现!
    若实现互为RMI服务器、客户机,也可以解决上述问题。

这里我们最终选择 RMI 实现模式,具体实现过程及分析见 HB个人博客:多文件云传输框架基础之模拟实现RMI

《资源发现》技术难点实现

  • 两池一表的线程安全问题(多线程安全问题)
    SoucePool 中使用了 ConcurrentHashMap 来保证线程安全。
	private static Map<Source, List<Node>> addressPool = new ConcurrentHashMap<Source, List<Node>>();

所有涉及到对 HolderList、HolderPool、SourcePool 中的内容进行操作(add、remove)时,都使用 synchronized 对要操作的对象加锁,来保证线程安全。

部分线程安全操作示例代码如下:

public class SourcePool {
	private static Map<Source, List<Node>> addressPool = new ConcurrentHashMap<Source, List<Node>>();
	
	synchronized static void addSource(Node address, Source source) {
		List<Node> addressList = addressPool.get(source);
		
		if (addressList == null) {
			addressList = new ArrayList<>();
			addressPool.put(source, addressList);
		}
		
		if (!addressList.contains(address)) {
			addressList.add(address);
		}
	}
	
	synchronized static void addSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			addSource(address, source);
		}
	}
	
	synchronized static void removeSource(Node address, Source source) {
		List<Node> addressList = addressPool.get(source);
		if (addressList == null) {
			return;
		}
		if (!addressList.contains(address)) {
			return;
		}
		addressList.remove(address);
	}
	
	synchronized static void removeSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			removeSource(address, source);
		}
	}
	
	synchronized static void removeSource(Node address) {
		List<Source> sourceList = HolderPool.getSourceList(address);
		removeSource(address, sourceList);
	}
	
	synchronized static List<Node> getAddress(Source source) {
		return addressPool.get(source);
	}
	
	synchronized static String getHolderInfo() {
		StringBuffer res = new StringBuffer();
		
		List<Source> sourceList = getSourceList();
		int sourceCount = sourceList.size();
		
		res.append("当前").append(sourceCount).append("个资源:");
		for (int index = 0; index < sourceCount; index++) {
			res.append("\n").append(index + 1).append("、");
			Source source = sourceList.get(index);
			res.append(source).append(":");
			
			List<Node> holderList = addressPool.get(source);
			for (Node holder : holderList) {
				res.append("\n\t").append(holder);
			}
		}
		res.append("\n");
		
		return res.toString();
	}
	
	private static List<Source> getSourceList() {
		List<Source> sourceList = new ArrayList<>();
		
		for (Source source : addressPool.keySet()) {
			sourceList.add(source);
		}
		
		return sourceList;
	}
}

Map<Source, List> addressPool 在代码中是static的,这个类主要是对其进行处理,因为这个类是多线程的,为了保证数据的可靠性和安全性,需要对方法加锁(synchronized),保证同一时刻只有一个线程可以访问其中的某一个方法。

  • 深克隆

在上述部分线程安全操作示例代码中,最后一个方法,获取List时,用深克隆的方法保护原对象:创造一个新的对象,为其分配内存空间,不会返回原有的List的地址,也就不会对原有的List更改。

  • 节点选择策略实现

节点选择策略接口类,方便未来 app 编写属于自己的节点选择策略。

public interface ISelectNodeStrategy {
	int SELECT_ALL = -1;
	
	List<Node> selectedSourceHolderList(List<Node> addressList, int maxCount);
}

默认节点选择策略接口对应的实现类:

public class SelectNodeStrategyAdapter implements ISelectNodeStrategy {

	public SelectNodeStrategyAdapter() {
	}
	
	@Override
	public List<Node> selectedSourceHolderList(List<Node> addressList, int maxCount) {
		int holderCount = addressList.size();
		if (holderCount <= maxCount) {
			return addressList;
		}
		
		List<Node> selectedAddressList = new ArrayList<Node>();
		
		List<List<Node>> healthArray = new ArrayList<>(Node.MAX_HEALTH_VALUE + 1);
		
		for (int index = 0; index < addressList.size(); index++) {
			Node address = addressList.get(index);
			int health = address.getHealth();
			
			List<Node> indexList = healthArray.get(health);
			if (indexList == null) {
				indexList = new ArrayList<>();
				healthArray.set(health, indexList);
			}
			
			indexList.add(address);
		}
		
		int count = 0;
		int index = 0;
		while (count < maxCount) {
			List<Node> addrList = healthArray.get(index);
			for (Node address : addrList) {
				selectedAddressList.add(address);
				if (++count >= maxCount) {
					break;
				}
			}
			index++;
		}
		
		return addressList;
	}
}
  • 僵尸地址的处理

资源请求者在接收到资源注册中心发来的资源节点类表中,如果发现已经掉线或发生异常的资源持有者,向资源资源注册中心汇报,资源注册中心将僵尸地址删除。

public interface ISourceRequesterFunction {
	void setISelectNodeStrategy(ISelectNodeStrategy selectNodeStrategy);
	
	List<Node> getAddressList(Source source);
	List<Node> getAddressList(Source source, int maxSourceHolderCount);
	
	void reportCorpse(Node address);
}

随后在对应接口的实现类 SourceHolderRequesterFunction 中完成接口中要实现的方法 reportCorpse(Node address)

@Override
	public void reportCorpse(Node address) {
		SourcePool.removeSource(address);
		HolderPool.removeHolder(address);
		HolderList.removeHolder(address);
	}

《资源发现》框架实现

资源发现基础类实现

  • 资源拥有者网络节点

因为网络节点这个东西,博主考虑到在以后只要涉及到网络节点编程的框架中,就需要用到,从面向对象的编程角度出发,所以博主将其工具化,产生 NodeAddress 网络节点这个工具类:

public class NodeAddress {
	private String ip;		//ip地址
	private int port;		//端口号

	public NodeAddress() {
	}

	public NodeAddress(String ip, int port) {
		this.ip = ip;
		this.port = port;
	}
	…………一系列getter、setter、toString、hashCode、equals方法
}

基于 NodeAddress 产生我们资源发现中的资源拥有者网络节点类 Node

package com.mec.source.core;

import com.mec.util.io.NodeAddress;

public class Node extends NodeAddress {
	public static final int MAX_HEALTH_VALUE = 1000;
	
	private int health;
	
	public Node() {
	}   
}
  • 资源的描述

资源由 id 和 appName 组成。

public class Source {
	private String appName;
	private String id;

	public Source() {
	}
	
	public Source(String str) {
		int index = str.indexOf("@");
		if (index == -1) {
			return;
		}
		this.appName = str.substring(0, index);
		this.id = str.substring(index + 1);
	}

	public Source(String appName, String id) {
		setAppName(appName);
		setId(id);
	}
    …………一系列getter、setter、toString、hashCode、equals方法
}

“ id、AppName ”中不能出现@符,所以在初始化的时候我们要进行一个简单的判断,并抛异常:

	if (appName.contains("@")) {
		throw new RuntimeException("AppName中不能出现@符!");
	}

资源注册中心的实现

资源注册中心这里使用 RMI 服务器,资源持有者、请求者(客户端)和资源注册中心(服务器端)是短连接模式,由客户端向服务器端发起“请求”,服务器连接客户端,并解析客户端传来的请求内容,并执行相关操作以得到“响应”信息,并将响应回送给该客户端,然后就立刻切断网络连接,实现了一个短而快的”请求/响应“过程。

因为资源注册中心只有一个,而资源请求者和资源拥有者会有很多,如果资源注册中心的功能过于繁琐可能会负载过大,所以本人打算将资源注册中心尽可能的“简单”化,因此资源注册中心不会关心任何的业务逻辑,只需要注册/销毁资源持有者,并且向资源请求者返回资源持有者的地址列表即可

  • 资源注册中心拥有的两池一表:

  • 资源持有者网络节点列表:

public class HolderList {
	private static List<Node> nodeList = new LinkedList<Node>();
	//获取资源持有者
	synchronized static Node getHolder(Node address) {
		int index = nodeList.indexOf(address);
		if (index == -1) {
			return null;
		}
		
		return nodeList.get(index);
	}
	// 添加资源持有者
	synchronized static void addHolder(Node address) {
		if (!nodeList.contains(address)) {
			nodeList.add(address);
		}
	}
	// 删除资源持有者
	synchronized static void removeHolder(Node address) {
		if (nodeList.contains(address)) {
			nodeList.remove(address);
		}
	}
	//设置节点健康值
	synchronized static void setHealth(Node address) {
		int index = nodeList.indexOf(address);
		if (index == -1) {
			return;
		}
		
		Node theNode = nodeList.get(index);
		theNode.setHealth(address.getHealth());
	}
	//获取资源持有者列表
	synchronized static List<Node> getHolderList() {
		List<Node> nodeList = new ArrayList<Node>();
		
		for (Node address : HolderList.nodeList) {
			nodeList.add(address);
		}
		
		return nodeList;
	}
}
  • 资源持有者和所持有资源列表的池子:

键为:资源拥有者节点;

值为:该节点所拥有的资源列表

public class HolderPool {
	private static final Map<Node, List<Source>> holderPool 
			= new ConcurrentHashMap<Node, List<Source>>();
	//给资源拥有者节点添加单个资源
	synchronized static void addSource(Node address, Source source) {
		List<Source> sourceList = holderPool.get(address);
		if (sourceList == null) {
			sourceList = new ArrayList<Source>();
			holderPool.put(address, sourceList);
		}
		
		if (!sourceList.contains(source)) {
			sourceList.add(source);
		}
	}
	//给资源拥有者节点添加多个资源
	synchronized static void addSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			addSource(address, source);
		}
	}
	//注销单个资源
	synchronized static void removeSource(Node address, Source source) {
		List<Source> sourceList = holderPool.get(address);

		if (sourceList.contains(source)) {
			sourceList.remove(source);
		}
	}
	//注销多个资源
	synchronized static void removeSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			removeSource(address, source);
		}
	}
	//注销某资源持有者节点(包括其所拥有的资源)
	synchronized static void removeHolder(Node address) {
		holderPool.remove(address);
	}
	//获取资源拥有者节点持有的资源列表
	synchronized static List<Source> getSourceList(Node address) {
		return holderPool.get(address);
	}
	//获取资源全部信息(资源持有者数量、所有资源持有者拥有的全部资源信息)
	synchronized static String getSourceInfo() {
		StringBuffer res = new StringBuffer();

		List<Node> holderList = HolderList.getHolderList();
		int holderCount = holderList.size();
		res.append("共有").append(holderCount).append("个资源持有者:\n");
		
		for (Node address : holderList) {
			res.append(address).append("\n");
			List<Source> sourceList = holderPool.get(address);
			for (Source source : sourceList) {
				res.append("\t").append(source).append("\n");
			}
		}
		
		return res.toString();
	}
	
}

  • 资源和拥有该资源的节点列表的池子:

键为:资源;

值为:拥有该资源的所有资源拥有者节点列表

public class SourcePool {
	private static Map<Source, List<Node>> addressPool = new ConcurrentHashMap<Source, List<Node>>();
	//兼顾性能和线程安全的,支持高并发更新与查询的哈希表
    
    //添加资源及拥有该资源的单个节点
	synchronized static void addSource(Node address, Source source) {
		List<Node> addressList = addressPool.get(source);
		
		if (addressList == null) {
			addressList = new ArrayList<>();
			addressPool.put(source, addressList);
		}
		
		if (!addressList.contains(address)) {
			addressList.add(address);
		}
	}
	//添加资源及拥有该资源的节点列表
	synchronized static void addSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			addSource(address, source);
		}
	}
	//删除资源及拥有该资源的单个节点
	synchronized static void removeSource(Node address, Source source) {
		List<Node> addressList = addressPool.get(source);
		if (addressList == null) {
			return;
		}
		if (!addressList.contains(address)) {
			return;
		}
		addressList.remove(address);
	}
	//删除某一个资源节点(从每一个资源对应的资源节点列表中删除)
	synchronized static void removeSource(Node address, List<Source> sourceList) {
		for (Source source : sourceList) {
			removeSource(address, source);
		}
	}
	//删除资源及拥有该资源的节点列表
	synchronized static void removeSource(Node address) {
		List<Source> sourceList = HolderPool.getSourceList(address);
		removeSource(address, sourceList);
	}
	//获取拥有某个资源的所有节点列表
	synchronized static List<Node> getAddress(Source source) {
		return addressPool.get(source);
	}
	//获取资源节点全部信息(资源数量、拥有该资源的所有资源节点信息)
	synchronized static String getHolderInfo() {
		StringBuffer res = new StringBuffer();
		
		List<Source> sourceList = getSourceList();
		int sourceCount = sourceList.size();
		
		res.append("当前").append(sourceCount).append("个资源:");
		for (int index = 0; index < sourceCount; index++) {
			res.append("\n").append(index + 1).append("、");
			Source source = sourceList.get(index);
			res.append(source).append(":");
			
			List<Node> holderList = addressPool.get(source);
			for (Node holder : holderList) {
				res.append("\n\t").append(holder);
			}
		}
		res.append("\n");
		
		return res.toString();
	}
	//获取资源列表
	private static List<Source> getSourceList() {
		List<Source> sourceList = new ArrayList<>();
		
		for (Source source : addressPool.keySet()) {
			sourceList.add(source);
		}
		
		return sourceList;
	}
}
  • 资源注册中心

这里采用观察者和侦听者模式,实现IListenerISpeaker两个接口,时刻侦听关注着资源请求者和持有者的请求和信息。

public class SourceRegistryCenter implements ISpeaker, IListener {
	public static final String DEFAULT_SOURCE_REGISTRY_CENTER_IP = "127.0.0.1";
	public static final int DEFAULT_SOURCE_REGISTRY_CENTER_PORT = 54186;
	private RmiServer server;
	
	private List<IListener> listenerList;

	public SourceRegistryCenter() throws Exception {
		RmiImplClassFactory.initRmi("/actionmapping.xml");
		
		this.server = new RmiServer();
		this.server.setPort(SourceRegistryCenter.DEFAULT_SOURCE_REGISTRY_CENTER_PORT);
		this.listenerList = new ArrayList<IListener>();
		this.server.addListener(this);
	}
	
	public boolean isStartup() {
		return this.server.isStartup();
	}
	
	public void startup() throws IOException {
		this.server.startup();
	}
	
	public void shutdown() {
		this.server.shutdown();
	}
	
	public String getHolderInfo() {
		return SourcePool.getHolderInfo();
	}
	
	public String getSourceInfo() {
		return HolderPool.getSourceInfo();
	}
	
	public void setPort(int port) {
		this.server.setPort(port);
	}

	@Override
	public void acceptMessage(String message) {
		publish(message);
	}

	@Override
	public void addListener(IListener listener) {
		if (!this.listenerList.contains(listener)) {
			this.listenerList.add(listener);
		}
	}

	@Override
	public void removeListener(IListener listener) {
		if (this.listenerList.contains(listener)) {
			this.listenerList.remove(listener);
		}
	}

	@Override
	public void publish(String message) {
		for (IListener listener : this.listenerList) {
			listener.acceptMessage(message);
		}
	}
}

资源持有者和资源请求者

  • 资源持有者
public class SourceHolder {
	private String selfIp;
	private RmiClient rmiClient;
	private ISourceHolderFunction sourceHolderFunction;
	private Node address;

	public SourceHolder() throws UnknownHostException {
		this.selfIp = MecIo.getIp();
		
		RmiProxy rmiProxy = new RmiProxy();
		this.rmiClient = rmiProxy.getClient();
		this.rmiClient.setServerAddress(SourceRegistryCenter.DEFAULT_SOURCE_REGISTRY_CENTER_IP, 
				SourceRegistryCenter.DEFAULT_SOURCE_REGISTRY_CENTER_PORT);
		this.sourceHolderFunction = rmiProxy.getProxy(ISourceHolderFunction.class);
	}
	
	public void registry(Source source) {
		this.sourceHolderFunction.registry(this.address, source);
	}
	
	public void registry(List<Source> sourceList) {
		this.sourceHolderFunction.registry(this.address, sourceList);
	}
	
	public void logout(Source source) {
		this.sourceHolderFunction.logout(this.address, source);
	}
	
	public void logout(List<Source> sourceList) {
		this.sourceHolderFunction.logout(this.address, sourceList);
	}
	
	public void logout() {
		this.sourceHolderFunction.logout(this.address);
	}
	
	public void setHealth(int health) {
		this.address.setHealth(health);
		this.sourceHolderFunction.setHealth(this.address);
	}

	public void setAddress(Node address) {
		this.address = address;
	}

	public String getSelfIp() {
		return this.selfIp;
	}
	
	public void setSourceRegistryCenterAddress(String ip, int port) {
		this.rmiClient.setServerAddress(ip, port);
	}
}
  • 资源请求者
public class SourceRequester {
	private RmiClient rmiClient;
	private ISourceRequesterFunction sourceRequesterFunction;

	public SourceRequester() {
		RmiProxy rmiProxy = new RmiProxy();
		this.rmiClient = rmiProxy.getClient();
		this.rmiClient.setServerAddress(SourceRegistryCenter.DEFAULT_SOURCE_REGISTRY_CENTER_IP, 
				SourceRegistryCenter.DEFAULT_SOURCE_REGISTRY_CENTER_PORT);
		this.sourceRequesterFunction = rmiProxy.getProxy(ISourceRequesterFunction.class);
	}

	public void setSelectNodeStrategy(ISelectNodeStrategy selectNodeStrategy) {
		this.sourceRequesterFunction.setISelectNodeStrategy(selectNodeStrategy);
	}
	
	public void setSourceRegistryCenterAddress(String ip, int port) {
		this.rmiClient.setServerAddress(ip, port);
	}

	public List<Node> getAddresses(Source source, int maxCount) {
		return this.sourceRequesterFunction.getAddressList(source, maxCount);
	}

	public List<Node> getAddresses(Source source) {
		return this.sourceRequesterFunction.getAddressList(source);
	}
	
	public void reportCorpse(Node address) {
		this.sourceRequesterFunction.reportCorpse(address);
	}	
}

资源持有者和资源请求者功能具体实现

资源持有者的注册和注销资源的一系列处理:

public interface ISourceHolderFunction {
	void registry(Node address, Source source);
	void registry(Node address, List<Source> sourceList);
	
	void logout(Node address, Source source);
	void logout(Node address, List<Source> sourceList);
	void logout(Node address);
	
	void setHealth(Node address);
}

资源请求者在请求资源等一系列处理:

public interface ISourceRequesterFunction {
	void setISelectNodeStrategy(ISelectNodeStrategy selectNodeStrategy);
	
	List<Node> getAddressList(Source source);
	List<Node> getAddressList(Source source, int maxSourceHolderCount);
	
	void reportCorpse(Node address);
}

资源持有者和资源请求者所要实现功能接口对应的实现类:

public class SourceHolderRequesterFunction implements ISourceHolderFunction, ISourceRequesterFunction {
	private ISelectNodeStrategy selectNodeStrategy;

	public SourceHolderRequesterFunction() {
		this.selectNodeStrategy = new SelectNodeStrategyAdapter();
	}
	
	@Override
	public List<Node> getAddressList(Source source) {
		List<Node> addressList =  SourcePool.getAddress(source);
		return addressList;
	}

	@Override
	public List<Node> getAddressList(Source source, int maxSourceHolderCount) {
		List<Node> addressList =  SourcePool.getAddress(source);
		int currentAddressCount = addressList.size();
		
		if (currentAddressCount <= maxSourceHolderCount) {
			return addressList;
		}
		
		List<Node> selectedAddressList = this.selectNodeStrategy.selectedSourceHolderList(
				addressList, maxSourceHolderCount);
		
		return selectedAddressList;
	}

	@Override
	public void reportCorpse(Node address) {
		SourcePool.removeSource(address);
		HolderPool.removeHolder(address);
		HolderList.removeHolder(address);
	}

	@Override
	public void registry(Node address, Source source) {
		Node holder = HolderList.getHolder(address);
		if (holder == null) {
			holder = address;
			HolderList.addHolder(holder);
		}
		SourcePool.addSource(holder, source);
		HolderPool.addSource(holder, source);
	}

	@Override
	public void registry(Node address, List<Source> sourceList) {
		Node holder = HolderList.getHolder(address);
		if (holder == null) {
			holder = address;
			HolderList.addHolder(address);
		}
		SourcePool.addSource(holder, sourceList);
		HolderPool.addSource(holder, sourceList);
	}

	@Override
	public void logout(Node address, Source source) {
		SourcePool.removeSource(address, source);
		HolderPool.removeSource(address, source);
	}

	@Override
	public void logout(Node address, List<Source> sourceList) {
		SourcePool.removeSource(address, sourceList);
		HolderPool.removeSource(address, sourceList);
	}

	@Override
	public void logout(Node address) {
		HolderList.removeHolder(address);
		SourcePool.removeSource(address);
		HolderPool.removeHolder(address);
	}

	@Override
	public void setHealth(Node address) {
		HolderList.setHealth(address);
	}
	
	@Override
	public void setISelectNodeStrategy(ISelectNodeStrategy selectNodeStrategy) {
		this.selectNodeStrategy = selectNodeStrategy;
	}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/609883.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA基础练习(4)

目录 1.利用循环打印九九乘法表 2.使用循环输出 100、95、90、85.......5 3.输入星期查看对应结果 4.几行数字展示 5.打印1-100之间13的倍数&#xff0c;使用for循环 6.用*来打印&#xff0c;根据用户输入rows和columns&#xff0c;来打印响应矩形 7.输入三个班&#xff…

YOLO8自定义检测实战

文章目录 资料模型介绍(或者叫weights)安装安装ultralytics&#xff08;yolo&#xff09;Torch测试命令 CLI命令行通过COCO128数据集体验yolov8标签predictsegment下载COCO 2017数据集ValTrain 自定义数据集标注标注软件labelimg分析训练结果 获得最佳训练结果提示 资料 Docs:…

docker学习记录

1.什么是docker&#xff1f; Docker是一个容器引擎&#xff0c;使用 Linux 内核功能&#xff08;如命名空间和控制组&#xff09;在操作系统之上创建容器。除了作为一种容器技术之外&#xff0c;Docker 还具有定义明确的包装器组件&#xff0c;这使打包应用程序变得十分容易&am…

Windows认证机制

windows认证基础 windows的认证包括三个部分&#xff1a; 本地认证&#xff1a;用户直接操作计算机登录账户网络认证&#xff1a;远程连接到工作组中的某个设备域认证&#xff1a;登录到域环境中的某个设备 本地认证 1、用户输入密码 2、系统收到密码后将用户输入的密码计…

LeetCode 周赛 348(2023/06/05)数位 DP 模板学会了吗

本文已收录到 AndroidFamily&#xff0c;技术和职场问题&#xff0c;请关注公众号 [彭旭锐] 加入知识星球提问&#xff01; 往期回顾&#xff1a;LeetCode 单周赛第 347 场 二维空间上的 LIS 最长递增子序列问题 周赛 348 概览 T1. 最小化字符串长度&#xff08;Medium&…

chatgpt赋能python:Python基础教程:如何利用Python进行地区查询

Python基础教程&#xff1a;如何利用Python进行地区查询 在现代社会&#xff0c;人们越来越关注自己所处的地理位置和周边环境。这就导致了地区查询变得越来越流行&#xff0c;因为它可以让人们更加方便地获取自己想要的信息。 Python作为一门强大的编程语言&#xff0c;不仅…

chatgpt赋能python:Python在线模拟:提高编程技能的必备工具

Python在线模拟&#xff1a;提高编程技能的必备工具 Python是一种广泛应用于各行业的编程语言&#xff0c;也是许多工程师的选择。成为一名Python工程师意味着拥有高薪、稳定的职业和无尽的机会。但是如何成为一名高效的Python工程师&#xff1f;在线模拟器做到了提高技能和编…

Servlet与Mabatis-1

Web 应用开发 get 和 post 请求方法 &#xff08;重点&#xff09; http 协议中定义的请求方法有 DELETE、HEAD、GET、OPTIONS、POST、PUT、TRACE 在 http 协议中的两种常见的传参方法 get/post&#xff0c;例如 get 和 post 的共同点&#xff1a;Get 提交和 post 提交都是…

SpringBoot 3.x 新特性

SpringBoot 3.x 新特性 引用文章地址 SpringNative GraalVM 打包 SpringBoot 为 Linux 的 单文件应用程序 目录 JDK版本spring.factories废弃GraalVM — Spring Native三方包升级jakarta代替javax改进ConstructorBinding检测Log4j2增强杂七杂八 JDK版本 Springboot 3.x 基…

chatgpt赋能python:Python图片处理:让图像处理更简单

Python 图片处理&#xff1a;让图像处理更简单 作为一门强大的编程语言&#xff0c;Python 可以处理多种任务&#xff0c;其中之一是图形处理。Python 程序员可以使用各种库和工具&#xff0c;在不同的平台上进行图片处理、编辑和转换。在本文中&#xff0c;我们将讨论 Python…

chatgpt赋能python:Python图像分块的简介

Python 图像分块的简介 Python 是一种高级编程语言&#xff0c;越来越多地应用于图像处理领域。图像分块是一种常见的图像处理技术&#xff0c;它是将图像分成大小相等的小块&#xff0c;从而方便进行后续的处理或者分析。 在这篇文章中&#xff0c;我们将着重介绍 Python 图…

MySQL UNION使用介绍及示例

MySQL UNION使用介绍及示例 1 用法介绍2 使用示例2.1 数据准备2.2 查询示例 1 用法介绍 说明作用UNION运算符用于组合两个或更多SELECT语句的结果集使用前提UNION中的每个SELECT语句必须具有相同的列数 1. 这些列的数据类型必须兼容&#xff1a;类型不必完全相同&#xff0c;…

(八)Spring之IOC控制反转、DI依赖注入介绍和使用(详解)

文章目录 前言SpringSpring IOC 简介BeanIOC 概述IOC 本质理解 Spring IOC 应用IOC xml装配IOC 依赖注入IOC Bean的作用域 IoC 自动装配Bean 的自动装配注解实现自动装配 IoC 使用注解开发模拟实现Spring IoC 前言 “Spring”在不同的上下文中表示不同的事物。它可以用来引用 …

c++继承相关内容(一)

目录 一.相关概念 二.派生类的相关注意事项 多层继承关系 成员对象和多层继承的区别 四.赋值兼容规则 五.继承关系中的构造函数和析构函数 一.相关概念 基类&#xff08;父类&#xff09;&#xff1a;被继承的类 派生类&#xff08;子类&#xff09;&#xff1a;新产生的…

JAVA基础学习(六)

第六章 二维数组 目录 第六章 二维数组 1.二维数组 1.1.冒泡排序 1.2.Arrays数组 1.3.多维数组 总结 内容仅供学习交流&#xff0c;如有问题请留言或私信&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 1.二维数组 1.1.冒泡排序 原理&#xff1a;比较…

日撸java_day37

第 37 天: 十字链表 package datastructures.graph;/*** ClassName: OrthogonalList* Package: datastructures.graph* Description: Orthogonal List for directed graph.** Author: luv_x_c* Create: 2023/5/28 14:53*/ public class OrthogonalList {/*** An inner class f…

chatgpt赋能python:Python图像分割——了解图像分割技术和Python实现

Python图像分割——了解图像分割技术和Python实现 图像分割是一种图像处理技术&#xff0c;它将一幅图像分成若干个区域&#xff0c;每个区域有自己的特征和属性。在图像处理中&#xff0c;图像分割常被用来提高图像质量和辨识度&#xff0c;降低图像处理和分析的难度&#xf…

2023年第十五届四川赛区ACM真题及官方题解

给大家看真题前&#xff0c;先给大家看看现场氛围 入场前&#xff1a; 结束后&#xff1a; 还是有点壮观的。 今年四川的ACM在都江堰举办。因为比赛时间很紧张&#xff0c;所以没来得及去公费旅个游哈哈&#xff0c; 不过题目很棒&#xff0c;志愿者效率很高&#xff0c;比赛…

从零手写操作系统之RVOS硬件定时器-05

从零手写操作系统之RVOS硬件定时器-05 RISC-V 定时器中断RISC-V CLINT 介绍寄存器 (Timer 部分)总体框架流程硬件定时器的应用时间管理测试 本系列参考: 学习开发一个RISC-V上的操作系统 - 汪辰 - 2021春 整理而来&#xff0c;主要作为xv6操作系统学习的一个前置基础。 RVOS是…