предисловие
Вчера я прочитал блог о реализации RPC-фреймворка с помощью нескольких строк кода.называется avatar.ITeye.com/blog/112391…, я многое почерпнул, поэтому хочу рационализировать свои идеи на основе этого блога, и добавить как можно больше комментариев, чтобы еще больше снизить порог для изучения принципов фреймворка RPC.
Схема
Первая схематическая диаграмма, читатели могут использовать эту диаграмму, чтобы помочь понять последующий код.
код
Базовый класс RpcFramework
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
public class RpcFramework {
/**
* 暴露服务
*
* @param service 服务实现
* @param port 服务端口
* @throws Exception
*/
public static void export(final Object service, int port) throws Exception {
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
ServerSocket server = new ServerSocket(port);
//一直轮询,相比while(true),这种方式性能更佳
for(;;) {
try {
//此处阻塞一直等到有consumer请求过来
final Socket socket = server.accept();
//每来一个消费请求就开启一个新的线程
new Thread(() -> {
try {
try {
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
//consumer会分三次发送所需要的方法信息,这里的readUTF(),readObject()都会发生阻塞
String methodName = input.readUTF();
Class<?>[] parameterTypes = (Class<?>[])input.readObject();
Object[] arguments = (Object[])input.readObject();
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
//获取到目标方法
Method method = service.getClass().getMethod(methodName, parameterTypes);
//通过反射执行目标方法并返回结果
Object result = method.invoke(service, arguments);
//将执行结果返回给consumer
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} finally {
input.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* 引用服务
*
* @param <T> 接口泛型
* @param interfaceClass 接口类型
* @param host 服务器主机名
* @param port 服务器端口
* @return 远程服务
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host, final int port) throws Exception {
System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
//通过JDK动态代理的方式直接返回给调用refer方法的调用者一个被动态代理处理过的 对象
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() {
@Override
//调用该对象的每个方法都会先去调用下面的逻辑
public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable {
//当方法真实被调用的时候才会发起RPC远程请求provider执行服务
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
//分三次发送方法所需要的信息
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
//得到服务执行的最终结果
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
}
}
сервисный интерфейс
public interface HelloService {
String hello(String name);
}
Реализация сервисного интерфейса
public class HelloServiceImpl implements HelloService {
@Override
public String hello(String name) {
System.out.println("被调用了");
return "Hello" + name;
}
}
класс начальной загрузки провайдера
public class RpcProvider {
public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
RpcFramework.export(service, 1234);
}
}
потребительский класс начальной загрузки
public class RpcConsumer {
public static void main(String[] args) throws Exception {
//此时获取到的service是被JDK动态代理包装后的service,在调用方法的时候会进行远程调用
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
for (int i = 0; i < Integer.MAX_VALUE; i ++) {
String hello = service.hello("World" + i);
System.out.println(hello);
Thread.sleep(1000);
}
}
}
На следующем рисунке представлена диаграмма структуры классов проекта провайдера.
Суммировать
Краткое изложение простого RPC-фреймворка.Блогер недавно изучает принцип даббо,поэтому он не распространял слишком много распределенного контента.Надеюсь написать статью о распределенном в будущем с углублением изучения,и подбадривать друг друга !