PRC演化DEMO
一 简介
RPC(Remote Procedure Call)远程过程调用协议,一种通过网络从远程计算机上请求服务,而不需要了解底层网络技术的协议。RPC它假定某些协议的存在,例如TPC/UDP等,为通信程序之间携带信息数据。在OSI网络七层模型中,RPC跨越了传输层和应用层,RPC使得开发,包括网络分布式多程序在内的应用程序更加容易。
客户端在不知道调用细节的情况下,调用存在于远程计算机上的某个对象,就像调用本地应用程序中的对象一样。
1.1 哪些框架支持 RPC?
RPC 框架可以分为以下几类:
- 基于 TCP/IP 的 RPC 框架:如 gRPC、Apache Thrift、Dubbo 等。
- 基于 HTTP 的 RPC 框架:如 RESTful API、gRPC-Web 等。
- 基于消息队列的 RPC 框架:如 RabbitMQ、ZeroMQ 等。
-
Thrift
thrift 是一个软件框架,用来进行可扩展且跨语言的服务的开发,包括C++, Java, Python等多种编程语言。 -
Dubbo
是一个分布式服务框架,以及 SOA 治理方案。其功能主要包括:高性能 NIO 通讯及多协议集成,服务动态寻址与路由,软负载均衡与容错,依赖分析与降级等 -
Spring Cloud
提供了搭建分布式系统及微服务常用的工具,满足了构建微服务所需的所有解决方案。 -
gRPC
一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用( RPC )系统。
二、手撸一个RPC
接下来,我们一步一步开始RPC的演化过程。
2.1 准备工作
首先准备一个用户类
package org.example.common;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 用户
* @author tanzhipeng
* @date 2025/05/14 12:03
**/
@Data
@AllArgsConstructor
@EqualsAndHashCode
public class User {
private int id;
private String name;
private int age;
}
给用户类提供一个查询接口:
package org.example.common;
/**
* 用户接口
*/
public interface IUserService {
/**
* 获取用户
*/
User getUser(int id);
}
2.2 最原始的二进制调用
- 实现用户类
package org.example.rpc01;
import org.example.common.IUserService;
import org.example.common.User;
/**
* 用户实现
* @author tanzhipeng
* @date 2025/05/14 12:08
**/
public class UserServiceImpl implements IUserService {
@Override
public User getUser(int id) {
return new User(1,"LikeCat",18);
}
}
- 实现服务端
package org.example.rpc01;
import org.example.common.User;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* 服务端
* @author tanzhipeng
* @date 2025/05/14 12:09
**/
public class Server {
private static final int PORT = 8888;
private static final boolean RUNNING = Boolean.TRUE;
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(PORT);
while (RUNNING) {
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
serverSocket.close();
}
private static void process(Socket socket) throws IOException {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
DataInputStream dis = new DataInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
int id = dis.readInt();
User user = new UserServiceImpl().getUser(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.writeInt(user.getAge());
dos.flush();
}
}
- 实现客户端
package org.example.rpc01;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 客户端
* @author tanzhipeng
* @date 2025/05/14 12:09
**/
public class Client {
private static final int PORT = 8888;
public static void main(String[] args) throws IOException {
Socket socket = new Socket("localhost", PORT);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
int id = 1;
dos.writeInt(id);
socket.getOutputStream().write(bos.toByteArray());
socket.getOutputStream().flush();
DataInputStream dis = new DataInputStream(socket.getInputStream());
int userId = dis.readInt();
String name = dis.readUTF();
int age = dis.readInt();
System.out.println(userId);
System.out.println(name);
System.out.println(age);
dos.close();
socket.close();
}
}
2.3 封装代理类
在查询用户的时候,在客户端每次我们都需要进行Socket相关的编程,其实我们可以把整个流程封装起来,客户端无需关心网络以及业务处理,只需要调用相应方法就能取得对应结果,而对于网络细节则交给其他类。
package org.example.rpc02;
import org.example.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;
/**
* 代理类
* @author tanzhipeng
* @date 2025/05/14 12:42
**/
public class Stub {
public User findUser(Integer id,String host ,Integer port) throws IOException {
Socket socket = new Socket(host, port);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(id);
socket.getOutputStream().write(bos.toByteArray());
socket.getOutputStream().flush();
DataInputStream dis = new DataInputStream(socket.getInputStream());
int userId = dis.readInt();
String name = dis.readUTF();
int age = dis.readInt();
System.out.println(userId);
System.out.println(name);
System.out.println(age);
dos.close();
socket.close();
return new User(userId, name, age);
}
}
在客户端调用就只需要关注对应方法就行了。
package org.example.rpc02;
import org.example.common.User;
import java.io.IOException;
/**
* 客户端2
* @author tanzhipeng
* @date 2025/05/14 12:45
**/
public class Client {
public static void main(String[] args) throws IOException {
Stub stub = new Stub();
User user = stub.findUser(1, "localhost", 8888);
System.out.println(user);
}
}
2.4 增强代理类
通过上述代理我们发现一个问题,Stub里面只有一个方法,如果此时我们再新增一个findByName方法,又要重写写一套Socket,这显然是不合理的。
为此,我们有了如下的改进:
package org.example.rpc03;
import org.example.common.IUserService;
import org.example.common.User;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
/**
* 代理类
* @author tanzhipeng
* @date 2025/05/14 12:42
**/
public class Stub {
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("localhost", 8888);
ByteArrayOutputStream bos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(bos);
dos.writeInt(1);
socket.getOutputStream().write(bos.toByteArray());
socket.getOutputStream().flush();
DataInputStream dis = new DataInputStream(socket.getInputStream());
int userId = dis.readInt();
String name = dis.readUTF();
int age = dis.readInt();
System.out.println(userId);
System.out.println(name);
System.out.println(age);
dos.close();
socket.close();
return new User(userId, name, age);
}
};
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
return (IUserService) o;
}
}
此时,我们只需专注业务,并不需要关心网络相关的逻辑,此时客户端的流程变成了:
package org.example.rpc03;
import org.example.common.IUserService;
import org.example.common.User;
import java.io.IOException;
/**
* 客户端2
* @author tanzhipeng
* @date 2025/05/14 12:45
**/
public class Client {
public static void main(String[] args) throws IOException {
IUserService i = Stub.getStub();
User user = i.getUser(1);
System.out.println(user);
}
}
2.4 二次增强代理类
此时,又有新的问题产生了,我们知道,每次代理对象的方法调用,最终都会执行到InvocationHandler当中去,这个时候我们会发现,其实每次执行的都是同一个方法同一套逻辑,拿到的也行相同的结果,这显然是不可取的。
要如何改进,首先至少在InvocationHandler当中,我们需要一个通用版本的实现。
package org.example.rpc04;
import org.example.common.IUserService;
import org.example.common.User;
import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
/**
* 代理类 提供服务器端需要调用哪个方法以及传入对应参数
* @author tanzhipeng
* @date 2025/05/14 12:42
**/
public class Stub {
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("localhost", 8888);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
String methodName = method.getName();
// 获取方法的参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
DataInputStream dis = new DataInputStream(socket.getInputStream());
int userId = dis.readInt();
String name = dis.readUTF();
int age = dis.readInt();
System.out.println(userId);
System.out.println(name);
System.out.println(age);
socket.close();
oos.close();
return new User(userId, name, age);
}
};
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
return (IUserService) o;
}
}
这时候我们给服务端传送了我们想调用的方法和参数,所以相应的服务端我们也需要改变一下。
package org.example.rpc04;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import org.example.common.IUserService;
import org.example.common.User;
import org.example.rpc01.UserServiceImpl;
/**
* 服务端
*
* @author tanzhipeng
* @date 2025/05/14 12:09
**/
public class Server {
private static final int PORT = 8888;
private static final boolean RUNNING = Boolean.TRUE;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
while (RUNNING) {
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
serverSocket.close();
}
private static void process(Socket socket) throws Exception {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(in);
DataOutputStream dos = new DataOutputStream(out);
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[])ois.readObject();
Object[] args = (Object[])ois.readObject();
// 通过反射调用
IUserService service = new UserServiceImpl();
Method method = service.getClass().getMethod(methodName, parameterTypes);
User user = (User)method.invoke(service, args);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.writeInt(user.getAge());
dos.flush();
}
}
客户端直接调用:
package org.example.rpc04;
import java.io.IOException;
import org.example.common.IUserService;
import org.example.common.User;
/**
* 客户端4
* @author tanzhipeng
* @date 2025/05/14 12:45
**/
public class Client {
public static void main(String[] args) throws IOException {
IUserService i = Stub.getStub();
User user = i.getUser(1);
System.out.println(user);
}
}
2.5 封装返回值
在上面的实现中,对于返回值User对象,我们是拆解成一个个属性了,其实可以直接返回一个user对象,这样可以支持任意类型的对象。
package org.example.rpc05;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import org.example.common.IUserService;
import org.example.common.User;
import org.example.rpc01.UserServiceImpl;
/**
* 服务端
**/
public class Server {
private static final int PORT = 8888;
private static final boolean RUNNING = Boolean.TRUE;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
while (RUNNING) {
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
serverSocket.close();
}
private static void process(Socket socket) throws Exception {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(in);
ObjectOutputStream dos = new ObjectOutputStream(out);
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[])ois.readObject();
Object[] args = (Object[])ois.readObject();
// 通过反射调用
IUserService service = new UserServiceImpl();
Method method = service.getClass().getMethod(methodName, parameterTypes);
User user = (User)method.invoke(service, args);
dos.writeObject(user);
dos.flush();
}
}
package org.example.rpc05;
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.Socket;
import org.example.common.IUserService;
import org.example.common.User;
/**
* 代理类
**/
public class Stub {
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("localhost", 8888);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
String methodName = method.getName();
// 获取方法的参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
ObjectInputStream dis = new ObjectInputStream(socket.getInputStream());
User user = (User) dis.readObject();
System.out.println(user.toString());
socket.close();
oos.close();
return user;
}
};
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[]{IUserService.class}, h);
return (IUserService) o;
}
}
到此,我们实现了对于服务端UserService的代理调用,无论UserService怎么新增方法和修改User类,对客户端都没影响。
2.6 提供更好的代理类型服务
随着业务的发展,又出现了新的问题,此时可能有新的类产生,不局限于UserService,此时上面的代理方法能力已经不够了。
这时应该怎么处理?
首先想到的是在getStub方法中,不能只返回IUserService,而是应该返回一个Object。所以我们只需要提供给服务端对应的class。
代码如下:
package org.example.rpc06;
import java.io.IOException;
import org.example.common.IUserService;
/**
* 客户端6
**/
public class Client {
public static void main(String[] args) throws IOException {
IUserService i = Stub.getStub(IUserService.class);
System.out.println(i.getUser(1));
}
}
在Stub中传入class给服务端
package org.example.rpc06;
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.Socket;
import org.example.common.IUserService;
import org.example.common.User;
/**
* 代理类
**/
public class Stub {
public static IUserService getStub(Class classz){
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("localhost", 8888);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
String classzName = classz.getName();
String methodName = method.getName();
// 获取方法的参数类型
Class<?>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(classzName);
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
ObjectInputStream dis = new ObjectInputStream(socket.getInputStream());
User user = (User) dis.readObject();
System.out.println(user.toString());
socket.close();
oos.close();
return user;
}
};
Object o = Proxy.newProxyInstance(classz.getClassLoader(), new Class[]{IUserService.class}, h);
return (IUserService) o;
}
}
对应的在服务端通过传入的class找到相应的类,并执行方法:
package org.example.rpc06;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import org.example.common.User;
import org.example.rpc01.UserServiceImpl;
/**
* 服务端
**/
public class Server {
private static final int PORT = 8888;
private static final boolean RUNNING = Boolean.TRUE;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
while (RUNNING) {
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
serverSocket.close();
}
private static void process(Socket socket) throws Exception {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(in);
ObjectOutputStream dos = new ObjectOutputStream(out);
String className = ois.readUTF();
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[])ois.readObject();
Object[] args = (Object[])ois.readObject();
//从服务注册表找到具体的类,在spring中可以通过spring容器找到对应的实现
Class classzz = UserServiceImpl.class;
Method method = classzz.getMethod(methodName, parameterTypes);
User user = (User)method.invoke(classzz.newInstance(), args);
dos.writeObject(user);
dos.flush();
}
}
上述代码中的实现可以通过Spring容器去找到最终的实现类并执行方法。
2.7 序列化
RPC 能帮助我们的应用透明地完成远程调用,即调用其他服务器的函数就像调用本地方法一样。发起调用请求的那一方叫做调用方,被调用的一方叫做服务提供方。
调用方和服务提供方一般是不同的服务器,所以就需要通过网络来传输数据,并且 RPC 常用于业务系统之间的数据交互,需要保证其可靠性,所以 RPC 一般默认采用 TCP 协议来传输。同时, HTTP 协议也是建立在 TCP 之上的。
网络传输的数据必须是二进制数据,但调用方请求的出入参数都是对象,而对象是肯定没法直接在网络中传输的,需要提前把「对象转成二进制数据」进行网络传输,这个转换过程就做序列化。相反,服务提供方收到网络数据后,需要将「二进制数据转成对象」,这个转换过程就叫做反序列化。
那么常用的序列化方式有哪些呢?
- JDK原生序列化
- Hessian
- Protobuf
- Thrift
- Json
- ...
这么多种序列化方式,如何去选型呢?
首先我们需要考虑的是,对于序列化来说,我们考虑的优先级如下:
安全性 > 通用性 > 兼容性 > 性能 > 效率 > 空间开销
首选Hessian与Protobuf,性能、时间开销、空间开销、通用性、兼容性和安全性上,都满足要求:
- Hessian使用更方便,在对象的兼容性上更好
- Protobuf则更加高效,更通用
2.7.2 JDK
package org.example.rpc07;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import org.example.common.User;
/**
* JDK
**/
public class HelloJDK {
public static void main(String[] args) throws Exception {
User user = new User(1, "LikeCat", 18);
byte[] bytes = serialize(user);
System.out.println(bytes.length);
User user1 = (User) deserialize(bytes);
System.out.println(user1.toString());
}
/**
* 序列化
*/
private static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream output = new ObjectOutputStream(baos);
output.writeObject(o);
output.flush();
byte[] bytes = baos.toByteArray();
baos.close();
output.close();
return bytes;
}
/**
* 反序列化
*/
private static Object deserialize(byte[] bytes) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
ObjectInputStream input = new ObjectInputStream(bais);
Object o = input.readObject();
bais.close();
input.close();
return o;
}
}
2.7.2 Hessian
package org.example.rpc07;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import org.example.common.User;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
/**
* Hessian
**/
public class HelloHessian {
public static void main(String[] args) throws Exception {
User user = new User(1, "LikeCat", 18);
byte[] bytes = serialize(user);
System.out.println(bytes.length);
User user1 = (User) deserialize(bytes);
System.out.println(user1.toString());
}
/**
* 序列化
*/
private static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(baos);
output.writeObject(o);
output.flush();
byte[] bytes = baos.toByteArray();
baos.close();
output.close();
return bytes;
}
/**
* 反序列化
*/
private static Object deserialize(byte[] bytes) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input input = new Hessian2Input(bais);
Object o = input.readObject();
bais.close();
input.close();
return o;
}
}
2.8 把序列化加入到Demo中
首先抽象一个Hessian工具类:
package org.example.rpc09;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
/**
* Hessian工具类
**/
public class HessianUtil {
/**
* 序列化
*/
public static byte[] serialize(Object o) throws Exception {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Hessian2Output output = new Hessian2Output(baos);
output.writeObject(o);
output.flush();
byte[] bytes = baos.toByteArray();
baos.close();
output.close();
return bytes;
}
/**
* 反序列化
*/
public static Object deserialize(byte[] bytes) throws Exception {
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
Hessian2Input input = new Hessian2Input(bais);
Object o = input.readObject();
bais.close();
input.close();
return o;
}
}
封装一个RPC请求:
package org.example.rpc09;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.ToString;
import java.io.Serializable;
/**
* 传输对象
**/
@Data
@AllArgsConstructor
@ToString
public class RpcRequest implements Serializable {
private String className;
private String methodName;
private Class[] parameterTypes;
private Object[] args;
}
服务端:
package org.example.rpc09;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;
import org.example.rpc01.UserServiceImpl;
/**
* 服务端
**/
public class Server {
private static final int PORT = 8888;
private static final boolean RUNNING = Boolean.TRUE;
public static void main(String[] args) throws Exception {
ServerSocket serverSocket = new ServerSocket(PORT);
while (RUNNING) {
Socket socket = serverSocket.accept();
process(socket);
socket.close();
}
serverSocket.close();
}
private static void process(Socket socket) throws Exception {
InputStream in = socket.getInputStream();
OutputStream out = socket.getOutputStream();
//反序列化对象
byte[] bytes = new byte[1024];
in.read(bytes);
RpcRequest rpcRequest = (RpcRequest) HessianUtil.deserialize(bytes);
System.out.println("服务端RPC:" + rpcRequest.toString());
String className = rpcRequest.getClassName();
String methodName = rpcRequest.getMethodName();
Class[] parameterTypes = rpcRequest.getParameterTypes();
Object[] args = rpcRequest.getArgs();
//从服务注册表找到具体的类,在spring中可以通过spring容器找到对应的实现
Class classzz = UserServiceImpl.class;
Method method = classzz.getMethod(methodName, parameterTypes);
//序列化
out.write(HessianUtil.serialize(method.invoke(classzz.newInstance(), args)));
out.flush();
}
}
Stub:
package org.example.rpc09;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import org.example.common.IUserService;
/**
* 代理类
**/
public class Stub {
public static Object getStub(Class classz){
InvocationHandler h = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("localhost", 8888);
OutputStream outputStream = socket.getOutputStream();
String classzName = classz.getName();
String methodName = method.getName();
RpcRequest rpcRequest = new RpcRequest(classzName, methodName, method.getParameterTypes(), args);
//序列化
outputStream.write(HessianUtil.serialize(rpcRequest));
outputStream.flush();
//反序列化
byte[] bytes = new byte[1024];
InputStream inputStream = socket.getInputStream();
inputStream.read(bytes);
Object o = HessianUtil.deserialize(bytes);
System.out.println(o.toString());
socket.close();
return o;
}
};
return Proxy.newProxyInstance(classz.getClassLoader(), new Class[]{IUserService.class}, h);
}
}
package org.example.rpc09;
import java.io.IOException;
import org.example.common.IUserService;
/**
* 客户端
**/
public class Client {
public static void main(String[] args) throws IOException {
IUserService i = (IUserService) Stub.getStub(IUserService.class);
System.out.println(i.getUser(1));
}
}
2.9 网络协议选择
RPC网络协议也不是固定的,主要有TCP、UDP、HTTP协议。
-
基于TCP协议
客户端和服务端建立Socket连接。
客户端通过Socket将需要调用的接口名称、方法名称及参数序列化后传递给服务端。
服务端反序列化后再利用反射调用对应的方法,将结果返回给客户端。 -
基于HTTP协议
客户端向服务端发送请求,如GET、POST、PUT、DELETE等请求。
服务端根据不同的请求参数和请求URL进行方法调用,返回JSON或者XML数据结果。 -
TCP和HTTP对比
基于TCP协议实现的RPC调用,由于是底层协议栈,更佳灵活的对协议字段进行定制,可减少网络开销,提高性能,实现更大的吞吐量和并发数。但,底层复杂,实现代价高。
基于HTTP协议实现的RPC调用,已封装实现序列化,但HTTP属于应用层协议,HTTP传输所占用的字节数比TCP更高,传输效率对比TCP较低。
2.10
一条小咸鱼