Search in sources :

Example 1 with TransportClient

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;
}
Also used : ServiceProperties(com.linkedin.d2.balancer.properties.ServiceProperties) TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) UriProperties(com.linkedin.d2.balancer.properties.UriProperties) ClusterProperties(com.linkedin.d2.balancer.properties.ClusterProperties) URI(java.net.URI) RewriteClient(com.linkedin.d2.balancer.clients.RewriteClient)

Example 2 with TransportClient

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;
}
Also used : TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) ConcurrentHashMap(java.util.concurrent.ConcurrentHashMap) HashMap(java.util.HashMap) TransportClientFactory(com.linkedin.r2.transport.common.TransportClientFactory) HashSet(java.util.HashSet)

Example 3 with TransportClient

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);
}
Also used : TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) DegraderImpl(com.linkedin.util.degrader.DegraderImpl) SystemClock(com.linkedin.util.clock.SystemClock) Clock(com.linkedin.util.clock.Clock) URI(java.net.URI) TrackerClient(com.linkedin.d2.balancer.clients.TrackerClient) UriProperties(com.linkedin.d2.balancer.properties.UriProperties)

Example 4 with TransportClient

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);
}
Also used : TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) DegraderControl(com.linkedin.util.degrader.DegraderControl) URI(java.net.URI) RestRequest(com.linkedin.r2.message.rest.RestRequest) Callback(com.linkedin.common.callback.Callback) TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SettableClock(com.linkedin.util.clock.SettableClock) RestRequestBuilder(com.linkedin.r2.message.rest.RestRequestBuilder) RequestContext(com.linkedin.r2.message.RequestContext) RemoteInvocationException(com.linkedin.r2.RemoteInvocationException) HashMap(java.util.HashMap) Map(java.util.Map) TimeoutException(java.util.concurrent.TimeoutException) CallTracker(com.linkedin.util.degrader.CallTracker) Test(org.testng.annotations.Test)

Example 5 with TransportClient

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);
}
Also used : TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) TransportClient(com.linkedin.r2.transport.common.bridge.client.TransportClient) StreamResponseBuilder(com.linkedin.r2.message.stream.StreamResponseBuilder) DegraderControl(com.linkedin.util.degrader.DegraderControl) URI(java.net.URI) StreamRequestBuilder(com.linkedin.r2.message.stream.StreamRequestBuilder) StreamRequest(com.linkedin.r2.message.stream.StreamRequest) StreamException(com.linkedin.r2.message.stream.StreamException) RestRequest(com.linkedin.r2.message.rest.RestRequest) Callback(com.linkedin.common.callback.Callback) TransportCallback(com.linkedin.r2.transport.common.bridge.common.TransportCallback) AtomicInteger(java.util.concurrent.atomic.AtomicInteger) SettableClock(com.linkedin.util.clock.SettableClock) RequestContext(com.linkedin.r2.message.RequestContext) RemoteInvocationException(com.linkedin.r2.RemoteInvocationException) HashMap(java.util.HashMap) Map(java.util.Map) TimeoutException(java.util.concurrent.TimeoutException) CallTracker(com.linkedin.util.degrader.CallTracker) Test(org.testng.annotations.Test)

Aggregations

TransportClient (com.linkedin.r2.transport.common.bridge.client.TransportClient)46 HashMap (java.util.HashMap)28 RequestContext (com.linkedin.r2.message.RequestContext)22 URI (java.net.URI)21 Test (org.testng.annotations.Test)17 HttpClientFactory (com.linkedin.r2.transport.http.client.HttpClientFactory)15 TransportClientFactory (com.linkedin.r2.transport.common.TransportClientFactory)13 TrackerClient (com.linkedin.d2.balancer.clients.TrackerClient)10 TransportClientAdapter (com.linkedin.r2.transport.common.bridge.client.TransportClientAdapter)10 Map (java.util.Map)10 CountDownLatch (java.util.concurrent.CountDownLatch)10 Callback (com.linkedin.common.callback.Callback)9 ServiceProperties (com.linkedin.d2.balancer.properties.ServiceProperties)8 RestRequest (com.linkedin.r2.message.rest.RestRequest)8 UriProperties (com.linkedin.d2.balancer.properties.UriProperties)7 LoadBalancerStrategy (com.linkedin.d2.balancer.strategies.LoadBalancerStrategy)7 AtomicBoolean (java.util.concurrent.atomic.AtomicBoolean)7 None (com.linkedin.common.util.None)6 ClusterProperties (com.linkedin.d2.balancer.properties.ClusterProperties)6 RestRequestBuilder (com.linkedin.r2.message.rest.RestRequestBuilder)6