前言

  RPC 的全称是 Remote Procedure Call,它是一种进程间通信方式。允许像调用本地服务一样调用远程服务。

  学习来源:《分布式系统架构:原理与实践》 - 李林锋

  1.RPC 框架原理

  RPC 框架的目标就是让远程过程(服务)调用更加简单、透明,RPC框架负责屏蔽底层的传输方式(TCP 或者 UDP)、序列化方式(XML、JSON、二进制)和通信细节。

  框架使用者只需要了解谁在什么位置,提供了什么样的远程服务接口即可,开发者不需要关心底层通信细节和调用过程。

  2.最简单的 RPC 框架实现

·  下面通过 JAVA 原生的序列化、TCP Socket通信、动态代理和反射机制,实现最简单的 RPC 框架。它由三部分组成:

  • 服务提供者:它运行在服务端,负责提供服务接口定义和服务实现类。(EchoService 和 EchoServiceImpl)
  • 服务发布者,它运行在 RPC 服务端,负责将本地服务发布成远程服务,供其他消费者调用。(RPCExporter)
  • 本地服务代理,它运行在 RPC 客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者。(RPCImporter)

 

  下面看具体代码,首先是服务端接口定义和服务实现类。

  代码清单 :EchoService

package com.rpc.test;

/**
* @Description - 调用接口
* @Author zww
* @Date 2018/12/10 17:29
*/
public interface EchoService {
String echo(String ping);
}

  代码清单:EchoServiceImpl

package com.rpc.test;

/**
* @Description - 调用接口实现
* @Author zww
* @Date 2018/12/10 17:30
*/
public class EchoServiceImpl implements EchoService {
@Override
public String echo(String ping) {
return ping != null ? ping + "挺不错的。" : "挺不错的。";
}
}

  

  RPC 服务端发布者代码实现如下:

  代码清单:RPCExporter

package com.rpc.test;

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.Executor;
import java.util.concurrent.Executors; /**
* @Description - 服务端发布者(提供服务)
* @Author zww
* @Date 2018/12/10 17:33
*/
public class RPCExporter {
//线程池
static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public static void exporter(String hostName, int port) throws Exception {
ServerSocket server = new ServerSocket(); //店家
server.bind(new InetSocketAddress(hostName, port)); //开店地址
try {
while (true) { //开启营业模式
executor.execute(new ExporterTask(server.accept())); //accept : 来客人了
}
} finally {
server.close();
}
} //根据约定规则解析请求,返回结果
private static class ExporterTask implements Runnable {
Socket client = null; //客户
public ExporterTask(Socket client) {
this.client = client;
} @Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
System.out.println("老板娘:诶,来咯!您要点什么!");
inputStream = new ObjectInputStream(client.getInputStream()); //接收请求
System.out.println("老板娘:要个回锅肉!");
String interfaceName = inputStream.readUTF();
Class<?> service =
Class.forName(interfaceName);
System.out.println("老板娘:微辣!");
String methodName = inputStream.readUTF();
System.out.println("老板娘:少油!");
Class<?>[] parameterType = (Class<?>[])inputStream.readObject();
System.out.println("老板娘:别放香菜!");
Object[] arguments = (Object[])inputStream.readObject();
System.out.println("老板娘:做菜快点!");
Method method = service.getMethod(methodName, parameterType);
Object result =
method.invoke(service.newInstance(), arguments);
System.out.println("老板娘:老头子,听清没!");
System.out.println("老板闷头做菜中!!!!");
System.out.println("老板娘:帅哥,你的菜好了!");
outputStream = new ObjectOutputStream(client.getOutputStream()); //返回结果
outputStream.writeObject(result);

} catch (Exception e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (inputStream != null) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (client != null) {
try {
client.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
}

  服务发布者的主要职责:

  • 监听客户端的 TCP 连接,接收到新的客户端连接之后,将其封装成 Task,由线程池执行。
  • 将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果。
  • 将执行结果反序列化,通过 Socket 发送给客户端。
  • 远程服务调用完成后,释放 Socket 等连接资源,防止句柄泄露。

  RPC 客户端本地服务代理代码:

  代码清单:RPCImporter

package com.rpc.test;

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; /**
* @Description - 路人(请求服务)
* @Author zww
* @Date 2018/12/11 10:31
*/
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() {
@Override
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);
//使用 TCP 方式请求远端方法,以下为约定的传输方式
System.out.println("路人:老板娘,点菜咯!");
outputStream = new ObjectOutputStream(socket.getOutputStream()); //发送请求
System.out.println("路人:要个回锅肉");
outputStream.writeUTF(serviceClass.getName());
System.out.println("路人:微辣!");
outputStream.writeUTF(method.getName());
System.out.println("路人:少油!");
outputStream.writeObject(method.getParameterTypes());
System.out.println("路人:别放香菜!");
outputStream.writeObject(args);
System.out.println("路人:上菜快点!");
inputStream = new ObjectInputStream(socket.getInputStream()); //获取结果
System.out.println("路人:吧唧吧唧!");
return inputStream.readObject();
} finally {
if (socket != null) socket.close();
}
}
});
} }

  本地服务代理的主要功能如下:

  • 将本地的接口调用转换成 JDK 的动态代理,在动态代理中实现接口的远程调用。
  • 创建 Socket 客户端,根据指定地址链接远程服务提供者。
  • 将远程服务调用所需的接口类、方法名、参数列表、返回参数 等编码后发送给服务提供者。
  • 同步阻塞服务端返回应答,获取应答之后返回。

  最后:编写测试代码,并看看执行结果

