RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用。

rpc的实现方式有很多,可以通过http和tcp协议进行实现

通过http协议的主要有:

  1. webService    可以参考我之前的博客  WebService 学习之路(一):了解并使用webService

webService学习之路(二):springMVC集成CXF快速发布webService

webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口

  1. restful           可以参考我之前的博客  Restful 介绍及SpringMVC+restful 实例讲解

而今天要讲的是通过TCP协议实现的远程调用。

为啥已经掌握了webservice和restful等通过http协议实现rpc技术外,还要研究tcp协议实现rpc呢?

因为网络七层协议中,http位于tcp之上,而从传输上而言,越底层同等条件下传输速度更快

另外影响rpc调用的除了传输方式外,另一个就是序列化,而java的阻塞式IO往往成为瓶颈,所以这里设计到了NIO,

NIO知识点就多了,请自己搜索学习。

言归正传,今天不借助其他仁和框架,用简单的代码还原rpc的过程。

大致可以分为下面几部(先了解过程,再看代码更容易理解):

  1. 书写好服务接口和实现,就是我们项目中的业务层,看着service,service.imp就熟悉了 o(∩_∩)o
  2. 把1写好的接口暴露给其他系统,以便调用
  3. 根据暴露了接口的地址和接口信息,进行调用

是不是感觉和调用本地的service一样, 最终就是要达到的这个效果。

文采不好,就要开始贴代码了:

首先是写接口和接口的实现类,和平时看见的、写的没任何区别

接口定义

package com.xiaochangwei.rpc;

public interface RpcTestService {
String testRpcCall(int count);
}

接口实现类

package com.xiaochangwei.rpc;

import java.text.SimpleDateFormat;
import java.util.Date; public class RpcTestServiceImpl implements RpcTestService { public final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:sss"); @Override
public String testRpcCall(int count) {
return dateFormat.format(new Date())+" 调用rpc次数为:" + count;
} }

然后就是rpc的精髓了,怎么把服务暴露给其他系统的

其实说白了就是使用java自带的

import java.net.ServerSocket;
import java.net.Socket;

网络编程相关的东西socket和对应的IO,因为我们要向服务器发送请求,然后服务器又要返回数据给请求方,

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

同时还要用到反射,因为我们不可能每个接口实现类的暴露都去写一套方法吧,得共通化吧, 先简单理解为泛型把    代码中看见过Class T 吧 o(∩_∩)o

先简单理解为过程和socket调用过程一样吧:

  1. 根据约定的端口,服务端起一个ServerSocket,并一直监听该端口
  2. 监听到有请求时,server端通过inputStream取得请求的相关信息
  3. 根据请求信息调用相应方法处理,并返回结果

简易PRC框架

