RPC-手写一个RPC框架

基于上一节的内容RPC-准备阶段,自己实现一个基本的RPC框架。

一、服务端代码

1. 可以注册的服务端框架

public class RpcServerFrameReg {
    //线程池
    private static ExecutorService executorService = Executors.newFixedThreadPool(
            Runtime.getRuntime().availableProcessors() );
    //用来容纳 接口名和接口实现类的 的容器
   private static final Map<String, Class> serviceHolder = new HashMap<>();
    private int port;
    public RpcServerFrameReg(int port) {
        this.port = port;
    }

    /**
     * 注册服务,就是将接口和实现类存储到上边定义的serviceHolder中,
     * @param serviceInterface 接口
     * @param impl             接口的实现类
     * @throws IOException
     */
    public void registerService(Class<?> serviceInterface, Class impl) throws IOException {
        //初始化Sockt
        Socket socket = null;
        ObjectInputStream inputStream = null;
        ObjectOutputStream outputStream = null;
        try {
            socket = new Socket();
            // 连接服务器,我们像这个服务器传入
             //       接口的名字,发送的ip,端口,以便将来能够通过接口名查询到往哪个服务器上发送信息

              // 同时将接口和实现类的映射在serviceHolder实现
            socket.connect( new InetSocketAddress( "127.0.0.1", 9999 ) );
            //初始化  ObjectOutputStream
            outputStream = new ObjectOutputStream( socket.getOutputStream() );
            //写索引
            outputStream.writeBoolean( false );
            //写接口名
            outputStream.writeUTF( serviceInterface.getName() );
            //写1P
            outputStream.writeUTF( "127.0.0.1" );
          //写端口
            outputStream.writeInt( port );
          // 清理缓存
            outputStream.flush();
            // 初始化 ObjectInputStream,但是他的意义  
            inputStream = new ObjectInputStream( socket.getInputStream() );
            if (inputStream.readBoolean()) {
                serviceHolder.put( serviceInterface.getName(), impl );
            }
        } finally {
            if (socket != null) socket.close();
            if (inputStream != null) inputStream.close();
            if (outputStream != null) outputStream.close();
        }


    }

