天天看點

【摘】Spring Cloud Context

原文連結:https://cloud.spring.io/spring-cloud-commons/reference/html/#spring-cloud-context-application-context-services

Many of those features are covered by Spring Boot, on which Spring Cloud builds. Some more features are delivered by Spring Cloud as two libraries: Spring Cloud Context and Spring Cloud Commons. Spring Cloud Context provides utilities and special services for the 

ApplicationContext

 of a Spring Cloud application (bootstrap context, encryption, refresh scope, and environment endpoints). Spring Cloud Commons is a set of abstractions and common classes used in different Spring Cloud implementations (such as Spring Cloud Netflix and Spring Cloud Consul).

目錄

1. Spring Cloud Context: Application Context Services

1.1. The Bootstrap Application Context

1.2. Application Context Hierarchies

1.3. Changing the Location of Bootstrap Properties

1.4. Overriding the Values of Remote Properties

1.5. Customizing the Bootstrap Configuration

1.6. Customizing the Bootstrap Property Sources

1.7. Logging Configuration

1.8. Environment Changes

1.9. Refresh Scope

1.10. Encryption and Decryption

1.11. Endpoints

2. Spring Cloud Commons: Common Abstractions

2.1. The @EnableDiscoveryClient Annotation

2.2. ServiceRegistry

2.3. Spring RestTemplate as a Load Balancer Client

2.4. Spring WebClient as a Load Balancer Client

2.5. Multiple RestTemplate Objects

2.6. Multiple WebClient Objects

2.7. Spring WebFlux WebClient as a Load Balancer Client

2.8. Ignore Network Interfaces

2.9. HTTP Client Factories

2.10. Enabled Features

2.11. Spring Cloud Compatibility Verification

3. Spring Cloud LoadBalancer

3.1. Spring Cloud LoadBalancer integrations

3.2. Spring Cloud LoadBalancer Caching

3.3. Zone-Based Load-Balancing

3.4. Instance Health-Check for LoadBalancer

3.5. Spring Cloud LoadBalancer Starter

3.6. Passing Your Own Spring Cloud LoadBalancer Configuration

4. Spring Cloud Circuit Breaker

4.1. Introduction

4.2. Core Concepts

4.3. Configuration

5. CachedRandomPropertySource

6. Configuration Properties

1. Spring Cloud Context: Application Context Services

Spring Boot has an opinionated view of how to build an application with Spring. For instance, it has conventional locations for common configuration files and has endpoints for common management and monitoring tasks. Spring Cloud builds on top of that and adds a few features that many components in a system would use or occasionally need.

1.1. The Bootstrap Application Context

A Spring Cloud application operates by creating a “bootstrap” context, which is a parent context for the main application. This context is responsible for loading configuration properties from the external sources and for decrypting properties in the local external configuration files. The two contexts share an 

Environment

, which is the source of external properties for any Spring application. By default, bootstrap properties (not 

bootstrap.properties

 but properties that are loaded during the bootstrap phase) are added with high precedence, so they cannot be overridden by local configuration.

The bootstrap context uses a different convention for locating external configuration than the main application context. Instead of 

application.yml

 (or 

.properties

), you can use 

bootstrap.yml

, keeping the external configuration for bootstrap and main context nicely separate. The following listing shows an example:

Example 1. bootstrap.yml

spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}
           

If your application needs any application-specific configuration from the server, it is a good idea to set the 

spring.application.name

 (in 

bootstrap.yml

 or 

application.yml

). For the property 

spring.application.name

 to be used as the application’s context ID, you must set it in 

bootstrap.[properties | yml]

.

If you want to retrieve specific profile configuration, you should also set 

spring.profiles.active

 in 

bootstrap.[properties | yml]

.

You can disable the bootstrap process completely by setting 

spring.cloud.bootstrap.enabled=false

 (for example, in system properties).

1.2. Application Context Hierarchies

If you build an application context from 

SpringApplication

 or 

SpringApplicationBuilder

, the Bootstrap context is added as a parent to that context. It is a feature of Spring that child contexts inherit property sources and profiles from their parent, so the “main” application context contains additional property sources, compared to building the same context without Spring Cloud Config. The additional property sources are:

  • “bootstrap”: If any 

    PropertySourceLocators

     are found in the bootstrap context and if they have non-empty properties, an optional 

    CompositePropertySource

     appears with high priority. An example would be properties from the Spring Cloud Config Server. See “Customizing the Bootstrap Property Sources” for how to customize the contents of this property source.
  • “applicationConfig: [classpath:bootstrap.yml]” (and related files if Spring profiles are active): If you have a 

    bootstrap.yml

     (or 

    .properties

    ), those properties are used to configure the bootstrap context. Then they get added to the child context when its parent is set. They have lower precedence than the 

    application.yml

     (or 

    .properties

    ) and any other property sources that are added to the child as a normal part of the process of creating a Spring Boot application. See “Changing the Location of Bootstrap Properties” for how to customize the contents of these property sources.

Because of the ordering rules of property sources, the “bootstrap” entries take precedence. However, note that these do not contain any data from 

bootstrap.yml

, which has very low precedence but can be used to set defaults.

You can extend the context hierarchy by setting the parent context of any 

ApplicationContext

 you create — for example, by using its own interface or with the 

SpringApplicationBuilder

 convenience methods (

parent()

child()

 and 

sibling()

). The bootstrap context is the parent of the most senior ancestor that you create yourself. Every context in the hierarchy has its own “bootstrap” (possibly empty) property source to avoid promoting values inadvertently from parents down to their descendants. If there is a config server, every context in the hierarchy can also (in principle) have a different 

