blob: 151e318cbdecf3b0618f906bf41948449649ce6b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.servicecomb.loadbalance;
import java.net.SocketException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.ws.rs.core.Response.Status;
import org.apache.servicecomb.config.ConfigUtil;
import org.apache.servicecomb.core.Invocation;
import org.apache.servicecomb.core.SCBEngine;
import org.apache.servicecomb.core.Transport;
import org.apache.servicecomb.core.bootstrap.SCBBootstrap;
import org.apache.servicecomb.core.transport.TransportManager;
import org.apache.servicecomb.foundation.common.Holder;
import org.apache.servicecomb.foundation.common.utils.SPIServiceUtils;
import org.apache.servicecomb.foundation.test.scaffolding.config.ArchaiusUtils;
import org.apache.servicecomb.registry.api.registry.MicroserviceInstance;
import org.apache.servicecomb.registry.cache.CacheEndpoint;
import org.apache.servicecomb.registry.discovery.DiscoveryFilter;
import org.apache.servicecomb.swagger.invocation.AsyncResponse;
import org.apache.servicecomb.swagger.invocation.Response;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import com.netflix.loadbalancer.LoadBalancerStats;
import mockit.Deencapsulation;
import mockit.Expectations;
import mockit.Injectable;
import mockit.Mock;
import mockit.MockUp;
import mockit.Mocked;
import org.junit.jupiter.api.Assertions;
/**
*
*
*/
public class TestLoadbalanceHandler {
static SCBEngine scbEngine;
static TransportManager transportManager;
String microserviceName = "ms";
LoadbalanceHandler handler;
Map<String, LoadBalancer> loadBalancerMap;
@Injectable
Invocation invocation;
@Mocked
Transport restTransport;
Response sendResponse;
@Before
public void setUp() {
ConfigUtil.installDynamicConfig();
scbEngine = SCBBootstrap.createSCBEngineForTest().run();
transportManager = scbEngine.getTransportManager();
new MockUp<Invocation>(invocation) {
@Mock
String getMicroserviceName() {
return microserviceName;
}
@Mock
void next(AsyncResponse asyncResp) throws Exception {
asyncResp.handle(sendResponse);
}
@Mock
public <T> T getLocalContext(String key) {
return (T) null;
}
};
new MockUp<TransportManager>(transportManager) {
@Mock
Transport findTransport(String transportName) {
return restTransport;
}
};
new Expectations(SPIServiceUtils.class) {
{
SPIServiceUtils.getSortedService(DiscoveryFilter.class);
result = Collections.emptyList();
}
};
BeansHolder holder = new BeansHolder();
List<ExtensionsFactory> extensionsFactories = new ArrayList<>();
extensionsFactories.add(new RuleNameExtentionsFactory());
Deencapsulation.setField(holder, "extentionsFactories", extensionsFactories);
holder.init();
handler = new LoadbalanceHandler();
loadBalancerMap = Deencapsulation.getField(handler, "loadBalancerMap");
}
@After
public void teardown() {
scbEngine.destroy();
ArchaiusUtils.resetConfig();
}
@Test
public void send_noEndPoint(@Injectable LoadBalancer loadBalancer) throws Exception {
new Expectations(loadBalancer) {
{
loadBalancer.chooseServer(invocation);
result = null;
}
};
Holder<Throwable> result = new Holder<>();
handler.send(invocation, resp -> result.value = resp.getResult(), loadBalancer);
Assertions.assertEquals("InvocationException: code=500;msg=CommonExceptionData [message=No available address found.]",
result.value.getMessage());
}
@Test
public void send_failed2(@Injectable LoadBalancer loadBalancer) throws Exception {
MicroserviceInstance instance1 = new MicroserviceInstance();
instance1.setInstanceId("1234");
CacheEndpoint cacheEndpoint = new CacheEndpoint("rest://localhost:8080", instance1);
ServiceCombServer server = new ServiceCombServer(null, restTransport, cacheEndpoint);
LoadBalancerStats stats = new LoadBalancerStats("test");
new Expectations(loadBalancer) {
{
loadBalancer.chooseServer(invocation);
result = server;
loadBalancer.getLoadBalancerStats();
result = stats;
}
};
sendResponse = Response.create(Status.BAD_REQUEST, "send failed");
Holder<Throwable> result = new Holder<>();
handler.send(invocation, resp -> result.value = resp.getResult(), loadBalancer);
// InvocationException is not taken as a failure
Assertions.assertEquals(0,
loadBalancer.getLoadBalancerStats().getSingleServerStat(server).getSuccessiveConnectionFailureCount());
Assertions.assertEquals("InvocationException: code=400;msg=send failed",
result.value.getMessage());
}
@Test
public void send_failed(@Injectable LoadBalancer loadBalancer) throws Exception {
MicroserviceInstance instance1 = new MicroserviceInstance();
instance1.setInstanceId("1234");
CacheEndpoint cacheEndpoint = new CacheEndpoint("rest://localhost:8080", instance1);
ServiceCombServer server = new ServiceCombServer(null, restTransport, cacheEndpoint);
LoadBalancerStats stats = new LoadBalancerStats("test");
new Expectations(loadBalancer) {
{
loadBalancer.chooseServer(invocation);
result = server;
loadBalancer.getLoadBalancerStats();
result = stats;
}
};
sendResponse = Response.consumerFailResp(new SocketException());
Holder<Throwable> result = new Holder<>();
handler.send(invocation, resp -> result.value = resp.getResult(), loadBalancer);
Assertions.assertEquals(1,
loadBalancer.getLoadBalancerStats().getSingleServerStat(server).getSuccessiveConnectionFailureCount());
Assertions.assertEquals(
"InvocationException: code=490;msg=CommonExceptionData [message=Unexpected consumer error, please check logs for details]",
result.value.getMessage());
}
@Test
public void send_success(@Injectable LoadBalancer loadBalancer) throws Exception {
MicroserviceInstance instance1 = new MicroserviceInstance();
instance1.setInstanceId("1234");
CacheEndpoint cacheEndpoint = new CacheEndpoint("rest://localhost:8080", instance1);
ServiceCombServer server = new ServiceCombServer(null, restTransport, cacheEndpoint);
LoadBalancerStats stats = new LoadBalancerStats("test");
new Expectations(loadBalancer) {
{
loadBalancer.chooseServer(invocation);
result = server;
loadBalancer.getLoadBalancerStats();
result = stats;
}
};
sendResponse = Response.ok("success");
Holder<String> result = new Holder<>();
handler.send(invocation, resp -> result.value = resp.getResult(), loadBalancer);
Assertions.assertEquals(1,
loadBalancer.getLoadBalancerStats().getSingleServerStat(server).getActiveRequestsCount());
Assertions.assertEquals("success", result.value);
}
@Test
public void testIsFailedResponse() {
Assertions.assertFalse(handler.isFailedResponse(Response.create(400, "", "")));
Assertions.assertFalse(handler.isFailedResponse(Response.create(500, "", "")));
Assertions.assertTrue(handler.isFailedResponse(Response.create(490, "", "")));
Assertions.assertTrue(handler.isFailedResponse(Response.consumerFailResp(new NullPointerException())));
}
}