public FeignLoadBalancer( ILoadBalancer lb, IClientConfig clientConfig, ServerIntrospector serverIntrospector) { super(lb, clientConfig); this.setRetryHandler(RetryHandler.DEFAULT); this.clientConfig = clientConfig; this.connectTimeout = clientConfig.get(CommonClientConfigKey.ConnectTimeout); this.readTimeout = clientConfig.get(CommonClientConfigKey.ReadTimeout); this.serverIntrospector = serverIntrospector; }
@Test public void testFeignOptionsClientConfig() { Request.Options options = new Request.Options(1111, 22222); IClientConfig config = new RibbonClient.FeignOptionsClientConfig(options); assertThat( config.get(CommonClientConfigKey.ConnectTimeout), equalTo(options.connectTimeoutMillis())); assertThat(config.get(CommonClientConfigKey.ReadTimeout), equalTo(options.readTimeoutMillis())); assertEquals(2, config.getProperties().size()); }
@Override public void initWithNiwsConfig(IClientConfig clientConfig) { clientName = clientConfig.getClientName(); vipAddresses = clientConfig.resolveDeploymentContextbasedVipAddresses(); if (vipAddresses == null && ConfigurationManager.getConfigInstance() .getBoolean("DiscoveryEnabledNIWSServerList.failFastOnNullVip", true)) { throw new NullPointerException("VIP address for client " + clientName + " is null"); } isSecure = Boolean.parseBoolean( "" + clientConfig.getProperty(CommonClientConfigKey.IsSecure, "false")); prioritizeVipAddressBasedServers = Boolean.parseBoolean( "" + clientConfig.getProperty( CommonClientConfigKey.PrioritizeVipAddressBasedServers, prioritizeVipAddressBasedServers)); datacenter = ConfigurationManager.getDeploymentContext().getDeploymentDatacenter(); targetRegion = (String) clientConfig.getProperty(CommonClientConfigKey.TargetRegion); // override client configuration and use client-defined port if (clientConfig.getPropertyAsBoolean( CommonClientConfigKey.ForceClientPortConfiguration, false)) { if (isSecure) { if (clientConfig.containsProperty(CommonClientConfigKey.SecurePort)) { overridePort = clientConfig.getPropertyAsInteger( CommonClientConfigKey.SecurePort, DefaultClientConfigImpl.DEFAULT_PORT); shouldUseOverridePort = true; } else { logger.warn( clientName + " set to force client port but no secure port is set, so ignoring"); } } else { if (clientConfig.containsProperty(CommonClientConfigKey.Port)) { overridePort = clientConfig.getPropertyAsInteger( CommonClientConfigKey.Port, DefaultClientConfigImpl.DEFAULT_PORT); shouldUseOverridePort = true; } else { logger.warn(clientName + " set to force client port but no port is set, so ignoring"); } } } }
@Override public RibbonResponse execute(RibbonRequest request, IClientConfig configOverride) throws IOException { Request.Options options; if (configOverride != null) { options = new Request.Options( configOverride.get(CommonClientConfigKey.ConnectTimeout, this.connectTimeout), (configOverride.get(CommonClientConfigKey.ReadTimeout, this.readTimeout))); } else { options = new Request.Options(this.connectTimeout, this.readTimeout); } Response response = request.client().execute(request.toRequest(), options); return new RibbonResponse(request.getUri(), response); }
/** Set necessary parameters from client configuration and register with Servo monitors. */ @Override public void initWithNiwsConfig(IClientConfig clientConfig) { if (clientConfig == null) { return; } vipAddresses = clientConfig.resolveDeploymentContextbasedVipAddresses(); clientName = clientConfig.getClientName(); if (clientName == null) { clientName = "default"; } try { maxAutoRetries = Integer.parseInt( clientConfig .getProperty(CommonClientConfigKey.MaxAutoRetries, maxAutoRetries) .toString()); } catch (Exception e) { logger.warn("Invalid maxRetries set for client:" + clientName); } try { maxAutoRetriesNextServer = Integer.parseInt( clientConfig .getProperty( CommonClientConfigKey.MaxAutoRetriesNextServer, maxAutoRetriesNextServer) .toString()); } catch (Exception e) { logger.warn("Invalid maxRetriesNextServer set for client:" + clientName); } try { Boolean bOkToRetryOnAllOperations = Boolean.valueOf( clientConfig .getProperty( CommonClientConfigKey.OkToRetryOnAllOperations, okToRetryOnAllOperations) .toString()); okToRetryOnAllOperations = bOkToRetryOnAllOperations.booleanValue(); } catch (Exception e) { logger.warn("Invalid OkToRetryOnAllOperations set for client:" + clientName); } tracer = Monitors.newTimer(clientName + "_OperationTimer", TimeUnit.MILLISECONDS); Monitors.registerObject("Client_" + clientName, this); }
/** * Get the client configuration given the name or create one with clientConfigClass if it does not * exist. An instance of IClientConfig is created and {@link IClientConfig#loadProperties(String)} * will be called. */ public static IClientConfig getNamedConfig( String name, Class<? extends IClientConfig> clientConfigClass) { IClientConfig config = namedConfig.get(name); if (config != null) { return config; } else { try { config = (IClientConfig) clientConfigClass.newInstance(); config.loadProperties(name); } catch (Throwable e) { logger.error("Unable to create client config instance", e); return null; } config.loadProperties(name); IClientConfig old = namedConfig.putIfAbsent(name, config); if (old != null) { config = old; } return config; } }
/** * Utility method to create client and load balancer (if enabled in client config) given the name * and client config. Instances are created using reflection (see {@link * #instantiateInstanceWithClientConfig(String, IClientConfig)} * * @param restClientName * @param clientConfig * @throws ClientException if any errors occurs in the process, or if the client with the same * name already exists */ public static synchronized IClient<?, ?> registerClientFromProperties( String restClientName, IClientConfig clientConfig) throws ClientException { IClient<?, ?> client = null; ILoadBalancer loadBalancer = null; if (simpleClientMap.get(restClientName) != null) { throw new ClientException( ClientException.ErrorType.GENERAL, "A Rest Client with this name is already registered. Please use a different name"); } try { String clientClassName = (String) clientConfig.getProperty(CommonClientConfigKey.ClientClassName); client = (IClient<?, ?>) instantiateInstanceWithClientConfig(clientClassName, clientConfig); boolean initializeNFLoadBalancer = Boolean.parseBoolean( clientConfig .getProperty( CommonClientConfigKey.InitializeNFLoadBalancer, DefaultClientConfigImpl.DEFAULT_ENABLE_LOADBALANCER) .toString()); if (initializeNFLoadBalancer) { loadBalancer = registerNamedLoadBalancerFromclientConfig(restClientName, clientConfig); } if (client instanceof AbstractLoadBalancerAwareClient) { ((AbstractLoadBalancerAwareClient) client).setLoadBalancer(loadBalancer); } } catch (Throwable e) { String message = "Unable to InitializeAndAssociateNFLoadBalancer set for RestClient:" + restClientName; logger.warn(message, e); throw new ClientException(ClientException.ErrorType.CONFIGURATION, message, e); } simpleClientMap.put(restClientName, client); Monitors.registerObject("Client_" + restClientName, client); logger.info("Client Registered:" + client.toString()); return client; }
/** * Create and register a load balancer with the name and given the class of configClass. * * @throws ClientException if load balancer with the same name already exists or any error occurs * @see #instantiateInstanceWithClientConfig(String, IClientConfig) */ public static ILoadBalancer registerNamedLoadBalancerFromclientConfig( String name, IClientConfig clientConfig) throws ClientException { if (namedLBMap.get(name) != null) { throw new ClientException("LoadBalancer for name " + name + " already exists"); } ILoadBalancer lb = null; try { String loadBalancerClassName = (String) clientConfig.getProperty(CommonClientConfigKey.NFLoadBalancerClassName); lb = (ILoadBalancer) ClientFactory.instantiateInstanceWithClientConfig( loadBalancerClassName, clientConfig); namedLBMap.put(name, lb); logger.info("Client:" + name + " instantiated a LoadBalancer:" + lb.toString()); return lb; } catch (Exception e) { throw new ClientException( "Unable to instantiate/associate LoadBalancer with Client:" + name, e); } }
/** * This method should be used when the caller wants to dispatch the request to a server chosen by * the load balancer, instead of specifying the server in the request's URI. It calculates the * final URI by calling {@link #computeFinalUriWithLoadBalancer(ClientRequest)} and then calls * {@link #execute(ClientRequest)}. * * @param request request to be dispatched to a server chosen by the load balancer. The URI can be * a partial URI which does not contain the host name or the protocol. */ public T executeWithLoadBalancer(S request) throws ClientException { int retries = 0; boolean done = false; boolean retryOkayOnOperation = okToRetryOnAllOperations; retryOkayOnOperation = request.isRetriable(); // Is it okay to retry for this particular operation? // see if maxRetries has been overriden int numRetries = maxAutoRetriesNextServer; IClientConfig overriddenClientConfig = request.getOverrideConfig(); if (overriddenClientConfig != null) { try { numRetries = Integer.parseInt( "" + overriddenClientConfig.getProperty( CommonClientConfigKey.MaxAutoRetriesNextServer, maxAutoRetriesNextServer)); } catch (Exception e) { logger.warn( "Invalid maxAutoRetriesNextServer requested for RestClient:" + this.getClientName()); } try { // Retry operation can be forcefully turned on or off for this particular request Boolean requestSpecificRetryOn = Boolean.valueOf( "" + overriddenClientConfig.getProperty( CommonClientConfigKey.RequestSpecificRetryOn, "false")); retryOkayOnOperation = requestSpecificRetryOn.booleanValue(); } catch (Exception e) { logger.warn("Invalid RequestSpecificRetryOn set for RestClient:" + this.getClientName()); } } T response = null; do { try { S resolved = computeFinalUriWithLoadBalancer(request); response = executeOnSingleServer(resolved); done = true; } catch (Exception e) { boolean shouldRetry = false; if (e instanceof ClientException) { // we dont want to retry for PUT/POST and DELETE, we can for GET shouldRetry = retryOkayOnOperation && numRetries > 0; } if (shouldRetry) { retries++; if (retries > numRetries) { throw new ClientException( ClientException.ErrorType.NUMBEROF_RETRIES_NEXTSERVER_EXCEEDED, "NUMBER_OF_RETRIES_NEXTSERVER_EXCEEDED :" + numRetries + " retries, while making a RestClient call for:" + request.getUri() + ":" + getDeepestCause(e).getMessage(), e); } logger.error( "Exception while executing request which is deemed retry-able, retrying ..., Next Server Retry Attempt#:" + retries + ", URI tried:" + request.getUri()); } else { if (e instanceof ClientException) { throw (ClientException) e; } else { throw new ClientException( ClientException.ErrorType.GENERAL, "Unable to execute request for URI:" + request.getUri(), e); } } } } while (!done); return response; }
/** * Execute the request on single server after the final URI is calculated. This method takes care * of retries and update server stats. */ protected T executeOnSingleServer(S request) throws ClientException { boolean done = false; int retries = 0; boolean retryOkayOnOperation = okToRetryOnAllOperations; if (request.isRetriable()) { retryOkayOnOperation = true; } int numRetries = maxAutoRetries; URI uri = request.getUri(); Server server = new Server(uri.getHost(), uri.getPort()); ServerStats serverStats = null; ILoadBalancer lb = this.getLoadBalancer(); if (lb instanceof AbstractLoadBalancer) { LoadBalancerStats lbStats = ((AbstractLoadBalancer) lb).getLoadBalancerStats(); serverStats = lbStats.getSingleServerStat(server); } IClientConfig overriddenClientConfig = request.getOverrideConfig(); if (overriddenClientConfig != null) { try { numRetries = Integer.parseInt( "" + overriddenClientConfig.getProperty( CommonClientConfigKey.MaxAutoRetries, maxAutoRetries)); } catch (Exception e) { logger.warn("Invalid maxRetries requested for RestClient:" + this.clientName); } } T response = null; Exception lastException = null; if (tracer == null) { tracer = Monitors.newTimer(this.getClass().getName() + "_ExecutionTimer", TimeUnit.MILLISECONDS); } do { noteOpenConnection(serverStats, request); Stopwatch w = tracer.start(); try { response = execute(request); done = true; } catch (Exception e) { if (serverStats != null) { serverStats.addToFailureCount(); } lastException = e; if (isCircuitBreakerException(e) && serverStats != null) { serverStats.incrementSuccessiveConnectionFailureCount(); } boolean shouldRetry = retryOkayOnOperation && numRetries >= 0 && isRetriableException(e); if (shouldRetry) { retries = handleRetry(uri.toString(), retries, numRetries, e); } else { ClientException niwsClientException = generateNIWSException(uri.toString(), e); throw niwsClientException; } } finally { w.stop(); noteRequestCompletion( serverStats, request, response, lastException, w.getDuration(TimeUnit.MILLISECONDS)); } } while (!done); return response; }
@Override public void initWithNiwsConfig(IClientConfig config) { this.appName = config.getClientName(); }
public DiscoveryEnabledNIWSServerList(String vipAddresses) { IClientConfig clientConfig = DefaultClientConfigImpl.getClientConfigWithDefaultValues(); clientConfig.set(Keys.DeploymentContextBasedVipAddresses, vipAddresses); initWithNiwsConfig(clientConfig); }