spring.application.name

 and, hence, a different remote property source. Normal Spring application context behavior rules apply to property resolution: properties from a child context override those in the parent, by name and also by property source name. (If the child has a property source with the same name as the parent, the value from the parent is not included in the child).

Note that the 

SpringApplicationBuilder

 lets you share an 

Environment

 amongst the whole hierarchy, but that is not the default. Thus, sibling contexts (in particular) do not need to have the same profiles or property sources, even though they may share common values with their parent.

1.3. Changing the Location of Bootstrap Properties

The 

bootstrap.yml

 (or 

.properties

) location can be specified by setting 

spring.cloud.bootstrap.name

 (default: 

bootstrap

), 

spring.cloud.bootstrap.location

 (default: empty) or 

spring.cloud.bootstrap.additional-location

 (default: empty) — for example, in System properties.

Those properties behave like the 

spring.config.*

 variants with the same name. With 

spring.cloud.bootstrap.location

 the default locations are replaced and only the specified ones are used. To add locations to the list of default ones, 

spring.cloud.bootstrap.additional-location

 could be used. In fact, they are used to set up the bootstrap 

ApplicationContext

 by setting those properties in its 

Environment

. If there is an active profile (from 

spring.profiles.active

 or through the 

Environment

 API in the context you are building), properties in that profile get loaded as well, the same as in a regular Spring Boot app — for example, from 

bootstrap-development.properties

 for a 

development

 profile.

1.4. Overriding the Values of Remote Properties

The property sources that are added to your application by the bootstrap context are often “remote” (from example, from Spring Cloud Config Server). By default, they cannot be overridden locally. If you want to let your applications override the remote properties with their own system properties or config files, the remote property source has to grant it permission by setting 

spring.cloud.config.allowOverride=true

 (it does not work to set this locally). Once that flag is set, two finer-grained settings control the location of the remote properties in relation to system properties and the application’s local configuration:

  • spring.cloud.config.overrideNone=true

    : Override from any local property source.
  • spring.cloud.config.overrideSystemProperties=false

    : Only system properties, command line arguments, and environment variables (but not the local config files) should override the remote settings.

1.5. Customizing the Bootstrap Configuration

The bootstrap context can be set to do anything you like by adding entries to 

/META-INF/spring.factories

 under a key named 

org.springframework.cloud.bootstrap.BootstrapConfiguration

. This holds a comma-separated list of Spring 

@Configuration

 classes that are used to create the context. Any beans that you want to be available to the main application context for autowiring can be created here. There is a special contract for 

@Beans

 of type 

ApplicationContextInitializer

. If you want to control the startup sequence, you can mark classes with the 

@Order

 annotation (the default order is 

last

).

When adding custom 

BootstrapConfiguration

, be careful that the classes you add are not 

@ComponentScanned

 by mistake into your “main” application context, where they might not be needed. Use a separate package name for boot configuration classes and make sure that name is not already covered by your 

@ComponentScan

 or 

@SpringBootApplication

 annotated configuration classes.

The bootstrap process ends by injecting initializers into the main 

SpringApplication

 instance (which is the normal Spring Boot startup sequence, whether it runs as a standalone application or is deployed in an application server). First, a bootstrap context is created from the classes found in 

spring.factories

. Then, all 

@Beans

 of type 

ApplicationContextInitializer

 are added to the main 

SpringApplication

 before it is started.

1.6. Customizing the Bootstrap Property Sources

The default property source for external configuration added by the bootstrap process is the Spring Cloud Config Server, but you can add additional sources by adding beans of type 

PropertySourceLocator

 to the bootstrap context (through 

spring.factories

). For instance, you can insert additional properties from a different server or from a database.

As an example, consider the following custom locator:

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

    @Override
    public PropertySource<?> locate(Environment environment) {
        return new MapPropertySource("customProperty",
                Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
    }

}
           

The 

Environment

 that is passed in is the one for the 

ApplicationContext

 about to be created — in other words, the one for which we supply additional property sources. It already has its normal Spring Boot-provided property sources, so you can use those to locate a property source specific to this 

Environment

 (for example, by keying it on 

spring.application.name

, as is done in the default Spring Cloud Config Server property source locator).

If you create a jar with this class in it and then add a 

META-INF/spring.factories

 containing the following setting, the 

customProperty

PropertySource

 appears in any application that includes that jar on its classpath:

org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
           

1.7. Logging Configuration

If you use Spring Boot to configure log settings, you should place this configuration in 

bootstrap.[yml | properties]

 if you would like it to apply to all events.

For Spring Cloud to initialize logging configuration properly, you cannot use a custom prefix. For example, using 

custom.loggin.logpath

 is not recognized by Spring Cloud when initializing the logging system.

1.8. Environment Changes

The application listens for an 

EnvironmentChangeEvent

 and reacts to the change in a couple of standard ways (additional 

ApplicationListeners

 can be added as 

@Beans

 in the normal way). When an 

EnvironmentChangeEvent

 is observed, it has a list of key values that have changed, and the application uses those to:

  • Re-bind any 

    @ConfigurationProperties

     beans in the context.
  • Set the logger levels for any properties in 

    logging.level.*

    .

Note that the Spring Cloud Config Client does not, by default, poll for changes in the 

Environment

. Generally, we would not recommend that approach for detecting changes (although you could set it up with a 

@Scheduled

 annotation). If you have a scaled-out client application, it is better to broadcast the 

