RPC原理以及手写实现一个简单的RPC框架

一、RPC原理

一次完整的RPC过程(同步调用)如下图所示:
rpc

(1)服务消费方(client)以本地调用的方式调用服务;
(2)client stub 接收到调用后,负责将方法、参数等信息组装成能够进行网络传输的消息体;
(3)client stub 将消息发送给服务端;
(4)server stub 将收到的消息进行解码;
(5)server stub 将解码后的方法、参数等信息传递给本地服务;
(6)本地服务调用实际方法返回结果给 server stub;
(7)server stub 将返回结果打包成能够进行网络传输的消息体发送至消费方;
(8)client stub 收到消息,进行解码;
(9)服务消费方(client)得到最终的结果。

RPC框架的目标就是将(2)~(8)这些步骤封装起来,对消费方是透明的。 消费方以调用本地接口的方式直接得到结果。

二、手写实现一个简单的RPC框架

1.RPC核心模块的服务端

由于服务端要一直监听客户端的请求,所以这里使用netty开启一个服务,具体使用时由服务提供方调用start方法开启服务:

package com.gjy.rpc.provider;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
//网络处理服务器
public class RPCServer {
    private int port;
    public RPCServer(int port) {
        this.port = port;
    }
    public void start() {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap serverBootstrap = new ServerBootstrap();
            serverBootstrap.group(bossGroup, workerGroup).channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 128).childOption(ChannelOption.SO_KEEPALIVE, true)
                    .localAddress(port).childHandler(new ChannelInitializer<SocketChannel>() {
                @Override
                protected void initChannel(SocketChannel ch) throws Exception {
                    ChannelPipeline pipeline = ch.pipeline();
                    // 编码器
                    pipeline.addLast("encoder", new ObjectEncoder());
                    // 解码器
                    pipeline.addLast("decoder", new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                    // 服务器端业务处理类
                    pipeline.addLast(new InvokeHandler());
                }
            });
            ChannelFuture future = serverBootstrap.bind(port).sync();
            System.out.println("......server is ready......");
            future.channel().closeFuture().sync();
        } catch (Exception e) {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

服务端调用具体的实现类是在InvokeHandler中

package com.gjy.rpc.provider;
import java.lang.reflect.Method;
import java.util.Set;
import org.reflections.Reflections;
import com.gjy.rpc.params.ClassInfo;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
//服务器端业务处理类
public class InvokeHandler extends ChannelInboundHandlerAdapter {
    //得到某接口下某个实现类的名字
    private String getImplClassName(ClassInfo classInfo) throws Exception{
        //服务方实现类所在的包路径
        String interfacePath="com.gjy.rpc.server";
        Class superClass=Class.forName(classInfo.getClassName());
        Reflections reflections = new Reflections(interfacePath);
        //得到某接口下的所有实现类
        Set<Class> ImplClassSet=reflections.getSubTypesOf(superClass);
        if(ImplClassSet.size()==0){
            System.out.println("未找到实现类");
            return null;
        }else if(ImplClassSet.size()>1){
            System.out.println("找到多个实现类,未明确使用哪一个");
            return null;
        }else {
            //把集合转换为数组
            Class[] classes=ImplClassSet.toArray(new Class[0]);
            return classes[0].getName(); //得到实现类的名字
        }
    }
    @Override  //读取客户端发来的数据并通过反射调用实现类的方法
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        ClassInfo classInfo = (ClassInfo) msg;
        Object clazz = Class.forName(getImplClassName(classInfo)).newInstance();
        Method method = clazz.getClass().getMethod(classInfo.getMethodName(), classInfo.getTypes());
        //通过反射调用实现类的方法
        Object result = method.invoke(clazz, classInfo.getObjects());
        ctx.writeAndFlush(result);
    }
}

这里channelRead()方法就是获取msg中的内容,得到调用的类和方法名称,然后通过放射调用实现类的方法。

2.RPC核心模块客户端

在使用时,服务消费者方通过NettyRPCProxy发起请求,获取到服务提供方的代理对象之后调用对应的方法:

package com.gjy.rpc.invoker;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import com.gjy.rpc.params.ClassInfo;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.serialization.ClassResolvers;
import io.netty.handler.codec.serialization.ObjectDecoder;
import io.netty.handler.codec.serialization.ObjectEncoder;
//客户端代理类
public class NettyRPCProxy {
   // 根据接口创建代理对象
   public static Object create(Class target) {
      return Proxy.newProxyInstance(target.getClassLoader(), new Class[] { target }, new InvocationHandler() {
         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 封装ClassInfo
            ClassInfo classInfo = new ClassInfo();
            classInfo.setClassName(target.getName());
            classInfo.setMethodName(method.getName());
            classInfo.setObjects(args);
            classInfo.setTypes(method.getParameterTypes());

            // 开始用Netty发送数据
            EventLoopGroup group = new NioEventLoopGroup();
            ResultHandler resultHandler = new ResultHandler();
            try {
               Bootstrap b = new Bootstrap();
               b.group(group).channel(NioSocketChannel.class).handler(new ChannelInitializer<SocketChannel>() {
                  @Override
                  public void initChannel(SocketChannel ch) throws Exception {
                     ChannelPipeline pipeline = ch.pipeline();
                     // 编码器
                     pipeline.addLast("encoder", new ObjectEncoder());
                     // 解码器 构造方法第一个参数设置二进制数据的最大字节数 第二个参数设置具体使用哪个类解析器
                     pipeline.addLast("decoder",
                           new ObjectDecoder(Integer.MAX_VALUE, ClassResolvers.cacheDisabled(null)));
                     // 客户端业务处理类
                     pipeline.addLast("handler", resultHandler);
                  }
               });
               ChannelFuture future = b.connect("127.0.0.1", 9999).sync();
               future.channel().writeAndFlush(classInfo).sync();
               future.channel().closeFuture().sync();
            } finally {
               group.shutdownGracefully();
            }
            return resultHandler.getResponse();
         }
      });
   }
}

