关于repeater录制的流量子调用的identity中带有~S的情况

前段时间同事问我,我们录制的流量中,尤其是dubbo的子调用显示经常他的末尾会带上一个小尾巴这个是什么意思呢,其实之前我没有太在意这个事情,只是同事这么疑问了,确实激起了好奇心,所以就差了下 到底是什么

我们先看下是什么样的现象, 如下图所示:
在这里插入图片描述

如上, 这里就会跟上一个~AP 的小尾巴,那这个到底是什么意思呢,是某个hash的结果吗?要了解这个,其实也不难,我们就看下dubbo子调用的identity是怎么赋值的就好了。所以我们来看下具体的逻辑

在录制过程中,事件过来的时候,如果是Before的情况下,就会进入如下内容,

protected void doBefore(BeforeEvent event) throws ProcessControlException {
    // 回放流量;如果是入口则放弃;子调用则进行mock
    if (RepeatCache.isRepeatFlow(Tracer.getTraceId())) {
        processor.doMock(event, entrance, invokeType);
        return;
    }
    Invocation invocation = initInvocation(event);
    invocation.setStart(System.currentTimeMillis());
    invocation.setTraceId(Tracer.getTraceId());
    invocation.setIndex(entrance ? 0 : SequenceGenerator.generate(Tracer.getTraceId()));
    invocation.setIdentity(processor.assembleIdentity(event));
    invocation.setEntrance(entrance);
    invocation.setType(invokeType);
    invocation.setProcessId(event.processId);
    invocation.setInvokeId(event.invokeId);
    invocation.setRequest(processor.assembleRequest(event));
    invocation.setResponse(processor.assembleResponse(event));
    invocation.setSerializeToken(ClassloaderBridge.instance().encode(event.javaClassLoader));
    try {
        // fix issue#14 : useGeneratedKeys
        if (processor.inTimeSerializeRequest(invocation, event)) {
            SerializerWrapper.inTimeSerialize(invocation);
        }
    } catch (SerializeException e) {
        Tracer.getContext().setSampled(false);
        log.error("Error occurred serialize", e);
    }
    RecordCache.cacheInvocation(event.invokeId, invocation);
}

所以identity 是经过各自的子调用处理器处理后生成的标识。这里我们具体的就是dubbo的调用,所以我们看下 DubboConsumerInvocationProcessor 的逻辑

public Identity assembleIdentity(BeforeEvent event) {
    Object invoker;
    Object invocation;
    if (ON_RESPONSE.equals(event.javaMethodName)) {
        // for record identity assemble
        // onResponse(Result appResponse, Invoker<?> invoker, Invocation invocation) {}
        invoker = event.argumentArray[1];
        invocation = event.argumentArray[2];
    } else {
        // for repeater identity assemble
        // invoke(Invoker<?> invoker, Invocation invocation)
        invoker = event.argumentArray[0];
        invocation = event.argumentArray[1];
    }

    try {
        // methodName
        String methodName = (String) MethodUtils.invokeMethod(invocation, "getMethodName");
        Class<?>[] parameterTypes = (Class<?>[]) MethodUtils.invokeMethod(invocation, "getParameterTypes");
        // interfaceName
        String  interfaceName = ((Class)MethodUtils.invokeMethod(invoker, "getInterface")).getCanonicalName();
        return new Identity(InvokeType.DUBBO.name(), interfaceName, getMethodDesc(methodName, parameterTypes), getExtra());
    } catch (Exception e) {
        // ignore
        LogUtil.error("error occurred when assemble dubbo request", e);
    }
    return new Identity(InvokeType.DUBBO.name(), "unknown", "unknown", null);
}

我们看到Identity 直接就是通过 new Identity(InvokeType.DUBBO.name(), interfaceName, getMethodDesc(methodName, parameterTypes), getExtra()) 完成的。 所以这的参数按照前面截图的来看的话,

  1. InvokeType.DUBBO.name()dubbo,

  2. interfaceName 则是 com.xx.xx.api.service.UserAgreementApiService

  3. getMethodDesc(methodName, parameterTypes) 这个看了下实现原来就是我们一直在寻找的带小尾巴的原因了, 我们来看下。

    protected String getMethodDesc(String methodName, Class<?>[] parameterTypes) {
            StringBuilder builder = new StringBuilder(methodName);
            if (parameterTypes != null && parameterTypes.length > 0) {
                builder.append("~");
                for (Class<?> parameterType : parameterTypes) {
                    String className = parameterType.getSimpleName();
                    builder.append(className.subSequence(0, 1));
                }
            }
            return builder.toString();
        }
    

    我们可以看到它这里就是就是在拼接方法跟参数类型,不过他没有拿参数类型的所有内容,而是参数类型的类名,比如说 Java.lang.String 结果就是string 了, 然后取这里的第一个字符。

  4. getExtra() 暂时位置,看着是http的query的字段, 不过看到所有的录制的子调用录制逻辑基本都是null, 我们这里暂时就先忽略了。

那我们就直接看下Identity 的构造函数是怎么样的吧。

public Identity(String scheme, String location, String endpoint, Map<String, String> extra) {
        this.scheme = scheme;
        this.location = location;
        this.endpoint = endpoint;
        this.extra = extra;
        StringBuilder sb = new StringBuilder();
        sb.append(scheme).append(HOST_SPLITTER).append(Joiner.on("/").join(location, endpoint));
        if (extra != null && !extra.isEmpty()) {
            boolean firstKey = true;
            for (Map.Entry<String, String> entry : extra.entrySet()) {
                if (firstKey) {
                    firstKey = false;
                    sb.append(QUERY_STRING_COLLECTOR);
                } else {
                    sb.append(KEY_VALUE_SPLITTER);
                }
                sb.append(entry.getKey()).append(KEY_VALUE_COLLECTOR).append(entry.getValue());
            }
        }
        this.uri = sb.toString();
    }

可以看到构造函数里面的逻辑其实重点是在构造一个uri, 而这个uri 应该就是我们最开始截图的时候看到的identity的内容了。 其实就是通过各个分隔符连接起来,构成我们传参进来的数据。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值