模拟Dubbo底层RPC实现,Socket通信,实现参数回调功能。
之前也写过自己的RPC框架,就想着去了解一下Dubbo底层核心的RPC并模拟实现一下。发现Dubbo底层的RPC相比我之前更加完善,特别是【参数回调】这个我之前并没有的功能:
RPC为远程方法调用
故,我们要实现的是消费者远程调用服务器的方法。
整体思路:消费者发送要实现的方法信息及参数,服务器反射执行后再发送回消费者。
目录
1.注册中心
先看注册中心,其作用是保存与他连接的服务暴露者并存储服务暴露者信息。通过长连接连接服务暴露者,如果服务暴露者异常中断,注册中心则会从服务列表中移除该服务。本项目中使用zookeeper。
2.服务提供者
先从服务提供者来看,服务提供者先向注册中心注册,需要发送给注册中心他的服务名和服务地址(ip:port),方便消费者从注册中心获得服务,连接,调用。
此外,为了让消费者通过接口调用服务暴露者方法,服务暴露者本身需要存储 <接口, 接口实现类模板> 的hashMap,当消费者通过接口远程调用方法时,匹配到实现类,并反射执行。
2.1 上下文中心
上下文中心的作用是保存隐式参数。
何为隐式参数?举个例子来说就是消费者远程调用服务暴露者方法,想要额外传递的参数。这个参数不是该方法所需的参数。
这里用到了ThreadLocal:即,每个线程拥有它自己的LOCAL和SERVER,说白了就是各自的一个map,以键值对的方式存放该线程服务器和消费者的隐式参数。键:参数名 值:参数
/**
* 隐式上下文参数对象
*/
public class FYContext {
/**
* 客户端本地上下文
*/
private static ThreadLocal<FYContext> LOCAL = new ThreadLocal<FYContext>();
/**
* 服务端上下文
*/
private static ThreadLocal<FYContext> SERVER = new ThreadLocal<FYContext>();
/**
* 隐式参数容器
*/
private final Map<String, String> attachment = new HashMap<String, String>();
/**
* 获得本地上下文对象
* @return
*/
public static FYContext getContext() {
FYContext fyContext = LOCAL.get();
if (fyContext == null) {
fyContext = new FYContext();
LOCAL.set(fyContext);
}
return fyContext;
}
/**
* 获得服务端上下文对象
* @return
*/
public static FYContext getServerContext() {
FYContext fyContext = SERVER.get();
if (fyContext == null) {
fyContext = new FYContext();
SERVER.set(fyContext);
}
return fyContext;
}
/**
* 清楚客户端上下文
*/
public void removeContext() {
LOCAL.remove();
}
/**
* 清除服务器上下文
*/
public void removeServerContext() {
SERVER.remove();
}
/**
* 存储需要传递的参数
* @param key 参数键
* @param value 参数值
*/
public void setAttachment(String key, String value) {
attachment.put(key, value);
}
/**
* @return 本地线程所有的隐方参数容器
*/
public Map<String, String> getAttachments() {
return attachment;
}
/**
* 获得容器参数值
* @param key
* @return
*/
public String getAttachment(String key) {
return attachment.get(key);
}
/**
* 接收所有的参数
* @param attachment 参数容器
*/
public void setAttachments(Map<String, String> attachment) {
this.attachment.putAll(attachment);
}
}
2.2 RPC服务端
rpc服务端就是服务暴露者,说说其需要实现的功能及实现逻辑。
1.向注册机注册服务
2.自身注册接口,创建SocketServer。
3.当有socket连接与之通信,接收需要的接口名,方法名,参数类型,参数值,隐士参数,反射执行,发送回消费者。(这是没有参数回调的情况)
4.当消费者传递来的参数是需要回调的,服务暴露者还需去和消费者“交流”,远程调用消费者的方法来“完善”参数,这里可能有点绕,一会下面有例子来说明。
下面为服务端代码:
/**
* RPC服务端
*/
public class Server {
/**
* zookeeper 服务注册根节点
*/
public static final String ZOOKEEPER_NOOD_FY = "/FY";
/**
* 服务本地注册容器
*/
private Map<String, Class> serverReigster = new HashMap<String, Class>();
/**
* 服务请求接收端
*/
private ServerSocket serverSocket;
/**
* 多线程容器
*/
private ExecutorService executorService = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
/**
* 结果标记
*/
public final static int FLAG_RESULT = 0;
/**
* 远程回调标记
*/
public final static int FLAG_INVOKER = 1;
/**
* zk服务地址
*/
public final static String ZOOKEEPER_ADDRESS = "localhost";
/**
* zk服务端口
*/
public final static int port = 2181;
/**
* zk会话超时时间
*/
public final static int TIMEOUT = 1000 * 3;
/**
* 服务提供者地址
*/
public final static int SERVER_PORT = 10086;
/**
* 开启服务
* @param ip 暴漏地址
* @param port 暴露端口
*/
publ