最简单的RPC框架实现
通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成:
1、服务提供者,运行再服务端,负责提供服务接口定义和服务实现类
2、服务发布者,运行再RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用
3、本地服务代理,运行再RPC客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者
服务端接口定义和实现,如下:
代码清单 1-1 接口定义
public interface EchoService {
String echo(String ping);
}
代码清单1-2
public class EchoServiceImpl implements EchoService {
public String echo(String ping) {
return ping != null ? ping + " -- I am ok." : "I am ok";
}
}
代码清单1-3
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.Executor;
import java.util.concurrent.Executors; /**
*
*/
public class RpcExporter {
static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
public static void exporter(String hostName , int port) throws Exception{
ServerSocket serverSocket = new ServerSocket();
serverSocket.bind(new InetSocketAddress(hostName,port));
try {
while (true){
executor.execute(new ExporterTask(serverSocket.accept()));
}
}finally {
serverSocket.close();
}
} private static class ExporterTask implements Runnable{
Socket client = null; public ExporterTask(Socket client) {
this.client = client;
} public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
inputStream = new ObjectInputStream(client.getInputStream());
String interfaceName = inputStream.readUTF();
Class<?> service = Class.forName(interfaceName);
String methodName = inputStream.readUTF();
Class<?> [] parameterTypes = (Class<?> [])inputStream.readObject();
Object [] arguments = (Object [])inputStream.readObject();
Method method = service.getMethod(methodName,parameterTypes);
Object result = method.invoke(service.newInstance(),arguments);
outputStream = new ObjectOutputStream(client.getOutputStream());
outputStream.writeObject(result); }catch (Exception e){
e.printStackTrace();
}finally {
if(outputStream != null){
try {
outputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
}catch (Exception e){
e.printStackTrace();
}
}
if(client != null){
try {
client.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
}
}
}
服务发布者的主要职责如下:
1、作为服务端,监控客户端的TCP连接,接收到新的客户端连接之后,将其封装成Task,由线程池执行
2、将客户端发送的码流反序列化成对象,反射调用实现者,获取执行结果
3、将执行结果对象序列化,通过socket发送给客户端
4、远程服务调用完成之后,释放Socket等连接字段,防止句柄泄露
RPC客户端本地服务代理源码如下:
代码清单1-4
package com.habit; import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory; 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; /**
*
*/
public class RpcImporter<S> { public S importer(final Class<?> serviceClass, final InetSocketAddress address) {
return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]},
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = null;
ObjectOutputStream outputStream = null;
ObjectInputStream inputStream = null;
try {
socket = new Socket();
socket.connect(address);
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeUTF(serviceClass.getName());
outputStream.writeUTF(method.getName());
outputStream.writeObject(method.getParameterTypes());
outputStream.writeObject(args);
inputStream = new ObjectInputStream(socket.getInputStream());
return inputStream.readObject(); } finally {
if (socket != null) {
socket.close();
}
if (outputStream != null) {
outputStream.close();
}
if (inputStream != null) {
inputStream.close();
}
}
}
}); }
}
本地服务代理的主要功能:
1、将本地的接口调用转成JDK的动态代理,再动态代理中实现接口的远程调用
2、创建Socket客户端,根据指定地址链接远程服务提供者
3、将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
4、同步阻塞等待服务端返回应答,获取应答之后返回
测试代码:
代码清单1-5
package com.habit; import java.net.InetSocketAddress; /**
*
*/
public class RpcTest {
public static void main(String[] args) {
new Thread(new Runnable() {
public void run() {
try {
RpcExporter.exporter("localhost",8088);
}catch (Exception e){
e.printStackTrace();
}
}
}).start(); RpcImporter<EchoService> importer = new RpcImporter<EchoService>();
EchoService echo = importer.importer(EchoServiceImpl.class,new InetSocketAddress("localhost",8088));
System.out.println(echo.echo("Are you ok?"));
}
}
创建一个异步发布服务端的线程并启动,用于接口rpc客户端的请求,根据请求参数调用服务实现类,返回结果给客户端
随后,创建客户端服务代理类,构建rpc请求参数,发起rpc请求,将调用结果输出到控制台,执行结果如下:
Are you ok? -- I am ok.
最简单的RPC框架实现的更多相关文章
- 分布式架构的基石.简单的 RPC 框架实现(JAVA)
前言 RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式.允许像调用本地服务一样调用远程服务. 学习来源:<分布式系统架构:原理与实践> - 李林锋 1. ...
- Java实现简单的RPC框架(美团面试)
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- 学习写简单的RPC框架demo
学习实现一个简单的RPC框架. 工程主要目录分级结构: rpc-common: 公共基础包,能力提供包 rpc-provider: 服务提供者 rpc-consumer:服务消费者 rpc-servi ...
- 徒手撸一个简单的RPC框架
来源:https://juejin.im/post/5c4481a4f265da613438aec3 之前在牛逼哄哄的 RPC 框架,底层到底什么原理得知了RPC(远程过程调用)简单来说就是调用远程的 ...
- Java实现简单的RPC框架
一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...
- Java 实现简单的RPC框架
0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...
- Java 实现简单的 RPC 框架
RPC 简介 RPC,全称为 Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式,而不需要了解底层网络技术 ...
- 动手实现一个简单的 rpc 框架到入门 grpc (上)
rpc 全称 Remote Procedure Call 远程过程调用,即调用远程方法.我们调用当前进程中的方法时很简单,但是想要调用不同进程,甚至不同主机.不同语言中的方法时就需要借助 rpc 来实 ...
- 动手实现一个简单的 rpc 框架到入门 grpc (下)
之前手动实现了一次简陋的 rpc 调用,为了简单使用了 json 编码信息,其实这是非常不可靠的,go 中 json 解析会有一些问题,比如整数会变成浮点数,而且 json 字符串比较占空间. gRP ...
随机推荐
- Mac Pro 坑记录
第一天app store没法登陆,登陆后没有任何的反应,试过了改系统时间为一致.修改权限:sudo chmod -R 1777 /Users/Shared 都没什么用. 最后是终端运行: defaul ...
- (转)MERGE语法详解
merge语法是根据源表对目标表进行匹配查询,匹配成功时更新,不成功时插入. 其基本语法规则是 merge into 目标表 a using 源表 b on(a.条件字段1=b.条件字段1 and a ...
- SSM框架下 Failed to load resource: the server responded with a status of 404 (Not Found)错误
这个错误提示的是js的引用路径有错: 1.检查应用路径是否正确(我的问题是路径是正确的但是去到页面就会提示404错误) 引用路径,最好都使用绝对路径 <script type="tex ...
- 团队的Kick off
团队名称:Super power 团队介绍:我们是一个积极向上不乏活力快咯的团队,在一起的任务是happy高效地完成我们的项目. 团队成员自我介绍: 李洪超(项目经理):(男,帅)性格内向,爱好学习, ...
- SlidingMenu第一篇 --- 导入SlidingMenu库
1. 下载地址:https://github.com/jfeinstein10/SlidingMenu 2. 找到下载好的SlidingMeun的library目录 3. 导入库(将上述地址复制到 ...
- 利用Android-FingerprintManager类实现指纹识别
安卓指纹识别 利用FingerprintManager主类进行指纹识别. Github项目地址 在安卓6.0中新增了API,FingerprintManager类,它是Google提供的帮助访问指纹硬 ...
- git-bash的alias别名设置
正常需要设置别名时,直接使用 alias gs="git status" 输入上边的命令之后,就可以使用gs(命令)代替git status(命令),这是一种设置别名简化输入,提升 ...
- EmberJS 为什么我偏爱 Ember.js 胜过 Angular 和 React.js
文章写的很老到,非常值得一看!评论也很精彩,值得一看 为什么我偏爱 Ember.js 胜过 Angular 和 React.js 前几天看到了这篇文章:Why I prefer Ember.js ov ...
- SP913 QTREE2 - Query on a tree II
思路 第一个可以倍增,第二个讨论在a到lca的路径上还是lca到b的路径上, 倍增即可 代码 #include <cstdio> #include <algorithm> #i ...
- Linux SSH 基于密钥交换的自动登录原理简介及配置说明
协商交互过程 1.客户端向目标服务器发送登录请求.在SSH 服务启用了证书验证登录方式后,会优先通过证书验证方式进行登录验证. 2.目标服务器根据 SSH 服务配置,在用户对应目录及文件中读取到有效的 ...