【Java】实现微服务框架之服务发现
概述
微服务
微服务是一种架构风格,一个大型复杂软件应用由一个或多个微服务组成。系统中的各个微服务可被独立部署,各个微服务之间是松耦合的。每个微服务仅关注于完成一件任务并很好地完成该任务。在所有情况下,每个任务代表着一个小的业务能力。
服务发现
1.什么是服务发现?
所谓的服务发现就是通过服务标签向注册中心获取能够提供该服务的服务器的网络地址(ip和端口)。
2.服务发现的作用
用户只用通过服务标签去可以找到对应的服务实例,至于如何找到一个合适的服务实例则由内部实现。而且,通过服务发现,还提高了容灾性。在传统的当某个服务器宕机,其所开启的所有服务都不可用,使得所有享受这台服务器提供服务的客户端都面临无法使用服务器所提供的服务,但通过服务发现,当发现问题时,可以找寻其它提供相同服务的服务器实例继续消费服务。还有一个特别的特点就是,注册中心如果宕机,并不影响已注册服务器和客户端的APP业务。
框架思想
1.注册中心主要负责资源(服务)的注册、注销、申请;在它里面维护了一张以服务标签为键(key),以服务器节点列表为值(value)的map。
2.服务提供端上线后向注册中心申请注册其所提供的服务,注册中心得到申请后将其加入表中并与该提供端建立长连接,不断检测其在线状况,当该服务提供端掉线(包含异常掉线)后,将其从map中剔除。
3.当服务消费端需要某个服务时,向注册中心申请该服务,当注册中心得到该申请后,通过该服务标签得到服务提供端的节点列表将其发送给该服务消费端。
通信层
Communication
package com.wh.sd.communication;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.ThreadPoolExecutor;
import com.wh.sd.netmessage.ENetCommand;
import com.wh.sd.netmessage.NetMessage;
import com.wh.sd.node.ServerNetNodePool;
/**
* 通信层类
* @author 闹闹小丫头
*
*/
public class Communication {
private Socket socket;
private DataInputStream dis;
private DataOutputStream dos;
private ServerCommunicationNode communicationNode;
private ThreadPoolExecutor threadPool;
private IServerCommunicationAction serverCommunicationAction;
private volatile int readTag = 1;
public Communication() {
}
public int getReadTag() {
return readTag;
}
public void setReadTag(int readTag) {
this.readTag = readTag;
}
public Socket getSocket() {
return socket;
}
public void setSocket(Socket socket) {
this.socket = socket;
}
public DataInputStream getDis() {
return dis;
}
public void setDis(DataInputStream dis) {
this.dis = dis;
}
public DataOutputStream getDos() {
return dos;
}
public void setDos(DataOutputStream dos) {
this.dos = dos;
}
public ServerCommunicationNode getCommunicationNode() {
return communicationNode;
}
public void setCommunicationNode(ServerCommunicationNode communicationNode) {
this.communicationNode = communicationNode;
}
public ThreadPoolExecutor getThreadPool() {
return threadPool;
}
public void setThreadPool(ThreadPoolExecutor threadPool) {
this.threadPool = threadPool;
}
public IServerCommunicationAction getServerCommunicationAction() {
return serverCommunicationAction;
}
public void setServerCommunicationAction(IServerCommunicationAction serverCommunicationAction) {
this.serverCommunicationAction = serverCommunicationAction;
}
/**
* 对端检测
*/
public void toEndingDetection() {
if(getReadTag() == 1) {
threadPool.execute(new ToEndTesting());
}
}
/**
* 检测接收,用于轮询操作
*/
public void checkReceive() {
try {
if(dis.available() > 0 && getReadTag() == 1) {
System.out.println(dis.available());
setReadTag(0);
System.out.println("读取消息");
threadPool.execute(new MessageProcessing());
}
} catch (IOException e) {
}
// try {
// socket.sendUrgentData(0xFF);
// } catch (IOException e1) {
// System.out.println("处理异常");
// serverNodePool.removeServerSocket(communicationNode);
// }
}
/**
* 通过通信信道向对端发送指定的消息
* @param message
*/
public void send(NetMessage message) {
// 发送消息
try {
dos.writeUTF(message.toString());
} catch (IOException e) {
ServerNetNodePool serverNetNodePool = new ServerNetNodePool();
serverNetNodePool.removeServerSocket(communicationNode);
}
}
/**
* 对端检测内部类,向对端发送检测消息
* @author 闹闹小丫头
*
*/
class ToEndTesting implements Runnable{
public ToEndTesting() {
}
@Override
public void run() {
NetMessage message = new NetMessage();
message.setNetCommand(ENetCommand.MONITOR_ONLINE);
message.setAction(null);
message.setPara(null);
send(message);
}
}
/**
* 消息处理内部类,处理这个通信层所接收到的消息
* @author 闹闹小丫头
*
*/
class MessageProcessing implements Runnable{
public MessageProcessing() {
}
@Override
public void run() {
try {
String message = dis.readUTF();
serverCommunicationAction.dealNetMessage(communicationNode, new NetMessage(message));
setReadTag(1);
} catch (IOException e) {
}
}
}
}
NetMessage
通信协议类
package com.wh.sd.netmessage;
/**
* 消息类,对网络发送过来的信息进行处理,生成固定的格式
* @author 闹闹小丫头丶
*
*/
public class NetMessage {
// 命令
private ENetCommand netCommand;
// 动作
private String action;
// 参数
private String para;
/**
* 无参构造
*/
public NetMessage() {
}
/**
* 单参构造,根据指定消息进行构造
* @param message 指定消息
*/
public NetMessage(String message) {
int index = message.indexOf(".");
if(index < 0) {
return;
}
String str = message.substring(0, index);
this.netCommand = ENetCommand.valueOf(str);
message = message.substring(index + 1);
index = message.indexOf(".");
if(index < 0) {
this.netCommand = null;
return;
}
str = message.substring(0, index);
this.action = str.equals(" ") ? null : str;
message = message.substring(index + 1);
this.para = message;
}
public ENetCommand getNetCommand() {
return netCommand;
}
public NetMessage setNetCommand(ENetCommand netCommand) {
this.netCommand = netCommand;
return this;
}
public String getAction() {
return action;
}
public NetMessage setAction(String action) {
this.action = action;
return this;
}
public String getPara() {
return para;
}
public NetMessage setPara(String para) {
this.para = para;
return this;
}
@Override
public String toString() {
StringBuffer result = new StringBuffer(netCommand.name());
result.append(".").append(this.action == null ? " " : this.para)
.append(".").append(this.para);
return result.toString();
}
}
NetNode
package com.wh.sd.node;
/**
* 节点基本信息
* @author 闹闹小丫头丶
*
*/
public class NetNode {
private String ip;
private int port;
public NetNode() {
}
public void setIp(String ip) {
this.ip = ip;
}
public void setPort(int port) {
this.port = port;
}
public String getIp() {
return this.ip;
}
public int getPort() {
return this.port;
}
@Override
public String toString() {
return "[ip=" + ip + ", port=" + port + "]";
}
}
注册中心
注册中心与服务提供端
当服务提供端上线后先连接到注册中心,注册中心通过该提供端生成对应的服务器连接节点类(ServerCommunicationNode ),将其放入节点轮询池内进行轮询操作,轮询过程中不断检测与消息服务提供端的通信信道是否有数据发送(dis.available()可检测),若有数据发送过来,则交给消息处理类(MessageProcessing)来进行处理,将其放入线程池中完成,以便不影响轮询池的轮询,在轮询的过程当中不断的进行心跳检测(HeartbeatDetection内部类),来对端检测是否掉线;若对端掉线则将对应的服务提供端从轮询池以及服务表中剔除。
这个方法看似完美也有很大的不足之处,若有大量的服务提供端则采用一个轮询池会有很大的时延性,对消息的处理不是很及时,要解决这个问题就需要将提供端放入多个轮询池内,对他们进行分组,这个轮询池的大小可以由用户自己配置。
注册中心与服务消费端
IServerCommunicationAction
package com.wh.sd.communication;
import com.wh.sd.netmessage.NetMessage;
/**
* ServerCommunicationAction需要实现的接口
* @author 闹闹小丫头
*
*/
public interface IServerCommunicationAction {
/**
* 处理指定ServerCommunicationNode的指定消息
* @param serverCommunicationNode 指定ServerCommunicationNode
* @param message 指定消息
*/
void dealNetMessage(ServerCommunicationNode serverCommunicationNode, NetMessage message);
}
ServerCommunicationNode
package com.wh.sd.communication;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.ThreadPoolExecutor;
import com.wh.sd.node.NetNode;
/**
* 服务器连接节点类
* 包含节点信息和通信层
* @author 闹闹小丫头
*
*/
public class ServerCommunicationNode {
private NetNode node;
private Communication communication;
public ServerCommunicationNode() {
}
public Communication getCommunication() {
return communication;
}
public void setCommunication(Communication communication) {
this.communication = communication;
}
public NetNode getNode() {
return node;
}
public void setNode(NetNode node) {
this.node = node;
}
/**
* 根据指定服务器,线程池和IServerCommunicationAction的实现类生成新的ServerCommunicationNode类
* @param socket 指定服务器
* @param threadPool 线程池
* @param serverCommunicationAction IServerCommunicationAction的实现类
* @return
*/
public ServerCommunicationNode generateServerCommunicationNode(Socket socket, ThreadPoolExecutor threadPool,
IServerCommunicationAction serverCommunicationAction) {
try {
this.communication.setSocket(socket);
this.communication.setCommunicationNode(this);
this.communication.setDis(new DataInputStream(socket.getInputStream()));
this.communication.setDos(new DataOutputStream(socket.getOutputStream()));
this.communication.setThreadPool(threadPool);
this.communication.setServerCommunicationAction(serverCommunicationAction);
this.node.setIp(socket.getInetAddress().getHostAddress());
this.node.setPort(socket.getPort());
} catch (IOException e) {
}
return this;
}
}
IClientRequest
package com.wh.sd.consumers;
import java.util.List;
import com.wh.mfct.resource.ResourceBaseInfo;
import com.wh.sd.node.NetNode;
/**
* 客户端请求接口,远程调用时需要使用
* @author 闹闹小丫头
*
*/
public interface IClientRequest {
/**
* 处理服务请求,根据指定的服务来处理
* @param service 指定服务
* @return 节点列表
*/
List<NetNode> dealServiceRequest(String service);
/**
* 处理资源请求
* @param rbi 根据指定资源处理
* @return 节点列表
*/
List<NetNode> dealResourceRequest(String rbi);
}
ClientRequest
package com.wh.sd.consumers;
import java.util.ArrayList;
import java.util.List;
import com.wh.annotation.Scanning;
import com.wh.mfct.resource.ResourceBaseInfo;
import com.wh.sd.node.NetNode;
import com.wh.sd.provider.ProviderAddressPool;
/**
* 客户端请求类,实现了IClientRequest接口
* @author 闹闹小丫头
*
*/
@Scanning(klass = IClientRequest.class)
public class ClientRequest implements IClientRequest {
private ProviderAddressPool providerAddressPool;
public ClientRequest() {
this.providerAddressPool = new ProviderAddressPool();
}
public void setProviderAddressPool(ProviderAddressPool providerAddressPool) {
this.providerAddressPool = providerAddressPool;
}
@Override
public List<NetNode> dealServiceRequest(String service) {
List<NetNode> serverNodeList = new ArrayList<NetNode>();
serverNodeList = providerAddressPool.getProvideAddressList(service);
if(serverNodeList == null) {
serverNodeList = new ArrayList<NetNode>();
NetNode node = new NetNode();
node.setIp("null");
node.setPort(0);
serverNodeList.add(node);
}
return serverNodeList;
}
@Override
public List<NetNode> dealResourceRequest(String rbi) {
// TODO
System.out.println(rbi);
System.out.println("申请资源");
return null;
}
}
ENetCommand
package com.wh.sd.netmessage;
/**
* 命令枚举
* @author 闹闹小丫头丶
*
*/
public enum ENetCommand {
/**
* 服务提供者注册服务
*/
SERVICE_REGISTRY,
/**
* 服务提供者注销服务
*/
SERVICE_LOGOUT,
/**
* 通知服务提供者注销服务
*/
ADVISE_SERVICE_LOGOUT,
/**
* 通知服务提供者注册服务
*/
ADVISE_SERVICE_REGISTRY,
/**
* 检测服务器是否在线
*/
MONITOR_ONLINE,
}
NetCommandParsing
package com.wh.sd.netmessage;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import com.wh.sd.communication.ServerCommunicationNode;
/**
* 命令解析类
* @功能 解析网络信息生成方法,并执行解析出来的方法
* @author 闹闹小丫头丶
*
*/
public class NetCommandParsing {
public NetCommandParsing() {
}
/**
* 根据指定对象处理指定节点的指定消息(NetMessage)
* @param object 指定对象,从中找到需要使用的方法
* @param cNode 指定节点
* @param message 指定消息
*/
public static void dealNetCommand(Object object,
ServerCommunicationNode cNode, NetMessage message) {
String command = message.getNetCommand().name().toLowerCase();
String spis[] = command.split("_");
String methodName = "deal";
for(String spi : spis) {
methodName += spi.substring(0, 1).toUpperCase() + spi.substring(1);
}
Class<?> klass = object.getClass();
try {
// 得到该指定对象内的指定方法
Method method = klass.getDeclaredMethod(methodName, new Class<?>[] {
ServerCommunicationNode.class,
NetMessage.class
});
method.invoke(object, cNode, message);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
ServerNetNodePool
package com.wh.sd.node;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ThreadPoolExecutor;
import com.wh.sd.communication.Communication;
import com.wh.sd.communication.IServerCommunicationAction;
import com.wh.sd.communication.ServerCommunicationNode;
import com.wh.sd.provider.ProviderAddressPool;
import com.wh.sd.util.DidaDida;
import com.wh.sd.util.IDidaDidaAction;
/**
* 服务器节点池类
* @author 闹闹小丫头
*
*/
public class ServerNetNodePool implements Runnable{
// 服务器连接节点列表
private static final CopyOnWriteArrayList<ServerCommunicationNode> serverList =
new CopyOnWriteArrayList<ServerCommunicationNode>();
// 服务提供池
private ProviderAddressPool providerAddressPool;
// 服务器连接所需要的动作
private IServerCommunicationAction serverCommunicationAction;
// 线程池
private ThreadPoolExecutor threadPool;
private volatile boolean goon;
private IDidaDidaAction didaDidaAction;
private DidaDida didaDida;
public ServerNetNodePool() {
init();
}
public void setThreadPool(ThreadPoolExecutor threadPool) {
this.threadPool = threadPool;
}
public void setServerCommunicationAction(IServerCommunicationAction serverCommunicationAction) {
this.serverCommunicationAction = serverCommunicationAction;
}
/**
* 根据指定的服务器生成新的ServerCommunicationNode
* @param socket 指定的服务器
* @return 返回新的ServerCommunicationNode
*/
public ServerCommunicationNode generateServerCommunicationNode(Socket socket) {
ServerCommunicationNode serverCommunicationNode = new ServerCommunicationNode();
Communication communication = new Communication();
try {
communication.setSocket(socket);
communication.setCommunicationNode(serverCommunicationNode);
communication.setDis(new DataInputStream(socket.getInputStream()));
communication.setDos(new DataOutputStream(socket.getOutputStream()));
communication.setThreadPool(threadPool);
communication.setServerCommunicationAction(serverCommunicationAction);
NetNode node = new NetNode();
node.setIp(socket.getInetAddress().getHostAddress());
node.setPort(socket.getPort());
serverCommunicationNode.setNode(node);
serverCommunicationNode.setCommunication(communication);
} catch (IOException e) {
}
return serverCommunicationNode;
}
/**
* 把指定服务器添加到服务器连接节点列表内
* @param socket
*/
public void addServerSocket(Socket socket) {
ServerCommunicationNode serverCommunicationNode = generateServerCommunicationNode(socket);
if(serverList.contains(serverCommunicationNode)) {
return;
}
serverList.add(serverCommunicationNode);
}
// public void addServerSocket(ServerCommunicationNode communicationNode) {
// if(serverList.contains(communicationNode)) {
// return;
// }
// serverList.add(communicationNode);
// }
/**
* 把指定服务器连接节点从服务器连接节点列表和服务器服务提供池中删除
* @param communicationNode
*/
public void removeServerSocket(ServerCommunicationNode communicationNode) {
if(!serverList.contains(communicationNode)) {
return;
}
serverList.remove(communicationNode);
System.out.println("从节点轮询池中移除了节点" + communicationNode.getNode());
providerAddressPool.removeProvider(communicationNode.getNode());
}
/**
* 启动服务器节点轮询线程
*/
public void startup() {
goon = true;
new Thread(this, "服务器节点轮询").start();
}
/**
* 关闭服务器节点轮询线程
*/
public void close() {
goon = false;
didaDida.stop();
}
/**
* 初始化数据
*/
public void init() {
providerAddressPool = new ProviderAddressPool();
didaDidaAction = new HeartbeatDetection();
didaDida = new DidaDida(1000, didaDidaAction);
}
@Override
public void run() {
didaDida.start();
while(goon) {
if(!serverList.isEmpty()) {
for(ServerCommunicationNode serverCommunicationNode : serverList) {
Communication communication = serverCommunicationNode.getCommunication();
communication.checkReceive();
}
}
}
}
/**
* 心跳检测内部类
* @author 闹闹小丫头
*
*/
class HeartbeatDetection implements IDidaDidaAction{
public HeartbeatDetection() {
}
@Override
public void doSmething() {
if(!serverList.isEmpty()) {
for(ServerCommunicationNode serverCommunicationNode : serverList) {
Communication communication = serverCommunicationNode.getCommunication();
communication.toEndingDetection();
}
}
}
}
}
ProviderAddressPool
package com.wh.sd.provider;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.wh.sd.communication.ServerCommunicationNode;
import com.wh.sd.node.NetNode;
/**
* 服务器提供者池 成员是以服务名称为键,服务提供者节点列表为值组成的Map
* @author 闹闹小丫头丶
*
*/
public class ProviderAddressPool {
private static final Map<String, List<NetNode>> servierPool =
new ConcurrentHashMap<String, List<NetNode>>();
public ProviderAddressPool() {
}
/**
* 根据指定的消息把指定ServerCommunicationNode的指定服务加入提供者池
* @param message 指定消息
* @param serverCommunicationNode 指定ServerCommunicationNode
*/
public void addProvider(String message, ServerCommunicationNode serverCommunicationNode) {
String[] paras = message.split("_");
String servier = paras[0];
int port = Integer.valueOf(paras[1]);
NetNode node = serverCommunicationNode.getNode();
node.setPort(port);
List<NetNode> nodeList = servierPool.get(servier);
if(nodeList == null) {
nodeList = new ArrayList<NetNode>();
servierPool.put(servier, nodeList);
}
nodeList.add(node);
System.out.println("服务器"+ node + "添加到" + servier + "服务中成功");
}
/**
* 清空服务者池
*/
public void clear() {
if(servierPool == null) {
return;
}
servierPool.clear();
}
/**
* 把指定节点从服务者池中删除
* @param node 指定节点
*/
public void removeProvider(NetNode node) {
for(String key : servierPool.keySet()) {
removeProviderServier(key, node);
}
}
/**
* 删除指定服务内的指定节点
* @param servier 指定服务
* @param node 指定节点
*/
public void removeProviderServier(String servier, NetNode node) {
List<NetNode> nodeList = servierPool.get(servier);
if(nodeList == null) {
return;
}
if(nodeList.contains(node)) {
nodeList.remove(node);
System.out.println("节点" + nodeList + "从服务" + servier + "中已经移除!");
if(nodeList.isEmpty()) {
servierPool.remove(servier);
System.out.println("服务" + servier + "已没有提供者");
// TODO 通知该服务提供者注册服务
}
}
}
/**
* 从服务者池中获得指定服务的节点列表
* @param seriver 指定服务
* @return 返回获得的节点列表
*/
public List<NetNode> getProvideAddressList(String seriver) {
return servierPool.get(seriver);
}
}
ServerCommunicationAction代码块
/**
* 服务提供者注册服务
* @param node 服务器节点
* @param netMessage 服务器发送过来的消息
*/
public void dealServiceRegistry(ServerCommunicationNode node, NetMessage netMessage) {
// THDO 需要完善
providerAddressPool.addProvider(netMessage.getPara(), node);
}
/**
* 服务提供者注销服务
* @param node 服务器节点
* @param netMessage 服务器发过来的消息
*/
public void dealServiceLogout(ServerCommunicationNode node, NetMessage netMessage) {
// TODO 需要完善
providerAddressPool.removeProviderServier(netMessage.getPara(), node.getNode());
}
/**
* 通知服务提供者注销服务
* @param service 服务名称
* @param node 服务器节点
*/
public void adviseServiceLogout(String service, ServerCommunicationNode node) {
// TODO 需要完善
NetMessage message = new NetMessage(null);
message.setNetCommand(null);
message.setAction("注销服务");
message.setPara(service);
node.getCommunication().send(message);
}
/**
* 通知服务提供者注册服务
* @param service 服务名称
* @param node 服务器节点
*/
public void adviseServiceRegistry(String service, ServerCommunicationNode node) {
// TODO 需要完善
NetMessage message = new NetMessage(null);
message.setNetCommand(null);
message.setAction("注册服务");
message.setPara(service);
node.getCommunication().send(message);
}
/**
* IServerCommunicationAction接口实现类
* 服务器连接动作
* @author 闹闹小丫头
*
*/
class ServerCommunicationAction implements IServerCommunicationAction{
public ServerCommunicationAction() {
}
@Override
public void dealNetMessage(ServerCommunicationNode serverCommunicationNode, NetMessage message) {
NetCommandParsing.dealNetCommand(RegistryServer.this, serverCommunicationNode, message);
}
}
补充:这是博主自己完成的关于服务发现的为服务框架,其实实现的方式有多种,如有兴趣可进行交流探讨,其中还有许多要完善的地方。