揭秘系统远程调用思想

前言

在xxl-job中存在调度中心远程调用执行器的过程, 在此可以很清晰的学习到远程调用的思想。  

常规的远程调用协议,dubbo, thrift , Protocol Buffer, 以及更轻量级的hessian协议。 这些市面上大同小异的

框架,在我们集成这些框架的时候,我们总会在调用端引用服务端暴露出来的接口,然后通过代理工厂创建代理对象

最终通过spring的自动注入的方式,在我们的容器里面生成代理对象,然后当我们需要使用的时候,直接使用代理对象,

就可以调用到另一个系统里面的服务了, 常规的配置我们都知道,但是真正内部是怎么实现的,为何我们调用代理

对象的方法,就能调用到远程系统的服务,这是我们值得思考的。 此处主要以xxl-job为原型,讲解这种模式, 其他专业

的RPC框架肯定比这个更加复杂, 所以这里主要是讲解一个思想。 

 

 

例:当A系统需要调用B系统的方法时,我们通常的做法是这样的。

B系统 暴露一个interface, 如:   exampleInterface.jar 

A系统引用exampleInterface.jar ,   同时在spring.xml 文件中配置B系统暴露出来的接口类(例:userService)

最终我们的使用方式如下: 

Example

@Authwired

 

UserService userService ;

  

public User getUsers(){

    return userService.getUsers()

}

上面那一段,非常简短的代码, 还原了一个非常简短的系统之间的调用, 但是为什么在A系统调用userService.getUsers() , 

这个方法最终就能调用到B系统去呢? 

 

代理对象生成

在xxl-job中调度中心需要调用执行器的代码, 下面来仔细分析一下、 

获取代理对象

public static ExecutorBiz getExecutorBiz(String address) throws Exception {

    // valid

    if (address==null || address.trim().length()==0) {

        return null;

    }

 

    // load-cache

    address = address.trim();

    // 从内存中获取执行器对象

    ExecutorBiz executorBiz = executorBizRepository.get(address);

    if (executorBiz != null) {

        return executorBiz;

    }

 

    // 通过代理类,创建代理对象,主要看这个创建过程

    executorBiz = (ExecutorBiz) new NetComClientProxy(ExecutorBiz.class, address, accessToken).getObject();

    // 放入内存中,以便下次调用

    executorBizRepository.put(address, executorBiz);

    return executorBiz;

}

从下面的代码中,可以看到NetComClientProxy 这个代理类,是一个factoryBean, 所以主要看他的getObject方法即可

NetComClientProxy

public class NetComClientProxy implements FactoryBean<Object> {

   private static final Logger logger = LoggerFactory.getLogger(NetComClientProxy.class);

 

 

   private Class<?> iface;

   private String serverAddress;

   private String accessToken;

   private JettyClient client = new JettyClient();

   public NetComClientProxy(Class<?> iface, String serverAddress, String accessToken) {

      this.iface = iface;

      this.serverAddress = serverAddress;

      this.accessToken = accessToken;

   }

 

   @Override

   public Object getObject() throws Exception {

      return Proxy.newProxyInstance(Thread.currentThread()

            .getContextClassLoader(), new Class[] { iface },

            new InvocationHandler() {

               @Override

               public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

 

                  // filter method like "Object.toString()"

                    if (Object.class.getName().equals(method.getDeclaringClass().getName())) {

                     logger.error(">>>>>>>>>>> xxl-rpc proxy class-method not support [{}.{}]", method.getDeclaringClass().getName(), method.getName());

                     throw new RuntimeException("xxl-rpc proxy class-method not support");

                  }

                   

                     //生成这个代理对象的invoke方法里面,并没有调用目标类的方法,而是将目标的信息发送给远程服务器。

                    RpcRequest request = new RpcRequest();

                    request.setServerAddress(serverAddress); // 服务器地址

                    request.setCreateMillisTime(System.currentTimeMillis()); // 创建时间, 用于判断请求是否超时

                    request.setAccessToken(accessToken);  // 数据校验

                    request.setClassName(method.getDeclaringClass().getName()); // 将目标类的class名称传给执行器,让那边来创建对象,并执行逻辑代码

                    request.setMethodName(method.getName());   // 方法名称为run

                    request.setParameterTypes(method.getParameterTypes());  // 参数类型

                    request.setParameters(args); // 参数

                    RpcResponse response = client.send(request); // 发送HTTP请求

                        

                       // valid response

                  if (response == null) {

                     logger.error(">>>>>>>>>>> xxl-rpc netty response not found.");

                     throw new Exception(">>>>>>>>>>> xxl-rpc netty response not found.");

                   }

                       if (response.isError()) {

                           throw new RuntimeException(response.getError());

                       else {

                           return response.getResult();

                       }

                       

               }

            });

   }

   @Override

   public Class<?> getObjectType() {

      return iface;

   }

   @Override

   public boolean isSingleton() {

      return false;

   }

 

}

从上面可以清晰的看到,生成的代理对象里面的 InvocationHandler 中的invoke方法,并没有真实的执行方法,而是将类名,方法名,参数等信息发送给远程服务器。 

仅仅只是做了这些操作而已。 这就是他的设计比较巧妙的地方。 

下面可以看一下,远程服务器拿到请求之后如何处理的。 

public static RpcResponse invokeService(RpcRequest request, Object serviceBean) {

   // request中的数据结构

   if (serviceBean==null) {

      //  这个serviceBean 就是在执行器启动的时候,initExecutorServer () 这个方法中,将一个ExecutorBiz的实例放进去了,此处通过

      // classname来获取这个实例

      serviceBean = serviceMap.get(request.getClassName());

   }

   if (serviceBean == null) {

      // TODO

   }

  

   RpcResponse response = new RpcResponse();

  // 判断是否超时

   if (System.currentTimeMillis() - request.getCreateMillisTime() > 180000) {

      response.setResult(new ReturnT<String>(ReturnT.FAIL_CODE, "The timestamp difference between admin and executor exceeds the limit."));

      return response;

   }

    // 数据校验,验证token是否匹配,前提是token不为空

   if (accessToken!=null && accessToken.trim().length()>0 && !accessToken.trim().equals(request.getAccessToken())) {

      response.setResult(new ReturnT<String>(ReturnT.FAIL_CODE, "The access token[" + request.getAccessToken() + "] is wrong."));

      return response;

   }

  

   try {

      // 获取class

      Class<?> serviceClass = serviceBean.getClass();

      // 拿到请求中的方法名字, 此处这个值 是  run 方法

      String methodName = request.getMethodName();

        //方法类型

      Class<?>[] parameterTypes = request.getParameterTypes();

      // 方法参数

      Object[] parameters = request.getParameters();

      // spring的工具类, 创建一个fastClass 实例

      FastClass serviceFastClass = FastClass.create(serviceClass);

      FastMethod serviceFastMethod = serviceFastClass.getMethod(methodName, parameterTypes);

       // 拿到方法之后执行方法的invoke ,

      Object result = serviceFastMethod.invoke(serviceBean, parameters);

  

      // 返回执行结果

      response.setResult(result);

   catch (Throwable t) {

      t.printStackTrace();

      response.setError(t.getMessage());

   }

  

   return response;

}

 

由此我们就可以知道,远程调用的大致思想。 

sharedCode源码交流群,欢迎喜欢阅读源码的朋友加群,添加下面的微信, 备注”加群“ 。 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值