Remote procedure call-RPC

What is RPC?

RPC (Remote Procedure Call) remote procedure call

The origin of RPC

Why is there such a thing as rpc? It is also because of the evolution of web service technology architecture. In the earliest days, web applications were monolithic architectures. If you want to use service services in actions, you can get the attributes of the service objects directly by adding a new service object in the action code and calling service. Object method.

But the times have changed. Because the monolithic architecture cannot support the increasing traffic, the monolithic architecture must be disassembled into a service-oriented architecture (SOA). After such a toss, the action and service classes may not be in one JVM. Not on a server anymore. But what remains unchanged is that action still needs to call the method in the service. What should we do? At this time, we need a tool that allows us to remotely call the service method on another server over the network. This tool is the RPC framework. The simple understanding is that a node requests services provided by another node .

Why not use Http protocol to transmit data?

Since HTTP is completed in the application layer, the cost of the entire communication is relatively high. In the remote procedure call, the remote call is directly based on TCP, and the data transmission is completed in the TCP layer of the transport layer. It is more suitable for scenarios with higher efficiency requirements. RPC mainly depends on the customer. Socket connection is established between the server and the server, and the underlying implementation is more complicated than REST.

RPC remote call process

  1. The client tells the server the function ID that needs to be called ( there is a mapping between the function and the process ID, which is stored in the Map )
  2. Transfer the ID and input parameters of the calling function (serialized and converted to byte stream) to the server
  3. The server accepts serialized data and deserializes it into an object
  4. The server executes the corresponding function according to the function ID for logical processing
  5. The server serializes the returned result and sends it to the client
  6. Client processing returns to the browser

Problems caused by remote calls

When calling remotely, the body of the function we need to execute is on the remote machine, which brings several new problems:

  1. Call ID mapping . How do we tell the remote machine which method we want to call? In the local call, the function body is directly specified by the function pointer. When we call a function, the compiler will automatically call its corresponding function pointer for us. But in remote calls, function pointers are not acceptable, because the address spaces of the two processes are completely different. Therefore, in RPC, all functions must have their own ID. This ID is uniquely determined in all processes. The client must attach this ID when making a remote procedure call. Then we need to maintain a {function<–> Call ID} correspondence table on the client and server respectively. The two tables do not necessarily need to be exactly the same, but the Call ID corresponding to the same function must be the same. When the client needs to make a remote call, it checks this table to find the corresponding Call ID, and then passes it to the server. The server also checks the table to determine the function that the client needs to call, and then executes the corresponding The code of the function.
  2. Serialization and deserialization . How does the client pass the parameter value to the remote function? In the local call, we only need to push the parameters onto the stack, and then let the function read it from the stack. But in the remote procedure call, the client and server are different processes, and parameters cannot be passed through memory. Even sometimes the client and server are not using the same language (for example, the server uses C++, the client uses Java or Python). At this time, the client needs to first convert the parameters into a byte stream, and then transfer the byte stream to a format that can be read by itself. This process is called serialization and deserialization. Similarly, the value returned from the server also needs to be serialized and deserialized.
  3. Network transmission . Remote calls are often used on the network, and the client and server are connected through the network. All data needs to be transmitted over the network, so a network transmission layer is needed. The network transport layer needs to stream the Call ID and serialized parameter bytes to the server, and then send the serialized call result back to the client. As long as it can accomplish both, it can be used as a transport layer. Therefore, the protocol it uses is actually unlimited, as long as the transmission can be completed. Although most RPC frameworks use TCP protocol, UDP is also available, while gRPC simply uses HTTP2. Java's Netty also belongs to this layer.
// Client端 
//    Student student = Call(ServerAddr, addAge, student)
4. 将这个调用映射为Call ID。
5. 将Call ID,student(params)序列化,以二进制形式打包
6.2中得到的数据包发送给ServerAddr,这需要使用网络传输层
7. 等待服务器返回结果
8. 如果服务器调用成功,那么就将结果反序列化,并赋给student,年龄更新

// Server端
9. 在本地维护一个Call ID到函数指针的映射call_id_map,可以用Map<String, Method> callIdMap
10. 等待服务端请求
11. 得到一个请求后,将其数据包反序列化,得到Call ID
12. 通过在callIdMap中查找,得到相应的函数指针
13. 将student(params)反序列化后,在本地调用study()函数,得到结果
14. 将student结果序列化后通过网络返回给Client

Code to implement RPC communication

First look at the directory structure
Insert picture description here

  • order-api is the common interface definition between microservices
  • order-provide is the order system microservice
  • user-service is the user system microservice

Now the user service needs to call the method in the order service, the code shows


Public interface

IOrderService

public interface IOrderService {
    
    

    String queryOrderList();

    String orderById(String id);
}

RpcRequest

package com.xhc.example;

import java.io.Serializable;


public class RpcRequest implements Serializable{
    
    

    private String className;
    private String methodName;
    private Object[] args;
    private Class[] types;

    public String getClassName() {
    
    
        return className;
    }

    public void setClassName(String className) {
    
    
        this.className = className;
    }

    public String getMethodName() {
    
    
        return methodName;
    }

    public void setMethodName(String methodName) {
    
    
        this.methodName = methodName;
    }

    public Object[] getArgs() {
    
    
        return args;
    }

    public void setArgs(Object[] args) {
    
    
        this.args = args;
    }

    public Class[] getTypes() {
    
    
        return types;
    }

