/*
* 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 com.navercorp.pinpoint.bootstrap.context.ServerMetaData;
import com.navercorp.pinpoint.bootstrap.context.ServerMetaDataHolder;
import com.navercorp.pinpoint.bootstrap.context.ServiceInfo;
import com.navercorp.pinpoint.common.Version;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.common.util.JvmUtils;
import com.navercorp.pinpoint.common.util.SystemPropertyKey;
import com.navercorp.pinpoint.profiler.context.DefaultServerMetaData;
import com.navercorp.pinpoint.profiler.context.DefaultServerMetaDataHolder;
import com.navercorp.pinpoint.profiler.sender.TcpDataSender;
import com.navercorp.pinpoint.rpc.PinpointSocket;
import com.navercorp.pinpoint.rpc.client.DefaultPinpointClientFactory;
import com.navercorp.pinpoint.rpc.client.PinpointClient;
import com.navercorp.pinpoint.rpc.client.PinpointClientFactory;
import com.navercorp.pinpoint.rpc.client.PinpointClientReconnectEventListener;
import com.navercorp.pinpoint.rpc.packet.HandshakeResponseCode;
import com.navercorp.pinpoint.rpc.packet.HandshakeResponseType;
import com.navercorp.pinpoint.rpc.packet.PingPacket;
import com.navercorp.pinpoint.rpc.packet.RequestPacket;
import com.navercorp.pinpoint.rpc.packet.SendPacket;
import com.navercorp.pinpoint.rpc.server.PinpointServer;
import com.navercorp.pinpoint.rpc.server.PinpointServerAcceptor;
import com.navercorp.pinpoint.rpc.server.ServerMessageListener;
import com.navercorp.pinpoint.rpc.util.ClientFactoryUtils;
import com.navercorp.pinpoint.thrift.dto.TResult;
import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializer;
import com.navercorp.pinpoint.thrift.io.HeaderTBaseSerializerFactory;
import org.apache.thrift.TException;
import org.junit.Assert;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SocketUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class AgentInfoSenderTest {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
public static final int PORT = SocketUtils.findAvailableTcpPort(50050);
public static final String HOST = "127.0.0.1";
private final int testAwaitTimeMs = 50;
private final TestAwaitUtils awaitUtils = new TestAwaitUtils(this.testAwaitTimeMs, 60000);
@Test
public void agentInfoShouldBeSent() throws InterruptedException {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 100L;
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory clientFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, clientFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo()).sendInterval(agentInfoSendRetryIntervalMs).build();
try {
agentInfoSender.start();
waitExpectedRequestCount(requestCount, 1);
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, clientFactory);
}
assertEquals(1, requestCount.get());
assertEquals(1, successCount.get());
}
@Test
public void agentInfoShouldRetryUntilSuccess() throws InterruptedException {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 100L;
final int expectedTriesUntilSuccess = AgentInfoSender.DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT;
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount, expectedTriesUntilSuccess);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory socketFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, socketFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo()).sendInterval(agentInfoSendRetryIntervalMs).build();
try {
agentInfoSender.start();
waitExpectedRequestCount(requestCount, expectedTriesUntilSuccess);
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, socketFactory);
}
assertEquals(expectedTriesUntilSuccess, requestCount.get());
assertEquals(1, successCount.get());
}
@Test
public void agentInfoShouldInitiallyRetryIndefinitely() throws InterruptedException {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 100L;
final int expectedTriesUntilSuccess = AgentInfoSender.DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT * 5;
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount, expectedTriesUntilSuccess);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory socketFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, socketFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo()).sendInterval(agentInfoSendRetryIntervalMs).build();
try {
agentInfoSender.start();
waitExpectedRequestCount(requestCount, expectedTriesUntilSuccess);
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, socketFactory);
}
assertEquals(expectedTriesUntilSuccess, requestCount.get());
assertEquals(1, successCount.get());
}
@Test
public void agentInfoShouldRetryUntilAttemptsAreExhaustedWhenRefreshing() throws InterruptedException {
final AtomicInteger successServerRequestCount = new AtomicInteger();
final AtomicInteger failServerRequestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 1000L;
final long agentInfoSendRefreshIntervalMs = 5000L;
final int expectedSuccessServerTries = 1;
final int expectedFailServerTries = AgentInfoSender.DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT;
final CountDownLatch agentReconnectLatch = new CountDownLatch(1);
ResponseServerMessageListener successServerListener = new ResponseServerMessageListener(successServerRequestCount, successCount);
ResponseServerMessageListener failServerListener = new ResponseServerMessageListener(failServerRequestCount, successCount, Integer.MAX_VALUE);
PinpointServerAcceptor successServerAcceptor = createServerAcceptor(successServerListener);
PinpointServerAcceptor failServerAcceptor = null;
PinpointClientFactory socketFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, socketFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
dataSender.addReconnectEventListener(new PinpointClientReconnectEventListener() {
@Override
public void reconnectPerformed(PinpointClient client) {
agentReconnectLatch.countDown();
}
});
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo())
.refreshInterval(agentInfoSendRefreshIntervalMs)
.sendInterval(agentInfoSendRetryIntervalMs)
.build();
try {
agentInfoSender.start();
waitExpectedRequestCount(successServerRequestCount, expectedSuccessServerTries);
successServerAcceptor.close();
Thread.sleep(agentInfoSendRetryIntervalMs * AgentInfoSender.DEFAULT_MAX_TRY_COUNT_PER_ATTEMPT);
failServerAcceptor = createServerAcceptor(failServerListener);
// wait till agent reconnects
agentReconnectLatch.await();
waitExpectedRequestCount(failServerRequestCount, expectedFailServerTries);
failServerAcceptor.close();
} finally {
closeAll(null, agentInfoSender, pinpointClient, socketFactory);
}
assertEquals(1, successCount.get());
assertEquals(expectedSuccessServerTries, successServerRequestCount.get());
assertEquals(expectedFailServerTries, failServerRequestCount.get());
}
@Test
public void agentInfoShouldBeSentOnlyOnceEvenAfterReconnect() throws Exception {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final AtomicInteger reconnectCount = new AtomicInteger();
final int expectedReconnectCount = 3;
final long agentInfoSendRetryIntervalMs = 100L;
final int maxTryCountPerAttempt = Integer.MAX_VALUE;
final CyclicBarrier reconnectEventBarrier = new CyclicBarrier(2);
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory clientFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, clientFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
dataSender.addReconnectEventListener(new PinpointClientReconnectEventListener() {
@Override
public void reconnectPerformed(PinpointClient client) {
reconnectCount.incrementAndGet();
try {
reconnectEventBarrier.await();
} catch (Exception e) {
// just fail
throw new RuntimeException(e);
}
}
});
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo())
.sendInterval(agentInfoSendRetryIntervalMs)
.maxTryPerAttempt(maxTryCountPerAttempt)
.build();
try {
// initial connect
agentInfoSender.start();
waitExpectedRequestCount(requestCount, 1);
serverAcceptor.close();
// reconnect
for (int i = 0; i < expectedReconnectCount; ++i) {
PinpointServerAcceptor reconnectServerAcceptor = createServerAcceptor(serverListener);
// wait for agent to reconnect
reconnectEventBarrier.await();
// wait to see if AgentInfo is sent again (it shouldn't)
Thread.sleep(1000L);
reconnectServerAcceptor.close();
reconnectEventBarrier.reset();
}
} finally {
closeAll(null, agentInfoSender, pinpointClient, clientFactory);
}
assertEquals(1, successCount.get());
assertEquals(expectedReconnectCount, reconnectCount.get());
}
@Test
public void agentInfoShouldKeepRefreshing() throws InterruptedException {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 100L;
final long agentInfoSendRefreshIntervalMs = 100L;
final int expectedRefreshCount = 5;
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory socketFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, socketFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo())
.refreshInterval(agentInfoSendRefreshIntervalMs)
.sendInterval(agentInfoSendRetryIntervalMs)
.build();
try {
agentInfoSender.start();
while (requestCount.get() < expectedRefreshCount) {
Thread.sleep(1000L);
}
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, socketFactory);
}
assertTrue(requestCount.get() >= expectedRefreshCount);
assertTrue(successCount.get() >= expectedRefreshCount);
}
@Test
public void serverMetaDataShouldBeSentOnPublish() throws InterruptedException {
// Given
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 1000L;
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(serverListener);
PinpointClientFactory clientFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, clientFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo()).sendInterval(agentInfoSendRetryIntervalMs).build();
final List<ServerMetaData> serverMetaDataObjects = new ArrayList<ServerMetaData>();
serverMetaDataObjects.add(new DefaultServerMetaData("server1", Collections.<String> emptyList(), Collections.<Integer, String> emptyMap(), Collections.<ServiceInfo> emptyList()));
serverMetaDataObjects.add(new DefaultServerMetaData("server2", Collections.<String> emptyList(), Collections.<Integer, String> emptyMap(), Collections.<ServiceInfo> emptyList()));
serverMetaDataObjects.add(new DefaultServerMetaData("server3", Collections.<String> emptyList(), Collections.<Integer, String> emptyMap(), Collections.<ServiceInfo> emptyList()));
serverMetaDataObjects.add(new DefaultServerMetaData("server4", Collections.<String> emptyList(), Collections.<Integer, String> emptyMap(), Collections.<ServiceInfo> emptyList()));
serverMetaDataObjects.add(new DefaultServerMetaData("server5", Collections.<String> emptyList(), Collections.<Integer, String> emptyMap(), Collections.<ServiceInfo> emptyList()));
// When
try {
for (ServerMetaData serverMetaData : serverMetaDataObjects) {
agentInfoSender.publishServerMetaData(serverMetaData);
}
waitExpectedRequestCount(requestCount, 5);
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, clientFactory);
}
// Then
assertEquals(5, requestCount.get());
assertEquals(5, successCount.get());
}
@Test
public void serverMetaDataCouldBePublishedFromMultipleThreads() throws InterruptedException {
// Given
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long agentInfoSendRetryIntervalMs = 1000L;
final int threadCount = 50;
final CountDownLatch initLatch = new CountDownLatch(threadCount);
final CountDownLatch startLatch = new CountDownLatch(1);
final CountDownLatch endLatch = new CountDownLatch(threadCount);
final ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
final Queue<Throwable> exceptions = new ConcurrentLinkedQueue<Throwable>();
ResponseServerMessageListener delayedServerListener = new ResponseServerMessageListener(requestCount, successCount);
PinpointServerAcceptor serverAcceptor = createServerAcceptor(delayedServerListener);
PinpointClientFactory clientFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, clientFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo()).sendInterval(agentInfoSendRetryIntervalMs).build();
final ServerMetaDataHolder metaDataContext = new DefaultServerMetaDataHolder(Collections.<String> emptyList());
metaDataContext.addListener(agentInfoSender);
// When
for (int i = 0; i < threadCount; ++i) {
final String serviceName = "/name" + i;
executorService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
initLatch.countDown();
try {
startLatch.await();
metaDataContext.addServiceInfo(serviceName, Collections.<String>emptyList());
metaDataContext.notifyListeners();
} catch (final Throwable t) {
exceptions.add(t);
} finally {
endLatch.countDown();
}
return null;
}
});
}
initLatch.await();
startLatch.countDown();
endLatch.await();
executorService.shutdown();
try {
waitExpectedRequestCount(requestCount, threadCount);
waitExpectedRequestCount(successCount, threadCount);
} finally {
closeAll(serverAcceptor, agentInfoSender, pinpointClient, clientFactory);
}
// Then
assertTrue("Failed with exceptions : " + exceptions, exceptions.isEmpty());
assertEquals(threadCount, requestCount.get());
assertEquals(threadCount, successCount.get());
}
public void reconnectionStressTest() throws InterruptedException {
final AtomicInteger requestCount = new AtomicInteger();
final AtomicInteger successCount = new AtomicInteger();
final long stressTestTime = 60 * 1000L;
final int randomMaxTime = 3000;
final long agentInfoSendRetryIntervalMs = 1000L;
final int maxTryPerAttempt = Integer.MAX_VALUE;
final int expectedTriesUntilSuccess = (int) stressTestTime / (randomMaxTime * 2);
ResponseServerMessageListener serverListener = new ResponseServerMessageListener(requestCount, successCount, expectedTriesUntilSuccess);
PinpointClientFactory clientFactory = createPinpointClientFactory();
PinpointClient pinpointClient = ClientFactoryUtils.createPinpointClient(HOST, PORT, clientFactory);
TcpDataSender dataSender = new TcpDataSender(pinpointClient);
AgentInfoSender agentInfoSender = new AgentInfoSender.Builder(dataSender, getAgentInfo())
.sendInterval(agentInfoSendRetryIntervalMs)
.maxTryPerAttempt(maxTryPerAttempt)
.build();
long startTime = System.currentTimeMillis();
try {
agentInfoSender.start();
Random random = new Random(System.currentTimeMillis());
while (System.currentTimeMillis() < startTime + stressTestTime) {
createAndDeleteServer(serverListener, Math.abs(random.nextInt(randomMaxTime)));
Thread.sleep(Math.abs(random.nextInt(1000)));
}
} finally {
closeAll(null, agentInfoSender, pinpointClient, clientFactory);
}
assertEquals(1, successCount.get());
assertEquals(expectedTriesUntilSuccess, requestCount.get());
}
private PinpointServerAcceptor createServerAcceptor(ServerMessageListener listener) {
PinpointServerAcceptor serverAcceptor = new PinpointServerAcceptor();
// server.setMessageListener(new
// NoResponseServerMessageListener(requestCount));
serverAcceptor.setMessageListener(listener);
serverAcceptor.bind(HOST, PORT);
return serverAcceptor;
}
private void createAndDeleteServer(ServerMessageListener listener, long waitTimeMillis) throws InterruptedException {
PinpointServerAcceptor server = null;
try {
server = createServerAcceptor(listener);
Thread.sleep(waitTimeMillis);
} finally {
if (server != null) {
server.close();
}
}
}
private void closeAll(PinpointServerAcceptor serverAcceptor, AgentInfoSender agentInfoSender, PinpointClient pinpointClient, PinpointClientFactory factory) {
if (serverAcceptor != null) {
serverAcceptor.close();
}
if (agentInfoSender != null) {
agentInfoSender.stop();
}
if (pinpointClient != null) {
pinpointClient.close();
}
if (factory != null) {
factory.release();
}
}
private AgentInformation getAgentInfo() {
AgentInformation agentInfo = new DefaultAgentInformation("agentId", "appName", System.currentTimeMillis(), 1111, "hostname", "127.0.0.1", ServiceType.USER,
JvmUtils.getSystemProperty(SystemPropertyKey.JAVA_VERSION), Version.VERSION);
return agentInfo;
}
class ResponseServerMessageListener implements ServerMessageListener {
private final AtomicInteger requestCount;
private final AtomicInteger successCount;
private final int successCondition;
public ResponseServerMessageListener(AtomicInteger requestCount, AtomicInteger successCount) {
this(requestCount, successCount, 1);
}
public ResponseServerMessageListener(AtomicInteger requestCount, AtomicInteger successCount, int successCondition) {
this.requestCount = requestCount;
this.successCount = successCount;
this.successCondition = successCondition;
}
@Override
public void handleSend(SendPacket sendPacket, PinpointSocket pinpointSocket) {
logger.debug("handleSend packet:{}, remote:{}", sendPacket, pinpointSocket.getRemoteAddress());
}
@Override
public void handleRequest(RequestPacket requestPacket, PinpointSocket pinpointSocket) {
logger.debug("handleRequest packet:{}, remote:{}", requestPacket, pinpointSocket.getRemoteAddress());
int requestCount = this.requestCount.incrementAndGet();
if (requestCount < successCondition) {
return;
}
try {
HeaderTBaseSerializer serializer = HeaderTBaseSerializerFactory.DEFAULT_FACTORY.createSerializer();
TResult result = new TResult(true);
byte[] resultBytes = serializer.serialize(result);
this.successCount.incrementAndGet();
pinpointSocket.response(requestPacket, resultBytes);
} catch (TException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public HandshakeResponseCode handleHandshake(@SuppressWarnings("rawtypes") Map arg0) {
return HandshakeResponseType.Success.DUPLEX_COMMUNICATION;
}
@Override
public void handlePing(PingPacket pingPacket, PinpointServer pinpointServer) {
logger.debug("ping received {} {} ", pingPacket, pinpointServer);
}
}
private PinpointClientFactory createPinpointClientFactory() {
PinpointClientFactory clientFactory = new DefaultPinpointClientFactory();
clientFactory.setTimeoutMillis(1000 * 5);
clientFactory.setProperties(Collections.<String, Object> emptyMap());
return clientFactory;
}
private void waitExpectedRequestCount(final AtomicInteger requestCount, final int expectedRequestCount) {
boolean pass = awaitUtils.await(new TestAwaitTaskUtils() {
@Override
public boolean checkCompleted() {
return requestCount.get() == expectedRequestCount;
}
});
Assert.assertTrue(pass);
}
}