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

自定义的RPC的Java实现

Java  /  管理员 发布于 5年前   387

        网上看到纯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)
    • 相关文章
    • 在java中实现的脱敏工具类代码示例分享(0个评论)
    • zookeeper安装流程步骤(0个评论)
    • 在java中你背的“八股文”可能已经过时了(2个评论)
    • 在php8.0+版本中使用属性来增加值代码示例(3个评论)
    • java 正则表达式基础,实例学习资料收集大全 原创(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-11
    • 2018-03
    • 2020-03
    • 2023-05
    • 2023-11
    • 2024-01
    Top

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

    侯体宗的博客