    //任务类,服务器启动服务后,如果有请求过来了,就会执行run方法
    private static class ServerTask implements Runnable {
        private Socket client = null;
        //       构造函数,用来初始化
        public ServerTask(Socket client) {
            this.client = client;
        }
        @Override
        public void run() {
            try {
                ObjectInputStream inputStream = new ObjectInputStream( client.getInputStream() );
                ObjectOutputStream outputStream = new ObjectOutputStream( client.getOutputStream() );
                //获取方法的类名
                String className = inputStream.readUTF();
                //获取方法的接口名
                String methodName = inputStream.readUTF();
                //获取方法参数类型
                Class[] paramType = (Class[]) inputStream.readObject();
                //获取方法的传入参数
                Object param = inputStream.readObject();


                //得到类
                Class serverClass = serviceHolder.get( className );
                if (serverClass == null) {
                    throw new ClassNotFoundException( className + "not found" );
                }
                //根据类,得到方法
                Method method = serverClass.getMethod( methodName, paramType );
                //根据方法,类实例,参数得到结果
                Object result = method.invoke( serverClass.newInstance(), param );

                //写到流里边。
                outputStream.writeObject( result );
                outputStream.flush();//返回去

            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                try {
                    if (client != null) client.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
//启动服务的方法
    public void startService() throws IOException {
        //创建服务端对象,
        ServerSocket serverSocket = new ServerSocket();
        serverSocket.bind( new InetSocketAddress( port ) );
        try {
            while (true) {
                //线程池处理任务
                executorService.execute( new ServerTask( serverSocket.accept() ) );
            }
        } finally {
            serverSocket.close();
        }
    }
}

2、服务端启动的时候,调用该框架

public class Test {
    public static void main(String[] args) throws IOException {
        RpcServerFrameReg regFram = new RpcServerFrameReg( 9999 );
        //这里可以是很多很多的实现类,需要依次列下来
        regFram.registerService( SendSms.class,SendSmsImpl.class );
        regFram.startService();
    }
}

二、客户端代码

1、客户端的框架


public class RpcClientFrameReg {
    //获取远程代理对象的方法,这个方法是简单的动态代理的方法,语法糖可以见上一章,动态代理的用法
    public static  <T> T getRemoteProxyObj(final Class<T> serviceInterface){
        final InetSocketAddress inetSocketAddress = new InetSocketAddress( "127.0.0.1",9999 );

        return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(),new Class[]{serviceInterface.getClass()},new DynProxy( serviceInterface,inetSocketAddress ));
    }


    /**
     * 动态代理类
     *  这个类的作用就是,传入对应的方法,以及参数,类找到对应的服务器,将类,方法,参数穿过去, 拿到返回的结果。
     */
    private static class DynProxy implements InvocationHandler {
        private final Class<?> serviceInterface;
        private final InetSocketAddress inetSocketAddress;
        private RegisterServiceVo[] serviceArray;//这个里边放的是远程服务器的地址信息
    
      //构造函数,传入的是接口,和地址信息
        public DynProxy(Class<?> serviceInterface, InetSocketAddress inetSocketAddress) {
            this.serviceInterface = serviceInterface;
            this.inetSocketAddress = inetSocketAddress;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Socket socket = null;
            ObjectInputStream inputStream = null;
            ObjectOutputStream outputStream = null;

            //第一步: 这个if判断用来获取远程服务器的信息,根据传入的接口,判断有哪些服务器能够调用
            if (serviceArray == null) {
                try {
                    socket = new Socket();
                    socket.connect( inetSocketAddress );//去这台服务器,通过接口名获取要访问的接口信息
                    outputStream = new ObjectOutputStream( socket.getOutputStream() );
                    outputStream.writeBoolean( true );
                    outputStream.writeUTF( serviceInterface.getName() );//传入接口的名字
                    outputStream.flush();
                    inputStream = new ObjectInputStream( socket.getInputStream() );
                    Set<RegisterServiceVo> set = (Set<RegisterServiceVo>) inputStream.readObject();//拿到接口信息
                    serviceArray = new RegisterServiceVo[set.size()];
                    set.toArray( serviceArray );//将服务器信息塞进去。

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

            }

          //第二部 、从上一步拿到的服务器列表中,随机(可以用负载均衡的算法)获取一台服务器,然后传入接口名, 方法名,参数类型,参数,拿到返回值,返回即可
            Random random = new Random();
            int index = random.nextInt( serviceArray.length );
            RegisterServiceVo vo = serviceArray[index];//随机获取一个服务器
            InetSocketAddress socketAddress = new InetSocketAddress( vo.getHost(), vo.getPort() );  //远程服务器链接地址。


            try {
                socket = new Socket();
                socket.connect( socketAddress );
                outputStream = new ObjectOutputStream( socket.getOutputStream() );
                outputStream.writeUTF( serviceInterface.getName() );//将方法名和
                outputStream.writeUTF( method.getName() );
                outputStream.writeObject( method.getParameterTypes() );
                outputStream.writeObject( args );
                outputStream.flush();

                inputStream = new ObjectInputStream( socket.getInputStream() );
                return inputStream.readObject();
            } finally {
                if (socket != null) socket.close();
                if (outputStream != null) outputStream.close();
                if (inputStream != null) inputStream.close();
            }
        }
    }


}

上边代码中用到了RegisterServiceVo这个类,这个类就是一个简单的ip端口的bean,如下

   private final  String host;
    private final int port;
    public RegisterServiceVo(String host, int port) {
        this.host = host;
        this.port = port;
    }
    public String getHost() {
        return host;
    }
    public int getPort() {
        return port;
    }

当客户端需要访问服务端的方法的时候,这样处理

  SendSms sendSms =RpcClientFrameReg.getRemoteProxyObj(SendSms.class);
  //然后就可以调用服务端的sendSms这个类的各个方法了
  sendSms.sendMail("123");//SendSms是自己实现的一个类
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
大学生参加学科竞赛有着诸多好处,不仅有助于个人综合素质的提升,还能为未来职业发展奠定良好基础。以下是一些分析: 首先,学科竞赛是提高专业知识和技能水平的有效途径。通过参与竞赛,学生不仅能够深入学习相关专业知识,还能够接触到最新的科研成果和技术发展趋势。这有助于拓展学生的学科视野,使其对专业领域有更深刻的理解。在竞赛过程中,学生通常需要解决实际问题,这锻炼了他们独立思考和解决问题的能力。 其次,学科竞赛培养了学生的团队合作精神。许多竞赛项目需要团队协作来完成,这促使学生学会有效地与他人合作、协调分工。在团队合作中,学生们能够学到如何有效沟通、共同制定目标和分工合作,这对于日后进入职场具有重要意义。 此外,学科竞赛是提高学生综合能力的一种途径。竞赛项目通常会涉及到理论知识、实际操作和创新思维等多个方面,要求参赛者具备全面的素质。在竞赛过程中,学生不仅需要展现自己的专业知识,还需要具备创新意识和解决问题的能力。这种全面的综合能力培养对于未来从事各类职业都具有积极作用。 此外,学科竞赛可以为学生提供展示自我、树立信心的机会。通过比赛的舞台,学生有机会展现自己在专业领域的优势,得到他人的认可和赞誉。这对于培养学生的自信心和自我价值感非常重要,有助于他们更加积极主动地投入学习和未来的职业生涯。 最后,学科竞赛对于个人职业发展具有积极的助推作用。在竞赛中脱颖而出的学生通常能够引起企业、研究机构等用人单位的关注。获得竞赛奖项不仅可以作为个人履历的亮点,还可以为进入理想的工作岗位提供有力的支持。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值