package com.rpc.test;

import java.net.InetSocketAddress;

/**
* 测试类
*/
public class TestApplication {
public static void main(String[] args) {
new Thread(new Runnable() {
@Override
public void run() {
try {
//启用服务提供端(设置 地址端口)
RPCExporter.exporter("localhost", 8080);
} catch (Exception e) {
e.printStackTrace();
}
}
}).start(); //发起服务请求
RPCImporter<EchoService> importer = new RPCImporter<>();
//使用远端代理(访问 地址端口)
EchoService echo = importer.importer(EchoServiceImpl.class, new InetSocketAddress("localhost", 8080));
System.out.println(echo.echo("这家店味道咋样? \n"
));
}
}

  执行测试结果:

Connected to the target VM, address: '127.0.0.1:57656', transport: 'socket'
路人:老板娘,点菜咯!
老板娘:诶,来咯!您要点什么!
路人:要个回锅肉
路人:微辣!
路人:少油!
路人:别放香菜!
路人:上菜快点!
老板娘:要个回锅肉!
老板娘:微辣!
老板娘:少油!
老板娘:别放香菜!
老板娘:做菜快点!
老板娘:老头子,听清没!
老板闷头做菜中!!!!
老板娘:帅哥,你的菜好了!
路人:吧唧吧唧!味道不错
这家店味道咋样?
挺不错的。

  

 
 
 

分布式架构的基石.简单的 RPC 框架实现(JAVA)的更多相关文章

  1. Java实现简单的RPC框架(美团面试)

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  2. 为什么说要搞定微服务架构,先搞定RPC框架?

    今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC框架呢? 一.需求缘起 服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队 ...

  3. 【58沈剑架构系列】为什么说要搞定微服务架构,先搞定RPC框架?

    第一章聊了[“为什么要进行服务化,服务化究竟解决什么问题”] 第二章聊了[“微服务的服务粒度选型”] 今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC ...

  4. 为什么说要搞定微服务架构,先搞定RPC框架

    今天开始聊一些微服务的实践,第一块,RPC框架的原理及实践,为什么说要搞定微服务架构,先搞定RPC框架呢? 一.需求缘起 服务化的一个好处就是,不限定服务的提供方使用什么技术选型,能够实现大公司跨团队 ...

  5. 最简单的RPC框架实现

    通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成: 1.服务提供者,运行再服务端,负责提供服务接口定义和服务实现类 2.服务发布者,运行再RPC服 ...

  6. 学习写简单的RPC框架demo

    学习实现一个简单的RPC框架. 工程主要目录分级结构: rpc-common: 公共基础包,能力提供包 rpc-provider: 服务提供者 rpc-consumer:服务消费者 rpc-servi ...

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

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

  8. Java实现简单的RPC框架

    一.RPC简介 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用) ...

  9. Java 实现简单的RPC框架

    0 引言 RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议.它允许像调用本地服务一样调用远程服务.它可以有不同的实现方式.如RMI(远程方法调用).He ...

随机推荐

  1. postman测试方法的 时候总是出现状态码500

    postman测试方法的 时候总是出现状态码500   {     "timestamp": "2018-07-23T05:43:51.773+0000",   ...

  2. Python绘图工具Plotly的简单使用

    1.Plotly被称为史上最好的绘图工具之一,为了更好的展示金融数据的复杂性. Plotly的官方网站为:https://plot.ly/ python量化的关键是金融数据可视化,无论是传统的K线图, ...

  3. nginx的rewrite ,如何在flask项目中获取重写前的url

    1. 在flask配一个重写到哪的路由,假设是/rewite/,然后到nginx的配置文件写重写规则,我这里重写全部的请求,接着测试能否重写成功 1. 添加一个路由 配置重写规则 测试成功 2.接下来 ...

  4. Scala类型限定

    package big.data.analyse.scala /** * 类型限定 * Created by zhen on 2018/12/9. */ object Lxxd { def main( ...

  5. java国际化---native2ascii.exe 的使用方法

    从另一个博客迁移 native2ascii.exe使用方法: 命令的语法格式: native2ascii -[options] [inputfile [outputfile]] 说明: -[optio ...

  6. Eclipse引入spring约束详细教程

    1.打开eclipse的window-preferences,搜索catalog. 2.点击add,点击File System,弹出页面选择spring-beans-4.2.xsd. 3.key ty ...

  7. 光盘yum源搭建

    先修改基本的yum原源,使其不生效 cd /etc/yum.repos.d/ mv CentOS-Base.repo CentOS-Base.repo.bak 在修改媒介yum源使其生效 检验是否生效 ...

  8. c/c++ 图相关的函数(二维数组法)

    c/c++ 图相关的函数(二维数组法) 遍历图 插入顶点 添加顶点间的线 删除顶点 删除顶点间的线 摧毁图 取得与v顶点有连线的第一个顶点 取得与v1顶点,v1顶点之后的v2顶点的之后的有连线的第一个 ...

  9. Sublime Text 2 配置PHP调试环境(在windows环境下)

    1:PHP安装,配置环境变量 PHP安装略过- 2:下载Sublime Text 2 下载地址:http://www.sublimetext.com/2 ,选择自己合适的版本 3:点击 sublime ...

  10. linux c 语言之--fseek(),fseeko(),fseeko64()讲解 (转载)

    转载:http://blog.csdn.net/lemoncyb/article/details/16841317 fseek() 函数讲解: 函数定义: int fseek(FILE *stream ...