EnvironmentChangeEvent

 to all the instances instead of having them polling for changes (for example, by using the Spring Cloud Bus).

The 

EnvironmentChangeEvent

 covers a large class of refresh use cases, as long as you can actually make a change to the 

Environment

 and publish the event. Note that those APIs are public and part of core Spring). You can verify that the changes are bound to 

@ConfigurationProperties

 beans by visiting the 

/configprops

 endpoint (a standard Spring Boot Actuator feature). For instance, a 

DataSource

 can have its 

maxPoolSize

 changed at runtime (the default 

DataSource

 created by Spring Boot is a 

@ConfigurationProperties

 bean) and grow capacity dynamically. Re-binding 

@ConfigurationProperties

 does not cover another large class of use cases, where you need more control over the refresh and where you need a change to be atomic over the whole 

ApplicationContext

. To address those concerns, we have 

@RefreshScope

.

1.9. Refresh Scope

When there is a configuration change, a Spring 

@Bean

 that is marked as 

@RefreshScope

 gets special treatment. This feature addresses the problem of stateful beans that get their configuration injected only when they are initialized. For instance, if a 

DataSource

 has open connections when the database URL is changed through the 

Environment

, you probably want the holders of those connections to be able to complete what they are doing. Then, the next time something borrows a connection from the pool, it gets one with the new URL.

Sometimes, it might even be mandatory to apply the 

@RefreshScope

 annotation on some beans that can be only initialized once. If a bean is “immutable”, you have to either annotate the bean with 

@RefreshScope

 or specify the classname under the property key: 

spring.cloud.refresh.extra-refreshable

.

If you hava a 

DataSource

 bean that is a 

HikariDataSource

, it can not be refreshed. It is the default value for 

spring.cloud.refresh.never-refreshable

. Choose a different 

DataSource

 implementation if you need it to be refreshed.

Refresh scope beans are lazy proxies that initialize when they are used (that is, when a method is called), and the scope acts as a cache of initialized values. To force a bean to re-initialize on the next method call, you must invalidate its cache entry.

The 

RefreshScope

 is a bean in the context and has a public 

refreshAll()

 method to refresh all beans in the scope by clearing the target cache. The 

/refresh

 endpoint exposes this functionality (over HTTP or JMX). To refresh an individual bean by name, there is also a 

refresh(String)

 method.

To expose the 

/refresh

 endpoint, you need to add following configuration to your application:

management:
  endpoints:
    web:
      exposure:
        include: refresh
           

@RefreshScope

 works (technically) on a 

@Configuration

 class, but it might lead to surprising behavior. For example, it does not mean that all the 

@Beans

 defined in that class are themselves in 

@RefreshScope

. Specifically, anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in 

@RefreshScope

. In that case, it is rebuilt on a refresh and its dependencies are re-injected. At that point, they are re-initialized from the refreshed 

@Configuration

).

1.10. Encryption and Decryption

Spring Cloud has an 

Environment

 pre-processor for decrypting property values locally. It follows the same rules as the Spring Cloud Config Server and has the same external configuration through 

encrypt.*

. Thus, you can use encrypted values in the form of 

{cipher}*

, and, as long as there is a valid key, they are decrypted before the main application context gets the 

Environment

 settings. To use the encryption features in an application, you need to include Spring Security RSA in your classpath (Maven co-ordinates: 

org.springframework.security:spring-security-rsa

), and you also need the full strength JCE extensions in your JVM.

If you get an exception due to "Illegal key size" and you use Sun’s JDK, you need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files. See the following links for more information:

  • Java 6 JCE
  • Java 7 JCE
  • Java 8 JCE

Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.

1.11. Endpoints

For a Spring Boot Actuator application, some additional management endpoints are available. You can use:

  • POST

     to 

    /actuator/env

     to update the 

    Environment

     and rebind 

    @ConfigurationProperties

     and log levels.
  • /actuator/refresh

     to re-load the boot strap context and refresh the 

    @RefreshScope

     beans.
  • /actuator/restart

     to close the 

    ApplicationContext

     and restart it (disabled by default).
  • /actuator/pause

     and 

    /actuator/resume

     for calling the 

    Lifecycle

     methods (

    stop()

     and 

    start()

     on the 

    ApplicationContext

    ).
If you disable the 

/actuator/restart

 endpoint then the 

/actuator/pause

 and 

/actuator/resume

 endpoints will also be disabled since they are just a special case of 

/actuator/restart

.

2. Spring Cloud Commons: Common Abstractions

Patterns such as service discovery, load balancing, and circuit breakers lend themselves to a common abstraction layer that can be consumed by all Spring Cloud clients, independent of the implementation (for example, discovery with Eureka or Consul).

2.1. The 

@EnableDiscoveryClient

 Annotation

Spring Cloud Commons provides the 

@EnableDiscoveryClient

 annotation. This looks for implementations of the 

DiscoveryClient

 and 

ReactiveDiscoveryClient

 interfaces with 

META-INF/spring.factories

. Implementations of the discovery client add a configuration class to 

spring.factories

 under the 

org.springframework.cloud.client.discovery.EnableDiscoveryClient

 key. Examples of 

DiscoveryClient

 implementations include Spring Cloud Netflix Eureka, Spring Cloud Consul Discovery, and Spring Cloud Zookeeper Discovery.

Spring Cloud will provide both the blocking and reactive service discovery clients by default. You can disable the blocking and/or reactive clients easily by setting 

