RPC远程过程调用学习之路(一):用最原始代码还原PRC框架
RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在自己系统中,需要从其他系统中进行调用实现,所以在系统间进行数据交互时经常使用。
rpc的实现方式有很多,可以通过http和tcp协议进行实现
通过http协议的主要有:
- webService 可以参考我之前的博客 WebService 学习之路(一):了解并使用webService
webService学习之路(二):springMVC集成CXF快速发布webService
webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口
- restful 可以参考我之前的博客 Restful 介绍及SpringMVC+restful 实例讲解
而今天要讲的是通过TCP协议实现的远程调用。
为啥已经掌握了webservice和restful等通过http协议实现rpc技术外,还要研究tcp协议实现rpc呢?
因为网络七层协议中,http位于tcp之上,而从传输上而言,越底层同等条件下传输速度更快
另外影响rpc调用的除了传输方式外,另一个就是序列化,而java的阻塞式IO往往成为瓶颈,所以这里设计到了NIO,
NIO知识点就多了,请自己搜索学习。
言归正传,今天不借助其他仁和框架,用简单的代码还原rpc的过程。
大致可以分为下面几部(先了解过程,再看代码更容易理解):
- 书写好服务接口和实现,就是我们项目中的业务层,看着service,service.imp就熟悉了 o(∩_∩)o
- 把1写好的接口暴露给其他系统,以便调用
- 根据暴露了接口的地址和接口信息,进行调用
是不是感觉和调用本地的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调用过程一样吧:
- 根据约定的端口,服务端起一个ServerSocket,并一直监听该端口
- 监听到有请求时,server端通过inputStream取得请求的相关信息
- 根据请求信息调用相应方法处理,并返回结果
简易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框架的更多相关文章
- Vue学习之路之登录注册实例代码
Vue学习之路之登录注册实例代码:https://www.jb51.net/article/118003.htm vue项目中路由验证和相应拦截的使用:https://blog.csdn.net/wa ...
- Python学习之路1☞简介及入门代码
在学习之前,首先了解一下python的前世今生 一.python简介与发展: python 是一种面向对象的解释性计算机程序设计语言. python由荷兰人Guido van Rossum 于1989 ...
- 微服务框架surging学习之路——序列化 (转载https://www.cnblogs.com/alangur/p/10407727.html)
微服务框架surging学习之路——序列化 1.对微服务的理解 之前看到在群里的朋友门都在讨论微服务,看到他们的讨论,我也有了一些自己的理解,所谓微服务就是系统里的每个服务都 可以自由组合.自由组 ...
- 用C代码简要模拟实现一下RPC(远程过程调用)并谈谈它在代码调测中的重要应用【转】
转自:http://blog.csdn.net/stpeace/article/details/44947925 版权声明:本文为博主原创文章,转载时请务必注明本文地址, 禁止用于任何商业用途, 否则 ...
- jQuery学习之路(1)-选择器
▓▓▓▓▓▓ 大致介绍 终于开始了我的jQuery学习之路!感觉不能再拖了,要边学习原生JavaScript边学习jQuery jQuery是什么? jQuery是一个快速.简洁的JavaScript ...
- Android开发学习之路-RecyclerView滑动删除和拖动排序
Android开发学习之路-RecyclerView使用初探 Android开发学习之路-RecyclerView的Item自定义动画及DefaultItemAnimator源码分析 Android开 ...
- webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口
webService学习之路一:讲解了通过传统方式怎么发布及调用webservice webService学习之路二:讲解了SpringMVC和CXF的集成及快速发布webservice 本篇文章将讲 ...
- [精品书单] C#/.NET 学习之路——从入门到放弃
C#/.NET 学习之路--从入门到放弃 此系列只包含 C#/CLR 学习,不包含应用框架(ASP.NET , WPF , WCF 等)及架构设计学习书籍和资料. C# 入门 <C# 本质论&g ...
- Redis——学习之路四(初识主从配置)
首先我们配置一台master服务器,两台slave服务器.master服务器配置就是默认配置 端口为6379,添加就一个密码CeshiPassword,然后启动master服务器. 两台slave服务 ...
随机推荐
- 旺财速啃H5框架之Bootstrap(四)
上一篇<<旺财速啃H5框架之Bootstrap(三)>>已经把导航做了,接下来搭建内容框架.... 对于不规整的网页,要做成自适应就有点玩大了.... 例如下面这种版式的页面. ...
- TypeScript Vs2013 下提示Can not compile modules unless '--module' flag is provided
VS在开发TypeScript程序时候,如果import了模块有的时候会有如下提示: 这种情况下,只需要对当前TypeScript项目生成设置为AMD规范即可!
- JavaScript自定义媒体播放器
使用<audio>和<video>元素的play()和pause()方法,可以手工控制媒体文件的播放.组合使用属性.事件和这两个方法,很容易创建一个自定义的媒体播放器,如下面的 ...
- JQuery easyUI DataGrid 创建复杂列表头(译)
» Create column groups in DataGrid The easyui DataGrid has ability to group columns, as the followin ...
- 微软开源代码编辑器monaco-editor
官网上给出:”The Monaco Editor is the code editor that powers VS Code. A good page describing the code edi ...
- AlloyTouch实战--60行代码搞定QQ看点资料卡
原文链接:https://github.com/AlloyTeam/AlloyTouch/wiki/kandian 先验货 访问DEMO你也可以点击这里 源代码可以点击这里 如你体验所见,流程的滚动的 ...
- ABAP实现屏幕自己刷新和跳转功能
ABAP开发工程中,有时候需要让跳转出的屏幕自动实现跳转和刷新的功能,该功能的实现需要在屏幕PBO 里面调用相应的事件执行. 关键代码为: SET TITLEBAR ' 屏幕自动程序'. IF g_c ...
- H3 BPM让天下没有难用的流程之技术体系
一.技术架构 H3 BPM 基于微软.NET 技术架构,采用C#语言开发,以高开放.高扩展.高性能为核心准则,遵循分层的设计原理,结合最新的B/S 以及智能手机应用开发技术研发的. 图:H3 BPM ...
- win7下利用ftp实现华为路由器的上传和下载
win7下利用ftp实现华为路由器的上传和下载 1. Win7下ftp的安装和配置 (1)开始->控制面板->程序->程序和功能->打开或关闭Windows功能 (2)在Wi ...
- XAMARIN ANDROID 二维码扫描示例
现在二维码的应用越来越普及,二维码扫描也成为手机应用程序的必备功能了.本文将基于 Xamarin.Android 平台使用 ZXing.Net.Mobile 做一个简单的 Android 条码扫描示 ...