【分布式】RPC初探

时间:2022-09-25 08:10:47

事先声明:本文代码参考自Dubbo作者的博客

RPC(Remote Procedure Call)远程过程调用,是分布式系统当中必不可少的一个玩意。比如说在单机系统当中,我想调用某个方法,直接调就可以了对吧,但是当环境变成多机分布式系统时,A机器上想调用B机器上的某个方法时,就需要用到RPC了。RPC的原理在知乎这个问答上有很清楚的解释。

简单点来说,就是客户端利用了socket把希望调用的方法的信息(方法名、方法需要的参数等)传给服务器端,服务器端把这些信息反序列化之后利用反射去调用指定的方法,并把返回值再通过socket返回给客户端。下面是代码示例,关键部分我写了自己理解的注释。

代码主要用到了socket通信和JDK的动态代理,这两部分我在之前的博客中也都有涉及。

RPCServer.java

public class RPCServer {
private static final int PORT = 8000;
/**
* 暴露服务
*
* @param service 服务的对象实例
* */
public static void open(final Object service) throws Exception {
if (service == null) {
throw new IllegalArgumentException();
}
System.out.println("Service is opening for " + service.getClass().getName() + " at port: " + PORT);
//开启ServerSocket监听8000端口
final ServerSocket server = new ServerSocket(PORT);
for (;;) {
try {
//接收到一个客户端请求
final Socket client = server.accept();
//开一个线程处理
new Thread(new Runnable() {
@Override
public void run() {
try {
ObjectInputStream input = new ObjectInputStream(client.getInputStream());
try {
String methodName = input.readUTF();
System.out.println(">>>>methodName: " + methodName);
Class<?>[] parameterTypes = (Class<?>[]) input.readObject();
Object[] arguments = (Object[]) input.readObject();
System.out.println(">>>>arguments: " + arguments.toString());
ObjectOutputStream out = new ObjectOutputStream(client.getOutputStream());
try {
//利用反射获取到方法对象
Method method = service.getClass().getMethod(methodName, parameterTypes);
//调用方法并获取返回值
Object result = method.invoke(service, arguments);
//把返回值写入socket,返回给客户端
out.writeObject(result);
// out.flush();
} catch (Throwable t) {
out.writeObject(t);
} finally {
out.close();
}
} finally {
input.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
} /**
* 指定在远程主机上的服务
*
* @param <T> 接口泛型
* @param interfaceClass 接口
* @param host 远程主机IP
* */ @SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host) {
if (interfaceClass == null) {
throw new IllegalArgumentException("invalid interface");
}
if (host == null || "".equals(host)) {
throw new IllegalArgumentException("invalid host");
}
System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + PORT);
//动态代理返回对象实例,并且利用泛型转成服务类型
return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Socket socket = new Socket(host, PORT);
try {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
try {
//发送方法名
out.writeUTF(method.getName());
//发生方法参数列表
out.writeObject(method.getParameterTypes());
//发生方法需要的参数
out.writeObject(args);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
//获取返回值
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
}finally {
input.close();
}
}finally {
out.close();
}
} finally {
socket.close();
}
}
});
}
}

接口 HelloService.java

public interface HelloService {
public String show(String name);
}

接口实现 HelloServiceImpl.java

public class HelloServiceImpl implements HelloService {
@Override
public String show(String name) {
System.out.println(name);
return "name: " + name;
}
}

测试:

服务端测试代码 ServerTest.java

public class ServerTest {
public static void main(String[] args) throws Exception {
HelloService helloService = new HelloServiceImpl();
//开启RPC服务,并且绑定一个对象实例,指定服务器上的服务类型
RPCServer.open(helloService);
}
}

客户端测试代码 ClientTest.java

public class ClientTest {
public static void main(String[] args) {
try {
//调用指定IP的远程主机上的指定服务
HelloService service = RPCServer.refer(HelloService.class, "127.0.0.1");
System.out.println(service.show("hello"));
}catch (Exception e) {
e.printStackTrace();
}
}
}

结果如下:

服务端:

【分布式】RPC初探

客户端:

【分布式】RPC初探

思考

关于这段示例代码,有哪些改进的地方呢?首先我想到的是把TCP通信模型改成NIO通信,不要用BIO这种低并发的模型;其次是传输的信息可以用其他方式进行压缩或者叫序列化,减少传输的大小从而降低服务器压力和提高传输速度;还有就是这段代码使用的动态代理是JDK自带的方法,有个很大的缺点是必须要接口,之前的文章也提到了,可以采用CGlib来改善一下。目前能想到的就这三点了,找时间我再来完善一下。

同时也可以去看看Dubbo源码。

【分布式】RPC初探的更多相关文章

  1. 基于netty轻量的高性能分布式RPC服务框架forest&lt&semi;下篇&gt&semi;

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

