基于Socket开发的简单RPC框架

RPC是什么?

RPC全称是远程过程调用。 也是一种计算机通信协议。
可以把它拆开来理解。远程就是其他的计算机,过程就是服务,调用就是客户端去调用服务。
总的来说就是可以调用远程计算机上的服务。通俗的讲就是你可以调用你的本地服务,使用RPC就可以调用别的计算机上的服务。

RPC的好处

用一个东西肯定要知道它有什么好的,不然用它干啥?

  1. 对于RPC,它可以基于HTTP和TCP协议。
  2. RPC,使用自定义的TCP协议,可以让请求报文体积更小,这样传输效率就大大提高了。
  3. 对于序列化和反序列化的性能消耗,RPC可以通过thrift去实现二进制数据的高效传输。
  4. RPC的负载均衡方面,也有自带的策略,使用起来就不会像HTTP那样还要配置Nginx等。

RPC调用过程

知道是什么,有什么好处,那我们就get它!一般我们学技术都要知道原理和过程,光会用不理解是会出问题的。
在这里插入图片描述
这是RPC基本的调用过程,我们通过注册中心去接收服务端的一些信息,然后客户端要执行方法前去注册中心查一下服务在哪,查到就带着参数去调用服务。
看懂这个过程,我们可以用Socket去写一个简单的RPC了。
开发RPC框架之前要了解一下RPC应该写些什么功能,了解步骤的一些情况吧。
功能有两个:暴露服务和引用服务。
开发步骤:
1. 实现RPC框架
2. 定义服务接口,并实现服务接口(实现类)
3. 暴露服务
4. 引用服务(调用)


第一步,先写服务框架类RpcFramework

package rpc_socket;

import java.io.IOException;
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.ServerSocket;
import java.net.Socket;

//服务框架类
//暴露服务
//引用服务
public class RpcFramework {
    /**
     * 暴露服务,通过服务对象和服务端口号就能获得服务,也就相当于暴露服务给别人去调
     * @param service 服务对象
     * @param port 服务端口号
     * @throws Exception
     */
    public static void export(final Object service,int port) throws Exception{
        //保证代码的健壮性,对参数进行校验
        if(null==service){
            throw new IllegalArgumentException("service instance == null");
        }
        if(port<=0 || port > 65535){
            throw new IllegalArgumentException("Invalid port"+port);
        }
        //服务socket程序
        ServerSocket serverSocket = new ServerSocket(port);
        while (true){
            //创建一个Socket网络套接字
            final Socket socket = serverSocket.accept();
            //一个请求对应一个线程
            new Thread(new Runnable() {
                @Override
                public void run() {
                    //对象传输
                    ObjectInputStream objectInputStream = null;
                    ObjectOutputStream objectOutputStream = null;
                    try{
                        objectInputStream = new ObjectInputStream(socket.getInputStream());
                        //通过反射获取方法名
                        String methodName = objectInputStream.readUTF();
                        //获取方法参数的类型,泛型
                        Class<?>[] parameterTypes = (Class<?>[]) objectInputStream.readObject();
                        //获取方法实参 ,使用对象数组 类封装
                        Object[] arguments = (Object[]) objectInputStream.readObject();

                        //
                        objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                        //反射里面的Method,得到了方法
                        Method method = service.getClass().getMethod(methodName,parameterTypes);
                        //调用方法并返回结果
                        Object result = method.invoke(service, arguments);//调用方法,传object和方法参数
                        //将方法执行完之后的结果返回,响应客户端
                        objectOutputStream.writeObject(result);

                    }catch (Exception e){
                        e.printStackTrace();
                    }finally {
                        //倒着关闭资源
                        try{
                            objectInputStream.close();
                            objectOutputStream.close();
                            socket.close();
                        }catch (IOException e){
                            e.printStackTrace();
                        }
                    }
                }

            }).start();
        }
    }
    //不知道返回什么类型就返回泛型
    //通过host确定主机,通过port确定端口

