本文基于 SkyWalking-Java-agent 8.15.0 版本
客户端信息上报
ServiceManagementClient
服务管理客户端,作用主要有
- 将当前 Agent Client 的基本信息汇报给 OAP
- 和 OAP 保持心跳
共实现了三个接口
BootService:agent服务接口GRPCChannelListener:gRpc 的Channel监听接口Runnalbe:JDK 的 Runnable 接口
一个注解:@DefaultImplementor 默认实现
GRPCChannelListener
实现 GRPCChannelListener 的 statusChanged 状态改变方法,当gRpc Channel 的连接状态(GRPCChannelStatus)发生变化时,会通过回调该方法,实现状态变更的消息通知。
@Override
public void statusChanged(GRPCChannelStatus status) {
if (GRPCChannelStatus.CONNECTED.equals(status)) {
// 找到 GRPCChannelManager 服务,拿到网络连接
Channel channel = ServiceManager.INSTANCE.findService(GRPCChannelManager.class).getChannel();
// grpc 的 stub 可以理解为 protobuf 中定义的 XxxService
managementServiceBlockingStub = ManagementServiceGrpc.newBlockingStub(channel);
} else {
managementServiceBlockingStub = null;
}
this.status = status;
}
BootService
实现 BootService 生命周期不同阶段:
- prepare(准备阶段)
- 将自身添加到 Channel 监听器里
- 把配置文件中的 Agent Client 信息放入集合,等待发送
- boot(启动阶段)
- 创建心跳定时任务的
单线程周期性线程池 - Config.Collector.HEARTBEAT_PERIOD:心跳周期,默认
30s
- 创建心跳定时任务的
- onComplete(启动完成阶段)
- 不做任何操作
- shutdown(关闭阶段)
- 取消心跳
- 基于线程的中断机制:
- 尝试取消执行此任务。
- 如果任务已经完成、已经取消或由于其他原因无法取消,则此尝试将失败。
- 如果成功,并且在调用cancel时此任务尚未启动,则此任务不应运行。
- 如果任务已经启动,那么mayinterruptirunning参数决定是否应该中断执行该任务的线程以尝试停止该任务。
- 在此方法返回后,对isDone的后续调用将始终返回true。如果此方法返回true,后续对isCancelled的调用将始终返回true
@Override
public void prepare() {
// 将自身添加到 Channel 监听器里
ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(this);
// 把配置文件中的 Agent Client 信息放入集合,等待发送
SERVICE_INSTANCE_PROPERTIES = InstanceJsonPropertiesUtil.parseProperties();
}
@Override
public void boot() {
// 心跳
heartbeatFuture = Executors.newSingleThreadScheduledExecutor(
new DefaultNamedThreadFactory("ServiceManagementClient")
).scheduleAtFixedRate(
new RunnableWithExceptionProtection(
this,
t -> LOGGER.error("unexpected exception.", t)
), 0, Config.Collector.HEARTBEAT_PERIOD,
TimeUnit.SECONDS
);
}
@Override
public void onComplete() {
}
@Override
public void shutdown() {
// 取消心跳
heartbeatFuture.cancel(true);
}
采集信息上传
TraceSegmentServiceClient
- 将 TraceSegment 发送到 OAP
- 采用 gRpc 将采集到的信息上传到 gRpc 服务端
- 每次发送必须强制等待所有数据都发送完毕
共实现了四个接口
TracingContextListener:Trace 链路上下文监听BootService:agent服务接口GRPCChannelListener:gRpc 的Channel监听接口IConsumer<T>:consumer 接口
一个注解:@DefaultImplementor 默认实现
TracingContextListener
实现 TracingContextListener 的 afterFinished 方法,
@Override
public void afterFinished(TraceSegment traceSegment) {
if (traceSegment.isIgnore()) {
return;
}
if (!carrier.produce(traceSegment)) {
if (LOGGER.isDebugEnable()) {
LOGGER.debug("One trace segment has been abandoned, cause by buffer is full.");
}
}
}
GRPCChannelListener
实现 GRPCChannelListener 的 statusChanged 状态改变方法
@Override
public void statusChanged(GRPCChannelStatus status) {
if (CONNECTED.equals(status)) {
Channel channel = ServiceManager.INSTANCE.findService(GRPCChannelManager.class).getChannel();
serviceStub = TraceSegmentReportServiceGrpc.newStub(channel);
}
this.status = status;
}
BootService
实现 BootService 生命周期不同阶段:
其中:
lastLogTime:上一次打印传输 TraceSegment 情况的日志的时间segmentUplinkedCounter:成功发送的 TraceSegment 数量segmentAbandonedCounter:因网络原因丢弃的 TraceSegment 数量carrier:数据缓存
@Override
public void prepare() {
ServiceManager.INSTANCE.findService(GRPCChannelManager.class).addChannelListener(this);
}
@Override
public void boot() {
// 上一次打印传输 TraceSegment 情况的日志的时间
lastLogTime = System.currentTimeMillis();
// 成功发送的 TraceSegment 数量
segmentUplinkedCounter = 0;
// 因网络原因丢弃的 TraceSegment 数量
segmentAbandonedCounter = 0;
// 创建数据缓存
carrier = new DataCarrier<>(CHANNEL_SIZE, BUFFER_SIZE, BufferStrategy.IF_POSSIBLE);
carrier.consume(this, 1);
}
@Override
public void onComplete() {
TracingContext.ListenerManager.add(this);
}
@Override
public void shutdown() {
TracingContext.ListenerManager.remove(this);
carrier.shutdownConsumers();
}
IConsumer
实现 IConsumer 接口
@Override
public void consume(List<TraceSegment> data) {
if (CONNECTED.equals(status)) {
final GRPCStreamServiceStatus status = new GRPCStreamServiceStatus(false);
StreamObserver<SegmentObject> upstreamSegmentStreamObserver = serviceStub.withDeadlineAfter(
Config.Collector.GRPC_UPSTREAM_TIMEOUT, TimeUnit.SECONDS
).collect(new StreamObserver<Commands>() {
@Override
public void onNext(Commands commands) {
ServiceManager.INSTANCE.findService(CommandService.class)
.receiveCommand(commands);
}
@Override
public void onError(
Throwable throwable) {
status.finished();
if (LOGGER.isErrorEnable()) {
LOGGER.error(
throwable,
"Send UpstreamSegment to collector fail with a grpc internal exception."
);
}
ServiceManager.INSTANCE
.findService(GRPCChannelManager.class)
.reportError(throwable);
}
@Override
public void onCompleted() {
status.finished();
}
});
try {
for (TraceSegment segment : data) {
// 将 segment 对象转成 Protobuf对象
SegmentObject upstreamSegment = segment.transform();
upstreamSegmentStreamObserver.onNext(upstreamSegment);
}
} catch (Throwable t) {
LOGGER.error(t, "Transform and send UpstreamSegment to collector fail.");
}
upstreamSegmentStreamObserver.onCompleted();
// 强制等待所有的 TraceSegment 都发送完成
status.wait4Finish();
// 上报的数量
segmentUplinkedCounter += data.size();
} else {
// 抛弃的数量
segmentAbandonedCounter += data.size();
}
printUplinkStatus();
}
KafkaTraceSegmentServiceClient
- 采用 Kafka 客户端将采集到的信息上传到 Kafka
- 每条数据包装为一条 Kafka 消息同步发送
共实现了四个接口
TracingContextListener:Trace 链路上下文监听BootService:agent服务接口KafkaConnectionStatusListener:kafka 的连接状态监听接口IConsumer<T>:consumer 接口
一个注解:@OverrideImplementor(TraceSegmentServiceClient.class) 覆盖实现
TracingContextListener
实现 TracingContextListener 的 afterFinished 方法
@Override
public void afterFinished(final TraceSegment traceSegment) {
if (LOGGER.isDebugEnable()) {
LOGGER.debug("Trace segment reporting, traceId: {}", traceSegment.getTraceSegmentId());
}
if (traceSegment.isIgnore()) {
LOGGER.debug("Trace[TraceId={}] is ignored.", traceSegment.getTraceSegmentId());
return;
}
carrier.produce(traceSegment);
}
KafkaConnectionStatusListener
实现 KafkaConnectionStatusListener 的 statusChanged 状态改变方法
@Override
public void onStatusChanged(KafkaConnectionStatus status) {
if (status == KafkaConnectionStatus.CONNECTED) {
producer = ServiceManager.INSTANCE.findService(KafkaProducerManager.class).getProducer();
}
}
BootService
实现 BootService 生命周期不同阶段:
@Override
public void prepare() {
KafkaProducerManager producerManager = ServiceManager.INSTANCE.findService(KafkaProducerManager.class);
producerManager.addListener(this);
topic = producerManager.formatTopicNameThenRegister(KafkaReporterPluginConfig.Plugin.Kafka.TOPIC_SEGMENT);
}
@Override
public void boot() {
carrier = new DataCarrier<>(CHANNEL_SIZE, BUFFER_SIZE, BufferStrategy.IF_POSSIBLE);
carrier.consume(this, 1);
}
@Override
public void onComplete() {
TracingContext.ListenerManager.add(this);
}
@Override
public void shutdown() {
TracingContext.ListenerManager.remove(this);
carrier.shutdownConsumers();
}
IConsumer
实现 IConsumer 接口
@Override
public void consume(final List<TraceSegment> data) {
if (producer == null) {
return;
}
data.forEach(traceSegment -> {
// 将 segment 对象转成 Protobuf对象
SegmentObject upstreamSegment = traceSegment.transform();
ProducerRecord<String, Bytes> record = new ProducerRecord<>(
topic,
upstreamSegment.getTraceSegmentId(),
Bytes.wrap(upstreamSegment.toByteArray())
);
producer.send(record, (m, e) -> {
if (Objects.nonNull(e)) {
LOGGER.error("Failed to report TraceSegment.", e);
}
});
});
}