spring.cloud.discovery.blocking.enabled=false

 or 

spring.cloud.discovery.reactive.enabled=false

. To completely disable service discovery you just need to set 

spring.cloud.discovery.enabled=false

.

By default, implementations of 

DiscoveryClient

 auto-register the local Spring Boot server with the remote discovery server. This behavior can be disabled by setting 

autoRegister=false

 in 

@EnableDiscoveryClient

.

@EnableDiscoveryClient

 is no longer required. You can put a 

DiscoveryClient

 implementation on the classpath to cause the Spring Boot application to register with the service discovery server.

2.1.1. Health Indicator

Commons creates a Spring Boot 

HealthIndicator

 that 

DiscoveryClient

 implementations can participate in by implementing 

DiscoveryHealthIndicator

. To disable the composite 

HealthIndicator

, set 

spring.cloud.discovery.client.composite-indicator.enabled=false

. A generic 

HealthIndicator

 based on 

DiscoveryClient

 is auto-configured (

DiscoveryClientHealthIndicator

). To disable it, set 

spring.cloud.discovery.client.health-indicator.enabled=false

. To disable the description field of the 

DiscoveryClientHealthIndicator

, set 

spring.cloud.discovery.client.health-indicator.include-description=false

. Otherwise, it can bubble up as the 

description

 of the rolled up 

HealthIndicator

.

2.1.2. Ordering 

DiscoveryClient

 instances

DiscoveryClient

 interface extends 

Ordered

. This is useful when using multiple discovery clients, as it allows you to define the order of the returned discovery clients, similar to how you can order the beans loaded by a Spring application. By default, the order of any 

DiscoveryClient

 is set to 

. If you want to set a different order for your custom 

DiscoveryClient

 implementations, you just need to override the 

getOrder()

 method so that it returns the value that is suitable for your setup. Apart from this, you can use properties to set the order of the 

DiscoveryClient

 implementations provided by Spring Cloud, among others 

ConsulDiscoveryClient

EurekaDiscoveryClient

 and 

ZookeeperDiscoveryClient

. In order to do it, you just need to set the 

spring.cloud.{clientIdentifier}.discovery.order

 (or 

eureka.client.order

 for Eureka) property to the desired value.

2.1.3. SimpleDiscoveryClient

If there is no Service-Registry-backed 

DiscoveryClient

 in the classpath, 

SimpleDiscoveryClient

 instance, that uses properties to get information on service and instances, will be used.

The information about the available instances should be passed to via properties in the following format: 

spring.cloud.discovery.client.simple.instances.service1[0].uri=http://s11:8080

, where 

spring.cloud.discovery.client.simple.instances

 is the common prefix, then 

service1

 stands for the ID of the service in question, while 

[0]

 indicates the index number of the instance (as visible in the example, indexes start with 

), and then the value of 

uri

 is the actual URI under which the instance is available.

2.2. ServiceRegistry

Commons now provides a 

ServiceRegistry

 interface that provides methods such as 

register(Registration)

 and 

deregister(Registration)

, which let you provide custom registered services. 

Registration

 is a marker interface.

The following example shows the 

ServiceRegistry

 in use:

@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
    private ServiceRegistry registry;

    public MyConfiguration(ServiceRegistry registry) {
        this.registry = registry;
    }

    // called through some external process, such as an event or a custom actuator endpoint
    public void register() {
        Registration registration = constructRegistration();
        this.registry.register(registration);
    }
}
           

Each 

ServiceRegistry

 implementation has its own 

Registry

 implementation.

  • ZookeeperRegistration

     used with 

    ZookeeperServiceRegistry

  • EurekaRegistration

     used with 

    EurekaServiceRegistry

  • ConsulRegistration

     used with 

    ConsulServiceRegistry

If you are using the 

ServiceRegistry

 interface, you are going to need to pass the correct 

Registry

 implementation for the 

ServiceRegistry

 implementation you are using.

2.2.1. ServiceRegistry Auto-Registration

By default, the 

ServiceRegistry

 implementation auto-registers the running service. To disable that behavior, you can set: * 

@EnableDiscoveryClient(autoRegister=false)

 to permanently disable auto-registration. * 

spring.cloud.service-registry.auto-registration.enabled=false

 to disable the behavior through configuration.

ServiceRegistry Auto-Registration Events

There are two events that will be fired when a service auto-registers. The first event, called 

InstancePreRegisteredEvent

, is fired before the service is registered. The second event, called 

InstanceRegisteredEvent

, is fired after the service is registered. You can register an 

ApplicationListener

(s) to listen to and react to these events.

These events will not be fired if the 

spring.cloud.service-registry.auto-registration.enabled

 property is set to 

false

.

2.2.2. Service Registry Actuator Endpoint

Spring Cloud Commons provides a 

/service-registry

 actuator endpoint. This endpoint relies on a 

Registration

 bean in the Spring Application Context. Calling 

/service-registry

 with GET returns the status of the 

Registration

. Using POST to the same endpoint with a JSON body changes the status of the current 

Registration

 to the new value. The JSON body has to include the 

status

 field with the preferred value. Please see the documentation of the 

ServiceRegistry

 implementation you use for the allowed values when updating the status and the values returned for the status. For instance, Eureka’s supported statuses are 

UP

DOWN

OUT_OF_SERVICE

, and 

UNKNOWN

.

2.3. Spring RestTemplate as a Load Balancer Client

You can configure a 

RestTemplate

 to use a Load-balancer client. To create a load-balanced 