    public void setTypes(Class[] types) {
    
    
        this.types = types;
    }
}


Order Center
Bootstrap

package com.xhc.example;


public class Bootstrap {
    
    


    public static void main(String[] args) {
    
    
        //SPRING BOOT
        IOrderService orderService=new OrderServiceImpl();
        RpcProxyServer rpcProxyServer=new RpcProxyServer();
        rpcProxyServer.publisher(orderService,8080);
    }
}

OrderServiceImpl

package com.xhc.example;


public class OrderServiceImpl implements IOrderService{
    
    

    @Override
    public String queryOrderList() {
    
    
        return "我是 queryOrderList 方法,是的没错!";
    }

    @Override
    public String orderById(String id) {
    
    
        return "我是 orderById 方法,是的没错!";
    }
}

ProcessorHandler

package com.xhc.example;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket;


public class ProcessorHandler implements Runnable{
    
    

    private Socket socket;
    private Object service;

    public ProcessorHandler(Socket socket, Object service) {
    
    
        this.socket = socket;
        this.service = service;
    }

    @Override
    public void run() {
    
    
        ObjectInputStream inputStream=null;
        ObjectOutputStream outputStream=null;
        try {
    
    
            inputStream=new ObjectInputStream(socket.getInputStream());//?
            RpcRequest request=(RpcRequest)inputStream.readObject(); //反序列化
            Object rs=invoke(request);
            System.out.println("服务端的执行结果:"+rs);
            outputStream=new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(rs);
            outputStream.flush();
        } catch (Exception e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            //TODO 关闭流
            if(inputStream!=null){
    
    
                try {
    
    
                    inputStream.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
            if (outputStream!=null){
    
    
                try {
    
    
                    outputStream.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }
    }

    private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {
    
    
        //通过反射进行服务的调用
        Class clazz=Class.forName(request.getClassName());
        //找到目标方法
        Method method=clazz.getMethod(request.getMethodName(),request.getTypes());
        return method.invoke(service,request.getArgs());
    }
}

RpcProxyServer

package com.xhc.example;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class RpcProxyServer {
    
    

    private final ExecutorService executorService= Executors.newCachedThreadPool();

    public void publisher(Object service,int port){
    
    
        ServerSocket serverSocket=null;
        try {
    
    
            serverSocket=new ServerSocket(port);
            while(true){
    
    
                Socket socket=serverSocket.accept(); //监听客户端请求
                executorService.execute(new ProcessorHandler(socket,service));
            }
        } catch (IOException e) {
    
    
            e.printStackTrace();
        }finally {
    
    
            if(serverSocket!=null){
    
    
                try {
    
    
                    serverSocket.close();
                } catch (IOException e) {
    
    
                    e.printStackTrace();
                }
            }
        }

    }
}


User Center
App

package com.xhc.example.rcp;

import com.xhc.example.IOrderService;

/**
 * Hello world!
 *
 */
public class App 
{
    
    
    public static void main( String[] args ){
    
    
        RpcProxyClient rpcProxyClient=new RpcProxyClient();
        IOrderService orderService=rpcProxyClient.clientProxy(IOrderService.class,"localhost",8080);

        System.out.println(orderService.queryOrderList());
        System.out.println(orderService.orderById("Mic"));

    }
}

RemoteInvocationHandler

package com.xhc.example.rcp;

import com.xhc.example.RpcRequest;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;


public class RemoteInvocationHandler implements InvocationHandler{
    
    
    private String host;
    private int port;

    public RemoteInvocationHandler(String host, int port) {
    
    
        this.host = host;
        this.port = port;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    
        //先建立远程连接
        RpcNetTransport rpcNetTransport=new RpcNetTransport(host,port);
        //传递数据了?
        // 调用哪个接口、 哪个方法、方法的参数?
        RpcRequest request=new RpcRequest();
        request.setArgs(args);
        request.setClassName(method.getDeclaringClass().getName());
        request.setTypes(method.getParameterTypes()); //参数的类型
        request.setMethodName(method.getName());
        return rpcNetTransport.send(request);
    }
}

RpcNetTransport

package com.xhc.example.rcp;

import com.xhc.example.RpcRequest;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;


public class RpcNetTransport {
    
    

    private String host;
    private int port;

    public RpcNetTransport(String host, int port) {
    
    
        this.host = host;
        this.port = port;
    }

    public Socket newSocket() throws IOException {
    
    
        Socket socket=new Socket(host,port);
        return socket;
    }

    public Object send(RpcRequest request){
    
    
        ObjectOutputStream outputStream=null;
        ObjectInputStream inputStream=null;
        try {
    
    
            Socket socket=newSocket();
            //IO操作
            outputStream=new ObjectOutputStream(socket.getOutputStream());
            outputStream.writeObject(request);
            outputStream.flush();
            inputStream=new ObjectInputStream(socket.getInputStream());
            return inputStream.readObject();
        } catch (IOException e) {
    
    
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            
        }
        return null;
    }
}

RpcProxyClient

package com.xhc.example.rcp;

import java.lang.reflect.Proxy;


public class RpcProxyClient {
    
    

    public <T> T clientProxy(final Class<T> interfaceCls,final String host,final int port){
    
    
        return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[]{
    
    interfaceCls},new RemoteInvocationHandler(host,port));
    }
}

Guess you like

Origin blog.csdn.net/nonage_bread/article/details/111311747