侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

自定义的RPC的Java实现

Java  /  管理员 发布于 3年前   207

        网上看到纯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类,他可以动态创建接口的实现类。

 

文章来源:http://jbm3072.iteye.com/blog/1088102


  • 上一条:
    JAVA版身份证获取性别、出生日期及年龄
    下一条:
    top命令找到占用CPU最高的java线程
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 在php8.0+版本中使用属性来增加值代码示例(0个评论)
    • java 正则表达式基础,实例学习资料收集大全 原创(0个评论)
    • java正则表达式彻底研究(0个评论)
    • java正则表达式验证函数(0个评论)
    • MVC、MVP和MVVM分别是什么(0个评论)
    • 近期文章
    • goose数据库迁移工具介绍及使用流程步骤(0个评论)
    • 中国程序员“翻墙”为海外软件公司打工,105.8万工资被罚没!转知乎(0个评论)
    • 在go语言gin框架中使用Sharding(Gorm分表中间件)实现分表流程步骤(0个评论)
    • 在PHP提高性能方式之开启OPCache扩展及OPCache配置参数详解(0个评论)
    • 在js的websocket客户端开发中遇到代码割裂情况解决方案(0个评论)
    • Laravel框架中适用于Eloquent的日期过滤软件包:lara-date-filter(0个评论)
    • Laravel 10.24版本发布(0个评论)
    • go语言多项目批量更新依赖及自动调用jenkins构建流程步骤(0个评论)
    • 在go语言中实现数学pow(x^y 的幂次)代码示例(0个评论)
    • Laravel应用程序性能监控 (APM) 工具:Scout (0个评论)
    • 近期评论
    • 路人 在

      php中使用hyperf框架调用讯飞星火大模型实现国内版chatgpt功能示例中评论 教程很详细,如果加个前端chatgpt对话页面就完美了..
    • 博主 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 @ mashrdn 多切换几个节点测试,免费ssr是没那么稳..
    • mashrdn 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 V2rayn免费节点添加上去了,youtobe无法打开网页,是怎么回事..
    • 张伟 在

      科学上网翻墙之v2rayN-Core客户端免费公益节点使用教程中评论 3q!有用,不过免费节点隔天就要去git上复制新的导进去..
    • 博主 在

      科学上网翻墙访问Google , 上外网神器佛跳墙VPN(永久免费)使用流程步骤中评论 该篇教程已不能用了,告知大家,免的老有老铁问我!..
    • 2016-11
    • 2018-03
    • 2020-03
    • 2023-05
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客