事先声明:本文代码参考自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. bind() live()和delegate 区别

    Event bubbling (aka event propagation)冒泡 我们的页面可以理解为一棵DOM树,当我们在叶子结点上做什么事情的时候(如click一个a元素),如果我们不人为的设置s ...

  2. windows server 2008 R2 FTP登陆错误。

    建立了一个域用户ftp. 始终登陆不上winserver 2008 R2上的FTP. 错误如下: 530-User cannot log in. Win32 error:   Logon failur ...

  3. Linux: Set OR Change The Library Path

    Linux: Set OR Change The Library Path by NIX CRAFT on APRIL 9, 2010 · 3 COMMENTS· LAST UPDATED AUGUS ...

  4. 线程互斥与析构函数中mutex的销毁

    正在实现一个线程池的pthread包装器,突然发现有人在讨论关于http://blog.csdn.net/Solstice/article/details/5238671 是一篇比较老的文章,考虑了下 ...

  5. 【linux】如何将Vim打造成一个成熟的IDE

    如果你稍微写过一点代码,就能知道“集成开发环境”(IDE)是多么的便利.不管是Java.C还是Python,当IDE会帮你检查语法.后台编译,或者自动导入你需要的库时,写代码就变得容易许多.另外,如果 ...

  6. 谢谢博客-园,让我不再有开源AYUI的想法

    第一次 第二次 教程不会在博客园上写了,具体的看我官网博客吧,谢谢大家了 ================= 我是个有素质的程序员 艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹艹 ...

  7. Codeforces Round #381 (Div. 1) B. Alyona and a tree dfs序 二分 前缀和

    B. Alyona and a tree 题目连接: http://codeforces.com/contest/739/problem/B Description Alyona has a tree ...

  8. C# inline-hook / api-hook

    我查阅了一下相关C#方面的资料,却没有发现有提供过关于api-hook方面的资 料包括应用库由此本人编写一套inline-hook的库用于支持x64.x86上的基于在 clr的公共语言,如: c#.c ...

  9. 【LeetCode】273. Integer to English Words

    Integer to English Words Convert a non-negative integer to its english words representation. Given i ...

  10. android 渐变展示启动屏

    启动界面Splash Screen在应用程序是很常用的,往往在启动界面中显示产品Logo.公司Logo或者开发者信息,如果应用程序启动时间比较长,那么启动界面就是一个很好的东西,可以让用户耐心等待这段 ...