涉及技术
 
序列化、Socket通信、Java动态代理技术,反射机制
 
角色
 
1、服务提供者:运行在服务端,是真实的服务实现类
 
2、服务发布监听者:运行在RPC服务端,1将服务端提供的服务暴露为远程服务并2监听客户端请求3调用真实服务
 
3、客户端代理:运行在RPC客户端,通过该代理调用远程服务提供者,将结果封装返回本地消费者
 
4、客户端消费者:委托客户端代理实现透明的RPC调用
 
代码实现
 
(1)服务提供者代码实现
 
接口
public interface IRealService {
    public void sayHello();
}
 
实现类
public class RealServiceImpl implements IRealService {
    @Override
    public void sayHello() {
        System.out.println("Hello,Client!");
    }
}
 
 
 
(2)服务发布监听者
 
package com.zerone.rpcdemo;
 
 
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
 
 
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
 
 
/**
* Created by Andy ye on 2019/4/12.
*
* @author Andy
*/
public class RpcServiceListenExecutor {
 
 
    static ThreadPoolTaskExecutor threadPoolTaskExecutor;
 
 
    /**
     * 开启Socket,监听RPC请求
     *
     * @param hostName
     * @param port
     * @throws IOException
     */
    public static void listenAndExecute(String hostName, int port) throws IOException {
 
 
        //1、利用springframework创建线程池
        RejectedExecutionHandler rejectedExecutionHandler = new RejectedExecutionHandler() {
            @Override
            public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
                new ThreadPoolExecutor.AbortPolicy();
            }
        };
        threadPoolTaskExecutor = new ThreadPoolTaskExecutor();
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setMaxPoolSize(100);
        threadPoolTaskExecutor.setQueueCapacity(2000);
        threadPoolTaskExecutor.setRejectedExecutionHandler(rejectedExecutionHandler);
        threadPoolTaskExecutor.initialize();
 
 
        //2绑定socket服务,监听socket客户端请求
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(hostName, port));
 
 
        //3当客户端代理请求到达时,接收socket客户端请求,并开启一个执行真实服务调用的线程
        while (true) {
            threadPoolTaskExecutor.execute(new PublishTask(serverSocket.accept()));
        }
    }
 
 
 
 
    /**
     * 接收客户端代理的RPC请求,反序列化数据对象,实现真实的服务调用
     */
    public static class PublishTask implements Runnable {
 
 
        Socket socket = null;
 
 
        public PublishTask(Socket socket) {
            this.socket = socket;
        }
 
 
        @Override
        public void run() {
 
 
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
 
 
            try {
 
 
                //socket.getInputStream() 是读取和客户端代理发送过来的序列化对象
                //该对象包含了本次服务调用的类名 方法名 参数类型 和 参数值
                inputStream = new ObjectInputStream(socket.getInputStream());
 
 
                // 反序列化数据
                // 读取接口名 方法名称 参数类型 参数值
                String interfaceName = inputStream.readUTF();
                String methodName = inputStream.readUTF();
                Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();
                Object[] args = (Object[]) inputStream.readObject();
 
 
                //通过反射实例化该类对象
                Class<?> service = Class.forName(interfaceName);
 
 
                //通过类对象和方法名 参数类型获取本类要执行的具体方法
                Method method = service.getMethod(methodName, parameterTypes);
 
 
                //调用该方法(在这里利用反射实现真实类的方法实际调用) 返回结果
                Object result = method.invoke(service.newInstance(), args);
                outputStream = new ObjectOutputStream(socket.getOutputStream());
                outputStream.writeObject(result);
 
 
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (inputStream != null) {
                    try {
                        inputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (outputStream != null) {
                    try {
                        outputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                if (socket != null) {
                    try {
                        socket.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
 
 
(3)客户端代理代码实现
 
package com.zerone.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.InetSocketAddress;
import java.net.Socket;
 
 
/**
* Created by Andy ye on 2019/4/12.
* S 代表了调用的真实服务类实现的接口
*/
public class RpcClientProxy<S> {
 
 
    public S callRealService(final Class<?> serviceClass, final InetSocketAddress addr) {
 
 
        //返回的是真实服务类实现的接口
        return (S) Proxy.newProxyInstance(
                serviceClass.getClassLoader(),
                new Class<?>[]{serviceClass.getInterfaces()[0]},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = null;
                        ObjectOutputStream outputStream = null;
                        ObjectInputStream inputStream = null;
 
 
                        try {
 
 
                            //建立socket连接客户端
                            socket = new Socket();
                            socket.connect(addr);
 
 
                            // 构造服务调用数据 接口 方法 参数类型 参数值
                            outputStream = new ObjectOutputStream(socket.getOutputStream());
                            outputStream.writeUTF(serviceClass.getName());
                            outputStream.writeUTF(method.getName());
                            outputStream.writeObject(method.getParameterTypes());
                            outputStream.writeObject(args);
 
 
                            // 序列化数据对象,以便将其发送到RPC监听服务器
                            inputStream = new ObjectInputStream(socket.getInputStream());
                            return inputStream.readObject();
 
 
                        } catch (Exception e) {
                            e.printStackTrace();
                            return null;
                        } finally {
                            if (socket != null) {
                                socket.close();
                            }
                            if (outputStream != null) {
                                outputStream.close();
                            }
                            if (inputStream != null) {
                                inputStream.close();
                            }
                        }
 
 
                    }
                }
        );
    }
}
 
 
 
(4)测试消费者调用RPC
 
package com.zerone.rpcdemo;
 
 
import java.net.InetSocketAddress;
 
 
/**
* Created by Andy ye on 2019/4/12.
*
* @author Andy
*/
public class ClientTest {
 
 
    public static void main(String[] args) {
 
 
        //开启一个RPC服务监听处理器
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    RpcServiceListenExecutor.listenAndExecute("localhost", 8088);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
 
 
        //创建客户端代理,构造RPC请求参数,发起RPC调用
        RpcClientProxy<IRealService> proxy = new RpcClientProxy<IRealService>();
        IRealService realService = proxy.callRealService(RealServiceImpl.class,
                new InetSocketAddress("localhost", 8088));
        realService.sayHello();
    }
}
 
 
结果:
 
 
 

 
 
一个细节:当我使用Executors创建线程池时,提示我通过ThreadPoolExcutor的方式去创建会避免资源消耗殆尽的风险
ThreadPoolExcutor可以指定线程的最小创建数量和最大创建数量,以此来控制线程池占用的内存
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

【RPC】手撸一个简单的RPC框架实现的更多相关文章

  1. 徒手撸一个简单的RPC框架

    来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...

  2. 手写一个简单到SpirngMVC框架

    spring对于java程序员来说,无疑就是吃饭到筷子.在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色.自己对spring有一定的自我见解, ...

  3. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  4. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  5. 动手实现一个简单的 rpc 框架到入门 grpc (上)

    rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法.我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机.不同语言中的方法时就需要借助 rpc 来实 ...

  6. 自己用 Netty 实现一个简单的 RPC

    目录: 需求 设计 实现 创建 maven 项目,导入 Netty 4.1.16. 项目目录结构 设计接口 提供者相关实现 消费者相关实现 测试结果 总结 源码地址:github 地址 前言 众所周知 ...

  7. 如何实现一个简单的RPC

    在如何给老婆解释什么是RPC中,我们讨论了RPC的实现思路. 那么这一次,就让我们通过代码来实现一个简单的RPC吧! RPC的实现原理 正如上一讲所说,RPC主要是为了解决的两个问题: 解决分布式系统 ...

  8. 教你用 Netty 实现一个简单的 RPC!

    众所周知,dubbo 底层使用了 Netty 作为网络通讯框架,而 Netty 的高性能我们之前也分析过源码,对他也算还是比较了解了. 今天我们就自己用 Netty 实现一个简单的 RPC 框架. 1 ...

  9. 手撸基于swoole 的分布式框架 实现分布式调用(20)讲

    最近看的一个swoole的课程,前段时间被邀请的参与的这个课程 比较有特点跟一定的深度,swoole的实战教程一直也不多,结合swoole构建一个新型框架,最后讲解如何实现分布式RPC的调用. 内容听 ...

随机推荐

  1. Ubuntu下常用指令

    James最近因为需要尝试着编译Android源代码,系统环境变成了Ubuntu.和熟悉Windows操作系统环境一样,都有过渡的阶段.下面记录常用的操作指令. 分类目录如下: uname -a 查看 ...

  2. [leetcode] 329. Longest Increasing Path in a Matrix My Submissions Question

    在递归调用的函数中使用了max = INT_MIN,结果报超时错误,改为max=0就对了,虽然在这题中最小就为0, 看来在之后最小为0的时候,就不要使用INT_MIN了.

  3. BZOJ2670 : Almost

    求出前缀和$s[]$,那么区间$[l,r]$的几乎平均数$=\frac{s[r]-s[l-1]}{r-l}$. 若只有一个询问,那么可以维护$(i,s[i-1])$的凸壳,在凸壳上二分点$(i,s[i ...

  4. navicat for mysql 只把指定的表数据结构导出

    第一步 右键点击数据库名字,点击数据传输,在常规视图下选择自己要导出的表, 选择要导出的表,点击文件,然后切换到高级视图下,把插入记录前面的对号取消勾选,点击开始即可只导出表结构

  5. es6学习笔记二:生成器 Generators

    今天这篇文章让我感到非常的兴奋,接下来我们将一起领略ES6中最具魔力的特性. 为什么说是“最具魔力的”?对于初学者来说,此特性与JS之前已有的特性截然不同,可能会觉得有点晦涩难懂.但是,从某种意义上来 ...

  6. Codeforces909D Colorful Points(缩点)

    http://codeforces.com/problemset/problem/909/D 直接模拟超时.要运用缩点的方法,把相同的一段缩成一点,记录有几个. 对于非首尾的缩点每次-2,首尾的-1. ...

  7. SSD固态硬盘测试工具收集(持续更新)

    https://www.crsky.com/zhuanti/gutaiyingpanceshi.html https://www.crsky.com/zhuanti/ssdjiance.html ht ...

  8. Python3 与 C# 网络编程之~ 网络基础篇

    最新版本查看:https://www.cnblogs.com/dotnetcrazy/p/9919202.html 入门篇 官方文档:https://docs.python.org/3/library ...

  9. NLP 第10章 基于深度学习的NLP 算法

  10. ThunderBird对只有回复地址的邮件过滤

    回复地址,其实就是reply-to 增加一个自定义的字段:reply-to即可