详解 服务发现 的基本实现
发展历程
对于网络通信,总共有如下三个历程:
单服务器 ———— 垂直应用架构:
和本人《C/SFramework》专栏中所使用的方式一样:
只存在一个服务器,这就需要服务器与客户端之间保持“长连接”,
那么,就客户端需要通过配置文件来获取服务器的ip和port
如下图所示:
而这样的形势是完全不合理的!
因为若是客流量较大,会非常容易导致服务器崩溃!
而若是我们限制了客流量,则会导致用户体验较差!
因此,基于上述不满,网络通信,发展为如下形式:
多服务器 ———— 分布式服务架构:
开设多个服务器,共同来处理客户端的请求
这种形式下,客户端还是需要一个配置文件,来存储每一个服务器的ip和port
如下图所示:
正如本人在上图中所讲那样:
这时若是新增一个服务器,
那么,所有客户端都需要修改配置文件,这样做是十分困难的!
而且,由于配置文件 是 写死的,因此可能会存在大量客户端同时访问其中一个服务器,而存在其它服务器 空闲的情况!
那么,为了解决上述问题,网络通信衍变为如下形式:
注册中心 + 多服务器 ———— 流动计算架构:
再次添加一个用于负载均衡(依照当前每一个APP服务器的“健康状态”来分配ip和port)的服务器 —— 分流服务器
如下图所示:
至此,网络通信不再需要在客户端存储访问APP服务器的配置文件,只需要存储分流服务器的相关信息
这样,我们既能简化客户端的操作,也能够实现负载均衡!
而最后一种网络通信形式,就被称之为“服务发现”
服务发现
何为“服务发现”?
所有服务器(无论是某一种APP的多个服务器,还是不同APP的多个服务器)在启动时,
都需要在“注册中心”进行注册;
客户端需要从“注册中心”获取它的请求所属APP的服务器(组) 的 地址信息;
从客户端角度看,注册中心起到以下两个作用:
1、是否存在某个APP的服务器;
2、获取某个APP服务器(组)的地址信息
所以,从客户端角度,这是一种“服务发现”的机制
服务发现的 妙用
1、服务器热插拔;
(即:在系统运行状态下,单个服务器发生异常情况,并不会影响全局)
2、服务器在线升级;
(基于 服务器热插拔 的性质)
3、容错机制;
(基于 服务器热插拔 的性质)
4、负载均衡
那么,在本篇博文中,本人将来基本实现下 服务发现:
基本思路
在上文的讲解中,我们也能发现:
服务发现的核心 就是 注册中心
那么,我们来思考下注册中心要完成的功能:
注册中心需求分析:
- 注册中心本身是一个服务器;
- 注册中心对服务提供者提供的一些功能;
- 注册中心对服务消费者提供的另一些功能
那么,我们能够从上面的讲解中,了解到:
注册中心的真正核心在于能够对APP服务器池进行轮询和负载均衡
那么,依照上述的思想,现在我们就先基本实现下 负载均衡 及 轮询:
轮询 与 负载均衡 的基本实现
首先是 一个用于获取 保存在 注册中心 的 APP服务器的 网络节点信息 的接口规范:
网络节点 —— INetNode接口:
package edu.youzg.balance.core;
/**
* 用于取出 “服务器池” 中的 服务器的ip和port
*/
public interface INetNode {
String getIp();
int getPort();
}
接下来是 基本实现了INetNode接口的 实现类:
网络节点实现类 —— DefaultNetNode类:
(注:该类仅是基本实现,并没有任何逻辑,是以便后续的操作而产生的)
package edu.youzg.balance.core;
/**
* 默认的 网络节点,存储每一个服务器的ip和port
*/
public class DefaultNetNode implements INetNode {
private String ip;
private int port;
public DefaultNetNode() {
}
public DefaultNetNode(String ip, int port) {
this.ip = ip;
this.port = port;
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
@Override
public String getIp() {
return ip;
}
@Override
public int getPort() {
return port;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((ip == null) ? 0 : ip.hashCode());
result = prime * result + port;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
DefaultNetNode other = (DefaultNetNode) obj;
if (ip == null) {
if (other.ip != null) {
return false;
}
} else if (!ip.equals(other.ip)) {
return false;
}
if (port != other.port) {
return false;
}
return true;
}
@Override
public String toString() {
return "[" + ip + ":" + port + "]";
}
}
可以看到:该类仅是一个用于保存 “注册过的”服务器的信息的类
接下来是 完善DefaultNetNode类,并附加“轮询功能”的网络节点类 —— PollingNetNode类:
用于 轮询 的 网络节点 —— PollingNetNode类:
package edu.youzg.balance.core;
/**
* 能够形成 顺序链(“双向链表”,以便我们取出 前驱和后继节点) 的 “网络节点”
*/
public class PollingNetNode extends DefaultNetNode {
private PollingNetNode next;
private PollingNetNode pre;
public PollingNetNode() {
this.next = this;
this.pre = this;
}
public PollingNetNode(String ip, int port) {
super(ip, port);
this.next = this;
this.pre = this;
}
public void setNext(PollingNetNode next) {
this.next = next;
}
public void setPre(PollingNetNode pre) {
this.pre = pre;
}
public PollingNetNode getNext() {
return this.next;
}
public PollingNetNode getPre() {
return this.pre;
}
}
接下来是 一个 规范了操作网络节点的接口:
负载均衡器 —— INetNodeBalance类:
package edu.youzg.balance.core;
/**
* 规范 “增、删、查” 网络节点 的接口(负载均衡器)
*/
public interface INetNodeBalance {
void addNode(INetNode node);
INetNode removeNode(INetNode node);
INetNode getNode();
}
然后是 基本实现该接口,并给出一个保存每一个注册过的服务器的抽象类:
未实现 获取节点方式 的 负载均衡器 —— AbstractNetNodeBalance抽象类:
package edu.youzg.balance.core;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* 提供 “网络节点池子”,以及这个池子的相关操作方法
*/
public abstract class AbstractNetNodeBalance implements INetNodeBalance {
protected final Map<Integer, INetNode> nodePool;
/**
* 初始化 “网络节点池子”
*/
public AbstractNetNodeBalance() {
this.nodePool = new ConcurrentHashMap<>();
}
@Override
public void addNode(INetNode node) {
if (node == null) {
return;
}
int nodeHashCode = node.hashCode();
if (nodePool.containsKey(nodeHashCode)) {
return;
}
nodePool.put(nodeHashCode, node);
}
public boolean isNodePoolEmpty() {
return nodePool.isEmpty();
}
@Override
public INetNode removeNode(INetNode node) {
if (node == null) {
return null;
}
return nodePool.remove(node.hashCode());
}
}
那么,现在我们就来完成下实现“轮询功能”的双向链表类:
轮询实现 —— PollingBalance类:
package edu.youzg.balance.core;
/**
* 实现 能够 “顺序轮询” 的 网络节点的 “双向链表”
*/
public class PollingBalance extends AbstractNetNodeBalance {
private PollingNetNode pollingNode;
private PollingNetNode currentNode;
/**
* 初始化 AbstractNetNodeBalance的map
*/
public PollingBalance() {
super();
}
/**
* “链表”操作 —— 将所传网络节点 加入到 双向链表 的表头之前(即:最后一个节点后)<br/>
* 并更新 AbstractNetNodeBalance 中的map
* @param node 要进行加入的 网络节点
*/
@Override
public synchronized void addNode(INetNode node) {
PollingNetNode newNode = new PollingNetNode(node.getIp(), node.getPort());
if (pollingNode == null) {
pollingNode = newNode;
currentNode = pollingNode;
super.addNode(newNode);
return;
}
newNode.setPre(pollingNode.getPre());
newNode.setNext(pollingNode);
pollingNode.setPre(newNode);
newNode.getPre().setNext(newNode);
super.addNode(newNode);
}
/**
* “链表”操作 —— 将所传网络节点 从 双向链表 中删除掉<br/>
* 并更新 AbstractNetNodeBalance 中的map
* @param node 要删除的 网络节点
* @return 成功删除 的 节点信息
*/
@Override
public synchronized INetNode removeNode(INetNode node) {
PollingNetNode target = new PollingNetNode(node.getIp(), node.getPort());
target = (PollingNetNode) super.removeNode(target);
if (target == null) {
return null;
}
if (isNodePoolEmpty()) {
pollingNode = null;
currentNode = null;
return pollingNode;
}
if (currentNode == target) {
currentNode = target.getNext();
}
if (pollingNode == target) {
pollingNode = target.getNext();
}
target.getPre().setNext(target.getNext());
target.getNext().setPre(target.getPre());
target.setPre(target);
target.setNext(target);
return target;
}
/**
* 按照“加入时间顺序” 轮询 双向链表
* @return
*/
@Override
public synchronized INetNode getNode() {
INetNode result = currentNode;
if (currentNode != null) {
currentNode = currentNode.getNext();
}
return result;
}
}
最后,由于本人说过:注册中心的一个核心功能 就是 负载均衡
而实现负载均衡,我们需要查询每一个App服务器的“健康状态”
最主要的还是高并发情况的处理
这就需要一套精妙的算法
由于本篇博文的目的是介绍以及初步实现服务发现
因此在这里本人就使用极简的方式,来优化下客户端获取APP服务器信息的操作,来初步实现 负载均衡:
负载均衡实现 —— RandomBalance类:
package edu.youzg.balance.core;
import java.util.LinkedList;
import java.util.List;
/**
* 随机访问(初步模拟“负载均衡”)的 双向链表
*/
public class RandomBalance extends AbstractNetNodeBalance {
private final List<INetNode> nodeList;
/**
* 初始化 AbstractNetNodeBalance的map 和 nodeList
*/
public RandomBalance() {
super();
nodeList = new LinkedList<>();
}
/**
* 按照顺序加入 网络节点
* 并更新 AbstractNetNodeBalance的map
* @param node 要进行加入的网络节点
*/
@Override
public void addNode(INetNode node) {
super.addNode(node);
if (node == null || nodeList.contains(node)) {
return;
}
nodeList.add(node);
}
/**
* 删除指定的 网络节点
* 并更新 AbstractNetNodeBalance的map
* @param node
* @return
*/
@Override
public INetNode removeNode(INetNode node) {
if (node == null || !nodeList.contains(node)) {
return null;
}
nodeList.remove(node);
return super.removeNode(node);
}
/**
* 通过 随机数,来随机访问 nodeList中的网络节点
* @return 随机取出的一个 网络节点
*/
@Override
public INetNode getNode() {
if (isNodePoolEmpty()) {
return null;
}
int index = (int) (Math.random() * (nodeList.size() + 1));
return this.nodeList.get(index);
}
}
那么,既然轮询以及负载均衡都基本实现了,
现在本人就来铺垫下,以便我们之后实现注册中心:
注册中心 的 铺垫
首先,本人给出一个用于保存某项服务的信息:
单项服务节点 —— ServiceNode类:
package edu.youzg.about_service_discovery.core;
import edu.youzg.balance.core.INetNode;
/**
* 用于保存 某项服务所需的 ip、port 和 服务名
*/
public class ServiceNode {
private String service;
private INetNode node;
public ServiceNode() {
}
public ServiceNode(String service, INetNode node) {
this.service = service;
this.node = node;
}
public String getService() {
return service;
}
public void setService(String service) {
this.service = service;
}
public INetNode getNode() {
return node;
}
public void setNode(INetNode node) {
this.node = node;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((node == null) ? 0 : node.hashCode());
result = prime * result + ((service == null) ? 0 : service.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
ServiceNode other = (ServiceNode) obj;
if (node == null) {
if (other.node != null) {
return false;
}
} else if (!node.equals(other.node)) {
return false;
}
if (service == null) {
if (other.service != null) {
return false;
}
} else if (!service.equals(other.service)) {
return false;
}
return true;
}
@Override
public String toString() {
return service + " : " + node;
}
}
之后我们向“池子”中 增加/删除APP服务器,都是基于单项服务节点的
那么,基于上述的单项服务节点,本人再来提供给一个接口:
基于单项服务节点的 基本操作 规范 —— IServiceNodeAction接口:
package edu.youzg.about_service_discovery.core;
import java.util.List;
/**
* 操作 单项服务节点 的 action
*/
public interface IServiceNodeAction {
void registryService(String nodeId, ServiceNode service);
void registryService(String nodeId, List<ServiceNode> serviceList);
void logout(String nodeId);
}
相应地,本人来给出上述接口的实现类:
基于单项服务节点的 基本操作 实现 —— ServiceNodeAction类:
package edu.youzg.about_service_discovery.core;
import java.util.List;
/**
* 实现 操作单项服务节点 的基本action
*/
public class ServiceNodeAction implements IServiceNodeAction {
public ServiceNodeAction() {
}
@Override
public void registryService(String nodeId, ServiceNode service) {
ServicePool.addService(service.getService(), service.getNode());
NodeServicePool.registryService(nodeId, service);
}
@Override
public void registryService(String nodeId, List<ServiceNode> serviceList) {
if (serviceList == null || serviceList.isEmpty()) {
return;
}
for (ServiceNode service : serviceList) {
registryService(nodeId, service);
}
}
@Override
public void logout(String nodeId) {
List<ServiceNode> serviceList = NodeServicePool.logout(nodeId);
if (serviceList == null || serviceList.isEmpty()) {
return;
}
for (ServiceNode service : serviceList) {
ServicePool.removeService(service.getService(), service.getNode());
}
}
}
可能会出现这样的情况:
一个服务器,提供多种服务
那么,我们若是想要 增删等操作一个服务器 的时候,就会很麻烦
因此,本人在这里给出一个 容器类:
网络节点池 —— NodeServicePool类:
package edu.youzg.about_service_discovery.core;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class NodeServicePool {
private static final Map<String, List<ServiceNode>> nodeServicePool
= new HashMap<String, List<ServiceNode>>(); // 以注册中心分配给该服务器的 id为键, 该服务器所提供的 服务列表 为值
public NodeServicePool() {
}
/**
* 通过 目标服务器的 id,从map中移除该服务器的信息
* @param nodeId 目标服务器的 id
* @return 该服务器的 服务列表
*/
public static List<ServiceNode> logout(String nodeId) {
return nodeServicePool.remove(nodeId);
}
/**
*
* @param nodeId 目标服务器的 id
* @param serviceNode 该项服务的 ip、port 和 服务映射名
*/
public static void registryService(String nodeId, ServiceNode serviceNode) {
List<ServiceNode> serviceNodeList = nodeServicePool.get(nodeId);
if (serviceNodeList == null) {
serviceNodeList = new ArrayList<ServiceNode>();
nodeServicePool.put(nodeId, serviceNodeList);
}
if (!serviceNodeList.contains(serviceNode)) {
serviceNodeList.add(serviceNode);
}
}
}
由于我们接下来的操作,会对客户端发送的请求,进行解析,
然后将按照 处理该请求的服务,发送给客户端 提供这些服务的APP服务器的信息
因此,本人现在来给出一个 容器,来按照功能 分别存储 每一类服务器:
服务池 —— ServicePool类:
package edu.youzg.about_service_discovery.core;
import edu.youzg.balance.core.INetNode;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class ServicePool {
private static final Map<String, List<INetNode>> servicePool
= new HashMap<>(); // 以 APP服务器的功能名 为键,APP服务器的节点信息 为值,存储的一个map
public ServicePool() {
}
/**
* 增加一个 APP服务器
* @param serviceName 目标APP服务器的 功能名
* @param node 目标APP服务器的 节点信息
*/
public void addService(String serviceName, INetNode node) {
List<INetNode> serviceList = servicePool.get(serviceName);
if (serviceList == null) {
serviceList = new ArrayList<>();
servicePool.put(serviceName, serviceList);
}
if (!serviceList.contains(node)) {
serviceList.add(node);
}
}
/**
* 删除一个 APP服务器
* @param serviceName 目标APP服务器的 功能名
* @param node 目标APP服务器的 节点信息
*/
public void removeService(String serviceName, INetNode node) {
List<INetNode> serviceList = servicePool.get(serviceName);
if (serviceList == null || !serviceList.contains(node)) {
return;
}
serviceList.remove(node);
}
/**
* 从“服务器池”中获取一个 满足相应功能需求的 APP服务器的网络节点信息
* @param serviceName
* @return
*/
public List<INetNode> getService(String serviceName) {
return servicePool.get(serviceName);
}
}
那么,所有的铺垫工作就做好了,
现在本人来基本讲解,并基本实现下注册中心的逻辑功能:
首先,我们要明确的几个知识点是:
- 客户端 和 APP服务器 相对于 注册中心,都是客户端
- 由于我们需要持续获知当前APP服务器的状态,
因此,APP服务器 与 注册中心 之间,必须是长连接- 在现代已经成熟并被广泛使用的服务发现机制中,
客户端与注册中心之间的关系大致如下:
注册中心保存着客户端的Socket,将其放在一个容器中,
每隔一段时间,将当前的APP服务器的状态轮询广播给这个容器中的每一个客户端的Socket
那么,本人先来完成APP服务器的基本功能:
APP服务器
由于 APP服务器 作为服务的提供者,
需要完成的基本功能为:
- 注册其功能
- 下线(告知注册中心:本APP服务器停止提供任何服务)
那么,依照上述思想,本人先来给出一个 用于规范APP服务器的基本操作的接口:
IServiceProvider接口:
package edu.youzg.about_service_discovery.core;
import java.util.List;
public interface IServiceProvider {
void registryService(String nodeId, ServiceNode service);
void registryService(String nodeId, List<ServiceNode> serviceList);
void logout(String nodeId);
}
ServiceProvider类:
相应地,本人来给出这个接口的实现类:
package edu.youzg.about_service_discovery.core;
import java.util.List;
public class ServiceProvider implements IServiceProvider {
public ServiceProvider() {
}
/**
* 注册服务
* @param nodeId 提供该服务的APP服务器的id(由注册中心分配)
* @param service 提供的服务
*/
@Override
public void registryService(String nodeId, ServiceNode service) {
ServicePool.addService(service.getService(), service.getNode());
NodeServicePool.registryService(nodeId, service);
}
/**
* 注册服务列表
* @param nodeId 提供该服务的APP服务器的id(由注册中心分配)
* @param serviceList 提供的服务列表
*/
@Override
public void registryService(String nodeId, List<ServiceNode> serviceList) {
if (serviceList == null || serviceList.isEmpty()) {
return;
}
for (ServiceNode service : serviceList) {
registryService(nodeId, service);
}
}
/**
* 注销 APP服务器
* @param nodeId 要注销的APP服务器的id(由注册中心分配)
*/
@Override
public void logout(String nodeId) {
List<ServiceNode> serviceList = NodeServicePool.logout(nodeId);
if (serviceList == null || serviceList.isEmpty()) {
return;
}
for (ServiceNode service : serviceList) {
ServicePool.removeService(service.getService(), service.getNode());
}
}
}
那么,接下来,接下来就是客户端的实现:
但是呢,由于写到这里,篇幅已经够长了
而且本文的目的,不是完全实现 服务发现机制
而是带领同学们 了解服务发现,熟悉服务发现的基本实现流程
因此,在这里,本人仅初步实现下客户端与注册中心之间的基本任务:
客户端(若需完成请自行思考)
思路:
注册中心需要对客户端提供哪些服务呢?
- 客户端向注册中心进行“注册”(即“服务请求”)
- “刷新服务地址列表”功能,
可以通过配置完成“服务发现框架”(例如:Netty框架)的启动
也可以启动“后台”执行的“刷新服务地址列表”的功能- “负载均衡”功能,
可以通过配置实现“负载均衡(策略)”的激活
IServiceCustomer接口:
客户端与注册中心之间,最基本要实现的功能就是 获取指定服务的APP链:
package edu.youzg.about_service_discovery.core;
import edu.youzg.balance.core.INetNode;
import java.util.List;
public interface IServiceCustomer {
List<INetNode> getServiceAddressList(String service);
}
相应地,本人来给出实现类:
ServiceCustomer类:
package edu.youzg.about_service_discovery.core;
import edu.youzg.balance.core.INetNode;
import java.util.List;
public class ServiceCustomer implements IServiceCustomer {
public ServiceCustomer() {
}
@Override
public List<INetNode> getServiceAddressList(String service) {
return ServicePool.getService(service);
}
}
在这里本人再次声明:
此处的客户端实现的是短链接模式,与实际实现模式有很大区别
而且很多的逻辑没有实现
仅是为了后文的注册中心的基本代码而给出的 不完整代码!
注册中心
铺垫了这么久,
但是,本人还是要遗憾地告诉同学们:
在这里,注册中心也是没有完全实现的
仅是在模仿,且为了实现方便,模拟也不会跟现有的完全一样
由于客户端的逻辑太过于复杂,因此在本文中只实现APP服务器与注册中心之间的基本逻辑
“基本”实现注册中心 —— RegistryCenter类:
package edu.youzg.about_service_discovery.core;
import edu.youzg.netframework.core.INetListener;
import edu.youzg.netframework.core.IServerAction;
import edu.youzg.netframework.core.Server;
import edu.youzg.netframework.core.ServerConversation;
import edu.youzg.rmi.core.RMIFactory;
import edu.youzg.rmi.core.RMIServer;
import edu.youzg.util.IMecView;
import edu.youzg.util.PropertiesParser;
import edu.youzg.util.ViewTool;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;
public class RegistryCenter implements IMecView, INetListener {
private static final int VIEW_WIDTH = 600;
private static final int VIEW_HTIGHT = 400;
private static final int VIEW_MIN_WIDTH = 600;
private static final int VIEW_MIN_HTIGHT = 400;
private Server server;
private RMIServer serviceProvider;
private RMIServer serviceCustomer;
private static int serverPort;
private static int serviceProviderPort;
private static int serviceCustomerPort;
private JFrame jfrmView;
private JTextField jtxtCommand;
private JTextArea jtatMessage;
static {
readConfig();
}
public RegistryCenter() {
}
public static void readConfig(String configPath) {
PropertiesParser parser = new PropertiesParser();
parser.loadProperties(configPath);
serverPort = Integer.valueOf(parser.value("port"));
serviceProviderPort = Integer.valueOf(parser.value("provider_port"));
serviceCustomerPort = Integer.valueOf(parser.value("customer_port"));
RMIFactory.scanRMIMapping("src/RegistryCenterRMIMapping.xml");
}
public static void readConfig() {
readConfig("src/netcfg.properties");
}
@Override
public void reinit() {
jtatMessage.setFocusable(false);
jtatMessage.setEditable(false);
jtxtCommand.requestFocus();
initServer();
}
private void initServer() {
server = new Server();
server.setServerAction(new IServerAction() {
@Override
public void clientAbnormalDrop(ServerConversation client) {
new ServiceProvider().logout(client.getId());
}
});
server.setPort(serverPort);
server.addListener(this);
serviceProvider = new RMIServer();
serviceCustomer = new RMIServer();
serviceProvider.setPort(serviceProviderPort);
serviceCustomer.setPort(serviceCustomerPort);
}
private void closeView() {
if (server == null || server.isStartup()) {
ViewTool.showMessage(jfrmView, "服务器为null或服务器未宕机");
return;
}
jfrmView.dispose();
System.exit(0);
}
@Override
public void dealEvent() {
jfrmView.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
closeView();
}
});
jtxtCommand.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String command = jtxtCommand.getText().trim();
if (command.length() > 0) {
dealCommand(command);
}
jtxtCommand.setText("");
}
});
}
private void startupServer() {
server.startup();
serviceProvider.startup();
serviceCustomer.startup();
}
private void shutdownServer() {
server.shutdown();
if (server.isStartup()) {
return;
}
serviceProvider.shutdown();
serviceCustomer.shutdown();
}
private void dealCommand(String command) {
if (command.equalsIgnoreCase("startup")
|| command.equalsIgnoreCase("st")) {
startupServer();
} else if (command.equalsIgnoreCase("shutdown")
|| command.equalsIgnoreCase("sd")) {
shutdownServer();
} else if (command.equalsIgnoreCase("exit")
|| command.equalsIgnoreCase("x")) {
closeView();
} else if (command.equalsIgnoreCase("fd")
|| command.equalsIgnoreCase("forcedown")) {
// TODO 注册中心强行宕机,这里的操作需要进一步讨论
}
}
@Override
public JFrame getFrame() {
return jfrmView;
}
@Override
public synchronized void dealMessage(String message) {
jtatMessage.append(message);
jtatMessage.append("\n");
jtatMessage.setCaretPosition(jtatMessage.getText().length());
}
@Override
public void init() {
jfrmView = new JFrame("服务发现 - 注册中心 - 监视器");
jfrmView.setSize(VIEW_WIDTH, VIEW_HTIGHT);
jfrmView.setMinimumSize(new Dimension(VIEW_MIN_WIDTH, VIEW_MIN_HTIGHT));
jfrmView.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
jfrmView.setLocationRelativeTo(null);
jfrmView.setLayout(new BorderLayout());
JLabel jlblCenterTopic = new JLabel("服务发现 - 注册中心 - 监视器", JLabel.CENTER);
jlblCenterTopic.setFont(topicFont);
jlblCenterTopic.setForeground(topicColor);
jfrmView.add(jlblCenterTopic, BorderLayout.NORTH);
JPanel jpnlLeftBlank = new JPanel();
jfrmView.add(jpnlLeftBlank, BorderLayout.WEST);
JPanel jpnlRightBlank = new JPanel();
jfrmView.add(jpnlRightBlank, BorderLayout.EAST);
jtatMessage = new JTextArea();
jtatMessage.setFont(normalFont);
JScrollPane jscpMessage = new JScrollPane(jtatMessage);
jfrmView.add(jscpMessage, BorderLayout.CENTER);
TitledBorder ttbdMessage = new TitledBorder("系统消息");
ttbdMessage.setTitleFont(normalFont);
ttbdMessage.setTitleColor(Color.red);
ttbdMessage.setTitlePosition(TitledBorder.ABOVE_TOP);
ttbdMessage.setTitleJustification(TitledBorder.CENTER);
jscpMessage.setBorder(ttbdMessage);
JPanel jpnlFooter = new JPanel(new FlowLayout(FlowLayout.CENTER));
jfrmView.add(jpnlFooter, BorderLayout.SOUTH);
JLabel jlblCommand = new JLabel("命令");
jlblCommand.setFont(normalFont);
jpnlFooter.add(jlblCommand);
jtxtCommand = new JTextField(30);
jtxtCommand.setFont(normalFont);
jpnlFooter.add(jtxtCommand);
}
}
上面的代码,引用了很多本人之前的小工具,
若有疑问,请观看本人往期博文
RMI配置文件 —— RegistryCenterRMIMapping.xml:
<?xml version="1.0" encoding="UTF-8"?>
<RmiMapping>
<mapping interface="com.mec.service_discovery.core.IServiceProvider"
class="com.mec.service_discovery.core.ServiceProvider"></mapping>
</RmiMapping>
初始化配置文件 —— netcfg.properties:
port=54188
provider_port=54189
customer_port=54190
max_client_count=20
autoStartup=true
结果展示
那么,本人在这里仅展示下界面: