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. Webstorm+Webpack+echarts构建个性化定制的数据可视化图表&&两个echarts详细教程(柱状图,南丁格尔图)

    Webstorm+Webpack+echarts   ECharts 特性介绍 ECharts,一个纯 Javascript 的图表库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(I ...

  2. C# Excel导入、导出【源码下载】

    本篇主要介绍C#的Excel导入.导出. 目录 1. 介绍:描述第三方类库NPOI以及Excel结构 2. Excel导入:介绍C#如何调用NPOI进行Excel导入,包含:流程图.NOPI以及C#代 ...

  3. 关于Android避免按钮重复点击事件

    最近测试人员测试我们的APP的时候,喜欢快速点击某个按钮,出现一个页面出现多次,测试人员能不能禁止这样.我自己点击了几下,确实存在这个问题,也感觉用户体验不太好.于是乎后来我搜了下加一个方法放在我们U ...

  4. ZKWeb网页框架1.1正式发布

    发行日志 https://github.com/zkweb-framework/ZKWeb/blob/master/ReleaseNotes/ReleaseNote.1.1.md 主要改动 添加EFC ...

  5. PHP好用但又容易忽略的小知识

    1.PHP函数之判断函数是否存在 当我们创建了自定义函数,并且了解了可变函数的用法,为了确保程序调用的函数是存在的,经常会先使用function_exists判断一下函数是否存在.同样的method_ ...

  6. [原]HAproxy 代理技术原理探究

    HAproxy 技术分享 简介 HAProxy是一款提供高可用性.负载均衡以及基于TCP(第四层)和HTTP(第七层)应用的代理软件 Features 1.免费 2.能够做到4层以上代理 3.高性能 ...

  7. seaJs学习笔记2 – seaJs组建库的使用

    原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以不断的学习将是源源不断. 最 ...

  8. golang struct扩展函数参数命名警告

    今天在使用VSCode编写golang代码时,定义一个struct,扩展几个方法,如下: package storage import ( "fmt" "github.c ...

  9. Android AndroidRuntime类

     AndroidRuntime类是安卓底层很重要的一个类,它负责启动虚拟机以及Java线程,AndroidRuntime类在一个进程中只有一个实例对象保存在全局变量,gCurRuntime中. 

  10. 安卓客户端a标签长按弹框提示解决办法

    昨天工作时候发现一个bug,是关于a标签的,在安卓客户端中,如果是a标签的话,长按会出现一个弹框,如图所示 是因为安卓客户端的长按触发机制,以后进行wap端开发的时候,如果用到跳转页面尽量不要用a标签 ...