RPC框架中动态代理的个人理解

之前看到设计一个RPC框架的时候有说到Client端用到了动态代理技术,一直不太明白动态代理有什么作用,用在了什么地方,今天写一个博客来记录一下。

RPC框架最重要的三个点:

1. 编解码(序列化与反序列化)

2. 通信协议

3. 网络传输

而在Client端使用动态代理的作用是什么呢?

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. 服务消费方得到最终结果。

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

 

其实就是为了透明化远程服务调用

怎么封装通信细节才能让用户像以本地调用方式调用远程服务呢?对java来说就是使用代理!

java代理有两种方式:1) jdk 动态代理;2)字节码生成。尽管字节码生成方式实现的代理更为强大和高效,但代码维护不易,大部分公司实现RPC框架时还是选择动态代理方式。

下面简单介绍下动态代理怎么实现我们的需求。

我们需要实现一个OrderService服务类,来模拟位于远程服务器上Service类。

import rpc.cyb.service.OrderService;

public class OrderServiceImpl implements OrderService {

    //模拟获取一个订单
    @Override
    public String getOrderNum() {
        return "获取的订单号为:129023929384745";
    }
}

之后我们需要实现一个本地注册的类LocalRegister,目的是为了实现接口名和实现类的映射,比如我们传入接口OrderService.class,程序就知道我们要使用的是OrderService的哪个实现类了。

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

//本地注册
public class LocalRegister {

    //存放接口名和实现类的映射
    private static Map<String, Class<?>> interfacesMap = new ConcurrentHashMap<>();

    /**
     * 接口名和实现类的映射
     * @param interfaceName 接口的全类名
     * @param implClass 对应实现类的class对象
     */
    public static void regist(String interfaceName, Class<?> implClass) {
        interfacesMap.put(interfaceName, implClass);
    }

    public static Class<?> get(String interfaceName) {
       return interfacesMap.get(interfaceName);
    }
}

实现了上面两步我们就可以来写最重要的Proxy代理类了,代理类的invoke方法中封装了与远端服务通信的细节。

import rpc.cyb.register.LocalRegister;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.concurrent.TimeUnit;

public class MyProxy implements InvocationHandler {

    //需要代理的目标对象
    private Object target;

    public MyProxy(Object target){
        this.target = target;
    }

    //获取代理对象
    public static Object getProxy(Class<?> target){
        //从本地注册中获取实现类的Class对象
        String interfaceName = target.getName();
        Class<?> implClass = LocalRegister.get(interfaceName);

        MyProxy invocationHandler = null;
        try {
            //用的是接口实现类来实例化对象
            invocationHandler = new MyProxy(implClass.newInstance());
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        assert invocationHandler != null;
        return Proxy.newProxyInstance(target.getClassLoader(), implClass.getInterfaces(),invocationHandler);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // ...执行通信相关逻辑
        // ...
        System.out.println("发起RPC调用...");
        TimeUnit.SECONDS.sleep(1);
        System.out.println("收到远程反馈");
        System.out.println("反序列化...");
        System.out.println("得到结果:");
        return method.invoke(target,args);
    }

}

最后是消费端,使用了动态代理,用户可以像调用本地方法一样调用远程方法

import rpc.cyb.proxy.MyProxy;
import rpc.cyb.register.LocalRegister;
import rpc.cyb.service.OrderService;
import rpc.cyb.service.UserService;
import rpc.cyb.service.impl.OrderServiceImpl;
import rpc.cyb.service.impl.UserServiceImpl;

public class RpcClient {

    static {
        //初始化接口和实现类对应关系
        LocalRegister.regist("rpc.cyb.service.UserService", UserServiceImpl.class);
        LocalRegister.regist("rpc.cyb.service.OrderService", OrderServiceImpl.class);
    }

    public static void main(String[] args) {
        //用户可以像调用本地方法一样调用远程方法

        OrderService orderService = (OrderService) MyProxy.getProxy(OrderService.class);
        System.out.println(orderService.getOrderNum());
    }
}

运行结果如下:

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值