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服务 ...
随机推荐
- 关于几个主流语音SDK的接入问题
这两周都在忙着游戏上线还有接入游戏语音,两周分别接了腾讯语音和百度语音!!! 关于腾讯语音的一些问题 由于发现腾讯语音的在录完音频后的数据是编过码的所以出现了一些问题: *不能解码(腾讯方不提供解码算 ...
- 简单粗暴地理解js原型链--js面向对象编程
原型链理解起来有点绕了,网上资料也是很多,每次晚上睡不着的时候总喜欢在网上找点原型链和闭包的文章看,效果极好. 不要纠结于那一堆术语了,那除了让你脑筋拧成麻花,真的不能帮你什么.简单粗暴点看原型链吧, ...
- HTML5 语义元素(一)页面结构
本篇主要介绍HTML5增加的语义元素中关于页面结构方面的,包含: <article>.<aside>.<figure>.<figcaption>.< ...
- [APUE]标准IO库(上)
一.流和FILE对象 系统IO都是针对文件描述符,当打开一个文件时,即返回一个文件描述符,然后用该文件描述符来进行下面的操作,而对于标准IO库,它们的操作则是围绕流(stream)进行的. 当打开一个 ...
- PHP获取客户端IP
/** * 获取客户端IP */ function getClientIp() { $ip = 'unknown'; $unknown = 'unknown'; if (isset($_SERVER[ ...
- Effective java笔记(二),所有对象的通用方法
Object类的所有非final方法(equals.hashCode.toString.clone.finalize)都要遵守通用约定(general contract),否则其它依赖于这些约定的类( ...
- 显示本地openssl支持的加密算法
参考页面: http://www.yuanjiaocheng.net/webapi/parameter-binding.html http://www.yuanjiaocheng.net/webapi ...
- eclipse如何添加Memory Analyzer
①启动Eclipse,并打开"Install New software..."对话框: ②点击Add,如图: ③点击OK,最后一直点next,完成
- HTML5笔记2——HTML5音/视频标签详解
音视频的发展史 早期:<embed>+<object>+文件 问题:不是所有浏览器都支持,而且embed不是标准. 现状:Realplay.window media.Quick ...
- Set up VLAN (802.1q) tagging on a network interface?
SOLUTION VERIFIED October 13 2015 KB39674 KB741413 environment Red Hat Enterprise Linux 4 Red Hat En ...