揭秘系统远程调用思想

前言

在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;
}


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


猜你喜欢

转载自blog.csdn.net/u012394095/article/details/79626846