mirror of
https://github.com/spring-cloud/spring-cloud-netflix.git
synced 2026-09-20 00:59:01 +00:00
Compare commits
233
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
993f04a6c7 | ||
|
|
e394c1227b | ||
|
|
c1afe1d2a0 | ||
|
|
89c37a3842 | ||
|
|
17e72988dc | ||
|
|
849f1e7e99 | ||
|
|
3f4c702aa5 | ||
|
|
1bd64613d1 | ||
|
|
86101cd733 | ||
|
|
a5734695b7 | ||
|
|
14c7ac2dda | ||
|
|
e839d1dfbd | ||
|
|
c5268f0d89 | ||
|
|
2b33f7a8b6 | ||
|
|
848bfab4ee | ||
|
|
4646484a30 | ||
|
|
6f1332ee72 | ||
|
|
bff9419201 | ||
|
|
7b90195ee8 | ||
|
|
df7ecf81c9 | ||
|
|
f2526d5e03 | ||
|
|
8e00d28bcc | ||
|
|
92cb1a7aa0 | ||
|
|
8ecb3dca51 | ||
|
|
df64a02013 | ||
|
|
74f7c0eefd | ||
|
|
4978f8f7de | ||
|
|
0b490f6d27 | ||
|
|
c6141c5b15 | ||
|
|
992269781a | ||
|
|
4ec5556420 | ||
|
|
2fe021fbdb | ||
|
|
a452c1d8e9 | ||
|
|
3bf50f3ef5 | ||
|
|
ab17bbfee2 | ||
|
|
7240308834 | ||
|
|
53c5b4ba86 | ||
|
|
ce82ea3b71 | ||
|
|
08add768d0 | ||
|
|
4064646b39 | ||
|
|
ce73a587f5 | ||
|
|
78ca251e96 | ||
|
|
6ce8123be1 | ||
|
|
d11f91cdb1 | ||
|
|
57053ed7eb | ||
|
|
0297304397 | ||
|
|
4bdb799c8c | ||
|
|
6e70f55049 | ||
|
|
be57fe3d8b | ||
|
|
931cca4f44 | ||
|
|
0d7c73c583 | ||
|
|
d498a6e091 | ||
|
|
34ede67962 | ||
|
|
24e842cfdd | ||
|
|
a1a9d8c599 | ||
|
|
c5e8b66368 | ||
|
|
88985f33af | ||
|
|
eeaeeb3e6f | ||
|
|
a3436d08eb | ||
|
|
5f3f35fd68 | ||
|
|
23d15f70bc | ||
|
|
072418826e | ||
|
|
cbc914259b | ||
|
|
896b33c109 | ||
|
|
a826a960e6 | ||
|
|
a579eb4594 | ||
|
|
624bbc8b50 | ||
|
|
4de8761123 | ||
|
|
8d5a3f8ec1 | ||
|
|
f6120d525f | ||
|
|
4bf57b9cd2 | ||
|
|
3bd44d69ca | ||
|
|
6506e03bcf | ||
|
|
443628827e | ||
|
|
85261361d7 | ||
|
|
34a07a15e2 | ||
|
|
21b8599be5 | ||
|
|
a8539b33db | ||
|
|
5d983b082e | ||
|
|
e556ccc7ed | ||
|
|
529b7ae12a | ||
|
|
a18479da64 | ||
|
|
8d19948f4b | ||
|
|
182951a0d5 | ||
|
|
0783016676 | ||
|
|
c36743de83 | ||
|
|
297f8950b1 | ||
|
|
849642ff84 | ||
|
|
dff3b91bef | ||
|
|
7db58f7d16 | ||
|
|
e820a484d6 | ||
|
|
2afba0977f | ||
|
|
61d2f956e3 | ||
|
|
17b9002fef | ||
|
|
d6f375490a | ||
|
|
bee39ec1bf | ||
|
|
0dc755518c | ||
|
|
ad3f2cb53a | ||
|
|
74f9fd4f9d | ||
|
|
c69a3eed70 | ||
|
|
0c12ec9132 | ||
|
|
2f6f0a0f05 | ||
|
|
c0174be906 | ||
|
|
7e96ed15f2 | ||
|
|
1b07143fde | ||
|
|
ea31d78891 | ||
|
|
492731632c | ||
|
|
551fecde83 | ||
|
|
aed8d19aea | ||
|
|
ff6b50d0a8 | ||
|
|
3cb98b70ce | ||
|
|
d1ffc8d72e | ||
|
|
49f88c4e7c | ||
|
|
53bffa7f04 | ||
|
|
8e1ada2b3d | ||
|
|
4122aadb73 | ||
|
|
92c0c7d503 | ||
|
|
34b1a07124 | ||
|
|
ae637ab3e5 | ||
|
|
2cd7e41c05 | ||
|
|
cf95248c37 | ||
|
|
b904eaa886 | ||
|
|
3455489f47 | ||
|
|
af919cac66 | ||
|
|
ead46658c0 | ||
|
|
732f7d40ea | ||
|
|
67444a07c2 | ||
|
|
a6da307048 | ||
|
|
55dabe8e6b | ||
|
|
7e82a47dd3 | ||
|
|
c56e4a35d5 | ||
|
|
02f3bb8bba | ||
|
|
a4fc71a5b1 | ||
|
|
a33387faa6 | ||
|
|
ec087d0747 | ||
|
|
23659f8aea | ||
|
|
b51a15c3f2 | ||
|
|
dc30eb2073 | ||
|
|
c6b8873f0c | ||
|
|
78cce48aa1 | ||
|
|
73c40cf561 | ||
|
|
a10d7b69d4 | ||
|
|
4164297a89 | ||
|
|
56534dba1c | ||
|
|
f50e3d60a9 | ||
|
|
e31d478c11 | ||
|
|
0fa901f6bc | ||
|
|
23366ac178 | ||
|
|
6fc234494a | ||
|
|
67fd109d68 | ||
|
|
3ecf5a7420 | ||
|
|
69f4c901a9 | ||
|
|
3130493a3c | ||
|
|
87529e0994 | ||
|
|
e8816398a8 | ||
|
|
bb1269d635 | ||
|
|
38cec4e35d | ||
|
|
2b5d246725 | ||
|
|
9b2405f3eb | ||
|
|
4ca1901417 | ||
|
|
2208011dd3 | ||
|
|
9a2b254d0c | ||
|
|
d74b319b44 | ||
|
|
3d3d528e02 | ||
|
|
28326b506b | ||
|
|
392565f395 | ||
|
|
532a95e783 | ||
|
|
ac60c28baa | ||
|
|
40ec6066c1 | ||
|
|
e8ff2ebaf8 | ||
|
|
24f5e0b043 | ||
|
|
8eb843c951 | ||
|
|
543ab09012 | ||
|
|
7389f25f1c | ||
|
|
669ce753b2 | ||
|
|
7dbac954d1 | ||
|
|
828bc82363 | ||
|
|
e8b50d80ed | ||
|
|
181d210aa5 | ||
|
|
d4db93a86a | ||
|
|
438d3f9b0e | ||
|
|
d5de0fc55c | ||
|
|
388faaa8e5 | ||
|
|
b386a78dae | ||
|
|
0ea0472b4f | ||
|
|
6d74bebf66 | ||
|
|
a99b5d66ca | ||
|
|
b0e694601e | ||
|
|
e160395978 | ||
|
|
46c25b38d7 | ||
|
|
455c60c15c | ||
|
|
00b419be94 | ||
|
|
df33d0bd07 | ||
|
|
9d40b5dc4e | ||
|
|
b8fb056981 | ||
|
|
b0ace4774e | ||
|
|
34c96c99cf | ||
|
|
c24e7e05bf | ||
|
|
fd710d8a7d | ||
|
|
62deb59314 | ||
|
|
925f460d64 | ||
|
|
6c2359a0ca | ||
|
|
571d1b7bb7 | ||
|
|
c01d9e6b59 | ||
|
|
609b4f5838 | ||
|
|
23e1c36462 | ||
|
|
4d765b37bb | ||
|
|
e606cc527e | ||
|
|
4403e1c8ff | ||
|
|
0b0f5d2334 | ||
|
|
25cd7a5810 | ||
|
|
7aab07365d | ||
|
|
6acaa9be8b | ||
|
|
11a9819e3b | ||
|
|
4997b69433 | ||
|
|
e2f1f368b5 | ||
|
|
7ea5c3a32f | ||
|
|
03b1e326ce | ||
|
|
7449915919 | ||
|
|
0e66862cbb | ||
|
|
ee9c704a4c | ||
|
|
105f8d85b3 | ||
|
|
cc6ec1572b | ||
|
|
37d39617bf | ||
|
|
3c92af4a06 | ||
|
|
eff47e0464 | ||
|
|
c1771dce56 | ||
|
|
88b71f05fa | ||
|
|
52e4f5ea59 | ||
|
|
77b8965e29 | ||
|
|
0791a02223 | ||
|
|
8fdc6f0898 | ||
|
|
3d5484e410 |
@@ -3,7 +3,7 @@
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
tracker for issues and merging pull requests into main. If you want
|
||||
to contribute even something trivial please do not hesitate, but
|
||||
follow the guidelines below.
|
||||
|
||||
@@ -17,7 +17,7 @@ given the ability to merge pull requests.
|
||||
|
||||
## Code of Conduct
|
||||
This project adheres to the Contributor Covenant [code of
|
||||
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
|
||||
conduct](https://github.com/spring-cloud/spring-cloud-build/blob/main/docs/src/main/asciidoc/code-of-conduct.adoc). By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
## Code Conventions and Housekeeping
|
||||
@@ -27,7 +27,7 @@ added after the original pull request but before a merge.
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
||||
[Spring Cloud Build](https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/spring-cloud-dependencies-parent/eclipse-code-formatter.xml) project. If using IntelliJ, you can use the
|
||||
[Eclipse Code Formatter Plugin](https://plugins.jetbrains.com/plugin/6546) to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
@@ -38,7 +38,7 @@ added after the original pull request but before a merge.
|
||||
than cosmetic changes).
|
||||
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
* If no-one else is using your branch, please rebase it against the current main (or
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow [these conventions](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html),
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a report to help us improve
|
||||
title: ''
|
||||
labels: waiting-for-triage
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
Please provide details of the problem, including the version of Spring Cloud that you
|
||||
are using.
|
||||
|
||||
**Sample**
|
||||
If possible, please provide a test case or sample application that reproduces
|
||||
the problem. This makes it much easier for us to diagnose the problem and to verify that
|
||||
we have fixed it.
|
||||
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for this project
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
@@ -18,3 +18,6 @@ _site/
|
||||
.shelf
|
||||
*.swp
|
||||
*.swo
|
||||
.vscode/
|
||||
.flattened-pom.xml
|
||||
.sdkmanrc
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
require 'asciidoctor'
|
||||
require 'erb'
|
||||
|
||||
options = {:mkdirs => true, :safe => :unsafe, :attributes => ['linkcss', 'allow-uri-read']}
|
||||
|
||||
guard 'shell' do
|
||||
watch(/^docs\/[A-Z-a-z][^#]*\.adoc$/) {|m|
|
||||
Asciidoctor.load_file('docs/src/main/asciidoc/README.adoc', :to_file => './README.adoc', safe: :safe, parse: false, attributes: 'allow-uri-read')
|
||||
Asciidoctor.render_file('docs/src/main/asciidoc/spring-cloud-netflix.adoc', options.merge(:to_dir => 'target/generated-docs'))
|
||||
}
|
||||
end
|
||||
+206
-6
@@ -1,10 +1,47 @@
|
||||
// Do not edit this file (e.g. go instead to src/main/asciidoc)
|
||||
////
|
||||
DO NOT EDIT THIS FILE. IT WAS GENERATED.
|
||||
Manual changes to this file will be lost when it is generated again.
|
||||
Edit the files in the src/main/asciidoc/ directory instead.
|
||||
////
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master"]
|
||||
|
||||
:doctype: book
|
||||
:idprefix:
|
||||
:idseparator: -
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:numbered:
|
||||
:sectanchors:
|
||||
:sectnums:
|
||||
:icons: font
|
||||
:hide-uri-scheme:
|
||||
:docinfo: shared,private
|
||||
|
||||
:sc-ext: java
|
||||
:project-full-name: Spring Cloud Netflix
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main"]
|
||||
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
|
||||
|
||||
|
||||
:doctype: book
|
||||
:idprefix:
|
||||
:idseparator: -
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:numbered:
|
||||
:sectanchors:
|
||||
:sectnums:
|
||||
:icons: font
|
||||
:hide-uri-scheme:
|
||||
:docinfo: shared,private
|
||||
|
||||
:sc-ext: java
|
||||
:project-full-name: Spring Cloud Netflix
|
||||
|
||||
This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
|
||||
and binding to the Spring Environment and other Spring programming model idioms. With a few
|
||||
simple annotations you can quickly enable and configure the common patterns inside your
|
||||
@@ -23,6 +60,21 @@ Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
:doctype: book
|
||||
:idprefix:
|
||||
:idseparator: -
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:numbered:
|
||||
:sectanchors:
|
||||
:sectnums:
|
||||
:icons: font
|
||||
:hide-uri-scheme:
|
||||
:docinfo: shared,private
|
||||
|
||||
:sc-ext: java
|
||||
:project-full-name: Spring Cloud Netflix
|
||||
|
||||
== Modules In Maintenance Mode
|
||||
|
||||
@@ -35,6 +87,7 @@ of the Greenwich release train.
|
||||
The following Spring Cloud Netflix modules and corresponding starters will be placed into maintenance mode:
|
||||
|
||||
* spring-cloud-netflix-archaius
|
||||
* spring-cloud-netflix-concurrency-limits
|
||||
* spring-cloud-netflix-hystrix-contract
|
||||
* spring-cloud-netflix-hystrix-dashboard
|
||||
* spring-cloud-netflix-hystrix-stream
|
||||
@@ -44,7 +97,7 @@ The following Spring Cloud Netflix modules and corresponding starters will be pl
|
||||
* spring-cloud-netflix-turbine
|
||||
* spring-cloud-netflix-zuul
|
||||
|
||||
NOTE: This does not include the Eureka or concurrency-limits modules.
|
||||
NOTE: This does not include the Eureka modules.
|
||||
|
||||
|
||||
== Building
|
||||
@@ -84,7 +137,7 @@ credentials and you already have those.
|
||||
|
||||
The projects that require middleware generally include a
|
||||
`docker-compose.yml`, so consider using
|
||||
https://compose.docker.io/[Docker Compose] to run the middeware servers
|
||||
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
|
||||
in Docker containers. See the README in the
|
||||
https://github.com/spring-cloud-samples/scripts[scripts demo
|
||||
repository] for specific instructions about the common cases of mongo,
|
||||
@@ -111,6 +164,11 @@ https://eclipse.org[Eclipse] when working with the code. We use the
|
||||
https://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue as long as they use Maven 3.3.3 or better.
|
||||
|
||||
==== Activate the Spring Maven profile
|
||||
Spring Cloud projects require the 'spring' Maven profile to be activated to resolve
|
||||
the spring milestone and snapshot repositories. Use your preferred IDE to set this
|
||||
profile to be active, or you may experience build errors.
|
||||
|
||||
==== Importing into eclipse with m2eclipse
|
||||
We recommend the https://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||
@@ -139,8 +197,13 @@ from the `file` menu.
|
||||
|
||||
|
||||
|
||||
NOTE: To build the module `spring-cloud-netflix-hystrix-contract` along with the entire Netflix project run the
|
||||
`build.sh` script in the `scripts` directory.
|
||||
|
||||
== Contributing
|
||||
|
||||
:spring-cloud-build-branch: master
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
@@ -184,4 +247,141 @@ added after the original pull request but before a merge.
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
message (where XXXX is the issue number).
|
||||
message (where XXXX is the issue number).
|
||||
|
||||
=== Checkstyle
|
||||
|
||||
Spring Cloud Build comes with a set of checkstyle rules. You can find them in the `spring-cloud-build-tools` module. The most notable files under the module are:
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
└── checkstyle.xml <1>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
|
||||
==== Checkstyle configuration
|
||||
|
||||
Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins.
|
||||
|
||||
.pom.xml
|
||||
----
|
||||
<properties>
|
||||
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> <1>
|
||||
<maven-checkstyle-plugin.failsOnViolation>true
|
||||
</maven-checkstyle-plugin.failsOnViolation> <2>
|
||||
<maven-checkstyle-plugin.includeTestSourceDirectory>true
|
||||
</maven-checkstyle-plugin.includeTestSourceDirectory> <3>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin> <4>
|
||||
<groupId>io.spring.javaformat</groupId>
|
||||
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</build>
|
||||
----
|
||||
<1> Fails the build upon Checkstyle errors
|
||||
<2> Fails the build upon Checkstyle violations
|
||||
<3> Checkstyle analyzes also the test sources
|
||||
<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules
|
||||
<5> Add checkstyle plugin to your build and reporting phases
|
||||
|
||||
If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example:
|
||||
|
||||
.projectRoot/src/checkstyle/checkstyle-suppresions.xml
|
||||
----
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
|
||||
</suppressions>
|
||||
----
|
||||
|
||||
It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script:
|
||||
|
||||
```bash
|
||||
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
|
||||
$ touch .springformat
|
||||
```
|
||||
|
||||
=== IDE setup
|
||||
|
||||
==== Intellij IDEA
|
||||
|
||||
In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.
|
||||
The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project.
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
├── checkstyle.xml <1>
|
||||
└── intellij
|
||||
├── Intellij_Project_Defaults.xml <4>
|
||||
└── Intellij_Spring_Boot_Java_Conventions.xml <5>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
<4> Project defaults for Intellij that apply most of Checkstyle rules
|
||||
<5> Project style conventions for Intellij that apply most of Checkstyle rules
|
||||
|
||||
.Code style
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file.
|
||||
|
||||
.Inspection profiles
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file.
|
||||
|
||||
.Checkstyle
|
||||
|
||||
To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle]
|
||||
|
||||
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||
|
||||
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
|
||||
== License
|
||||
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/main/LICENSE.txt[here].
|
||||
|
||||
@@ -1,329 +0,0 @@
|
||||
////
|
||||
DO NOT EDIT THIS FILE. IT WAS GENERATED.
|
||||
Manual changes to this file will be lost when it is generated again.
|
||||
Edit the files in the src/main/asciidoc/ directory instead.
|
||||
////
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master"]
|
||||
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
|
||||
|
||||
|
||||
This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
|
||||
and binding to the Spring Environment and other Spring programming model idioms. With a few
|
||||
simple annotations you can quickly enable and configure the common patterns inside your
|
||||
application and build large distributed systems with battle-tested Netflix components. The
|
||||
patterns provided include Service Discovery (Eureka), Circuit Breaker (Hystrix),
|
||||
Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon).
|
||||
|
||||
|
||||
== Features
|
||||
|
||||
* Service Discovery: Eureka instances can be registered and clients can discover the instances using Spring-managed beans
|
||||
* Service Discovery: an embedded Eureka server can be created with declarative Java configuration
|
||||
* Circuit Breaker: Hystrix clients can be built with a simple annotation-driven method decorator
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
|
||||
== Modules In Maintenance Mode
|
||||
|
||||
Placing a module in maintenance mode means that the Spring Cloud team will no longer be adding new features to the module.
|
||||
We will fix blocker bugs and security issues, and we will also consider and review small pull requests from the community.
|
||||
|
||||
We intend to continue to support these modules for a period of at least a year from the general availability
|
||||
of the Greenwich release train.
|
||||
|
||||
The following Spring Cloud Netflix modules and corresponding starters will be placed into maintenance mode:
|
||||
|
||||
* spring-cloud-netflix-archaius
|
||||
* spring-cloud-netflix-hystrix-contract
|
||||
* spring-cloud-netflix-hystrix-dashboard
|
||||
* spring-cloud-netflix-hystrix-stream
|
||||
* spring-cloud-netflix-hystrix
|
||||
* spring-cloud-netflix-ribbon
|
||||
* spring-cloud-netflix-turbine-stream
|
||||
* spring-cloud-netflix-turbine
|
||||
* spring-cloud-netflix-zuul
|
||||
|
||||
NOTE: This does not include the Eureka or concurrency-limits modules.
|
||||
|
||||
|
||||
== Building
|
||||
|
||||
:jdkversion: 1.8
|
||||
|
||||
=== Basic Compile and Test
|
||||
|
||||
To build the source you will need to install JDK {jdkversion}.
|
||||
|
||||
Spring Cloud uses Maven for most build-related activities, and you
|
||||
should be able to get off the ground quite quickly by cloning the
|
||||
project you are interested in and typing
|
||||
|
||||
----
|
||||
$ ./mvnw install
|
||||
----
|
||||
|
||||
NOTE: You can also install Maven (>=3.3.3) yourself and run the `mvn` command
|
||||
in place of `./mvnw` in the examples below. If you do that you also
|
||||
might need to add `-P spring` if your local Maven settings do not
|
||||
contain repository declarations for spring pre-release artifacts.
|
||||
|
||||
NOTE: Be aware that you might need to increase the amount of memory
|
||||
available to Maven by setting a `MAVEN_OPTS` environment variable with
|
||||
a value like `-Xmx512m -XX:MaxPermSize=128m`. We try to cover this in
|
||||
the `.mvn` configuration, so if you find you have to do it to make a
|
||||
build succeed, please raise a ticket to get the settings added to
|
||||
source control.
|
||||
|
||||
For hints on how to build the project look in `.travis.yml` if there
|
||||
is one. There should be a "script" and maybe "install" command. Also
|
||||
look at the "services" section to see if any services need to be
|
||||
running locally (e.g. mongo or rabbit). Ignore the git-related bits
|
||||
that you might find in "before_install" since they're related to setting git
|
||||
credentials and you already have those.
|
||||
|
||||
The projects that require middleware generally include a
|
||||
`docker-compose.yml`, so consider using
|
||||
https://docs.docker.com/compose/[Docker Compose] to run the middeware servers
|
||||
in Docker containers. See the README in the
|
||||
https://github.com/spring-cloud-samples/scripts[scripts demo
|
||||
repository] for specific instructions about the common cases of mongo,
|
||||
rabbit and redis.
|
||||
|
||||
NOTE: If all else fails, build with the command from `.travis.yml` (usually
|
||||
`./mvnw install`).
|
||||
|
||||
=== Documentation
|
||||
|
||||
The spring-cloud-build module has a "docs" profile, and if you switch
|
||||
that on it will try to build asciidoc sources from
|
||||
`src/main/asciidoc`. As part of that process it will look for a
|
||||
`README.adoc` and process it by loading all the includes, but not
|
||||
parsing or rendering it, just copying it to `${main.basedir}`
|
||||
(defaults to `${basedir}`, i.e. the root of the project). If there are
|
||||
any changes in the README it will then show up after a Maven build as
|
||||
a modified file in the correct place. Just commit it and push the change.
|
||||
|
||||
=== Working with the code
|
||||
If you don't have an IDE preference we would recommend that you use
|
||||
https://www.springsource.com/developer/sts[Spring Tools Suite] or
|
||||
https://eclipse.org[Eclipse] when working with the code. We use the
|
||||
https://eclipse.org/m2e/[m2eclipse] eclipse plugin for maven support. Other IDEs and tools
|
||||
should also work without issue as long as they use Maven 3.3.3 or better.
|
||||
|
||||
==== Importing into eclipse with m2eclipse
|
||||
We recommend the https://eclipse.org/m2e/[m2eclipse] eclipse plugin when working with
|
||||
eclipse. If you don't already have m2eclipse installed it is available from the "eclipse
|
||||
marketplace".
|
||||
|
||||
NOTE: Older versions of m2e do not support Maven 3.3, so once the
|
||||
projects are imported into Eclipse you will also need to tell
|
||||
m2eclipse to use the right profile for the projects. If you
|
||||
see many different errors related to the POMs in the projects, check
|
||||
that you have an up to date installation. If you can't upgrade m2e,
|
||||
add the "spring" profile to your `settings.xml`. Alternatively you can
|
||||
copy the repository settings from the "spring" profile of the parent
|
||||
pom into your `settings.xml`.
|
||||
|
||||
==== Importing into eclipse without m2eclipse
|
||||
If you prefer not to use m2eclipse you can generate eclipse project metadata using the
|
||||
following command:
|
||||
|
||||
[indent=0]
|
||||
----
|
||||
$ ./mvnw eclipse:eclipse
|
||||
----
|
||||
|
||||
The generated eclipse projects can be imported by selecting `import existing projects`
|
||||
from the `file` menu.
|
||||
|
||||
|
||||
|
||||
NOTE: To build the module `spring-cloud-netflix-hystrix-contract` along with the entire Netflix project run the
|
||||
`build.sh` script in the `scripts` directory.
|
||||
|
||||
== Contributing
|
||||
|
||||
:spring-cloud-build-branch: master
|
||||
|
||||
Spring Cloud is released under the non-restrictive Apache 2.0 license,
|
||||
and follows a very standard Github development process, using Github
|
||||
tracker for issues and merging pull requests into master. If you want
|
||||
to contribute even something trivial please do not hesitate, but
|
||||
follow the guidelines below.
|
||||
|
||||
=== Sign the Contributor License Agreement
|
||||
Before we accept a non-trivial patch or pull request we will need you to sign the
|
||||
https://cla.pivotal.io/sign/spring[Contributor License Agreement].
|
||||
Signing the contributor's agreement does not grant anyone commit rights to the main
|
||||
repository, but it does mean that we can accept your contributions, and you will get an
|
||||
author credit if we do. Active contributors might be asked to join the core team, and
|
||||
given the ability to merge pull requests.
|
||||
|
||||
=== Code of Conduct
|
||||
This project adheres to the Contributor Covenant https://github.com/spring-cloud/spring-cloud-build/blob/master/docs/src/main/asciidoc/code-of-conduct.adoc[code of
|
||||
conduct]. By participating, you are expected to uphold this code. Please report
|
||||
unacceptable behavior to spring-code-of-conduct@pivotal.io.
|
||||
|
||||
=== Code Conventions and Housekeeping
|
||||
None of these is essential for a pull request, but they will all help. They can also be
|
||||
added after the original pull request but before a merge.
|
||||
|
||||
* Use the Spring Framework code format conventions. If you use Eclipse
|
||||
you can import formatter settings using the
|
||||
`eclipse-code-formatter.xml` file from the
|
||||
https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-dependencies-parent/eclipse-code-formatter.xml[Spring
|
||||
Cloud Build] project. If using IntelliJ, you can use the
|
||||
https://plugins.jetbrains.com/plugin/6546[Eclipse Code Formatter
|
||||
Plugin] to import the same file.
|
||||
* Make sure all new `.java` files to have a simple Javadoc class comment with at least an
|
||||
`@author` tag identifying you, and preferably at least a paragraph on what the class is
|
||||
for.
|
||||
* Add the ASF license header comment to all new `.java` files (copy from existing files
|
||||
in the project)
|
||||
* Add yourself as an `@author` to the .java files that you modify substantially (more
|
||||
than cosmetic changes).
|
||||
* Add some Javadocs and, if you change the namespace, some XSD doc elements.
|
||||
* A few unit tests would help a lot as well -- someone has to do it.
|
||||
* If no-one else is using your branch, please rebase it against the current master (or
|
||||
other target branch in the main project).
|
||||
* When writing a commit message please follow https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html[these conventions],
|
||||
if you are fixing an existing issue please add `Fixes gh-XXXX` at the end of the commit
|
||||
message (where XXXX is the issue number).
|
||||
|
||||
=== Checkstyle
|
||||
|
||||
Spring Cloud Build comes with a set of checkstyle rules. You can find them in the `spring-cloud-build-tools` module. The most notable files under the module are:
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
└── checkstyle.xml <1>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
|
||||
==== Checkstyle configuration
|
||||
|
||||
Checkstyle rules are *disabled by default*. To add checkstyle to your project just define the following properties and plugins.
|
||||
|
||||
.pom.xml
|
||||
----
|
||||
<properties>
|
||||
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError> <1>
|
||||
<maven-checkstyle-plugin.failsOnViolation>true
|
||||
</maven-checkstyle-plugin.failsOnViolation> <2>
|
||||
<maven-checkstyle-plugin.includeTestSourceDirectory>true
|
||||
</maven-checkstyle-plugin.includeTestSourceDirectory> <3>
|
||||
</properties>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin> <4>
|
||||
<groupId>io.spring.javaformat</groupId>
|
||||
<artifactId>spring-javaformat-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin> <5>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-checkstyle-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</reporting>
|
||||
</build>
|
||||
----
|
||||
<1> Fails the build upon Checkstyle errors
|
||||
<2> Fails the build upon Checkstyle violations
|
||||
<3> Checkstyle analyzes also the test sources
|
||||
<4> Add the Spring Java Format plugin that will reformat your code to pass most of the Checkstyle formatting rules
|
||||
<5> Add checkstyle plugin to your build and reporting phases
|
||||
|
||||
If you need to suppress some rules (e.g. line length needs to be longer), then it's enough for you to define a file under `${project.root}/src/checkstyle/checkstyle-suppressions.xml` with your suppressions. Example:
|
||||
|
||||
.projectRoot/src/checkstyle/checkstyle-suppresions.xml
|
||||
----
|
||||
<?xml version="1.0"?>
|
||||
<!DOCTYPE suppressions PUBLIC
|
||||
"-//Puppy Crawl//DTD Suppressions 1.1//EN"
|
||||
"https://www.puppycrawl.com/dtds/suppressions_1_1.dtd">
|
||||
<suppressions>
|
||||
<suppress files=".*ConfigServerApplication\.java" checks="HideUtilityClassConstructor"/>
|
||||
<suppress files=".*ConfigClientWatch\.java" checks="LineLengthCheck"/>
|
||||
</suppressions>
|
||||
----
|
||||
|
||||
It's advisable to copy the `${spring-cloud-build.rootFolder}/.editorconfig` and `${spring-cloud-build.rootFolder}/.springformat` to your project. That way, some default formatting rules will be applied. You can do so by running this script:
|
||||
|
||||
```bash
|
||||
$ curl https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/.editorconfig -o .editorconfig
|
||||
$ touch .springformat
|
||||
```
|
||||
|
||||
=== IDE setup
|
||||
|
||||
==== Intellij IDEA
|
||||
|
||||
In order to setup Intellij you should import our coding conventions, inspection profiles and set up the checkstyle plugin.
|
||||
The following files can be found in the https://github.com/spring-cloud/spring-cloud-build/tree/master/spring-cloud-build-tools[Spring Cloud Build] project.
|
||||
|
||||
.spring-cloud-build-tools/
|
||||
----
|
||||
└── src
|
||||
├── checkstyle
|
||||
│ └── checkstyle-suppressions.xml <3>
|
||||
└── main
|
||||
└── resources
|
||||
├── checkstyle-header.txt <2>
|
||||
├── checkstyle.xml <1>
|
||||
└── intellij
|
||||
├── Intellij_Project_Defaults.xml <4>
|
||||
└── Intellij_Spring_Boot_Java_Conventions.xml <5>
|
||||
----
|
||||
<1> Default Checkstyle rules
|
||||
<2> File header setup
|
||||
<3> Default suppression rules
|
||||
<4> Project defaults for Intellij that apply most of Checkstyle rules
|
||||
<5> Project style conventions for Intellij that apply most of Checkstyle rules
|
||||
|
||||
.Code style
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-code-style.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Code style`. There click on the icon next to the `Scheme` section. There, click on the `Import Scheme` value and pick the `Intellij IDEA code style XML` option. Import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Spring_Boot_Java_Conventions.xml` file.
|
||||
|
||||
.Inspection profiles
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-inspections.png[Code style]
|
||||
|
||||
Go to `File` -> `Settings` -> `Editor` -> `Inspections`. There click on the icon next to the `Profile` section. There, click on the `Import Profile` and import the `spring-cloud-build-tools/src/main/resources/intellij/Intellij_Project_Defaults.xml` file.
|
||||
|
||||
.Checkstyle
|
||||
|
||||
To have Intellij work with Checkstyle, you have to install the `Checkstyle` plugin. It's advisable to also install the `Assertions2Assertj` to automatically convert the JUnit assertions
|
||||
|
||||
image::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/{spring-cloud-build-branch}/docs/src/main/asciidoc/images/intellij-checkstyle.png[Checkstyle]
|
||||
|
||||
Go to `File` -> `Settings` -> `Other settings` -> `Checkstyle`. There click on the `+` icon in the `Configuration file` section. There, you'll have to define where the checkstyle rules should be picked from. In the image above, we've picked the rules from the cloned Spring Cloud Build repository. However, you can point to the Spring Cloud Build's GitHub repository (e.g. for the `checkstyle.xml` : `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle.xml`). We need to provide the following variables:
|
||||
|
||||
- `checkstyle.header.file` - please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/main/resources/checkstyle-header.txt` URL.
|
||||
- `checkstyle.suppressions.file` - default suppressions. Please point it to the Spring Cloud Build's, `spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` file either in your cloned repo or via the `https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/spring-cloud-build-tools/src/checkstyle/checkstyle-suppressions.xml` URL.
|
||||
- `checkstyle.additional.suppressions.file` - this variable corresponds to suppressions in your local project. E.g. you're working on `spring-cloud-contract`. Then point to the `project-root/src/checkstyle/checkstyle-suppressions.xml` folder. Example for `spring-cloud-contract` would be: `/home/username/spring-cloud-contract/src/checkstyle/checkstyle-suppressions.xml`.
|
||||
|
||||
IMPORTANT: Remember to set the `Scan Scope` to `All sources` since we apply checkstyle rules for production and test sources.
|
||||
+56
-23
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-docs</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
@@ -13,45 +13,78 @@
|
||||
<description>Spring Cloud Docs</description>
|
||||
<properties>
|
||||
<docs.main>spring-cloud-netflix</docs.main>
|
||||
<docs.whitelisted.branches>1.2.x,1.3.x,1.4.x</docs.whitelisted.branches>
|
||||
<main.basedir>${basedir}/..</main.basedir>
|
||||
<configprops.inclusionPattern>
|
||||
.*.eureka.*|.*.hystrix.*|.*.ribbon.*|.*.sidecar.*|.*.turbine.*|.*.zuul.*|.*.archaius.*
|
||||
</configprops.inclusionPattern>
|
||||
<upload-docs-zip.phase>deploy</upload-docs-zip.phase>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
<configuration>
|
||||
<skip>true</skip>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-archaius</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-turbine-stream</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>docs</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<groupId>pl.project13.maven</groupId>
|
||||
<artifactId>git-commit-id-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.asciidoctor</groupId>
|
||||
<artifactId>asciidoctor-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>com.agilejava.docbkx</groupId>
|
||||
<artifactId>docbkx-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-antrun-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<inherited>false</inherited>
|
||||
<artifactId>maven-deploy-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/master"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/master"]
|
||||
include::_attributes.adoc[]
|
||||
|
||||
image::https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main.svg?style=svg["CircleCI", link="https://circleci.com/gh/spring-cloud/spring-cloud-netflix/tree/main"]
|
||||
image::https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main/graph/badge.svg["Codecov", link="https://codecov.io/gh/spring-cloud/spring-cloud-netflix/branch/main"]
|
||||
image::https://api.codacy.com/project/badge/Grade/a6885a06921e4f72a0df0b7aabd6d118["Codacy code quality", link="https://www.codacy.com/app/Spring-Cloud/spring-cloud-netflix?utm_source=github.com&utm_medium=referral&utm_content=spring-cloud/spring-cloud-netflix&utm_campaign=Badge_Grade"]
|
||||
|
||||
|
||||
@@ -19,11 +21,15 @@ include::maintenance-mode.adoc[]
|
||||
|
||||
== Building
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/building-jdk8.adoc[]
|
||||
|
||||
NOTE: To build the module `spring-cloud-netflix-hystrix-contract` along with the entire Netflix project run the
|
||||
`build.sh` script in the `scripts` directory.
|
||||
|
||||
== Contributing
|
||||
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/master/docs/src/main/asciidoc/contributing.adoc[]
|
||||
include::https://raw.githubusercontent.com/spring-cloud/spring-cloud-build/main/docs/src/main/asciidoc/contributing.adoc[]
|
||||
|
||||
== License
|
||||
|
||||
The project license file is available https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/main/LICENSE.txt[here].
|
||||
@@ -0,0 +1,15 @@
|
||||
:doctype: book
|
||||
:idprefix:
|
||||
:idseparator: -
|
||||
:toc: left
|
||||
:toclevels: 4
|
||||
:tabsize: 4
|
||||
:numbered:
|
||||
:sectanchors:
|
||||
:sectnums:
|
||||
:icons: font
|
||||
:hide-uri-scheme:
|
||||
:docinfo: shared,private
|
||||
|
||||
:sc-ext: java
|
||||
:project-full-name: Spring Cloud Netflix
|
||||
@@ -0,0 +1,27 @@
|
||||
|===
|
||||
|Name | Default | Description
|
||||
|
||||
|eureka.client.eureka-connection-idle-timeout-seconds | `30` | Indicates how much time (in seconds) that the HTTP connections to eureka server can stay idle before it can be closed. In the AWS environment, it is recommended that the values is 30 seconds or less, since the firewall cleans up the connection information after a few mins leaving the connection hanging in limbo.
|
||||
|eureka.client.eureka-server-connect-timeout-seconds | `5` | Indicates how long to wait (in seconds) before a connection to eureka server needs to timeout. Note that the connections in the client are pooled by org.apache.http.client.HttpClient and this setting affects the actual connection creation and also the wait time to get the connection from the pool.
|
||||
|eureka.client.eureka-server-d-n-s-name | | Gets the DNS name to be queried to get the list of eureka servers.This information is not required if the contract returns the service urls by implementing serviceUrls. The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|
||||
|eureka.client.eureka-server-port | | Gets the port to be used to construct the service url to contact eureka server when the list of eureka servers come from the DNS.This information is not required if the contract returns the service urls eurekaServerServiceUrls(String). The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|
||||
|eureka.client.eureka-server-read-timeout-seconds | `8` | Indicates how long to wait (in seconds) before a read from eureka server needs to timeout.
|
||||
|eureka.client.eureka-server-total-connections | `200` | Gets the total number of connections that is allowed from eureka client to all eureka servers.
|
||||
|eureka.client.eureka-server-total-connections-per-host | `50` | Gets the total number of connections that is allowed from eureka client to a eureka server host.
|
||||
|eureka.client.eureka-server-u-r-l-context | | Gets the URL context to be used to construct the service url to contact eureka server when the list of eureka servers come from the DNS. This information is not required if the contract returns the service urls from eurekaServerServiceUrls. The DNS mechanism is used when useDnsForFetchingServiceUrls is set to true and the eureka client expects the DNS to configured a certain way so that it can fetch changing eureka servers dynamically. The changes are effective at runtime.
|
||||
|eureka.client.eureka-service-url-poll-interval-seconds | `0` | Indicates how often(in seconds) to poll for changes to eureka server information. Eureka servers could be added or removed and this setting controls how soon the eureka clients should know about it.
|
||||
|eureka.client.prefer-same-zone-eureka | `true` | Indicates whether or not this instance should try to use the eureka server in the same zone for latency and/or other reason. Ideally eureka clients are configured to talk to servers in the same zone The changes are effective at runtime at the next registry fetch cycle as specified by registryFetchIntervalSeconds
|
||||
|eureka.client.register-with-eureka | `true` | Indicates whether or not this instance should register its information with eureka server for discovery by others. In some cases, you do not want your instances to be discovered whereas you just want do discover other instances.
|
||||
|eureka.server.peer-eureka-nodes-update-interval-ms | `0` |
|
||||
|eureka.server.peer-eureka-status-refresh-time-interval-ms | `0` |
|
||||
|management.endpoint.hystrix.config | | Hystrix settings. These are traditionally set using servlet parameters. Refer to the documentation of Hystrix for more details.
|
||||
|management.endpoint.hystrix.stream.enabled | `true` | Whether to enable the hystrix.stream endpoint.
|
||||
|management.metrics.binders.hystrix.enabled | `true` | Enables creation of OK Http Client factory beans.
|
||||
|ribbon.eureka.enabled | `true` | Enables the use of Eureka with Ribbon.
|
||||
|spring.cloud.circuitbreaker.hystrix.enabled | `true` | Enables auto-configuration of the Hystrix Spring Cloud CircuitBreaker API implementation.
|
||||
|spring.cloud.loadbalancer.eureka.approximate-zone-from-hostname | `false` | Used to determine whether we should try to get the `zone` value from host name.
|
||||
|spring.cloud.loadbalancer.ribbon.enabled | `true` | Causes `RibbonLoadBalancerClient` to be used by default.
|
||||
|zuul.ribbon-isolation-strategy | |
|
||||
|zuul.ribbon.eager-load.enabled | `false` | Enables eager loading of Ribbon clients on startup.
|
||||
|
||||
|===
|
||||
@@ -0,0 +1,14 @@
|
||||
:numbered!:
|
||||
[appendix]
|
||||
[[common-application-properties]]
|
||||
== Common application properties
|
||||
|
||||
include::_attributes.adoc[]
|
||||
|
||||
Various properties can be specified inside your `application.properties` file, inside your `application.yml` file, or as command line switches.
|
||||
This appendix provides a list of common {project-full-name} properties and references to the underlying classes that consume them.
|
||||
|
||||
NOTE: Property contributions can come from additional jar files on your classpath, so you should not consider this an exhaustive list.
|
||||
Also, you can define your own properties.
|
||||
|
||||
include::_configprops.adoc[]
|
||||
@@ -39,7 +39,7 @@ function check_if_anything_to_sync() {
|
||||
}
|
||||
|
||||
function retrieve_current_branch() {
|
||||
# Code getting the name of the current branch. For master we want to publish as we did until now
|
||||
# Code getting the name of the current branch. For main we want to publish as we did until now
|
||||
# https://stackoverflow.com/questions/1593051/how-to-programmatically-determine-the-current-checked-out-git-branch
|
||||
# If there is a branch already passed will reuse it - otherwise will try to find it
|
||||
CURRENT_BRANCH=${BRANCH}
|
||||
@@ -134,8 +134,8 @@ function add_docs_from_target() {
|
||||
|
||||
# Copies the docs by using the retrieved properties from Maven build
|
||||
function copy_docs_for_current_version() {
|
||||
if [[ "${CURRENT_BRANCH}" == "master" ]] ; then
|
||||
echo -e "Current branch is master - will copy the current docs only to the root folder"
|
||||
if [[ "${CURRENT_BRANCH}" == "main" ]] ; then
|
||||
echo -e "Current branch is main - will copy the current docs only to the root folder"
|
||||
for f in docs/target/generated-docs/*; do
|
||||
file=${f#docs/target/generated-docs/*}
|
||||
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^$file$; then
|
||||
@@ -216,7 +216,7 @@ function commit_changes_if_applicable() {
|
||||
git commit -a -m "Sync docs from ${CURRENT_BRANCH} to gh-pages" && COMMIT_SUCCESSFUL="yes" || echo "Failed to commit changes"
|
||||
|
||||
# Uncomment the following push if you want to auto push to
|
||||
# the gh-pages branch whenever you commit to master locally.
|
||||
# the gh-pages branch whenever you commit to main locally.
|
||||
# This is a little extreme. Use with care!
|
||||
###################################################################
|
||||
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
|
||||
@@ -249,7 +249,7 @@ The idea of this script is to update gh-pages branch with the generated docs. Wi
|
||||
the script will work in the following manner:
|
||||
|
||||
- if there's no gh-pages / target for docs module then the script ends
|
||||
- for master branch the generated docs are copied to the root of gh-pages branch
|
||||
- for main branch the generated docs are copied to the root of gh-pages branch
|
||||
- for any other branch (if that branch is whitelisted) a subfolder with branch name is created
|
||||
and docs are copied there
|
||||
- if the version switch is passed (-v) then a tag with (v) prefix will be retrieved and a folder
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
include::_attributes.adoc[]
|
||||
|
||||
This project provides Netflix OSS integrations for Spring Boot apps through autoconfiguration
|
||||
and binding to the Spring Environment and other Spring programming model idioms. With a few
|
||||
simple annotations you can quickly enable and configure the common patterns inside your
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
include::_attributes.adoc[]
|
||||
|
||||
== Modules In Maintenance Mode
|
||||
|
||||
@@ -10,6 +11,7 @@ of the Greenwich release train.
|
||||
The following Spring Cloud Netflix modules and corresponding starters will be placed into maintenance mode:
|
||||
|
||||
* spring-cloud-netflix-archaius
|
||||
* spring-cloud-netflix-concurrency-limits
|
||||
* spring-cloud-netflix-hystrix-contract
|
||||
* spring-cloud-netflix-hystrix-dashboard
|
||||
* spring-cloud-netflix-hystrix-stream
|
||||
@@ -19,5 +21,5 @@ The following Spring Cloud Netflix modules and corresponding starters will be pl
|
||||
* spring-cloud-netflix-turbine
|
||||
* spring-cloud-netflix-zuul
|
||||
|
||||
NOTE: This does not include the Eureka or concurrency-limits modules.
|
||||
NOTE: This does not include the Eureka modules.
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
Spring Cloud Netflix provides Netflix OSS integrations for Spring Boot apps through autoconfiguration and binding to the Spring Environment and other Spring programming model idioms. With a few simple annotations you can quickly enable and configure the common patterns inside your application and build large distributed systems with battle-tested Netflix components. The patterns provided include Service Discovery (Eureka), Circuit Breaker (Hystrix), Intelligent Routing (Zuul) and Client Side Load Balancing (Ribbon)..
|
||||
|
||||
## Features
|
||||
|
||||
Spring Cloud Netflix features:
|
||||
|
||||
* Service Discovery: Eureka instances can be registered and clients can discover the instances using Spring-managed beans
|
||||
* Service Discovery: an embedded Eureka server can be created with declarative Java configuration
|
||||
* Circuit Breaker: Hystrix clients can be built with a simple annotation-driven method decorator
|
||||
* Circuit Breaker: embedded Hystrix dashboard with declarative Java configuration
|
||||
* Declarative REST Client: Feign creates a dynamic implementation of an interface decorated with JAX-RS or Spring MVC annotations
|
||||
* Client Side Load Balancer: Ribbon
|
||||
* External Configuration: a bridge from the Spring Environment to Archaius (enables native configuration of Netflix components using Spring Boot conventions)
|
||||
* Router and Filter: automatic registration of Zuul filters, and a simple convention over configuration approach to reverse proxy creation
|
||||
|
||||
## Getting Started
|
||||
|
||||
As long as Spring Cloud Netflix and Eureka Core are on the
|
||||
classpath any Spring Boot application with `@EnableEurekaClient` will try to contact a Eureka
|
||||
server on `http://localhost:8761` (the default value of
|
||||
`eureka.client.serviceUrl.defaultZone`):
|
||||
|
||||
```java
|
||||
@SpringBootApplication
|
||||
@EnableEurekaClient
|
||||
@RestController
|
||||
public class Application {
|
||||
|
||||
@RequestMapping("/")
|
||||
public String home() {
|
||||
return "Hello World";
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(Application.class, args);
|
||||
}
|
||||
|
||||
}
|
||||
```
|
||||
|
||||
To run your own server use the `spring-cloud-starter-netflix-eureka-server` dependency and `@EnableEurekaServer`.
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions. You can read more on how to contribute to the project https://github.com/spring-cloud/spring-cloud-netflix#contributing[here].
|
||||
|
||||
## Community Support
|
||||
|
||||
* You can report issues through https://github.com/spring-cloud/spring-cloud-netflix/issues[Github].
|
||||
* We monitor https://stackoverflow.com/[StackOverflow] for questions with the `spring-cloud-netflix` tag.
|
||||
* You can contact our team at https://gitter.im/spring-cloud/spring-cloud[Gitter].
|
||||
|
||||
## Commercial Support
|
||||
|
||||
Commercial Support is provided as part of the https://spring.io/support[VMware Spring Runtime] offering.
|
||||
@@ -0,0 +1,70 @@
|
||||
include::_attributes.adoc[]
|
||||
=== Disabling Spring Cloud Circuit Breaker Hystrix
|
||||
|
||||
You can disable the auto-configuration by setting `spring.cloud.circuitbreaker.hystrix.enabled`
|
||||
to `false`.
|
||||
|
||||
=== Configuring Hystrix Circuit Breakers
|
||||
|
||||
==== Default Configuration
|
||||
|
||||
To provide a default configuration for all of your circuit breakers create a `Customize` bean that is passed a
|
||||
`HystrixCircuitBreakerFactory` or `ReactiveHystrixCircuitBreakerFactory`.
|
||||
The `configureDefault` method can be used to provide a default configuration.
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public Customizer<HystrixCircuitBreakerFactory> defaultConfig() {
|
||||
return factory -> factory.configureDefault(id -> HystrixCommand.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(id))
|
||||
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
|
||||
.withExecutionTimeoutInMilliseconds(4000)));
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
===== Reactive Example
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public Customizer<ReactiveHystrixCircuitBreakerFactory> defaultConfig() {
|
||||
return factory -> factory.configureDefault(id -> HystrixObservableCommand.Setter
|
||||
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(id))
|
||||
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
|
||||
.withExecutionTimeoutInMilliseconds(4000)));
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
==== Specific Circuit Breaker Configuration
|
||||
|
||||
Similarly to providing a default configuration, you can create a `Customize` bean this is passed a
|
||||
`HystrixCircuitBreakerFactory`
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public Customizer<HystrixCircuitBreakerFactory> customizer() {
|
||||
return factory -> factory.configure(builder -> builder.commandProperties(
|
||||
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(2000)), "foo", "bar");
|
||||
}
|
||||
----
|
||||
====
|
||||
|
||||
===== Reactive Example
|
||||
|
||||
====
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public Customizer<ReactiveHystrixCircuitBreakerFactory> customizer() {
|
||||
return factory -> factory.configure(builder -> builder.commandProperties(
|
||||
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(2000)), "foo", "bar");
|
||||
}
|
||||
----
|
||||
====
|
||||
@@ -1,10 +1,14 @@
|
||||
:github-tag: master
|
||||
include::_attributes.adoc[]
|
||||
|
||||
|
||||
:github-tag: main
|
||||
:github-repo: spring-cloud/spring-cloud-netflix
|
||||
:github-raw: https://raw.github.com/{github-repo}/{github-tag}
|
||||
:github-code: https://github.com/{github-repo}/tree/{github-tag}
|
||||
:all: {asterisk}{asterisk}
|
||||
:nofooter:
|
||||
:branch: master
|
||||
:branch: main
|
||||
|
||||
= Spring Cloud Netflix
|
||||
|
||||
*{spring-cloud-version}*
|
||||
@@ -63,7 +67,9 @@ eureka:
|
||||
defaultZone: http://localhost:8761/eureka/
|
||||
----
|
||||
|
||||
In the preceding example, "defaultZone" is a magic string fallback value that provides the service URL for any client that does not express a preference (in other words, it is a useful default).
|
||||
In the preceding example, `defaultZone` is a magic string fallback value that provides the service URL for any client that does not express a preference (in other words, it is a useful default).
|
||||
|
||||
WARNING: The `defaultZone` property is case sensitive and requires camel case because the `serviceUrl` property is a `Map<String, String>`. Therefore, the `defaultZone` property does not follow the normal Spring Boot snake-case convention of `default-zone`.
|
||||
|
||||
The default application name (that is, the service ID), virtual host, and non-secure port (taken from the `Environment`) are `${spring.application.name}`, `${spring.application.name}` and `${server.port}`, respectively.
|
||||
|
||||
@@ -76,11 +82,31 @@ To disable the Eureka Discovery Client, you can set `eureka.client.enabled` to `
|
||||
|
||||
=== Authenticating with the Eureka Server
|
||||
|
||||
HTTP basic authentication is automatically added to your eureka client if one of the `eureka.client.serviceUrl.defaultZone` URLs has credentials embedded in it (curl style, as follows: `http://user:password@localhost:8761/eureka`).
|
||||
HTTP basic authentication is automatically added to your eureka client if one of the `eureka.client.serviceUrl.defaultZone` URLs has credentials embedded in it (curl style, as follows: `https://user:password@localhost:8761/eureka`).
|
||||
For more complex needs, you can create a `@Bean` of type `DiscoveryClientOptionalArgs` and inject `ClientFilter` instances into it, all of which is applied to the calls from the client to the server.
|
||||
|
||||
NOTE: Because of a limitation in Eureka, it is not possible to support per-server basic auth credentials, so only the first set that are found is used.
|
||||
|
||||
When Eureka server requires client side certificate for authentication, the client side certificate and trust store can be configured via properties, as shown in following example:
|
||||
|
||||
.application.yml
|
||||
[source,yaml]
|
||||
----
|
||||
eureka:
|
||||
client:
|
||||
tls:
|
||||
enabled: true
|
||||
key-store: <path-of-key-store>
|
||||
key-store-type: PKCS12
|
||||
key-store-password: <key-store-password>
|
||||
key-password: <key-password>
|
||||
trust-store: <path-of-trust-store>
|
||||
trust-store-type: PKCS12
|
||||
trust-store-password: <trust-store-password>
|
||||
----
|
||||
|
||||
The `eureka.client.tls.enabled` needs to be true to enable Eureka client side TLS. When `eureka.client.tls.trust-store` is omitted, a JVM default trust store is used. The default value for `eureka.client.tls.key-store-type` and `eureka.client.tls.trust-store-type` is PKCS12. When password properties are omitted, empty password is assumed.
|
||||
|
||||
=== Status Page and Health Indicator
|
||||
|
||||
The status page and health indicators for a Eureka instance default to `/info` and `/health` respectively, which are the default locations of useful endpoints in a Spring Boot Actuator application.
|
||||
@@ -200,7 +226,7 @@ public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
|
||||
A vanilla Netflix Eureka instance is registered with an ID that is equal to its host name (that is, there is only one service per host).
|
||||
Spring Cloud Eureka provides a sensible default, which is defined as follows:
|
||||
|
||||
`${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}`
|
||||
`${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}`
|
||||
|
||||
An example is `myhost:myappname:8080`.
|
||||
|
||||
@@ -331,6 +357,18 @@ When a refresh occurs clients will be unregistered from the Eureka server and th
|
||||
where all instance of a given service are not available. One way to eliminate this from happening is to disable
|
||||
the ability to refresh Eureka clients. To do this set `eureka.client.refresh.enable=false`.
|
||||
|
||||
=== Using Eureka with Spring Cloud LoadBalancer
|
||||
|
||||
We offer support for the Spring Cloud LoadBalancer `ZonePreferenceServiceInstanceListSupplier`.
|
||||
The `zone` value from the Eureka instance metadata (`eureka.instance.metadataMap.zone`) is used for setting the
|
||||
value of `spring-clod-loadbalancer-zone` property that is used to filter service instances by zone.
|
||||
|
||||
If that is missing and if the `spring.cloud.loadbalancer.eureka.approximateZoneFromHostname` flag is set to `true`,
|
||||
it can use the domain name from the server hostname as a proxy for the zone.
|
||||
|
||||
If there is no other source of zone data, then a guess is made, based on the client configuration (as opposed to the instance configuration).
|
||||
We take `eureka.client.availabilityZones`, which is a map from region name to a list of zones, and pull out the first zone for the instance's own region (that is, the `eureka.client.region`, which defaults to "us-east-1", for compatibility with native Netflix).
|
||||
|
||||
[[spring-cloud-eureka-server]]
|
||||
== Service Discovery: Eureka Server
|
||||
|
||||
@@ -450,7 +488,7 @@ eureka:
|
||||
hostname: peer1
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://peer2/eureka/
|
||||
defaultZone: https://peer2/eureka/
|
||||
|
||||
---
|
||||
spring:
|
||||
@@ -460,7 +498,7 @@ eureka:
|
||||
hostname: peer2
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://peer1/eureka/
|
||||
defaultZone: https://peer1/eureka/
|
||||
----
|
||||
|
||||
In the preceding example, we have a YAML file that can be used to run the same server on two hosts (`peer1` and `peer2`) by running it in different Spring profiles.
|
||||
@@ -479,7 +517,7 @@ the registrations amongst themselves.
|
||||
eureka:
|
||||
client:
|
||||
serviceUrl:
|
||||
defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
|
||||
defaultZone: https://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
|
||||
|
||||
---
|
||||
spring:
|
||||
@@ -541,6 +579,34 @@ For more information on CSRF see the https://docs.spring.io/spring-security/site
|
||||
|
||||
A demo Eureka Server can be found in the Spring Cloud Samples https://github.com/spring-cloud-samples/eureka/tree/Eureka-With-Security[repo].
|
||||
|
||||
=== Disabling Ribbon with Eureka Server and Client starters
|
||||
|
||||
`spring-cloud-starter-netflix-eureka-server` and `spring-cloud-starter-netflix-eureka-client` come along with
|
||||
a `spring-cloud-starter-netflix-ribbon`. Since Ribbon load-balancer is now in maintenance mode,
|
||||
we suggest switching to using the Spring Cloud LoadBalancer, also included in Eureka starters, instead.
|
||||
|
||||
In order to that, you can set the value of `spring.cloud.loadbalancer.ribbon.enabled` property to `false`.
|
||||
|
||||
You can then also exclude ribbon-related dependencies from Eureka starters in your build files, like so:
|
||||
|
||||
[source,xml,indent=0]
|
||||
----
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-ribbon</artifactId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-eureka</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
----
|
||||
|
||||
=== JDK 11 Support
|
||||
|
||||
The JAXB modules which the Eureka server depends upon were removed in JDK 11. If you intend to use JDK 11
|
||||
@@ -554,6 +620,10 @@ when running a Eureka server you must include these dependencies in your POM or
|
||||
</dependency>
|
||||
----
|
||||
|
||||
== Circuit Breaker: Spring Cloud Circuit Breaker With Hystrix
|
||||
|
||||
include::spring-cloud-circuitbreaker-hystrix.adoc[]
|
||||
|
||||
== Circuit Breaker: Hystrix Clients
|
||||
|
||||
Netflix has created a library called https://github.com/Netflix/Hystrix[Hystrix] that implements the https://martinfowler.com/bliki/CircuitBreaker.html[circuit breaker pattern].
|
||||
@@ -703,6 +773,10 @@ Then visit `/hystrix` and point the dashboard to an individual instance's `/hyst
|
||||
NOTE: When connecting to a `/hystrix.stream` endpoint that uses HTTPS, the certificate used by the server must be trusted by the JVM.
|
||||
If the certificate is not trusted, you must import the certificate into the JVM in order for the Hystrix Dashboard to make a successful connection to the stream endpoint.
|
||||
|
||||
NOTE: In order to use the `/proxy.stream` endpoint you must configure a list of hosts to allow connections to. To set the
|
||||
list of allowed hosts use `hystrix.dashboard.proxyStreamAllowList`. You can use an Ant-style pattern in the host name to
|
||||
match against a wider range of host names.
|
||||
|
||||
=== Turbine
|
||||
|
||||
Looking at an individual instance's Hystrix data is not very useful in terms of the overall health of the system. https://github.com/Netflix/Turbine[Turbine] is an application that aggregates all of the relevant `/hystrix.stream` endpoints into a combined `/turbine.stream` for use in the Hystrix Dashboard.
|
||||
@@ -806,7 +880,7 @@ The Turbine Stream server requires the use of Spring Webflux, therefore `spring-
|
||||
By default `spring-boot-starter-webflux` is included when adding `spring-cloud-starter-netflix-turbine-stream` to your application.
|
||||
|
||||
You can then point the Hystrix Dashboard to the Turbine Stream Server instead of individual Hystrix streams.
|
||||
If Turbine Stream is running on port 8989 on myhost, then put `http://myhost:8989` in the stream input field in the Hystrix Dashboard.
|
||||
If Turbine Stream is running on port 8989 on myhost, then put `https://myhost:8989` in the stream input field in the Hystrix Dashboard.
|
||||
Circuits are prefixed by their respective `serviceId`, followed by a dot (`.`), and then the circuit name.
|
||||
|
||||
Spring Cloud provides a `spring-cloud-starter-netflix-turbine-stream` that has all the dependencies you need to get a Turbine Stream server running.
|
||||
@@ -866,7 +940,7 @@ public class TestConfiguration {
|
||||
In this case, the client is composed from the components already in `RibbonClientConfiguration`, together with any in `CustomConfiguration` (where the latter generally overrides the former).
|
||||
// TODO "generally"? We should link to the rules that dictate this behavior.
|
||||
|
||||
WARNING: The `CustomConfiguration` clas must be a `@Configuration` class, but take care that it is not in a `@ComponentScan` for the main application context.
|
||||
WARNING: The `CustomConfiguration` class must be a `@Configuration` class, but take care that it is not in a `@ComponentScan` for the main application context.
|
||||
Otherwise, it is shared by all the `@RibbonClients`. If you use `@ComponentScan` (or `@SpringBootApplication`), you need to take steps to avoid it being included (for instance, you can put it in a separate, non-overlapping package or specify the packages to scan explicitly in the `@ComponentScan`).
|
||||
|
||||
The following table shows the beans that Spring Cloud Netflix provides by default for Ribbon:
|
||||
@@ -888,7 +962,7 @@ Creating a bean of one of those type and placing it in a `@RibbonClient` configu
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../../../spring-cloud-netflix-ribbon/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsPreprocessorIntegrationTests.java[tags=sample_override_ribbon_config,indent=0]
|
||||
include::{project-root}/spring-cloud-netflix-ribbon/src/test/java/org/springframework/cloud/netflix/ribbon/RibbonClientsPreprocessorIntegrationTests.java[tags=sample_override_ribbon_config,indent=0]
|
||||
----
|
||||
|
||||
The include statement in the preceding example replaces `NoOpPing` with `PingUrl` and provides a custom `serverListFilter`.
|
||||
@@ -899,7 +973,7 @@ A default configuration can be provided for all Ribbon Clients by using the `@Ri
|
||||
|
||||
[source,java,indent=0]
|
||||
----
|
||||
include::../../../../spring-cloud-netflix-ribbon/src/test/java/org/springframework/cloud/netflix/ribbon/test/RibbonClientDefaultConfigurationTestsConfig.java[tags=sample_default_ribbon_config,indent=0]
|
||||
include::{project-root}/spring-cloud-netflix-ribbon/src/test/java/org/springframework/cloud/netflix/ribbon/test/RibbonClientDefaultConfigurationTestsConfig.java[tags=sample_default_ribbon_config,indent=0]
|
||||
|
||||
----
|
||||
|
||||
@@ -987,7 +1061,7 @@ public class MyClass {
|
||||
|
||||
public void doStuff() {
|
||||
ServiceInstance instance = loadBalancer.choose("stores");
|
||||
URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
|
||||
URI storesUri = URI.create(String.format("https://%s:%s", instance.getHost(), instance.getPort()));
|
||||
// ... do something with the URI
|
||||
}
|
||||
}
|
||||
@@ -1326,7 +1400,7 @@ path, rendering the `users` path unreachable.
|
||||
|
||||
The default HTTP client used by Zuul is now backed by the Apache HTTP Client instead of the deprecated Ribbon `RestClient`.
|
||||
To use `RestClient` or `okhttp3.OkHttpClient`, set `ribbon.restclient.enabled=true` or `ribbon.okhttp.enabled=true`, respectively.
|
||||
If you would like to customize the Apache HTTP client or the OK HTTP client, provide a bean of type `ClosableHttpClient` or `OkHttpClient`.
|
||||
If you would like to customize the Apache HTTP client or the OK HTTP client, provide a bean of type `CloseableHttpClient` or `OkHttpClient`.
|
||||
|
||||
=== Cookies and Sensitive Headers
|
||||
|
||||
@@ -1546,7 +1620,7 @@ The following example maps all paths in "/api/{all}" to the Zuul filter chain:
|
||||
=== Disable Zuul Filters
|
||||
|
||||
Zuul for Spring Cloud comes with a number of `ZuulFilter` beans enabled by default in both proxy and server mode.
|
||||
See https://github.com/spring-cloud/spring-cloud-netflix/tree/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters[the Zuul filters package] for the list of filters that you can enable.
|
||||
See https://github.com/spring-cloud/spring-cloud-netflix/tree/main/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters[the Zuul filters package] for the list of filters that you can enable.
|
||||
If you want to disable one, set `zuul.<SimpleClassName>.<filterType>.disable=true`.
|
||||
By convention, the package after `filters` is the Zuul filter type.
|
||||
For example to disable `org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter`, set `zuul.SendResponseFilter.post.disable=true`.
|
||||
@@ -1751,7 +1825,7 @@ This path can be changed with the `zuul.servlet-path` property.
|
||||
To pass information between filters, Zuul uses a https://github.com/Netflix/zuul/blob/1.x/zuul-core/src/main/java/com/netflix/zuul/context/RequestContext.java[`RequestContext`].
|
||||
Its data is held in a `ThreadLocal` specific to each request.
|
||||
Information about where to route requests, errors, and the actual `HttpServletRequest` and `HttpServletResponse` are stored there.
|
||||
The `RequestContext` extends `ConcurrentHashMap`, so anything can be stored in the context. https://github.com/spring-cloud/spring-cloud-netflix/blob/master/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/support/FilterConstants.java[`FilterConstants`] contains the keys used by the filters installed by Spring Cloud Netflix (more on these <<zuul-developer-guide-enable-filters,later>>).
|
||||
The `RequestContext` extends `ConcurrentHashMap`, so anything can be stored in the context. https://github.com/spring-cloud/spring-cloud-netflix/blob/main/spring-cloud-netflix-zuul/src/main/java/org/springframework/cloud/netflix/zuul/filters/support/FilterConstants.java[`FilterConstants`] contains the keys used by the filters installed by Spring Cloud Netflix (more on these <<zuul-developer-guide-enable-filters,later>>).
|
||||
|
||||
==== `@EnableZuulProxy` vs. `@EnableZuulServer`
|
||||
|
||||
@@ -1768,7 +1842,7 @@ The following filters are installed (as normal Spring Beans):
|
||||
** `ServletDetectionFilter`: Detects whether the request is through the Spring Dispatcher. Sets a boolean with a key of `FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY`.
|
||||
** `FormBodyWrapperFilter`: Parses form data and re-encodes it for downstream requests.
|
||||
** `DebugFilter`: If the `debug` request parameter is set, sets `RequestContext.setDebugRouting()` and `RequestContext.setDebugRequest()` to `true`.
|
||||
*Route filters:
|
||||
* Route filters:
|
||||
** `SendForwardFilter`: Forwards requests by using the Servlet `RequestDispatcher`. The forwarding location is stored in the `RequestContext` attribute, `FilterConstants.FORWARD_TO_KEY`. This is useful for forwarding to endpoints in the current application.
|
||||
* Post filters:
|
||||
** `SendResponseFilter`: Writes responses from proxied requests to the current response.
|
||||
@@ -2050,14 +2124,14 @@ The following example response for `/hosts/customers` returns two instances on d
|
||||
{
|
||||
"host": "myhost",
|
||||
"port": 9000,
|
||||
"uri": "http://myhost:9000",
|
||||
"uri": "https://myhost:9000",
|
||||
"serviceId": "CUSTOMERS",
|
||||
"secure": false
|
||||
},
|
||||
{
|
||||
"host": "myhost2",
|
||||
"port": 9000,
|
||||
"uri": "http://myhost2:9000",
|
||||
"uri": "https://myhost2:9000",
|
||||
"serviceId": "CUSTOMERS",
|
||||
"secure": false
|
||||
}
|
||||
@@ -2129,6 +2203,8 @@ See the https://github.com/Netflix/ribbon/wiki/Getting-Started#the-properties-fi
|
||||
WARNING: Enabling `client.ribbon.OkToRetryOnAllOperations` includes retrying POST requests, which can have an impact
|
||||
on the server's resources, due to the buffering of the request body.
|
||||
|
||||
CAUTION: The property names are case-sensitive, and since some of these properties are defined in the Netflix Ribbon project, they are in Pascal Case and the ones from Spring Cloud are in Camel Case.
|
||||
|
||||
In addition, you may want to retry requests when certain status codes are returned in the response.
|
||||
You can list the response codes you would like the Ribbon client to retry by setting the `clientName.ribbon.retryableStatusCodes` property, as shown in the following example:
|
||||
|
||||
@@ -2152,8 +2228,8 @@ You can also disable retry functionality on a route-by-route basis by setting `z
|
||||
|
||||
Spring Cloud Netflix automatically creates the HTTP client used by Ribbon, Feign, and Zuul for you.
|
||||
However, you can also provide your own HTTP clients customized as you need them to be.
|
||||
To do so, you can create a bean of type `ClosableHttpClient` if you
|
||||
are using the Apache Http Cient or `OkHttpClient` if you are using OK HTTP.
|
||||
To do so, you can create a bean of type `CloseableHttpClient` if you
|
||||
are using the Apache HTTP Client or `OkHttpClient` if you are using OK HTTP.
|
||||
|
||||
NOTE: When you create your own HTTP client, you are also responsible for implementing the correct connection management strategies for these clients.
|
||||
Doing so improperly can result in resource management issues.
|
||||
@@ -2161,3 +2237,9 @@ Doing so improperly can result in resource management issues.
|
||||
|
||||
include::maintenance-mode.adoc[]
|
||||
|
||||
== Configuration properties
|
||||
|
||||
To see the list of all Spring Cloud Netflix related configuration properties please check link:appendix.html[the Appendix page].
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
@REM "License"); you may not use this file except in compliance
|
||||
@REM with the License. You may obtain a copy of the License at
|
||||
@REM
|
||||
@REM http://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM https://www.apache.org/licenses/LICENSE-2.0
|
||||
@REM
|
||||
@REM Unless required by applicable law or agreed to in writing,
|
||||
@REM software distributed under the License is distributed on an
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Spring Cloud Netflix</name>
|
||||
<description>Spring Cloud Netflix</description>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-build</artifactId>
|
||||
<version>2.1.9.RELEASE</version>
|
||||
<version>2.3.5.RELEASE</version>
|
||||
<relativePath />
|
||||
</parent>
|
||||
<scm>
|
||||
@@ -21,27 +21,25 @@
|
||||
</scm>
|
||||
<properties>
|
||||
<bintray.package>netflix</bintray.package>
|
||||
<spring-cloud-commons.version>2.1.4.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>2.1.5.RELEASE</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Fishtown.SR4</spring-cloud-stream.version>
|
||||
<spring-cloud-commons.version>2.2.9.RELEASE</spring-cloud-commons.version>
|
||||
<spring-cloud-config.version>2.2.8.RELEASE</spring-cloud-config.version>
|
||||
<spring-cloud-stream.version>Horsham.SR13</spring-cloud-stream.version>
|
||||
<!-- Has to be a stable version (not one that depends on this version of netflix): -->
|
||||
<donotreplacespring-cloud-contract.version>2.1.3.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
<donotreplacespring-cloud-contract.version>2.2.4.RELEASE</donotreplacespring-cloud-contract.version>
|
||||
|
||||
<!-- Sonar -->
|
||||
<sonar.java.coveragePlugin>jacoco</sonar.java.coveragePlugin>
|
||||
<sonar.dynamicAnalysis>reuseReports</sonar.dynamicAnalysis>
|
||||
<sonar.jacoco.reportPath>${project.basedir}/../target/jacoco.exec</sonar.jacoco.reportPath>
|
||||
<sonar.language>java</sonar.language>
|
||||
<maven-checkstyle-plugin.includeTestSourceDirectory>true
|
||||
</maven-checkstyle-plugin.includeTestSourceDirectory>
|
||||
<maven-checkstyle-plugin.failsOnError>true</maven-checkstyle-plugin.failsOnError>
|
||||
<maven-checkstyle-plugin.failOnViolation>true
|
||||
</maven-checkstyle-plugin.failOnViolation>
|
||||
<eureka-jersey.version>1.19.1</eureka-jersey.version>
|
||||
<xstream.version>1.4.11.1</xstream.version>
|
||||
<eureka-jersey.version>1.19.4</eureka-jersey.version>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>flatten-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-eclipse-plugin</artifactId>
|
||||
@@ -171,11 +169,6 @@
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
<version>${eureka-jersey.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<version>${xstream.version}</version>
|
||||
</dependency>
|
||||
<!-- end eureka deps -->
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
@@ -205,6 +198,7 @@
|
||||
<module>spring-cloud-netflix-ribbon</module>
|
||||
<module>spring-cloud-starter-netflix</module>
|
||||
<module>spring-cloud-netflix-hystrix</module>
|
||||
<module>spring-cloud-netflix-eureka-client-tls-tests</module>
|
||||
<module>docs</module>
|
||||
</modules>
|
||||
<profiles>
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
|
||||
(cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install -B -Pdocs ${@})
|
||||
./mvnw clean install -B -Pdocs ${@}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
set -o errexit
|
||||
|
||||
(cd spring-cloud-netflix-hystrix-contract && ../mvnw clean install -B -Pdocs -DskipTests)
|
||||
./mvnw clean install -B -Pdocs -DskipTests -fae
|
||||
|
||||
@@ -4,7 +4,7 @@ set -o errexit
|
||||
|
||||
mkdir -p target
|
||||
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/brewery/master/runAcceptanceTests.sh"
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/brewery/main/runAcceptanceTests.sh"
|
||||
AT_WHAT_TO_TEST="EUREKA"
|
||||
|
||||
cd target
|
||||
@@ -18,7 +18,7 @@ echo "Killing all running apps"
|
||||
|
||||
./runAcceptanceTests.sh -t "${AT_WHAT_TO_TEST}" --killattheend
|
||||
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/tests/master/scripts/runTests.sh"
|
||||
SCRIPT_URL="https://raw.githubusercontent.com/spring-cloud-samples/tests/main/scripts/runTests.sh"
|
||||
|
||||
curl "${SCRIPT_URL}" --output runIntegrationTests.sh
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
|
||||
|
||||
+11
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -41,7 +41,7 @@ import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnEnabledEndpoint;
|
||||
import org.springframework.boot.actuate.autoconfigure.endpoint.condition.ConditionalOnAvailableEndpoint;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureOrder;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
@@ -51,6 +51,7 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationListener;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.core.env.Environment;
|
||||
@@ -65,8 +66,10 @@ import static com.netflix.config.ConfigurationManager.URL_CONFIG_NAME;
|
||||
|
||||
/**
|
||||
* @author Spencer Gibb
|
||||
* @author Liang Yong
|
||||
*/
|
||||
@Configuration
|
||||
@Lazy(false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass({ ConcurrentCompositeConfiguration.class,
|
||||
ConfigurationBuilder.class })
|
||||
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
|
||||
@@ -194,20 +197,21 @@ public class ArchaiusAutoConfiguration {
|
||||
ReflectionUtils.setField(field, null, value);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Health.class)
|
||||
protected static class ArchaiusEndpointConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnEnabledEndpoint
|
||||
@ConditionalOnAvailableEndpoint
|
||||
protected ArchaiusEndpoint archaiusEndpoint() {
|
||||
return new ArchaiusEndpoint();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent", matchIfMissing = true)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "archaius.propagate.environmentChangedEvent",
|
||||
matchIfMissing = true)
|
||||
@ConditionalOnClass(EnvironmentChangeEvent.class)
|
||||
protected static class PropagateEventsConfiguration
|
||||
implements ApplicationListener<EnvironmentChangeEvent> {
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.archaius;
|
||||
|
||||
import com.netflix.config.DynamicProperty;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* @author Liang Yong
|
||||
*/
|
||||
|
||||
@SpringBootTest(classes = LazyLoadConfigurationTests.TestConfig.class,
|
||||
properties = { "client.ribbon.listOfServers=foo.com,bar.com",
|
||||
"spring.main.lazy-initialization=true" })
|
||||
public class LazyLoadConfigurationTests {
|
||||
|
||||
@Test
|
||||
public void enableLazyInitialization() {
|
||||
DynamicProperty instance = DynamicProperty
|
||||
.getInstance("client.ribbon.listOfServers");
|
||||
assertThat(instance.getString()).isEqualTo("foo.com,bar.com");
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
static class TestConfig {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
/**
|
||||
* @author Alexandru-George Burghelea
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class TestArchaiusExternalConfiguration {
|
||||
|
||||
@Bean
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-concurrency-limits</artifactId>
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,9 @@ import io.micrometer.core.instrument.Tags;
|
||||
* A Micrometer-specific {@link MetricRegistry} implementation.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class MicrometerMetricRegistry implements MetricRegistry {
|
||||
|
||||
private final MeterRegistry meterRegistry;
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,7 +29,9 @@ import org.springframework.web.server.WebFilterChain;
|
||||
* {@link Limiter} to handle requests.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConcurrencyLimitsWebFilter implements WebFilter {
|
||||
|
||||
private final Limiter<ServerWebExchange> limiter;
|
||||
|
||||
+4
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,8 +35,10 @@ import org.springframework.web.server.WebFilter;
|
||||
* Reactive autoconfiguration class for registering Netflix {@link Limiter} bean.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@Deprecated
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = Type.REACTIVE)
|
||||
@ConditionalOnClass({ WebFilter.class, Mono.class })
|
||||
public class ReactiveConcurrencyLimitsAutoConfiguration {
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,9 @@ import org.springframework.web.server.ServerWebExchange;
|
||||
* Builder for ServerWebExchange Limiter.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class ServerWebExchangeLimiterBuilder extends
|
||||
AbstractPartitionedLimiter.Builder<ServerWebExchangeLimiterBuilder, ServerWebExchange> {
|
||||
|
||||
|
||||
+3
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,7 +34,9 @@ import org.springframework.web.servlet.ModelAndView;
|
||||
* {@link Limiter} to handle requests.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Deprecated
|
||||
public class ConcurrencyLimitsHandlerInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final Limiter<HttpServletRequest> limiter;
|
||||
|
||||
+5
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,10 +39,12 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
* MVC autoconfiguration class for registering Netflix {@link Limiter} bean.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @deprecated to be removed in 3.0.0
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnWebApplication(type = Type.SERVLET)
|
||||
@ConditionalOnClass({ HttpServletRequest.class, HandlerInterceptor.class })
|
||||
@Deprecated
|
||||
public class MvcConcurrencyLimitsAutoConfiguration implements WebMvcConfigurer {
|
||||
|
||||
private final ObjectProvider<Consumer<ServletLimiterBuilder>> configurerProvider;
|
||||
@@ -62,7 +64,7 @@ public class MvcConcurrencyLimitsAutoConfiguration implements WebMvcConfigurer {
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
protected static class HandlerInterceptorConfiguration implements WebMvcConfigurer {
|
||||
|
||||
@Autowired
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -66,7 +66,7 @@ public class AbstractConcurrencyLimitsTests {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@RestController
|
||||
protected static class HelloControllerConfiguration {
|
||||
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -37,7 +37,8 @@ import org.springframework.web.reactive.function.client.WebClient;
|
||||
import static org.springframework.boot.test.context.SpringBootTest.WebEnvironment.RANDOM_PORT;
|
||||
|
||||
@RunWith(SpringRunner.class)
|
||||
@SpringBootTest(properties = "logging.level.reactor.netty=DEBUG", webEnvironment = RANDOM_PORT)
|
||||
@SpringBootTest(properties = "logging.level.reactor.netty=DEBUG",
|
||||
webEnvironment = RANDOM_PORT)
|
||||
public class ConcurrencyLimitsHandlerInterceptorTests
|
||||
extends AbstractConcurrencyLimitsTests {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-core</artifactId>
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,7 +27,7 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @deprecated Module spring-cloud-netflix-core is deprecated as of 2.1.0, use
|
||||
* spring-cloud-netflix-hystrix instead.
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@Deprecated
|
||||
public class CoreAutoConfiguration {
|
||||
|
||||
|
||||
@@ -5,20 +5,20 @@
|
||||
<parent>
|
||||
<artifactId>spring-cloud-dependencies-parent</artifactId>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<version>2.1.9.RELEASE</version>
|
||||
<version>2.3.5.RELEASE</version>
|
||||
<relativePath/>
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-dependencies</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>spring-cloud-netflix-dependencies</name>
|
||||
<description>Spring Cloud Netflix Dependencies</description>
|
||||
<properties>
|
||||
<archaius.version>0.7.6</archaius.version>
|
||||
<archaius.version>0.7.7</archaius.version>
|
||||
<concurrency-limits.version>0.1.12</concurrency-limits.version>
|
||||
<eureka.version>1.9.13</eureka.version>
|
||||
<eureka.version>1.10.14</eureka.version>
|
||||
<eventbus.version>0.3.0</eventbus.version>
|
||||
<hystrix.version>1.5.18</hystrix.version>
|
||||
<okhttp3.version>3.8.1</okhttp3.version>
|
||||
<ribbon.version>2.3.0</ribbon.version>
|
||||
<servo.version>0.12.21</servo.version>
|
||||
<zuul.version>1.3.1</zuul.version>
|
||||
@@ -56,11 +56,6 @@
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix-contract</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
|
||||
@@ -370,14 +365,9 @@
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>okhttp</artifactId>
|
||||
<version>${okhttp3.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.squareup.okhttp3</groupId>
|
||||
<artifactId>logging-interceptor</artifactId>
|
||||
<version>${okhttp3.version}</version>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-eventbus</artifactId>
|
||||
<version>${eventbus.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client-tls-tests</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Cloud Netflix Eureka Client TLS Tests</name>
|
||||
<description>Spring Cloud Netflix Eureka Client TLS Tests</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-eureka-server</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-commons</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-context</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.retry</groupId>
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-aop</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-autoconfigure-processor</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.junit.vintage</groupId>
|
||||
<artifactId>junit-vintage-engine</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk15on</artifactId>
|
||||
<version>1.68</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.xml</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.1</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
+125
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import org.springframework.util.SocketUtils;
|
||||
|
||||
public class AppRunner implements AutoCloseable {
|
||||
|
||||
private Class<?> appClass;
|
||||
|
||||
private Map<String, String> props;
|
||||
|
||||
private ConfigurableApplicationContext app;
|
||||
|
||||
public AppRunner(Class<?> appClass) {
|
||||
this.appClass = appClass;
|
||||
props = new LinkedHashMap<>();
|
||||
}
|
||||
|
||||
public void property(String key, String value) {
|
||||
props.put(key, value);
|
||||
}
|
||||
|
||||
public void start() {
|
||||
if (app == null) {
|
||||
SpringApplicationBuilder builder = new SpringApplicationBuilder(appClass);
|
||||
builder.properties("spring.jmx.enabled=false");
|
||||
builder.properties(String.format("server.port=%d", availabeTcpPort()));
|
||||
builder.properties(props());
|
||||
|
||||
app = builder.build().run();
|
||||
}
|
||||
}
|
||||
|
||||
private int availabeTcpPort() {
|
||||
return SocketUtils.findAvailableTcpPort();
|
||||
}
|
||||
|
||||
private String[] props() {
|
||||
List<String> result = new ArrayList<>();
|
||||
|
||||
for (String key : props.keySet()) {
|
||||
String value = props.get(key);
|
||||
result.add(String.format("%s=%s", key, value));
|
||||
}
|
||||
|
||||
return result.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (app != null) {
|
||||
app.stop();
|
||||
app = null;
|
||||
}
|
||||
}
|
||||
|
||||
public ConfigurableApplicationContext app() {
|
||||
return app;
|
||||
}
|
||||
|
||||
public String getProperty(String key) {
|
||||
return app.getEnvironment().getProperty(key);
|
||||
}
|
||||
|
||||
public <T> T getBean(Class<T> type) {
|
||||
return app.getBean(type);
|
||||
}
|
||||
|
||||
public ApplicationContext parent() {
|
||||
return app.getParent();
|
||||
}
|
||||
|
||||
public <T> Map<String, T> getParentBeans(Class<T> type) {
|
||||
return parent().getBeansOfType(type);
|
||||
}
|
||||
|
||||
public int port() {
|
||||
if (app == null) {
|
||||
throw new RuntimeException("App is not running.");
|
||||
}
|
||||
return app.getEnvironment().getProperty("server.port", Integer.class, -1);
|
||||
}
|
||||
|
||||
public String root() {
|
||||
if (app == null) {
|
||||
throw new RuntimeException("App is not running.");
|
||||
}
|
||||
|
||||
String protocol = tlsEnabled() ? "https" : "http";
|
||||
return String.format("%s://localhost:%d/", protocol, port());
|
||||
}
|
||||
|
||||
private boolean tlsEnabled() {
|
||||
return app.getEnvironment().getProperty("server.ssl.enabled", Boolean.class,
|
||||
false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
stop();
|
||||
}
|
||||
|
||||
}
|
||||
+93
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.security.KeyStore;
|
||||
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
public abstract class BaseCertTest {
|
||||
|
||||
protected static final String KEY_STORE_PASSWORD = "test-key-store-password";
|
||||
|
||||
protected static final String KEY_PASSWORD = "test-key-password";
|
||||
|
||||
protected static final String WRONG_PASSWORD = "test-wrong-password";
|
||||
|
||||
protected static File caCert;
|
||||
|
||||
protected static File wrongCaCert;
|
||||
|
||||
protected static File serverCert;
|
||||
|
||||
protected static File clientCert;
|
||||
|
||||
protected static File wrongClientCert;
|
||||
|
||||
protected BaseCertTest() {
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void createCertificates() throws Exception {
|
||||
KeyTool tool = new KeyTool();
|
||||
|
||||
KeyAndCert ca = tool.createCA("MyCA");
|
||||
KeyAndCert server = ca.sign("server");
|
||||
KeyAndCert client = ca.sign("client");
|
||||
|
||||
caCert = saveCert(ca);
|
||||
serverCert = saveKeyAndCert(server);
|
||||
clientCert = saveKeyAndCert(client);
|
||||
|
||||
KeyAndCert wrongCa = tool.createCA("WrongCA");
|
||||
KeyAndCert wrongClient = wrongCa.sign("client");
|
||||
|
||||
wrongCaCert = saveCert(wrongCa);
|
||||
wrongClientCert = saveKeyAndCert(wrongClient);
|
||||
}
|
||||
|
||||
private static File saveKeyAndCert(KeyAndCert keyCert) throws Exception {
|
||||
return saveKeyStore(keyCert.subject(),
|
||||
() -> keyCert.storeKeyAndCert(KEY_PASSWORD));
|
||||
}
|
||||
|
||||
private static File saveCert(KeyAndCert keyCert) throws Exception {
|
||||
return saveKeyStore(keyCert.subject(), () -> keyCert.storeCert());
|
||||
}
|
||||
|
||||
private static File saveKeyStore(String prefix, KeyStoreSupplier func)
|
||||
throws Exception {
|
||||
File result = File.createTempFile(prefix, ".p12");
|
||||
result.deleteOnExit();
|
||||
|
||||
try (OutputStream output = new FileOutputStream(result)) {
|
||||
KeyStore store = func.createKeyStore();
|
||||
store.store(output, KEY_STORE_PASSWORD.toCharArray());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
interface KeyStoreSupplier {
|
||||
|
||||
KeyStore createKeyStore() throws Exception;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.function.BooleanSupplier;
|
||||
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
|
||||
public class EurekaClientRunner extends AppRunner {
|
||||
|
||||
public EurekaClientRunner(Class<?> appClass, AppRunner server) {
|
||||
super(appClass);
|
||||
|
||||
property("eureka.client.registerWithEureka", "false");
|
||||
property("eureka.client.fetchRegistry", "true");
|
||||
property("eureka.client.serviceUrl.defaultZone", server.root() + "eureka/");
|
||||
property("eureka.client.refresh.enable", "true");
|
||||
}
|
||||
|
||||
public EurekaClientRunner(Class<?> appClass, AppRunner server, String service) {
|
||||
this(appClass, server);
|
||||
property("eureka.client.registerWithEureka", "true");
|
||||
property("spring.application.name", service);
|
||||
}
|
||||
|
||||
public void enableTls() {
|
||||
property("eureka.client.tls.enabled", "true");
|
||||
}
|
||||
|
||||
public void disableTls() {
|
||||
property("eureka.client.tls.enabled", "false");
|
||||
}
|
||||
|
||||
public void setKeyStore(File keyStore, String keyStorePassword, String keyPassword) {
|
||||
property("eureka.client.tls.key-store", pathOf(keyStore));
|
||||
property("eureka.client.tls.key-store-password", keyStorePassword);
|
||||
property("eureka.client.tls.key-password", keyPassword);
|
||||
}
|
||||
|
||||
public void setKeyStore(File keyStore) {
|
||||
property("eureka.client.tls.key-store", pathOf(keyStore));
|
||||
}
|
||||
|
||||
public void setTrustStore(File trustStore, String password) {
|
||||
property("eureka.client.tls.trust-store", pathOf(trustStore));
|
||||
property("eureka.client.tls.trust-store-password", password);
|
||||
}
|
||||
|
||||
public void setTrustStore(File trustStore) {
|
||||
property("eureka.client.tls.trust-store", pathOf(trustStore));
|
||||
}
|
||||
|
||||
private String pathOf(File file) {
|
||||
return String.format("file:%s", file.getAbsolutePath());
|
||||
}
|
||||
|
||||
public void waitServiceViaEureka(int seconds) {
|
||||
assertInSeconds(() -> foundServiceViaEureka(), seconds);
|
||||
}
|
||||
|
||||
private void assertInSeconds(BooleanSupplier assertion, int seconds) {
|
||||
long start = System.currentTimeMillis();
|
||||
long limit = 1000L * seconds;
|
||||
long duration = 0;
|
||||
|
||||
do {
|
||||
if (assertion.getAsBoolean()) {
|
||||
return;
|
||||
}
|
||||
duration = System.currentTimeMillis() - start;
|
||||
Thread.yield();
|
||||
|
||||
}
|
||||
while (duration < limit);
|
||||
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
public boolean foundServiceViaEureka() {
|
||||
DiscoveryClient discovery = getBean(DiscoveryClient.class);
|
||||
return !discovery.getServices().isEmpty();
|
||||
}
|
||||
|
||||
}
|
||||
+158
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanCreationException;
|
||||
import org.springframework.boot.SpringBootConfiguration;
|
||||
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
public class EurekaClientTest extends BaseCertTest {
|
||||
|
||||
private static EurekaServerRunner server;
|
||||
|
||||
private static EurekaClientRunner service;
|
||||
|
||||
@BeforeClass
|
||||
public static void setupAll() {
|
||||
startEurekaServer();
|
||||
startService();
|
||||
waitForRegistration();
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void tearDownAll() {
|
||||
stopService();
|
||||
stopEurekaServer();
|
||||
}
|
||||
|
||||
private static void startEurekaServer() {
|
||||
server = new EurekaServerRunner(TestEurekaServer.class);
|
||||
server.enableTls();
|
||||
server.setKeyStore(serverCert, KEY_STORE_PASSWORD, "server", KEY_PASSWORD);
|
||||
server.setTrustStore(caCert, KEY_STORE_PASSWORD);
|
||||
|
||||
server.start();
|
||||
}
|
||||
|
||||
private static void stopEurekaServer() {
|
||||
server.stop();
|
||||
}
|
||||
|
||||
private static void startService() {
|
||||
service = new EurekaClientRunner(TestApp.class, server, "testservice");
|
||||
enableTlsClient(service);
|
||||
service.start();
|
||||
}
|
||||
|
||||
private static void stopService() {
|
||||
service.stop();
|
||||
}
|
||||
|
||||
private static void waitForRegistration() {
|
||||
try (EurekaClientRunner client = createEurekaClient()) {
|
||||
enableTlsClient(client);
|
||||
client.start();
|
||||
client.waitServiceViaEureka(60);
|
||||
}
|
||||
}
|
||||
|
||||
private static EurekaClientRunner createEurekaClient() {
|
||||
return new EurekaClientRunner(TestApp.class, server);
|
||||
}
|
||||
|
||||
private static void enableTlsClient(EurekaClientRunner runner) {
|
||||
runner.enableTls();
|
||||
runner.setKeyStore(clientCert, KEY_STORE_PASSWORD, KEY_PASSWORD);
|
||||
runner.setTrustStore(caCert, KEY_STORE_PASSWORD);
|
||||
}
|
||||
|
||||
/**
|
||||
* Already proved this in waitForRegistration(). Keep this Test to express test
|
||||
* purpose explicitly.
|
||||
*/
|
||||
@Test
|
||||
public void clientCertCanWork() {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCertCannotWork() {
|
||||
try (EurekaClientRunner client = createEurekaClient()) {
|
||||
client.disableTls();
|
||||
client.start();
|
||||
assertThat(client.foundServiceViaEureka()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongCertCannotWork() {
|
||||
try (EurekaClientRunner client = createEurekaClient()) {
|
||||
enableTlsClient(client);
|
||||
client.setKeyStore(wrongClientCert);
|
||||
client.start();
|
||||
assertThat(client.foundServiceViaEureka()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void wrongPasswordCauseFailure() {
|
||||
EurekaClientRunner client = createEurekaClient();
|
||||
enableTlsClient(client);
|
||||
client.setKeyStore(clientCert, WRONG_PASSWORD, WRONG_PASSWORD);
|
||||
client.start();
|
||||
}
|
||||
|
||||
@Test(expected = BeanCreationException.class)
|
||||
public void nonExistKeyStoreCauseFailure() {
|
||||
EurekaClientRunner client = createEurekaClient();
|
||||
enableTlsClient(client);
|
||||
client.setKeyStore(new File("nonExistFile"));
|
||||
client.start();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void wrongTrustStoreCannotWork() {
|
||||
try (EurekaClientRunner client = createEurekaClient()) {
|
||||
enableTlsClient(client);
|
||||
client.setTrustStore(wrongCaCert);
|
||||
client.start();
|
||||
assertThat(client.foundServiceViaEureka()).isFalse();
|
||||
}
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
public static class TestApp {
|
||||
|
||||
}
|
||||
|
||||
@SpringBootConfiguration
|
||||
@EnableAutoConfiguration
|
||||
@EnableEurekaServer
|
||||
public static class TestEurekaServer {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class EurekaServerRunner extends AppRunner {
|
||||
|
||||
public EurekaServerRunner(Class<?> appClass) {
|
||||
super(appClass);
|
||||
|
||||
property("eureka.client.registerWithEureka", "false");
|
||||
property("eureka.client.fetchRegistry", "false");
|
||||
property("eureka.server.waitTimeInMsWhenSyncEmpty", "0");
|
||||
property("eureka.client.refresh.enable", "true");
|
||||
}
|
||||
|
||||
public void enableTls() {
|
||||
property("server.ssl.enabled", "true");
|
||||
property("server.ssl.client-auth", "need");
|
||||
}
|
||||
|
||||
public void setKeyStore(File keyStore, String keyStorePassword, String key,
|
||||
String keyPassword) {
|
||||
property("server.ssl.key-store", pathOf(keyStore));
|
||||
property("server.ssl.key-store-type", "PKCS12");
|
||||
property("server.ssl.key-store-password", keyStorePassword);
|
||||
property("server.ssl.key-alias", key);
|
||||
property("server.ssl.key-password", keyPassword);
|
||||
}
|
||||
|
||||
public void setTrustStore(File trustStore, String password) {
|
||||
property("server.ssl.trust-store", pathOf(trustStore));
|
||||
property("server.ssl.trust-store-type", "PKCS12");
|
||||
property("server.ssl.trust-store-password", password);
|
||||
}
|
||||
|
||||
private String pathOf(File file) {
|
||||
return String.format("file:%s", file.getAbsolutePath());
|
||||
}
|
||||
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyStore;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.cert.Certificate;
|
||||
import java.security.cert.X509Certificate;
|
||||
|
||||
public class KeyAndCert {
|
||||
|
||||
private KeyPair keyPair;
|
||||
|
||||
private X509Certificate certificate;
|
||||
|
||||
public KeyAndCert(KeyPair keyPair, X509Certificate certificate) {
|
||||
this.keyPair = keyPair;
|
||||
this.certificate = certificate;
|
||||
}
|
||||
|
||||
public KeyPair keyPair() {
|
||||
return keyPair;
|
||||
}
|
||||
|
||||
public PublicKey publicKey() {
|
||||
return keyPair.getPublic();
|
||||
}
|
||||
|
||||
public PrivateKey privateKey() {
|
||||
return keyPair.getPrivate();
|
||||
}
|
||||
|
||||
public X509Certificate certificate() {
|
||||
return certificate;
|
||||
}
|
||||
|
||||
public String subject() {
|
||||
String dn = certificate.getSubjectDN().getName();
|
||||
int index = dn.indexOf('=');
|
||||
return dn.substring(index + 1);
|
||||
}
|
||||
|
||||
public KeyAndCert sign(String subject) throws Exception {
|
||||
KeyTool tool = new KeyTool();
|
||||
return tool.signCertificate(subject, this);
|
||||
}
|
||||
|
||||
public KeyAndCert sign(KeyPair keyPair, String subject) throws Exception {
|
||||
KeyTool tool = new KeyTool();
|
||||
return tool.signCertificate(keyPair, subject, this);
|
||||
}
|
||||
|
||||
public KeyStore storeKeyAndCert(String keyPassword) throws Exception {
|
||||
KeyStore result = KeyStore.getInstance("PKCS12");
|
||||
result.load(null);
|
||||
|
||||
result.setKeyEntry(subject(), keyPair.getPrivate(), keyPassword.toCharArray(),
|
||||
certChain());
|
||||
return result;
|
||||
}
|
||||
|
||||
private Certificate[] certChain() {
|
||||
return new Certificate[] { certificate() };
|
||||
}
|
||||
|
||||
public KeyStore storeCert() throws Exception {
|
||||
return storeCert("PKCS12");
|
||||
}
|
||||
|
||||
public KeyStore storeCert(String storeType) throws Exception {
|
||||
KeyStore result = KeyStore.getInstance(storeType);
|
||||
result.load(null);
|
||||
|
||||
result.setCertificateEntry(subject(), certificate());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
* Copyright 2018-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.KeyPairGenerator;
|
||||
import java.security.PrivateKey;
|
||||
import java.security.PublicKey;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Date;
|
||||
|
||||
import org.bouncycastle.asn1.DERSequence;
|
||||
import org.bouncycastle.asn1.x500.X500Name;
|
||||
import org.bouncycastle.asn1.x509.BasicConstraints;
|
||||
import org.bouncycastle.asn1.x509.Extension;
|
||||
import org.bouncycastle.asn1.x509.GeneralName;
|
||||
import org.bouncycastle.asn1.x509.GeneralNames;
|
||||
import org.bouncycastle.asn1.x509.KeyUsage;
|
||||
import org.bouncycastle.cert.X509CertificateHolder;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509CertificateConverter;
|
||||
import org.bouncycastle.cert.jcajce.JcaX509v3CertificateBuilder;
|
||||
import org.bouncycastle.operator.ContentSigner;
|
||||
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
|
||||
|
||||
public class KeyTool {
|
||||
|
||||
private static final long ONE_DAY = 1000L * 60L * 60L * 24L;
|
||||
|
||||
private static final long TEN_YEARS = ONE_DAY * 365L * 10L;
|
||||
|
||||
public KeyAndCert createCA(String ca) throws Exception {
|
||||
KeyPair keyPair = createKeyPair();
|
||||
X509Certificate certificate = createCert(keyPair, ca);
|
||||
return new KeyAndCert(keyPair, certificate);
|
||||
}
|
||||
|
||||
public KeyAndCert signCertificate(String subject, KeyAndCert signer)
|
||||
throws Exception {
|
||||
return signCertificate(createKeyPair(), subject, signer);
|
||||
}
|
||||
|
||||
public KeyAndCert signCertificate(KeyPair keyPair, String subject, KeyAndCert signer)
|
||||
throws Exception {
|
||||
X509Certificate certificate = createCert(keyPair.getPublic(), signer.privateKey(),
|
||||
signer.subject(), subject);
|
||||
KeyAndCert result = new KeyAndCert(keyPair, certificate);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public KeyPair createKeyPair() throws Exception {
|
||||
return createKeyPair(1024);
|
||||
}
|
||||
|
||||
public KeyPair createKeyPair(int keySize) throws Exception {
|
||||
KeyPairGenerator gen = KeyPairGenerator.getInstance("RSA");
|
||||
gen.initialize(keySize, new SecureRandom());
|
||||
return gen.generateKeyPair();
|
||||
}
|
||||
|
||||
public X509Certificate createCert(KeyPair keyPair, String ca) throws Exception {
|
||||
JcaX509v3CertificateBuilder builder = certBuilder(keyPair.getPublic(), ca, ca);
|
||||
builder.addExtension(Extension.keyUsage, true,
|
||||
new KeyUsage(KeyUsage.keyCertSign));
|
||||
builder.addExtension(Extension.basicConstraints, false,
|
||||
new BasicConstraints(true));
|
||||
|
||||
return signCert(builder, keyPair.getPrivate());
|
||||
}
|
||||
|
||||
public X509Certificate createCert(PublicKey publicKey, PrivateKey privateKey,
|
||||
String issuer, String subject) throws Exception {
|
||||
JcaX509v3CertificateBuilder builder = certBuilder(publicKey, issuer, subject);
|
||||
builder.addExtension(Extension.keyUsage, true,
|
||||
new KeyUsage(KeyUsage.digitalSignature));
|
||||
builder.addExtension(Extension.basicConstraints, false,
|
||||
new BasicConstraints(false));
|
||||
|
||||
GeneralName[] names = new GeneralName[] {
|
||||
new GeneralName(GeneralName.dNSName, "localhost") };
|
||||
builder.addExtension(Extension.subjectAlternativeName, false,
|
||||
GeneralNames.getInstance(new DERSequence(names)));
|
||||
|
||||
return signCert(builder, privateKey);
|
||||
}
|
||||
|
||||
private JcaX509v3CertificateBuilder certBuilder(PublicKey publicKey, String issuer,
|
||||
String subject) {
|
||||
X500Name issuerName = new X500Name(String.format("dc=%s", issuer));
|
||||
X500Name subjectName = new X500Name(String.format("dc=%s", subject));
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
BigInteger serialNum = BigInteger.valueOf(now);
|
||||
Date notBefore = new Date(now - ONE_DAY);
|
||||
Date notAfter = new Date(now + TEN_YEARS);
|
||||
|
||||
return new JcaX509v3CertificateBuilder(issuerName, serialNum, notBefore, notAfter,
|
||||
subjectName, publicKey);
|
||||
}
|
||||
|
||||
private X509Certificate signCert(JcaX509v3CertificateBuilder builder,
|
||||
PrivateKey privateKey) throws Exception {
|
||||
ContentSigner signer = new JcaContentSignerBuilder("SHA256WithRSA")
|
||||
.build(privateKey);
|
||||
X509CertificateHolder holder = builder.build(signer);
|
||||
|
||||
return new JcaX509CertificateConverter().getCertificate(holder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,7 +5,7 @@
|
||||
<parent>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix</artifactId>
|
||||
<version>2.1.4.RELEASE</version>
|
||||
<version>2.2.9.RELEASE</version>
|
||||
<relativePath>..</relativePath> <!-- lookup parent from repository -->
|
||||
</parent>
|
||||
<artifactId>spring-cloud-netflix-eureka-client</artifactId>
|
||||
@@ -27,6 +27,11 @@
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-webflux</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-netflix-hystrix</artifactId>
|
||||
@@ -46,6 +51,11 @@
|
||||
<artifactId>spring-cloud-config-server</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.cloud</groupId>
|
||||
<artifactId>spring-cloud-loadbalancer</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.eureka</groupId>
|
||||
<artifactId>eureka-client</artifactId>
|
||||
@@ -62,11 +72,6 @@
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<!-- Eureka core deps that are now optional -->
|
||||
<dependency>
|
||||
<groupId>com.thoughtworks.xstream</groupId>
|
||||
<artifactId>xstream</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey.contribs</groupId>
|
||||
<artifactId>jersey-apache-client4</artifactId>
|
||||
@@ -82,6 +87,11 @@
|
||||
<artifactId>ribbon</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.netflix-commons</groupId>
|
||||
<artifactId>netflix-eventbus</artifactId>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.netflix.ribbon</groupId>
|
||||
<artifactId>ribbon-core</artifactId>
|
||||
@@ -127,5 +137,10 @@
|
||||
<artifactId>spring-retry</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.projectreactor</groupId>
|
||||
<artifactId>reactor-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+37
-38
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -50,13 +50,11 @@ import org.springframework.cloud.autoconfigure.RefreshAutoConfiguration;
|
||||
import org.springframework.cloud.client.CommonsClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.actuator.HasFeatures;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationProperties;
|
||||
import org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration;
|
||||
import org.springframework.cloud.commons.util.InetUtils;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScope;
|
||||
import org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.metadata.DefaultManagementMetadataProvider;
|
||||
import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadata;
|
||||
import org.springframework.cloud.netflix.eureka.metadata.ManagementMetadataProvider;
|
||||
@@ -68,7 +66,6 @@ import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Conditional;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
import org.springframework.core.env.ConfigurableEnvironment;
|
||||
import org.springframework.util.StringUtils;
|
||||
@@ -83,17 +80,17 @@ import static org.springframework.cloud.commons.util.IdUtils.getDefaultInstanceI
|
||||
* @author Ryan Baxter
|
||||
* @author Daniel Lavoie
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@Import(DiscoveryClientOptionalArgsConfiguration.class)
|
||||
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
|
||||
CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
|
||||
@AutoConfigureAfter(name = {
|
||||
"org.springframework.cloud.netflix.eureka.config.DiscoveryClientOptionalArgsConfiguration",
|
||||
"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration",
|
||||
"org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration",
|
||||
"org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration" })
|
||||
@@ -111,15 +108,10 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = EurekaClientConfig.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
|
||||
EurekaClientConfigBean client = new EurekaClientConfigBean();
|
||||
if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) {
|
||||
// We don't register during bootstrap by default, but there will be another
|
||||
// chance later.
|
||||
client.setRegisterWithEureka(false);
|
||||
}
|
||||
return client;
|
||||
return new EurekaClientConfigBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@@ -133,7 +125,8 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils,
|
||||
ManagementMetadataProvider managementMetadataProvider) {
|
||||
String hostname = getProperty("eureka.instance.hostname");
|
||||
@@ -215,12 +208,6 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
@Bean
|
||||
public DiscoveryClient discoveryClient(EurekaClient client,
|
||||
EurekaClientConfig clientConfig) {
|
||||
return new EurekaDiscoveryClient(client, clientConfig);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EurekaServiceRegistry eurekaServiceRegistry() {
|
||||
return new EurekaServiceRegistry();
|
||||
@@ -242,14 +229,16 @@ public class EurekaClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
value = "spring.cloud.service-registry.auto-registration.enabled",
|
||||
matchIfMissing = true)
|
||||
public EurekaAutoServiceRegistration eurekaAutoServiceRegistration(
|
||||
ApplicationContext context, EurekaServiceRegistry registry,
|
||||
EurekaRegistration registration) {
|
||||
return new EurekaAutoServiceRegistration(context, registry, registration);
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnMissingRefreshScope
|
||||
protected static class EurekaClientConfiguration {
|
||||
|
||||
@@ -260,7 +249,8 @@ public class EurekaClientAutoConfiguration {
|
||||
private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = EurekaClient.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public EurekaClient eurekaClient(ApplicationInfoManager manager,
|
||||
EurekaClientConfig config) {
|
||||
return new CloudEurekaClient(manager, config, this.optionalArgs,
|
||||
@@ -268,7 +258,8 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = ApplicationInfoManager.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public ApplicationInfoManager eurekaApplicationInfoManager(
|
||||
EurekaInstanceConfig config) {
|
||||
InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
|
||||
@@ -277,18 +268,20 @@ public class EurekaClientAutoConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
value = "spring.cloud.service-registry.auto-registration.enabled",
|
||||
matchIfMissing = true)
|
||||
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
|
||||
CloudEurekaInstanceConfig instanceConfig,
|
||||
ApplicationInfoManager applicationInfoManager,
|
||||
@Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
|
||||
ApplicationInfoManager applicationInfoManager, @Autowired(
|
||||
required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
|
||||
return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager)
|
||||
.with(eurekaClient).with(healthCheckHandler).build();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnRefreshScope
|
||||
protected static class RefreshableEurekaClientConfiguration {
|
||||
|
||||
@@ -299,7 +292,8 @@ public class EurekaClientAutoConfiguration {
|
||||
private AbstractDiscoveryClientOptionalArgs<?> optionalArgs;
|
||||
|
||||
@Bean(destroyMethod = "shutdown")
|
||||
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = EurekaClient.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
@org.springframework.cloud.context.config.annotation.RefreshScope
|
||||
@Lazy
|
||||
public EurekaClient eurekaClient(ApplicationInfoManager manager,
|
||||
@@ -326,7 +320,8 @@ public class EurekaClientAutoConfiguration {
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnMissingBean(value = ApplicationInfoManager.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
@org.springframework.cloud.context.config.annotation.RefreshScope
|
||||
@Lazy
|
||||
public ApplicationInfoManager eurekaApplicationInfoManager(
|
||||
@@ -338,11 +333,13 @@ public class EurekaClientAutoConfiguration {
|
||||
@Bean
|
||||
@org.springframework.cloud.context.config.annotation.RefreshScope
|
||||
@ConditionalOnBean(AutoServiceRegistrationProperties.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
|
||||
@ConditionalOnProperty(
|
||||
value = "spring.cloud.service-registry.auto-registration.enabled",
|
||||
matchIfMissing = true)
|
||||
public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient,
|
||||
CloudEurekaInstanceConfig instanceConfig,
|
||||
ApplicationInfoManager applicationInfoManager,
|
||||
@Autowired(required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
|
||||
ApplicationInfoManager applicationInfoManager, @Autowired(
|
||||
required = false) ObjectProvider<HealthCheckHandler> healthCheckHandler) {
|
||||
return EurekaRegistration.builder(instanceConfig).with(applicationInfoManager)
|
||||
.with(eurekaClient).with(healthCheckHandler).build();
|
||||
}
|
||||
@@ -362,7 +359,8 @@ public class EurekaClientAutoConfiguration {
|
||||
@Documented
|
||||
@ConditionalOnClass(RefreshScope.class)
|
||||
@ConditionalOnBean(RefreshAutoConfiguration.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.refresh.enable", havingValue = "true", matchIfMissing = true)
|
||||
@ConditionalOnProperty(value = "eureka.client.refresh.enable", havingValue = "true",
|
||||
matchIfMissing = true)
|
||||
@interface ConditionalOnRefreshScope {
|
||||
|
||||
}
|
||||
@@ -383,14 +381,15 @@ public class EurekaClientAutoConfiguration {
|
||||
|
||||
}
|
||||
|
||||
@ConditionalOnProperty(value = "eureka.client.refresh.enable", havingValue = "false")
|
||||
@ConditionalOnProperty(value = "eureka.client.refresh.enable",
|
||||
havingValue = "false")
|
||||
static class OnPropertyDisabled {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(Health.class)
|
||||
protected static class EurekaHealthIndicatorConfiguration {
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -1086,7 +1086,7 @@ public class EurekaClientConfigBean implements EurekaClientConfig, Ordered {
|
||||
.append(decoderName).append("', ").append("clientDataAccept='")
|
||||
.append(clientDataAccept).append("', ")
|
||||
.append("shouldUnregisterOnShutdown='").append(shouldUnregisterOnShutdown)
|
||||
.append("', ").append("shouldEnforceRegistrationAtInit='")
|
||||
.append("shouldEnforceRegistrationAtInit='")
|
||||
.append(shouldEnforceRegistrationAtInit).append("', ").append("order='")
|
||||
.append(order).append("'}").toString();
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+11
-56
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,11 +16,9 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
@@ -29,13 +27,9 @@ import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.DiscoveryClient;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static com.netflix.appinfo.InstanceInfo.PortType.SECURE;
|
||||
|
||||
/**
|
||||
* A {@link DiscoveryClient} implementation for Eureka.
|
||||
@@ -106,58 +100,19 @@ public class EurekaDiscoveryClient implements DiscoveryClient {
|
||||
}
|
||||
|
||||
/**
|
||||
* An Eureka-specific {@link ServiceInstance} implementation.
|
||||
* An Eureka-specific {@link ServiceInstance} implementation. Extends
|
||||
* {@link org.springframework.cloud.netflix.eureka.EurekaServiceInstance} for
|
||||
* backwards compatibility.
|
||||
*
|
||||
* @deprecated In favor of
|
||||
* {@link org.springframework.cloud.netflix.eureka.EurekaServiceInstance}.
|
||||
*/
|
||||
public static class EurekaServiceInstance implements ServiceInstance {
|
||||
|
||||
private InstanceInfo instance;
|
||||
@Deprecated
|
||||
public static class EurekaServiceInstance
|
||||
extends org.springframework.cloud.netflix.eureka.EurekaServiceInstance {
|
||||
|
||||
public EurekaServiceInstance(InstanceInfo instance) {
|
||||
Assert.notNull(instance, "Service instance required");
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public InstanceInfo getInstanceInfo() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return this.instance.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.instance.getAppName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return this.instance.getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
if (isSecure()) {
|
||||
return this.instance.getSecurePort();
|
||||
}
|
||||
return this.instance.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
// assume if secure is enabled, that is the default
|
||||
return this.instance.isPortEnabled(SECURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return DefaultServiceInstance.getUri(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return this.instance.getMetadata();
|
||||
super(instance);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+32
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,12 +21,13 @@ import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
|
||||
import org.springframework.boot.actuate.health.SimpleStatusAggregator;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.ConditionalOnBlockingDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
|
||||
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
|
||||
@@ -40,39 +41,61 @@ import org.springframework.context.annotation.Configuration;
|
||||
* @author Jon Schneider
|
||||
* @author Jakub Narloch
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@ConditionalOnBlockingDiscoveryEnabled
|
||||
public class EurekaDiscoveryClientConfiguration {
|
||||
|
||||
/**
|
||||
* Deprecated in favor of auto configuration order.
|
||||
* @return Marker bean
|
||||
* @deprecated in favor of auto configuration order.
|
||||
*/
|
||||
@Deprecated
|
||||
@Bean
|
||||
public Marker eurekaDiscoverClientMarker() {
|
||||
return new Marker();
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EurekaDiscoveryClient discoveryClient(EurekaClient client,
|
||||
EurekaClientConfig clientConfig) {
|
||||
return new EurekaDiscoveryClient(client, clientConfig);
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnProperty(value = "eureka.client.healthcheck.enabled",
|
||||
matchIfMissing = false)
|
||||
protected static class EurekaHealthCheckHandlerConfiguration {
|
||||
|
||||
@Autowired(required = false)
|
||||
private HealthAggregator healthAggregator = new OrderedHealthAggregator();
|
||||
private StatusAggregator statusAggregator = new SimpleStatusAggregator();
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(HealthCheckHandler.class)
|
||||
public EurekaHealthCheckHandler eurekaHealthCheckHandler() {
|
||||
return new EurekaHealthCheckHandler(this.healthAggregator);
|
||||
return new EurekaHealthCheckHandler(this.statusAggregator);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Deprecated in favor of auto configuration order.
|
||||
*
|
||||
* @deprecated in favor of auto configuration order.
|
||||
*/
|
||||
@Deprecated
|
||||
class Marker {
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(RefreshScopeRefreshedEvent.class)
|
||||
protected static class EurekaClientConfigurationRefresher
|
||||
implements ApplicationListener<RefreshScopeRefreshedEvent> {
|
||||
|
||||
+174
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,24 +17,36 @@
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.netflix.appinfo.HealthCheckHandler;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.boot.actuate.health.CompositeHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.DefaultHealthIndicatorRegistry;
|
||||
import org.springframework.boot.actuate.health.Health;
|
||||
import org.springframework.boot.actuate.health.HealthAggregator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicator;
|
||||
import org.springframework.boot.actuate.health.HealthIndicatorRegistryFactory;
|
||||
import org.springframework.boot.actuate.health.ReactiveHealthIndicator;
|
||||
import org.springframework.boot.actuate.health.Status;
|
||||
import org.springframework.boot.actuate.health.StatusAggregator;
|
||||
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthContributor;
|
||||
import org.springframework.cloud.client.discovery.health.DiscoveryCompositeHealthIndicator;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
import org.springframework.context.ApplicationContextAware;
|
||||
import org.springframework.context.Lifecycle;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
|
||||
/**
|
||||
* A Eureka health checker, maps the application status into {@link InstanceStatus} that
|
||||
* will be propagated to Eureka registry.
|
||||
@@ -43,12 +55,20 @@ import static com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
* {@link HealthCheckHandler}. By default this implementation will perform aggregation of
|
||||
* all registered {@link HealthIndicator} through registered {@link HealthAggregator}.
|
||||
*
|
||||
* A {@code null} status is returned when the application context is closed (or in the
|
||||
* process of being closed). This prevents Eureka from updating the health status and only
|
||||
* consider the status present in the current InstanceInfo.
|
||||
*
|
||||
* @author Jakub Narloch
|
||||
* @author Spencer Gibb
|
||||
* @author Nowrin Anwar Joyita
|
||||
* @author Bertrand Renuart
|
||||
* @see HealthCheckHandler
|
||||
* @see StatusAggregator
|
||||
* @see HealthAggregator
|
||||
*/
|
||||
public class EurekaHealthCheckHandler
|
||||
implements HealthCheckHandler, ApplicationContextAware, InitializingBean {
|
||||
public class EurekaHealthCheckHandler implements HealthCheckHandler,
|
||||
ApplicationContextAware, InitializingBean, Ordered, Lifecycle {
|
||||
|
||||
private static final Map<Status, InstanceInfo.InstanceStatus> STATUS_MAPPING = new HashMap<Status, InstanceInfo.InstanceStatus>() {
|
||||
{
|
||||
@@ -59,13 +79,41 @@ public class EurekaHealthCheckHandler
|
||||
}
|
||||
};
|
||||
|
||||
private final CompositeHealthIndicator healthIndicator;
|
||||
private StatusAggregator statusAggregator;
|
||||
|
||||
private ApplicationContext applicationContext;
|
||||
|
||||
/**
|
||||
* {@code true} until the context is stopped.
|
||||
*/
|
||||
private boolean running = true;
|
||||
|
||||
private Map<String, HealthIndicator> healthIndicators;
|
||||
|
||||
private Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = new HashMap<>();
|
||||
|
||||
@Deprecated
|
||||
private CompositeHealthIndicator healthIndicator;
|
||||
|
||||
@Deprecated
|
||||
private HealthIndicatorRegistryFactory healthIndicatorRegistryFactory;
|
||||
|
||||
@Deprecated
|
||||
private HealthAggregator healthAggregator;
|
||||
|
||||
@Deprecated
|
||||
public EurekaHealthCheckHandler(HealthAggregator healthAggregator) {
|
||||
Assert.notNull(healthAggregator, "HealthAggregator must not be null");
|
||||
this.healthIndicator = new CompositeHealthIndicator(healthAggregator);
|
||||
this.healthAggregator = healthAggregator;
|
||||
healthIndicatorRegistryFactory = new HealthIndicatorRegistryFactory();
|
||||
healthIndicator = new CompositeHealthIndicator(this.healthAggregator,
|
||||
new DefaultHealthIndicatorRegistry());
|
||||
}
|
||||
|
||||
public EurekaHealthCheckHandler(StatusAggregator statusAggregator) {
|
||||
this.statusAggregator = statusAggregator;
|
||||
Assert.notNull(statusAggregator, "StatusAggregator must not be null");
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,7 +126,22 @@ public class EurekaHealthCheckHandler
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
final Map<String, HealthIndicator> healthIndicators = applicationContext
|
||||
.getBeansOfType(HealthIndicator.class);
|
||||
this.healthIndicators = new HashMap<>();
|
||||
|
||||
final Map<String, ReactiveHealthIndicator> reactiveHealthIndicators = applicationContext
|
||||
.getBeansOfType(ReactiveHealthIndicator.class);
|
||||
|
||||
if (statusAggregator != null) {
|
||||
populateHealthIndicators(healthIndicators);
|
||||
populateReactiveHealthIndicators(reactiveHealthIndicators);
|
||||
}
|
||||
else {
|
||||
createHealthIndicator(healthIndicators);
|
||||
}
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
void createHealthIndicator(Map<String, HealthIndicator> healthIndicators) {
|
||||
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
|
||||
|
||||
// ignore EurekaHealthIndicator and flatten the rest of the composite
|
||||
@@ -89,28 +152,101 @@ public class EurekaHealthCheckHandler
|
||||
for (DiscoveryCompositeHealthIndicator.Holder holder : indicator
|
||||
.getHealthIndicators()) {
|
||||
if (!(holder.getDelegate() instanceof EurekaHealthIndicator)) {
|
||||
healthIndicator.addHealthIndicator(holder.getDelegate().getName(),
|
||||
holder);
|
||||
this.healthIndicators.put(holder.getDelegate().getName(), holder);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
else {
|
||||
healthIndicator.addHealthIndicator(entry.getKey(), entry.getValue());
|
||||
this.healthIndicators.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
healthIndicator = new CompositeHealthIndicator(healthAggregator,
|
||||
healthIndicatorRegistryFactory
|
||||
.createHealthIndicatorRegistry(this.healthIndicators));
|
||||
}
|
||||
|
||||
void populateHealthIndicators(Map<String, HealthIndicator> healthIndicators) {
|
||||
for (Map.Entry<String, HealthIndicator> entry : healthIndicators.entrySet()) {
|
||||
// ignore EurekaHealthIndicator and flatten the rest of the composite
|
||||
// otherwise there is a never ending cycle of down. See gh-643
|
||||
if (entry.getValue() instanceof DiscoveryCompositeHealthContributor) {
|
||||
DiscoveryCompositeHealthContributor indicator = (DiscoveryCompositeHealthContributor) entry
|
||||
.getValue();
|
||||
indicator.forEach(contributor -> {
|
||||
if (!(contributor
|
||||
.getContributor() instanceof EurekaHealthIndicator)) {
|
||||
this.healthIndicators.put(contributor.getName(),
|
||||
(HealthIndicator) contributor.getContributor());
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.healthIndicators.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void populateReactiveHealthIndicators(
|
||||
Map<String, ReactiveHealthIndicator> reactiveHealthIndicators) {
|
||||
for (Map.Entry<String, ReactiveHealthIndicator> entry : reactiveHealthIndicators
|
||||
.entrySet()) {
|
||||
this.reactiveHealthIndicators.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public InstanceStatus getStatus(InstanceStatus instanceStatus) {
|
||||
return getHealthStatus();
|
||||
if (running) {
|
||||
return getHealthStatus();
|
||||
}
|
||||
else {
|
||||
// Return nothing if the context is not running, so the status held by the
|
||||
// InstanceInfo remains unchanged.
|
||||
// See gh-1571
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
protected InstanceStatus getHealthStatus() {
|
||||
final Status status = getHealthIndicator().health().getStatus();
|
||||
final Status status;
|
||||
if (statusAggregator != null) {
|
||||
status = getStatus(statusAggregator);
|
||||
}
|
||||
else {
|
||||
status = getStatus(getHealthIndicator());
|
||||
}
|
||||
return mapToInstanceStatus(status);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
private Status getStatus(CompositeHealthIndicator healthIndicator) {
|
||||
Status status;
|
||||
status = healthIndicator.health().getStatus();
|
||||
return status;
|
||||
}
|
||||
|
||||
protected Status getStatus(StatusAggregator statusAggregator) {
|
||||
Status status;
|
||||
|
||||
Set<Status> statusSet = new HashSet<>();
|
||||
if (healthIndicators != null) {
|
||||
statusSet.addAll(
|
||||
healthIndicators.values().stream().map(HealthIndicator::health)
|
||||
.map(Health::getStatus).collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
if (reactiveHealthIndicators != null) {
|
||||
statusSet.addAll(reactiveHealthIndicators.values().stream()
|
||||
.map(ReactiveHealthIndicator::health).map(Mono::block)
|
||||
.filter(Objects::nonNull).map(Health::getStatus)
|
||||
.collect(Collectors.toSet()));
|
||||
}
|
||||
|
||||
status = statusAggregator.getAggregateStatus(statusSet);
|
||||
return status;
|
||||
}
|
||||
|
||||
protected InstanceStatus mapToInstanceStatus(Status status) {
|
||||
if (!STATUS_MAPPING.containsKey(status)) {
|
||||
return InstanceStatus.UNKNOWN;
|
||||
@@ -118,8 +254,34 @@ public class EurekaHealthCheckHandler
|
||||
return STATUS_MAPPING.get(status);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
protected CompositeHealthIndicator getHealthIndicator() {
|
||||
return healthIndicator;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
// registered with a high order priority so the close() method is invoked early
|
||||
// and *BEFORE* EurekaAutoServiceRegistration
|
||||
// (must be in effect when the registration is closed and the eureka replication
|
||||
// triggered -> health check handler is
|
||||
// consulted at that moment)
|
||||
return Ordered.HIGHEST_PRECEDENCE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
running = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
running = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRunning() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -266,7 +266,7 @@ public class EurekaInstanceConfigBean
|
||||
|
||||
/**
|
||||
* Flag to say that, when guessing a hostname, the IP address of the server should be
|
||||
* used in prference to the hostname reported by the OS.
|
||||
* used in preference to the hostname reported by the OS.
|
||||
*/
|
||||
private boolean preferIpAddress = false;
|
||||
|
||||
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
|
||||
import org.springframework.cloud.client.DefaultServiceInstance;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.core.style.ToStringCreator;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
import static com.netflix.appinfo.InstanceInfo.PortType.SECURE;
|
||||
|
||||
/**
|
||||
* An Eureka-specific {@link ServiceInstance} implementation.
|
||||
*
|
||||
* @author Spencer Gibb
|
||||
* @author Dave Syer
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaServiceInstance implements ServiceInstance {
|
||||
|
||||
private InstanceInfo instance;
|
||||
|
||||
public EurekaServiceInstance(InstanceInfo instance) {
|
||||
Assert.notNull(instance, "Service instance required");
|
||||
this.instance = instance;
|
||||
}
|
||||
|
||||
public InstanceInfo getInstanceInfo() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getInstanceId() {
|
||||
return this.instance.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getServiceId() {
|
||||
return this.instance.getAppName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getHost() {
|
||||
return this.instance.getHostName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPort() {
|
||||
if (isSecure()) {
|
||||
return this.instance.getSecurePort();
|
||||
}
|
||||
return this.instance.getPort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSecure() {
|
||||
// assume if secure is enabled, that is the default
|
||||
return this.instance.isPortEnabled(SECURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public URI getUri() {
|
||||
return DefaultServiceInstance.getUri(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Map<String, String> getMetadata() {
|
||||
return this.instance.getMetadata();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getScheme() {
|
||||
return getUri().getScheme();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) {
|
||||
return true;
|
||||
}
|
||||
if (o == null || getClass() != o.getClass()) {
|
||||
return false;
|
||||
}
|
||||
EurekaServiceInstance that = (EurekaServiceInstance) o;
|
||||
return Objects.equals(this.instance, that.instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.instance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return new ToStringCreator(this).append("instance", instance).toString();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2015 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+93
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,35 +16,119 @@
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
|
||||
import java.io.IOException;
|
||||
import java.security.GeneralSecurityException;
|
||||
|
||||
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.cloud.configuration.SSLContextFactory;
|
||||
import org.springframework.cloud.configuration.TlsProperties;
|
||||
import org.springframework.cloud.netflix.eureka.MutableDiscoveryClientOptionalArgs;
|
||||
import org.springframework.cloud.netflix.eureka.http.RestTemplateDiscoveryClientOptionalArgs;
|
||||
import org.springframework.cloud.netflix.eureka.http.WebClientDiscoveryClientOptionalArgs;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
public class DiscoveryClientOptionalArgsConfiguration {
|
||||
|
||||
protected static final Log logger = LogFactory
|
||||
.getLog(DiscoveryClientOptionalArgsConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConfigurationProperties("eureka.client.tls")
|
||||
public TlsProperties tlsProperties() {
|
||||
return new TlsProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
|
||||
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
|
||||
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs() {
|
||||
return new RestTemplateDiscoveryClientOptionalArgs();
|
||||
@ConditionalOnMissingBean(value = { AbstractDiscoveryClientOptionalArgs.class },
|
||||
search = SearchStrategy.CURRENT)
|
||||
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
|
||||
matchIfMissing = true, havingValue = "false")
|
||||
public RestTemplateDiscoveryClientOptionalArgs restTemplateDiscoveryClientOptionalArgs(
|
||||
TlsProperties tlsProperties) throws GeneralSecurityException, IOException {
|
||||
logger.info("Eureka HTTP Client uses RestTemplate.");
|
||||
RestTemplateDiscoveryClientOptionalArgs result = new RestTemplateDiscoveryClientOptionalArgs();
|
||||
setupTLS(result, tlsProperties);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(name = "com.sun.jersey.api.client.filter.ClientFilter")
|
||||
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class, search = SearchStrategy.CURRENT)
|
||||
public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
|
||||
return new MutableDiscoveryClientOptionalArgs();
|
||||
@ConditionalOnMissingBean(value = AbstractDiscoveryClientOptionalArgs.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public MutableDiscoveryClientOptionalArgs discoveryClientOptionalArgs(
|
||||
TlsProperties tlsProperties) throws GeneralSecurityException, IOException {
|
||||
logger.info("Eureka HTTP Client uses Jersey");
|
||||
MutableDiscoveryClientOptionalArgs result = new MutableDiscoveryClientOptionalArgs();
|
||||
setupTLS(result, tlsProperties);
|
||||
return result;
|
||||
}
|
||||
|
||||
private static void setupTLS(AbstractDiscoveryClientOptionalArgs<?> args,
|
||||
TlsProperties properties) throws GeneralSecurityException, IOException {
|
||||
if (properties.isEnabled()) {
|
||||
SSLContextFactory factory = new SSLContextFactory(properties);
|
||||
args.setSSLContext(factory.createSSLContext());
|
||||
}
|
||||
}
|
||||
|
||||
@ConditionalOnMissingClass("com.sun.jersey.api.client.filter.ClientFilter")
|
||||
@ConditionalOnClass(
|
||||
name = "org.springframework.web.reactive.function.client.WebClient")
|
||||
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
|
||||
havingValue = "true")
|
||||
protected static class WebClientConfiguration {
|
||||
|
||||
@Autowired
|
||||
private TlsProperties tlsProperties;
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(
|
||||
value = { AbstractDiscoveryClientOptionalArgs.class,
|
||||
RestTemplateDiscoveryClientOptionalArgs.class },
|
||||
search = SearchStrategy.CURRENT)
|
||||
public WebClientDiscoveryClientOptionalArgs webClientDiscoveryClientOptionalArgs(
|
||||
ObjectProvider<WebClient.Builder> builder)
|
||||
throws GeneralSecurityException, IOException {
|
||||
logger.info("Eureka HTTP Client uses WebClient.");
|
||||
WebClientDiscoveryClientOptionalArgs result = new WebClientDiscoveryClientOptionalArgs(
|
||||
builder::getIfAvailable);
|
||||
setupTLS(result, tlsProperties);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration
|
||||
@ConditionalOnMissingClass({ "com.sun.jersey.api.client.filter.ClientFilter",
|
||||
"org.springframework.web.reactive.function.client.WebClient" })
|
||||
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
|
||||
havingValue = "true")
|
||||
protected static class WebClientNotFoundConfiguration {
|
||||
|
||||
public WebClientNotFoundConfiguration() {
|
||||
throw new IllegalStateException("eureka.client.webclient.enabled is true, "
|
||||
+ "but WebClient is not on the classpath. Please add "
|
||||
+ "spring-boot-starter-webflux as a dependency.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2014 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -34,7 +34,7 @@ import org.springframework.util.StringUtils;
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@Configuration
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass({ EurekaInstanceConfigBean.class, EurekaClient.class,
|
||||
ConfigServerProperties.class })
|
||||
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.endpoint.EndpointUtils;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import com.netflix.discovery.shared.resolver.DefaultEndpoint;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpClient;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.ObjectProvider;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.autoconfigure.condition.SearchStrategy;
|
||||
import org.springframework.boot.autoconfigure.http.codec.CodecsAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.config.client.ConfigServerInstanceProvider;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientConfigBean;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
|
||||
import org.springframework.cloud.netflix.eureka.http.RestTemplateEurekaHttpClient;
|
||||
import org.springframework.cloud.netflix.eureka.http.RestTemplateTransportClientFactory;
|
||||
import org.springframework.cloud.netflix.eureka.http.WebClientEurekaHttpClient;
|
||||
import org.springframework.cloud.netflix.eureka.http.WebClientTransportClientFactory;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled",
|
||||
matchIfMissing = false)
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
public class EurekaConfigServerBootstrapConfiguration {
|
||||
|
||||
private static final Log log = LogFactory
|
||||
.getLog(EurekaConfigServerBootstrapConfiguration.class);
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(value = EurekaClientConfig.class,
|
||||
search = SearchStrategy.CURRENT)
|
||||
public EurekaClientConfigBean eurekaClientConfigBean() {
|
||||
return new EurekaClientConfigBean();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaHttpClient.class)
|
||||
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
|
||||
matchIfMissing = true, havingValue = "false")
|
||||
public RestTemplateEurekaHttpClient configDiscoveryRestTemplateEurekaHttpClient(
|
||||
EurekaClientConfigBean config, Environment env) {
|
||||
return (RestTemplateEurekaHttpClient) new RestTemplateTransportClientFactory()
|
||||
.newClient(new DefaultEndpoint(getEurekaUrl(config, env)));
|
||||
}
|
||||
|
||||
private static String getEurekaUrl(EurekaClientConfigBean config, Environment env) {
|
||||
List<String> urls = EndpointUtils.getDiscoveryServiceUrls(config,
|
||||
EurekaClientConfigBean.DEFAULT_ZONE, new HostnameBasedUrlRandomizer(
|
||||
env.getProperty("eureka.instance.hostname")));
|
||||
return urls.get(0);
|
||||
}
|
||||
|
||||
private boolean isSuccessful(EurekaHttpResponse<Applications> response) {
|
||||
HttpStatus httpStatus = HttpStatus.resolve(response.getStatusCode());
|
||||
return httpStatus != null && httpStatus.is2xxSuccessful();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ConfigServerInstanceProvider.Function eurekaConfigServerInstanceProvider(
|
||||
EurekaHttpClient client, EurekaClientConfig config) {
|
||||
|
||||
return serviceId -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("eurekaConfigServerInstanceProvider finding instances for "
|
||||
+ serviceId);
|
||||
}
|
||||
EurekaHttpResponse<Applications> response = client
|
||||
.getApplications(config.getRegion());
|
||||
List<ServiceInstance> instances = new ArrayList<>();
|
||||
if (!isSuccessful(response) || response.getEntity() == null) {
|
||||
return instances;
|
||||
}
|
||||
|
||||
Applications applications = response.getEntity();
|
||||
applications.shuffleInstances(config.shouldFilterOnlyUpInstances());
|
||||
List<InstanceInfo> infos = applications
|
||||
.getInstancesByVirtualHostName(serviceId);
|
||||
for (InstanceInfo info : infos) {
|
||||
instances.add(new EurekaServiceInstance(info));
|
||||
}
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("eurekaConfigServerInstanceProvider found " + infos.size()
|
||||
+ " instance(s) for " + serviceId + ", " + instances);
|
||||
}
|
||||
return instances;
|
||||
};
|
||||
}
|
||||
|
||||
private static final class HostnameBasedUrlRandomizer
|
||||
implements EndpointUtils.ServiceUrlRandomizer {
|
||||
|
||||
private final String hostname;
|
||||
|
||||
private HostnameBasedUrlRandomizer(String hostname) {
|
||||
this.hostname = hostname;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void randomize(List<String> urlList) {
|
||||
int listSize = 0;
|
||||
if (urlList != null) {
|
||||
listSize = urlList.size();
|
||||
}
|
||||
if (!StringUtils.hasText(hostname) || listSize == 0) {
|
||||
return;
|
||||
}
|
||||
// Find the hashcode of the instance hostname and use it to find an entry
|
||||
// and then arrange the rest of the entries after this entry.
|
||||
int instanceHashcode = hostname.hashCode();
|
||||
if (instanceHashcode < 0) {
|
||||
instanceHashcode = instanceHashcode * -1;
|
||||
}
|
||||
int backupInstance = instanceHashcode % listSize;
|
||||
for (int i = 0; i < backupInstance; i++) {
|
||||
String zone = urlList.remove(0);
|
||||
urlList.add(zone);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(
|
||||
name = "org.springframework.web.reactive.function.client.WebClient")
|
||||
@ConditionalOnProperty(prefix = "eureka.client", name = "webclient.enabled",
|
||||
havingValue = "true")
|
||||
@ImportAutoConfiguration({ CodecsAutoConfiguration.class,
|
||||
WebClientAutoConfiguration.class })
|
||||
protected static class WebClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean(EurekaHttpClient.class)
|
||||
public WebClientEurekaHttpClient configDiscoveryWebClientEurekaHttpClient(
|
||||
EurekaClientConfigBean config, ObjectProvider<WebClient.Builder> builder,
|
||||
Environment env) {
|
||||
return (WebClientEurekaHttpClient) new WebClientTransportClientFactory(
|
||||
builder::getIfAvailable)
|
||||
.newClient(new DefaultEndpoint(getEurekaUrl(config, env)));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
-55
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
|
||||
/**
|
||||
* Bootstrap configuration for a config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnBean({ EurekaDiscoveryClientConfiguration.class })
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
|
||||
public class EurekaDiscoveryClientConfigServiceAutoConfiguration {
|
||||
|
||||
@Autowired
|
||||
private ConfigurableApplicationContext context;
|
||||
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
if (this.context.getParent() != null) {
|
||||
if (this.context.getBeanNamesForType(EurekaClient.class).length > 0
|
||||
&& this.context.getParent()
|
||||
.getBeanNamesForType(EurekaClient.class).length > 0) {
|
||||
// If the parent has a EurekaClient as well it should be shutdown, so the
|
||||
// local one can register accurate instance info
|
||||
this.context.getParent().getBean(EurekaClient.class).shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.cloud.config.client.ConfigServicePropertySourceLocator;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
|
||||
/**
|
||||
* Eureka-specific helper for config client that wants to lookup the config server via
|
||||
* discovery.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
@ConditionalOnClass(ConfigServicePropertySourceLocator.class)
|
||||
@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)
|
||||
@Configuration
|
||||
@Import({ EurekaDiscoveryClientConfiguration.class, // this emulates
|
||||
// @EnableDiscoveryClient, the import
|
||||
// selector doesn't run before the
|
||||
// bootstrap phase
|
||||
EurekaClientAutoConfiguration.class })
|
||||
public class EurekaDiscoveryClientConfigServiceBootstrapConfiguration {
|
||||
|
||||
}
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.config;
|
||||
|
||||
import org.springframework.cloud.configuration.TlsProperties;
|
||||
|
||||
/**
|
||||
* Eureka client TLS properties.
|
||||
*/
|
||||
|
||||
public class EurekaTlsProperties extends TlsProperties {
|
||||
|
||||
/**
|
||||
* Prefix for Eureka client TLS properties.
|
||||
*/
|
||||
public static final String PREFIX = "eureka.client.tls";
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -62,6 +62,10 @@ public class RestTemplateEurekaHttpClient implements EurekaHttpClient {
|
||||
}
|
||||
}
|
||||
|
||||
public String getServiceUrl() {
|
||||
return this.serviceUrl;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Void> register(InstanceInfo info) {
|
||||
String urlPath = serviceUrl + "apps/" + info.getAppName();
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -39,7 +39,7 @@ import com.netflix.discovery.shared.transport.EurekaHttpClient;
|
||||
import com.netflix.discovery.shared.transport.TransportClientFactory;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.client.support.BasicAuthorizationInterceptor;
|
||||
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.client.DefaultResponseErrorHandler;
|
||||
import org.springframework.web.client.RestTemplate;
|
||||
@@ -66,7 +66,7 @@ public class RestTemplateTransportClientFactory implements TransportClientFactor
|
||||
if (serviceURI.getUserInfo() != null) {
|
||||
String[] credentials = serviceURI.getUserInfo().split(":");
|
||||
if (credentials.length == 2) {
|
||||
restTemplate.getInterceptors().add(new BasicAuthorizationInterceptor(
|
||||
restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(
|
||||
credentials[0], credentials[1]));
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.http;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.netflix.discovery.AbstractDiscoveryClientOptionalArgs;
|
||||
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
* @author Haytham Mohamed
|
||||
*/
|
||||
public class WebClientDiscoveryClientOptionalArgs
|
||||
extends AbstractDiscoveryClientOptionalArgs<Void> {
|
||||
|
||||
@Deprecated
|
||||
public WebClientDiscoveryClientOptionalArgs() {
|
||||
this(WebClient::builder);
|
||||
}
|
||||
|
||||
public WebClientDiscoveryClientOptionalArgs(Supplier<WebClient.Builder> builder) {
|
||||
setTransportClientFactories(new WebClientTransportClientFactories(builder));
|
||||
}
|
||||
|
||||
}
|
||||
+240
@@ -0,0 +1,240 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.http;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.appinfo.InstanceInfo.InstanceStatus;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpClient;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpResponse;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpResponse.EurekaHttpResponseBuilder;
|
||||
import com.netflix.discovery.util.StringUtil;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.reactive.function.BodyInserters;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
import static com.netflix.discovery.shared.transport.EurekaHttpResponse.anEurekaHttpResponse;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
* @author Haytham Mohamed
|
||||
*/
|
||||
public class WebClientEurekaHttpClient implements EurekaHttpClient {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private WebClient webClient;
|
||||
|
||||
public WebClientEurekaHttpClient(WebClient webClient) {
|
||||
this.webClient = webClient;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Void> register(InstanceInfo info) {
|
||||
return webClient.post().uri("apps/" + info.getAppName(), Void.class)
|
||||
.body(BodyInserters.fromValue(info))
|
||||
.header(HttpHeaders.ACCEPT_ENCODING, "gzip")
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.exchange().map(response -> eurekaHttpResponse(response)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Void> cancel(String appName, String id) {
|
||||
return webClient.delete().uri("apps/" + appName + '/' + id, Void.class).exchange()
|
||||
.map(response -> eurekaHttpResponse(response)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id,
|
||||
InstanceInfo info, InstanceStatus overriddenStatus) {
|
||||
String urlPath = "apps/" + appName + '/' + id + "?status="
|
||||
+ info.getStatus().toString() + "&lastDirtyTimestamp="
|
||||
+ info.getLastDirtyTimestamp().toString() + (overriddenStatus != null
|
||||
? "&overriddenstatus=" + overriddenStatus.name() : "");
|
||||
|
||||
ClientResponse response = webClient.put().uri(urlPath, InstanceInfo.class)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
|
||||
.block();
|
||||
|
||||
EurekaHttpResponseBuilder<InstanceInfo> builder = anEurekaHttpResponse(
|
||||
statusCodeValueOf(response), InstanceInfo.class)
|
||||
.headers(headersOf(response));
|
||||
|
||||
InstanceInfo entity = response.toEntity(InstanceInfo.class).block().getBody();
|
||||
|
||||
if (entity != null) {
|
||||
builder.entity(entity);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Void> statusUpdate(String appName, String id,
|
||||
InstanceStatus newStatus, InstanceInfo info) {
|
||||
String urlPath = "apps/" + appName + '/' + id + "/status?value="
|
||||
+ newStatus.name() + "&lastDirtyTimestamp="
|
||||
+ info.getLastDirtyTimestamp().toString();
|
||||
|
||||
return webClient.put().uri(urlPath, Void.class)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.exchange().map(response -> eurekaHttpResponse(response)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id,
|
||||
InstanceInfo info) {
|
||||
String urlPath = "apps/" + appName + '/' + id + "/status?lastDirtyTimestamp="
|
||||
+ info.getLastDirtyTimestamp().toString();
|
||||
|
||||
return webClient.delete().uri(urlPath, Void.class)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.exchange().map(response -> eurekaHttpResponse(response)).block();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Applications> getApplications(String... regions) {
|
||||
return getApplicationsInternal("apps/", regions);
|
||||
}
|
||||
|
||||
private EurekaHttpResponse<Applications> getApplicationsInternal(String urlPath,
|
||||
String[] regions) {
|
||||
String url = urlPath;
|
||||
|
||||
if (regions != null && regions.length > 0) {
|
||||
url = url + (urlPath.contains("?") ? "&" : "?") + "regions="
|
||||
+ StringUtil.join(regions);
|
||||
}
|
||||
|
||||
ClientResponse response = webClient.get().uri(url, Applications.class)
|
||||
.header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
|
||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
|
||||
.block();
|
||||
|
||||
int statusCode = statusCodeValueOf(response);
|
||||
|
||||
Applications body = response.toEntity(Applications.class).block().getBody();
|
||||
|
||||
return anEurekaHttpResponse(statusCode,
|
||||
statusCode == HttpStatus.OK.value() && body != null ? body : null)
|
||||
.headers(headersOf(response)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Applications> getDelta(String... regions) {
|
||||
return getApplicationsInternal("apps/delta", regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Applications> getVip(String vipAddress, String... regions) {
|
||||
return getApplicationsInternal("vips/" + vipAddress, regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Applications> getSecureVip(String secureVipAddress,
|
||||
String... regions) {
|
||||
return getApplicationsInternal("svips/" + secureVipAddress, regions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<Application> getApplication(String appName) {
|
||||
|
||||
ClientResponse response = webClient.get()
|
||||
.uri("apps/" + appName, Application.class)
|
||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
|
||||
.block();
|
||||
|
||||
int statusCode = statusCodeValueOf(response);
|
||||
Application body = response.toEntity(Application.class).block().getBody();
|
||||
|
||||
Application application = statusCode == HttpStatus.OK.value() && body != null
|
||||
? body : null;
|
||||
|
||||
return anEurekaHttpResponse(statusCode, application).headers(headersOf(response))
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<InstanceInfo> getInstance(String appName, String id) {
|
||||
return getInstanceInternal("apps/" + appName + '/' + id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpResponse<InstanceInfo> getInstance(String id) {
|
||||
return getInstanceInternal("instances/" + id);
|
||||
}
|
||||
|
||||
private EurekaHttpResponse<InstanceInfo> getInstanceInternal(String urlPath) {
|
||||
ClientResponse response = webClient.get().uri(urlPath, InstanceInfo.class)
|
||||
.header(HttpHeaders.ACCEPT, MediaType.APPLICATION_JSON_VALUE).exchange()
|
||||
.block();
|
||||
|
||||
int statusCode = statusCodeValueOf(response);
|
||||
InstanceInfo body = response.toEntity(InstanceInfo.class).block().getBody();
|
||||
|
||||
return anEurekaHttpResponse(statusCode,
|
||||
statusCode == HttpStatus.OK.value() && body != null ? body : null)
|
||||
.headers(headersOf(response)).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
public WebClient getWebClient() {
|
||||
return this.webClient;
|
||||
}
|
||||
|
||||
private static Map<String, String> headersOf(ClientResponse response) {
|
||||
ClientResponse.Headers httpHeaders = response.headers();
|
||||
if (httpHeaders == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
HttpHeaders asHeaders = httpHeaders.asHttpHeaders();
|
||||
if (asHeaders == null) {
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
asHeaders.entrySet().stream().forEach(entry -> entry.getValue().stream()
|
||||
.forEach(v -> headers.put(entry.getKey(), v)));
|
||||
return headers;
|
||||
}
|
||||
|
||||
private int statusCodeValueOf(ClientResponse response) {
|
||||
return response.statusCode().value();
|
||||
}
|
||||
|
||||
private EurekaHttpResponse<Void> eurekaHttpResponse(ClientResponse response) {
|
||||
return anEurekaHttpResponse(statusCodeValueOf(response))
|
||||
.headers(headersOf(response)).build();
|
||||
}
|
||||
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.http;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.net.ssl.HostnameVerifier;
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.shared.transport.TransportClientFactory;
|
||||
import com.netflix.discovery.shared.transport.jersey.EurekaJerseyClient;
|
||||
import com.netflix.discovery.shared.transport.jersey.TransportClientFactories;
|
||||
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* @author Daniel Lavoie
|
||||
* @author Haytham Mohamed
|
||||
*/
|
||||
public class WebClientTransportClientFactories implements TransportClientFactories<Void> {
|
||||
|
||||
private final Supplier<WebClient.Builder> builder;
|
||||
|
||||
public WebClientTransportClientFactories(Supplier<WebClient.Builder> builder) {
|
||||
this.builder = builder;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportClientFactory newTransportClientFactory(
|
||||
Collection<Void> additionalFilters, EurekaJerseyClient providedJerseyClient) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportClientFactory newTransportClientFactory(
|
||||
EurekaClientConfig clientConfig, Collection<Void> additionalFilters,
|
||||
InstanceInfo myInstanceInfo) {
|
||||
return new WebClientTransportClientFactory(builder);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TransportClientFactory newTransportClientFactory(
|
||||
final EurekaClientConfig clientConfig,
|
||||
final Collection<Void> additionalFilters, final InstanceInfo myInstanceInfo,
|
||||
final Optional<SSLContext> sslContext,
|
||||
final Optional<HostnameVerifier> hostnameVerifier) {
|
||||
return new WebClientTransportClientFactory(builder);
|
||||
}
|
||||
|
||||
}
|
||||
+195
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.http;
|
||||
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.fasterxml.jackson.databind.BeanDescription;
|
||||
import com.fasterxml.jackson.databind.DeserializationFeature;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
|
||||
import com.fasterxml.jackson.databind.SerializationConfig;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.databind.module.SimpleModule;
|
||||
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
|
||||
import com.fasterxml.jackson.databind.ser.std.BeanSerializerBase;
|
||||
import com.netflix.appinfo.InstanceInfo;
|
||||
import com.netflix.discovery.converters.jackson.mixin.ApplicationsJsonMixIn;
|
||||
import com.netflix.discovery.converters.jackson.mixin.InstanceInfoJsonMixIn;
|
||||
import com.netflix.discovery.converters.jackson.serializer.InstanceInfoJsonBeanSerializer;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import com.netflix.discovery.shared.resolver.EurekaEndpoint;
|
||||
import com.netflix.discovery.shared.transport.EurekaHttpClient;
|
||||
import com.netflix.discovery.shared.transport.TransportClientFactory;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.http.codec.ClientCodecConfigurer;
|
||||
import org.springframework.http.codec.json.Jackson2JsonDecoder;
|
||||
import org.springframework.http.codec.json.Jackson2JsonEncoder;
|
||||
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
|
||||
import org.springframework.web.reactive.function.client.ClientResponse;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunction;
|
||||
import org.springframework.web.reactive.function.client.ExchangeFilterFunctions;
|
||||
import org.springframework.web.reactive.function.client.WebClient;
|
||||
|
||||
/**
|
||||
* Provides the custom {@link WebClient.Builder} required by the
|
||||
* {@link WebClientEurekaHttpClient}. Relies on Jackson for serialization and
|
||||
* deserialization.
|
||||
*
|
||||
* @author Daniel Lavoie
|
||||
* @author Haytham Mohamed
|
||||
*/
|
||||
public class WebClientTransportClientFactory implements TransportClientFactory {
|
||||
|
||||
private final Supplier<WebClient.Builder> builderSupplier;
|
||||
|
||||
@Deprecated
|
||||
public WebClientTransportClientFactory() {
|
||||
this(WebClient::builder);
|
||||
}
|
||||
|
||||
public WebClientTransportClientFactory(Supplier<WebClient.Builder> builderSupplier) {
|
||||
this.builderSupplier = builderSupplier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
|
||||
// we want a copy to modify. Don't change the original
|
||||
WebClient.Builder builder = this.builderSupplier.get().clone();
|
||||
setUrl(builder, endpoint.getServiceUrl());
|
||||
setCodecs(builder);
|
||||
builder.filter(http4XxErrorExchangeFilterFunction());
|
||||
return new WebClientEurekaHttpClient(builder.build());
|
||||
}
|
||||
|
||||
private WebClient.Builder setUrl(WebClient.Builder builder, String serviceUrl) {
|
||||
String url = serviceUrl;
|
||||
try {
|
||||
URI serviceURI = new URI(serviceUrl);
|
||||
if (serviceURI.getUserInfo() != null) {
|
||||
String[] credentials = serviceURI.getUserInfo().split(":");
|
||||
if (credentials.length == 2) {
|
||||
builder.filter(ExchangeFilterFunctions
|
||||
.basicAuthentication(credentials[0], credentials[1]));
|
||||
url = serviceUrl.replace(credentials[0] + ":" + credentials[1] + "@",
|
||||
"");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (URISyntaxException ignore) {
|
||||
}
|
||||
return builder.baseUrl(url);
|
||||
}
|
||||
|
||||
private void setCodecs(WebClient.Builder builder) {
|
||||
ObjectMapper objectMapper = objectMapper();
|
||||
builder.codecs(configurer -> {
|
||||
ClientCodecConfigurer.ClientDefaultCodecs defaults = configurer
|
||||
.defaultCodecs();
|
||||
defaults.jackson2JsonEncoder(
|
||||
new Jackson2JsonEncoder(objectMapper, MediaType.APPLICATION_JSON));
|
||||
defaults.jackson2JsonDecoder(
|
||||
new Jackson2JsonDecoder(objectMapper, MediaType.APPLICATION_JSON));
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// Skip over 4xx http errors
|
||||
private ExchangeFilterFunction http4XxErrorExchangeFilterFunction() {
|
||||
return ExchangeFilterFunction.ofResponseProcessor(clientResponse -> {
|
||||
// literally 400 pass the tests, not 4xxClientError
|
||||
if (clientResponse.statusCode().value() == 400) {
|
||||
ClientResponse newResponse = ClientResponse.from(clientResponse)
|
||||
.statusCode(HttpStatus.OK).build();
|
||||
newResponse.body(
|
||||
(clientHttpResponse, context) -> clientHttpResponse.getBody());
|
||||
return Mono.just(newResponse);
|
||||
}
|
||||
return Mono.just(clientResponse);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the serialization configurations required by the Eureka Server. JSON
|
||||
* content exchanged with eureka requires a root node matching the entity being
|
||||
* serialized or deserialized. Achieved with
|
||||
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
|
||||
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
|
||||
* {@link PropertyNamingStrategy.SnakeCaseStrategy} is applied to the underlying
|
||||
* {@link ObjectMapper}.
|
||||
* @deprecated to be removed.
|
||||
* @return a {@link MappingJackson2HttpMessageConverter} object
|
||||
*/
|
||||
@Deprecated
|
||||
public MappingJackson2HttpMessageConverter mappingJacksonHttpMessageConverter() {
|
||||
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
|
||||
converter.setObjectMapper(objectMapper());
|
||||
return converter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the serialization configurations required by the Eureka Server. JSON
|
||||
* content exchanged with eureka requires a root node matching the entity being
|
||||
* serialized or deserialized. Achieved with
|
||||
* {@link SerializationFeature#WRAP_ROOT_VALUE} and
|
||||
* {@link DeserializationFeature#UNWRAP_ROOT_VALUE}.
|
||||
* {@link PropertyNamingStrategy.SnakeCaseStrategy} is applied to the underlying
|
||||
* {@link ObjectMapper}.
|
||||
* @return a {@link ObjectMapper} object
|
||||
*/
|
||||
private ObjectMapper objectMapper() {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
objectMapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
|
||||
|
||||
SimpleModule jsonModule = new SimpleModule();
|
||||
jsonModule.setSerializerModifier(createJsonSerializerModifier());
|
||||
objectMapper.registerModule(jsonModule);
|
||||
|
||||
objectMapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
|
||||
objectMapper.configure(DeserializationFeature.UNWRAP_ROOT_VALUE, true);
|
||||
objectMapper.addMixIn(Applications.class, ApplicationsJsonMixIn.class);
|
||||
objectMapper.addMixIn(InstanceInfo.class, InstanceInfoJsonMixIn.class);
|
||||
|
||||
return objectMapper;
|
||||
}
|
||||
|
||||
@Deprecated // reduce visibility in future release
|
||||
public static BeanSerializerModifier createJsonSerializerModifier() {
|
||||
return new BeanSerializerModifier() {
|
||||
@Override
|
||||
public JsonSerializer<?> modifySerializer(SerializationConfig config,
|
||||
BeanDescription beanDesc, JsonSerializer<?> serializer) {
|
||||
if (beanDesc.getBeanClass().isAssignableFrom(InstanceInfo.class)) {
|
||||
return new InstanceInfoJsonBeanSerializer(
|
||||
(BeanSerializerBase) serializer, false);
|
||||
}
|
||||
return serializer;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown() {
|
||||
}
|
||||
|
||||
}
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.loadbalancer;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
|
||||
import com.netflix.appinfo.EurekaInstanceConfig;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
|
||||
import org.springframework.cloud.netflix.ribbon.eureka.ZoneUtils;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import static org.springframework.cloud.netflix.eureka.loadbalancer.LoadBalancerEurekaAutoConfiguration.LOADBALANCER_ZONE;
|
||||
|
||||
/**
|
||||
* A configuration for Spring Cloud LoadBalancer that retrieves client instance zone from
|
||||
* Eureka and sets it as a property. Based on
|
||||
* {@link EurekaLoadBalancerClientConfiguration}.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 2.2.1
|
||||
* @see EurekaLoadBalancerClientConfiguration
|
||||
*/
|
||||
@Configuration
|
||||
@ConditionalOnBean({ LoadBalancerZoneConfig.class, EurekaLoadBalancerProperties.class })
|
||||
public class EurekaLoadBalancerClientConfiguration {
|
||||
|
||||
private static final Log LOG = LogFactory
|
||||
.getLog(EurekaLoadBalancerClientConfiguration.class);
|
||||
|
||||
private final EurekaClientConfig clientConfig;
|
||||
|
||||
private final EurekaInstanceConfig eurekaConfig;
|
||||
|
||||
private final LoadBalancerZoneConfig zoneConfig;
|
||||
|
||||
private final EurekaLoadBalancerProperties eurekaLoadBalancerProperties;
|
||||
|
||||
public EurekaLoadBalancerClientConfiguration(
|
||||
@Autowired(required = false) EurekaClientConfig clientConfig,
|
||||
@Autowired(required = false) EurekaInstanceConfig eurekaInstanceConfig,
|
||||
LoadBalancerZoneConfig zoneConfig,
|
||||
EurekaLoadBalancerProperties eurekaLoadBalancerProperties) {
|
||||
this.clientConfig = clientConfig;
|
||||
this.eurekaConfig = eurekaInstanceConfig;
|
||||
this.zoneConfig = zoneConfig;
|
||||
this.eurekaLoadBalancerProperties = eurekaLoadBalancerProperties;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void postprocess() {
|
||||
if (!StringUtils.isEmpty(zoneConfig.getZone())) {
|
||||
return;
|
||||
}
|
||||
String zone = getZoneFromEureka();
|
||||
if (!StringUtils.isEmpty(zone)) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Setting the value of '" + LOADBALANCER_ZONE + "' to " + zone);
|
||||
}
|
||||
zoneConfig.setZone(zone);
|
||||
}
|
||||
}
|
||||
|
||||
private String getZoneFromEureka() {
|
||||
String zone;
|
||||
boolean approximateZoneFromHostname = eurekaLoadBalancerProperties
|
||||
.isApproximateZoneFromHostname();
|
||||
if (approximateZoneFromHostname && eurekaConfig != null) {
|
||||
return ZoneUtils.extractApproximateZone(this.eurekaConfig.getHostName(false));
|
||||
}
|
||||
else {
|
||||
zone = eurekaConfig == null ? null
|
||||
: eurekaConfig.getMetadataMap().get("zone");
|
||||
if (StringUtils.isEmpty(zone)) {
|
||||
String[] zones = clientConfig
|
||||
.getAvailabilityZones(clientConfig.getRegion());
|
||||
// Pick the first one from the regions we want to connect to
|
||||
zone = zones != null && zones.length > 0 ? zones[0] : null;
|
||||
}
|
||||
return zone;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2012-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.loadbalancer;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
/**
|
||||
* A {@link ConfigurationProperties} bean for the Eureka-specific instrumentation of
|
||||
* Spring Cloud LoadBalancer.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 2.2.1
|
||||
*/
|
||||
@ConfigurationProperties("spring.cloud.loadbalancer.eureka")
|
||||
public class EurekaLoadBalancerProperties {
|
||||
|
||||
/**
|
||||
* Used to determine whether we should try to get the `zone` value from host name.
|
||||
*/
|
||||
private boolean approximateZoneFromHostname = false;
|
||||
|
||||
public boolean isApproximateZoneFromHostname() {
|
||||
return approximateZoneFromHostname;
|
||||
}
|
||||
|
||||
public void setApproximateZoneFromHostname(boolean approximateZoneFromHostname) {
|
||||
this.approximateZoneFromHostname = approximateZoneFromHostname;
|
||||
}
|
||||
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.loadbalancer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClientConfigurationRegistrar;
|
||||
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;
|
||||
import org.springframework.cloud.loadbalancer.config.LoadBalancerZoneConfig;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.env.Environment;
|
||||
|
||||
/**
|
||||
* An Autoconfiguration that loads default config for Spring Cloud LoadBalancer clients.
|
||||
*
|
||||
* @author Olga Maciaszek-Sharma
|
||||
* @since 2.2.1
|
||||
* @see EurekaLoadBalancerClientConfiguration
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@EnableConfigurationProperties
|
||||
@ConditionalOnClass(LoadBalancerClientConfigurationRegistrar.class)
|
||||
@LoadBalancerClients(defaultConfiguration = EurekaLoadBalancerClientConfiguration.class)
|
||||
@ConditionalOnProperty(name = "eureka.client.enabled", matchIfMissing = true)
|
||||
public class LoadBalancerEurekaAutoConfiguration {
|
||||
|
||||
/**
|
||||
* Spring Cloud LoadBalancer Zone property name.
|
||||
*/
|
||||
public static final String LOADBALANCER_ZONE = "spring.cloud.loadbalancer.zone";
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
EurekaLoadBalancerProperties eurekaLoadBalancerProperties() {
|
||||
return new EurekaLoadBalancerProperties();
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
LoadBalancerZoneConfig zoneConfig(Environment environment) {
|
||||
return new LoadBalancerZoneConfig(environment.getProperty(LOADBALANCER_ZONE));
|
||||
}
|
||||
|
||||
}
|
||||
+9
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -123,7 +123,7 @@ public class DefaultManagementMetadataProvider implements ManagementMetadataProv
|
||||
String refinedContextPath = '/'
|
||||
+ StringUtils.trimLeadingCharacter(contextPath, '/');
|
||||
URL base = new URL(scheme, hostname, port, refinedContextPath);
|
||||
String refinedStatusPath = StringUtils.trimLeadingCharacter(statusPath, '/');
|
||||
String refinedStatusPath = refinedStatusPath(statusPath, contextPath);
|
||||
return new URL(base, refinedStatusPath).toString();
|
||||
}
|
||||
catch (MalformedURLException e) {
|
||||
@@ -133,6 +133,13 @@ public class DefaultManagementMetadataProvider implements ManagementMetadataProv
|
||||
}
|
||||
}
|
||||
|
||||
private String refinedStatusPath(String statusPath, String contextPath) {
|
||||
if (statusPath.startsWith(contextPath) && !"/".equals(contextPath)) {
|
||||
statusPath = StringUtils.replace(statusPath, contextPath, "");
|
||||
}
|
||||
return StringUtils.trimLeadingCharacter(statusPath, '/');
|
||||
}
|
||||
|
||||
private String getErrorMessage(String scheme, String hostname, int port,
|
||||
String contextPath, String statusPath) {
|
||||
return String.format(
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2017-2019 the original author or authors.
|
||||
* Copyright 2017-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.reactive;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
import com.netflix.discovery.shared.Application;
|
||||
import com.netflix.discovery.shared.Applications;
|
||||
import reactor.core.publisher.Flux;
|
||||
import reactor.core.publisher.Mono;
|
||||
|
||||
import org.springframework.cloud.client.ServiceInstance;
|
||||
import org.springframework.cloud.client.discovery.ReactiveDiscoveryClient;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaServiceInstance;
|
||||
import org.springframework.core.Ordered;
|
||||
|
||||
/**
|
||||
* A {@link ReactiveDiscoveryClient} implementation for Eureka.
|
||||
*
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
public class EurekaReactiveDiscoveryClient implements ReactiveDiscoveryClient {
|
||||
|
||||
private final EurekaClient eurekaClient;
|
||||
|
||||
private final EurekaClientConfig clientConfig;
|
||||
|
||||
public EurekaReactiveDiscoveryClient(EurekaClient eurekaClient,
|
||||
EurekaClientConfig clientConfig) {
|
||||
this.eurekaClient = eurekaClient;
|
||||
this.clientConfig = clientConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String description() {
|
||||
return "Spring Cloud Eureka Reactive Discovery Client";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<ServiceInstance> getInstances(String serviceId) {
|
||||
return Flux
|
||||
.defer(() -> Flux.fromIterable(
|
||||
eurekaClient.getInstancesByVipAddress(serviceId, false)))
|
||||
.map(EurekaServiceInstance::new);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Flux<String> getServices() {
|
||||
return Flux.defer(() -> Mono.justOrEmpty(eurekaClient.getApplications()))
|
||||
.flatMapIterable(Applications::getRegisteredApplications)
|
||||
.filter(application -> !application.getInstances().isEmpty())
|
||||
.map(Application::getName).map(String::toLowerCase);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return clientConfig instanceof Ordered ? ((Ordered) clientConfig).getOrder()
|
||||
: ReactiveDiscoveryClient.DEFAULT_ORDER;
|
||||
}
|
||||
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright 2019-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.cloud.netflix.eureka.reactive;
|
||||
|
||||
import com.netflix.discovery.EurekaClient;
|
||||
import com.netflix.discovery.EurekaClientConfig;
|
||||
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
|
||||
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
|
||||
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
|
||||
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnDiscoveryHealthIndicatorEnabled;
|
||||
import org.springframework.cloud.client.ConditionalOnReactiveDiscoveryEnabled;
|
||||
import org.springframework.cloud.client.ReactiveCommonsClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.composite.reactive.ReactiveCompositeDiscoveryClientAutoConfiguration;
|
||||
import org.springframework.cloud.client.discovery.health.DiscoveryClientHealthIndicatorProperties;
|
||||
import org.springframework.cloud.client.discovery.health.reactive.ReactiveDiscoveryClientHealthIndicator;
|
||||
import org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
/**
|
||||
* Configuration related to service discovery when using Netflix Eureka.
|
||||
*
|
||||
* @author Tim Ysewyn
|
||||
*/
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
@ConditionalOnClass(EurekaClientConfig.class)
|
||||
@ConditionalOnDiscoveryEnabled
|
||||
@ConditionalOnReactiveDiscoveryEnabled
|
||||
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
|
||||
@EnableConfigurationProperties
|
||||
@AutoConfigureAfter({ EurekaClientAutoConfiguration.class,
|
||||
ReactiveCompositeDiscoveryClientAutoConfiguration.class })
|
||||
@AutoConfigureBefore(ReactiveCommonsClientAutoConfiguration.class)
|
||||
@ImportAutoConfiguration(EurekaClientAutoConfiguration.class)
|
||||
public class EurekaReactiveDiscoveryClientConfiguration {
|
||||
|
||||
@Bean
|
||||
@ConditionalOnMissingBean
|
||||
public EurekaReactiveDiscoveryClient eurekaReactiveDiscoveryClient(
|
||||
EurekaClient client, EurekaClientConfig clientConfig) {
|
||||
return new EurekaReactiveDiscoveryClient(client, clientConfig);
|
||||
}
|
||||
|
||||
@Bean
|
||||
@ConditionalOnClass(
|
||||
name = "org.springframework.boot.actuate.health.ReactiveHealthIndicator")
|
||||
@ConditionalOnDiscoveryHealthIndicatorEnabled
|
||||
public ReactiveDiscoveryClientHealthIndicator eurekaReactiveDiscoveryClientHealthIndicator(
|
||||
EurekaReactiveDiscoveryClient client,
|
||||
DiscoveryClientHealthIndicatorProperties properties) {
|
||||
return new ReactiveDiscoveryClientHealthIndicator(client, properties);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2016 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013-2019 the original author or authors.
|
||||
* Copyright 2013-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user