RPC理解及手写RPC框架

一:概念

RPC 的全称是 Remote Procedure Call 是一种进程间通信方式。它允许程序调用另一个地址空间(通常是共享网络的另一台机器上)的过程或函数,而不用程序员显式编码这个远程调用的细节。即无论是调用本地接口/服务的还是远程的接口/服务,本质上编写的调用代码基本相同。

二:流程及原理

1:服务提供者A向注册中心注册自身服务(接口及实现类作为参数)

2:服务提供者接口类作为参数,通过动态代理生成一个动态Proxy实现类对象P。

3:动态生成的类P主要功能:socket连接注册中心;向注册中心发送 接口类名称/方法/方法参数 等参数;

4:注册中心接收到客户端发来的 接口类/方法/参数 然后根据接口类名称从已经注册的服务中获取对应的服务;

5:通过反射获取服务的方法,调用方法;

6:返回结果

7:消费者获取结果;

三:常用的RPC框架

1:dubbo

2:springcloud

四:手写RPC框架

package com.test.rpc;

import com.alibaba.fastjson.JSONObject;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;

public class RpcNioTest {
    private static Integer port =8888;
    public static void main(String[] args) throws Exception {
        RpcClientProxy<MyService> clientProxy = new RpcClientProxy(MyService.class,port);
        MyService myService = clientProxy.getObj();
        System.out.println(myService.getVal("123456"));
    }
}

//服务提供者
interface MyService{
    public String getVal(String param);
}

class MyServiceImpl implements MyService{

    @Override
    public String getVal(String param) {
        return "Rpc Test"+param;
    }
}

//注册中心
class MyCenter{
    private static Integer port =8888;

    public static void main(String[] args) throws Exception {
        start();//启动注册中心
    }

    private static Map<String,Class> services = new HashMap<>();//服务列表

    //注册服务
    public void register(Class service,Class impl){
        services.put(service.getName(),impl);
    }

    //启动注册中心
    public static void start() throws Exception {
        NioTest nioTest = new NioTest();
        nioTest.startSocket();
    }

    //socket服务
    static class NioTest{
        public void startSocket() throws Exception {
            //声明一个selector
            Selector selector = Selector.open();
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.bind(new InetSocketAddress(port));
            serverSocketChannel.configureBlocking(false);//非阻塞
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);//注册到selector

            while (true){
                //阻塞消息
                selector.select();
                Set<SelectionKey> selectionKeySet = selector.selectedKeys();//获取所有消息
                Iterator<SelectionKey> it = selectionKeySet.iterator();
                while (it.hasNext()){//遍历所有消息
                    SelectionKey selectionKey = it.next();//拿到消息,删除Set中的信息
                    selectionKeySet.remove(selectionKey);
//                    selector.selectedKeys().remove(selectionKey);
                    //判断消息类型
                    if(selectionKey.isValid() && selectionKey.isAcceptable()){//连接信息
                        //生成一个通道
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector,SelectionKey.OP_READ);
                    }
                    if(selectionKey.isValid() && selectionKey.isReadable()){//读消息
                        SocketChannel channel = (SocketChannel) selectionKey.channel();
                        ByteBuffer byteBuffer = ByteBuffer.allocate(2048);
                        channel.read(byteBuffer);
                        String msg = new String(byteBuffer.array()).trim();
                        System.out.println(channel.hashCode()+"客户端消息:"+msg);
                        JSONObject obj = JSONObject.parseObject(msg);
                        //消息类型
                        String msgType = obj.getString("msgType");
                        if("rpc".equals(msgType)){
                            String className = obj.getString("className");
                            String methodName = obj.getString("methodName");
                            Class<?>[] parameterTypes = getPTypes(obj);
                            Object[] arguments = obj.getString("arguments").split(",");//获取客户端发来的参数
                            //从注册中心中查找服务
                            Class service = services.get(className);
                            Method method = service.getMethod(methodName,parameterTypes);
                            Object res = method.invoke(service.newInstance(),arguments);
                            //返回客户端结果
                            channel.write(ByteBuffer.wrap(res.toString().getBytes(StandardCharsets.UTF_8)));
                        }
                    }
                }

            }
        }

        private Class<?>[] getPTypes(JSONObject obj) throws ClassNotFoundException {
            String []types=obj.getString("types").split(",");
            Class<?> []res = new Class[types.length];
            for(int i=0;i<types.length;i++){
                String type = types[i];
                Class clz = Class.forName(type);
                res[i] = clz;
            }
            return res;
        }
    }
}

//被代理的RPC对象
class RpcClientProxy<T> {
    private static Class service;
    private static Integer port;
    public RpcClientProxy(Class service,Integer port){
        this.service=service;
        this.port = port;
    }
    public T getObj(){
        T obj = (T) Proxy.newProxyInstance(service.getClassLoader(),new Class[]{service},new RpcProxy());
        return obj;
    }

    static class RpcProxy implements InvocationHandler {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Socket socket = null;
            ObjectOutputStream output = null;
            ObjectInputStream input = null;
            /**
             * 下面是动态生成的代理类做的动作
             * 1:socket连接注册中心
             * 2:向注册中心发送接口类,方法,参数等
             * 3:注册中心收到上述信息,根据上述信息的接口类名称参数  获取注册到该注册中心的服务
             * 4:通过反射获取服务的方法,调用
             * 5:接收注册中心调用接口返回的结果
             */
            try {
                // 2.创建Socket客户端,根据指定地址连接远程服务提供者
                socket = new Socket();
                socket.connect(new InetSocketAddress("127.0.0.1", port));
                output = new ObjectOutputStream(socket.getOutputStream());
                // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
                JSONObject param = new JSONObject();
                param.put("msgType","rpc");
                param.put("className",service.getName());
                param.put("methodName",method.getName());
                param.put("arguments",args);
                param.put("types","java.lang.String");
                /*output = new ObjectOutputStream(socket.getOutputStream());
                output.writeUTF(service.getName());//向注册中心发接口类
                output.writeUTF(method.getName());//向注册中心发送方法名
                output.writeObject(method.getParameterTypes());//向注册中心发送参数类型
                output.writeObject(args);//向注册中心发送方法参数*/
                output.write(param.toJSONString().getBytes());
                // 4.同步阻塞等待服务器返回应答,获取应答后返回
                input = new ObjectInputStream(socket.getInputStream());//等待注册中心返回结果
                Object obj = input.readObject();
                return obj;
            } finally {
                if (socket != null) socket.close();
                if (output != null) output.close();
                if (input != null) input.close();
            }
        }
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值