前言

  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. mumu模拟器安装xposed--如何在android模拟器上进行root

    问题描述 安装xposed表示failed to access root权限,新版的mumu模拟器没有了root选项,需要自己root. 1.先关掉应用兼容性,然后重启 电脑一般都是x86的,mumu ...

  2. matlab练习程序(加权最小二乘)

    起本篇题目还是比较纠结的,原因是我本意打算寻找这样一个算法:在测量数据有比较大离群点时如何估计原始模型. 上一篇曲面拟合是假设测量数据基本符合均匀分布,没有特别大的离群点的情况下,我们使用最小二乘得到 ...

  3. coTurn 使用测试方法

    做个记录 1.从"../examples/etc/" 目录拷贝turnserver.conf文件到"/usr/local/etc/"目录 2.修改配置文件 主要 ...

  4. Uvision4创建工程

    创建工程 创建好工程会出现如下的界面 创建文件( .asm 或 .c ) 选择菜单栏中的File->New- 按 Ctrl+S 保存文件并填写文件名 添加到之前创建的工程中 选择Target1- ...

  5. Ubuntu下永久修改主机名和创建用户

    使用hostname 进行修改. 创建用户,修改密码

  6. Centos7安装搭建FTP服务器(最简便方法)

    简介: vsftpd 是“very secure FTP daemon”的缩写,安全性是它的一个最大的特点. vsftpd 是一个 UNIX 类操作系统上运行的服务器的名字,它可以运行在诸如 Linu ...

  7. FCM算法的matlab程序(初步)

    FCM算法的matlab程序 在https://www.cnblogs.com/kailugaji/p/9648430.html文章中已经介绍了FCM算法,现在用matlab程序实现它. 作者:凯鲁嘎 ...

  8. nginx 拦截 swagger 登录

    随着微服务的也来越多,每个服务都有单独的文档,那么问题来了,怎么把所有文档整合在一起呢 本方法采用服务器拦截的方式进行处理 首先需要在opt 的主目录中 /opt/ 创建一个新文件 htpasswd此 ...

  9. MY Views on Doctor-patient relationship 英语医患关系议论文

    MY Views on Doctor-patient relationship Author:Pleiades_Antares(www.cnblogs.com/irischen) 1. In rece ...

  10. Ecelipse上添加Server

    第一步,打开eclipse工具,点击菜单栏中的"Help",选择"Install New Software" 第二步,点击Add按钮 第三步,Name输入:&q ...