涉及技术
序列化、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框架
来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...
- 手写一个简单到SpirngMVC框架
spring对于java程序员来说,无疑就是吃饭到筷子.在每次编程工作到时候,我们几乎都离不开它,相信无论过去,还是现在或是未来到一段时间,它仍会扮演着重要到角色.自己对spring有一定的自我见解, ...
- 通过 Netty、ZooKeeper 手撸一个 RPC 服务
说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...
- C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架
C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...
- 动手实现一个简单的 rpc 框架到入门 grpc (上)
rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法.我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机.不同语言中的方法时就需要借助 rpc 来实 ...
- 自己用 Netty 实现一个简单的 RPC
目录: 需求 设计 实现 创建 maven 项目,导入 Netty 4.1.16. 项目目录结构 设计接口 提供者相关实现 消费者相关实现 测试结果 总结 源码地址:github 地址 前言 众所周知 ...
- 如何实现一个简单的RPC
在如何给老婆解释什么是RPC中,我们讨论了RPC的实现思路. 那么这一次,就让我们通过代码来实现一个简单的RPC吧! RPC的实现原理 正如上一讲所说,RPC主要是为了解决的两个问题: 解决分布式系统 ...
- 教你用 Netty 实现一个简单的 RPC!
众所周知,dubbo 底层使用了 Netty 作为网络通讯框架,而 Netty 的高性能我们之前也分析过源码,对他也算还是比较了解了. 今天我们就自己用 Netty 实现一个简单的 RPC 框架. 1 ...
- 手撸基于swoole 的分布式框架 实现分布式调用(20)讲
最近看的一个swoole的课程,前段时间被邀请的参与的这个课程 比较有特点跟一定的深度,swoole的实战教程一直也不多,结合swoole构建一个新型框架,最后讲解如何实现分布式RPC的调用. 内容听 ...
随机推荐
- Java基础(十三) 文件高级技术
文件高级技术 一.常见文件类型处理 一)属性文件 属性文件很简单,一行表示一个属性,属性就是键值对,键和值用(=)或者(:)分隔. #ready to work name = tang age = p ...
- 使用VSFTPD传输文件
使用VSFTPD传输文件 一.文件传输协议 FTP(File Transfer Protocol)是一种在互联网进行文件传输的协议,基于B/S模式,默认使用20.21端口,其中20端口(数据端口)用于 ...
- BZOJ3273 : liars
枚举每个人,计算他必定是诚实者的情况下至少有几个人说谎,若超过$t$则他肯定是说谎者. 对于至少有几个人说谎,区间信息可以合并: 每个区间维护最左最右两个人$l,r$以及$f[i][j]$表示$l$和 ...
- BZOJ4095 : [Usaco2013 Dec]The Bessie Shuffle
首先将排列和整个序列以及询问都反过来,问题变成给定一个位置$x$,问它经过若干轮置换后会到达哪个位置. 每次置换之后窗口都会往右滑动一个,因此其实真实置换是$p[i]-1$. 对于每个询问,求出轮数, ...
- Dead Fraction [POJ1930]
题意: 很有意思的一道题,,将一个无限循环小数转化成分母最小的精确分数值....,循环的部分不一定是最后一位. Sample Input 0.2... 0.20... 0.474612399... 0 ...
- SqlHelper分享
using Model; using System; using System.Collections.Generic; using System.Configuration; using Syste ...
- django之session与分页
前面我们介绍了cookies,主要应用在用户登录上,保存用户登录状态,不过cookies直接放在了浏览器上,安全性较低,所以我们便引出了session功能与cookies相同,不同的是它放在了客户端, ...
- delphi TTBXToolBar 增加外部控件
这样可以引用外部控件,还是比较方便
- [Web 前端] qs.parse()、qs.stringify()使用方法
cp from : https://blog.csdn.net/suwu150/article/details/78333452 qs是一个npm仓库所管理的包,可通过npm install qs命令 ...
- virltualbox 升级之后 苹果虚拟机报The installed support driver doesn't match the version of the user解决方案
1.反安装virtualbox后,不要重启 2.删除virtualbox的安装目录 3.进入%userprofile% 目录 (比如: C:\users\me) 删除 .VirtualBox Virt ...