  2. 基于netty轻量的高性能分布式RPC服务框架forest&lt&semi;上篇&gt&semi;

    工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...

  3. 一个轻量级分布式RPC框架--NettyRpc

    1.背景 最近在搜索Netty和Zookeeper方面的文章时,看到了这篇文章<轻量级分布式 RPC 框架>,作者用Zookeeper.Netty和Spring写了一个轻量级的分布式RPC ...

  4. 轻量级分布式 RPC 框架

    @import url(/css/cuteeditor.css); 源码地址:http://git.oschina.net/huangyong/rpc RPC,即 Remote Procedure C ...

  5. 基于开源Dubbo分布式RPC服务框架的部署整合

    一.前言 Dubbo 作为SOA服务化治理方案的核心框架,用于提高业务逻辑的复用.整合.集中管理,具有极高的可靠性(HA)和伸缩性,被应用于阿里巴巴各成员站点,同时在包括JD.当当在内的众多互联网项目 ...

  6. 【转】轻量级分布式 RPC 框架

    第一步:编写服务接口 第二步:编写服务接口的实现类 第三步:配置服务端 第四步:启动服务器并发布服务 第五步:实现服务注册 第六步:实现 RPC 服务器 第七步:配置客户端 第八步:实现服务发现 第九 ...

  7. 【原】Storm分布式RPC

    5. Storm高级篇 序列化 分布式RPC High level overview LinearDRPCTopologyBuilder Local mode DRPC Remote mode DRP ...

  8. 轻量级分布式RPC框架

    随笔- 139  文章- 0  评论- 387  一个轻量级分布式RPC框架--NettyRpc   1.背景 最近在搜索Netty和Zookeeper方面的文章时,看到了这篇文章<轻量级分布式 ...

  9. RSF 分布式 RPC 服务框架的分层设计

    RSF 是个什么东西? 一个高可用.高性能.轻量级的分布式服务框架.支持容灾.负载均衡.集群.一个典型的应用场景是,将同一个服务部署在多个Server上提供 request.response 消息通知 ...

  10. 一个轻量级分布式 RPC 框架 — NettyRpc

    原文出处: 阿凡卢 1.背景 最近在搜索Netty和Zookeeper方面的文章时,看到了这篇文章<轻量级分布式 RPC 框架>,作者用Zookeeper.Netty和Spring写了一个 ...

随机推荐

  1. Sql Server 远程过程调用失败

    很多搞开发的同志们,相信在刚刚使用sql server2008+c#2012(2012以上版本)会出现下面图片中的问题,这时因为安装Visual Studio 2013或者2012版本的时候,会自动安 ...

  2. sax(用于处理XML事件驱动的推模型)解析例子

    SAX解析 Java程序如下: import org.xml.sax.helpers.DefaultHandler; import javax.xml.parsers.SAXParser; impor ...

  3. vmware虚拟机如何安装ubuntu14&period;10系统

    vmware虚拟机安装ubuntu14.10系统安装步骤如下:

  4. Genymotion下载慢或者下载失败的解决办法

    转.原文地址:http://blog.csdn.net/sean_css/article/details/52674091 办法如下: 1.首先点击界面上的 + 号(Add)按钮,选择你要下载的模拟器 ...

  5. VueJs&lpar;5&rpar;---V-bind指令

    V-bind指令 一.概述 v-bind  主要用于属性绑定,比方你的class属性,style属性,value属性,href属性等等,只要是属性,就可以用v-bind指令进行绑定. 示例: < ...

  6. &lbrack;转&rsqb;centos每天自动备份mysql数据库

    本文转自:https://www.cnblogs.com/chongchong88/p/5566645.html #!/bin/bash PATH=/bin:/sbin:/usr/bin:/usr/s ...

  7. bs4&period;FeatureNotFound&colon; Couldn’t find a tree builder with the features you requested&colon; lxml&period;

    python3 bs4解析网页时报错: bs4.FeatureNotFound: Couldn’t find a tree builder with the features you requeste ...

  8. 【Python】xml 解析

    1. XML:指可扩展标记语言,是一种标记语言,用于存储数据和传输数据,但没有像HTML那样具有预定义标签,需要程序猿自定义标签 2. XML的解析:读取XML数据结构中的某些信息,比如读取书的属性 ...

  9. RecyclerView添加条目点击事件setOnItemClickListener,不是在Adapter中设置;

    RecyclerView不像ListView,可以直接写setOnItemClickListener,我们大部分都是在Adapter中的设置点击事件,这个是使用RecyclerView的addOnIt ...

  10. Spring Boot项目简单上手&plus;swagger配置&plus;项目发布&lpar;可能是史上最详细的&rpar;

    Spring Boot项目简单上手+swagger配置 1.项目实践 项目结构图 项目整体分为四部分:1.source code 2.sql-mapper 3.application.properti ...