/* * Copyright 2014 NAVER Corp. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.navercorp.pinpoint.profiler; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.ThreadFactory; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import com.navercorp.pinpoint.profiler.context.provider.JvmInformationProvider; import com.navercorp.pinpoint.thrift.dto.TJvmGcType; import com.navercorp.pinpoint.thrift.dto.TJvmInfo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.navercorp.pinpoint.bootstrap.context.ServerMetaData; import com.navercorp.pinpoint.bootstrap.context.ServerMetaDataHolder.ServerMetaDataListener; import com.navercorp.pinpoint.bootstrap.context.ServiceInfo; import com.navercorp.pinpoint.common.Version; import com.navercorp.pinpoint.common.util.PinpointThreadFactory; import com.navercorp.pinpoint.profiler.sender.EnhancedDataSender; import com.navercorp.pinpoint.thrift.dto.TAgentInfo; import com.navercorp.pinpoint.thrift.dto.TServerMetaData; import com.navercorp.pinpoint.thrift.dto.TServiceInfo; /** * @author emeroad * @author koo.taejin * @author HyunGil Jeong */ public class AgentInfoSender implements ServerMetaDataListener { // refresh daily public static final long DEFAULT_AGENT_INFO_REFRESH_INTERVAL_MS = 24 * 60 * 60 * 1000L; // retry every 3 seconds public static final long DEFAULT_AGENT_INFO_SEND_INTERVAL_MS = 3 * 1000L; // retry 3 times per attempt public static final int DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT = 3; private static final Logger LOGGER = LoggerFactory.getLogger(AgentInfoSender.class); private static final ThreadFactory THREAD_FACTORY = new PinpointThreadFactory("Pinpoint-agentInfo-sender", true); private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor(THREAD_FACTORY); private final EnhancedDataSender dataSender; private final long refreshIntervalMs; private final long sendIntervalMs; private final int maxTryPerAttempt; private final AgentInformation agentInformation; private final JvmInformation jvmInformation; private volatile ServerMetaData serverMetaData; private AgentInfoSender(Builder builder) { this.dataSender = builder.dataSender; this.agentInformation = builder.agentInformation; this.jvmInformation = builder.jvmInformation; this.refreshIntervalMs = builder.refreshIntervalMs; this.sendIntervalMs = builder.sendIntervalMs; this.maxTryPerAttempt = builder.maxTryPerAttempt; } @Override public void publishServerMetaData(ServerMetaData serverMetaData) { this.serverMetaData = serverMetaData; submit(this.maxTryPerAttempt); } public void start() { submit(Integer.MAX_VALUE); this.executor.scheduleWithFixedDelay(new Runnable() { @Override public void run() { submit(maxTryPerAttempt); } }, this.refreshIntervalMs, this.refreshIntervalMs, TimeUnit.MILLISECONDS); } public void stop() { this.executor.shutdown(); try { this.executor.awaitTermination(3000, TimeUnit.MILLISECONDS); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } LOGGER.info("AgentInfoSender stopped"); } private void submit(final int maxTryPerAttempt) { new AgentInfoSendRunnableWrapper(new AgentInfoSendRunnable(), maxTryPerAttempt).repeatWithFixedDelay(this.executor, 0, this.sendIntervalMs, TimeUnit.MILLISECONDS); } private TAgentInfo createTAgentInfo() { final TAgentInfo agentInfo = new TAgentInfo(); agentInfo.setIp(this.agentInformation.getHostIp()); agentInfo.setHostname(this.agentInformation.getMachineName()); agentInfo.setPorts(""); agentInfo.setAgentId(this.agentInformation.getAgentId()); agentInfo.setApplicationName(this.agentInformation.getApplicationName()); agentInfo.setPid(this.agentInformation.getPid()); agentInfo.setStartTimestamp(this.agentInformation.getStartTime()); agentInfo.setServiceType(this.agentInformation.getServerType().getCode()); agentInfo.setVmVersion(this.agentInformation.getJvmVersion()); agentInfo.setAgentVersion(Version.VERSION); if (this.serverMetaData != null) { agentInfo.setServerMetaData(createTServiceInfo()); } agentInfo.setJvmInfo(createTJvmInfo()); return agentInfo; } private TServerMetaData createTServiceInfo() { TServerMetaData tServerMetaData = new TServerMetaData(); tServerMetaData.setServerInfo(serverMetaData.getServerInfo()); tServerMetaData.setVmArgs(serverMetaData.getVmArgs()); List<TServiceInfo> tServiceInfos = new ArrayList<TServiceInfo>(); for (ServiceInfo serviceInfo : serverMetaData.getServiceInfos()) { TServiceInfo tServiceInfo = new TServiceInfo(); tServiceInfo.setServiceName(serviceInfo.getServiceName()); tServiceInfo.setServiceLibs(serviceInfo.getServiceLibs()); tServiceInfos.add(tServiceInfo); } tServerMetaData.setServiceInfos(tServiceInfos); return tServerMetaData; } private TJvmInfo createTJvmInfo() { TJvmInfo tJvmInfo = new TJvmInfo(); tJvmInfo.setVmVersion(this.jvmInformation.getJvmVersion()); TJvmGcType gcType = TJvmGcType.findByValue(this.jvmInformation.getGcTypeCode()); if (gcType == null) { gcType = TJvmGcType.UNKNOWN; } tJvmInfo.setGcType(gcType); return tJvmInfo; } private static class AgentInfoSendRunnableWrapper implements Runnable { private final AgentInfoSendRunnable delegate; private final int maxTryCount; private final AtomicInteger tryCount = new AtomicInteger(); private volatile ScheduledFuture<?> self; private AgentInfoSendRunnableWrapper(AgentInfoSendRunnable agentInfoSendRunnable, int maxTryCount) { if (agentInfoSendRunnable == null) { throw new NullPointerException("agentInfoSendRunnable must not be null"); } if (maxTryCount < 0) { throw new IllegalArgumentException("maxTryCount must not be less than 0"); } this.delegate = agentInfoSendRunnable; this.maxTryCount = maxTryCount; } @Override public void run() { // Cancel self when delegated runnable is completed successfully, or when max try count has been reached if (this.delegate.isSuccessful() || this.tryCount.getAndIncrement() == this.maxTryCount) { this.self.cancel(false); } else { this.delegate.run(); } } private void repeatWithFixedDelay(ScheduledExecutorService scheduledExecutorService, long initialDelay, long delay, TimeUnit unit) { this.self = scheduledExecutorService.scheduleWithFixedDelay(this, initialDelay, delay, unit); } } private class AgentInfoSendRunnable implements Runnable { private final AtomicBoolean isSuccessful = new AtomicBoolean(false); private final AgentInfoSenderListener agentInfoSenderListener = new AgentInfoSenderListener(this.isSuccessful); private final TAgentInfo agentInfo; private AgentInfoSendRunnable() { this.agentInfo = createTAgentInfo(); } @Override public void run() { if (!isSuccessful.get()) { LOGGER.info("Sending AgentInfo {}", agentInfo); dataSender.request(this.agentInfo, this.agentInfoSenderListener); } } public boolean isSuccessful() { return this.isSuccessful.get(); } } public static class Builder { private final EnhancedDataSender dataSender; private final AgentInformation agentInformation; private final JvmInformation jvmInformation; private long refreshIntervalMs = DEFAULT_AGENT_INFO_REFRESH_INTERVAL_MS; private long sendIntervalMs = DEFAULT_AGENT_INFO_SEND_INTERVAL_MS; private int maxTryPerAttempt = DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT; Builder(EnhancedDataSender dataSender, AgentInformation agentInformation) { this(dataSender, agentInformation, new JvmInformationProvider().get()); } public Builder(EnhancedDataSender dataSender, AgentInformation agentInformation, JvmInformation jvmInformation) { if (dataSender == null) { throw new NullPointerException("enhancedDataSender must not be null"); } if (agentInformation == null) { throw new NullPointerException("agentInformation must not be null"); } if (jvmInformation == null) { throw new NullPointerException("jvmInformation must not be null"); } this.dataSender = dataSender; this.agentInformation = agentInformation; this.jvmInformation = jvmInformation; } public Builder refreshInterval(long refreshIntervalMs) { this.refreshIntervalMs = refreshIntervalMs; return this; } public Builder sendInterval(long sendIntervalMs) { this.sendIntervalMs = sendIntervalMs; return this; } public Builder maxTryPerAttempt(int maxTryCountPerAttempt) { this.maxTryPerAttempt = maxTryCountPerAttempt; return this; } public AgentInfoSender build() { if (this.refreshIntervalMs <= 0) { throw new IllegalStateException("agentInfoRefreshIntervalMs must be greater than 0"); } if (this.sendIntervalMs <= 0) { throw new IllegalStateException("agentInfoSendIntervalMs must be greater than 0"); } if (this.maxTryPerAttempt <= 0) { throw new IllegalStateException("maxTryPerAttempt must be greater than 0"); } return new AgentInfoSender(this); } } }