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服务 ...
随机推荐
- mobx @computed的解读
写在前面:我一开始看不懂官网的@computed的作用,因为即使我把@computed去掉,依然能正确的report,然后我百度谷歌都找不到答案,下面都是我自己的理解,如果是有问题的,不对的,请务必留 ...
- 【原】Github+Hexo+NextT搭建个人博客
摘要 GitHub 是一个开源项目的托管网站,相信很多人都听过.在上面有很多高质量的项目代码,我们也可以把自己的项目代码托管到GitHub,与朋友们共享交流.GitHub Pages 是Github为 ...
- Android 6.0 权限知识学习笔记
最近在项目上因为6.0运行时权限吃了亏,发现之前对运行时权限的理解不足,决定回炉重造,重新学习一下Android Permission. 进入正题: Android权限 在Android系统中,权限分 ...
- 前端学HTTP之安全HTTP
前面的话 HTTP的主要不足包括通信使用明文(不加密),内容可能会被窃听:不验证通信方的身份,有可能遭遇伪装:无法证明报文的完整性,有可能被篡改 基本认证和摘要认证能够使得用户识别后较安全的访问服务器 ...
- ASP.NET Core应用针对静态文件请求的处理[4]: DirectoryBrowserMiddleware中间件如何呈现目录结构
和StaticFileMiddleware中间件一样,DirectoryBrowserMiddleware中间本质上还是定义了一个请求地址与某个物理目录之间的映射关系,而目标目录体现为一个FilePr ...
- .net erp(办公oa)开发平台架构概要说明之表单设计器
背景:搭建一个适合公司erp业务的开发平台. 架构概要图: 表单设计开发部署示例图 表单设计开发部署示例说明1)每个开发人员可以自己部署表单设计至本地一份(当然也可以共用一套开发环境,但是如 ...
- 跟着老男孩教育学Python开发【第二篇】:Python基本数据类型
运算符 设定:a=10,b=20 . 算数运算 2.比较运算 3.赋值运算 4.逻辑运算 5.成员运算 基本数据类型 1.数字 int(整型) 在32位机器上,整数的位数为32位,取值范围为-2**3 ...
- MMORPG大型游戏设计与开发(攻击区域 扇形)
距离上次发布已经有了很长一段时间,期间由于各种原因没有更新这方面的技术分享,在这里深表遗憾.在MMO或其他的游戏中,会有针对各种形状的计算,通常在攻击区域里不会很复杂,常见的为矩形.圆形.扇形.今天分 ...
- 解决Chrome 下载带半角分号出现net::ERR_RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION的问题
方式一:添加双引号Response.AddHeader("content-disposition", "attachment; filename=\"" ...
- 学习笔记:7z在delphi的应用
最近做个发邮件的功能,需要将日志文件通过邮件发送回来用于分析,但是日志文件可能会超级大,测算下来一天可能会有800M的大小.所以压缩是不可避免了,delphi中的默认压缩算法整了半天不太好使,就看了看 ...