RPC: Remote Procedure Call 远程过程调用,即业务的具体实现不是在本身系统中,须要从其余系统中进行调用实现,因此在系统间进行数据交互时常常使用。html
rpc的实现方式有不少,能够经过http和tcp协议进行实现java
经过http协议的主要有: web
webService学习之路(二):springMVC集成CXF快速发布webServicespring
webService学习之路(三):springMVC集成CXF后调用已知的wsdl接口编程
而今天要讲的是经过TCP协议实现的远程调用。服务器
为啥已经掌握了webservice和restful等经过http协议实现rpc技术外,还要研究tcp协议实现rpc呢?restful
由于网络七层协议中,http位于tcp之上,而从传输上而言,越底层同等条件下传输速度更快网络
另外影响rpc调用的除了传输方式外,另外一个就是序列化,而java的阻塞式IO每每成为瓶颈,因此这里设计到了NIO,框架
NIO知识点就多了,请本身搜索学习。socket
言归正传,今天不借助其余仁和框架,用简单的代码还原rpc的过程。
大体能够分为下面几部(先了解过程,再看代码更容易理解):
是否是感受和调用本地的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调用过程同样吧:
简易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