RestTemplate

, create a 

RestTemplate

@Bean

 and use the 

@LoadBalanced

 qualifier, as the following example shows:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    public String doOtherStuff() {
        String results = restTemplate.getForObject("http://stores/stores", String.class);
        return results;
    }
}
           

RestTemplate

 bean is no longer created through auto-configuration. Individual applications must create it.

The URI needs to use a virtual host name (that is, a service name, not a host name). The BlockingLoadBalancerClient is used to create a full physical address.

To use a load-balanced 

RestTemplate

, you need to have a load-balancer implementation in your classpath. Add Spring Cloud LoadBalancer starter to your project in order to use it.

2.4. Spring WebClient as a Load Balancer Client

You can configure 

WebClient

 to automatically use a load-balancer client. To create a load-balanced 

WebClient

, create a 

WebClient.Builder

@Bean

 and use the 

@LoadBalanced

 qualifier, as follows:

@Configuration
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    public Mono<String> doOtherStuff() {
        return webClientBuilder.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }
}
           

The URI needs to use a virtual host name (that is, a service name, not a host name). The Spring Cloud LoadBalancer is used to create a full physical address.

If you want to use a 

@LoadBalanced WebClient.Builder

, you need to have a load balancer implementation in the classpath. We recommend that you add the Spring Cloud LoadBalancer starter to your project. Then, 

ReactiveLoadBalancer

 is used underneath.

2.4.1. Retrying Failed Requests

A load-balanced 

RestTemplate

 can be configured to retry failed requests. By default, this logic is disabled. You can enable it by adding Spring Retry to your application’s classpath. If you would like to disable the retry logic with Spring Retry on the classpath, you can set 

spring.cloud.loadbalancer.retry.enabled=false

.

If you would like to implement a 

BackOffPolicy

 in your retries, you need to create a bean of type 

LoadBalancedRetryFactory

 and override the 

createBackOffPolicy

 method:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}
           

If you want to add one or more 

RetryListener

 implementations to your retry functionality, you need to create a bean of type 

LoadBalancedRetryListenerFactory

 and return the 

RetryListener

 array you would like to use for a given service, as the following example shows:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryListenerFactory retryListenerFactory() {
        return new LoadBalancedRetryListenerFactory() {
            @Override
            public RetryListener[] createRetryListeners(String service) {
                return new RetryListener[]{new RetryListener() {
                    @Override
                    public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                        //TODO Do you business...
                        return true;
                    }

                    @Override
                     public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }

                    @Override
                    public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
                        //TODO Do you business...
                    }
                }};
            }
        };
    }
}
           

2.5. Multiple 

RestTemplate

 Objects

If you want a 

RestTemplate

 that is not load-balanced, create a 

RestTemplate

 bean and inject it. To access the load-balanced 

RestTemplate

, use the 

@LoadBalanced

 qualifier when you create your 

@Bean

, as the following example shows:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
@Autowired
private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("http://example.com", String.class);
    }
}
           
Notice the use of the 

@Primary

 annotation on the plain 

RestTemplate

 declaration in the preceding example to disambiguate the unqualified 

@Autowired

 injection.
If you see errors such as 

java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89

, try injecting 

RestOperations

 or setting 

spring.aop.proxyTargetClass=true

.

2.6. Multiple WebClient Objects

If you want a 

WebClient

 that is not load-balanced, create a 

WebClient

 bean and inject it. To access the load-balanced 

WebClient

, use the 

@LoadBalanced

 qualifier when you create your 

@Bean

, as the following example shows:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    WebClient.Builder loadBalanced() {
        return WebClient.builder();
    }

    @Primary
    @Bean
    WebClient.Builder webClient() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    @Autowired
    @LoadBalanced
    private WebClient.Builder loadBalanced;

    public Mono<String> doOtherStuff() {
        return loadBalanced.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }

    public Mono<String> doStuff() {
        return webClientBuilder.build().get().uri("http://example.com")
                        .retrieve().bodyToMono(String.class);
    }
}
           

2.7. Spring WebFlux 

WebClient

 as a Load Balancer Client

The Spring WebFlux can work with both reactive and non-reactive 

WebClient

 configurations, as the topics describe:

  • Spring WebFlux 

    WebClient

     with 

    ReactorLoadBalancerExchangeFilterFunction

  • [load-balancer-exchange-filter-functionload-balancer-exchange-filter-function]

2.7.1. Spring WebFlux 

WebClient

 with 

ReactorLoadBalancerExchangeFilterFunction

You can configure 

WebClient

 to use the 

ReactiveLoadBalancer

. If you add Spring Cloud LoadBalancer starter to your project and if 

spring-webflux

 is on the classpath, 

ReactorLoadBalancerExchangeFilterFunction

 is auto-configured. The following example shows how to configure a 

WebClient

 to use reactive load-balancer:

public class MyClass {
    @Autowired
    private ReactorLoadBalancerExchangeFilterFunction lbFunction;

    public Mono<String> doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}
           

The URI needs to use a virtual host name (that is, a service name, not a host name). The 

ReactorLoadBalancer

 is used to create a full physical address.

2.7.2. Spring WebFlux 

WebClient

 with a Non-reactive Load Balancer Client

If 

spring-webflux

 is on the classpath, 

LoadBalancerExchangeFilterFunction

 is auto-configured. Note, however, that this uses a non-reactive client under the hood. The following example shows how to configure a 

WebClient

 to use load-balancer:

