最简单的RPC框架实现
通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成:
1、服务提供者,运行再服务端,负责提供服务接口定义和服务实现类
2、服务发布者,运行再RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用
3、本地服务代理,运行再RPC客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者
服务端接口定义和实现,如下:
代码清单 1-1 接口定义
public interface EchoService { String echo(String ping); }
代码清单1-2
public class EchoServiceImpl implements EchoService { public String echo(String ping) { return ping != null ? ping + " -- I am ok." : "I am ok"; } }
代码清单1-3
import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.lang.reflect.Method; import java.net.InetSocketAddress; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.Executor; import java.util.concurrent.Executors; /** * */ public class RpcExporter { static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); public static void exporter(String hostName , int port) throws Exception{ ServerSocket serverSocket = new ServerSocket(); serverSocket.bind(new InetSocketAddress(hostName,port)); try { while (true){ executor.execute(new ExporterTask(serverSocket.accept())); } }finally { serverSocket.close(); } } private static class ExporterTask implements Runnable{ Socket client = null; public ExporterTask(Socket client) { this.client = client; } public void run() { ObjectInputStream inputStream = null; ObjectOutputStream outputStream = null; try { inputStream = new ObjectInputStream(client.getInputStream()); String interfaceName = inputStream.readUTF(); Class<?> service = Class.forName(interfaceName); String methodName = inputStream.readUTF(); Class<?> [] parameterTypes = (Class<?> [])inputStream.readObject(); Object [] arguments = (Object [])inputStream.readObject(); Method method = service.getMethod(methodName,parameterTypes); Object result = method.invoke(service.newInstance(),arguments); outputStream = new ObjectOutputStream(client.getOutputStream()); outputStream.writeObject(result); }catch (Exception e){ e.printStackTrace(); }finally { if(outputStream != null){ try { outputStream.close(); }catch (Exception e){ e.printStackTrace(); } } if(inputStream != null){ try { inputStream.close(); }catch (Exception e){ e.printStackTrace(); } } if(client != null){ try { client.close(); }catch (Exception e){ e.printStackTrace(); } } } } } }
服务发布者的主要职责如下:
1、作为服务端,监控客户端的TCP连接,接收到新的客户端连接之后,将其封装成Task,由线程池执行
2、将客户端发送的码流反序列化成对象,反射调用实现者,获取执行结果
3、将执行结果对象序列化,通过socket发送给客户端
4、远程服务调用完成之后,释放Socket等连接字段,防止句柄泄露
RPC客户端本地服务代理源码如下:
代码清单1-4
package com.habit; import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory; 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.InetSocketAddress; import java.net.Socket; /** * */ public class RpcImporter<S> { public S importer(final Class<?> serviceClass, final InetSocketAddress address) { return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Socket socket = null; ObjectOutputStream outputStream = null; ObjectInputStream inputStream = null; try { socket = new Socket(); socket.connect(address); outputStream = new ObjectOutputStream(socket.getOutputStream()); outputStream.writeUTF(serviceClass.getName()); outputStream.writeUTF(method.getName()); outputStream.writeObject(method.getParameterTypes()); outputStream.writeObject(args); inputStream = new ObjectInputStream(socket.getInputStream()); return inputStream.readObject(); } finally { if (socket != null) { socket.close(); } if (outputStream != null) { outputStream.close(); } if (inputStream != null) { inputStream.close(); } } } }); } }
本地服务代理的主要功能:
1、将本地的接口调用转成JDK的动态代理,再动态代理中实现接口的远程调用
2、创建Socket客户端,根据指定地址链接远程服务提供者
3、将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者
4、同步阻塞等待服务端返回应答,获取应答之后返回
测试代码:
代码清单1-5
package com.habit; import java.net.InetSocketAddress; /** * */ public class RpcTest { public static void main(String[] args) { new Thread(new Runnable() { public void run() { try { RpcExporter.exporter("localhost",8088); }catch (Exception e){ e.printStackTrace(); } } }).start(); RpcImporter<EchoService> importer = new RpcImporter<EchoService>(); EchoService echo = importer.importer(EchoServiceImpl.class,new InetSocketAddress("localhost",8088)); System.out.println(echo.echo("Are you ok?")); } }
创建一个异步发布服务端的线程并启动,用于接口rpc客户端的请求,根据请求参数调用服务实现类,返回结果给客户端
随后,创建客户端服务代理类,构建rpc请求参数,发起rpc请求,将调用结果输出到控制台,执行结果如下:
Are you ok? -- I am ok.