前言

  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. git 入门教程之变基合并

    git 鼓励大量使用分支---"早建分支!多用分支!",这是因为即便创建再多的分支也不会造成存储或内存开销,并且分支的作用有助于我们分解逻辑工作,这样一样其实比维护单一臃肿分支要简 ...

  2. (网页)a标签下载

    HTML <a> download 属性 <a href="/images/myw3schoolimage.jpg" download="w3logo& ...

  3. html常用标签学习笔记

    本文内容: 前言:本文讲述的内容包括几类常用标签,以及这些标签的一些常用属性(有一些属性由于已经有CSS样式来代替,所以对于一些不重要的这里选择不讲) 排版标签 段落标签:p div span 标题标 ...

  4. The log scan number (620023:3702:1) passed to log scan in database 'xxxx' is not valid

    昨天一台SQL Server 2008R2的数据库在凌晨5点多抛出下面告警信息: The log scan number (620023:3702:1) passed to log scan in d ...

  5. c/c++ 用普利姆(prim)算法构造最小生成树

    c/c++ 用普利姆(prim)算法构造最小生成树 最小生成树(Minimum Cost Spanning Tree)的概念: ​ 假设要在n个城市之间建立公路,则连通n个城市只需要n-1条线路.这时 ...

  6. Cs231n课堂内容记录-Lecture1 导论

    Lecture 1 视频网址:https://www.bilibili.com/video/av17204303/?p=2 https://zhuanlan.zhihu.com/p/21930884? ...

  7. ACDSee清除旧版本残余

    新建记事本,输入以下内容: reg delete "HKEY_LOCAL_MACHINE\SOFTWARE\ACD Systems" /f reg delete "HKE ...

  8. redis常用命令及结构

    ##常用结构及命令: keys * #查询所有key randomkey #随机返回key type key #返回key的类型 exists key #判断key是否存在 del key1 key2 ...

  9. Linux 小知识翻译 - 「SCP和SFTP」

    这次想说说「SCP和SFTP」. 不管SCP还是SFTP,都是SSH的功能之一.都是使用SSH协议来传输文件的. 不用说文件内容,就是登录时的用户信息都是经过SSH加密后才传输的,所以说SCP和SFT ...

  10. js中split()方法得到的数组长度

    js 中split(",")方法通过 ”,“ 分割字符串, 如果字符串中没有 “,” , 返回的是字符串本身 var str = “abc”://分隔符个数为0 var newSt ...