目录
1、RPC代码分析1
2、RPC代码分析2
3、RPC代码分析3
4、RPC代码分析4
5、RPC代码分析5
6、RPC代码分析6
7、RPC序列化框架
8、Hessian
参考:https://blog.csdn.net/ss123mlk/article/details/108555850
RPC组成
TCP/IP模拟RPC
01 最基础二进制传递
02 简化客户端的流程 引入stub(客户端存根)
03 使用动态代理生成service类供客户端调用 彻底隐藏所有网络细节
04 引入客户端存根的真正含义-支持多个方法的打包 服务端利用反射解析打包过来的消息并invoke执行
05 服务端支持不同参数的函数的返回结果
06 服务端支持对不同类的不同函数、不同参数的结果返回
总结
从单机都分布式 即分布式通信最基本的是二进制书的TCP/IP传输。
实现功能:根据id查询出用户实体。
1、实体类User
@Data
@AllArgsConstructor
public class User implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
}
2、服务端
通过数据流读出客户端的数据。
将对象通过输出流dos写出到客户端。
public class Server {
private static boolean running = true;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws IOException {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
DataInputStream dis = new DataInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
int id = dis.readInt();
IUserService userService = new UserServiceImpl();
User user = userService.findUserById(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
3、客户端
客户端需要将数据转为二进制才能进行网络传输,故使用ByteArrayOutputStream。通过dos.write(123)将整数123写入baos输出流中,通过baos.toByteArray()将整数转为字节数组。
再通过输入流读入服务端的数据。
public class Client {
/**
* 客户端先将数据写入DataOutputStream中,DataOutputStream包装在
* ByteArrayOutputStream的外边,最终写出的是ByteArrayOutputStream。
**/
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.write(123);
socket.getOutputStream().write(baos.toByteArray());
socket.getOutputStream().flush();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
int id = dis.readInt();
String name = dis.readUTF();
User user = new User(id, name);
System.out.println(user);
dos.close();
socket.close();
}
}
4、Service层
public interface IUserService {
public User findUserById(Integer id);
}
public class UserServiceImpl implements IUserService{
@Override
public User findUserById(Integer id) {
return new User(id,"Alice");
}
}
1、Stub代理类
Stub就是一个代理类,封装了socket底层。开发者只需要关系业务逻辑即可。 问题:此时代理类Stub只能代理一个方法,返回一个对象。
/**
* Stub就是一个代理类,封装了socket底层。开发者只需要关系业务逻辑即可
* 问题:此时代理类Stub只能代理一个方法,返回一个对象。
**/
public class Stub {
public User findUserById(Integer id) throws IOException {
Socket socket = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.write(123);
socket.getOutputStream().write(baos.toByteArray());
socket.getOutputStream().flush();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
int receivedId = dis.readInt();
String name = dis.readUTF();
User user = new User(receivedId, name);
dos.close();
socket.close();
return user;
}
}
2、客户端Client
public class Client {
public static void main(String[] args) throws IOException {
Stub stub = new Stub();
User user = stub.findUserById(123);
}
}
3、服务端Server
public class Server {
private static boolean running = true;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws IOException {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
DataInputStream dis = new DataInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
int id = dis.readInt();
IUserService userService = new UserServiceImpl();
User user = userService.findUserById(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
动态代理:现有一个含有方法的接口IUserService,现要动态产生一个该接口的对象,而该对象称为代理对象。在调用代理对象的方法时,会自动调用InnocationHandler的Invoke方法。
invoke()参数是1、代理的对象;2、调用的方法;3、方法的参数。
1、客户端
客户端需求就是根据id获取User实体,而如果能在客户端直接调用service.findUserById()就更好了。
实现:可以通过动态代理在客户端产生一个IUserService的代理对象,在调用代理对象的findUserById()方法时,会自动调用InvocationHandler【调用处理器】的invoke方法,再该方法的参数中含有【代理对象,实现的方法,方法的参数】,即可以实现在 调用代理对象时进行一些操作。。。
public class Client {
public static void main(String[] args) {
IUserService service = Stub.getStub();
//在findUserById内部加入了网络细节
User userById = service.findUserById(123);
System.out.println(userById);
}
}
2、代理对象
/**
* 代理模式 -- 动态代理【rpc核心之一】
* 通过动态代理,动态产生类
**/
public class Stub {
/**
* 动态产生了,一个实现接口的实现类,当调用实现类内所有方法时,都执行invoke方法。
*
* InvocationHandler调用处理器
**/
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 8888);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
dos.write(123);
socket.getOutputStream().write(baos.toByteArray());
socket.getOutputStream().flush();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
int receivedId = dis.readInt();
String name = dis.readUTF();
User user = new User(receivedId, name);
dos.close();
socket.close();
return user;
}
};
//new代理类对象【参数2是代理类实现的接口】
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},h);
//证明是动态产生的类 com.sun.proxy.$Proxy0
System.out.println(o.getClass().getName());
//interface rpc.test.common.IUserService
System.out.println(o.getClass().getInterfaces()[0]);
return (IUserService)o;
}
}
3、服务端
public class Server {
private static boolean running = true;
public static void main(String[] args) throws IOException {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws IOException {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
DataInputStream dis = new DataInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
int id = dis.readInt();
IUserService userService = new UserServiceImpl();
User user = userService.findUserById(id);
dos.writeInt(user.getId());
dos.writeUTF(user.getName());
dos.flush();
}
}
版本3的问题:动态代理的实现只能调用findUserById()方法,并且参数id写死了也不够灵活。如果接口中有其他方法则不能调用。
实现思路:客户端获取具体需要调用的方法名【saveUser(),findUserById】和方法参数【避免方法重载】并将其传给服务端。
1、客户端
public class Client {
public static void main(String[] args) {
IUserService service = Stub.getStub();
//在findUserById内部加入了网络细节
User userById = service.findUserById(123);
System.out.println(userById);
}
}
2、代理对象
/**
* 代理模式 -- 动态代理【rpc核心之一】
* 通过动态代理,动态产生类
**/
public class Stub {
/**
* 动态产生了,一个实现接口的实现类,当调用实现类内所有方法时,都执行invoke方法。
* InvocationHandler调用处理器
**/
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 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();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
int receivedId = dis.readInt();
String name = dis.readUTF();
User user = new User(receivedId, name);
oos.close();
socket.close();
return user;
}
};
//new代理类对象【参数2是代理类实现的接口】
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},h);
return (IUserService)o;
}
}
3、服务端
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws Exception {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
ObjectInputStream oos = new ObjectInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
String methodName = oos.readUTF();
Class[] parameterTypes = (Class[])oos.readObject();
Object[] args = (Object[])oos.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.flush();
}
}
版本4的问题:服务端中返回值到客户端时,将User拆分后返回,不如很直接返回User对象更好。
实现:返回值用Object封装,支持任意类型。
1、客户端
public class Client {
public static void main(String[] args) {
IUserService service = Stub.getStub();
//在findUserById内部加入了网络细节
User userById = service.findUserById(123);
System.out.println(userById);
}
}
2、代理对象
/**
* 代理模式 -- 动态代理【rpc核心之一】
* 通过动态代理,动态产生类
**/
public class Stub {
/**
* 动态产生了,一个实现接口的实现类,当调用实现类内所有方法时,都执行invoke方法。
* InvocationHandler调用处理器
**/
public static IUserService getStub(){
InvocationHandler h = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 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();
InputStream is = socket.getInputStream();
DataInputStream dis = new DataInputStream(is);
int receivedId = dis.readInt();
String name = dis.readUTF();
User user = new User(receivedId, name);
oos.close();
socket.close();
return user;
}
};
//new代理类对象【参数2是代理类实现的接口】
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},h);
return (IUserService)o;
}
}
3、服务端
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws Exception {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
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);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(user);
dos.flush();
}
}
版本5的问题:客户端中IUserService service = Stub.getStub();表示只能够得到IUserService接口,不能得到其他接口
实现:通过传入xxx.class,得到对应的Object
1、服务端
public class Client {
public static void main(String[] args) {
IProductService service = (IProductService) Stub.getStub(IUserService.class);
System.out.println(service.findProductById(321));
}
}
2、新实体
public interface IProductService {
public Product findProductById(Integer id);
}
public class ProductServiceImpl implements IProductService{
@Override
public Product findProductById(Integer id) {
return new Product(id,"Alice");
}
}
@Data
@AllArgsConstructor
public class Product implements Serializable {
private static final long serialVersionUID = 1L;
private Integer id;
private String name;
}
3、stub
public class Stub {
/**
* 动态产生了,一个实现接口的实现类,当调用实现类内所有方法时,都执行invoke方法。
* InvocationHandler调用处理器
**/
public static Object getStub(Class clazz){
InvocationHandler h = new InvocationHandler(){
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket("127.0.0.1", 8888);
ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
//类名、方法名、参数类型【防止重载】、参数
String clazzName= clazz.getName();
String methodName = method.getName();
Class>[] parameterTypes = method.getParameterTypes();
oos.writeUTF(clazzName);
oos.writeUTF(methodName);
oos.writeObject(parameterTypes);
oos.writeObject(args);
oos.flush();
ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
Object o = ois.readObject();
oos.close();
socket.close();
return o;
}
};
//new代理类对象【参数2是代理类实现的接口】
Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(),new Class[]{IUserService.class},h);
return o;
}
}
4、服务端
public class Server {
private static boolean running = true;
public static void main(String[] args) throws Exception {
ServerSocket ss = new ServerSocket(8888);
while(running){
Socket s = ss.accept();
process(s);
s.close();
}
ss.close();
}
private static void process(Socket s) throws Exception {
InputStream is = s.getInputStream();
OutputStream os = s.getOutputStream();
ObjectInputStream ois = new ObjectInputStream(is);
DataOutputStream dos = new DataOutputStream(os);
String clazzName = ois.readUTF();
String methodName = ois.readUTF();
Class[] parameterTypes = (Class[])ois.readObject();
Object[] args = (Object[])ois.readObject();
Class clazz = null;
//从服务注册表找到具体的类
clazz = UserServiceImpl.class;
Method method = clazz.getMethod(methodName, parameterTypes);
Object o = (Object)method.invoke(clazz.newInstance(), args);
ObjectOutputStream oos = new ObjectOutputStream(os);
oos.writeObject(o);
dos.flush();
}
}
1、java.io.Serializable【jdk效率低】
2、Hessian
3、google protobuf
4、facebook Thrift
5、kyro
6、fst
7、json序列化框架【jaskson】google Gson、Ali FastJson】
8、xmlrpc(xstream)
...
一个 RPC 的核心功能主要有 5 个部分组成,分别是:客户端、客户端 Stub、网络传输模块、服务端 Stub、服务端等。
由服务的调用方与服务的提供方建立 Socket 连接,并由服务的调用方通过 Socket 将需要调用的接口名称、方法名称和参数序列化后传递给服务的提供方,服务的提供方反序列化后再利用反射调用相关的方法。
***将结果返回给服务的调用方,整个基于 TCP 协议的 RPC 调用大致如此。
客户端存根(Client Stub):存放服务端地址信息,将客户端的请求参数数据信息打包成网络消息,再通过网络传输发送给服务端。
client还是调用希望调用的函数 stub却对函数进行了规则化的传递 不在在自己这里处理了
server:
现在支持不同功能的函数经过存根打包 在网络中传输给服务端 那服务端也要对相应的服务进行解析并且返回函数
存根就是网络消息打包存放的地方
现在已经升级为通过动态代理 支持所有的类的所有的函数并且根据类型和参数能够区别重写的函数
返回结果也支持所有的类型
目前就是客户端做调用 然后存根进行打包运输给服务端
服务端解析并且返回对象 存根再将对象写回
server 获取名字
一次 RPC 调用流程如下:
服务消费者(Client 客户端)通过本地调用的方式调用服务。
客户端存根(Client Stub)接收到调用请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息体。
客户端存根(Client Stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
服务端存根(Server Stub)收到消息后进行解码(反序列化操作)。
服务端存根(Server Stub)根据解码结果调用本地的服务进行相关处理
服务端(Server)本地服务业务处理。
处理结果返回给服务端存根(Server Stub)。
服务端存根(Server Stub)序列化结果。
服务端存根(Server Stub)将结果通过网络发送至消费方。
客户端存根(Client Stub)接收到消息,并进行解码(反序列化)。
服务消费方得到最终结果
–
马老师讲的是本机的rpc调用 在微服务框架中微服务应用都是在不同的JVM、不同的内存中
这时候简单的动态代理和反射就可能调用到其他微服务的内存空间里了 甚至会造成并发的一个错乱
所以就引入了 相关的注册中心(Eureka zookeeper) 服务发现 根据ID