public class MyClass {
    @Autowired
    private LoadBalancerExchangeFilterFunction lbFunction;

    public Mono<String> doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}
           

The URI needs to use a virtual host name (that is, a service name, not a host name). The 

LoadBalancerClient

 is used to create a full physical address.

WARN: This approach is now deprecated. We suggest that you use WebFlux with reactive Load-Balancer instead.

2.8. Ignore Network Interfaces

Sometimes, it is useful to ignore certain named network interfaces so that they can be excluded from Service Discovery registration (for example, when running in a Docker container). A list of regular expressions can be set to cause the desired network interfaces to be ignored. The following configuration ignores the 

docker0

 interface and all interfaces that start with 

veth

:

Example 2. application.yml

spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*
           

You can also force the use of only specified network addresses by using a list of regular expressions, as the following example shows:

Example 3. bootstrap.yml

spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0
           

You can also force the use of only site-local addresses, as the following example shows:

Example 4. application.yml

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true
           

See Inet4Address.html.isSiteLocalAddress() for more details about what constitutes a site-local address.

2.9. HTTP Client Factories

Spring Cloud Commons provides beans for creating both Apache HTTP clients (

ApacheHttpClientFactory

) and OK HTTP clients (

OkHttpClientFactory

). The 

OkHttpClientFactory

 bean is created only if the OK HTTP jar is on the classpath. In addition, Spring Cloud Commons provides beans for creating the connection managers used by both clients: 

ApacheHttpClientConnectionManagerFactory

 for the Apache HTTP client and 

OkHttpClientConnectionPoolFactory

 for the OK HTTP client. If you would like to customize how the HTTP clients are created in downstream projects, you can provide your own implementation of these beans. In addition, if you provide a bean of type 

HttpClientBuilder

 or 

OkHttpClient.Builder

, the default factories use these builders as the basis for the builders returned to downstream projects. You can also disable the creation of these beans by setting 

spring.cloud.httpclientfactories.apache.enabled

 or 

spring.cloud.httpclientfactories.ok.enabled

 to 

false

.

2.10. Enabled Features

Spring Cloud Commons provides a 

/features

 actuator endpoint. This endpoint returns features available on the classpath and whether they are enabled. The information returned includes the feature type, name, version, and vendor.

2.10.1. Feature types

There are two types of 'features': abstract and named.

Abstract features are features where an interface or abstract class is defined and that an implementation the creates, such as 

DiscoveryClient

LoadBalancerClient

, or 

LockService

. The abstract class or interface is used to find a bean of that type in the context. The version displayed is 

bean.getClass().getPackage().getImplementationVersion()

.

Named features are features that do not have a particular class they implement. These features include “Circuit Breaker”, “API Gateway”, “Spring Cloud Bus”, and others. These features require a name and a bean type.

2.10.2. Declaring features

Any module can declare any number of 

HasFeature

 beans, as the following examples show:

@Bean
public HasFeatures commonsFeatures() {
  return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
}

