RPC协议

来源:互联网 发布:硬盘文件恢复软件 编辑:程序博客网 时间:2024/06/12 19:05

一、什么是RPC协议

RPC,全称为Remote Procedure Call,即远程过程调用,它是一个计算机通信协议。它允许像调用本地服务一样调用远程服务。它可以有不同的实现方式。如RMI(远程方法调用)HessianHttp invoker等。另外,RPC是与语言无关的。

二、RPC结构拆解

深入浅出 RPC

三、RPC简单案例
案例参考

public class HelloServiceImpl implements HelloService {    @Override    public String sayHi(String name) {        return "Hi, " + name;    }}
public class ServiceCenter implements Server {    private static ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());    private static final HashMap<String, Class> serviceRegistry = new HashMap<String, Class>();    private static boolean isRunning = false;    private static int port;    public ServiceCenter(int port) {        this.port = port;    }    public void stop() {        isRunning = false;        executor.shutdown();    }    public void start() throws IOException {        ServerSocket server = new ServerSocket();        server.bind(new InetSocketAddress(port));        System.out.println("start server");        try {            while (true) {                // 1.监听客户端的TCP连接,接到TCP连接后将其封装成task,由线程池执行                executor.execute(new ServiceTask(server.accept()));            }        } finally {            server.close();        }    }    public void register(Class serviceInterface, Class impl) {        serviceRegistry.put(serviceInterface.getName(), impl);    }    public boolean isRunning() {        return isRunning;    }    public int getPort() {        return port;
private static class ServiceTask implements Runnable {    Socket clent = null;    public ServiceTask(Socket client) {        this.clent = client;    }    public void run() {        ObjectInputStream input = null;        ObjectOutputStream output = null;        try {            // 2.将客户端发送的码流反序列化成对象,反射调用服务实现者,获取执行结果            input = new ObjectInputStream(clent.getInputStream());            String serviceName = input.readUTF();            String methodName = input.readUTF();            Class<?>[] parameterTypes = (Class<?>[]) input.readObject();            Object[] arguments = (Object[]) input.readObject();            Class serviceClass = serviceRegistry.get(serviceName);            if (serviceClass == null) {                throw new ClassNotFoundException(serviceName + " not found");            }            Method method = serviceClass.getMethod(methodName, parameterTypes);            Object result = method.invoke(serviceClass.newInstance(), arguments);            // 3.将执行结果反序列化,通过socket发送给客户端            output = new ObjectOutputStream(clent.getOutputStream());            output.writeObject(result);        } catch (Exception e) {            e.printStackTrace();        } finally {            if (output != null) {                try {                    output.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (input != null) {                try {                    input.close();                } catch (IOException e) {                    e.printStackTrace();                }            }            if (clent != null) {                try {                    clent.close();                } catch (IOException e) {                    e.printStackTrace();                }            }        }    }}
public class RPCClient<T> {    @SuppressWarnings("unchecked")    public static <T> T getRemoteProxyObj(final Class<?> serviceInterface, final InetSocketAddress addr) {        // 1.将本地的接口调用转换成JDK的动态代理,在动态代理中实现接口的远程调用        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface},                new InvocationHandler() {                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                        Socket socket = null;                        ObjectOutputStream output = null;                        ObjectInputStream input = null;                        try {                            // 2.创建Socket客户端,根据指定地址连接远程服务提供者                            socket = new Socket();                            socket.connect(addr);                            // 3.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者                            output = new ObjectOutputStream(socket.getOutputStream());                            output.writeUTF(serviceInterface.getName());                            output.writeUTF(method.getName());                            output.writeObject(method.getParameterTypes());                            output.writeObject(args);                            // 4.同步阻塞等待服务器返回应答,获取应答后返回                            input = new ObjectInputStream(socket.getInputStream());                            return input.readObject();                        } finally {                            if (socket != null) socket.close();                            if (output != null) output.close();                            if (input != null) input.close();                        }                    }                });    }}
public class RPCTest {    @Test     public void product() throws IOException{         Server serviceServer = new ServiceCenter(8088);         serviceServer.register(HelloService.class, HelloServiceImpl.class);         serviceServer.start();     }     @Test     public void consume(){         HelloService service = RPCClient.getRemoteProxyObj(HelloService.class, new InetSocketAddress("localhost", 8088));         System.out.println(service.sayHi("test"));     }}

案例总结: 这里实现的简单RPC框架是使用Java语言开发,与Java语言高度耦合,并且通信方式采用的Socket是基于BIO实现的,IO效率不高,还有Java原生的序列化机制占内存太多,运行效率也不高。可以考虑从下面几种方法改进。

可以采用基于JSON**数据传输**的RPC框架;
可以使用NIO或直接使用Netty替代BIO实现(网络传输);
使用开源的序列化机制,如Hadoop Avro与Google protobuf等;
服务注册可以使用Zookeeper进行管理,能够让应用更加稳定。