likes
comments
collection
share

SkyWalking】改造SkyWalking实现性能低损耗采集Dubbo参数(二)

作者站长头像
站长
· 阅读数 9

一、背景

二、支持动态配置

参数采集在高采集频率、大参数的情况下,性能损耗消耗不小,增加应用的内存、CPU、网络开销,甚至影响业务的运行。

所以,我们需要能通过动态配置参数采集相关的配置项,例如:是否开始采集;每秒、每分钟采集的频率;不做参数采集的endpoint。

1 实现思路

SkyWalking原生提供了Agent端动态配置的功能,配置是从配置中心-->SkyWalking OapServer-->agent这个链路触发配置变更。

SkyWalking】改造SkyWalking实现性能低损耗采集Dubbo参数(二)

2 详细实现方案

2.1 配置更新延迟

agent端配置更新的延迟最大40(20+20)秒,对于agent端来说可以接受。

2.2 配置项

在配置中心修改agent相关的配置,agent配置key是configuration-discovery.default.agentConfigurations,对应的配置示例:

configurations:
  serviceA:
    race.sample_rate: 1000
    trace.ignore_path: /api/seller/seller/*
    # 新增收集参数的配置
    collectParam.limit_per_second: 10
    collectParam.ignore_path: com.company.user.api*
  serviceB:
    trace.sample_rate: 1000
    trace.ignore_path: /api/seller/seller/*
    # 新增收集参数的配置
    collectParam.limit_per_second: 80
    collectParam.ignore_path: com.company.user.api*

2.3 默认配置

现有的SkyWalking只支持一个一个应用动态配置,但是不支持全部应用提供默认的动态配置,例如,希望所有的应用采集参数的每秒频率都调整15,但是现有的SkyWalking配置方法,需要每个应用都配置一下这个配置项,这过于麻烦。

所以,可以改造一下SkyWalking服务端,提供一个全局默认配置,应用不单独配置,则取默认配置值。

服务端提供的查询service(即应用)对应的agent配置的现有接口如下:

// org.apache.skywalking.oap.server.receiver.configuration.discovery.handler.grpc.ConfigurationDiscoveryServiceHandler

public void fetchConfigurations(final ConfigurationSyncRequest request,
                                    final StreamObserver<Commands> responseObserver) {
        Commands.Builder commandsBuilder = Commands.newBuilder();

        AgentConfigurations agentConfigurations = agentConfigurationsWatcher.getAgentConfigurations(
            request.getService());
        if (null != agentConfigurations) {
            // 判断service旧的配置uuid和服务端配置的uuid是否一致,不一致说明配置有更新,需要返回新配置。一致则说明没更新,不用返回数据,这样可以减少服务端响应数据量,减少服务端和agent端做不必要的处理和网络开销
            if (disableMessageDigest || !Objects.equals(agentConfigurations.getUuid(), request.getUuid())) {
                ConfigurationDiscoveryCommand configurationDiscoveryCommand =
                    newAgentDynamicConfigCommand(agentConfigurations);
                commandsBuilder.addCommands(configurationDiscoveryCommand.serialize().build());
            }
        }
        responseObserver.onNext(commandsBuilder.build());
        responseObserver.onCompleted();
    }

我们增加了默认配置的设计,则fetchConfigurations()返回的配置应该是默认配置和应用自定义配置合并后的配置。

这里要重点关注uuid的设计,uuid是配置的hash值,新的uuid被设计成 hash(默认配置) + "_" + hash(service自定义配置),这样改造量小。

2.4 agent监听器

agent需要增加对应的监听器,来监听对应的key发生了变化。

这块可以参考SkyWalking现有的org.apache.skywalking.apm.agent.core.conf.dynamic.watcher.SpanLimitWatcher,这里不详述。

三、全链路参数采集

1 实现思路

如果某个链路被参数采集规则命中了要采样,我们希望这个链路中dubbo参数都要采集,毕竟一个链路里,部分span有参数,部分没有,排查效果不好,要采集就尽量都采集。

也就是需要单个链路的参数全采集的策略需要从头传到尾,链路中有这个标志,则采集参数。这个就需要在跨进程、跨线程的情况,提供一个采集参数的标志,能一直传递下去。

这个实现思路和SkyWalking的强制链路采样的设计是一样的。

这里笔者是借助SkyWalking的sw8跨进程协议里的Correlation Header项来实现,并没有像SkyWalking的强制链路采样那样使用Standard Header项,因为Correlation Header项更灵活,且改造Standard Header项担心出现协议兼容性问题。

2 详细实现方案

核心是调用下面的CorrelationContext.put()方法:

public class CorrelationContext {

    private final Map<String, String> data;
    public Optional<String> put(String key, String value) {
       ...
       data.put(key, value);
    }
}

// 设置全链路采集参数的标志
CorrelationContext.put("needCollectParam", "Y");

我们可以在网关这些入口应用根据配置决定当前链路是否采集参数,如果需要采集参数,则设置全链路采集参数的标志(全链路采集参数的标志)到CorrelationContext,SkyWalking agent将自动传递全链路采集参数的标志。

下面是一个全链路采集参数标志跨进程传递的示例图:

SkyWalking】改造SkyWalking实现性能低损耗采集Dubbo参数(二)

四、总结

文章主要讲了如何改造SkyWalking agent端和服务端实现低性能损耗采集Dubbo参数。

其中,有一些关键的要素:

  • 动态配置来控制采集频率、采集开关等。
  • 参数序列化使用了自定义的序列化工具类,通过异步执行、限定最大长度的方式实现低性能损耗。
  • 自动降级和恢复的方式控制风险。
  • 借助sw8协议的Correlation Header项传递参数采集标志,实现全链路参数采集。

希望能给大家带来帮助,转载请注明出处。