前言
RPC说到底就是远程调用,要实现远程调用,只需要2步。
1.消费者传输(类名+方法名+参数)给服务端
2.服务端收到(类名+方法名+参数),执行具体方法,将执行结果返回给消费者。
市面上所有的RPC框架都是上述两步,不一样的只是传输协议,也就是怎么传输(类名+方法名+参数)。一般就是HTTP协议和自研协议,如SpringCloud使用HTTP,dubbo使用自研。
我们这里使用,Socket+Protostuff,附件我会上传(环境是JDK1.8),尽可能的写的简单明了,强烈建议大家下载下来用idea导进去,对照学习。
一、项目总体架构
一般来说,生产环境下,消费端+服务端+公用接口,是分成3个子模块的,服务端和消费端依赖公用接口(二方包),服务端实现接口,消费端调用接口。
二、具体实现
1.服务端
1.1 收到客户端的(类名+方法名+参数)
1.2 通过Java反射执行方法
1.3 返回结果
ServerSocket serverSocket = new ServerSocket(8888);
while (true) {
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
//1.1 收到客户端的(类名+方法名+参数)
TransferObject transferObject = SerializingUtil.deserialize(inputStream, TransferObject.class);
String apiClassName = transferObject.getApiName();
String methodName = transferObject.getMethodName();
Class[] paramTypes = transferObject.getParamTypes();
Object[] args4Method = transferObject.getArgs();
Class clazz = null;
if (apiClassName.equals(IUserService.class.getName())) {
clazz = UserServiceImpl.class;
}
Method method = clazz.getMethod(methodName, paramTypes);
//1.2 通过反射执行方法
Object returnObject = method.invoke(clazz.newInstance(), args4Method);
//1.3 返回结果
OutputStream outputStream = socket.getOutputStream();
outputStream.write(SerializingUtil.serialize(returnObject));
outputStream.flush();
outputStream.close();
inputStream.close();
socket.close();
}
2.消费端
2.1 发送(类名+方法名+参数)给服务端
注意这里的IUserService是不能new的,因为消费端没有实现类,只能通过Java动态代理。
动态代理的简要意思是,对于没有实现类的接口来说,可以代为实现一个“虚拟”类,执行方法,返回结果。
这里的执行方法,可以是本地的,也可以是远程的,是Java代码就行。RPC的核心就是这里。
2.2 收到服务的执行结果
public class ConsumerStarter {
public static void main(String[] args) {
IUserService userService = (IUserService) ProxyFactory.getProxyInstance(IUserService.class);
User temp = new User();
temp.setId(1);
temp.setName("张三");
userService.add(temp);
User user = userService.findById(1);
System.out.println(user);
}
}
public class ProxyFactory {
public static Object getProxyInstance(Class clazz) {
return Proxy.newProxyInstance(
clazz.getClassLoader(),
new Class[]{clazz},
(proxy, method, args) -> {
Socket socket = new Socket("127.0.0.1", 8888);
String apiName = clazz.getName();
String methodName = method.getName();
Class returnClass = method.getReturnType();
Class[] paramTypes = method.getParameterTypes();
TransferObject transferObject = new TransferObject();
transferObject.setApiName(apiName);
transferObject.setMethodName(methodName);
transferObject.setParamTypes(paramTypes);
transferObject.setArgs(args);
2.1 发送(类名+方法名+参数)给服务端
OutputStream outputStream = socket.getOutputStream();
outputStream.write(SerializingUtil.serialize(transferObject));
outputStream.flush();
socket.shutdownOutput();
2.2 收到服务的执行结果
InputStream inputStream = socket.getInputStream();
Object returnObject = SerializingUtil.deserialize(inputStream, returnClass);
inputStream.close();
outputStream.close();
socket.close();
return returnObject;
});
}
}
结语
我不喜欢blog一堆代码,因为重点是思路,代码能体现思路就行,具体的代码建议大家下载下来,亲自跑一跑。修改修改,比如我使用的是Protostuff序列化,你可以修改为JSON试试,或者用Java自带的序列化。
我使用的是socket,你换成HTTP试试,或者用netty试试。
RPC和核心就是 消费端【Java动态代理】+服务端【Java反射】
工程已上传,审核中,也可以去GitHub看。
项目地址:https://github.com/cklogic/blog/tree/master/rpc-demo