    /**
     * 引用服务,可以让客户端去调的
     * @param interfaceClass
     * @param host
     * @param port
     * @param <T>
     * @return
     * @throws Exception
     */
    public static <T> T refer(final Class<T> interfaceClass,final String host,final int port) throws Exception{
        if(interfaceClass == null){
            throw new IllegalArgumentException("interface class == null");
        }
        //动态代理方式 , java 原生的动态代理对象,必须要实现接口
        if(! interfaceClass.isInterface()){
            throw new IllegalArgumentException("接口"+interfaceClass.getName()+"必须是接口类!!");
        }
        if(host == null || host.length() == 0){
            throw new IllegalArgumentException("host == null");
        }
        if(port<=0 || port > 65535){
            throw new IllegalArgumentException("Invalid port"+port);
        }
        //动态代理使用Proxy类,传入三个参数
        return (T) Proxy.newProxyInstance(
                //代理对象的类加载器
                interfaceClass.getClassLoader(),
                //代理对象的实现的接口的数据,数组是因为我们可以实现多个接口
                new Class<?>[]{interfaceClass},
                //使用动态代理的处理内部类方法
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //先构建Socket套接字  和服务端建立连接
                        Socket socket = new Socket(host,port);
                        ObjectOutputStream objectOutputStream = null;
                        ObjectInputStream objectInputStream = null;
                        try{
                            //服务端给啥。我们接啥
                            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                            objectOutputStream.writeUTF(method.getName());
                            objectOutputStream.writeObject(method.getParameterTypes());
                            objectOutputStream.writeObject(args);

                            //接住服务端给的result
                            objectInputStream = new ObjectInputStream(socket.getInputStream());
                            Object result = objectInputStream.readObject();

                            //返回对象
                            return result;
                        }catch (Exception e){
                            e.printStackTrace();
                        }finally {
                            //关闭资源
                            objectInputStream.close();
                            objectOutputStream.close();
                            socket.close();
                        }
                        return null;
                    }
                });
    }
}

注释写的很清楚了,我们这个类有两个方法,一个暴露服务,一个引用服务,
用到了反射技术去获取参数,服务方法等一些信息,
对于不确定的返回内容使用泛型可以缓解你的选择恐惧症,
还用到了jdk动态代理(一定要实现接口,没有接口就用不了),
还需要注意的是,使用流和socket,要正着开,反着关,否则容易出现泄露,异常错误一些的情况。


第二步:写个服务接口和实现类

//服务接口,say "hello world"
public interface HelloService {
    String hello(String name);
}


//服务接口实现类,代码中调用接口,因为实现类实现了接口,那么调用接口时就会自动找到实现类
public class HelloServiceImpl implements HelloService{

    @Override
    public String hello(String name) {
        return "hello"+name;
    }
}


第三步:开发生产方(server)和消费方(consumer)

//服务提供者: 暴露服务/发布服务
public class RpcProduce {
    public static void main(String[] args) throws Exception {//这个最好自己写自定义的异常,写Exception不好
        HelloService helloService = new HelloServiceImpl();
        //通过rpc框架的 export 方法开始暴露服务
        RpcFramework.export(helloService,6666);
    }
}

//服务消费者 : 消费服务,调用服务接口里面的方法的
public class RpcConsumer {
    public static void main(String[] args) throws Exception {
        //消费方是在另一台机器上,不能去new 接口去调用的
        //所以我们用RPC框架的引用服务,所以这里实际是一个代理对象
        HelloService helloService = RpcFramework.refer(HelloService.class,"localhost",6666);
        //调用服务对象的方法
        for (int i = 0; i < 10; i++) {
            String hello = helloService.hello("world"+i);
            System.out.println(hello);
            Thread.sleep(1000);
        }
    }
}


最后测试:
先打开服务方,再打开调用方,结果如下:
在这里插入图片描述
一个简单的基于Socket的RPC框架就让我们实现了 。
rpc主要用于公司内部的服务调用,性能消耗低,传输高效,服务治理很方便。相对HTTP用于对外的异构环境,浏览器接口调用,app和第三方接口调用来说使用更优!
当然最重要的还是那个流程图,一定要理解,对学微服务,分布式,SOA啥的都有很大帮助!
如果觉得写的还不错的话,请点点关注喔!
在这里插入图片描述

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值