定义:RPC(Remote Procedure Call) 远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样通过网络传输去访问远端系统资源
分析:?
微服务的服务端(提供者): 第一步:需要提供一个端口连接地址 第二步:微服务服务如何接收客户端的传参,从而调用自己的方法呢?那么这时候就需要一个客户端传参类名,方法名,参数,类型等信息,利用反射机制调用自己的方法,然后返回结果值。第三步:参数值如何通过网络传递呢?就需要通过流的方式,那么就需要用到序列化和反序列化,将参数转化为流和将返回消息反序列化出来
微服务的客户端(消费者):第一步:首先需要连接服务提供者的网络;第二步:如何把请求参数传递过去?对参数信息进行包装?那么如果有大量的网络接口调用呢,每一个都要进行网络传递参数的包装吗?显然是比较冗余且麻烦的,如果接口信息有变化呢,改动的地方就比较多。因此考虑以上因素,微服务可以使用jdk动态代理(可以参考博客:设计模式之深入分析JDK动态代理-CSDN博客)进行参数的传递 第三步:序列化请求消息 反序列化 响应消息
采用了一个线程池的写法,把服务的连接放到多个线程中,避免阻塞
public class RpcProxyServer {
// 设置一个多线程
private final ExecutorService executorService = Executors.newCachedThreadPool();
public void publisher(Object service, int port) throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true) {
Socket socket = serverSocket.accept();
executorService.execute(new ProcessorHandler(socket, service));
}
}
}
请求参数的接收和反序列化
// 拿到客户端的请求信息 先转到内存中
try {
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
// 可以拿到哪些信息 需要哪些信息 (需要反射调用我们的方法)
// 反序列化
RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
Object invoke = invoke(rpcRequest);
// 把执行完我们的方法 结果返回给客户端
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(invoke);
objectOutputStream.flush();
objectInputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
private Object invoke(RpcRequest rpcRequest) throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
Class<?> aClass = Class.forName(rpcRequest.getClassName());
Method method = aClass.getMethod(rpcRequest.getMethodName(),rpcRequest.getTypes());
Object invoke = method.invoke(service, rpcRequest.getArgs());
return invoke;
}
将微服务客户端的请求? 参数设置和序列化请求消息 反序列化响应消息 都放在了jdk动态代理的内部类invoke方法中,只要调用了接口方法,就会执行该方法 拿到返回响应消息?
public class RemoteInvocation implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 请求参数的设置
System.out.println("我是客户端代理");
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setMethodName(method.getName());
rpcRequest.setArgs(args);
rpcRequest.setTypes(method.getParameterTypes());
// 连接服务提供者 序列化请求消息
Socket socket = new Socket("localhost", 8080);
ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
objectOutputStream.writeObject(rpcRequest);
objectOutputStream.flush();
// 反序列化响应消息
ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
Object instance = objectInputStream.readObject();
objectInputStream.close();
return instance;
}
}
动态代理的通用写法
传参 接口的类加载器? 该接口中的方法 自定义的内部类实现
public class RpcProxyClient {
public <T> T clientProxy(final Class<T> interfaceCls) {
return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{interfaceCls}, new RemoteInvocation());
}
}
客户端main方法
直接调用动态代理 然后只关注调用服务提供者的接口即可
public static void main( String[] args )
{
// helloService.sayHello("java");
RpcProxyClient rpcProxyClient = new RpcProxyClient();
HelloService helloService = rpcProxyClient.clientProxy(HelloService.class);
String s = helloService.sayHello("java");
System.out.println(s);
}