656 lines
47 KiB
XML
656 lines
47 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<?asciidoc-toc?>
|
|
<?asciidoc-numbered?>
|
|
<book xmlns="http://docbook.org/ns/docbook" xmlns:xl="http://www.w3.org/1999/xlink" version="5.0" xml:lang="en">
|
|
<info>
|
|
<title>Cloud Native Applications</title>
|
|
<date>2019-03-05</date>
|
|
</info>
|
|
<preface>
|
|
<title></title>
|
|
<simpara><link xl:href="http://pivotal.io/platform-as-a-service/migrating-to-cloud-native-application-architectures-ebook">Cloud Native</link> is a style of application development that encourages easy adoption of best practices in the areas of continuous delivery and value-driven development.
|
|
A related discipline is that of building <link xl:href="http://12factor.net/">12-factor Applications</link>, in which development practices are aligned with delivery and operations goals — for instance, by using declarative programming and management and monitoring.
|
|
Spring Cloud facilitates these styles of development in a number of specific ways.
|
|
The starting point is a set of features to which all components in a distributed system need easy access.</simpara>
|
|
<simpara>Many of those features are covered by <link xl:href="http://projects.spring.io/spring-boot">Spring Boot</link>, 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 <literal>ApplicationContext</literal> 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).</simpara>
|
|
<simpara>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:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html">Java 6 JCE</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">Java 7 JCE</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">Java 8 JCE</link></simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.</simpara>
|
|
<note>
|
|
<simpara>Spring Cloud is released under the non-restrictive Apache 2.0 license.
|
|
If you would like to contribute to this section of the documentation or if you find an error, you can find the source code and issue trackers for the project at <link xl:href="https://github.com/spring-cloud/spring-cloud-commons/tree/master/docs/src/main/asciidoc">github</link>.</simpara>
|
|
</note>
|
|
</preface>
|
|
<chapter xml:id="_spring_cloud_context_application_context_services">
|
|
<title>Spring Cloud Context: Application Context Services</title>
|
|
<simpara>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 probably all components in a system would use or occasionally need.</simpara>
|
|
<section xml:id="_the_bootstrap_application_context">
|
|
<title>The Bootstrap Application Context</title>
|
|
<simpara>A Spring Cloud application operates by creating a <quote>bootstrap</quote> context, which is a parent context for the main application.
|
|
It 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 <literal>Environment</literal>, which is the source of external properties for any Spring application.
|
|
By default, bootstrap properties (not <literal>bootstrap.properties</literal> but properties that are loaded during the bootstrap phase) are added with high precedence, so they cannot be overridden by local configuration.</simpara>
|
|
<simpara>The bootstrap context uses a different convention for locating external configuration than the main application context.
|
|
Instead of <literal>application.yml</literal> (or <literal>.properties</literal>), you can use <literal>bootstrap.yml</literal>, keeping the external configuration for bootstrap and main context
|
|
nicely separate.
|
|
The following listing shows an example:</simpara>
|
|
<formalpara>
|
|
<title>bootstrap.yml</title>
|
|
<para>
|
|
<screen>spring:
|
|
application:
|
|
name: foo
|
|
cloud:
|
|
config:
|
|
uri: ${SPRING_CONFIG_URI:http://localhost:8888}</screen>
|
|
</para>
|
|
</formalpara>
|
|
<simpara>If your application needs any application-specific configuration from the server, it is a good idea to set the <literal>spring.application.name</literal> (in <literal>bootstrap.yml</literal> or <literal>application.yml</literal>).</simpara>
|
|
<simpara>You can disable the bootstrap process completely by setting <literal>spring.cloud.bootstrap.enabled=false</literal> (for example, in system properties).</simpara>
|
|
</section>
|
|
<section xml:id="_application_context_hierarchies">
|
|
<title>Application Context Hierarchies</title>
|
|
<simpara>If you build an application context from <literal>SpringApplication</literal> or <literal>SpringApplicationBuilder</literal>, then 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 <quote>main</quote> application context contains additional property sources, compared to building the same context without Spring Cloud Config.
|
|
The additional property sources are:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><quote>bootstrap</quote>: If any <literal>PropertySourceLocators</literal> are found in the Bootstrap context and if they have non-empty properties, an optional <literal>CompositePropertySource</literal> appears with high priority.
|
|
An example would be properties from the Spring Cloud Config Server.
|
|
See <quote><xref linkend="customizing-bootstrap-property-sources"/></quote> for instructions on how to customize the contents of this property source.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><quote>applicationConfig: [classpath:bootstrap.yml]</quote> (and related files if Spring profiles are active): If you have a <literal>bootstrap.yml</literal> (or <literal>.properties</literal>), 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 <literal>application.yml</literal> (or <literal>.properties</literal>) 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 <quote><xref linkend="customizing-bootstrap-properties"/></quote> for instructions on how to customize the contents of these property sources.</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>Because of the ordering rules of property sources, the <quote>bootstrap</quote> entries take precedence.
|
|
However, note that these do not contain any data from <literal>bootstrap.yml</literal>, which has very low precedence but can be used to set defaults.</simpara>
|
|
<simpara>You can extend the context hierarchy by setting the parent context of any <literal>ApplicationContext</literal> you create — for example, by using its own interface or with the <literal>SpringApplicationBuilder</literal> convenience methods (<literal>parent()</literal>, <literal>child()</literal> and <literal>sibling()</literal>).
|
|
The bootstrap context is the parent of the most senior ancestor that you create yourself.
|
|
Every context in the hierarchy has its own <quote>bootstrap</quote> (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 <literal>spring.application.name</literal> 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).</simpara>
|
|
<simpara>Note that the <literal>SpringApplicationBuilder</literal> lets you share an <literal>Environment</literal> 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.</simpara>
|
|
</section>
|
|
<section xml:id="customizing-bootstrap-properties">
|
|
<title>Changing the Location of Bootstrap Properties</title>
|
|
<simpara>The <literal>bootstrap.yml</literal> (or <literal>.properties</literal>) location can be specified by setting <literal>spring.cloud.bootstrap.name</literal> (default: <literal>bootstrap</literal>) or <literal>spring.cloud.bootstrap.location</literal> (default: empty) — for example, in System properties.
|
|
Those properties behave like the <literal>spring.config.*</literal> variants with the same name.
|
|
In fact, they are used to set up the bootstrap <literal>ApplicationContext</literal> by setting those properties in its <literal>Environment</literal>.
|
|
If there is an active profile (from <literal>spring.profiles.active</literal> or through the <literal>Environment</literal> 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 <literal>bootstrap-development.properties</literal> for a <literal>development</literal> profile.</simpara>
|
|
</section>
|
|
<section xml:id="overriding-bootstrap-properties">
|
|
<title>Overriding the Values of Remote Properties</title>
|
|
<simpara>The property sources that are added to your application by the bootstrap context are often <quote>remote</quote> (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 <literal>spring.cloud.config.allowOverride=true</literal> (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:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><literal>spring.cloud.config.overrideNone=true</literal>: Override from any local property source.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>spring.cloud.config.overrideSystemProperties=false</literal>: Only system properties, command line arguments, and environment variables (but not the local config files) should override the remote settings.</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
</section>
|
|
<section xml:id="_customizing_the_bootstrap_configuration">
|
|
<title>Customizing the Bootstrap Configuration</title>
|
|
<simpara>The bootstrap context can be set to do anything you like by adding entries to <literal>/META-INF/spring.factories</literal> under a key named <literal>org.springframework.cloud.bootstrap.BootstrapConfiguration</literal>.
|
|
This holds a comma-separated list of Spring <literal>@Configuration</literal> 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 <literal>@Beans</literal> of type <literal>ApplicationContextInitializer</literal>.
|
|
If you want to control the startup sequence, classes can be marked with an <literal>@Order</literal> annotation (the default order is <literal>last</literal>).</simpara>
|
|
<warning>
|
|
<simpara>When adding custom <literal>BootstrapConfiguration</literal>, be careful that the classes you add are not <literal>@ComponentScanned</literal> by mistake into your <quote>main</quote> 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 <literal>@ComponentScan</literal> or <literal>@SpringBootApplication</literal> annotated configuration classes.</simpara>
|
|
</warning>
|
|
<simpara>The bootstrap process ends by injecting initializers into the main <literal>SpringApplication</literal> instance (which is the normal Spring Boot startup sequence, whether it is running as a standalone application or deployed in an application server).
|
|
First, a bootstrap context is created from the classes found in <literal>spring.factories</literal>.
|
|
Then, all <literal>@Beans</literal> of type <literal>ApplicationContextInitializer</literal> are added to the main <literal>SpringApplication</literal> before it is started.</simpara>
|
|
</section>
|
|
<section xml:id="customizing-bootstrap-property-sources">
|
|
<title>Customizing the Bootstrap Property Sources</title>
|
|
<simpara>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 <literal>PropertySourceLocator</literal> to the bootstrap context (through <literal>spring.factories</literal>).
|
|
For instance, you can insert additional properties from a different server or from a database.</simpara>
|
|
<simpara>As an example, consider the following custom locator:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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"));
|
|
}
|
|
|
|
}</programlisting>
|
|
<simpara>The <literal>Environment</literal> that is passed in is the one for the <literal>ApplicationContext</literal> about to be created — in other words, the one for which we supply additional property sources for.
|
|
It already has its normal Spring Boot-provided property sources, so you can use those to locate a property source specific to this <literal>Environment</literal> (for example, by keying it on <literal>spring.application.name</literal>, as is done in the default Spring Cloud Config Server property source locator).</simpara>
|
|
<simpara>If you create a jar with this class in it and then add a <literal>META-INF/spring.factories</literal> containing the following, the <literal>customProperty</literal> <literal>PropertySource</literal> appears in any application that includes that jar on its classpath:</simpara>
|
|
<screen>org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator</screen>
|
|
</section>
|
|
<section xml:id="_logging_configuration">
|
|
<title>Logging Configuration</title>
|
|
<simpara>If you are going to use Spring Boot to configure log settings than
|
|
you should place this configuration in `bootstrap.[yml | properties]
|
|
if you would like it to apply to all events.</simpara>
|
|
<note>
|
|
<simpara>For Spring Cloud to initialize logging configuration properly you cannot use a custom prefix. For example,
|
|
using <literal>custom.loggin.logpath</literal> will not be recognized by Spring Cloud when initializing the logging system.</simpara>
|
|
</note>
|
|
</section>
|
|
<section xml:id="_environment_changes">
|
|
<title>Environment Changes</title>
|
|
<simpara>The application listens for an <literal>EnvironmentChangeEvent</literal> and reacts to the change in a couple of standard ways (additional <literal>ApplicationListeners</literal> can be added as <literal>@Beans</literal> by the user in the normal way).
|
|
When an <literal>EnvironmentChangeEvent</literal> is observed, it has a list of key values that have changed, and the application uses those to:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara>Re-bind any <literal>@ConfigurationProperties</literal> beans in the context</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara>Set the logger levels for any properties in <literal>logging.level.*</literal></simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>Note that the Config Client does not, by default, poll for changes in the <literal>Environment</literal>.
|
|
Generally, we would not recommend that approach for detecting changes (although you could set it up with a
|
|
<literal>@Scheduled</literal> annotation).
|
|
If you have a scaled-out client application, it is better to broadcast the <literal>EnvironmentChangeEvent</literal> to all the instances instead of having them polling for changes (for example, by using the <link xl:href="https://github.com/spring-cloud/spring-cloud-bus">Spring Cloud Bus</link>).</simpara>
|
|
<simpara>The <literal>EnvironmentChangeEvent</literal> covers a large class of refresh use cases, as long as you can actually make a change to the <literal>Environment</literal> and publish the event.
|
|
Note that those APIs are public and part of core Spring).
|
|
You can verify that the changes are bound to <literal>@ConfigurationProperties</literal> beans by visiting the <literal>/configprops</literal> endpoint (a normal Spring Boot Actuator feature).
|
|
For instance, a <literal>DataSource</literal> can have its <literal>maxPoolSize</literal> changed at runtime (the default <literal>DataSource</literal> created by Spring Boot is an <literal>@ConfigurationProperties</literal> bean) and grow capacity dynamically.
|
|
Re-binding <literal>@ConfigurationProperties</literal> 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 <literal>ApplicationContext</literal>.
|
|
To address those concerns, we have <literal>@RefreshScope</literal>.</simpara>
|
|
</section>
|
|
<section xml:id="refresh-scope">
|
|
<title>Refresh Scope</title>
|
|
<simpara>When there is a configuration change, a Spring <literal>@Bean</literal> that is marked as <literal>@RefreshScope</literal> gets special treatment.
|
|
This feature addresses the problem of stateful beans that only get their configuration injected when they are initialized.
|
|
For instance, if a <literal>DataSource</literal> has open connections when the database URL is changed via the <literal>Environment</literal>, 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.</simpara>
|
|
<simpara>Sometimes, it might even be mandatory to apply the <literal>@RefreshScope</literal>
|
|
annotation on some beans which can be only initialized once. If a bean
|
|
is "immutable", you will have to either annotate the bean with <literal>@RefreshScope</literal>
|
|
or specify the classname under the property key
|
|
<literal>spring.cloud.refresh.extra-refreshable</literal>.</simpara>
|
|
<important>
|
|
<simpara>If you create a <literal>DataSource</literal> bean yourself and the implementation is a <literal>HikariDataSource</literal>, return the
|
|
most specific type, in this case <literal>HikariDataSource</literal>. Otherwise, you will need to set
|
|
<literal>spring.cloud.refresh.extra-refreshable=javax.sql.DataSource</literal>.</simpara>
|
|
</important>
|
|
<simpara>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.</simpara>
|
|
<simpara>The <literal>RefreshScope</literal> is a bean in the context and has a public <literal>refreshAll()</literal> method to refresh all beans in the scope by clearing the target cache.
|
|
The <literal>/refresh</literal> endpoint exposes this functionality (over HTTP or JMX).
|
|
To refresh an individual bean by name, there is also a <literal>refresh(String)</literal> method.</simpara>
|
|
<simpara>To expose the <literal>/refresh</literal> endpoint, you need to add following configuration to your application:</simpara>
|
|
<programlisting language="yaml" linenumbering="unnumbered">management:
|
|
endpoints:
|
|
web:
|
|
exposure:
|
|
include: refresh</programlisting>
|
|
<note>
|
|
<simpara><literal>@RefreshScope</literal> works (technically) on an <literal>@Configuration</literal> class, but it might lead to surprising behavior.
|
|
For example, it does not mean that all the <literal>@Beans</literal> defined in that class are themselves in <literal>@RefreshScope</literal>.
|
|
Specifically, anything that depends on those beans cannot rely on them being updated when a refresh is initiated, unless it is itself in <literal>@RefreshScope</literal>.
|
|
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 <literal>@Configuration</literal>).</simpara>
|
|
</note>
|
|
</section>
|
|
<section xml:id="_encryption_and_decryption">
|
|
<title>Encryption and Decryption</title>
|
|
<simpara>Spring Cloud has an <literal>Environment</literal> pre-processor for decrypting property values locally.
|
|
It follows the same rules as the Config Server and has the same external configuration through <literal>encrypt.*</literal>.
|
|
Thus, you can use encrypted values in the form of <literal>{cipher}*</literal> and, as long as there is a valid key, they are decrypted before the main application context gets the <literal>Environment</literal> 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.</simpara>
|
|
<simpara>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:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html">Java 6 JCE</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html">Java 7 JCE</link></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><link xl:href="http://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html">Java 8 JCE</link></simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.</simpara>
|
|
</section>
|
|
<section xml:id="_endpoints">
|
|
<title>Endpoints</title>
|
|
<simpara>For a Spring Boot Actuator application, some additional management endpoints are available. You can use:</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><literal>POST</literal> to <literal>/actuator/env</literal> to update the <literal>Environment</literal> and rebind <literal>@ConfigurationProperties</literal> and log levels.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>/actuator/refresh</literal> to re-load the boot strap context and refresh the <literal>@RefreshScope</literal> beans.</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>/actuator/restart</literal> to close the <literal>ApplicationContext</literal> and restart it (disabled by default).</simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>/actuator/pause</literal> and <literal>/actuator/resume</literal> for calling the <literal>Lifecycle</literal> methods (<literal>stop()</literal> and <literal>start()</literal> on the <literal>ApplicationContext</literal>).</simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<note>
|
|
<simpara>If you disable the <literal>/actuator/restart</literal> endpoint then the <literal>/actuator/pause</literal> and <literal>/actuator/resume</literal> endpoints
|
|
will also be disabled since they are just a special case of <literal>/actuator/restart</literal>.</simpara>
|
|
</note>
|
|
</section>
|
|
</chapter>
|
|
<chapter xml:id="_spring_cloud_commons_common_abstractions">
|
|
<title>Spring Cloud Commons: Common Abstractions</title>
|
|
<simpara>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).</simpara>
|
|
<section xml:id="_enablediscoveryclient">
|
|
<title>@EnableDiscoveryClient</title>
|
|
<simpara>Spring Cloud Commons provides the <literal>@EnableDiscoveryClient</literal> annotation.
|
|
This looks for implementations of the <literal>DiscoveryClient</literal> interface with <literal>META-INF/spring.factories</literal>.
|
|
Implementations of the Discovery Client add a configuration class to <literal>spring.factories</literal> under the <literal>org.springframework.cloud.client.discovery.EnableDiscoveryClient</literal> key.
|
|
Examples of <literal>DiscoveryClient</literal> implementations include <link xl:href="http://cloud.spring.io/spring-cloud-netflix/">Spring Cloud Netflix Eureka</link>, <link xl:href="http://cloud.spring.io/spring-cloud-consul/">Spring Cloud Consul Discovery</link>, and <link xl:href="http://cloud.spring.io/spring-cloud-zookeeper/">Spring Cloud Zookeeper Discovery</link>.</simpara>
|
|
<simpara>By default, implementations of <literal>DiscoveryClient</literal> auto-register the local Spring Boot server with the remote discovery server.
|
|
This behavior can be disabled by setting <literal>autoRegister=false</literal> in <literal>@EnableDiscoveryClient</literal>.</simpara>
|
|
<note>
|
|
<simpara><literal>@EnableDiscoveryClient</literal> is no longer required.
|
|
You can put a <literal>DiscoveryClient</literal> implementation on the classpath to cause the Spring Boot application to register with the service discovery server.</simpara>
|
|
</note>
|
|
<section xml:id="_health_indicator">
|
|
<title>Health Indicator</title>
|
|
<simpara>Commons creates a Spring Boot <literal>HealthIndicator</literal> that <literal>DiscoveryClient</literal> implementations can participate in by implementing <literal>DiscoveryHealthIndicator</literal>.
|
|
To disable the composite <literal>HealthIndicator</literal>, set <literal>spring.cloud.discovery.client.composite-indicator.enabled=false</literal>.
|
|
A generic <literal>HealthIndicator</literal> based on <literal>DiscoveryClient</literal> is auto-configured (<literal>DiscoveryClientHealthIndicator</literal>).
|
|
To disable it, set <literal>spring.cloud.discovery.client.health-indicator.enabled=false</literal>.
|
|
To disable the description field of the <literal>DiscoveryClientHealthIndicator</literal>, set <literal>spring.cloud.discovery.client.health-indicator.include-description=false</literal>.
|
|
Otherwise, it can bubble up as the <literal>description</literal> of the rolled up <literal>HealthIndicator</literal>.</simpara>
|
|
</section>
|
|
<section xml:id="_ordering_discoveryclient_instances">
|
|
<title>Ordering <literal>DiscoveryClient</literal> instances</title>
|
|
<simpara><literal>DiscoveryClient</literal> interface extends <literal>Ordered</literal>. 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 <literal>DiscoveryClient</literal> is set to
|
|
<literal>0</literal>. If you want to set a different order for your custom <literal>DiscoveryClient</literal> implementations, you just need to override
|
|
the <literal>getOrder()</literal> 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 <literal>DiscoveryClient</literal>
|
|
implementations provided by Spring Cloud, among others <literal>ConsulDiscoveryClient</literal>, <literal>EurekaDiscoveryClient</literal> and
|
|
<literal>ZookeeperDiscoveryClient</literal>. In order to do it, you just need to set the
|
|
<literal>spring.cloud.{clientIdentifier}.discovery.order</literal> (or <literal>eureka.client.order</literal> for Eureka) property to the desired value.</simpara>
|
|
</section>
|
|
</section>
|
|
<section xml:id="_serviceregistry">
|
|
<title>ServiceRegistry</title>
|
|
<simpara>Commons now provides a <literal>ServiceRegistry</literal> interface that provides methods such as <literal>register(Registration)</literal> and <literal>deregister(Registration)</literal>, which let you provide custom registered services.
|
|
<literal>Registration</literal> is a marker interface.</simpara>
|
|
<simpara>The following example shows the <literal>ServiceRegistry</literal> in use:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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);
|
|
}
|
|
}</programlisting>
|
|
<simpara>Each <literal>ServiceRegistry</literal> implementation has its own <literal>Registry</literal> implementation.</simpara>
|
|
<itemizedlist>
|
|
<listitem>
|
|
<simpara><literal>ZookeeperRegistration</literal> used with <literal>ZookeeperServiceRegistry</literal></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>EurekaRegistration</literal> used with <literal>EurekaServiceRegistry</literal></simpara>
|
|
</listitem>
|
|
<listitem>
|
|
<simpara><literal>ConsulRegistration</literal> used with <literal>ConsulServiceRegistry</literal></simpara>
|
|
</listitem>
|
|
</itemizedlist>
|
|
<simpara>If you are using the <literal>ServiceRegistry</literal> interface, you are going to need to pass the
|
|
correct <literal>Registry</literal> implementation for the <literal>ServiceRegistry</literal> implementation you
|
|
are using.</simpara>
|
|
<section xml:id="_serviceregistry_auto_registration">
|
|
<title>ServiceRegistry Auto-Registration</title>
|
|
<simpara>By default, the <literal>ServiceRegistry</literal> implementation auto-registers the running service.
|
|
To disable that behavior, you can set:
|
|
* <literal>@EnableDiscoveryClient(autoRegister=false)</literal> to permanently disable auto-registration.
|
|
* <literal>spring.cloud.service-registry.auto-registration.enabled=false</literal> to disable the behavior through configuration.</simpara>
|
|
<section xml:id="_serviceregistry_auto_registration_events">
|
|
<title>ServiceRegistry Auto-Registration Events</title>
|
|
<simpara>There are two events that will be fired when a service auto-registers. The first event, called
|
|
<literal>InstancePreRegisteredEvent</literal>, is fired before the service is registered. The second
|
|
event, called <literal>InstanceRegisteredEvent</literal>, is fired after the service is registered. You can register an
|
|
<literal>ApplicationListener</literal>(s) to listen to and react to these events.</simpara>
|
|
<note>
|
|
<simpara>These events will not be fired if <literal>spring.cloud.service-registry.auto-registration.enabled</literal> is set to <literal>false</literal>.</simpara>
|
|
</note>
|
|
</section>
|
|
</section>
|
|
<section xml:id="_service_registry_actuator_endpoint">
|
|
<title>Service Registry Actuator Endpoint</title>
|
|
<simpara>Spring Cloud Commons provides a <literal>/service-registry</literal> actuator endpoint.
|
|
This endpoint relies on a <literal>Registration</literal> bean in the Spring Application Context.
|
|
Calling <literal>/service-registry</literal> with GET returns the status of the <literal>Registration</literal>.
|
|
Using POST to the same endpoint with a JSON body changes the status of the current <literal>Registration</literal> to the new value.
|
|
The JSON body has to include the <literal>status</literal> field with the preferred value.
|
|
Please see the documentation of the <literal>ServiceRegistry</literal> 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 <literal>UP</literal>, <literal>DOWN</literal>, <literal>OUT_OF_SERVICE</literal>, and <literal>UNKNOWN</literal>.</simpara>
|
|
</section>
|
|
</section>
|
|
<section xml:id="_spring_resttemplate_as_a_load_balancer_client">
|
|
<title>Spring RestTemplate as a Load Balancer Client</title>
|
|
<simpara><literal>RestTemplate</literal> can be automatically configured to use ribbon.
|
|
To create a load-balanced <literal>RestTemplate</literal>, create a <literal>RestTemplate</literal> <literal>@Bean</literal> and use the <literal>@LoadBalanced</literal> qualifier, as shown in the following example:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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;
|
|
}
|
|
}</programlisting>
|
|
<caution>
|
|
<simpara>A <literal>RestTemplate</literal> bean is no longer created through auto-configuration.
|
|
Individual applications must create it.</simpara>
|
|
</caution>
|
|
<simpara>The URI needs to use a virtual host name (that is, a service name, not a host name).
|
|
The Ribbon client is used to create a full physical address.
|
|
See <link xl:href="https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-core/src/main/java/org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java">RibbonAutoConfiguration</link> for details of how the <literal>RestTemplate</literal> is set up.</simpara>
|
|
</section>
|
|
<section xml:id="_spring_webclient_as_a_load_balancer_client">
|
|
<title>Spring WebClient as a Load Balancer Client</title>
|
|
<simpara><literal>WebClient</literal> can be automatically configured to use the <literal>LoadBalancerClient</literal>.
|
|
To create a load-balanced <literal>WebClient</literal>, create a <literal>WebClient.Builder</literal> <literal>@Bean</literal> and use the <literal>@LoadBalanced</literal> qualifier, as shown in the following example:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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);
|
|
}
|
|
}</programlisting>
|
|
<simpara>The URI needs to use a virtual host name (that is, a service name, not a host name).
|
|
The Ribbon client is used to create a full physical address.</simpara>
|
|
<section xml:id="_retrying_failed_requests">
|
|
<title>Retrying Failed Requests</title>
|
|
<simpara>A load-balanced <literal>RestTemplate</literal> can be configured to retry failed requests.
|
|
By default, this logic is disabled.
|
|
You can enable it by adding <link xl:href="https://github.com/spring-projects/spring-retry">Spring Retry</link> to your application’s classpath.
|
|
The load-balanced <literal>RestTemplate</literal> honors some of the Ribbon configuration values related to retrying failed requests.
|
|
You can use <literal>client.ribbon.MaxAutoRetries</literal>, <literal>client.ribbon.MaxAutoRetriesNextServer</literal>, and <literal>client.ribbon.OkToRetryOnAllOperations</literal> properties.
|
|
If you would like to disable the retry logic with Spring Retry on the classpath, you can set <literal>spring.cloud.loadbalancer.retry.enabled=false</literal>.
|
|
See the <link xl:href="https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-file-sample-clientproperties">Ribbon documentation</link> for a description of what these properties do.</simpara>
|
|
<simpara>If you would like to implement a <literal>BackOffPolicy</literal> in your retries, you need to create a bean of type <literal>LoadBalancedRetryFactory</literal> and override the <literal>createBackOffPolicy</literal> method:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@Configuration
|
|
public class MyConfiguration {
|
|
@Bean
|
|
LoadBalancedRetryFactory retryFactory() {
|
|
return new LoadBalancedRetryFactory() {
|
|
@Override
|
|
public BackOffPolicy createBackOffPolicy(String service) {
|
|
return new ExponentialBackOffPolicy();
|
|
}
|
|
};
|
|
}
|
|
}</programlisting>
|
|
<note>
|
|
<simpara><literal>client</literal> in the preceding examples should be replaced with your Ribbon client’s name.</simpara>
|
|
</note>
|
|
<simpara>If you want to add one or more <literal>RetryListener</literal> implementations to your retry functionality, you need to
|
|
create a bean of type <literal>LoadBalancedRetryListenerFactory</literal> and return the <literal>RetryListener</literal> array
|
|
you would like to use for a given service, as shown in the following example:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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...
|
|
}
|
|
}};
|
|
}
|
|
};
|
|
}
|
|
}</programlisting>
|
|
</section>
|
|
</section>
|
|
<section xml:id="_multiple_resttemplate_objects">
|
|
<title>Multiple RestTemplate objects</title>
|
|
<simpara>If you want a <literal>RestTemplate</literal> that is not load-balanced, create a <literal>RestTemplate</literal> bean and inject it.
|
|
To access the load-balanced <literal>RestTemplate</literal>, use the <literal>@LoadBalanced</literal> qualifier when you create your <literal>@Bean</literal>, as shown in the following example:\</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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);
|
|
}
|
|
}</programlisting>
|
|
<important>
|
|
<simpara>Notice the use of the <literal>@Primary</literal> annotation on the plain <literal>RestTemplate</literal> declaration in the preceding example to disambiguate the unqualified <literal>@Autowired</literal> injection.</simpara>
|
|
</important>
|
|
<tip>
|
|
<simpara>If you see errors such as <literal>java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89</literal>, try injecting <literal>RestOperations</literal> or setting <literal>spring.aop.proxyTargetClass=true</literal>.</simpara>
|
|
</tip>
|
|
</section>
|
|
<section xml:id="loadbalanced-webclient">
|
|
<title>Spring WebFlux WebClient as a Load Balancer Client</title>
|
|
<simpara><literal>WebClient</literal> can be configured to use the <literal>LoadBalancerClient</literal>. <literal>LoadBalancerExchangeFilterFunction</literal> is auto-configured if <literal>spring-webflux</literal> is on the classpath. The following example shows how to configure a <literal>WebClient</literal> to use load balancer:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">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);
|
|
}
|
|
}</programlisting>
|
|
<simpara>The URI needs to use a virtual host name (that is, a service name, not a host name).
|
|
The <literal>LoadBalancerClient</literal> is used to create a full physical address.</simpara>
|
|
</section>
|
|
<section xml:id="ignore-network-interfaces">
|
|
<title>Ignore Network Interfaces</title>
|
|
<simpara>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 <literal>docker0</literal> interface and all interfaces that start with <literal>veth</literal>:</simpara>
|
|
<formalpara>
|
|
<title>application.yml</title>
|
|
<para>
|
|
<screen>spring:
|
|
cloud:
|
|
inetutils:
|
|
ignoredInterfaces:
|
|
- docker0
|
|
- veth.*</screen>
|
|
</para>
|
|
</formalpara>
|
|
<simpara>You can also force the use of only specified network addresses by using a list of regular expressions, as shown in the following example:</simpara>
|
|
<formalpara>
|
|
<title>bootstrap.yml</title>
|
|
<para>
|
|
<screen>spring:
|
|
cloud:
|
|
inetutils:
|
|
preferredNetworks:
|
|
- 192.168
|
|
- 10.0</screen>
|
|
</para>
|
|
</formalpara>
|
|
<simpara>You can also force the use of only site-local addresses, as shown in the following example:
|
|
.application.yml</simpara>
|
|
<screen>spring:
|
|
cloud:
|
|
inetutils:
|
|
useOnlySiteLocalInterfaces: true</screen>
|
|
<simpara>See <link xl:href="https://docs.oracle.com/javase/8/docs/api/java/net/Inet4Address.html#isSiteLocalAddress--">Inet4Address.html.isSiteLocalAddress()</link> for more details about what constitutes a site-local address.</simpara>
|
|
</section>
|
|
<section xml:id="http-clients">
|
|
<title>HTTP Client Factories</title>
|
|
<simpara>Spring Cloud Commons provides beans for creating both Apache HTTP clients (<literal>ApacheHttpClientFactory</literal>) and OK HTTP clients (<literal>OkHttpClientFactory</literal>).
|
|
The <literal>OkHttpClientFactory</literal> 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: <literal>ApacheHttpClientConnectionManagerFactory</literal> for the Apache HTTP client and <literal>OkHttpClientConnectionPoolFactory</literal> 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 <literal>HttpClientBuilder</literal> or <literal>OkHttpClient.Builder</literal>, 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 <literal>spring.cloud.httpclientfactories.apache.enabled</literal> or <literal>spring.cloud.httpclientfactories.ok.enabled</literal> to <literal>false</literal>.</simpara>
|
|
</section>
|
|
<section xml:id="enabled-features">
|
|
<title>Enabled Features</title>
|
|
<simpara>Spring Cloud Commons provides a <literal>/features</literal> 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.</simpara>
|
|
<section xml:id="_feature_types">
|
|
<title>Feature types</title>
|
|
<simpara>There are two types of 'features': abstract and named.</simpara>
|
|
<simpara>Abstract features are features where an interface or abstract class is defined and that an implementation the creates, such as <literal>DiscoveryClient</literal>, <literal>LoadBalancerClient</literal>, or <literal>LockService</literal>.
|
|
The abstract class or interface is used to find a bean of that type in the context.
|
|
The version displayed is <literal>bean.getClass().getPackage().getImplementationVersion()</literal>.</simpara>
|
|
<simpara>Named features are features that do not have a particular class they implement, such as "Circuit Breaker", "API Gateway", "Spring Cloud Bus", and others. These features require a name and a bean type.</simpara>
|
|
</section>
|
|
<section xml:id="_declaring_features">
|
|
<title>Declaring features</title>
|
|
<simpara>Any module can declare any number of <literal>HasFeature</literal> beans, as shown in the following examples:</simpara>
|
|
<programlisting language="java" linenumbering="unnumbered">@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(Foo.class)
|
|
.namedFeature(new NamedFeature("Bar Feature", Bar.class))
|
|
.abstractFeature(Baz.class)
|
|
.build();
|
|
}</programlisting>
|
|
<simpara>Each of these beans should go in an appropriately guarded <literal>@Configuration</literal>.</simpara>
|
|
</section>
|
|
</section>
|
|
<section xml:id="_spring_cloud_compatibility_verification">
|
|
<title>Spring Cloud Compatibility Verification</title>
|
|
<simpara>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.</simpara>
|
|
<simpara>At the moment we verify which version of Spring Boot is added to your classpath.</simpara>
|
|
<simpara>Example of a report</simpara>
|
|
<screen>***************************
|
|
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.</screen>
|
|
<simpara>In order to disable this feature, set <literal>spring.cloud.compatibility-verifier.enabled</literal> to <literal>false</literal>.
|
|
If you want to override the compatible Spring Boot versions, just set the
|
|
<literal>spring.cloud.compatibility-verifier.compatible-boot-versions</literal> property with a comma separated list
|
|
of compatible Spring Boot versions.</simpara>
|
|
</section>
|
|
</chapter>
|
|
</book> |