@Bean
public HasFeatures consulFeatures() {
  return HasFeatures.namedFeatures(
    new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
    new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
}

@Bean
HasFeatures localFeatures() {
  return HasFeatures.builder()
      .abstractFeature(Something.class)
      .namedFeature(new NamedFeature("Some Other Feature", Someother.class))
      .abstractFeature(Somethingelse.class)
      .build();
}
           

Each of these beans should go in an appropriately guarded 

@Configuration

.

2.11. Spring Cloud Compatibility Verification

Due to the fact that some users have problem with setting up Spring Cloud application, we’ve decided to add a compatibility verification mechanism. It will break if your current setup is not compatible with Spring Cloud requirements, together with a report, showing what exactly went wrong.

At the moment we verify which version of Spring Boot is added to your classpath.

Example of a report

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train


Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.
           

In order to disable this feature, set 

spring.cloud.compatibility-verifier.enabled

 to 

false

. If you want to override the compatible Spring Boot versions, just set the 

spring.cloud.compatibility-verifier.compatible-boot-versions

 property with a comma separated list of compatible Spring Boot versions.

3. Spring Cloud LoadBalancer

Spring Cloud provides its own client-side load-balancer abstraction and implementation. For the load-balancing mechanism, 

ReactiveLoadBalancer

 interface has been added and a Round-Robin-based implementation has been provided for it. In order to get instances to select from reactive 

ServiceInstanceListSupplier

 is used. Currently we support a service-discovery-based implementation of 

ServiceInstanceListSupplier

 that retrieves available instances from Service Discovery using a Discovery Client available in the classpath.

3.1. Spring Cloud LoadBalancer integrations

In order to make it easy to use Spring Cloud LoadBalancer, we provide 

ReactorLoadBalancerExchangeFilterFunction

 that can be used with 

WebClient

 and 

BlockingLoadBalancerClient

 that works with 

RestTemplate

. You can see more information and examples of usage in the following sections:

  • Spring RestTemplate as a Load Balancer Client
  • Spring WebClient as a Load Balancer Client
  • Spring WebFlux WebClient with 

    ReactorLoadBalancerExchangeFilterFunction

3.2. Spring Cloud LoadBalancer Caching

Apart from the basic 

ServiceInstanceListSupplier

 implementation that retrieves instances via 

DiscoveryClient

 each time it has to choose an instance, we provide two caching implementations.

3.2.1. Caffeine-backed LoadBalancer Cache Implementation

If you have 

com.github.ben-manes.caffeine:caffeine

 in the classpath, Caffeine-based implementation will be used. See the LoadBalancerCacheConfiguration section for information on how to configure it.

If you are using Caffeine, you can also override the default Caffeine Cache setup for the LoadBalancer by passing your own Caffeine Specification in the 

spring.cloud.loadbalancer.cache.caffeine.spec

 property.

WARN: Passing your own Caffeine specification will override any other LoadBalancerCache settings, including General LoadBalancer Cache Configuration fields, such as 

ttl

 and 

capacity

.

3.2.2. Default LoadBalancer Cache Implementation

If you do not have Caffeine in the classpath, the 

DefaultLoadBalancerCache

, which comes automatically with 

spring-cloud-starter-loadbalancer

, will be used. See the LoadBalancerCacheConfiguration section for information on how to configure it.

To use Caffeine instead of the default cache, add the 

com.github.ben-manes.caffeine:caffeine

 dependency to classpath.

3.2.3. LoadBalancer Cache Configuration

You can set your own 

ttl

 value (the time after write after which entries should be expired), expressed as 

Duration

, by passing a 

String

 compliant with the Spring Boot 

String

 to 

Duration

 converter syntax. as the value of the 

spring.cloud.loadbalancer.cache.ttl

 property. You can also set your own LoadBalancer cache initial capacity by setting the value of the 

spring.cloud.loadbalancer.cache.capacity

 property.

The default setup includes 

ttl

 set to 35 seconds and the default 

initialCapacity

 is 

256

.

You can also altogether disable loadBalancer caching by setting the value of 

spring.cloud.loadbalancer.cache.enabled

 to 

false

.

Although the basic, non-cached, implementation is useful for prototyping and testing, it’s much less efficient than the cached versions, so we recommend always using the cached version in production.

3.3. Zone-Based Load-Balancing

To enable zone-based load-balancing, we provide the 

ZonePreferenceServiceInstanceListSupplier

. We use 

DiscoveryClient

-specific 

zone

 configuration (for example, 

eureka.instance.metadata-map.zone

) to pick the zone that the client tries to filter available service instances for.

You can also override 

DiscoveryClient

-specific zone setup by setting the value of 

spring.cloud.loadbalancer.zone

 property.
For the time being, only Eureka Discovery Client is instrumented to set the LoadBalancer zone. For other discovery client, set the 

spring.cloud.loadbalancer.zone

 property. More instrumentations coming shortly.
To determine the zone of a retrieved 

ServiceInstance

, we check the value under the 

"zone"

 key in its metadata map.

The 

ZonePreferenceServiceInstanceListSupplier

 filters retrieved instances and only returns the ones within the same zone. If the zone is 

null

 or there are no instances within the same zone, it returns all the retrieved instances.

In order to use the zone-based load-balancing approach, you will have to instantiate a 

ZonePreferenceServiceInstanceListSupplier

 bean in a custom configuration.

We use delegates to work with 

ServiceInstanceListSupplier

 beans. We suggest passing a 

DiscoveryClientServiceInstanceListSupplier

 delegate in the constructor of 

ZonePreferenceServiceInstanceListSupplier

 and, in turn, wrapping the latter with a 

CachingServiceInstanceListSupplier

 to leverage LoadBalancer caching mechanism.

You could use this sample configuration to set it up:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSuppliers.builder()
                    .withDiscoveryClient()
                    .withZonePreference()
                    .withCaching()
                    .build(context);
    }
}
           

3.4. Instance Health-Check for LoadBalancer

It is possible to enable a scheduled HealthCheck for the LoadBalancer. The 

HealthCheckServiceInstanceListSupplier

 is provided for that. It regularly verifies if the instances provided by a delegate 

ServiceInstanceListSupplier

 are still alive and only returns the healthy instances, unless there are none - then it returns all the retrieved instances.

This mechanism is particularly helpful while using the 

SimpleDiscoveryClient

. For the clients backed by an actual Service Registry, it’s not necessary to use, as we already get healthy instances after querying the external ServiceDiscovery.

TIP

This supplier is also recommended for setups with a small number of instances per service in order to avoid retrying calls on a failing instance.

HealthCheckServiceInstanceListSupplier

 uses properties prefixed with 

spring.cloud.loadbalancer.health-check

. You can set the 

initialDelay

 and 

interval

 for the scheduler. You can set the default path for the healthcheck URL by setting the value of the 

spring.cloud.loadbalancer.health-check.path.default

. You can also set a specific value for any given service by setting the value of the 

spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]

, substituting the 

[SERVICE_ID]

 with the correct ID of your service. If the path is not set, 

/actuator/health

 is used by default.

TIP

If you rely on the default path (

/actuator/health

), make sure you add 

spring-boot-starter-actuator

 to your collaborator’s dependencies, unless you are planning to add such an endpoint on your own.

In order to use the health-check scheduler approach, you will have to instantiate a 

HealthCheckServiceInstanceListSupplier

 bean in a custom configuration.

We use delegates to work with 

ServiceInstanceListSupplier

 beans. We suggest passing a 

DiscoveryClientServiceInstanceListSupplier

 delegate in the constructor of 

HealthCheckServiceInstanceListSupplier

.

You could use this sample configuration to set it up:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withHealthChecks()
                    .build(context);
        }
    }
           

NOTE

HealthCheckServiceInstanceListSupplier

 has its own caching mechanism based on Reactor Flux 

replay()

, therefore, if it’s being used, you may want to skip wrapping that supplier with 

CachingServiceInstanceListSupplier

.

