简单类名转为class_手写一个最简单的rpc demo

学习于:分布式服务框架原理与实践_李林锋著

rpc 远程过程调用,也就是调用远程的方法,然后得到结果回本地

最简单的底层理解:根据java反射知识,方法调用需要四个元素:类名,方法名,参数类型数组,参数值数组。类名用于生成实例,方法名和参数类型数组确定唯一的方法签名,最后调用需要传递参数值数组。所以就是就是通过网络传输这这个属性到远端进程,然后按照协议进行解析调用。先是将属性使用java原生的对象序列化方式序列化成二进制数据,然后这里使用java的socket,即TCP进行网络数据传输,然后到服务端再按照协议反序列化成对象,接着反射调用实例,得到结果,然后再把数据写回去。

然后具体看服务端实现思路:首先是绑定端口,然后使用线程池处理每个接入进来的消费端socket,具体任务实现就是根据协议使用objectinputstream读取数据,然后反射调用,接着把返回值写回去。这个比较简单。

消费端思路:大家可以先自己想一下怎么写。我一开始凭着直觉写,写着写着发现不知道把参数值写入到网络当中,就是这个中间过程不知道怎么操作。。然后还是看了会书。。。

由于要做的是远程调用,调用端看起来还是和本地调用一样,只是通过代理屏蔽了底层的网络数据传输的细节。这里选择使用jdk动态代理来屏蔽底层过程,然后获取传入的参数值进行写入。

Java 动态代理作用是什么? 收藏过这篇文章觉得还不错,分享一下

然后直接上代码:

接口

public interface EchoService {
    String echo(String ping);
}

实现类:

public class EchoServiceImpl implements EchoService {
    public String echo(String ping) {
        return ping==null?"you are null":ping;
    }
}

服务方:

public class ServiceExporter {
    public void init() throws Exception{
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(), Runtime.getRuntime().availableProcessors() * 2, 60, TimeUnit.SECONDS, new LinkedBlockingQueue(100), new ThreadFactory() {
            private AtomicInteger number = new AtomicInteger(0);
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);//注意这里要传个r
                thread.setName("mythread"+number.getAndIncrement());
                return thread;
            }
        }, new ThreadPoolExecutor.CallerRunsPolicy());
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress("localhost",9090));
        System.out.println("服务端绑定9090成功");
        while (true){
            threadPoolExecutor.execute(new MyTask(server.accept()));
        }


    }
}
class MyTask implements Runnable{

    private Socket socket;
    public MyTask(Socket socket){
        this.socket=socket;
    }
    public void run() {
        ObjectInputStream objectInputStream = null;
                ObjectOutputStream objectOutputStream = null;
        try {
             objectInputStream = new ObjectInputStream( socket.getInputStream());
             
            //方法名 参数类型数组 参数值数组 类名(反射生成实例)
            String methodName = objectInputStream.readUTF();
            String intergaceName = objectInputStream.readUTF();
            Class<?>[] paramType = (Class<?>[])objectInputStream.readObject();
            Object[] args = (Object[])objectInputStream.readObject();
            Class<?> className = Class.forName(intergaceName);
            Object instance = className.newInstance();
            Method method = className.getMethod(methodName, paramType);
            Object result = method.invoke(instance, args);

            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            if(socket!=null){
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(objectInputStream!=null){
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(objectOutputStream!=null){
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }
}

消费者和main:

public class ServiceInvoker<T> {
    public T toInvoker (final Class<?> clazz, final InetSocketAddress address) throws Exception{

        //忽然发现有这种创建数组并初始化的方式,千年不见。。返回只创建实现第一个接口的对象
        return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[]{clazz.getInterfaces()[0]}, new InvocationHandler() {
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Socket socket = new Socket();
                socket.connect(address);
                System.out.println("客户端连接9090成功");

                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                String methodName = method.getName();
                objectOutputStream.writeUTF(methodName);
                String interfaceName = clazz.getName();
                objectOutputStream.writeUTF(interfaceName);
                Class<?>[] parameterTypes = method.getParameterTypes();
                objectOutputStream.writeObject(parameterTypes);
                //怎么把参数值传过去呢  代理,屏蔽掉中间的过程
                objectOutputStream.writeObject(args);
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                Object result = objectInputStream.readObject();
                //省略关闭流的操作
                return result;
            }
        });
    }

    public static void main(String[] args) throws Exception {
        new Thread(()->{
            try {
                ServiceExporter server = new ServiceExporter();
                server.init();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }).start();
        ServiceInvoker<EchoService> serviceInvoker = new ServiceInvoker<>();
        EchoService echoService = serviceInvoker.toInvoker(EchoServiceImpl.class, new InetSocketAddress("localhost", 9090));
        String good = echoService.echo(null);
        System.out.println(good);
    }


}

这里注意消费方这里直接new一个接口的泛型传入,然后toinvoke调用的时候传入的是实现类。这是简化版的写法,实现方式还有很多种。

a88a5ba15c1ec25f7220d66f3bae3803.png

030a5a123df7f5622c6e35ebb51cf32d.png

6519554265421f42580ef31f68801044.png

这是书上的介绍。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值