最简单的RPC框架实现

通过java原生的序列化,Socket通信,动态代理和反射机制,实现一个简单的RPC框架,由三部分组成:

1、服务提供者,运行再服务端,负责提供服务接口定义和服务实现类

2、服务发布者,运行再RPC服务端,负责将本地服务发布成远程服务,供其他消费者调用

3、本地服务代理,运行再RPC客户端,通过代理调用远程服务提供者,然后将结果进行封装返回给本地消费者

服务端接口定义和实现,如下:

代码清单 1-1 接口定义

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

代码清单1-2

public class EchoServiceImpl implements EchoService {
    public String echo(String ping) {
        return ping != null ? ping + " -- I am ok." : "I am ok";
    }
}

代码清单1-3

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;

/**
 * 
 */
public class RpcExporter {
    static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    public static void exporter(String hostName , int port) throws  Exception{
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind(new InetSocketAddress(hostName,port));
        try {
            while (true){
                executor.execute(new ExporterTask(serverSocket.accept()));
            }
        }finally {
            serverSocket.close();
        }
    }

    private static class ExporterTask implements Runnable{
        Socket client = null;

        public ExporterTask(Socket client) {
            this.client = client;
        }

        public void run() {
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;
            try {
                inputStream = new ObjectInputStream(client.getInputStream());
                String interfaceName = inputStream.readUTF();
                Class<?> service = Class.forName(interfaceName);
                String methodName = inputStream.readUTF();
                Class<?> [] parameterTypes = (Class<?> [])inputStream.readObject();
                Object [] arguments = (Object [])inputStream.readObject();
                Method method = service.getMethod(methodName,parameterTypes);
                Object result = method.invoke(service.newInstance(),arguments);
                outputStream = new ObjectOutputStream(client.getOutputStream());
                outputStream.writeObject(result);


            }catch (Exception e){
                e.printStackTrace();
            }finally {
                if(outputStream != null){
                    try {
                        outputStream.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
                if(inputStream != null){
                    try {
                        inputStream.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
                if(client != null){
                    try {
                        client.close();
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

服务发布者的主要职责如下:

1、作为服务端,监控客户端的TCP连接,接收到新的客户端连接之后,将其封装成Task,由线程池执行

2、将客户端发送的码流反序列化成对象,反射调用实现者,获取执行结果

3、将执行结果对象序列化,通过socket发送给客户端

4、远程服务调用完成之后,释放Socket等连接字段,防止句柄泄露

RPC客户端本地服务代理源码如下:

代码清单1-4

package com.habit;


import com.sun.org.apache.xml.internal.serializer.OutputPropertiesFactory;

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.InetSocketAddress;
import java.net.Socket;

/**
 * 
 */
public class RpcImporter<S> {

    public S importer(final Class<?> serviceClass, final InetSocketAddress address) {
        return (S) Proxy.newProxyInstance(serviceClass.getClassLoader(), new Class<?>[]{serviceClass.getInterfaces()[0]},
                new InvocationHandler() {
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        Socket socket = null;
                        ObjectOutputStream outputStream = null;
                        ObjectInputStream inputStream = null;
                        try {
                            socket = new Socket();
                            socket.connect(address);
                            outputStream = new ObjectOutputStream(socket.getOutputStream());
                            outputStream.writeUTF(serviceClass.getName());
                            outputStream.writeUTF(method.getName());
                            outputStream.writeObject(method.getParameterTypes());
                            outputStream.writeObject(args);
                            inputStream = new ObjectInputStream(socket.getInputStream());
                            return inputStream.readObject();


                        } finally {
                            if (socket != null) {
                                socket.close();
                            }
                            if (outputStream != null) {
                                outputStream.close();
                            }
                            if (inputStream != null) {
                                inputStream.close();
                            }
                        }
                    }
                });

    }
}

本地服务代理的主要功能:

1、将本地的接口调用转成JDK的动态代理,再动态代理中实现接口的远程调用

2、创建Socket客户端,根据指定地址链接远程服务提供者

3、将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者

4、同步阻塞等待服务端返回应答,获取应答之后返回

测试代码:

代码清单1-5

package com.habit;

import java.net.InetSocketAddress;

/**
 * 
 */
public class RpcTest {
    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    RpcExporter.exporter("localhost",8088);
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }).start();

        RpcImporter<EchoService> importer = new RpcImporter<EchoService>();
        EchoService echo = importer.importer(EchoServiceImpl.class,new InetSocketAddress("localhost",8088));
        System.out.println(echo.echo("Are you ok?"));
    }
}

创建一个异步发布服务端的线程并启动,用于接口rpc客户端的请求,根据请求参数调用服务实现类,返回结果给客户端

随后,创建客户端服务代理类,构建rpc请求参数,发起rpc请求,将调用结果输出到控制台,执行结果如下:

Are you ok? -- I am ok.

 

posted @ 2018-06-02 21:59  鹊南  阅读(1214)  评论(0编辑  收藏  举报