首页 > 其他分享 >RPC原理简析

RPC原理简析

时间:2023-03-17 21:02:46浏览次数:37  
标签:RPC System 简析 new println 原理 port out


RPC作用是什么?
通过动态代理和反射透明友好地调用远程服务器。即调用过程跟本地调用服务一致,让构建分布式应用、微服务更简单。


为什么要用RPC?
过去的Java应用一般采用Controller->Service->Dao结构,并且写在同一个应用上,方便但存在以下缺点:

  • 无高可用性,单体应用挂了,那所有业务均不能访问
  • 无可拓展性,业务耦合,不利于团队开发

但在大型服务网公司的系统一般都是由成千上万的服务组成,部署在不同服务器上。而采用RPC则可将业务拆分为多个服务到远程服务器,不同服务之间可进行透明的远程调用,增强应用的高可用性、可拓展性。而dubbo则是其中RPC的典型实现,由服务提供者、消费者组成。


RPC有限制传输协议吗?
没有,原则上只要是远程调用的都可以叫RPC,与是tcp还是http无关,比如thrift,grpc,xml-rpc,json-rpc都是通过HTTP传输,dubbo是通过tcp传输。相对而言使用http传输效率没tcp高,但使用http更方便,任何一个web服务器均能使用。


RPC如何调用他人的远程服务?

RPC的调用流程如下图:

RPC原理简析_rpc

用RPC调用时,会屏蔽2-8的细节,然后返回9的结果回去。通俗的讲大致流程如下:

消费方通过rpc调用发起一个类似代理的中转类将数据转为二进制,然后通过dns解析找到对应的提供方服务器地址后请求服务方的代理类,当前代理类会将二进制数据转为正常的格式后传输给提供方的然后通过重复上述数据转换过程返回到消费方。


RPC如何做到透明化远程服务调用?
通过代理封装调用服务的细节,java就包含JDK动态代理和cglib两种方式。多数采用RPC选择JDK动态代理。
但是为什么用JDK动态代理就可以做到透明化?以下RpcFramework.java代码中就对invoke做了处理,发送RPC请求和接口响应。


RPC例子讲解
这里借助以下例子进行讲解(​​javascript:void(0)​​)
首先定义RpcFramework.java,用于暴露/引用服务。

/**
* @Author: lisam
*/
public class RpcFramework {
/**
* 暴露服务
*
* @param service 服务实现
* @param port 服务端口
* @throws Exception
*/
public static void export(final Object service, int port) throws Exception {
if (service == null) {
throw new IllegalArgumentException("service instance == null");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port " + port);
}
System.out.println("Export service " + service.getClass().getName() + " on port " + port);
// 建立Socket服务端
ServerSocket server = new ServerSocket(port);
for (; ; ) {
try {
// 监听Socket请求
final Socket socket = server.accept(); //阻塞等待新的连接
new Thread(new Runnable() {
@Override
public void run() {
try {
try {
/* 获取请求流,Server解析并获取请求*/
// 构建对象输入流,从源中读取对象到程序中
ObjectInputStream input = new ObjectInputStream(
socket.getInputStream());
try {
System.out.println("\nServer解析请求 : ");
String methodName = input.readUTF();
System.out.println("methodName : " + methodName);
// 泛型与数组是不兼容的,除了通配符作泛型参数以外
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
System.out.println(
"parameterTypes : " + Arrays.toString(parameterTypes));
Object[] arguments = (Object[]) input.readObject();
System.out.println("arguments : " + Arrays.toString(arguments));
/* Server 处理请求,进行响应*/
ObjectOutputStream output = new ObjectOutputStream(
socket.getOutputStream());
try {
// service类型为Object的(可以发布任何服务),故只能通过反射调用处理请求
// 反射调用,处理请求
Method method = service.getClass().getMethod(methodName, parameterTypes);
Object result = method.invoke(service, arguments);
System.out.println("\nServer 处理并生成响应 :");
System.out.println("result : " + result);
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 {
if (interfaceClass == null) {
throw new IllegalArgumentException("Interface class == null");
}
// JDK 动态代理的约束,只能实现对接口的代理
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
"The " + interfaceClass.getName() + " must be interface class!");
}
if (host == null || host.length() == 0) {
throw new IllegalArgumentException("Host == null!");
}
if (port <= 0 || port > 65535) {
throw new IllegalArgumentException("Invalid port " + port);
}
System.out.println(
"Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
// JDK 动态代理
T proxy = (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class<?>[]{interfaceClass}, new InvocationHandler() {
// invoke方法本意是对目标方法的增强,在这里用于发送RPC请求和接收响应 定义处理逻辑
@Override
public Object invoke(Object proxy, Method method, Object[] arguments)
throws Throwable {
// 创建Socket客户端,并与服务端建立链接
Socket socket = new Socket(host, port);
try {
/* 客户端像服务端进行请求,并将请求参数写入流中*/
// 将对象写入到对象输出流,并将其发送到Socket流中去
ObjectOutputStream output = new ObjectOutputStream(
socket.getOutputStream());
try {
// 发送请求
System.out.println("\nClient发送请求 : ");
output.writeUTF(method.getName());
System.out.println("methodName : " + method.getName());
output.writeObject(method.getParameterTypes());
System.out.println("parameterTypes : " + ArraysztoString(method.getParameterTypes()));
output.writeObject(arguments);
System.out.println("arguments : " + Arrays.toString(arguments));
/* 客户端读取并返回服务端的响应*/
ObjectInputStream input = new ObjectInputStream(
socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
System.out.println("\nClient收到响应 : ");
System.out.println("result : " + result);
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
return proxy;
}
}

然后定义对应的服务接口及其实现类HelloService.javaHelloServiceImpl.java如下:

public interface HelloService {
String hello(String name);
String hi(String msg);
}
//------------------实现类----------------------
public interface HelloService {
String hello(String name);
String hi(String msg);
}

 

定义服务消费者提供方 RpcProvider.java如下:

public static void main(String[] args) throws Exception {
HelloService service = new HelloServiceImpl();
// RPC框架将服务暴露出来,供客户端消费
RpcFramework.export(service, 1234);
}

定义服务消费者消费方 RpcConsumer.java如下:

public static void main(String[] args) throws Exception {
// 由RpcFramework生成的HelloService的代理
HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
String hello = service.hello("World");
System.out.println("客户端收到远程调用的结果 : " + hello);
}

注:通过以上例子我们大致可以了解到RPC的简单实现,底层是通过socket进行连接的。封装了细节,做到了透明化无感知的连接远程服务。

 

标签:RPC,System,简析,new,println,原理,port,out
From: https://blog.51cto.com/u_13854513/6128389

相关文章