前言
学习RPC需要会的前置知识:基于TCP的Socket、Java反射的基本使用、序列化、代理模式之动态代理(JDK就行)、多线程,都是一些Java基础知识,不会的自行补课。
什么是 RPC 框架
RPC 框架—– 远程过程调用协议RPC(Remote Procedure Call Protocol)—–允许像调用本地服务一样调用远程服务。
言:RPC知识一种框架思想,A端等接收到B端的序列化数据,在处理好后,再能序列化给B端,我们就可以称它是PRC,以下知识只我个人对PRC的某一种实现方式理解
简单的来说就是我这台服务器A(注册中心)要调服务器B的业务,服务器B有具体的执行逻辑, 而服务器A只需要知道接口方法就行。
为什么服务器A只需要知道接口方法,中间在哪用到了动态代理、反射、Socket技术等技术
- 服务器A执行的方法是实际是动态代理对象在执行, 这时我们就可以用代理对象InvocationHandler中的invoke()方法,这个方法可以知道当前对象正执行的方法名和方法参数,
- 在通过这个方法以及得到的信息去使用ServerSocket+序列化发送给服务器B,让B接收一些class名 、方法名、方法参数等
- 服务器B在拿到这些参数就可以利用反射技术执行具体逻辑了,然后再将执行后结果继续使用socket的方式返回给A。(为了避免socket接收后阻塞,可以用多线程接收)
RPC 调用的基本流程
1,远程服务之间建立通讯协议
2,寻址:服务器(如主机或IP地址)以及特定的端口,方法的名称名称是什么
3,通过序列化和反序列化进行数据传递
4,将传递过来的数据通过java反射原理定位接口方法和参数
5,暴露服务:用map将寻址的信息暴露给远方服务(提供一个endpoint URI或者一个前端展示页面)
6,多线程并发请求业务
代码实现
服务器A (注册中心),为什么叫注册中心,类似SpringClond服务发现、服务注册功能,把具体接口的实现类用一个map 装起来,供服务B发送接口方式时,服务A好利用反射实现。
主要任务是
1)注册供远程调用的实现类
2)等待服务器B连接
3)反射出具体对象,并执行方法
4)发送处理结果
// 规范接口
public interface Server {
public void start() throws IOException;
public void stop();
public void register(Class service, Class serviceImpl);
}
/**====================== **/
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @version 1.0
* @date 2022/5/5 10:01
* @desc 注册中心
*/
public class ServerCenter implements Server{
private static int port;
private static Boolean isRunning = false;
// key : 接口名,value :接口实现类
private static Map<String,Class> serviceRegister = new HashMap<>();
// 连接池
private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public ServerCenter(int port){
this.port = port;
}
@Override
public void start() throws IOException {
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(port));
isRunning = true;
System.out.println("服务器启动完毕");
while (isRunning){
Socket socket = serverSocket.accept();// 等待客户端连接
// 接收到一个连接,就交给一个线程去处理具体业务
executor.execute(new Task(socket));
}
}
@Override
public void stop() {
isRunning = false;
executor.shutdown();
System.out.println("服务器关闭");
}
/**
* @param service 接口的类
* @param serviceImpl 接口的实现类
*/
@Override
public void register(Class service, Class serviceImpl) {
serviceRegister.put(service.getName(),serviceImpl);
}
class Task implements Runnable{
Socket socket;
public Task(Socket socket) {
this.socket = socket;
}
@Override
public void run() {
ObjectOutputStream output = null;
ObjectInputStream input = null;
try {
// 接收用户请求
input = new ObjectInputStream( socket.getInputStream());
String serviceName = input.readUTF(); // 接口名
String methodName = input.readUTF(); // 方法名
Class[] parameterTypes = (Class[])input.readObject(); // 参数类型
Object[] arguments = (Object[])input.readObject(); // 参数
// 去注册中心serviceRegister拿具体接口,反射实现类
Class serviceClass = serviceRegister.get(serviceName);
Method method = serviceClass.getMethod(methodName, parameterTypes);
Object result = method.invoke(serviceClass.newInstance(), arguments);
// 回馈用户
output = new ObjectOutputStream(socket.getOutputStream());
output.writeObject(result);
} catch (Exception exception) {
exception.printStackTrace();
} finally {
try {
output.close();
input.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
}
}
服务B客户端,主要任务是
1)与服务器A建立连接
2)将接口方法和参数等信息发送给服务器A
3)等待接收处理结果
import java.io.IOException;
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.InetSocketAddress;
import java.net.Socket;
/**
* @version 1.0
* @date 2022/5/5 10:12
* @desc 客户端, 动态代理
*/
public class Client {
// 获取代表服务端接口的动态代理对象
/**
*
* @param serviceInterface 请求接口名
* @param address ip和端口
* @param <T>
*/
public static <T> T getRemoteProxyObj(Class serviceInterface , InetSocketAddress address){
/**
* Proxy.newProxyInstance(a,b,c)
* a : 类加载器,需要代理哪个类
* b : 需要代理的对象,具备哪些方法
* c : 动态代理的对象
*/
return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() {
// proxy :newProxyInstance返回代理的对象 mothod:代理对象的哪个方法: args:参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
Socket socket =new Socket();
ObjectOutputStream outputStream = null;
ObjectInputStream input = null;
try {
socket.connect(address);
outputStream = new ObjectOutputStream(socket.getOutputStream());
// 顺序自身定义 - 告诉注册中心你要执行哪些方法
// 接口名、方法名
outputStream.writeUTF(serviceInterface.getName());
outputStream.writeUTF(method.getName());
// 方法参数类型 方法参数
outputStream.writeObject(method.getParameterTypes());
outputStream.writeObject(args);
// 等待服务端
// 接收服务的处理后的值
input = new ObjectInputStream(socket.getInputStream());
return input.readObject();
} catch (Exception exception) {
exception.printStackTrace();
return null;
} finally {
try {
outputStream.close();
input.close();
} catch (IOException exception) {
exception.printStackTrace();
}
}
}
});
}
}
Service
public interface RpcService {
String getName(String name);
}
/**===========**/
public class RpcServiceImpl implements RpcService{
@Override
public String getName(String name) {
return name + " 你好呀!";
}
}
模拟服务端和客户端
public class RPCServerTest {
public static void main(String[] args) throws IOException, InterruptedException {
Server server = new ServerCenter(9999);
server.register(RpcService.class, RpcServiceImpl.class); //服务注册
server.start();
}
}
public class RPCClientTest {
public static void main(String[] args) {
RpcService remoteProxyObj = new Client().getRemoteProxyObj(RpcService.class, new InetSocketAddress("127.0.0.1", 9999));
System.out.println(remoteProxyObj.getName("zs"));
}
}
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/4804.html