package com.xiaochangwei.rpc;

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.ServerSocket;
import java.net.Socket; /**
* rpc简易框架
*/
public class RpcFramework { /**
* 暴露服务
*/
@SuppressWarnings("resource")
public static void export(final Object service, int port) throws Exception {
if (service == null)
throw new IllegalArgumentException("服务实例为空");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("端口号不正确");
System.out.println("通过端口 [" + port +"] 暴露服务[" + service.getClass().getName() + "]" ); ServerSocket server = new ServerSocket(port);
while(true){
try {
final Socket socket = server.accept();
new Thread(new Runnable() {
@Override
public void run() {
try {
try {
ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
try {
String methodName = inputStream.readUTF();
Class<?>[] parameterTypes = (Class<?>[]) inputStream.readObject();
Object[] arguments = (Object[]) inputStream.readObject();
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
Method method = service.getClass().getMethod(methodName,parameterTypes);
Object result = method.invoke(service,arguments);
output.writeObject(result);
} catch (Throwable t) {
output.writeObject(t);
} finally {
output.close();
}
} finally {
inputStream.close();
}
} finally {
socket.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
} catch (Exception e) {
e.printStackTrace();
}
}
} /**
* 引用服务
*/
@SuppressWarnings("unchecked")
public static <T> T refer(final Class<T> interfaceClass, final String host,
final int port) throws Exception {
if (interfaceClass == null)
throw new IllegalArgumentException("接口为空");
if (!interfaceClass.isInterface())
throw new IllegalArgumentException(interfaceClass.getName() + " 不是接口");
if (host == null || host.length() == 0)
throw new IllegalArgumentException("主机地址为空");
if (port <= 0 || port > 65535)
throw new IllegalArgumentException("端口不正确" + port);
System.out.println("从服务器[" + host + ":" + port + "]取得远程服务[" + interfaceClass.getName() + "]" ); return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(),
new Class<?>[] { interfaceClass }, new InvocationHandler() {
public Object invoke(Object proxy, Method method,Object[] arguments) throws Throwable {
Socket socket = new Socket(host, port);
try {
ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream());
try {
output.writeUTF(method.getName());
output.writeObject(method.getParameterTypes());
output.writeObject(arguments);
ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
try {
Object result = input.readObject();
if (result instanceof Throwable) {
throw (Throwable) result;
}
return result;
} finally {
input.close();
}
} finally {
output.close();
}
} finally {
socket.close();
}
}
});
} /**
* 暴露RPC服务主调函数
*/
public static void main(String[] args) throws Exception {
RpcTestService rpcTestService = new RpcTestServiceImpl();
RpcFramework.export(rpcTestService, 3125);
}
}

指定其中的main后,我们的接口就通过指定的端口暴露给其他系统了

其他系统调用也很简单

package com.xiaochangwei.rpc;

public class RpcConsumer {
public static void main(String[] args) throws Exception {
RpcTestService rpcTestService = RpcFramework.refer(RpcTestService.class,"127.0.0.1", 3125);
for (int i = 0; i < 10; i++) {
String response = rpcTestService.testRpcCall(i);
System.out.println(response);
Thread.sleep(1000);
}
}
}

是不是感觉和本地调用一样 o(∩_∩)o

看下效果吧,先暴露接口,再调用

执行调用

测试发现,调用是成功的 o(∩_∩)o

RPC远程过程调用学习之路(一):用最原始代码还原PRC框架的更多相关文章

  1. Vue学习之路之登录注册实例代码

    Vue学习之路之登录注册实例代码:https://www.jb51.net/article/118003.htm vue项目中路由验证和相应拦截的使用:https://blog.csdn.net/wa ...

  2. Python学习之路1☞简介及入门代码

    在学习之前,首先了解一下python的前世今生 一.python简介与发展: python 是一种面向对象的解释性计算机程序设计语言. python由荷兰人Guido van Rossum 于1989 ...

  3. 微服务框架surging学习之路——序列化 (转载https://www.cnblogs.com/alangur/p/10407727.html)

    微服务框架surging学习之路——序列化   1.对微服务的理解 之前看到在群里的朋友门都在讨论微服务,看到他们的讨论,我也有了一些自己的理解,所谓微服务就是系统里的每个服务都 可以自由组合.自由组 ...

  4. 用C代码简要模拟实现一下RPC(远程过程调用)并谈谈它在代码调测中的重要应用【转】

    转自:http://blog.csdn.net/stpeace/article/details/44947925 版权声明:本文为博主原创文章,转载时请务必注明本文地址, 禁止用于任何商业用途, 否则 ...

  5. jQuery学习之路(1)-选择器

    ▓▓▓▓▓▓ 大致介绍 终于开始了我的jQuery学习之路!感觉不能再拖了,要边学习原生JavaScript边学习jQuery jQuery是什么? jQuery是一个快速.简洁的JavaScript ...

  6. Android开发学习之路-RecyclerView滑动删除和拖动排序

    Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...

  7. webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口

    webService学习之路一:讲解了通过传统方式怎么发布及调用webservice webService学习之路二:讲解了SpringMVC和CXF的集成及快速发布webservice 本篇文章将讲 ...

  8. [精品书单] C#/.NET 学习之路——从入门到放弃

    C#/.NET 学习之路--从入门到放弃 此系列只包含 C#/CLR 学习,不包含应用框架(ASP.NET , WPF , WCF 等)及架构设计学习书籍和资料. C# 入门 <C# 本质论&g ...

  9. Redis——学习之路四(初识主从配置)

    首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...

随机推荐

  1. HTML URL地址解析

    通过JavaScript的location对象,可获取URL中的协议.主机名.端口.锚点.查询参数等信息. 示例 URL:http://www.akmsg.com/WebDemo/URLParsing ...

  2. 窥探Vue.js 2.0 - Virtual DOM到底是个什么鬼?

    引言 你可能听说在Vue.js 2.0已经发布,并且在其中新添加如了一些新功能.其中一个功能就是"Virtual DOM". Virtual DOM是什么 在之前,React和Em ...

  3. EntityFramework之DetectChanges's Secrets(三)(我为EF正名)

    前言 对于应用程序开发者来说,通常不需要考虑太多对于Entity Framework中的变更追踪(change tracking),但是变更追踪和DetectChanges方法是栈的一部分,在这其中, ...

  4. JavaScript常见的五种数组去重的方式

    ▓▓▓▓▓▓ 大致介绍 JavaScript的数组去重问题在许多面试中都会遇到,现在做个总结 先来建立一个数组 var arr = [1,2,3,3,2,'我','我',34,'我的',NaN,NaN ...

  5. iOS逆向工程之Hopper+LLDB调试第三方App

    LLDB是Low Level Debugger的简称,在iOS开发的调试中LLDB是经常使用的,LLDB是Xcode内置的动态调试工具.使用LLDB可以动态的调试你的应用程序,如果你不做其他的额外处理 ...

  6. Flyweight(享元模式)

    import java.util.Hashtable; /** * 享元模式 * @author TMAC-J * 享元模式一般和工厂模式一起使用,但此处为了更好说明,只用享元模式 * 定义:享元模式 ...

  7. Spring配置文件标签报错:The prefix "XXX" for element "XXX:XXX" is not bound. .

    例如:The prefix "context" for element "context:annotation-config" is not bound. 这种 ...

  8. Dynamics CRM 2015-Data Encryption激活报错

    在CRM的日常开发中,Data Encryption经常是不得不开启的一个功能.但是有时,我们可能遇到一种情况,Organization导入之后,查看Data Encryption是已激活的状态,但是 ...

  9. Android中的沉浸式状态栏效果

    无意间了解到沉浸式状态栏,感觉贼拉的高大上,于是就是试着去了解一下,就有了这篇文章.下面就来了解一下啥叫沉浸式状态栏.传统的手机状态栏是呈现出黑色条状的,有的和手机主界面有很明显的区别.这一样就在一定 ...

  10. DevExpress - 使用 GaugeControl 标尺组件制作抽奖程序 附源码

    前不久,公司举办了15周年庆,其中添加了一个抽奖环节,要从在读学员中随机抽取幸运学员,当然,这个任务就分到了我这里. 最后的效果如下,启动有个欢迎页面,数据是来自Excel的,点击开始则上面的学号及姓 ...