一、背景
承接上一篇【SkyWalking】改造SkyWalking实现性能低损耗采集Dubbo参数(一),本篇讲解后续的部分。
二、支持动态配置
参数采集在高采集频率、大参数的情况下,性能损耗消耗不小,增加应用的内存、CPU、网络开销,甚至影响业务的运行。
所以,我们需要能通过动态配置参数采集相关的配置项,例如:是否开始采集;每秒、每分钟采集的频率;不做参数采集的endpoint。
1 实现思路
SkyWalking原生提供了Agent端动态配置的功能,配置是从配置中心-->SkyWalking OapServer-->agent这个链路触发配置变更。
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 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项担心出现协议兼容性问题。
关于sw8跨进程协议,可以参考笔者的文章:
【SkyWalking】SkyWalking是如何实现跨进程传播链路数据?
【SkyWalking】巧用SkyWalking实现全链路传递userId等自定义数据
2 详细实现方案
核心是调用下面的CorrelationContext.put()
方法:
public class CorrelationContext {
private final Map data;
public Optional put(String key, String value) {
...
data.put(key, value);
}
}
// 设置全链路采集参数的标志
CorrelationContext.put("needCollectParam", "Y");
我们可以在网关这些入口应用根据配置决定当前链路是否采集参数,如果需要采集参数,则设置全链路采集参数的标志(全链路采集参数的标志)到CorrelationContext,SkyWalking agent将自动传递全链路采集参数的标志。
下面是一个全链路采集参数标志跨进程传递的示例图:
四、总结
文章主要讲了如何改造SkyWalking agent端和服务端实现低性能损耗采集Dubbo参数。
其中,有一些关键的要素:
- 动态配置来控制采集频率、采集开关等。
- 参数序列化使用了自定义的序列化工具类,通过异步执行、限定最大长度的方式实现低性能损耗。
- 自动降级和恢复的方式控制风险。
- 借助sw8协议的Correlation Header项传递参数采集标志,实现全链路参数采集。
希望能给大家带来帮助,转载请注明出处。