RPC基本原理与实践

什么是RPC

RPC即远程过程调用(Remote Procedure Call),通过RPC可以实现在本地对远程服务的调用,用于分布式系统中不同服务间的调用。比如订单服务、仓库服务部署在两台机器上,创建订单时需要查询仓库中的库存,此时就可以使用RPC。

为什么要用RPC

就如上面所述,查询库存,直接发送REST请求不就可以了,为什么要用RPC。REST请求使用HTTP协议实现,而RPC则不一定使用HTTP。比如常用的RPC框架Dubbo支持的协议就有dubbo、rmi、http等,这里是Dubbo支持的协议,感兴趣的可以看一下 。RPC 可以获得更好的性能(省去了 HTTP 报头等一系列东西),记得看过之前看过一篇测试的文章,dubbo协议的RPC请求的效率是HTTP请求的1.5~2倍左右。

RPC原理及简单RPC框架Demo

在这里插入图片描述
1、客户端调用服务端提供的服务,客户端需要拿到服务端注册发布的接口。
2、客户端调用接口的时候通过网络传输数据到服务端(协议就是用于网络层)。
3、服务端处理请求后,返回给客户端结果。

接口定义及实现

public interface IHello {
    String hello(String msg);
}

暴露服务的接口,服务端及客户端都有

public class HelloImpl implements IHello {
    public HelloImpl() {}

    @Override
    public String hello(String msg) {
        return "Hello," + msg;
    }
}

接口实现,只有服务端有

客户端

客户端需要与服务端建立网络连接,并传输数据,接收返回结果。对于客户端来说,只关心调用的是接口的什么方法及参数,数据传输由框架完成。这里用到JDK的动态代理,调用服务接口时,代理类进行处理。

RpcProxy

public class RpcProxy {

    public static Object getClientProxy(String serviceName, Class<?> service, String host, int port) {
        return Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
                new RpcInvocationHandler(serviceName, host, port));
    }
}

RpcInvocationHandler

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

    /**
     * 使用socket连接,传递需调用的服务、方法、参数信息到服务端
     * 返回调用结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //建立连接
        Socket socket = null;
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;
        Object result = null;
        
        RpcRequest rpcRequest = new RpcRequest(service, method.getName(), args);
        try {
            socket = new Socket(host, port);
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rpcRequest);
            objectOutputStream.flush();

            objectInputStream = new ObjectInputStream(socket.getInputStream());
            result = objectInputStream.readObject();
        } catch (Exception e) {
            System.out.println("远程调用失败");
        } finally {
            if (null != objectInputStream) {
                objectInputStream.close();
            }
            if (null != objectOutputStream) {
                objectOutputStream.close();
            }
            if (null != socket) {
                socket.close();
            }
        }
        return result;
    }

RpcRequest:网络传输封装的对象,实现序列化接口。

    public RpcRequest(String serviceName, String methodName, Object[] args) {
        this.serviceName = serviceName;
        this.methodName = methodName;
        this.args = args;
    }

ClientDemo

public class ClientDemo {
    private static final String host = "127.0.0.1";
    private static final int port = 9999;

    public static void main(String[] args) {

        IHello helloService = (IHello) RpcProxy.getClientProxy("HelloService",
                IHello.class, host, port);
        System.out.println(helloService.hello("RPC"));
    }
}

服务端

服务端注册服务,监听网络连接,处理请求后返回结果。

public class Server {
    //serviceName -> serviceImpl
    private Map<String, Object> services;
    private int port;
    private ExecutorService executorService;

    public Server(int port) {
        services = new HashMap<>();
        this.port = port;
        executorService = Executors.newCachedThreadPool();
    }

    /**
     * 服务发布
     */
    public void register(String serviceName, Object service) {
        if (service != null) {
            services.put(serviceName, service);
            System.out.println("服务注册成功");
        }
    }

    /**
     * 监听
     * 服务处理
     * */
    public void start() {
        ServerSocket serverSocket = null;
        Socket socket = null;
        try {
            serverSocket = new ServerSocket(port);
            System.out.println("服务启动");
            for(;;) {
                socket = serverSocket.accept();
                executorService.submit(new RequestProcessor(socket, services));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != serverSocket) {
                try {
                    serverSocket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != socket) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

RequestProcessor 请求处理线程

public class RequestProcessor implements Runnable {
    private Socket socket;
    private Map services;

    public RequestProcessor(Socket socket, Map services) {
        this.socket = socket;
        this.services = services;
    }

    @Override
    public void run() {
        System.out.println("开始处理请求");
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            RpcRequest request = (RpcRequest) objectInputStream.readObject();
            Object[] args =request.getArgs();
            Class<?>[] paraTypes = new Class[args.length];
            for (int i = 0 ; i < args.length; i++) {
                paraTypes[i] = args[i].getClass();
            }
            Object service = services.get(request.getServiceName());
            if (null != service) {
                Method method = service.getClass().getMethod(request.getMethodName(), paraTypes);
                Object object = method.invoke(service, request.getArgs());
                objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                objectOutputStream.writeObject(object);
                objectOutputStream.flush();
                System.out.println("处理完毕");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (null != objectInputStream) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != objectOutputStream) {
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (null != socket) {
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

RpcServerDemo

public class RpcServerDemo {
    public static void main(String[] args) {
        IHello helloService = new HelloImpl();
        Server server = new Server(9999);
        server.register("HelloService", helloService);
        server.start();
    }
}

启动服务端后,启动客户端
客户端输出:Hello,RPC
服务端输出:服务注册成功
服务启动
开始处理请求
处理完毕

至此,一个简单的RPC实现就完成了。

思考:

1、客户端接口代理的通过Spring的AOP实现?
2、客户端储存着服务端的IP地址及端口信息,为了提高系统吞吐量,服务端会有多个提供者,客户端怎么高效的、动态的感知和存储服务端的网络信息;服务的负载均衡?
3、服务端的接口实现通过Spring实现注入和管理?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Netty是一个基于NIO的高性能网络通信框架,它的核心原理是通过事件驱动和异步处理机制来实现高效的网络传输。其特点是可扩展、可重用、易于维护、性能卓越。 RPC(Remote Procedure Call)是一种基于网络通信的分布式计算模型,通过这种模型,客户端调用服务端的远程方法就像调用本地方法一样简单。在RPC实践过程中,使用Netty框架可以有效提升RPC的性能和稳定性。 Netty的核心原理是基于事件驱动的模型,它的主要组成部分包括NioEventLoop、Channel、Buffer、Codec等。NioEventLoop是Netty中的核心组件之一,它是一个事件循环线程,通过不断遍历注册在它上面的Channel监听器,来处理网络传输过程中的事件,从而实现网络的异步非阻塞传输。 在RPC实践中,Netty通过对协议进行编解码,来实现远程方法的调用和响应过程。通常情况下,客户端与服务端之间需要使用一种协议来进行通信,需要对协议进行编解码处理,这个过程需要在客户端和服务端都实现一遍,使用Netty框架可以简化这个过程,使得开发人员只需要实现协议的编解码策略即可。 总之,Netty作为高性能网络通信框架,其核心原理是基于事件驱动的机制实现的。在RPC实践中,Netty可以高效地实现协议的编解码,提升RPC的性能和稳定性,因此在分布式计算中得到了广泛的应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值