客户端执行远程调用滞后,服务方实际执行并返回结果,返回的结果在ResultHandler这个类中:

package com.gjy.rpc.invoker;
import io.netty.channel.*;
//客户端业务处理类
public class ResultHandler extends ChannelInboundHandlerAdapter {
   private Object response;
   public Object getResponse() {
      return response;
   }
   @Override // 读取服务器端返回的数据(远程调用的结果)
   public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
      response = msg;
      ctx.close();
   }
}

ClassInfo这个类封装了代理对象的信息,包括类名、方法名、参数等:

package com.gjy.rpc.params;
import java.io.Serializable;
//封装类信息
public class ClassInfo implements Serializable {
  private static final long serialVersionUID = 1L;
  private String className;  //类名
  private String methodName;//方法名
  private Class<?>[] types; //参数类型
  private Object[] objects;//参数列表
  public String getClassName() {
      return className;
  }
  public void setClassName(String className) {
      this.className = className;
  }
  public String getMethodName() {
      return methodName;
  }
  public void setMethodName(String methodName) {
      this.methodName = methodName;
  }

  public Class<?>[] getTypes() {
      return types;
  }
  public void setTypes(Class<?>[] types) {
      this.types = types;
  }
  public Object[] getObjects() {
      return objects;
  }
  public void setObjects(Object[] objects) {
      this.objects = objects;
  }
}
3.RPC框架具体使用

(1)定义抽象接口HelloRPC

package com.gjy.rpc;
public interface HelloRPC {
    String say(String name);
}

(2)服务提供者
先实现一个具体类HelloRPCImpl,里面重写say()方法。

然后调用RPCServer的start方法开启监听服务:

package com.gjy.rpc;
import com.gjy.rpc.provider.RPCServer;
public class RpcApplitation {
    public static void main(String[] args) throws Exception {
           new RPCServer(9999).start();
       }
}

(3)客户端(服务消费者)透明调用
通过NettyRPCProxy类的create方法向服务提供方请求一个HelloRPC代理对象,得到之后调用它的say方法

package com.gjy.rpc.proxy;
import com.gjy.rpc.HelloRPC;
import com.gjy.rpc.invoker.NettyRPCProxy;
public class RpcApplication {
   public static void main(String[] args) {
      HelloRPC helloRPC = (HelloRPC) NettyRPCProxy.create(HelloRPC.class);
      System.out.println(helloRPC.say("RPC"));
   }
}

THE END.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值