Rpc了解

1、为什么要有rpc?

因为微服务之间需要进行服务间的通信,不同服务之间的接口要互相调用。而常见的通信协议主要有 RPC 和 REST 协议

使用rpc的好处是:

引入RPC框架对我们现有的代码影响最小,同时又可以帮我们实现架构上的扩展

两者对比

2、什么是Rpc

Remote Procdeure Call ,即远程过程调用,是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络实现的技术。

RPC 是一种进程间通信方式,允许像调用本地服务一样调用远程服务,只要是像调本地接口一样调用远程接口的方式,就是rpc.

例如微服务中有订单服务需要调用用户服务获取用户信息,之前的方法可能是通过http请求来访问接口,获取信息,现在调用时,只需要知道获取用户信息的接口的类名、方法名,传入相应参数,就可以获取到对应的用户信息。

3、Rpc流程

大部分的RPC框架都遵循如下三个开发步骤:

1. 定义一个接口说明文件:描述了对象(结构体)、对象成员、接口方法等一系列信息;
2. 通过RPC框架所提供的编译器,将接口说明文件编译成具体的语言文件;
3. 在客户端和服务器端分别引入RPC编译器所生成的文件,即可像调用本地方法一样调用服务端代码;

客户端如何知道服务端有哪些方法,就是通过引入编译器生成的文件知道的。

  1. 客户端以本地调用方式发起请求,请求内容包括:远程方法在本地的模拟对象,方法名,方法参数,将请求内容包装后的信息通过网络发送到服务端。
  2. 服务端收到消息后,进行解码为实际的方法名和参数
  3. 服务端本地服务执行并将结果返回
  4. 服务端将返回结果打包成消息并发送至客户端
  5. 客户端收到消息后,解码,获取响应内容

4、Rpc使用到的核心技术

  1. 代理,为什么要有代理,因为客户端只管发送请求,获取响应,至于过程如何不关心,所以需要一个代理来进行中间过程的封装。
  2. 反射,服务端收到请求后,需要通过反射机制来获取要调用的方法的对象,执行对应的方法
  3. 序列化  ,网络传输过程中需要先将数据进行序列化才能进行传输,接收到数据后需要将数据进行反序列化才能进行下一步操作。

5、一个简单的RpcDemo

发起服务端

package com.ljx.splearn.RpcDemo;

public class Main{

    public static void main(String[] args) throws Exception{
        //RPC调用,客户端获取要调用的服务并发起调用
        UserService service=(UserService)Client.getStub(UserService.class);
        User user = service.findUserByID(222);
        System.out.println(user.getName());

    }
}

client实现,重要类

package com.ljx.splearn.RpcDemo;

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;

public class Client{
    //远程调用类
    public static Object getStub(final Class clazz) throws Exception{
        InvocationHandler handler = new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //通过socket将请求发送到服务方
                Socket socket = new Socket("127.0.0.1", 8888);
                ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
                String className = clazz.getName();
                String methodName = method.getName();
                Class[] parametersTypes = method.getParameterTypes();
                oos.writeUTF(className);
                oos.writeUTF(methodName);
                oos.writeObject(parametersTypes);
                oos.writeObject(args);
                oos.flush();
                ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());
                Object o = ois.readObject();
                oos.close();
                socket.close();
                return o ;
            }
        };
        //通过代理实现方法调用
        Object o = Proxy.newProxyInstance(clazz.getClassLoader(),
                new Class[]{clazz},handler);
        return o;
    }
}

服务端启动一个socket,接收传递过来的请求数据,通过反射获取方法名和参数,并调用方法返回结果

package com.ljx.splearn.RpcDemo;

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;

/**
 * 服务端
 */
public class Server {
    private static boolean running = true;
    public static void main(String[] args) throws  Exception{
        //启动一个socket方法
        ServerSocket serverSocket = new ServerSocket(8888);
        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);
        
        // 拿到客户端传递过来的class
        String clazzName =ois.readUTF();
        String methodName =ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args =(Object[])ois.readObject();
        //反射拿到class
        //通过反射可以获取所有的接口类
        Class clazz =Class.forName(clazzName);
        if(clazz.isInterface()){
            if(clazzName.equals("com.ljx.splearn.RpcDemo.UserService")){
                clazz = UserServiceImpl.class;
            }
        }
        //使用反射获取结果
        Method method = clazz.getMethod(methodName,parameterTypes);
        Object object = method.invoke(clazz.newInstance(),args);
        //通过socket将结果返回
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(object);
        oos.flush();
    }
}

服务调用的接口和实体类

package com.ljx.splearn.RpcDemo;

import lombok.AllArgsConstructor;
import lombok.Data;

import java.io.Serializable;

/**
 * @author lijianxi
 * @date 2023年01月16日 10:40 上午
 */
@Data
@AllArgsConstructor
public class User implements Serializable {
    Integer id;
    String name;

}

package com.ljx.splearn.RpcDemo;

public interface UserService {
    public User findUserByID(Integer id);
}

package com.ljx.splearn.RpcDemo;

public class UserServiceImpl implements UserService {
    @Override
    public User findUserByID(Integer id) {
        return new User(id, "ljx");
    }

 
}

启动server,调用main

结果如下

 6、RPC和HTTP、Restful区别

    1、RPC是远程过程调用,只是对不同应用间相互调用的一种描述,它是一种思想。实现方式可以是最直接的TCP通信,也可以是HTTP方式。
    2、HTTP是应用层的一种协议,负责服务之间的通信操作。
    3、dubbo是基于tcp通信的,gRPC是Google公布的开源软件,基于最新的HTTP2.0协议,底层使用到了Netty框架的支持。

REST是一种架构风格,指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是 RESTful

REST基于HTTP协议(应用层)超文本传输协议。

通过HTTP协议中的POST/GET/PUT/DELETE等方法和一个可读性强的URL来提供一个http请求。

RPC 适用于内网服务调用,对外提供服务请走 REST。

IO 密集的服务调用用 RPC,低频服务用 REST

服务调用过于密集与复杂,RPC 就比较适用

 
 

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值