/* * Copyright 2017 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.web.service; import com.navercorp.pinpoint.rpc.Future; import com.navercorp.pinpoint.rpc.PinpointSocket; import com.navercorp.pinpoint.rpc.ResponseMessage; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannel; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannelContext; import com.navercorp.pinpoint.rpc.stream.ClientStreamChannelMessageListener; import com.navercorp.pinpoint.rpc.stream.StreamChannelStateChangeEventHandler; import com.navercorp.pinpoint.rpc.util.ListUtils; import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCount; import com.navercorp.pinpoint.thrift.dto.command.TCmdActiveThreadCountRes; import com.navercorp.pinpoint.thrift.dto.command.TCommandTransfer; import com.navercorp.pinpoint.thrift.dto.command.TRouteResult; import com.navercorp.pinpoint.thrift.io.DeserializerFactory; import com.navercorp.pinpoint.thrift.io.HeaderTBaseDeserializer; import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer; import com.navercorp.pinpoint.thrift.io.SerializerFactory; import com.navercorp.pinpoint.thrift.util.SerializationUtils; import com.navercorp.pinpoint.web.cluster.ClusterManager; import com.navercorp.pinpoint.web.cluster.DefaultPinpointRouteResponse; import com.navercorp.pinpoint.web.cluster.FailedPinpointRouteResponse; import com.navercorp.pinpoint.web.cluster.PinpointRouteResponse; import com.navercorp.pinpoint.web.vo.AgentActiveThreadCount; import com.navercorp.pinpoint.web.vo.AgentActiveThreadCountFactory; import com.navercorp.pinpoint.web.vo.AgentActiveThreadCountList; import com.navercorp.pinpoint.web.vo.AgentInfo; import org.apache.thrift.TBase; import org.apache.thrift.TException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.TimeUnit; /** * @author HyunGil Jeong * @Author Taejin Koo */ @Service public class AgentServiceImpl implements AgentService { private static final long DEFAULT_FUTURE_TIMEOUT = 3000; private long timeDiffMs; @Autowired private AgentInfoService agentInfoService; @Autowired private ClusterManager clusterManager; @Autowired private SerializerFactory<HeaderTBaseSerializer> commandSerializerFactory; @Autowired private DeserializerFactory<HeaderTBaseDeserializer> commandDeserializerFactory; @Value("#{pinpointWebProps['web.activethread.activeAgent.duration.days'] ?: 7}") private void setTimeDiffMs(int durationDays) { this.timeDiffMs = TimeUnit.MILLISECONDS.convert(durationDays, TimeUnit.DAYS); } @Override public AgentInfo getAgentInfo(String applicationName, String agentId) { long currentTime = System.currentTimeMillis(); Set<AgentInfo> agentInfos = agentInfoService.getAgentsByApplicationName(applicationName, currentTime); for (AgentInfo agentInfo : agentInfos) { if (agentInfo == null) { continue; } if (!agentInfo.getApplicationName().equals(applicationName)) { continue; } if (!agentInfo.getAgentId().equals(agentId)) { continue; } return agentInfo; } return null; } @Override public AgentInfo getAgentInfo(String applicationName, String agentId, long startTimeStamp) { return getAgentInfo(applicationName, agentId, startTimeStamp, false); } @Override public AgentInfo getAgentInfo(String applicationName, String agentId, long startTimeStamp, boolean checkDB) { if (checkDB) { long currentTime = System.currentTimeMillis(); Set<AgentInfo> agentInfos = agentInfoService.getAgentsByApplicationName(applicationName, currentTime); for (AgentInfo agentInfo : agentInfos) { if (agentInfo == null) { continue; } if (!agentInfo.getApplicationName().equals(applicationName)) { continue; } if (!agentInfo.getAgentId().equals(agentId)) { continue; } if (agentInfo.getStartTimestamp() != startTimeStamp) { continue; } return agentInfo; } return null; } else { AgentInfo agentInfo = new AgentInfo(); agentInfo.setApplicationName(applicationName); agentInfo.setAgentId(agentId); agentInfo.setStartTimestamp(startTimeStamp); return agentInfo; } } @Override public List<AgentInfo> getRecentAgentInfoList(String applicationName) { return this.getRecentAgentInfoList(applicationName, this.timeDiffMs); } @Override public List<AgentInfo> getRecentAgentInfoList(String applicationName, long timeDiff) { List<AgentInfo> agentInfoList = new ArrayList<>(); long currentTime = System.currentTimeMillis(); Set<AgentInfo> agentInfos = agentInfoService.getRecentAgentsByApplicationName(applicationName, currentTime, timeDiff); for (AgentInfo agentInfo : agentInfos) { ListUtils.addIfValueNotNull(agentInfoList, agentInfo); } return agentInfoList; } @Override public boolean isConnected(AgentInfo agentInfo) { return clusterManager.isConnected(agentInfo); } @Override public PinpointRouteResponse invoke(AgentInfo agentInfo, TBase<?, ?> tBase) throws TException { byte[] payload = serializeRequest(tBase); return invoke(agentInfo, payload); } @Override public PinpointRouteResponse invoke(AgentInfo agentInfo, TBase<?, ?> tBase, long timeout) throws TException { byte[] payload = serializeRequest(tBase); return invoke(agentInfo, payload, timeout); } @Override public PinpointRouteResponse invoke(AgentInfo agentInfo, byte[] payload) throws TException { return invoke(agentInfo, payload, DEFAULT_FUTURE_TIMEOUT); } @Override public PinpointRouteResponse invoke(AgentInfo agentInfo, byte[] payload, long timeout) throws TException { TCommandTransfer transferObject = createCommandTransferObject(agentInfo, payload); PinpointSocket socket = clusterManager.getSocket(agentInfo); Future<ResponseMessage> future = null; if (socket != null) { future = socket.request(serializeRequest(transferObject)); } PinpointRouteResponse response = getResponse(future, timeout); return response; } @Override public Map<AgentInfo, PinpointRouteResponse> invoke(List<AgentInfo> agentInfoList, TBase<?, ?> tBase) throws TException { byte[] payload = serializeRequest(tBase); return invoke(agentInfoList, payload); } @Override public Map<AgentInfo, PinpointRouteResponse> invoke(List<AgentInfo> agentInfoList, TBase<?, ?> tBase, long timeout) throws TException { byte[] payload = serializeRequest(tBase); return invoke(agentInfoList, payload, timeout); } @Override public Map<AgentInfo, PinpointRouteResponse> invoke(List<AgentInfo> agentInfoList, byte[] payload) throws TException { return invoke(agentInfoList, payload, DEFAULT_FUTURE_TIMEOUT); } @Override public Map<AgentInfo, PinpointRouteResponse> invoke(List<AgentInfo> agentInfoList, byte[] payload, long timeout) throws TException { Map<AgentInfo, Future<ResponseMessage>> futureMap = new HashMap<>(); for (AgentInfo agentInfo : agentInfoList) { TCommandTransfer transferObject = createCommandTransferObject(agentInfo, payload); PinpointSocket socket = clusterManager.getSocket(agentInfo); if (socket != null) { Future<ResponseMessage> future = socket.request(serializeRequest(transferObject)); futureMap.put(agentInfo, future); } else { futureMap.put(agentInfo, null); } } long startTime = System.currentTimeMillis(); Map<AgentInfo, PinpointRouteResponse> result = new HashMap<>(); for (Map.Entry<AgentInfo, Future<ResponseMessage>> futureEntry : futureMap.entrySet()) { AgentInfo agentInfo = futureEntry.getKey(); Future<ResponseMessage> future = futureEntry.getValue(); PinpointRouteResponse response = getResponse(future, getTimeoutMillis(startTime, timeout)); result.put(agentInfo, response); } return result; } @Override public ClientStreamChannelContext openStream(AgentInfo agentInfo, TBase<?, ?> tBase, ClientStreamChannelMessageListener messageListener) throws TException { byte[] payload = serializeRequest(tBase); return openStream(agentInfo, payload, messageListener, null); } @Override public ClientStreamChannelContext openStream(AgentInfo agentInfo, byte[] payload, ClientStreamChannelMessageListener messageListener) throws TException { return openStream(agentInfo, payload, messageListener, null); } @Override public ClientStreamChannelContext openStream(AgentInfo agentInfo, TBase<?, ?> tBase, ClientStreamChannelMessageListener messageListener, StreamChannelStateChangeEventHandler<ClientStreamChannel> stateChangeListener) throws TException { byte[] payload = serializeRequest(tBase); return openStream(agentInfo, payload, messageListener, stateChangeListener); } @Override public ClientStreamChannelContext openStream(AgentInfo agentInfo, byte[] payload, ClientStreamChannelMessageListener messageListener, StreamChannelStateChangeEventHandler<ClientStreamChannel> stateChangeListener) throws TException { TCommandTransfer transferObject = createCommandTransferObject(agentInfo, payload); PinpointSocket socket = clusterManager.getSocket(agentInfo); if (socket != null) { return socket.openStream(serializeRequest(transferObject), messageListener, stateChangeListener); } return null; } @Override public AgentActiveThreadCountList getActiveThreadCount(List<AgentInfo> agentInfoList) throws TException { byte[] activeThread = serializeRequest(new TCmdActiveThreadCount()); return getActiveThreadCount(agentInfoList, activeThread); } @Override public AgentActiveThreadCountList getActiveThreadCount(List<AgentInfo> agentInfoList, byte[] payload) throws TException { AgentActiveThreadCountList activeThreadCountList = new AgentActiveThreadCountList(agentInfoList.size()); Map<AgentInfo, PinpointRouteResponse> responseList = invoke(agentInfoList, payload); for (Map.Entry<AgentInfo, PinpointRouteResponse> entry : responseList.entrySet()) { AgentInfo agentInfo = entry.getKey(); PinpointRouteResponse response = entry.getValue(); AgentActiveThreadCount activeThreadCount = createActiveThreadCount(agentInfo.getAgentId(), response); activeThreadCountList.add(activeThreadCount); } return activeThreadCountList; } private AgentActiveThreadCount createActiveThreadCount(String agentId, PinpointRouteResponse response) { TRouteResult routeResult = response.getRouteResult(); if (routeResult == TRouteResult.OK) { AgentActiveThreadCountFactory factory = new AgentActiveThreadCountFactory(); factory.setAgentId(agentId); return factory.create(response.getResponse(TCmdActiveThreadCountRes.class, null)); } else { AgentActiveThreadCountFactory factory = new AgentActiveThreadCountFactory(); factory.setAgentId(agentId); return factory.createFail(routeResult.name()); } } private TCommandTransfer createCommandTransferObject(AgentInfo agentInfo, byte[] payload) { TCommandTransfer transferObject = new TCommandTransfer(); transferObject.setApplicationName(agentInfo.getApplicationName()); transferObject.setAgentId(agentInfo.getAgentId()); transferObject.setStartTime(agentInfo.getStartTimestamp()); transferObject.setPayload(payload); return transferObject; } private PinpointRouteResponse getResponse(Future<ResponseMessage> future, long timeout) { if (future == null) { return new FailedPinpointRouteResponse(TRouteResult.NOT_FOUND, null); } boolean completed = future.await(timeout); if (completed) { DefaultPinpointRouteResponse response = new DefaultPinpointRouteResponse(future.getResult().getMessage()); response.parse(commandDeserializerFactory); return response; } else { return new FailedPinpointRouteResponse(TRouteResult.TIMEOUT, null); } } private long getTimeoutMillis(long startTime, long timeout) { return Math.max(startTime + timeout - System.currentTimeMillis(), 100L); } @Override public byte[] serializeRequest(TBase<?, ?> tBase) throws TException { return SerializationUtils.serialize(tBase, commandSerializerFactory); } @Override public byte[] serializeRequest(TBase<?, ?> tBase, byte[] defaultValue) { return SerializationUtils.serialize(tBase, commandSerializerFactory, defaultValue); } @Override public TBase<?, ?> deserializeResponse(byte[] objectData) throws TException { return SerializationUtils.deserialize(objectData, commandDeserializerFactory); } @Override public TBase<?, ?> deserializeResponse(byte[] objectData, TBase<?, ?> defaultValue) { return SerializationUtils.deserialize(objectData, commandDeserializerFactory, defaultValue); } }