use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class SimpleLoadBalancer method getClient.
/**
* Given a Request, returns a TransportClient that can handle requests for the Request.
*
*
* @param request
* A request whose URI is a URL of the format "d2://>servicename</optional/path".
* @param requestContext context for this request
* @return A client that can be called to retrieve data for the URN.
* @throws ServiceUnavailableException
* If the load balancer can't figure out how to reach a service for the given
* URN, an ServiceUnavailableException will be thrown.
*/
@Override
public TransportClient getClient(Request request, RequestContext requestContext) throws ServiceUnavailableException {
TransportClient client;
URI uri = request.getURI();
debug(_log, "get client for uri: ", uri);
ServiceProperties service = listenToServiceAndCluster(uri);
String serviceName = service.getServiceName();
String clusterName = service.getClusterName();
ClusterProperties cluster = getClusterProperties(serviceName, clusterName);
// Check if we want to override the service URL and bypass choosing among the existing
// tracker clients. This is useful when the service we want is not announcing itself to
// the cluster, ie a private service for a set of clients.
URI targetService = LoadBalancerUtil.TargetHints.getRequestContextTargetService(requestContext);
if (targetService == null) {
LoadBalancerStateItem<UriProperties> uriItem = getUriItem(serviceName, clusterName, cluster);
UriProperties uris = uriItem.getProperty();
List<LoadBalancerState.SchemeStrategyPair> orderedStrategies = _state.getStrategiesForService(serviceName, service.getPrioritizedSchemes());
TrackerClient trackerClient = chooseTrackerClient(request, requestContext, serviceName, clusterName, cluster, uriItem, uris, orderedStrategies, service);
String clusterAndServiceUriString = trackerClient.getUri() + service.getPath();
client = new RewriteClient(serviceName, URI.create(clusterAndServiceUriString), trackerClient);
_serviceAvailableStats.inc();
} else {
_log.debug("service hint found, using generic client for target: {}", targetService);
TransportClient transportClient = _state.getClient(serviceName, targetService.getScheme());
client = new RewriteClient(serviceName, targetService, transportClient);
}
return client;
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class SimpleLoadBalancerState method createAndInsertTransportClientTo.
private Map<String, TransportClient> createAndInsertTransportClientTo(ServiceProperties serviceProperties) {
Map<String, Object> transportClientProperties = new HashMap<String, Object>(serviceProperties.getTransportClientProperties());
Object allowedClientOverrideKeysObj = transportClientProperties.remove(PropertyKeys.ALLOWED_CLIENT_OVERRIDE_KEYS);
Set<String> allowedClientOverrideKeys = new HashSet<String>(ConfigValueExtractor.buildList(allowedClientOverrideKeysObj, LIST_SEPARATOR));
Map<String, Object> clientSuppliedServiceProperties = _clientServicesConfig.get(serviceProperties.getServiceName());
if (clientSuppliedServiceProperties != null) {
debug(_log, "Client supplied configs for service {}", new Object[] { serviceProperties.getServiceName() });
// check for overrides
for (String clientSuppliedKey : clientSuppliedServiceProperties.keySet()) {
// clients can only override config properties which have been allowed by the service
if (allowedClientOverrideKeys.contains(clientSuppliedKey)) {
if (ClientServiceConfigValidator.isValidValue(transportClientProperties, clientSuppliedServiceProperties, clientSuppliedKey)) {
transportClientProperties.put(clientSuppliedKey, clientSuppliedServiceProperties.get(clientSuppliedKey));
info(_log, "Client overrode config property {} for service {}. This is being used to instantiate the Transport Client", new Object[] { clientSuppliedKey, serviceProperties.getServiceName() });
} else {
warn(_log, "Client supplied config property {} with an invalid value {} for service {}", new Object[] { clientSuppliedKey, clientSuppliedServiceProperties.get(clientSuppliedKey), serviceProperties.getServiceName() });
}
}
}
}
List<String> schemes = serviceProperties.getPrioritizedSchemes();
Map<String, TransportClient> newTransportClients = new HashMap<String, TransportClient>();
if (schemes != null && !schemes.isEmpty()) {
for (String scheme : schemes) {
TransportClientFactory factory = _clientFactories.get(scheme);
if ("https".equals(scheme)) {
if (_isSSLEnabled) {
// should have been passed in during creation.
if (_sslContext != null && _sslParameters != null) {
transportClientProperties.put(HttpClientFactory.HTTP_SSL_CONTEXT, _sslContext);
transportClientProperties.put(HttpClientFactory.HTTP_SSL_PARAMS, _sslParameters);
} else {
_log.error("https specified as a prioritized scheme for service: " + serviceProperties.getServiceName() + " but no SSLContext or SSLParameters have been configured.");
if (schemes.size() == 1) {
// throw exception when https is the only scheme specified
throw new IllegalStateException("SSL enabled but required SSLContext and SSLParameters" + "were not both present.");
}
// Do not create the transport client for https.
continue;
}
} else {
// is requested later on, getTrackerClient will catch this situation and log an error.
continue;
}
}
if (factory != null) {
transportClientProperties.put(HttpClientFactory.HTTP_SERVICE_NAME, serviceProperties.getServiceName());
TransportClient client = factory.getClient(transportClientProperties);
newTransportClients.put(scheme.toLowerCase(), client);
} else {
_log.warn("Failed to find client factory for scheme {}", scheme);
}
}
} else {
_log.warn("Prioritized schemes is null for service properties = " + serviceProperties.getServiceName());
}
return newTransportClients;
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class SimpleLoadBalancerState method refreshTransportClientsPerService.
void refreshTransportClientsPerService(ServiceProperties serviceProperties) {
String serviceName = serviceProperties.getServiceName();
//create new TransportClients
Map<String, TransportClient> newTransportClients = createAndInsertTransportClientTo(serviceProperties);
// clients-by-scheme map is never edited, only replaced.
newTransportClients = Collections.unmodifiableMap(newTransportClients);
final Map<String, TransportClient> oldTransportClients = _serviceClients.put(serviceName, newTransportClients);
// gets the information for configuring the parameter for how DegraderImpl should behave for
// each tracker clients that we instantiate here. If there's no such information, then we'll instantiate
// each tracker clients with default configuration
DegraderImpl.Config config = null;
if (serviceProperties.getDegraderProperties() != null && !serviceProperties.getDegraderProperties().isEmpty()) {
config = DegraderConfigFactory.toDegraderConfig(serviceProperties.getDegraderProperties());
} else {
debug(_log, "trying to see if there's a special degraderImpl properties but serviceInfo.getDegraderImpl() is null" + " for service name = " + serviceName + " so we'll set config to default");
}
Clock clk = SystemClock.instance();
if (serviceProperties.getLoadBalancerStrategyProperties() != null) {
Map<String, Object> loadBalancerStrategyProperties = serviceProperties.getLoadBalancerStrategyProperties();
clk = MapUtil.getWithDefault(loadBalancerStrategyProperties, PropertyKeys.CLOCK, SystemClock.instance(), Clock.class);
}
Map<URI, TrackerClient> newTrackerClients;
// update all tracker clients to use new configs
LoadBalancerStateItem<UriProperties> uriItem = _uriProperties.get(serviceProperties.getClusterName());
UriProperties uriProperties = uriItem == null ? null : uriItem.getProperty();
if (uriProperties != null) {
Set<URI> uris = uriProperties.Uris();
// clients-by-uri map may be edited later by UriPropertiesListener.handlePut
newTrackerClients = new ConcurrentHashMap<URI, TrackerClient>(CollectionUtils.getMapInitialCapacity(uris.size(), 0.75f), 0.75f, 1);
long trackerClientInterval = getTrackerClientInterval(serviceProperties);
String errorStatusPattern = getErrorStatusPattern(serviceProperties);
for (URI uri : uris) {
TrackerClient trackerClient = getTrackerClient(serviceName, uri, uriProperties.getPartitionDataMap(uri), config, clk, trackerClientInterval, errorStatusPattern);
if (trackerClient != null) {
newTrackerClients.put(uri, trackerClient);
}
}
} else {
// clients-by-uri map may be edited later by UriPropertiesListener.handlePut
newTrackerClients = new ConcurrentHashMap<URI, TrackerClient>(16, 0.75f, 1);
}
//override the oldTrackerClients with newTrackerClients
_trackerClients.put(serviceName, newTrackerClients);
// No need to shut down oldTrackerClients, because they all point directly to the TransportClient for the service
// We do need to shut down the old transport clients
shutdownTransportClients(oldTransportClients, serviceName);
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class TrackerClientTest method testCallTrackingRestRequest.
@Test
public void testCallTrackingRestRequest() throws Exception {
URI uri = URI.create("http://test.qa.com:1234/foo");
SettableClock clock = new SettableClock();
AtomicInteger action = new AtomicInteger(0);
TransportClient tc = new TransportClient() {
@Override
public void restRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<RestResponse> callback) {
clock.addDuration(5);
switch(action.get()) {
// success
case 0:
callback.onResponse(TransportResponseImpl.success(RestResponse.NO_RESPONSE));
break;
// fail with rest exception
case 1:
callback.onResponse(TransportResponseImpl.error(RestException.forError(500, "rest exception")));
break;
// fail with timeout exception
case 2:
callback.onResponse(TransportResponseImpl.error(new RemoteInvocationException(new TimeoutException())));
break;
// fail with other exception
default:
callback.onResponse(TransportResponseImpl.error(new RuntimeException()));
break;
}
}
@Override
public void shutdown(Callback<None> callback) {
}
};
TrackerClient client = createTrackerClient(tc, clock, uri);
CallTracker callTracker = client.getCallTracker();
CallTracker.CallStats stats;
DegraderControl degraderControl = client.getDegraderControl(DefaultPartitionAccessor.DEFAULT_PARTITION_ID);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 0);
Assert.assertEquals(stats.getCallCountTotal(), 1);
Assert.assertEquals(stats.getErrorCountTotal(), 0);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.0, 0.001);
action.set(1);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 2);
Assert.assertEquals(stats.getErrorCountTotal(), 1);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
action.set(2);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 3);
Assert.assertEquals(stats.getErrorCountTotal(), 2);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.4, 0.001);
action.set(3);
client.restRequest(new RestRequestBuilder(uri).build(), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 4);
Assert.assertEquals(stats.getErrorCountTotal(), 3);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
}
use of com.linkedin.r2.transport.common.bridge.client.TransportClient in project rest.li by linkedin.
the class TrackerClientTest method testCallTrackingStreamRequest.
@Test
public void testCallTrackingStreamRequest() throws Exception {
URI uri = URI.create("http://test.qa.com:1234/foo");
SettableClock clock = new SettableClock();
AtomicInteger action = new AtomicInteger(0);
TransportClient tc = new TransportClient() {
@Override
public void restRequest(RestRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<RestResponse> callback) {
}
@Override
public void streamRequest(StreamRequest request, RequestContext requestContext, Map<String, String> wireAttrs, TransportCallback<StreamResponse> callback) {
clock.addDuration(5);
switch(action.get()) {
// success
case 0:
callback.onResponse(TransportResponseImpl.success(new StreamResponseBuilder().build(EntityStreams.emptyStream())));
break;
// fail with stream exception
case 1:
callback.onResponse(TransportResponseImpl.error(new StreamException(new StreamResponseBuilder().setStatus(500).build(EntityStreams.emptyStream()))));
break;
// fail with timeout exception
case 2:
callback.onResponse(TransportResponseImpl.error(new RemoteInvocationException(new TimeoutException())));
break;
// fail with other exception
default:
callback.onResponse(TransportResponseImpl.error(new RuntimeException()));
break;
}
}
@Override
public void shutdown(Callback<None> callback) {
}
};
TrackerClient client = createTrackerClient(tc, clock, uri);
CallTracker callTracker = client.getCallTracker();
CallTracker.CallStats stats;
DegraderControl degraderControl = client.getDegraderControl(DefaultPartitionAccessor.DEFAULT_PARTITION_ID);
DelayConsumeCallback delayConsumeCallback = new DelayConsumeCallback();
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), delayConsumeCallback);
clock.addDuration(5);
// we only recorded the time when stream response arrives, but callcompletion.endcall hasn't been called yet.
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 0);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 0);
// delay
clock.addDuration(100);
delayConsumeCallback.consume();
clock.addDuration(5000);
// now that we consumed the entity stream, callcompletion.endcall has been called.
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 0);
Assert.assertEquals(stats.getCallCountTotal(), 1);
Assert.assertEquals(stats.getErrorCountTotal(), 0);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.0, 0.001);
action.set(1);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), delayConsumeCallback);
clock.addDuration(5);
// we endcall with error immediately for stream exception, even before the entity is consumed
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 2);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 1);
delayConsumeCallback.consume();
clock.addDuration(5000);
// no change in tracking after entity is consumed
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 2);
Assert.assertEquals(stats.getErrorCountTotal(), 1);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
action.set(2);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5);
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 3);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 2);
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 3);
Assert.assertEquals(stats.getErrorCountTotal(), 2);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.4, 0.001);
action.set(3);
client.streamRequest(new StreamRequestBuilder(uri).build(EntityStreams.emptyStream()), new RequestContext(), new HashMap<>(), new TestTransportCallback<>());
clock.addDuration(5);
Assert.assertEquals(callTracker.getCurrentCallCountTotal(), 4);
Assert.assertEquals(callTracker.getCurrentErrorCountTotal(), 3);
clock.addDuration(5000);
stats = callTracker.getCallStats();
Assert.assertEquals(stats.getCallCount(), 1);
Assert.assertEquals(stats.getErrorCount(), 1);
Assert.assertEquals(stats.getCallCountTotal(), 4);
Assert.assertEquals(stats.getErrorCountTotal(), 3);
Assert.assertEquals(degraderControl.getCurrentComputedDropRate(), 0.2, 0.001);
}
Aggregations