package com.netflix.loadbalancer;
import com.netflix.appinfo.InstanceInfo;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.discovery.CacheRefreshedEvent;
import com.netflix.discovery.DiscoveryClient;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaEventListener;
import com.netflix.discovery.util.InstanceInfoGenerator;
import com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList;
import com.netflix.niws.loadbalancer.DiscoveryEnabledServer;
import com.netflix.niws.loadbalancer.EurekaNotificationServerListUpdater;
import org.easymock.Capture;
import org.easymock.EasyMock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.powermock.api.easymock.PowerMock;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import javax.inject.Provider;
import java.util.List;
import java.util.concurrent.TimeUnit;
/**
* A test for {@link com.netflix.loadbalancer.DynamicServerListLoadBalancer} using the
* {@link com.netflix.niws.loadbalancer.EurekaNotificationServerListUpdater} instead of the default.
*
* @author David Liu
*/
@RunWith(PowerMockRunner.class)
@PrepareForTest(DiscoveryClient.class)
@PowerMockIgnore("javax.management.*")
public class EurekaDynamicServerListLoadBalancerTest {
private final List<InstanceInfo> servers = InstanceInfoGenerator.newBuilder(10, 1).build().toInstanceList();
private final int initialServerListSize = 4;
private final int secondServerListSize = servers.size() - initialServerListSize;
private final String vipAddress = servers.get(0).getVIPAddress();
private DefaultClientConfigImpl config;
private EurekaClient eurekaClientMock;
private Provider<EurekaClient> eurekaClientProvider;
@Before
public void setUp() {
PowerMock.mockStatic(DiscoveryClient.class);
EasyMock
.expect(DiscoveryClient.getZone(EasyMock.isA(InstanceInfo.class)))
.andReturn("zone")
.anyTimes();
eurekaClientMock = setUpEurekaClientMock(servers);
eurekaClientProvider = new Provider<EurekaClient>() {
@Override
public EurekaClient get() {
return eurekaClientMock;
}
};
config = DefaultClientConfigImpl.getClientConfigWithDefaultValues();
config.setProperty(CommonClientConfigKey.DeploymentContextBasedVipAddresses, vipAddress);
config.setProperty(CommonClientConfigKey.ServerListUpdaterClassName, EurekaNotificationServerListUpdater.class.getName());
}
@Test
public void testLoadBalancerHappyCase() throws Exception {
Assert.assertNotEquals("the two test server list counts should be different",
secondServerListSize, initialServerListSize);
DynamicServerListLoadBalancer<DiscoveryEnabledServer> lb = null;
try {
Capture<EurekaEventListener> eventListenerCapture = new Capture<EurekaEventListener>();
eurekaClientMock.registerEventListener(EasyMock.capture(eventListenerCapture));
PowerMock.replay(DiscoveryClient.class);
PowerMock.replay(eurekaClientMock);
// actual testing
// initial creation and loading of the first serverlist
lb = new DynamicServerListLoadBalancer<DiscoveryEnabledServer>(
config,
new AvailabilityFilteringRule(),
new DummyPing(),
new DiscoveryEnabledNIWSServerList(vipAddress, eurekaClientProvider),
new ZoneAffinityServerListFilter<DiscoveryEnabledServer>(),
new EurekaNotificationServerListUpdater(eurekaClientProvider)
);
Assert.assertEquals(initialServerListSize, lb.getServerCount(false));
// trigger an eureka CacheRefreshEvent
eventListenerCapture.getValue().onEvent(new CacheRefreshedEvent());
Assert.assertTrue(verifyFinalServerListCount(secondServerListSize, lb));
} finally {
if (lb != null) {
lb.shutdown();
PowerMock.verify(eurekaClientMock);
PowerMock.verify(DiscoveryClient.class);
}
}
}
@Test
public void testShutdownMultiple() {
try {
eurekaClientMock.registerEventListener(EasyMock.anyObject(EurekaEventListener.class));
EasyMock.expectLastCall().anyTimes();
PowerMock.replay(DiscoveryClient.class);
PowerMock.replay(eurekaClientMock);
DynamicServerListLoadBalancer<DiscoveryEnabledServer> lb1 = new DynamicServerListLoadBalancer<DiscoveryEnabledServer>(
config,
new AvailabilityFilteringRule(),
new DummyPing(),
new DiscoveryEnabledNIWSServerList(vipAddress, eurekaClientProvider),
new ZoneAffinityServerListFilter<DiscoveryEnabledServer>(),
new EurekaNotificationServerListUpdater(eurekaClientProvider)
);
DynamicServerListLoadBalancer<DiscoveryEnabledServer> lb2 = new DynamicServerListLoadBalancer<DiscoveryEnabledServer>(
config,
new AvailabilityFilteringRule(),
new DummyPing(),
new DiscoveryEnabledNIWSServerList(vipAddress, eurekaClientProvider),
new ZoneAffinityServerListFilter<DiscoveryEnabledServer>(),
new EurekaNotificationServerListUpdater(eurekaClientProvider)
);
DynamicServerListLoadBalancer<DiscoveryEnabledServer> lb3 = new DynamicServerListLoadBalancer<DiscoveryEnabledServer>(
config,
new AvailabilityFilteringRule(),
new DummyPing(),
new DiscoveryEnabledNIWSServerList(vipAddress, eurekaClientProvider),
new ZoneAffinityServerListFilter<DiscoveryEnabledServer>(),
new EurekaNotificationServerListUpdater(eurekaClientProvider)
);
lb3.shutdown();
lb1.shutdown();
lb2.shutdown();
} finally {
PowerMock.verify(eurekaClientMock);
PowerMock.verify(DiscoveryClient.class);
}
}
// a hacky thread sleep loop to get around some minor async behaviour
// max wait time is 2 seconds, but should complete well before that.
private boolean verifyFinalServerListCount(int finalCount, DynamicServerListLoadBalancer lb) throws Exception {
long stepSize = TimeUnit.MILLISECONDS.convert(50l, TimeUnit.MILLISECONDS);
long maxTime = TimeUnit.MILLISECONDS.convert(2l, TimeUnit.SECONDS);
for (int i = 0; i < maxTime; i += stepSize) {
if (finalCount == lb.getServerCount(false)) {
return true;
} else {
Thread.sleep(stepSize);
}
}
return false;
}
private EurekaClient setUpEurekaClientMock(List<InstanceInfo> servers) {
final EurekaClient eurekaClientMock = PowerMock.createMock(EurekaClient.class);
EasyMock
.expect(eurekaClientMock.getInstancesByVipAddress(EasyMock.anyString(), EasyMock.anyBoolean(), EasyMock.anyString()))
.andReturn(servers.subList(0, initialServerListSize)).times(1)
.andReturn(servers.subList(initialServerListSize, servers.size())).anyTimes();
EasyMock
.expectLastCall();
EasyMock
.expect(eurekaClientMock.unregisterEventListener(EasyMock.isA(EurekaEventListener.class)))
.andReturn(true).anyTimes();
return eurekaClientMock;
}
}