自定义的RPC的Java实现
Java  /  管理员 发布于 4年前   355
网上看到纯java实现的RPC,很不错。
RPC的全名Remote Process Call,即远程过程调用。使用RPC,可以像使用本地的程序一样使用远程服务器上的程序。下面是一个简单的RPC 调用实例,从中可以看到RPC如何使用以及好处:
package com.bijian.rpc;import com.bijian.rpc.op.Echo;public class MainClient { public static void main(String[] args) { Echo echo = RPC.getProxy(Echo.class, "127.0.0.1", 8080); System.out.println(echo.echo("hello,hello")); System.out.println(echo.echo("hellow,rod")); System.out.println(echo.echo("hellow,bijian")); System.out.println(echo.echo("hellow,rod")); System.out.println(echo.echo("hellow,rod")); System.out.println(echo.echo("hellow,rod")); }}
package com.bijian.rpc.op;public interface Echo { public String echo(String string);}
使用RPC.getProxy生成接口Echo的代理实现类。然后就可以像使用本地的程序一样来调用Echo中的echo方法。
使用RPC的好处是简化了远程服务访问。提高了开发效率。在分发代码时,只需要将接口分发给客户端使用,在客户端看来只有接口,没有具体类实现。这样保证了代码的可扩展性和安全性。
在看了RPCClient如何使用,我们再来定义一个RPC服务器的接口,看看服务器都提供什么操作:
package com.bijian.rpc.support;import com.bijian.rpc.protocal.Invocation;public interface Server { public void stop(); public void start(); public void register(Class interfaceDefiner, Class impl); public void call(Invocation invo); public boolean isRunning(); public int getPort();}
服务器提供了start和stop方法。使用register注册一个接口和对应的实现类。call方法用于执行Invocation指定的接口的方法名。isRunning返回了服务器的状态,getPort()则返回了服务器使用的端口。
来看看Invocation的定义:
package com.bijian.rpc.protocal;import java.io.Serializable;import java.util.Arrays;public class Invocation implements Serializable { /** * */ private static final long serialVersionUID = 1L; private Class interfaces; private Method method; private Object[] params; private Object result; /** * @return the result */ public Object getResult() { return result; } /** * @param result the result to set */ public void setResult(Object result) { this.result = result; } /** * @return the interfaces */ public Class getInterfaces() { return interfaces; } /** * @param interfaces the interfaces to set */ public void setInterfaces(Class interfaces) { this.interfaces = interfaces; } /** * @return the method */ public Method getMethod() { return method; } /** * @param method the method to set */ public void setMethod(Method method) { this.method = method; } /** * @return the params */ public Object[] getParams() { return params; } /** * @param params the params to set */ public void setParams(Object[] params) { this.params = params; } @Override public String toString() { return interfaces.getName() + "." + method.getMethodName() + "(" + Arrays.toString(params) + ")"; }}
具体服务器实现类中的call方法是这样使用Invocation的:
package com.bijian.rpc;import java.lang.reflect.Method;import java.util.HashMap;import java.util.Map;import com.bijian.rpc.protocal.Invocation;import com.bijian.rpc.support.Listener;import com.bijian.rpc.support.Server;public class RPC { public static class RPCServer implements Server { private int port = 8080; private Listener listener; private boolean isRuning = true; /** * @param isRuning the isRuning to set */ public void setRuning(boolean isRuning) { this.isRuning = isRuning; } /** * @return the port */ public int getPort() { return port; } /** * @param port the port to set */ public void setPort(int port) { this.port = port; } private Map<String, Object> serviceEngine = new HashMap<String, Object>(); @Override public void call(Invocation invo) { System.out.println(invo.getClass().getName()); Object obj = serviceEngine.get(invo.getInterfaces().getName()); if (obj != null) { try { Method m = obj.getClass().getMethod(invo.getMethod().getMethodName(), invo.getMethod().getParams()); Object result = m.invoke(obj, invo.getParams()); invo.setResult(result); } catch (Throwable th) { th.printStackTrace(); } } else { throw new IllegalArgumentException("has no these class"); } } @Override public void register(Class interfaceDefiner, Class impl) { try { this.serviceEngine.put(interfaceDefiner.getName(), impl.newInstance()); System.out.println(serviceEngine); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public void start() { System.out.println("开始启动服务"); listener = new Listener(this); this.isRuning = true; listener.start(); } @Override public void stop() { this.setRuning(false); } @Override public boolean isRunning() { return isRuning; } }}
下面来看服务器接收连接并处理连接请求的核心代码:
package com.bijian.rpc.support;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.ServerSocket;import java.net.Socket;import com.bijian.rpc.protocal.Invocation;public class Listener extends Thread { private ServerSocket socket; private Server server; public Listener(Server server) { this.server = server; } @Override public void run() { System.out.println("启动服务器中,打开端口" + server.getPort()); try { socket = new ServerSocket(server.getPort()); } catch (IOException e1) { e1.printStackTrace(); return; } while (server.isRunning()) { try { System.out.println("等待请求"); Socket client = socket.accept(); System.out.println("请求到来"); ObjectInputStream ois = new ObjectInputStream(client.getInputStream()); Invocation invo = (Invocation) ois.readObject(); System.out.println("远程调用:" + invo); server.call(invo); ObjectOutputStream oos = new ObjectOutputStream(client.getOutputStream()); oos.writeObject(invo); oos.flush(); oos.close(); ois.close(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } try { if (socket != null && !socket.isClosed()) socket.close(); } catch (IOException e) { e.printStackTrace(); } }}
服务器端代码搞定后,来看看客户端的代码,先看看我们刚开始使用RPC.getProxy方法:
package com.bijian.rpc;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;import com.bijian.rpc.protocal.Invocation;import com.bijian.rpc.support.Client;public class RPC { public static <T> T getProxy(final Class<T> clazz, String host, int port) { final Client client = new Client(host, port); InvocationHandler handler = new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Invocation invo = new Invocation(); invo.setInterfaces(clazz); invo.setMethod(new com.bijian.rpc.protocal.Method(method.getName(), method.getParameterTypes())); invo.setParams(args); client.invoke(invo); return invo.getResult(); } }; T t = (T) Proxy.newProxyInstance(RPC.class.getClassLoader(), new Class[] { clazz }, handler); return t; }}
Client类的代码如下:
package com.bijian.rpc.support;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.net.Socket;import java.net.UnknownHostException;import com.bijian.rpc.protocal.Invocation;public class Client { private String host; private int port; private Socket socket; private ObjectOutputStream oos; private ObjectInputStream ois; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public Client(String host, int port) { this.host = host; this.port = port; } public void init() throws UnknownHostException, IOException { socket = new Socket(host, port); oos = new ObjectOutputStream(socket.getOutputStream()); } public void invoke(Invocation invo) throws UnknownHostException, IOException, ClassNotFoundException { init(); System.out.println("写入数据"); oos.writeObject(invo); oos.flush(); ois = new ObjectInputStream(socket.getInputStream()); Invocation result = (Invocation) ois.readObject(); invo.setResult(result.getResult()); }}
至此,RPC的客户端和服务器端代码完成,启动服务器的代码如下:
package com.bijian.rpc;import com.bijian.rpc.op.Echo;import com.bijian.rpc.op.RemoteEcho;import com.bijian.rpc.support.Server;public class Main { public static void main(String[] args) { Server server = new RPC.RPCServer(); server.register(Echo.class, RemoteEcho.class); server.start(); }}
现在先运行服务器端代码,再运行客户端代码,就可以成功运行。
Client运行输出:
写入数据服务器回应:hello,hello写入数据服务器回应:hellow,rod写入数据服务器回应:hellow,bijian写入数据服务器回应:hellow,rod写入数据服务器回应:hellow,rod写入数据服务器回应:hellow,rod
Server运行输出:
{com.bijian.rpc.op.Echo=com.bijian.rpc.op.RemoteEcho@1fb8ee3}开始启动服务启动服务器中,打开端口8080等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hello,hello])com.bijian.rpc.protocal.Invocation等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])com.bijian.rpc.protocal.Invocation等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hellow,bijian])com.bijian.rpc.protocal.Invocation等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])com.bijian.rpc.protocal.Invocation等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])com.bijian.rpc.protocal.Invocation等待请求请求到来远程调用:com.bijian.rpc.op.Echo.echo([hellow,rod])com.bijian.rpc.protocal.Invocation等待请求
这个RPC实例,在数据串行化上,使用了java的标准io序列化机制,虽然不能跨平台,但是做DEMO还是不错的;另外在处理客户端请求上,使用了ServerSocket,而没有使用ServerSocketChannel这个java nio中的新特性;在动态生成接口的实现类上,使用了java.lang.reflet中的Proxy类,他可以动态创建接口的实现类。
123 在
Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..原梓番博客 在
在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..博主 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..1111 在
佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..路人 在
php中使用hyperf框架调用讯飞星火大模型实现国内版chatgpt功能示例中评论 教程很详细,如果加个前端chatgpt对话页面就完美了..Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号