事先声明:本文代码参考自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();
}
}
}

结果如下:

服务端:

客户端:

思考

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

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

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

  1. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

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

  2. 基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    工作几年,用过不不少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. Xamarin.Forms bug? System.ObjectDisposedException: Cannot access a disposed object

    Hi, My Android Xamarin.Forms application uses a Navigation stack to display various views, I often h ...

  2. 搭建windows的solr6服务器

    准备工作: 目前最新版本6.0.下载solr 6.0:Solr6.0下载 JDK8 下载jdk1.8:jdk1.8[solr6.0是基于jdk8开发的] tomcat8.0 下载:tomcat8 ## ...

  3. Moccakids-Tangram Puzzle 限免啦!

    圣诞前上线的App,Tangram Puzzle 限免,大家去下载玩玩. 介绍网站:http://www.mokamisu.com/ App Store:https://itunes.apple.co ...

  4. Dalvik VM (DVM) 与Java VM (JVM)之间有哪些区别?

    Dalvik虚拟机存在于Android系统,JVM是java虚拟机,两者都是虚拟机,本文就对两者进行比较,讲述它们的不同. Dalvik虚拟机是Google等厂商合作开发的Android移动设备平台的 ...

  5. Android 源码获取-----在Windows环境下通过Git得到Android源代码

    在学习Android的过程中,深入其源代码研究对我们来说是非常重要的,这里将介绍如何通过在Windows环境下使用Git来得到我们的Android源代码. 1.首先确保你电脑上安装了Git,这个通过  ...

  6. redis的基本使用

    Redis的应用 http://www.redis.cn/commands.html http://www.cnblogs.com/ikodota/archive/2012/03/05/php_red ...

  7. android:style.xml

    <?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2006 The Andr ...

  8. Codeforces Round #383 (Div. 2) 题解【ABCDE】

    Codeforces Round #383 (Div. 2) A. Arpa's hard exam and Mehrdad's naive cheat 题意 求1378^n mod 10 题解 直接 ...

  9. [SQL SERVER 2005]数据库差异备份及还原

    因为之前遇到还原差异备份,最开始遇到SQLServer报错:”无法还原日志备份或差异备份,因为没有文件可用于前滚“.查阅很多资料后,终于得到解决.收集整理成这篇随笔. 问题原因:出现这种错误绝大多数是 ...

  10. Android学习笔记----Activity的生命周期图示

    转载,一目了然.