Skip to content

Implement_callback

Geng Zhang edited this page Feb 19, 2017 · 4 revisions

Callback实现细节

设计

需求

Callback指的是服务端调用客户端(多次)。 主要依赖的是服务端客户客户端的长连接,连接两端都可以发起请求。

接口设计

public interface Callback<Q, S> {

    /**
     * 回调通知
     *
     * @param result 通知对象
     * @return 返回值对象 s
     */
    S notify(Q result);
}

实现

1.启动阶段

不管是服务端发布者还是服务调用者,在启动时候都会扫描接口下的每个方法、 如果满足如下条件,则保存,要不然就报错。

  1. 不存在多个Callback参数,只能一个
  2. Callback类型必须指定类型,不能不指定或者泛型。 正确:例如:Callback<List,String>, 错误:Callback, Callback,?>, Callback<List<?>,String>, Callback<List<List>,String>,

记录到缓存中,格式为:{接口+方法名:实际类型}

2.Callback生成

  1. 客户端在调用有Callback参数的方法(以下简称Callback方法)的时候,会传递一个Callback实现类,例如:

    helloService.register(param, new Callback<List<String>, String>() {
            @Override
            public String notify(List<String> result) {
               // do something
            }
        });
  2. 发现这个方法是一个Callback方法(之前启动阶段已解析),进行特殊处理

    if (CallbackUtils.hasCallbackParameter(key)) {
       CallbackUtils.preMsgSend(request, this.channel);
    }
  3. 先对这个实现类生成一个CallbackInsKey,保留CallbackInsKey和实现类的关系

  4. 虚拟一个CallbackStub对象(保存CallbackInsKey),然后将Callback方法的Callback参数替换为CallbackStub

    request.getArgs()[i] = new CallbackStub<>(callbackInsKey, reqClass);
  5. 视为一个正常请求,发送给服务端

服务端收到请求后,会经过服务端处理:

  1. 发现这个方法是一个Callback方法(之前启动阶段已解析),进行特殊处理

    if (CallbackUtils.hasCallbackParameter(methodKey)) {
        CallbackUtils.preMsgHandle(request, channel);
    }
  2. 先根据当前长连接(客户端->服务端)生成一个反向长连接(服务端->客户端),按连接信息缓存起来

    // 使用一个已有的channel虚拟化一个反向长连接
    ClientTransport clientTransport = ClientTransportFactory.getReverseClientTransport(channel);
  3. 读取Callback方法的CallbackStub参数,然后将反向长连接设置到Stub中

    stub.setClientTransport(clientTransport).initByMessage(request);
  4. 视为一个正常请求,进行调用。此时服务端实现类能拿到Callback对象。

3.Callback使用

  1. 服务端拿到Callback代理类(其实是CallbackStub)后可以调用notify(Q)方法。

  2. 服务端的CallbackStub拦截方法,拼装为RpcRequest,然后设置Head里的CallbackInsKey为stub的CallbackInsKey

  3. 服务端通过反向长连接,发送请求到客户端

  4. 客户端收到RpcRequest,发现Head里面带CallbackInsKey关键字,生成一个CallbackTask,异步执行

    if (callbackInsKey != null) { // 服务端发来的callback请求
        CallbackTask task = new CallbackTask(request, clientTransport.getChannel());
        AsyncContext.getAsyncThreadPool().execute(task);
    }
  5. 客户端根据CallbackInsKey找到真正的Callback实例。

    Callback callback = CallbackContext.getCallbackIns(callbackInsKey);
  6. 客户端执行真正调用后,作为一个正常的RpcResponse返回。

  7. 服务端收到响应后,根据连接信息,找到反向长连接,执行回复事件。

    ClientTransport clientTransport = ClientTransportFactory.getReverseClientTransport(channelKey);
    clientTransport.receiveRpcResponse(response);

资源回收

服务端

服务端主要维护如下:

  1. Callback方法缓存:启动时扫描加载,不回收。
  2. 反向长连接列表:在客户端断开长连接时候进行回收。

客户端

客户端主要维护如下:

  1. Callback方法缓存:启动时扫描加载,不回收。
  2. Callback实例列表:保留了callbackInsKey与真正Callback实例的对应关系**???**
Clone this wiki locally