1. 程式人生 > >(十五)RpcContext物件

(十五)RpcContext物件

比如現在消費者A呼叫提供者B,
在RPC呼叫之前,消費者可以呼叫RpcContext.getContext().setAttachment(key, value);
設定一些隱含引數,然後在提供者B可以通過RpcContext.getContext().getAttachment(key);拿到key的value。
如果B又接著呼叫C,那麼RpcContext則會放著B的呼叫C的引數,而之前A呼叫B的引數已經是不存在的。
直接看下RpcContext的原始碼。

public class RpcContext {
    
    private static final ThreadLocal<RpcContext> LOCAL = new ThreadLocal<RpcContext>() {
        @Override
        protected RpcContext initialValue() {
            return new RpcContext();
        }
    };

    /**
     * get context.
     *
     * @return context
     */
    public static RpcContext getContext() {
        return LOCAL.get();
    }

    //省略其他屬性
    private Future<?> future;

    private final Map<String, String> attachments = new HashMap<String, String>();

        /**
     * get attachment.
     *
     * @param key
     * @return attachment
     */
    public String getAttachment(String key) {
        return attachments.get(key);
    }

    /**
     * set attachment.
     *
     * @param key
     * @param value
     * @return context
     */
    public RpcContext setAttachment(String key, String value) {
        if (value == null) {
            attachments.remove(key);
        } else {
            attachments.put(key, value);
        }
        return this;
    }


看出來RpcContext物件是繫結線上程臨時變數LOCAL上,所以可以通過執行緒臨時變數來獲取到RpcContext的相關引數值。
下面看看RPC呼叫時是怎麼將RpcContext上的引數傳輸給提供者的,類com.alibaba.dubbo.rpc.protocol.AbstractInvoker<T>的一段原始碼:

public Result invoke(Invocation inv) throws RpcException {
    if(destroyed) {
        throw new RpcException("Rpc invoker for service " + this + " on consumer " + NetUtils.getLocalHost()
                                        + " use dubbo version " + Version.getVersion()
                                        + " is DESTROYED, can not be invoked any more!");
    }
    RpcInvocation invocation = (RpcInvocation) inv;
    invocation.setInvoker(this);
    if (attachment != null && attachment.size() > 0) {
        invocation.addAttachmentsIfAbsent(attachment);
    }
    Map<String, String> context = RpcContext.getContext().getAttachments(); //41處
    if (context != null) {
        invocation.addAttachmentsIfAbsent(context);
    }
    if (getUrl().getMethodParameter(invocation.getMethodName(), Constants.ASYNC_KEY, false)){
        invocation.setAttachment(Constants.ASYNC_KEY, Boolean.TRUE.toString());
    }
    RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);
    
    
    try {
        return doInvoke(invocation);
    } catch (InvocationTargetException e) { // biz exception
        Throwable te = e.getTargetException();
        if (te == null) {
            return new RpcResult(e);
        } else {
            if (te instanceof RpcException) {
                ((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
            }
            return new RpcResult(te);
        }
    } catch (RpcException e) {
        if (e.isBiz()) {
            return new RpcResult(e);
        } else {
            throw e;
        }
    } catch (Throwable e) {
        return new RpcResult(e);
    }
}

protected abstract Result doInvoke(Invocation invocation) throws Throwable;


在呼叫提供者之前,在原始碼“41處”,會獲取當前執行緒臨時變數裡的RpcContext物件,再將RpcContext物件裡的引數設定到Invocation物件,最後呼叫doInvoke(Invocation invocation)方法,就會發送引數給提供者。

自己寫了個RPC:

可以給個star,^0^.