3.5. Spring Cloud LoadBalancer Starter

We also provide a starter that allows you to easily add Spring Cloud LoadBalancer in a Spring Boot app. In order to use it, just add 

org.springframework.cloud:spring-cloud-starter-loadbalancer

 to your Spring Cloud dependencies in your build file.

Spring Cloud LoadBalancer starter includes Spring Boot Caching and Evictor.

3.6. Passing Your Own Spring Cloud LoadBalancer Configuration

You can also use the 

@LoadBalancerClient

 annotation to pass your own load-balancer client configuration, passing the name of the load-balancer client and the configuration class, as follows:

@Configuration
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}
           

TIP

In order to make working on your own LoadBalancer configuration easier, we have added a 

builder()

 method to the 

ServiceInstanceListSupplier

 class.

TIP

You can also use our alternative predefined configurations in place of the default ones by setting the value of 

spring.cloud.loadbalancer.configurations

 property to 

zone-preference

 to use 

ZonePreferenceServiceInstanceListSupplier

 with caching or to 

health-check

 to use 

HealthCheckServiceInstanceListSupplier

 with caching.

You can use this feature to instantiate different implementations of 

ServiceInstanceListSupplier

 or 

ReactorLoadBalancer

, either written by you, or provided by us as alternatives (for example 

ZonePreferenceServiceInstanceListSupplier

) to override the default setup.

You can see an example of a custom configuration here.

The annotation 

value

 arguments (

stores

 in the example above) specifies the service id of the service that we should send the requests to with the given custom configuration.

You can also pass multiple configurations (for more than one load-balancer client) through the 

@LoadBalancerClients

 annotation, as the following example shows:

@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}
           

4. Spring Cloud Circuit Breaker

4.1. Introduction

Spring Cloud Circuit breaker provides an abstraction across different circuit breaker implementations. It provides a consistent API to use in your applications, letting you, the developer, choose the circuit breaker implementation that best fits your needs for your application.

4.1.1. Supported Implementations

Spring Cloud supports the following circuit-breaker implementations:

  • Resilience4J
  • Sentinel
  • Spring Retry

4.2. Core Concepts

To create a circuit breaker in your code, you can use the 

CircuitBreakerFactory

 API. When you include a Spring Cloud Circuit Breaker starter on your classpath, a bean that implements this API is automatically created for you. The following example shows a simple example of how to use this API:

@Service
public static class DemoControllerService {
    private RestTemplate rest;
    private CircuitBreakerFactory cbFactory;

    public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
        this.rest = rest;
        this.cbFactory = cbFactory;
    }

    public String slow() {
        return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
    }

}
           

The 

CircuitBreakerFactory.create

 API creates an instance of a class called 

CircuitBreaker

. The 

run

 method takes a 

Supplier

 and a 

Function

. The 

Supplier

 is the code that you are going to wrap in a circuit breaker. The 

Function

 is the fallback that is executed if the circuit breaker is tripped. The function is passed the 

Throwable

 that caused the fallback to be triggered. You can optionally exclude the fallback if you do not want to provide one.

4.2.1. Circuit Breakers In Reactive Code

If Project Reactor is on the class path, you can also use 

ReactiveCircuitBreakerFactory

 for your reactive code. The following example shows how to do so:

@Service
public static class DemoControllerService {
    private ReactiveCircuitBreakerFactory cbFactory;
    private WebClient webClient;


    public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) {
        this.webClient = webClient;
        this.cbFactory = cbFactory;
    }

    public Mono<String> slow() {
        return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform(
        it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback")));
    }
}
           

The 

ReactiveCircuitBreakerFactory.create

 API creates an instance of a class called 

ReactiveCircuitBreaker

. The 

run

 method takes a 

Mono

 or a 

Flux

 and wraps it in a circuit breaker. You can optionally profile a fallback 

Function

, which will be called if the circuit breaker is tripped and is passed the 

Throwable

 that caused the failure.

4.3. Configuration

You can configure your circuit breakers by creating beans of type 

Customizer

. The 

Customizer

 interface has a single method (called 

customize

) that takes the 

Object

 to customize.

For detailed information on how to customize a given implementation see the following documentation:

  • Resilience4J
  • Sentinal
  • Spring Retry

Some 

CircuitBreaker

 implementations such as 

Resilience4JCircuitBreaker

 call 

customize

 method every time 

CircuitBreaker#run

 is called. It can be inefficient. In that case, you can use 

CircuitBreaker#once

 method. It is useful where calling 

customize

 many times doesn’t make sense, for example, in case of consuming Resilience4j’s events.

The following example shows the way for each 

io.github.resilience4j.circuitbreaker.CircuitBreaker

 to consume events.

Customizer.once(circuitBreaker -> {
  circuitBreaker.getEventPublisher()
    .onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition()));
}, CircuitBreaker::getName)
           

5. CachedRandomPropertySource

Spring Cloud Context provides a 

PropertySource

 that caches random values based on a key. Outside of the caching functionality it works the same as Spring Boot’s 

RandomValuePropertySource

. This random value might be useful in the case where you want a random value that is consistent even after the Spring Application context restarts. The property value takes the form of 

cachedrandom.[yourkey].[type]

 where 

yourkey

 is the key in the cache. The 

type

 value can be any type supported by Spring Boot’s 

RandomValuePropertySource

.

myrandom=${cachedrandom.appname.value}
           

6. Configuration Properties

To see the list of all Spring Cloud Commons related configuration properties please check the Appendix page.

繼續閱讀