`
javatar
  • 浏览: 1677656 次
  • 性别: Icon_minigender_1
  • 来自: 杭州699号
社区版块
存档分类
最新评论

RPC框架几行代码就够了

阅读更多
转于自己在公司的Blog:
http://pt.alibaba-inc.com/wp/experience_1330/simple-rpc-framework.html

因为要给百技上实训课,让新同学们自行实现一个简易RPC框架,在准备PPT时,就想写个示例,发现原来一个RPC框架只要一个类,10来分钟就可以写完了,虽然简陋,也晒晒:

/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.framework;

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;

/**
 * RpcFramework
 * 
 * @author william.liangf
 */
public class RpcFramework {

    /**
     * 暴露服务
     * 
     * @param service 服务实现
     * @param port 服务端口
     * @throws Exception
     */
    public static void export(final Object service, int port) throws Exception {
        if (service == null)
            throw new IllegalArgumentException("service instance == null");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Export service " + service.getClass().getName() + " on port " + port);
        ServerSocket server = new ServerSocket(port);
        for(;;) {
            try {
                final Socket socket = server.accept();
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            try {
                                ObjectInputStream input = new ObjectInputStream(socket.getInputStream());
                                try {
                                    String methodName = input.readUTF();
                                    Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                                    Object[] arguments = (Object[])input.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 {
                                    input.close();
                                }
                            } finally {
                                socket.close();
                            }
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                }).start();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * 引用服务
     * 
     * @param <T> 接口泛型
     * @param interfaceClass 接口类型
     * @param host 服务器主机名
     * @param port 服务器端口
     * @return 远程服务
     * @throws Exception
     */
    @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("Interface class == null");
        if (! interfaceClass.isInterface())
            throw new IllegalArgumentException("The " + interfaceClass.getName() + " must be interface class!");
        if (host == null || host.length() == 0)
            throw new IllegalArgumentException("Host == null!");
        if (port <= 0 || port > 65535)
            throw new IllegalArgumentException("Invalid port " + port);
        System.out.println("Get remote service " + interfaceClass.getName() + " from server " + host + ":" + port);
        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();
                }
            }
        });
    }

}


用起来也像模像样:

(1) 定义服务接口
/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

/**
 * HelloService
 * 
 * @author william.liangf
 */
public interface HelloService {

    String hello(String name);

}


(2) 实现服务
/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

/**
 * HelloServiceImpl
 * 
 * @author william.liangf
 */
public class HelloServiceImpl implements HelloService {

    public String hello(String name) {
        return "Hello " + name;
    }

}


(3) 暴露服务
/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

import com.alibaba.study.rpc.framework.RpcFramework;

/**
 * RpcProvider
 * 
 * @author william.liangf
 */
public class RpcProvider {

    public static void main(String[] args) throws Exception {
        HelloService service = new HelloServiceImpl();
        RpcFramework.export(service, 1234);
    }

}


(4) 引用服务
/*
 * Copyright 2011 Alibaba.com All right reserved. This software is the
 * confidential and proprietary information of Alibaba.com ("Confidential
 * Information"). You shall not disclose such Confidential Information and shall
 * use it only in accordance with the terms of the license agreement you entered
 * into with Alibaba.com.
 */
package com.alibaba.study.rpc.test;

import com.alibaba.study.rpc.framework.RpcFramework;

/**
 * RpcConsumer
 * 
 * @author william.liangf
 */
public class RpcConsumer {
    
    public static void main(String[] args) throws Exception {
        HelloService service = RpcFramework.refer(HelloService.class, "127.0.0.1", 1234);
        for (int i = 0; i < Integer.MAX_VALUE; i ++) {
            String hello = service.hello("World" + i);
            System.out.println(hello);
            Thread.sleep(1000);
        }
    }
    
}
分享到:
评论
33 楼 大胡子爸爸 2018-10-24  
String, Class 都实现了Serializable接口,唯一可能出错的是参数,这里参数是String,所以不会出错。

darkness_t 写道
u010533788 写道
iamascv 写道
HelloService没有序列化,但是也不报错,为什么?


为什么需要序列化?消费者和生产者所在的项目都有这个接口,只需要传输调用的方法名和传入的参数就行了。

u010533788 写道
iamascv 写道
HelloService没有序列化,但是也不报错,为什么?


为什么需要序列化?消费者和生产者所在的项目都有这个接口,只需要传输调用的方法名和传入的参数就行了。


output.writeObject就包含了序列化这个动作了.

32 楼 lss598018587 2018-04-02  
谢谢大神分享,比起新手看复杂的dubbo框架

还不如看大神的这一篇简短的文章更有意义
31 楼 我没名字30 2017-06-30  
嵌套这么多try好吗
30 楼 不弄结球 2017-05-17  
请问楼主,能转载不?
29 楼 darkness_t 2016-12-15  
u010533788 写道
iamascv 写道
HelloService没有序列化,但是也不报错,为什么?


为什么需要序列化?消费者和生产者所在的项目都有这个接口,只需要传输调用的方法名和传入的参数就行了。

u010533788 写道
iamascv 写道
HelloService没有序列化,但是也不报错,为什么?


为什么需要序列化?消费者和生产者所在的项目都有这个接口,只需要传输调用的方法名和传入的参数就行了。


output.writeObject就包含了序列化这个动作了.
28 楼 u010533788 2016-12-12  
iamascv 写道
HelloService没有序列化,但是也不报错,为什么?


为什么需要序列化?消费者和生产者所在的项目都有这个接口,只需要传输调用的方法名和传入的参数就行了。
27 楼 iamascv 2016-12-10  
HelloService没有序列化,但是也不报错,为什么?
26 楼 553847780 2016-03-15  
原来大神之后就开发了dubbo了。
25 楼 水木桶 2016-03-05  
Thanks for your code. Know you from the dubbo.
24 楼 musa875643dn 2015-12-23  
 
dubbo框架作者,牛x
学习学习
23 楼 cn_bboy 2015-11-04  
hi,我对这个不太明白,有两个问题。

1.客户端调用服务,服务要把接口弄成jar包吗?
2.服务端要注册多个服务应该怎么注册?
请解答,谢谢!
22 楼 m890701 2015-09-14  
利用动态代理,对每个接口类的方法调用进行的隐藏。 动态代理,原来就是这么用的。  刚学习设计模式没多久,一直停留在乱用设计模式的境界上。  
21 楼 xiaoduanayu 2015-09-13  
写的真好,简洁明了,之前对于动态代理在RPC中的作用一直很模糊,现在豁然开朗!
20 楼 jasshine 2015-08-02  
这个是个很简单的rpc模型
1.服务端 接受客户端来的socket流, 接受约定为
     1.1 方法名
     1.2 参数类型
     1.3 方法所需参数

2  客户端动态代理生成 代理service,调用该service的方法实则 交给invoke方法处理

逻辑,在该逻辑中实现远程连接,起多个线程。

19 楼 louistz 2015-07-16  
yymn_love 写道
Hi,我在这个方面比较无知,我只是觉得RPC应该是远程调用的,就像webservice。我的疑问是:
1 RPC是否是类似webservice的服务?
  (如果是或者类似的话看第二个,否则就不要看了)
2 服务器端有代码,是提供其他的客户端访问的。那么肯定客户端不是和服务器是同一套代码,客户端肯定不能使用服务器的类了。这个RPC是怎么工作的?


希望得到您的回复。谢谢!


1、这个问题可以搜一下: RPC、RMI、WebService、JMS 的区别,结合理解
2、我觉得RPC的核心是动态代理 。
客户端看到的是接口的行为(这个行为没有被实现),服务端放的是接口行为的具体实现。
客户端把行为和行为入参提供给服务端,然后服务端的接口实现执行这个行为,最后再把执行结果返回给客户端。 看起来是客户端执行了行为,但其实是通过动态代理交给服务端执行的。其中,行为和入参这些数据通过socket由客户端传给了服务端。
18 楼 louistz 2015-07-16  
jyjava 写道
说白了就是Socket编程,传输Object

我觉得精髓在动态代理。
17 楼 khlee 2015-06-27  
豁然开朗
16 楼 string2020 2015-06-17  
写的很不错,支持支持支持。不过,有个问题想问一下:
这个只是短连接吧,楼主能否把你的这个例子改成长连接的做法
15 楼 zd987 2015-04-20  
多谢楼主,学习了!
14 楼 lee_govern 2015-03-03  
写得真心不错~支持~

相关推荐

Global site tag (gtag.js) - Google Analytics