RPC简单实现

本文介绍了一个基于Java的简易RPC框架实现,通过动态代理、反射和socket通信,实现了远程过程调用。客户端通过动态代理调用远程服务,服务端通过反射机制响应调用,展示了完整的代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

可以通过java原生的序列化,socket通信,动态代理和反射机制,实现最简单的RPC框架。

 

服务发布者创建服务端的serverSocket:

 

我之前想象调用应该是这个样子:

客户端将类名,方法名,参数名和值序列化后发给服务端,服务端获取字节信息后反序列化成java对象并对类名,方法名和参数名及值进行手动遍历做case匹配然后去调用。反射调用解决了手动匹配的问题。

本地手动序列化用动态代理解决了。

 

客户端:

package rpcLocal;
/**
 * @description:1.将本地接口调用转换成JDK的动态代理,动态代理中实现接口的远程调用(其实就是做了个序列化而已) 2.创建socket客户端,根据指定地址连接远程服务提供者
 * 3.将远程服务调用所需的接口类,方法名,参数列表等编码后发送给服务提供者
 * 4.同步阻塞等待服务端返回应答,获取应答之后返回
 */
public class RpcImporter<S> {
    public S importer(final Class<?> serviceClass, final InetSocketAddress addr) {
        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 output = null;
                ObjectInputStream input = null;
                try {
                    socket = new Socket();
                    socket.connect(addr);
                    output = new ObjectOutputStream(socket.getOutputStream());

                    /*序列化*/
                    output.writeUTF(serviceClass.getName());
                    output.writeUTF(method.getName());
                    output.writeObject(method.getParameterTypes());
                    output.writeObject(args);
                    input = new ObjectInputStream(socket.getInputStream());
                    return input.readObject();
                } finally {
                    if (socket != null) {
                        socket.close();
                    }
                    if (output != null) {
                        output.close();
                    }
                    if (input != null) {
                        input.close();
                    }
                }
            }
        });
    }
}
 

客户端main方法:

package rpcLocal;
public class MainTest {
    public static void main(String[] args) throws Exception{
        new Thread(new Runnable(){
            public void run(){
                try{//启动服务端的发布类
                    RPCexporter.exporter("localhost",8002);
                }catch(Exception e){
                    e.printStackTrace();
                }
            }
        }).start();

        RpcImporter<EchoService> importer = new RpcImporter<EchoService>();
        EchoService echo = importer.importer(EchoServiceImpl.class,new InetSocketAddress("localhost",8002));
        echo.echo("我是ping");
    }
}

服务端发布类:

package rpc.publisher;
/**
 * @description:职责:1.监听客户端的TCP连接,将其封装成Task,由线程池执行
 *                   2.将客户端的码流反序列化成对象,反射调用服务实现者,获取执行结果
 *                   3.将执行结果对象反序列化,通过socket发送给客户端
 *                   4.远程服务调用完成之后,释放socket等资源链接,防止句柄泄露
 */
public class RPCexporter {
    private final static Logger logger = LoggerFactory.getLogger(RPCexporter.class);
    static Executor executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
    public static void exporter(String hostName,int port)throws Exception{
        ServerSocket server = new ServerSocket();
        server.bind(new InetSocketAddress(hostName,port));
        try{
            System.out.println("开始向线程池提交任务");
            while(true){
                //提交任务线程至线程池
                executor.execute(new ExporterTask(server.accept()));
            }

        }
        finally{
            server.close();
        }

    }
    
    /*内部类*/
    public static class ExporterTask implements Runnable{
        Socket client = null;
        public ExporterTask(Socket client){
            this.client = client;
        }
        @Override
        public void run(){
            ObjectInputStream input = null;
            ObjectOutputStream output = null;
            try{
                //获取客户端输入流
                input = new ObjectInputStream(client.getInputStream());
                String interfaceName = input.readUTF();
                System.out.println("服务端获取到的输入流——"+interfaceName);
                Class<?> service = Class.forName(interfaceName);
                String methodName = input.readUTF();
                System.out.println("服务端获取到的输入流——"+methodName);
                Class<?>[] parameterTypes = (Class<?>[])input.readObject();
                Object[] arguments = (Object[])input.readObject();
                logger.info("参数类型:{},参数值:{}", JSON.toJSON(parameterTypes),JSON.toJSON(arguments));
                Method method = service.getMethod(methodName,parameterTypes);
                Object result = method.invoke(service.newInstance(),arguments);
                output = new ObjectOutputStream(client.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(client!=null){
                    try{
                        client.close();
                    }catch(IOException e){
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

服务端服务接口:

package rpc;
public interface EchoService {
     void echo(String ping);
}

服务端服务实现:

package rpc;
public class EchoServiceImpl implements EchoService{

    public void echo(String ping){
        System.out.println("打印ping执行:"+ping!=null?ping+"--im ok":"ping is null");
    }

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值