Sync docs from vFinchley.SR4 to gh-pages

This commit is contained in:
Marcin Grzejszczak
2019-06-11 10:07:49 +02:00
parent 03e260146e
commit ce5b410e71
228 changed files with 60046 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
/*
code highlight CSS resemblign the Eclipse IDE default color schema
@author Costin Leau
*/
.hl-keyword {
color: #7F0055;
font-weight: bold;
}
.hl-comment {
color: #3F5F5F;
font-style: italic;
}
.hl-multiline-comment {
color: #3F5FBF;
font-style: italic;
}
.hl-tag {
color: #3F7F7F;
}
.hl-attribute {
color: #7F007F;
}
.hl-value {
color: #2A00FF;
}
.hl-string {
color: #2A00FF;
}

View File

@@ -0,0 +1,9 @@
@IMPORT url("manual.css");
body.firstpage {
background: url("../images/background.png") no-repeat center top;
}
div.part h1 {
border-top: none;
}

View File

@@ -0,0 +1,6 @@
@IMPORT url("manual.css");
body {
background: url("../images/background.png") no-repeat center top;
}

342
Finchley.SR4/css/manual.css Normal file
View File

@@ -0,0 +1,342 @@
@IMPORT url("highlight.css");
html {
padding: 0pt;
margin: 0pt;
}
body {
color: #333333;
margin: 15px 30px;
font-family: Helvetica, Arial, Freesans, Clean, Sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
code {
font-size: 16px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
:not(a) > code {
color: #6D180B;
}
:not(pre) > code {
background-color: #F2F2F2;
border: 1px solid #CCCCCC;
border-radius: 4px;
padding: 1px 3px 0;
text-shadow: none;
white-space: nowrap;
}
body > *:first-child {
margin-top: 0 !important;
}
div {
margin: 0pt;
}
hr {
border: 1px solid #CCCCCC;
background: #CCCCCC;
}
h1, h2, h3, h4, h5, h6 {
color: #000000;
cursor: text;
font-weight: bold;
margin: 30px 0 10px;
padding: 0;
}
h1, h2, h3 {
margin: 40px 0 10px;
}
h1 {
margin: 70px 0 30px;
padding-top: 20px;
}
div.part h1 {
border-top: 1px dotted #CCCCCC;
}
h1, h1 code {
font-size: 32px;
}
h2, h2 code {
font-size: 24px;
}
h3, h3 code {
font-size: 20px;
}
h4, h1 code, h5, h5 code, h6, h6 code {
font-size: 18px;
}
div.book, div.chapter, div.appendix, div.part, div.preface {
min-width: 300px;
max-width: 1200px;
margin: 0 auto;
}
p.releaseinfo {
font-weight: bold;
margin-bottom: 40px;
margin-top: 40px;
}
div.authorgroup {
line-height: 1;
}
p.copyright {
line-height: 1;
margin-bottom: -5px;
}
.legalnotice p {
font-style: italic;
font-size: 14px;
line-height: 1;
}
div.titlepage + p, div.titlepage + p {
margin-top: 0;
}
pre {
line-height: 1.0;
color: black;
}
a {
color: #4183C4;
text-decoration: none;
}
p {
margin: 15px 0;
text-align: left;
}
ul, ol {
padding-left: 30px;
}
li p {
margin: 0;
}
div.table {
margin: 1em;
padding: 0.5em;
text-align: center;
}
div.table table, div.informaltable table {
display: table;
width: 100%;
}
div.table td {
padding-left: 7px;
padding-right: 7px;
}
.sidebar {
line-height: 1.4;
padding: 0 20px;
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
}
.sidebar p.title {
color: #6D180B;
}
pre.programlisting, pre.screen {
font-size: 15px;
padding: 6px 10px;
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
clear: both;
overflow: auto;
line-height: 1.4;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #DDDDDD !important;
border-radius: 4px !important;
border-collapse: separate !important;
line-height: 1.6;
}
table thead {
background: #F5F5F5;
}
table tr {
border: none;
border-bottom: none;
}
table th {
font-weight: bold;
}
table th, table td {
border: none !important;
padding: 6px 13px;
}
table tr:nth-child(2n) {
background-color: #F8F8F8;
}
td p {
margin: 0 0 15px 0;
}
div.table-contents td p {
margin: 0;
}
div.important *, div.note *, div.tip *, div.warning *, div.navheader *, div.navfooter *, div.calloutlist * {
border: none !important;
background: none !important;
margin: 0;
}
div.important p, div.note p, div.tip p, div.warning p {
color: #6F6F6F;
line-height: 1.6;
}
div.important code, div.note code, div.tip code, div.warning code {
background-color: #F2F2F2 !important;
border: 1px solid #CCCCCC !important;
border-radius: 4px !important;
padding: 1px 3px 0 !important;
text-shadow: none !important;
white-space: nowrap !important;
}
.note th, .tip th, .warning th {
display: none;
}
.note tr:first-child td, .tip tr:first-child td, .warning tr:first-child td {
border-right: 1px solid #CCCCCC !important;
padding-top: 10px;
}
div.calloutlist p, div.calloutlist td {
padding: 0;
margin: 0;
}
div.calloutlist > table > tbody > tr > td:first-child {
padding-left: 10px;
width: 30px !important;
}
div.important, div.note, div.tip, div.warning {
margin-left: 0px !important;
margin-right: 20px !important;
margin-top: 20px;
margin-bottom: 20px;
padding-top: 10px;
padding-bottom: 10px;
}
div.toc {
line-height: 1.2;
}
dl, dt {
margin-top: 1px;
margin-bottom: 0;
}
div.toc > dl > dt {
font-size: 32px;
font-weight: bold;
margin: 30px 0 10px 0;
display: block;
}
div.toc > dl > dd > dl > dt {
font-size: 24px;
font-weight: bold;
margin: 20px 0 10px 0;
display: block;
}
div.toc > dl > dd > dl > dd > dl > dt {
font-weight: bold;
font-size: 20px;
margin: 10px 0 0 0;
}
tbody.footnotes * {
border: none !important;
}
div.footnote p {
margin: 0;
line-height: 1;
}
div.footnote p sup {
margin-right: 6px;
vertical-align: middle;
}
div.navheader {
border-bottom: 1px solid #CCCCCC;
}
div.navfooter {
border-top: 1px solid #CCCCCC;
}
.title {
margin-left: -1em;
padding-left: 1em;
}
.title > a {
position: absolute;
visibility: hidden;
display: block;
font-size: 0.85em;
margin-top: 0.05em;
margin-left: -1em;
vertical-align: text-top;
color: black;
}
.title > a:before {
content: "\00A7";
}
.title:hover > a, .title > a:hover, .title:hover > a:hover {
visibility: visible;
}
.title:focus > a, .title > a:focus, .title:focus > a:focus {
outline: 0;
}

348
Finchley.SR4/ghpages.sh Normal file
View File

@@ -0,0 +1,348 @@
#!/bin/bash -x
set -e
# Set default props like MAVEN_PATH, ROOT_FOLDER etc.
function set_default_props() {
# The script should be executed from the root folder
ROOT_FOLDER=`pwd`
echo "Current folder is ${ROOT_FOLDER}"
if [[ ! -e "${ROOT_FOLDER}/.git" ]]; then
echo "You're not in the root folder of the project!"
exit 1
fi
# Prop that will let commit the changes
COMMIT_CHANGES="no"
MAVEN_PATH=${MAVEN_PATH:-}
echo "Path to Maven is [${MAVEN_PATH}]"
REPO_NAME=${PWD##*/}
echo "Repo name is [${REPO_NAME}]"
SPRING_CLOUD_STATIC_REPO=${SPRING_CLOUD_STATIC_REPO:-git@github.com:spring-cloud/spring-cloud-static.git}
echo "Spring Cloud Static repo is [${SPRING_CLOUD_STATIC_REPO}"
}
# Check if gh-pages exists and docs have been built
function check_if_anything_to_sync() {
git remote set-url --push origin `git config remote.origin.url | sed -e 's/^git:/https:/'`
if ! (git remote set-branches --add origin gh-pages && git fetch -q) && [[ "${RELEASE_TRAIN}" != "yes" ]] ; then
echo "No gh-pages, so not syncing"
exit 0
fi
if ! [ -d docs/target/generated-docs ] && ! [ "${BUILD}" == "yes" ]; then
echo "No gh-pages sources in docs/target/generated-docs, so not syncing"
exit 0
fi
}
function retrieve_current_branch() {
# Code getting the name of the current branch. For master we want to publish as we did until now
# http://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}
if [[ -z "${CURRENT_BRANCH}" ]] ; then
CURRENT_BRANCH=$(git symbolic-ref -q HEAD)
CURRENT_BRANCH=${CURRENT_BRANCH##refs/heads/}
CURRENT_BRANCH=${CURRENT_BRANCH:-HEAD}
fi
echo "Current branch is [${CURRENT_BRANCH}]"
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
}
# Switches to the provided value of the release version. We always prefix it with `v`
function switch_to_tag() {
if [[ "${RELEASE_TRAIN}" != "yes" ]] ; then
git checkout v${VERSION}
fi
}
# Build the docs if switch is on
function build_docs_if_applicable() {
if [[ "${BUILD}" == "yes" ]] ; then
./mvnw clean install -P docs -pl docs -DskipTests
fi
}
# Get the name of the `docs.main` property
# Get whitelisted branches - assumes that a `docs` module is available under `docs` profile
function retrieve_doc_properties() {
MAIN_ADOC_VALUE=$("${MAVEN_PATH}"mvn -q \
-Dexec.executable="echo" \
-Dexec.args='${docs.main}' \
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
-P docs \
-pl docs)
echo "Extracted 'main.adoc' from Maven build [${MAIN_ADOC_VALUE}]"
WHITELIST_PROPERTY=${WHITELIST_PROPERTY:-"docs.whitelisted.branches"}
WHITELISTED_BRANCHES_VALUE=$("${MAVEN_PATH}"mvn -q \
-Dexec.executable="echo" \
-Dexec.args="\${${WHITELIST_PROPERTY}}" \
org.codehaus.mojo:exec-maven-plugin:1.3.1:exec \
-P docs \
-pl docs)
echo "Extracted '${WHITELIST_PROPERTY}' from Maven build [${WHITELISTED_BRANCHES_VALUE}]"
}
# Stash any outstanding changes
function stash_changes() {
git diff-index --quiet HEAD && dirty=$? || (echo "Failed to check if the current repo is dirty. Assuming that it is." && dirty="1")
if [ "$dirty" != "0" ]; then git stash; fi
}
# Switch to gh-pages branch to sync it with current branch
function add_docs_from_target() {
local DESTINATION_REPO_FOLDER
if [[ -z "${DESTINATION}" && -z "${CLONE}" ]] ; then
DESTINATION_REPO_FOLDER=${ROOT_FOLDER}
elif [[ "${CLONE}" == "yes" ]]; then
mkdir -p ${ROOT_FOLDER}/target
local clonedStatic=${ROOT_FOLDER}/target/spring-cloud-static
if [[ ! -e "${clonedStatic}/.git" ]]; then
echo "Cloning Spring Cloud Static to target"
git clone ${SPRING_CLOUD_STATIC_REPO} ${clonedStatic} && cd ${clonedStatic} && git checkout gh-pages
else
echo "Spring Cloud Static already cloned - will pull changes"
cd ${clonedStatic} && git checkout gh-pages && git pull origin gh-pages
fi
if [[ -z "${RELEASE_TRAIN}" ]] ; then
DESTINATION_REPO_FOLDER=${clonedStatic}/${REPO_NAME}
else
DESTINATION_REPO_FOLDER=${clonedStatic}
fi
mkdir -p ${DESTINATION_REPO_FOLDER}
else
if [[ ! -e "${DESTINATION}/.git" ]]; then
echo "[${DESTINATION}] is not a git repository"
exit 1
fi
if [[ -z "${RELEASE_TRAIN}" ]] ; then
DESTINATION_REPO_FOLDER=${DESTINATION}/${REPO_NAME}
else
DESTINATION_REPO_FOLDER=${DESTINATION}
fi
mkdir -p ${DESTINATION_REPO_FOLDER}
echo "Destination was provided [${DESTINATION}]"
fi
cd ${DESTINATION_REPO_FOLDER}
git checkout gh-pages
git pull origin gh-pages
# Add git branches
###################################################################
if [[ -z "${VERSION}" && -z "${RELEASE_TRAIN}" ]] ; then
copy_docs_for_current_version
else
copy_docs_for_provided_version
fi
commit_changes_if_applicable
}
# 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"
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
# Not ignored...
cp -rf $f ${ROOT_FOLDER}/
git add -A ${ROOT_FOLDER}/$file
fi
done
COMMIT_CHANGES="yes"
else
echo -e "Current branch is [${CURRENT_BRANCH}]"
# http://stackoverflow.com/questions/29300806/a-bash-script-to-check-if-a-string-is-present-in-a-comma-separated-list-of-strin
if [[ ",${WHITELISTED_BRANCHES_VALUE}," = *",${CURRENT_BRANCH},"* ]] ; then
mkdir -p ${ROOT_FOLDER}/${CURRENT_BRANCH}
echo -e "Branch [${CURRENT_BRANCH}] is whitelisted! Will copy the current docs to the [${CURRENT_BRANCH}] 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
# Not ignored...
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
if [[ "${file}" == "${MAIN_ADOC_VALUE}.html" ]] ; then
# We don't want to copy the spring-cloud-sleuth.html
# we want it to be converted to index.html
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/index.html
else
cp -rf $f ${ROOT_FOLDER}/${CURRENT_BRANCH}
git add -A ${ROOT_FOLDER}/${CURRENT_BRANCH}/$file
fi
fi
done
COMMIT_CHANGES="yes"
else
echo -e "Branch [${CURRENT_BRANCH}] is not on the white list! Check out the Maven [${WHITELIST_PROPERTY}] property in
[docs] module available under [docs] profile. Won't commit any changes to gh-pages for this branch."
fi
fi
}
# Copies the docs by using the explicitly provided version
function copy_docs_for_provided_version() {
local FOLDER=${DESTINATION_REPO_FOLDER}/${VERSION}
mkdir -p ${FOLDER}
echo -e "Current tag is [v${VERSION}] Will copy the current docs to the [${FOLDER}] folder"
for f in ${ROOT_FOLDER}/docs/target/generated-docs/*; do
file=${f#${ROOT_FOLDER}/docs/target/generated-docs/*}
copy_docs_for_branch ${file} ${FOLDER}
done
COMMIT_CHANGES="yes"
CURRENT_BRANCH="v${VERSION}"
}
# Copies the docs from target to the provided destination
# Params:
# $1 - file from target
# $2 - destination to which copy the files
function copy_docs_for_branch() {
local file=$1
local destination=$2
if ! git ls-files -i -o --exclude-standard --directory | grep -q ^${file}$; then
# Not ignored...
# We want users to access 1.0.0.RELEASE/ instead of 1.0.0.RELEASE/spring-cloud.sleuth.html
if [[ ("${file}" == "${MAIN_ADOC_VALUE}.html") || ("${file}" == "${REPO_NAME}.html") ]] ; then
# We don't want to copy the spring-cloud-sleuth.html
# we want it to be converted to index.html
cp -rf $f ${destination}/index.html
git add -A ${destination}/index.html
else
cp -rf $f ${destination}
git add -A ${destination}/$file
fi
fi
}
function commit_changes_if_applicable() {
if [[ "${COMMIT_CHANGES}" == "yes" ]] ; then
COMMIT_SUCCESSFUL="no"
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.
# This is a little extreme. Use with care!
###################################################################
if [[ "${COMMIT_SUCCESSFUL}" == "yes" ]] ; then
git push origin gh-pages
fi
fi
}
# Switch back to the previous branch and exit block
function checkout_previous_branch() {
# If -version was provided we need to come back to root project
cd ${ROOT_FOLDER}
git checkout ${CURRENT_BRANCH} || echo "Failed to check the branch... continuing with the script"
if [ "$dirty" != "0" ]; then git stash pop; fi
exit 0
}
# Assert if properties have been properly passed
function assert_properties() {
echo "VERSION [${VERSION}], RELEASE_TRAIN [${RELEASE_TRAIN}], DESTINATION [${DESTINATION}], CLONE [${CLONE}]"
if [[ "${VERSION}" != "" && (-z "${DESTINATION}" && -z "${CLONE}") ]] ; then echo "Version was set but destination / clone was not!"; exit 1;fi
if [[ ("${DESTINATION}" != "" && "${CLONE}" != "") && -z "${VERSION}" ]] ; then echo "Destination / clone was set but version was not!"; exit 1;fi
if [[ "${DESTINATION}" != "" && "${CLONE}" == "yes" ]] ; then echo "Destination and clone was set. Pick one!"; exit 1;fi
if [[ "${RELEASE_TRAIN}" != "" && -z "${VERSION}" ]] ; then echo "Release train was set but no version was passed!"; exit 1;fi
}
# Prints the usage
function print_usage() {
cat <<EOF
The idea of this script is to update gh-pages branch with the generated docs. Without any options
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 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
with that version number will be created in the gh-pages branch. WARNING! No whitelist verification will take place
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
- if the destination switch is passed (-d) then the script will check if the provided dir is a git repo and then will
switch to gh-pages of that repo and copy the generated docs to `docs/<project-name>/<version>`
- if the release train switch is passed (-r) then the script will check if the provided dir is a git repo and then will
switch to gh-pages of that repo and copy the generated docs to `docs/<version>`
USAGE:
You can use the following options:
-v|--version - the script will apply the whole procedure for a particular library version
-r|--releasetrain - instead of nesting the docs under the project_name/version folder the docs will end up in version
-d|--destination - the root of destination folder where the docs should be copied. You have to use the full path.
E.g. point to spring-cloud-static folder. Can't be used with (-c)
-b|--build - will run the standard build process after checking out the branch
-c|--clone - will automatically clone the spring-cloud-static repo instead of providing the destination.
Obviously can't be used with (-d)
EOF
}
# ==========================================
# ____ ____ _____ _____ _____ _______
# / ____|/ ____| __ \|_ _| __ \__ __|
# | (___ | | | |__) | | | | |__) | | |
# \___ \| | | _ / | | | ___/ | |
# ____) | |____| | \ \ _| |_| | | |
# |_____/ \_____|_| \_\_____|_| |_|
#
# ==========================================
while [[ $# > 0 ]]
do
key="$1"
case ${key} in
-v|--version)
VERSION="$2"
shift # past argument
;;
-r|--releasetrain)
RELEASE_TRAIN="yes"
;;
-d|--destination)
DESTINATION="$2"
shift # past argument
;;
-b|--build)
BUILD="yes"
;;
-c|--clone)
CLONE="yes"
;;
-h|--help)
print_usage
exit 0
;;
*)
echo "Invalid option: [$1]"
print_usage
exit 1
;;
esac
shift # past argument or value
done
assert_properties
set_default_props
check_if_anything_to_sync
if [[ -z "${VERSION}" ]] ; then
retrieve_current_branch
else
switch_to_tag
fi
build_docs_if_applicable
retrieve_doc_properties
stash_changes
add_docs_from_target
checkout_previous_branch

View File

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
Finchley.SR4/images/pws.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

BIN
Finchley.SR4/images/tip.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

550
Finchley.SR4/index.html Normal file
View File

@@ -0,0 +1,550 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!--[if IE]><meta http-equiv="X-UA-Compatible" content="IE=edge"><![endif]-->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 1.5.7.1">
<title>Untitled</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700">
<style>
/* Asciidoctor default stylesheet | MIT License | http://asciidoctor.org */
/* Uncomment @import statement below to use as custom stylesheet */
/*@import "https://fonts.googleapis.com/css?family=Open+Sans:300,300italic,400,400italic,600,600italic%7CNoto+Serif:400,400italic,700,700italic%7CDroid+Sans+Mono:400,700";*/
article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block}
audio,canvas,video{display:inline-block}
audio:not([controls]){display:none;height:0}
script{display:none!important}
html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}
a{background:transparent}
a:focus{outline:thin dotted}
a:active,a:hover{outline:0}
h1{font-size:2em;margin:.67em 0}
abbr[title]{border-bottom:1px dotted}
b,strong{font-weight:bold}
dfn{font-style:italic}
hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0}
mark{background:#ff0;color:#000}
code,kbd,pre,samp{font-family:monospace;font-size:1em}
pre{white-space:pre-wrap}
q{quotes:"\201C" "\201D" "\2018" "\2019"}
small{font-size:80%}
sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}
sup{top:-.5em}
sub{bottom:-.25em}
img{border:0}
svg:not(:root){overflow:hidden}
figure{margin:0}
fieldset{border:1px solid silver;margin:0 2px;padding:.35em .625em .75em}
legend{border:0;padding:0}
button,input,select,textarea{font-family:inherit;font-size:100%;margin:0}
button,input{line-height:normal}
button,select{text-transform:none}
button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer}
button[disabled],html input[disabled]{cursor:default}
input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0}
button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0}
textarea{overflow:auto;vertical-align:top}
table{border-collapse:collapse;border-spacing:0}
*,*::before,*::after{-moz-box-sizing:border-box;-webkit-box-sizing:border-box;box-sizing:border-box}
html,body{font-size:100%}
body{background:#fff;color:rgba(0,0,0,.8);padding:0;margin:0;font-family:"Noto Serif","DejaVu Serif",serif;font-weight:400;font-style:normal;line-height:1;position:relative;cursor:auto;tab-size:4;-moz-osx-font-smoothing:grayscale;-webkit-font-smoothing:antialiased}
a:hover{cursor:pointer}
img,object,embed{max-width:100%;height:auto}
object,embed{height:100%}
img{-ms-interpolation-mode:bicubic}
.left{float:left!important}
.right{float:right!important}
.text-left{text-align:left!important}
.text-right{text-align:right!important}
.text-center{text-align:center!important}
.text-justify{text-align:justify!important}
.hide{display:none}
img,object,svg{display:inline-block;vertical-align:middle}
textarea{height:auto;min-height:50px}
select{width:100%}
.center{margin-left:auto;margin-right:auto}
.stretch{width:100%}
.subheader,.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{line-height:1.45;color:#7a2518;font-weight:400;margin-top:0;margin-bottom:.25em}
div,dl,dt,dd,ul,ol,li,h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6,pre,form,p,blockquote,th,td{margin:0;padding:0;direction:ltr}
a{color:#2156a5;text-decoration:underline;line-height:inherit}
a:hover,a:focus{color:#1d4b8f}
a img{border:none}
p{font-family:inherit;font-weight:400;font-size:1em;line-height:1.6;margin-bottom:1.25em;text-rendering:optimizeLegibility}
p aside{font-size:.875em;line-height:1.35;font-style:italic}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{font-family:"Open Sans","DejaVu Sans",sans-serif;font-weight:300;font-style:normal;color:#ba3925;text-rendering:optimizeLegibility;margin-top:1em;margin-bottom:.5em;line-height:1.0125em}
h1 small,h2 small,h3 small,#toctitle small,.sidebarblock>.content>.title small,h4 small,h5 small,h6 small{font-size:60%;color:#e99b8f;line-height:0}
h1{font-size:2.125em}
h2{font-size:1.6875em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.375em}
h4,h5{font-size:1.125em}
h6{font-size:1em}
hr{border:solid #ddddd8;border-width:1px 0 0;clear:both;margin:1.25em 0 1.1875em;height:0}
em,i{font-style:italic;line-height:inherit}
strong,b{font-weight:bold;line-height:inherit}
small{font-size:60%;line-height:inherit}
code{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;color:rgba(0,0,0,.9)}
ul,ol,dl{font-size:1em;line-height:1.6;margin-bottom:1.25em;list-style-position:outside;font-family:inherit}
ul,ol{margin-left:1.5em}
ul li ul,ul li ol{margin-left:1.25em;margin-bottom:0;font-size:1em}
ul.square li ul,ul.circle li ul,ul.disc li ul{list-style:inherit}
ul.square{list-style-type:square}
ul.circle{list-style-type:circle}
ul.disc{list-style-type:disc}
ol li ul,ol li ol{margin-left:1.25em;margin-bottom:0}
dl dt{margin-bottom:.3125em;font-weight:bold}
dl dd{margin-bottom:1.25em}
abbr,acronym{text-transform:uppercase;font-size:90%;color:rgba(0,0,0,.8);border-bottom:1px dotted #ddd;cursor:help}
abbr{text-transform:none}
blockquote{margin:0 0 1.25em;padding:.5625em 1.25em 0 1.1875em;border-left:1px solid #ddd}
blockquote cite{display:block;font-size:.9375em;color:rgba(0,0,0,.6)}
blockquote cite::before{content:"\2014 \0020"}
blockquote cite a,blockquote cite a:visited{color:rgba(0,0,0,.6)}
blockquote,blockquote p{line-height:1.6;color:rgba(0,0,0,.85)}
@media screen and (min-width:768px){h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2}
h1{font-size:2.75em}
h2{font-size:2.3125em}
h3,#toctitle,.sidebarblock>.content>.title{font-size:1.6875em}
h4{font-size:1.4375em}}
table{background:#fff;margin-bottom:1.25em;border:solid 1px #dedede}
table thead,table tfoot{background:#f7f8f7}
table thead tr th,table thead tr td,table tfoot tr th,table tfoot tr td{padding:.5em .625em .625em;font-size:inherit;color:rgba(0,0,0,.8);text-align:left}
table tr th,table tr td{padding:.5625em .625em;font-size:inherit;color:rgba(0,0,0,.8)}
table tr.even,table tr.alt,table tr:nth-of-type(even){background:#f8f8f7}
table thead tr th,table tfoot tr th,table tbody tr td,table tr td,table tfoot tr td{display:table-cell;line-height:1.6}
h1,h2,h3,#toctitle,.sidebarblock>.content>.title,h4,h5,h6{line-height:1.2;word-spacing:-.05em}
h1 strong,h2 strong,h3 strong,#toctitle strong,.sidebarblock>.content>.title strong,h4 strong,h5 strong,h6 strong{font-weight:400}
.clearfix::before,.clearfix::after,.float-group::before,.float-group::after{content:" ";display:table}
.clearfix::after,.float-group::after{clear:both}
*:not(pre)>code{font-size:.9375em;font-style:normal!important;letter-spacing:0;padding:.1em .5ex;word-spacing:-.15em;background-color:#f7f7f8;-webkit-border-radius:4px;border-radius:4px;line-height:1.45;text-rendering:optimizeSpeed;word-wrap:break-word}
*:not(pre)>code.nobreak{word-wrap:normal}
*:not(pre)>code.nowrap{white-space:nowrap}
pre,pre>code{line-height:1.45;color:rgba(0,0,0,.9);font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;font-weight:400;text-rendering:optimizeSpeed}
em em{font-style:normal}
strong strong{font-weight:400}
.keyseq{color:rgba(51,51,51,.8)}
kbd{font-family:"Droid Sans Mono","DejaVu Sans Mono",monospace;display:inline-block;color:rgba(0,0,0,.8);font-size:.65em;line-height:1.45;background-color:#f7f7f7;border:1px solid #ccc;-webkit-border-radius:3px;border-radius:3px;-webkit-box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em white inset;box-shadow:0 1px 0 rgba(0,0,0,.2),0 0 0 .1em #fff inset;margin:0 .15em;padding:.2em .5em;vertical-align:middle;position:relative;top:-.1em;white-space:nowrap}
.keyseq kbd:first-child{margin-left:0}
.keyseq kbd:last-child{margin-right:0}
.menuseq,.menuref{color:#000}
.menuseq b:not(.caret),.menuref{font-weight:inherit}
.menuseq{word-spacing:-.02em}
.menuseq b.caret{font-size:1.25em;line-height:.8}
.menuseq i.caret{font-weight:bold;text-align:center;width:.45em}
b.button::before,b.button::after{position:relative;top:-1px;font-weight:400}
b.button::before{content:"[";padding:0 3px 0 2px}
b.button::after{content:"]";padding:0 2px 0 3px}
p a>code:hover{color:rgba(0,0,0,.9)}
#header,#content,#footnotes,#footer{width:100%;margin-left:auto;margin-right:auto;margin-top:0;margin-bottom:0;max-width:62.5em;*zoom:1;position:relative;padding-left:.9375em;padding-right:.9375em}
#header::before,#header::after,#content::before,#content::after,#footnotes::before,#footnotes::after,#footer::before,#footer::after{content:" ";display:table}
#header::after,#content::after,#footnotes::after,#footer::after{clear:both}
#content{margin-top:1.25em}
#content::before{content:none}
#header>h1:first-child{color:rgba(0,0,0,.85);margin-top:2.25rem;margin-bottom:0}
#header>h1:first-child+#toc{margin-top:8px;border-top:1px solid #ddddd8}
#header>h1:only-child,body.toc2 #header>h1:nth-last-child(2){border-bottom:1px solid #ddddd8;padding-bottom:8px}
#header .details{border-bottom:1px solid #ddddd8;line-height:1.45;padding-top:.25em;padding-bottom:.25em;padding-left:.25em;color:rgba(0,0,0,.6);display:-ms-flexbox;display:-webkit-flex;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap}
#header .details span:first-child{margin-left:-.125em}
#header .details span.email a{color:rgba(0,0,0,.85)}
#header .details br{display:none}
#header .details br+span::before{content:"\00a0\2013\00a0"}
#header .details br+span.author::before{content:"\00a0\22c5\00a0";color:rgba(0,0,0,.85)}
#header .details br+span#revremark::before{content:"\00a0|\00a0"}
#header #revnumber{text-transform:capitalize}
#header #revnumber::after{content:"\00a0"}
#content>h1:first-child:not([class]){color:rgba(0,0,0,.85);border-bottom:1px solid #ddddd8;padding-bottom:8px;margin-top:0;padding-top:1rem;margin-bottom:1.25rem}
#toc{border-bottom:1px solid #efefed;padding-bottom:.5em}
#toc>ul{margin-left:.125em}
#toc ul.sectlevel0>li>a{font-style:italic}
#toc ul.sectlevel0 ul.sectlevel1{margin:.5em 0}
#toc ul{font-family:"Open Sans","DejaVu Sans",sans-serif;list-style-type:none}
#toc li{line-height:1.3334;margin-top:.3334em}
#toc a{text-decoration:none}
#toc a:active{text-decoration:underline}
#toctitle{color:#7a2518;font-size:1.2em}
@media screen and (min-width:768px){#toctitle{font-size:1.375em}
body.toc2{padding-left:15em;padding-right:0}
#toc.toc2{margin-top:0!important;background-color:#f8f8f7;position:fixed;width:15em;left:0;top:0;border-right:1px solid #efefed;border-top-width:0!important;border-bottom-width:0!important;z-index:1000;padding:1.25em 1em;height:100%;overflow:auto}
#toc.toc2 #toctitle{margin-top:0;margin-bottom:.8rem;font-size:1.2em}
#toc.toc2>ul{font-size:.9em;margin-bottom:0}
#toc.toc2 ul ul{margin-left:0;padding-left:1em}
#toc.toc2 ul.sectlevel0 ul.sectlevel1{padding-left:0;margin-top:.5em;margin-bottom:.5em}
body.toc2.toc-right{padding-left:0;padding-right:15em}
body.toc2.toc-right #toc.toc2{border-right-width:0;border-left:1px solid #efefed;left:auto;right:0}}
@media screen and (min-width:1280px){body.toc2{padding-left:20em;padding-right:0}
#toc.toc2{width:20em}
#toc.toc2 #toctitle{font-size:1.375em}
#toc.toc2>ul{font-size:.95em}
#toc.toc2 ul ul{padding-left:1.25em}
body.toc2.toc-right{padding-left:0;padding-right:20em}}
#content #toc{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
#content #toc>:first-child{margin-top:0}
#content #toc>:last-child{margin-bottom:0}
#footer{max-width:100%;background-color:rgba(0,0,0,.8);padding:1.25em}
#footer-text{color:rgba(255,255,255,.8);line-height:1.44}
#content{margin-bottom:.625em}
.sect1{padding-bottom:.625em}
@media screen and (min-width:768px){#content{margin-bottom:1.25em}
.sect1{padding-bottom:1.25em}}
.sect1:last-child{padding-bottom:0}
.sect1+.sect1{border-top:1px solid #efefed}
#content h1>a.anchor,h2>a.anchor,h3>a.anchor,#toctitle>a.anchor,.sidebarblock>.content>.title>a.anchor,h4>a.anchor,h5>a.anchor,h6>a.anchor{position:absolute;z-index:1001;width:1.5ex;margin-left:-1.5ex;display:block;text-decoration:none!important;visibility:hidden;text-align:center;font-weight:400}
#content h1>a.anchor::before,h2>a.anchor::before,h3>a.anchor::before,#toctitle>a.anchor::before,.sidebarblock>.content>.title>a.anchor::before,h4>a.anchor::before,h5>a.anchor::before,h6>a.anchor::before{content:"\00A7";font-size:.85em;display:block;padding-top:.1em}
#content h1:hover>a.anchor,#content h1>a.anchor:hover,h2:hover>a.anchor,h2>a.anchor:hover,h3:hover>a.anchor,#toctitle:hover>a.anchor,.sidebarblock>.content>.title:hover>a.anchor,h3>a.anchor:hover,#toctitle>a.anchor:hover,.sidebarblock>.content>.title>a.anchor:hover,h4:hover>a.anchor,h4>a.anchor:hover,h5:hover>a.anchor,h5>a.anchor:hover,h6:hover>a.anchor,h6>a.anchor:hover{visibility:visible}
#content h1>a.link,h2>a.link,h3>a.link,#toctitle>a.link,.sidebarblock>.content>.title>a.link,h4>a.link,h5>a.link,h6>a.link{color:#ba3925;text-decoration:none}
#content h1>a.link:hover,h2>a.link:hover,h3>a.link:hover,#toctitle>a.link:hover,.sidebarblock>.content>.title>a.link:hover,h4>a.link:hover,h5>a.link:hover,h6>a.link:hover{color:#a53221}
.audioblock,.imageblock,.literalblock,.listingblock,.stemblock,.videoblock{margin-bottom:1.25em}
.admonitionblock td.content>.title,.audioblock>.title,.exampleblock>.title,.imageblock>.title,.listingblock>.title,.literalblock>.title,.stemblock>.title,.openblock>.title,.paragraph>.title,.quoteblock>.title,table.tableblock>.title,.verseblock>.title,.videoblock>.title,.dlist>.title,.olist>.title,.ulist>.title,.qlist>.title,.hdlist>.title{text-rendering:optimizeLegibility;text-align:left;font-family:"Noto Serif","DejaVu Serif",serif;font-size:1rem;font-style:italic}
table.tableblock.fit-content>caption.title{white-space:nowrap;width:0}
.paragraph.lead>p,#preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:1.21875em;line-height:1.6;color:rgba(0,0,0,.85)}
table.tableblock #preamble>.sectionbody>[class="paragraph"]:first-of-type p{font-size:inherit}
.admonitionblock>table{border-collapse:separate;border:0;background:none;width:100%}
.admonitionblock>table td.icon{text-align:center;width:80px}
.admonitionblock>table td.icon img{max-width:none}
.admonitionblock>table td.icon .title{font-weight:bold;font-family:"Open Sans","DejaVu Sans",sans-serif;text-transform:uppercase}
.admonitionblock>table td.content{padding-left:1.125em;padding-right:1.25em;border-left:1px solid #ddddd8;color:rgba(0,0,0,.6)}
.admonitionblock>table td.content>:last-child>:last-child{margin-bottom:0}
.exampleblock>.content{border-style:solid;border-width:1px;border-color:#e6e6e6;margin-bottom:1.25em;padding:1.25em;background:#fff;-webkit-border-radius:4px;border-radius:4px}
.exampleblock>.content>:first-child{margin-top:0}
.exampleblock>.content>:last-child{margin-bottom:0}
.sidebarblock{border-style:solid;border-width:1px;border-color:#e0e0dc;margin-bottom:1.25em;padding:1.25em;background:#f8f8f7;-webkit-border-radius:4px;border-radius:4px}
.sidebarblock>:first-child{margin-top:0}
.sidebarblock>:last-child{margin-bottom:0}
.sidebarblock>.content>.title{color:#7a2518;margin-top:0;text-align:center}
.exampleblock>.content>:last-child>:last-child,.exampleblock>.content .olist>ol>li:last-child>:last-child,.exampleblock>.content .ulist>ul>li:last-child>:last-child,.exampleblock>.content .qlist>ol>li:last-child>:last-child,.sidebarblock>.content>:last-child>:last-child,.sidebarblock>.content .olist>ol>li:last-child>:last-child,.sidebarblock>.content .ulist>ul>li:last-child>:last-child,.sidebarblock>.content .qlist>ol>li:last-child>:last-child{margin-bottom:0}
.literalblock pre,.listingblock pre:not(.highlight),.listingblock pre[class="highlight"],.listingblock pre[class^="highlight "],.listingblock pre.CodeRay,.listingblock pre.prettyprint{background:#f7f7f8}
.sidebarblock .literalblock pre,.sidebarblock .listingblock pre:not(.highlight),.sidebarblock .listingblock pre[class="highlight"],.sidebarblock .listingblock pre[class^="highlight "],.sidebarblock .listingblock pre.CodeRay,.sidebarblock .listingblock pre.prettyprint{background:#f2f1f1}
.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{-webkit-border-radius:4px;border-radius:4px;word-wrap:break-word;padding:1em;font-size:.8125em}
.literalblock pre.nowrap,.literalblock pre[class].nowrap,.listingblock pre.nowrap,.listingblock pre[class].nowrap{overflow-x:auto;white-space:pre;word-wrap:normal}
@media screen and (min-width:768px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:.90625em}}
@media screen and (min-width:1280px){.literalblock pre,.literalblock pre[class],.listingblock pre,.listingblock pre[class]{font-size:1em}}
.literalblock.output pre{color:#f7f7f8;background-color:rgba(0,0,0,.9)}
.listingblock pre.highlightjs{padding:0}
.listingblock pre.highlightjs>code{padding:1em;-webkit-border-radius:4px;border-radius:4px}
.listingblock pre.prettyprint{border-width:0}
.listingblock>.content{position:relative}
.listingblock code[data-lang]::before{display:none;content:attr(data-lang);position:absolute;font-size:.75em;top:.425rem;right:.5rem;line-height:1;text-transform:uppercase;color:#999}
.listingblock:hover code[data-lang]::before{display:block}
.listingblock.terminal pre .command::before{content:attr(data-prompt);padding-right:.5em;color:#999}
.listingblock.terminal pre .command:not([data-prompt])::before{content:"$"}
table.pyhltable{border-collapse:separate;border:0;margin-bottom:0;background:none}
table.pyhltable td{vertical-align:top;padding-top:0;padding-bottom:0;line-height:1.45}
table.pyhltable td.code{padding-left:.75em;padding-right:0}
pre.pygments .lineno,table.pyhltable td:not(.code){color:#999;padding-left:0;padding-right:.5em;border-right:1px solid #ddddd8}
pre.pygments .lineno{display:inline-block;margin-right:.25em}
table.pyhltable .linenodiv{background:none!important;padding-right:0!important}
.quoteblock{margin:0 1em 1.25em 1.5em;display:table}
.quoteblock>.title{margin-left:-1.5em;margin-bottom:.75em}
.quoteblock blockquote,.quoteblock blockquote p{color:rgba(0,0,0,.85);font-size:1.15rem;line-height:1.75;word-spacing:.1em;letter-spacing:0;font-style:italic;text-align:justify}
.quoteblock blockquote{margin:0;padding:0;border:0}
.quoteblock blockquote::before{content:"\201c";float:left;font-size:2.75em;font-weight:bold;line-height:.6em;margin-left:-.6em;color:#7a2518;text-shadow:0 1px 2px rgba(0,0,0,.1)}
.quoteblock blockquote>.paragraph:last-child p{margin-bottom:0}
.quoteblock .attribution{margin-top:.5em;margin-right:.5ex;text-align:right}
.quoteblock .quoteblock{margin-left:0;margin-right:0;padding:.5em 0;border-left:3px solid rgba(0,0,0,.6)}
.quoteblock .quoteblock blockquote{padding:0 0 0 .75em}
.quoteblock .quoteblock blockquote::before{display:none}
.verseblock{margin:0 1em 1.25em}
.verseblock pre{font-family:"Open Sans","DejaVu Sans",sans;font-size:1.15rem;color:rgba(0,0,0,.85);font-weight:300;text-rendering:optimizeLegibility}
.verseblock pre strong{font-weight:400}
.verseblock .attribution{margin-top:1.25rem;margin-left:.5ex}
.quoteblock .attribution,.verseblock .attribution{font-size:.9375em;line-height:1.45;font-style:italic}
.quoteblock .attribution br,.verseblock .attribution br{display:none}
.quoteblock .attribution cite,.verseblock .attribution cite{display:block;letter-spacing:-.025em;color:rgba(0,0,0,.6)}
.quoteblock.abstract{margin:0 1em 1.25em;display:block}
.quoteblock.abstract>.title{margin:0 0 .375em;font-size:1.15em;text-align:center}
.quoteblock.abstract blockquote,.quoteblock.abstract blockquote p{word-spacing:0;line-height:1.6}
.quoteblock.abstract blockquote::before,.quoteblock.abstract p::before{display:none}
table.tableblock{max-width:100%;border-collapse:separate}
p.tableblock:last-child{margin-bottom:0}
td.tableblock>.content{margin-bottom:-1.25em}
table.tableblock,th.tableblock,td.tableblock{border:0 solid #dedede}
table.grid-all>thead>tr>.tableblock,table.grid-all>tbody>tr>.tableblock{border-width:0 1px 1px 0}
table.grid-all>tfoot>tr>.tableblock{border-width:1px 1px 0 0}
table.grid-cols>*>tr>.tableblock{border-width:0 1px 0 0}
table.grid-rows>thead>tr>.tableblock,table.grid-rows>tbody>tr>.tableblock{border-width:0 0 1px}
table.grid-rows>tfoot>tr>.tableblock{border-width:1px 0 0}
table.grid-all>*>tr>.tableblock:last-child,table.grid-cols>*>tr>.tableblock:last-child{border-right-width:0}
table.grid-all>tbody>tr:last-child>.tableblock,table.grid-all>thead:last-child>tr>.tableblock,table.grid-rows>tbody>tr:last-child>.tableblock,table.grid-rows>thead:last-child>tr>.tableblock{border-bottom-width:0}
table.frame-all{border-width:1px}
table.frame-sides{border-width:0 1px}
table.frame-topbot,table.frame-ends{border-width:1px 0}
table.stripes-all tr,table.stripes-odd tr:nth-of-type(odd){background:#f8f8f7}
table.stripes-none tr,table.stripes-odd tr:nth-of-type(even){background:none}
th.halign-left,td.halign-left{text-align:left}
th.halign-right,td.halign-right{text-align:right}
th.halign-center,td.halign-center{text-align:center}
th.valign-top,td.valign-top{vertical-align:top}
th.valign-bottom,td.valign-bottom{vertical-align:bottom}
th.valign-middle,td.valign-middle{vertical-align:middle}
table thead th,table tfoot th{font-weight:bold}
tbody tr th{display:table-cell;line-height:1.6;background:#f7f8f7}
tbody tr th,tbody tr th p,tfoot tr th,tfoot tr th p{color:rgba(0,0,0,.8);font-weight:bold}
p.tableblock>code:only-child{background:none;padding:0}
p.tableblock{font-size:1em}
td>div.verse{white-space:pre}
ol{margin-left:1.75em}
ul li ol{margin-left:1.5em}
dl dd{margin-left:1.125em}
dl dd:last-child,dl dd:last-child>:last-child{margin-bottom:0}
ol>li p,ul>li p,ul dd,ol dd,.olist .olist,.ulist .ulist,.ulist .olist,.olist .ulist{margin-bottom:.625em}
ul.checklist,ul.none,ol.none,ul.no-bullet,ol.no-bullet,ol.unnumbered,ul.unstyled,ol.unstyled{list-style-type:none}
ul.no-bullet,ol.no-bullet,ol.unnumbered{margin-left:.625em}
ul.unstyled,ol.unstyled{margin-left:0}
ul.checklist{margin-left:.625em}
ul.checklist li>p:first-child>.fa-square-o:first-child,ul.checklist li>p:first-child>.fa-check-square-o:first-child{width:1.25em;font-size:.8em;position:relative;bottom:.125em}
ul.checklist li>p:first-child>input[type="checkbox"]:first-child{margin-right:.25em}
ul.inline{display:-ms-flexbox;display:-webkit-box;display:flex;-ms-flex-flow:row wrap;-webkit-flex-flow:row wrap;flex-flow:row wrap;list-style:none;margin:0 0 .625em -1.25em}
ul.inline>li{margin-left:1.25em}
.unstyled dl dt{font-weight:400;font-style:normal}
ol.arabic{list-style-type:decimal}
ol.decimal{list-style-type:decimal-leading-zero}
ol.loweralpha{list-style-type:lower-alpha}
ol.upperalpha{list-style-type:upper-alpha}
ol.lowerroman{list-style-type:lower-roman}
ol.upperroman{list-style-type:upper-roman}
ol.lowergreek{list-style-type:lower-greek}
.hdlist>table,.colist>table{border:0;background:none}
.hdlist>table>tbody>tr,.colist>table>tbody>tr{background:none}
td.hdlist1,td.hdlist2{vertical-align:top;padding:0 .625em}
td.hdlist1{font-weight:bold;padding-bottom:1.25em}
.literalblock+.colist,.listingblock+.colist{margin-top:-.5em}
.colist td:not([class]):first-child{padding:.4em .75em 0;line-height:1;vertical-align:top}
.colist td:not([class]):first-child img{max-width:none}
.colist td:not([class]):last-child{padding:.25em 0}
.thumb,.th{line-height:0;display:inline-block;border:solid 4px #fff;-webkit-box-shadow:0 0 0 1px #ddd;box-shadow:0 0 0 1px #ddd}
.imageblock.left,.imageblock[style*="float: left"]{margin:.25em .625em 1.25em 0}
.imageblock.right,.imageblock[style*="float: right"]{margin:.25em 0 1.25em .625em}
.imageblock>.title{margin-bottom:0}
.imageblock.thumb,.imageblock.th{border-width:6px}
.imageblock.thumb>.title,.imageblock.th>.title{padding:0 .125em}
.image.left,.image.right{margin-top:.25em;margin-bottom:.25em;display:inline-block;line-height:0}
.image.left{margin-right:.625em}
.image.right{margin-left:.625em}
a.image{text-decoration:none;display:inline-block}
a.image object{pointer-events:none}
sup.footnote,sup.footnoteref{font-size:.875em;position:static;vertical-align:super}
sup.footnote a,sup.footnoteref a{text-decoration:none}
sup.footnote a:active,sup.footnoteref a:active{text-decoration:underline}
#footnotes{padding-top:.75em;padding-bottom:.75em;margin-bottom:.625em}
#footnotes hr{width:20%;min-width:6.25em;margin:-.25em 0 .75em;border-width:1px 0 0}
#footnotes .footnote{padding:0 .375em 0 .225em;line-height:1.3334;font-size:.875em;margin-left:1.2em;margin-bottom:.2em}
#footnotes .footnote a:first-of-type{font-weight:bold;text-decoration:none;margin-left:-1.05em}
#footnotes .footnote:last-of-type{margin-bottom:0}
#content #footnotes{margin-top:-.625em;margin-bottom:0;padding:.75em 0}
.gist .file-data>table{border:0;background:#fff;width:100%;margin-bottom:0}
.gist .file-data>table td.line-data{width:99%}
div.unbreakable{page-break-inside:avoid}
.big{font-size:larger}
.small{font-size:smaller}
.underline{text-decoration:underline}
.overline{text-decoration:overline}
.line-through{text-decoration:line-through}
.aqua{color:#00bfbf}
.aqua-background{background-color:#00fafa}
.black{color:#000}
.black-background{background-color:#000}
.blue{color:#0000bf}
.blue-background{background-color:#0000fa}
.fuchsia{color:#bf00bf}
.fuchsia-background{background-color:#fa00fa}
.gray{color:#606060}
.gray-background{background-color:#7d7d7d}
.green{color:#006000}
.green-background{background-color:#007d00}
.lime{color:#00bf00}
.lime-background{background-color:#00fa00}
.maroon{color:#600000}
.maroon-background{background-color:#7d0000}
.navy{color:#000060}
.navy-background{background-color:#00007d}
.olive{color:#606000}
.olive-background{background-color:#7d7d00}
.purple{color:#600060}
.purple-background{background-color:#7d007d}
.red{color:#bf0000}
.red-background{background-color:#fa0000}
.silver{color:#909090}
.silver-background{background-color:#bcbcbc}
.teal{color:#006060}
.teal-background{background-color:#007d7d}
.white{color:#bfbfbf}
.white-background{background-color:#fafafa}
.yellow{color:#bfbf00}
.yellow-background{background-color:#fafa00}
span.icon>.fa{cursor:default}
a span.icon>.fa{cursor:inherit}
.admonitionblock td.icon [class^="fa icon-"]{font-size:2.5em;text-shadow:1px 1px 2px rgba(0,0,0,.5);cursor:default}
.admonitionblock td.icon .icon-note::before{content:"\f05a";color:#19407c}
.admonitionblock td.icon .icon-tip::before{content:"\f0eb";text-shadow:1px 1px 2px rgba(155,155,0,.8);color:#111}
.admonitionblock td.icon .icon-warning::before{content:"\f071";color:#bf6900}
.admonitionblock td.icon .icon-caution::before{content:"\f06d";color:#bf3400}
.admonitionblock td.icon .icon-important::before{content:"\f06a";color:#bf0000}
.conum[data-value]{display:inline-block;color:#fff!important;background-color:rgba(0,0,0,.8);-webkit-border-radius:100px;border-radius:100px;text-align:center;font-size:.75em;width:1.67em;height:1.67em;line-height:1.67em;font-family:"Open Sans","DejaVu Sans",sans-serif;font-style:normal;font-weight:bold}
.conum[data-value] *{color:#fff!important}
.conum[data-value]+b{display:none}
.conum[data-value]::after{content:attr(data-value)}
pre .conum[data-value]{position:relative;top:-.125em}
b.conum *{color:inherit!important}
.conum:not([data-value]):empty{display:none}
dt,th.tableblock,td.content,div.footnote{text-rendering:optimizeLegibility}
h1,h2,p,td.content,span.alt{letter-spacing:-.01em}
p strong,td.content strong,div.footnote strong{letter-spacing:-.005em}
p,blockquote,dt,td.content,span.alt{font-size:1.0625rem}
p{margin-bottom:1.25rem}
.sidebarblock p,.sidebarblock dt,.sidebarblock td.content,p.tableblock{font-size:1em}
.exampleblock>.content{background-color:#fffef7;border-color:#e0e0dc;-webkit-box-shadow:0 1px 4px #e0e0dc;box-shadow:0 1px 4px #e0e0dc}
.print-only{display:none!important}
@page{margin:1.25cm .75cm}
@media print{*{-webkit-box-shadow:none!important;box-shadow:none!important;text-shadow:none!important}
html{font-size:80%}
a{color:inherit!important;text-decoration:underline!important}
a.bare,a[href^="#"],a[href^="mailto:"]{text-decoration:none!important}
a[href^="http:"]:not(.bare)::after,a[href^="https:"]:not(.bare)::after{content:"(" attr(href) ")";display:inline-block;font-size:.875em;padding-left:.25em}
abbr[title]::after{content:" (" attr(title) ")"}
pre,blockquote,tr,img,object,svg{page-break-inside:avoid}
thead{display:table-header-group}
svg{max-width:100%}
p,blockquote,dt,td.content{font-size:1em;orphans:3;widows:3}
h2,h3,#toctitle,.sidebarblock>.content>.title{page-break-after:avoid}
#toc,.sidebarblock,.exampleblock>.content{background:none!important}
#toc{border-bottom:1px solid #ddddd8!important;padding-bottom:0!important}
body.book #header{text-align:center}
body.book #header>h1:first-child{border:0!important;margin:2.5em 0 1em}
body.book #header .details{border:0!important;display:block;padding:0!important}
body.book #header .details span:first-child{margin-left:0!important}
body.book #header .details br{display:block}
body.book #header .details br+span::before{content:none!important}
body.book #toc{border:0!important;text-align:left!important;padding:0!important;margin:0!important}
body.book #toc,body.book #preamble,body.book h1.sect0,body.book .sect1>h2{page-break-before:always}
.listingblock code[data-lang]::before{display:block}
#footer{padding:0 .9375em}
.hide-on-print{display:none!important}
.print-only{display:block!important}
.hide-for-print{display:none!important}
.show-for-print{display:inherit!important}}
@media print,amzn-kf8{#header>h1:first-child{margin-top:1.25rem}
.sect1{padding:0!important}
.sect1+.sect1{border:0}
#footer{background:none}
#footer-text{color:rgba(0,0,0,.6);font-size:.9em}}
@media amzn-kf8{#header,#content,#footnotes,#footer{padding:0}}
</style>
<style>
.hidden {
display: none;
}
.switch {
border-width: 1px 1px 0 1px;
border-style: solid;
border-color: #7a2518;
display: inline-block;
}
.switch--item {
padding: 10px;
background-color: #ffffff;
color: #7a2518;
display: inline-block;
cursor: pointer;
}
.switch--item.selected {
background-color: #7a2519;
color: #ffffff;
}
</style>
<script src="http://cdnjs.cloudflare.com/ajax/libs/zepto/1.2.0/zepto.min.js"></script>
<script type="text/javascript">
function addBlockSwitches() {
$('.primary').each(function() {
primary = $(this);
createSwitchItem(primary, createBlockSwitch(primary)).item.addClass("selected");
primary.children('.title').remove();
});
$('.secondary').each(function(idx, node) {
secondary = $(node);
primary = findPrimary(secondary);
switchItem = createSwitchItem(secondary, primary.children('.switch'));
switchItem.content.addClass('hidden');
findPrimary(secondary).append(switchItem.content);
secondary.remove();
});
}
function createBlockSwitch(primary) {
blockSwitch = $('<div class="switch"></div>');
primary.prepend(blockSwitch);
return blockSwitch;
}
function findPrimary(secondary) {
candidate = secondary.prev();
while (!candidate.is('.primary')) {
candidate = candidate.prev();
}
return candidate;
}
function createSwitchItem(block, blockSwitch) {
blockName = block.children('.title').text();
content = block.children('.content').first().append(block.next('.colist'));
item = $('<div class="switch--item">' + blockName + '</div>');
item.on('click', '', content, function(e) {
$(this).addClass('selected');
$(this).siblings().removeClass('selected');
e.data.siblings('.content').addClass('hidden');
e.data.removeClass('hidden');
});
blockSwitch.append(item);
return {'item': item, 'content': content};
}
$(addBlockSwitches);
</script>
</head>
<body class="article">
<div id="header">
</div>
<div id="content">
<div class="literalblock">
<div class="content">
<pre>:linkcss:
:stylesdir: css
:stylesheet: manual-singlepage.css</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>= {docs-main}</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>{spring-cloud-version}</pre>
</div>
</div>
<div class="literalblock">
<div class="content">
<pre>== Pick The Documentation Option</pre>
</div>
</div>
<div class="ulist">
<ul>
<li>
<p><a href="single/spring-cloud.html">Single
HTML</a></p>
</li>
<li>
<p><a href="multi/multi_spring-cloud.html">Multi
HTML</a></p>
</li>
</ul>
</div>
</div>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prettify/r298/prettify.min.js"></script>
<script>prettyPrint()</script>
</body>
</html>

View File

@@ -0,0 +1,35 @@
/*
code highlight CSS resemblign the Eclipse IDE default color schema
@author Costin Leau
*/
.hl-keyword {
color: #7F0055;
font-weight: bold;
}
.hl-comment {
color: #3F5F5F;
font-style: italic;
}
.hl-multiline-comment {
color: #3F5FBF;
font-style: italic;
}
.hl-tag {
color: #3F7F7F;
}
.hl-attribute {
color: #7F007F;
}
.hl-value {
color: #2A00FF;
}
.hl-string {
color: #2A00FF;
}

View File

@@ -0,0 +1,9 @@
@IMPORT url("manual.css");
body.firstpage {
background: url("../images/background.png") no-repeat center top;
}
div.part h1 {
border-top: none;
}

View File

@@ -0,0 +1,6 @@
@IMPORT url("manual.css");
body {
background: url("../images/background.png") no-repeat center top;
}

View File

@@ -0,0 +1,342 @@
@IMPORT url("highlight.css");
html {
padding: 0pt;
margin: 0pt;
}
body {
color: #333333;
margin: 15px 30px;
font-family: Helvetica, Arial, Freesans, Clean, Sans-serif;
line-height: 1.6;
-webkit-font-smoothing: antialiased;
}
code {
font-size: 16px;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
:not(a) > code {
color: #6D180B;
}
:not(pre) > code {
background-color: #F2F2F2;
border: 1px solid #CCCCCC;
border-radius: 4px;
padding: 1px 3px 0;
text-shadow: none;
white-space: nowrap;
}
body > *:first-child {
margin-top: 0 !important;
}
div {
margin: 0pt;
}
hr {
border: 1px solid #CCCCCC;
background: #CCCCCC;
}
h1, h2, h3, h4, h5, h6 {
color: #000000;
cursor: text;
font-weight: bold;
margin: 30px 0 10px;
padding: 0;
}
h1, h2, h3 {
margin: 40px 0 10px;
}
h1 {
margin: 70px 0 30px;
padding-top: 20px;
}
div.part h1 {
border-top: 1px dotted #CCCCCC;
}
h1, h1 code {
font-size: 32px;
}
h2, h2 code {
font-size: 24px;
}
h3, h3 code {
font-size: 20px;
}
h4, h1 code, h5, h5 code, h6, h6 code {
font-size: 18px;
}
div.book, div.chapter, div.appendix, div.part, div.preface {
min-width: 300px;
max-width: 1200px;
margin: 0 auto;
}
p.releaseinfo {
font-weight: bold;
margin-bottom: 40px;
margin-top: 40px;
}
div.authorgroup {
line-height: 1;
}
p.copyright {
line-height: 1;
margin-bottom: -5px;
}
.legalnotice p {
font-style: italic;
font-size: 14px;
line-height: 1;
}
div.titlepage + p, div.titlepage + p {
margin-top: 0;
}
pre {
line-height: 1.0;
color: black;
}
a {
color: #4183C4;
text-decoration: none;
}
p {
margin: 15px 0;
text-align: left;
}
ul, ol {
padding-left: 30px;
}
li p {
margin: 0;
}
div.table {
margin: 1em;
padding: 0.5em;
text-align: center;
}
div.table table, div.informaltable table {
display: table;
width: 100%;
}
div.table td {
padding-left: 7px;
padding-right: 7px;
}
.sidebar {
line-height: 1.4;
padding: 0 20px;
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
}
.sidebar p.title {
color: #6D180B;
}
pre.programlisting, pre.screen {
font-size: 15px;
padding: 6px 10px;
background-color: #F8F8F8;
border: 1px solid #CCCCCC;
border-radius: 3px 3px 3px 3px;
clear: both;
overflow: auto;
line-height: 1.4;
font-family: Consolas, "Liberation Mono", Courier, monospace;
}
table {
border-collapse: collapse;
border-spacing: 0;
border: 1px solid #DDDDDD !important;
border-radius: 4px !important;
border-collapse: separate !important;
line-height: 1.6;
}
table thead {
background: #F5F5F5;
}
table tr {
border: none;
border-bottom: none;
}
table th {
font-weight: bold;
}
table th, table td {
border: none !important;
padding: 6px 13px;
}
table tr:nth-child(2n) {
background-color: #F8F8F8;
}
td p {
margin: 0 0 15px 0;
}
div.table-contents td p {
margin: 0;
}
div.important *, div.note *, div.tip *, div.warning *, div.navheader *, div.navfooter *, div.calloutlist * {
border: none !important;
background: none !important;
margin: 0;
}
div.important p, div.note p, div.tip p, div.warning p {
color: #6F6F6F;
line-height: 1.6;
}
div.important code, div.note code, div.tip code, div.warning code {
background-color: #F2F2F2 !important;
border: 1px solid #CCCCCC !important;
border-radius: 4px !important;
padding: 1px 3px 0 !important;
text-shadow: none !important;
white-space: nowrap !important;
}
.note th, .tip th, .warning th {
display: none;
}
.note tr:first-child td, .tip tr:first-child td, .warning tr:first-child td {
border-right: 1px solid #CCCCCC !important;
padding-top: 10px;
}
div.calloutlist p, div.calloutlist td {
padding: 0;
margin: 0;
}
div.calloutlist > table > tbody > tr > td:first-child {
padding-left: 10px;
width: 30px !important;
}
div.important, div.note, div.tip, div.warning {
margin-left: 0px !important;
margin-right: 20px !important;
margin-top: 20px;
margin-bottom: 20px;
padding-top: 10px;
padding-bottom: 10px;
}
div.toc {
line-height: 1.2;
}
dl, dt {
margin-top: 1px;
margin-bottom: 0;
}
div.toc > dl > dt {
font-size: 32px;
font-weight: bold;
margin: 30px 0 10px 0;
display: block;
}
div.toc > dl > dd > dl > dt {
font-size: 24px;
font-weight: bold;
margin: 20px 0 10px 0;
display: block;
}
div.toc > dl > dd > dl > dd > dl > dt {
font-weight: bold;
font-size: 20px;
margin: 10px 0 0 0;
}
tbody.footnotes * {
border: none !important;
}
div.footnote p {
margin: 0;
line-height: 1;
}
div.footnote p sup {
margin-right: 6px;
vertical-align: middle;
}
div.navheader {
border-bottom: 1px solid #CCCCCC;
}
div.navfooter {
border-top: 1px solid #CCCCCC;
}
.title {
margin-left: -1em;
padding-left: 1em;
}
.title > a {
position: absolute;
visibility: hidden;
display: block;
font-size: 0.85em;
margin-top: 0.05em;
margin-left: -1em;
vertical-align: text-top;
color: black;
}
.title > a:before {
content: "\00A7";
}
.title:hover > a, .title > a:hover, .title:hover > a:hover {
visibility: visible;
}
.title:focus > a, .title > a:focus, .title:focus > a:focus {
outline: 0;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 353 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 350 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 931 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,4 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>49.&nbsp;Additional Resources</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"><link rel="prev" href="multi__introduction.html" title="48.&nbsp;Introduction"><link rel="next" href="multi__features_2.html" title="50.&nbsp;Features"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">49.&nbsp;Additional Resources</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__introduction.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__features_2.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_additional_resources" href="#_additional_resources"></a>49.&nbsp;Additional Resources</h2></div></div></div><p>You can watch a video of <a class="link" href="https://twitter.com/reshmi9k" target="_top">Reshmi Krishna</a> and <a class="link" href="https://twitter.com/mgrzejszczak" target="_top">Marcin Grzejszczak</a> talking about Spring Cloud
Sleuth and Zipkin <a class="link" href="https://content.pivotal.io/springone-platform-2017/distributed-tracing-latency-analysis-for-your-microservices-grzejszczak-krishna" target="_top">by clicking here</a>.</p><p>You can check different setups of Sleuth and Brave <a class="link" href="https://github.com/openzipkin/sleuth-webmvc-example" target="_top">in the openzipkin/sleuth-webmvc-example repository</a>.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__introduction.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_sleuth.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__features_2.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">48.&nbsp;Introduction&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;50.&nbsp;Features</td></tr></table></div></body></html>

View File

@@ -0,0 +1,6 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>43.&nbsp;Addressing All Instances of a Service</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_bus.html" title="Part&nbsp;VII.&nbsp;Spring Cloud Bus"><link rel="prev" href="multi__addressing_an_instance.html" title="42.&nbsp;Addressing an Instance"><link rel="next" href="multi__service_id_must_be_unique.html" title="44.&nbsp;Service ID Must Be Unique"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">43.&nbsp;Addressing All Instances of a Service</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__addressing_an_instance.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VII.&nbsp;Spring Cloud Bus</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__service_id_must_be_unique.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_addressing_all_instances_of_a_service" href="#_addressing_all_instances_of_a_service"></a>43.&nbsp;Addressing All Instances of a Service</h2></div></div></div><p>The <span class="quote">&#8220;<span class="quote">destination</span>&#8221;</span> parameter is used in a Spring <code class="literal">PathMatcher</code> (with the path separator
as a colon&#8201;&#8212;&#8201;<code class="literal">:</code>) to determine if an instance processes the message. Using the example
from earlier, <code class="literal">/bus-env/customers:**</code> targets all instances of the
<span class="quote">&#8220;<span class="quote">customers</span>&#8221;</span> service regardless of the rest of the service ID.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__addressing_an_instance.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_bus.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__service_id_must_be_unique.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">42.&nbsp;Addressing an Instance&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;44.&nbsp;Service ID Must Be Unique</td></tr></table></div></body></html>

View File

@@ -0,0 +1,12 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>42.&nbsp;Addressing an Instance</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_bus.html" title="Part&nbsp;VII.&nbsp;Spring Cloud Bus"><link rel="prev" href="multi__bus_endpoints.html" title="41.&nbsp;Bus Endpoints"><link rel="next" href="multi__addressing_all_instances_of_a_service.html" title="43.&nbsp;Addressing All Instances of a Service"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">42.&nbsp;Addressing an Instance</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__bus_endpoints.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VII.&nbsp;Spring Cloud Bus</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__addressing_all_instances_of_a_service.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_addressing_an_instance" href="#_addressing_an_instance"></a>42.&nbsp;Addressing an Instance</h2></div></div></div><p>Each instance of the application has a service ID, whose value can be set with
<code class="literal">spring.cloud.bus.id</code> and whose value is expected to be a colon-separated list of
identifiers, in order from least specific to most specific. The default value is
constructed from the environment as a combination of the <code class="literal">spring.application.name</code> and
<code class="literal">server.port</code> (or <code class="literal">spring.application.index</code>, if set). The default value of the ID is
constructed in the form of <code class="literal">app:index:id</code>, where:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">app</code> is the <code class="literal">vcap.application.name</code>, if it exists, or <code class="literal">spring.application.name</code></li><li class="listitem"><code class="literal">index</code> is the <code class="literal">vcap.application.instance_index</code>, if it exists,
<code class="literal">spring.application.index</code>, <code class="literal">local.server.port</code>, <code class="literal">server.port</code>, or <code class="literal">0</code> (in that order).</li><li class="listitem"><code class="literal">id</code> is the <code class="literal">vcap.application.instance_id</code>, if it exists, or a random value.</li></ul></div><p>The HTTP endpoints accept a <span class="quote">&#8220;<span class="quote">destination</span>&#8221;</span> path parameter, such as
<code class="literal">/bus-refresh/customers:9000</code>, where <code class="literal">destination</code> is a service ID. If the ID
is owned by an instance on the bus, it processes the message, and all other instances
ignore it.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__bus_endpoints.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_bus.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__addressing_all_instances_of_a_service.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">41.&nbsp;Bus Endpoints&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;43.&nbsp;Addressing All Instances of a Service</td></tr></table></div></body></html>

View File

@@ -0,0 +1,304 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>37.&nbsp;Apache Kafka Binder</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__binder_implementations.html" title="Part&nbsp;VI.&nbsp;Binder Implementations"><link rel="prev" href="multi__binder_implementations.html" title="Part&nbsp;VI.&nbsp;Binder Implementations"><link rel="next" href="multi__apache_kafka_streams_binder.html" title="38.&nbsp;Apache Kafka Streams Binder"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">37.&nbsp;Apache Kafka Binder</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__binder_implementations.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VI.&nbsp;Binder Implementations</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__apache_kafka_streams_binder.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_apache_kafka_binder" href="#_apache_kafka_binder"></a>37.&nbsp;Apache Kafka Binder</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_usage" href="#_usage"></a>37.1&nbsp;Usage</h2></div></div></div><p>To use Apache Kafka binder, you need to add <code class="literal">spring-cloud-stream-binder-kafka</code> as a dependency to your Spring Cloud Stream application, as shown in the following example for Maven:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-kafka<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p>Alternatively, you can also use the Spring Cloud Stream Kafka Starter, as shown inn the following example for Maven:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-stream-kafka<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_apache_kafka_binder_overview" href="#_apache_kafka_binder_overview"></a>37.2&nbsp;Apache Kafka Binder Overview</h2></div></div></div><p>The following image shows a simplified diagram of how the Apache Kafka binder operates:</p><div class="figure"><a name="d0e11625" href="#d0e11625"></a><p class="title"><b>Figure&nbsp;37.1.&nbsp;Kafka Binder</b></p><div class="figure-contents"><div class="mediaobject"><img src="images/images/kafka-binder.png" alt="kafka binder"></div></div></div><br class="figure-break"><p>The Apache Kafka Binder implementation maps each destination to an Apache Kafka topic.
The consumer group maps directly to the same Apache Kafka concept.
Partitioning also maps directly to Apache Kafka partitions as well.</p><p>The binder currently uses the Apache Kafka <code class="literal">kafka-clients</code> 1.0.0 jar and is designed to be used with a broker of at least that version.
This client can communicate with older brokers (see the Kafka documentation), but certain features may not be available.
For example, with versions earlier than 0.11.x.x, native headers are not supported.
Also, 0.11.x.x does not support the <code class="literal">autoAddPartitions</code> property.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_options_2" href="#_configuration_options_2"></a>37.3&nbsp;Configuration Options</h2></div></div></div><p>This section contains the configuration options used by the Apache Kafka binder.</p><p>For common configuration options and properties pertaining to binder, see the <a class="link" href="multi__configuration_options.html#binding-properties" title="29.2&nbsp;Binding Properties">core documentation</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_kafka_binder_properties" href="#_kafka_binder_properties"></a>37.3.1&nbsp;Kafka Binder Properties</h3></div></div></div><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.binder.brokers</span></dt><dd><p class="simpara">A list of brokers to which the Kafka binder connects.</p><p class="simpara">Default: <code class="literal">localhost</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.defaultBrokerPort</span></dt><dd><p class="simpara"><code class="literal">brokers</code> allows hosts specified with or without port information (for example, <code class="literal">host1,host2:port2</code>).
This sets the default port when no port is configured in the broker list.</p><p class="simpara">Default: <code class="literal">9092</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.configuration</span></dt><dd><p class="simpara">Key/Value map of client properties (both producers and consumer) passed to all clients created by the binder.
Due to the fact that these properties are used by both producers and consumers, usage should be restricted to common properties&#8201;&#8212;&#8201;for example, security settings.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.headers</span></dt><dd><p class="simpara">The list of custom headers that are transported by the binder.
Only required when communicating with older applications (&#8656; 1.3.x) with a <code class="literal">kafka-clients</code> version &lt; 0.11.0.0. Newer versions support headers natively.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.healthTimeout</span></dt><dd><p class="simpara">The time to wait to get partition information, in seconds.
Health reports as down if this timer expires.</p><p class="simpara">Default: 10.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.requiredAcks</span></dt><dd><p class="simpara">The number of required acks on the broker.
See the Kafka documentation for the producer <code class="literal">acks</code> property.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.minPartitionCount</span></dt><dd><p class="simpara">Effective only if <code class="literal">autoCreateTopics</code> or <code class="literal">autoAddPartitions</code> is set.
The global minimum number of partitions that the binder configures on topics on which it produces or consumes data.
It can be superseded by the <code class="literal">partitionCount</code> setting of the producer or by the value of <code class="literal">instanceCount * concurrency</code> settings of the producer (if either is larger).</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.replicationFactor</span></dt><dd><p class="simpara">The replication factor of auto-created topics if <code class="literal">autoCreateTopics</code> is active.
Can be overridden on each binding.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.autoCreateTopics</span></dt><dd><p class="simpara">If set to <code class="literal">true</code>, the binder creates new topics automatically.
If set to <code class="literal">false</code>, the binder relies on the topics being already configured.
In the latter case, if the topics do not exist, the binder fails to start.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>This setting is independent of the <code class="literal">auto.topic.create.enable</code> setting of the broker and does not influence it.
If the server is set to auto-create topics, they may be created as part of the metadata retrieval request, with default broker settings.</p></td></tr></table></div><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.autoAddPartitions</span></dt><dd><p class="simpara">If set to <code class="literal">true</code>, the binder creates new partitions if required.
If set to <code class="literal">false</code>, the binder relies on the partition size of the topic being already configured.
If the partition count of the target topic is smaller than the expected value, the binder fails to start.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix</span></dt><dd><p class="simpara">Enables transactions in the binder. See <code class="literal">transaction.id</code> in the Kafka documentation and <a class="link" href="https://docs.spring.io/spring-kafka/reference/html/_reference.html#transactions" target="_top">Transactions</a> in the <code class="literal">spring-kafka</code> documentation.
When transactions are enabled, individual <code class="literal">producer</code> properties are ignored and all producers use the <code class="literal">spring.cloud.stream.kafka.binder.transaction.producer.*</code> properties.</p><p class="simpara">Default <code class="literal">null</code> (no transactions)</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.transaction.producer.*</span></dt><dd><p class="simpara">Global producer properties for producers in a transactional binder.
See <code class="literal">spring.cloud.stream.kafka.binder.transaction.transactionIdPrefix</code> and <a class="xref" href="multi__apache_kafka_binder.html#kafka-producer-properties" title="37.3.3&nbsp;Kafka Producer Properties">Section&nbsp;37.3.3, &#8220;Kafka Producer Properties&#8221;</a> and the general producer properties supported by all binders.</p><p class="simpara">Default: See individual producer properties.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.headerMapperBeanName</span></dt><dd><p class="simpara">The bean name of a <code class="literal">KafkaHeaderMapper</code> used for mapping <code class="literal">spring-messaging</code> headers to and from Kafka headers.
Use this, for example, if you wish to customize the trusted packages in a <code class="literal">DefaultKafkaHeaderMapper</code> that uses JSON deserialization for the headers.</p><p class="simpara">Default: none.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="kafka-consumer-properties" href="#kafka-consumer-properties"></a>37.3.2&nbsp;Kafka Consumer Properties</h3></div></div></div><p>The following properties are available for Kafka consumers only and
must be prefixed with <code class="literal">spring.cloud.stream.kafka.bindings.&lt;channelName&gt;.consumer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">admin.configuration</span></dt><dd><p class="simpara">A <code class="literal">Map</code> of Kafka topic properties used when provisioning topics&#8201;&#8212;&#8201;for example, <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0</code></p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replicas-assignment</span></dt><dd><p class="simpara">A Map&lt;Integer, List&lt;Integer&gt;&gt; of replica assignments, with the key being the partition and the value being the assignments.
Used when provisioning new topics.
See the <code class="literal">NewTopic</code> Javadocs in the <code class="literal">kafka-clients</code> jar.</p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replication-factor</span></dt><dd><p class="simpara">The replication factor to use when provisioning topics. Overrides the binder-wide setting.
Ignored if <code class="literal">replicas-assignments</code> is present.</p><p class="simpara">Default: none (the binder-wide default of 1 is used).</p></dd><dt><span class="term">autoRebalanceEnabled</span></dt><dd><p class="simpara">When <code class="literal">true</code>, topic partitions is automatically rebalanced between the members of a consumer group.
When <code class="literal">false</code>, each consumer is assigned a fixed set of partitions based on <code class="literal">spring.cloud.stream.instanceCount</code> and <code class="literal">spring.cloud.stream.instanceIndex</code>.
This requires both the <code class="literal">spring.cloud.stream.instanceCount</code> and <code class="literal">spring.cloud.stream.instanceIndex</code> properties to be set appropriately on each launched instance.
The value of the <code class="literal">spring.cloud.stream.instanceCount</code> property must typically be greater than 1 in this case.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">ackEachRecord</span></dt><dd><p class="simpara">When <code class="literal">autoCommitOffset</code> is <code class="literal">true</code>, this setting dictates whether to commit the offset after each record is processed.
By default, offsets are committed after all records in the batch of records returned by <code class="literal">consumer.poll()</code> have been processed.
The number of records returned by a poll can be controlled with the <code class="literal">max.poll.records</code> Kafka property, which is set through the consumer <code class="literal">configuration</code> property.
Setting this to <code class="literal">true</code> may cause a degradation in performance, but doing so reduces the likelihood of redelivered records when a failure occurs.
Also, see the binder <code class="literal">requiredAcks</code> property, which also affects the performance of committing offsets.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">autoCommitOffset</span></dt><dd><p class="simpara">Whether to autocommit offsets when a message has been processed.
If set to <code class="literal">false</code>, a header with the key <code class="literal">kafka_acknowledgment</code> of the type <code class="literal">org.springframework.kafka.support.Acknowledgment</code> header is present in the inbound message.
Applications may use this header for acknowledging messages.
See the examples section for details.
When this property is set to <code class="literal">false</code>, Kafka binder sets the ack mode to <code class="literal">org.springframework.kafka.listener.AbstractMessageListenerContainer.AckMode.MANUAL</code> and the application is responsible for acknowledging records.
Also see <code class="literal">ackEachRecord</code>.</p><p class="simpara">Default: <code class="literal">true</code>.</p></dd><dt><span class="term">autoCommitOnError</span></dt><dd><p class="simpara">Effective only if <code class="literal">autoCommitOffset</code> is set to <code class="literal">true</code>.
If set to <code class="literal">false</code>, it suppresses auto-commits for messages that result in errors and commits only for successful messages. It allows a stream to automatically replay from the last successfully processed message, in case of persistent failures.
If set to <code class="literal">true</code>, it always auto-commits (if auto-commit is enabled).
If not set (the default), it effectively has the same value as <code class="literal">enableDlq</code>, auto-committing erroneous messages if they are sent to a DLQ and not committing them otherwise.</p><p class="simpara">Default: not set.</p></dd><dt><span class="term">resetOffsets</span></dt><dd><p class="simpara">Whether to reset offsets on the consumer to the value provided by startOffset.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">startOffset</span></dt><dd><p class="simpara">The starting offset for new groups.
Allowed values: <code class="literal">earliest</code> and <code class="literal">latest</code>.
If the consumer group is set explicitly for the consumer 'binding' (through <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.group</code>), 'startOffset' is set to <code class="literal">earliest</code>. Otherwise, it is set to <code class="literal">latest</code> for the <code class="literal">anonymous</code> consumer group.
Also see <code class="literal">resetOffsets</code> (earlier in this list).</p><p class="simpara">Default: null (equivalent to <code class="literal">earliest</code>).</p></dd><dt><span class="term">enableDlq</span></dt><dd><p class="simpara">When set to true, it enables DLQ behavior for the consumer.
By default, messages that result in errors are forwarded to a topic named <code class="literal">error.&lt;destination&gt;.&lt;group&gt;</code>.
The DLQ topic name can be configurable by setting the <code class="literal">dlqName</code> property.
This provides an alternative option to the more common Kafka replay scenario for the case when the number of errors is relatively small and replaying the entire original topic may be too cumbersome.
See <a class="xref" href="multi__apache_kafka_binder.html#kafka-dlq-processing" title="37.6&nbsp;Dead-Letter Topic Processing">Section&nbsp;37.6, &#8220;Dead-Letter Topic Processing&#8221;</a> processing for more information.
Starting with version 2.0, messages sent to the DLQ topic are enhanced with the following headers: <code class="literal">x-original-topic</code>, <code class="literal">x-exception-message</code>, and <code class="literal">x-exception-stacktrace</code> as <code class="literal">byte[]</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">configuration</span></dt><dd><p class="simpara">Map with a key/value pair containing generic Kafka consumer properties.</p><p class="simpara">Default: Empty map.</p></dd><dt><span class="term">dlqName</span></dt><dd><p class="simpara">The name of the DLQ topic to receive the error messages.</p><p class="simpara">Default: null (If not specified, messages that result in errors are forwarded to a topic named <code class="literal">error.&lt;destination&gt;.&lt;group&gt;</code>).</p></dd><dt><span class="term">dlqProducerProperties</span></dt><dd><p class="simpara">Using this, DLQ-specific producer properties can be set.
All the properties available through kafka producer properties can be set through this property.</p><p class="simpara">Default: Default Kafka producer properties.</p></dd><dt><span class="term">standardHeaders</span></dt><dd><p class="simpara">Indicates which standard headers are populated by the inbound channel adapter.
Allowed values: <code class="literal">none</code>, <code class="literal">id</code>, <code class="literal">timestamp</code>, or <code class="literal">both</code>.
Useful if using native deserialization and the first component to receive a message needs an <code class="literal">id</code> (such as an aggregator that is configured to use a JDBC message store).</p><p class="simpara">Default: <code class="literal">none</code></p></dd><dt><span class="term">converterBeanName</span></dt><dd><p class="simpara">The name of a bean that implements <code class="literal">RecordMessageConverter</code>. Used in the inbound channel adapter to replace the default <code class="literal">MessagingMessageConverter</code>.</p><p class="simpara">Default: <code class="literal">null</code></p></dd><dt><span class="term">idleEventInterval</span></dt><dd><p class="simpara">The interval, in milliseconds, between events indicating that no messages have recently been received.
Use an <code class="literal">ApplicationListener&lt;ListenerContainerIdleEvent&gt;</code> to receive these events.
See <a class="xref" href="multi__apache_kafka_binder.html#pause-resume" title="Example: Pausing and Resuming the Consumer">the section called &#8220;Example: Pausing and Resuming the Consumer&#8221;</a> for a usage example.</p><p class="simpara">Default: <code class="literal">30000</code></p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="kafka-producer-properties" href="#kafka-producer-properties"></a>37.3.3&nbsp;Kafka Producer Properties</h3></div></div></div><p>The following properties are available for Kafka producers only and
must be prefixed with <code class="literal">spring.cloud.stream.kafka.bindings.&lt;channelName&gt;.producer.</code>.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">admin.configuration</span></dt><dd><p class="simpara">A <code class="literal">Map</code> of Kafka topic properties used when provisioning new topics&#8201;&#8212;&#8201;for example, <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.admin.configuration.message.format.version=0.9.0.0</code></p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replicas-assignment</span></dt><dd><p class="simpara">A Map&lt;Integer, List&lt;Integer&gt;&gt; of replica assignments, with the key being the partition and the value being the assignments.
Used when provisioning new topics.
See <code class="literal">NewTopic</code> javadocs in the <code class="literal">kafka-clients</code> jar.</p><p class="simpara">Default: none.</p></dd><dt><span class="term">admin.replication-factor</span></dt><dd><p class="simpara">The replication factor to use when provisioning new topics. Overrides the binder-wide setting.
Ignored if <code class="literal">replicas-assignments</code> is present.</p><p class="simpara">Default: none (the binder-wide default of 1 is used).</p></dd><dt><span class="term">bufferSize</span></dt><dd><p class="simpara">Upper limit, in bytes, of how much data the Kafka producer attempts to batch before sending.</p><p class="simpara">Default: <code class="literal">16384</code>.</p></dd><dt><span class="term">sync</span></dt><dd><p class="simpara">Whether the producer is synchronous.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">batchTimeout</span></dt><dd><p class="simpara">How long the producer waits to allow more messages to accumulate in the same batch before sending the messages.
(Normally, the producer does not wait at all and simply sends all the messages that accumulated while the previous send was in progress.) A non-zero value may increase throughput at the expense of latency.</p><p class="simpara">Default: <code class="literal">0</code>.</p></dd><dt><span class="term">messageKeyExpression</span></dt><dd><p class="simpara">A SpEL expression evaluated against the outgoing message used to populate the key of the produced Kafka message&#8201;&#8212;&#8201;for example, <code class="literal">headers['myKey']</code>.
The payload cannot be used because, by the time this expression is evaluated, the payload is already in the form of a <code class="literal">byte[]</code>.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">headerPatterns</span></dt><dd><p class="simpara">A comma-delimited list of simple patterns to match Spring messaging headers to be mapped to the Kafka <code class="literal">Headers</code> in the <code class="literal">ProducerRecord</code>.
Patterns can begin or end with the wildcard character (asterisk).
Patterns can be negated by prefixing with <code class="literal">!</code>.
Matching stops after the first match (positive or negative).
For example <code class="literal">!ask,as*</code> will pass <code class="literal">ash</code> but not <code class="literal">ask</code>.
<code class="literal">id</code> and <code class="literal">timestamp</code> are never mapped.</p><p class="simpara">Default: <code class="literal">*</code> (all headers - except the <code class="literal">id</code> and <code class="literal">timestamp</code>)</p></dd><dt><span class="term">configuration</span></dt><dd><p class="simpara">Map with a key/value pair containing generic Kafka producer properties.</p><p class="simpara">Default: Empty map.</p></dd></dl></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The Kafka binder uses the <code class="literal">partitionCount</code> setting of the producer as a hint to create a topic with the given partition count (in conjunction with the <code class="literal">minPartitionCount</code>, the maximum of the two being the value being used).
Exercise caution when configuring both <code class="literal">minPartitionCount</code> for a binder and <code class="literal">partitionCount</code> for an application, as the larger value is used.
If a topic already exists with a smaller partition count and <code class="literal">autoAddPartitions</code> is disabled (the default), the binder fails to start.
If a topic already exists with a smaller partition count and <code class="literal">autoAddPartitions</code> is enabled, new partitions are added.
If a topic already exists with a larger number of partitions than the maximum of (<code class="literal">minPartitionCount</code> or <code class="literal">partitionCount</code>), the existing partition count is used.</p></td></tr></table></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_usage_examples" href="#_usage_examples"></a>37.3.4&nbsp;Usage examples</h3></div></div></div><p>In this section, we show the use of the preceding properties for specific scenarios.</p><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_example_setting_autocommitoffset_to_false_and_relying_on_manual_acking" href="#_example_setting_autocommitoffset_to_false_and_relying_on_manual_acking"></a>Example: Setting <code class="literal">autoCommitOffset</code> to <code class="literal">false</code> and Relying on Manual Acking</h4></div></div></div><p>This example illustrates how one may manually acknowledge offsets in a consumer application.</p><p>This example requires that <code class="literal">spring.cloud.stream.kafka.bindings.input.consumer.autoCommitOffset</code> be set to <code class="literal">false</code>.
Use the corresponding input channel name for your example.</p><pre class="screen">@SpringBootApplication
@EnableBinding(Sink.class)
public class ManuallyAcknowdledgingConsumer {
public static void main(String[] args) {
SpringApplication.run(ManuallyAcknowdledgingConsumer.class, args);
}
@StreamListener(Sink.INPUT)
public void process(Message&lt;?&gt; message) {
Acknowledgment acknowledgment = message.getHeaders().get(KafkaHeaders.ACKNOWLEDGMENT, Acknowledgment.class);
if (acknowledgment != null) {
System.out.println("Acknowledgment provided");
acknowledgment.acknowledge();
}
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_example_security_configuration" href="#_example_security_configuration"></a>Example: Security Configuration</h4></div></div></div><p>Apache Kafka 0.9 supports secure connections between client and brokers.
To take advantage of this feature, follow the guidelines in the <a class="link" href="https://kafka.apache.org/090/documentation.html#security_configclients" target="_top">Apache Kafka Documentation</a> as well as the Kafka 0.9 <a class="link" href="https://docs.confluent.io/2.0.0/kafka/security.html" target="_top">security guidelines from the Confluent documentation</a>.
Use the <code class="literal">spring.cloud.stream.kafka.binder.configuration</code> option to set security properties for all clients created by the binder.</p><p>For example, to set <code class="literal">security.protocol</code> to <code class="literal">SASL_SSL</code>, set the following property:</p><pre class="screen">spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_SSL</pre><p>All the other security properties can be set in a similar manner.</p><p>When using Kerberos, follow the instructions in the <a class="link" href="https://kafka.apache.org/090/documentation.html#security_sasl_clientconfig" target="_top">reference documentation</a> for creating and referencing the JAAS configuration.</p><p>Spring Cloud Stream supports passing JAAS configuration information to the application by using a JAAS configuration file and using Spring Boot properties.</p><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_using_jaas_configuration_files" href="#_using_jaas_configuration_files"></a>Using JAAS Configuration Files</h5></div></div></div><p>The JAAS and (optionally) krb5 file locations can be set for Spring Cloud Stream applications by using system properties.
The following example shows how to launch a Spring Cloud Stream application with SASL and Kerberos by using a JAAS configuration file:</p><pre class="programlisting"> java -Djava.security.auth.login.config=/path.to/kafka_client_jaas.conf -jar log.jar \
--spring.cloud.stream.kafka.binder.brokers=secure.server:<span class="hl-number">9092</span> \
--spring.cloud.stream.bindings.input.destination=stream.ticktock \
--spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT</pre></div><div class="section"><div class="titlepage"><div><div><h5 class="title"><a name="_using_spring_boot_properties" href="#_using_spring_boot_properties"></a>Using Spring Boot Properties</h5></div></div></div><p>As an alternative to having a JAAS configuration file, Spring Cloud Stream provides a mechanism for setting up the JAAS configuration for Spring Cloud Stream applications by using Spring Boot properties.</p><p>The following properties can be used to configure the login context of the Kafka client:</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.loginModule</span></dt><dd><p class="simpara">The login module name. Not necessary to be set in normal cases.</p><p class="simpara">Default: <code class="literal">com.sun.security.auth.module.Krb5LoginModule</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.controlFlag</span></dt><dd><p class="simpara">The control flag of the login module.</p><p class="simpara">Default: <code class="literal">required</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.binder.jaas.options</span></dt><dd><p class="simpara">Map with a key/value pair containing the login module options.</p><p class="simpara">Default: Empty map.</p></dd></dl></div><p>The following example shows how to launch a Spring Cloud Stream application with SASL and Kerberos by using Spring Boot configuration properties:</p><pre class="programlisting"> java --spring.cloud.stream.kafka.binder.brokers=secure.server:<span class="hl-number">9092</span> \
--spring.cloud.stream.bindings.input.destination=stream.ticktock \
--spring.cloud.stream.kafka.binder.autoCreateTopics=false \
--spring.cloud.stream.kafka.binder.configuration.security.protocol=SASL_PLAINTEXT \
--spring.cloud.stream.kafka.binder.jaas.options.useKeyTab=true \
--spring.cloud.stream.kafka.binder.jaas.options.storeKey=true \
--spring.cloud.stream.kafka.binder.jaas.options.keyTab=/etc/security/keytabs/kafka_client.keytab \
--spring.cloud.stream.kafka.binder.jaas.options.principal=kafka-client-<span class="hl-number">1</span>@EXAMPLE.COM</pre><p>The preceding example represents the equivalent of the following JAAS file:</p><pre class="screen">KafkaClient {
com.sun.security.auth.module.Krb5LoginModule required
useKeyTab=true
storeKey=true
keyTab="/etc/security/keytabs/kafka_client.keytab"
principal="kafka-client-1@EXAMPLE.COM";
};</pre><p>If the topics required already exist on the broker or will be created by an administrator, autocreation can be turned off and only client JAAS properties need to be sent.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Do not mix JAAS configuration files and Spring Boot properties in the same application.
If the <code class="literal">-Djava.security.auth.login.config</code> system property is already present, Spring Cloud Stream ignores the Spring Boot properties.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Be careful when using the <code class="literal">autoCreateTopics</code> and <code class="literal">autoAddPartitions</code> with Kerberos.
Usually, applications may use principals that do not have administrative rights in Kafka and Zookeeper.
Consequently, relying on Spring Cloud Stream to create/modify topics may fail.
In secure environments, we strongly recommend creating topics and managing ACLs administratively by using Kafka tooling.</p></td></tr></table></div></div></div><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="pause-resume" href="#pause-resume"></a>Example: Pausing and Resuming the Consumer</h4></div></div></div><p>If you wish to suspend consumption but not cause a partition rebalance, you can pause and resume the consumer.
This is facilitated by adding the <code class="literal">Consumer</code> as a parameter to your <code class="literal">@StreamListener</code>.
To resume, you need an <code class="literal">ApplicationListener</code> for <code class="literal">ListenerContainerIdleEvent</code> instances.
The frequency at which events are published is controlled by the <code class="literal">idleEventInterval</code> property.
Since the consumer is not thread-safe, you must call these methods on the calling thread.</p><p>The following simple application shows how to pause and resume:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> in(String in, <em><span class="hl-annotation" style="color: gray">@Header(KafkaHeaders.CONSUMER)</span></em> Consumer&lt;?, ?&gt; consumer) {
System.out.println(in);
consumer.pause(Collections.singleton(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> TopicPartition(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myTopic"</span>, <span class="hl-number">0</span>)));
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ApplicationListener&lt;ListenerContainerIdleEvent&gt; idleListener() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> event -&gt; {
System.out.println(event);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (event.getConsumer().paused().size() &gt; <span class="hl-number">0</span>) {
event.getConsumer().resume(event.getConsumer().paused());
}
};
}
}</pre></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-error-channels" href="#kafka-error-channels"></a>37.4&nbsp;Error Channels</h2></div></div></div><p>Starting with version 1.3, the binder unconditionally sends exceptions to an error channel for each consumer destination and can also be configured to send async producer send failures to an error channel.
See <a class="xref" href="multi__programming_model.html#spring-cloud-stream-overview-error-handling" title="27.4&nbsp;Error Handling">Section&nbsp;27.4, &#8220;Error Handling&#8221;</a> for more information.</p><p>The payload of the <code class="literal">ErrorMessage</code> for a send failure is a <code class="literal">KafkaSendFailureException</code> with properties:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">failedMessage</code>: The Spring Messaging <code class="literal">Message&lt;?&gt;</code> that failed to be sent.</li><li class="listitem"><code class="literal">record</code>: The raw <code class="literal">ProducerRecord</code> that was created from the <code class="literal">failedMessage</code></li></ul></div><p>There is no automatic handling of producer exceptions (such as sending to a <a class="link" href="multi__apache_kafka_binder.html#kafka-dlq-processing" title="37.6&nbsp;Dead-Letter Topic Processing">Dead-Letter queue</a>).
You can consume these exceptions with your own Spring Integration flow.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-metrics" href="#kafka-metrics"></a>37.5&nbsp;Kafka Metrics</h2></div></div></div><p>Kafka binder module exposes the following metrics:</p><p><code class="literal">spring.cloud.stream.binder.kafka.offset</code>: This metric indicates how many messages have not been yet consumed from a given binder&#8217;s topic by a given consumer group.
The metrics provided are based on the Mircometer metrics library. The metric contains the consumer group information, topic and the actual lag in committed offset from the latest offset on the topic.
This metric is particularly useful for providing auto-scaling feedback to a PaaS platform.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="kafka-dlq-processing" href="#kafka-dlq-processing"></a>37.6&nbsp;Dead-Letter Topic Processing</h2></div></div></div><p>Because you cannot anticipate how users would want to dispose of dead-lettered messages, the framework does not provide any standard mechanism to handle them.
If the reason for the dead-lettering is transient, you may wish to route the messages back to the original topic.
However, if the problem is a permanent issue, that could cause an infinite loop.
The sample Spring Boot application within this topic is an example of how to route those messages back to the original topic, but it moves them to a <span class="quote">&#8220;<span class="quote">parking lot</span>&#8221;</span> topic after three attempts.
The application is another spring-cloud-stream application that reads from the dead-letter topic.
It terminates when no messages are received for 5 seconds.</p><p>The examples assume the original destination is <code class="literal">so8400out</code> and the consumer group is <code class="literal">so8400</code>.</p><p>There are a couple of strategies to consider:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Consider running the rerouting only when the main application is not running.
Otherwise, the retries for transient errors are used up very quickly.</li><li class="listitem">Alternatively, use a two-stage approach: Use this application to route to a third topic and another to route from there back to the main topic.</li></ul></div><p>The following code listings show the sample application:</p><p><b>application.properties.&nbsp;</b>
</p><pre class="screen">spring.cloud.stream.bindings.input.group=so8400replay
spring.cloud.stream.bindings.input.destination=error.so8400out.so8400
spring.cloud.stream.bindings.output.destination=so8400out
spring.cloud.stream.bindings.output.producer.partitioned=true
spring.cloud.stream.bindings.parkingLot.destination=so8400in.parkingLot
spring.cloud.stream.bindings.parkingLot.producer.partitioned=true
spring.cloud.stream.kafka.binder.configuration.auto.offset.reset=earliest
spring.cloud.stream.kafka.binder.headers=x-retries</pre><p>
</p><p><b>Application.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(TwoOutputProcessor.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ReRouteDlqKApplication <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">implements</span> CommandLineRunner {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String X_RETRIES_HEADER = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"x-retries"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(ReRouteDlqKApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args).close();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> AtomicInteger processed = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> AtomicInteger();
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> MessageChannel parkingLot;
<em><span class="hl-annotation" style="color: gray">@StreamListener(Processor.INPUT)</span></em>
<em><span class="hl-annotation" style="color: gray">@SendTo(Processor.OUTPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Message&lt;?&gt; reRoute(Message&lt;?&gt; failed) {
processed.incrementAndGet();
Integer retries = failed.getHeaders().get(X_RETRIES_HEADER, Integer.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retries == null) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"First retry for "</span> + failed);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.fromMessage(failed)
.setHeader(X_RETRIES_HEADER, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Integer(<span class="hl-number">1</span>))
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (retries.intValue() &lt; <span class="hl-number">3</span>) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Another retry for "</span> + failed);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.fromMessage(failed)
.setHeader(X_RETRIES_HEADER, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Integer(retries.intValue() + <span class="hl-number">1</span>))
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build();
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">else</span> {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Retries exhausted for "</span> + failed);
parkingLot.send(MessageBuilder.fromMessage(failed)
.setHeader(BinderHeaders.PARTITION_OVERRIDE,
failed.getHeaders().get(KafkaHeaders.RECEIVED_PARTITION_ID))
.build());
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> run(String... args) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">while</span> (true) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> count = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.processed.get();
Thread.sleep(<span class="hl-number">5000</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (count == <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.processed.get()) {
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Idle, terminating"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span>;
}
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> TwoOutputProcessor <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> Processor {
<em><span class="hl-annotation" style="color: gray">@Output("parkingLot")</span></em>
MessageChannel parkingLot();
}
}</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_partitioning_with_the_kafka_binder" href="#_partitioning_with_the_kafka_binder"></a>37.7&nbsp;Partitioning with the Kafka Binder</h2></div></div></div><p>Apache Kafka supports topic partitioning natively.</p><p>Sometimes it is advantageous to send data to specific partitions&#8201;&#8212;&#8201;for example, when you want to strictly order message processing (all messages for a particular customer should go to the same partition).</p><p>The following example shows how to configure the producer and consumer side:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Source.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> KafkaPartitionProducerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Random RANDOM = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> Random(System.currentTimeMillis());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> String[] data = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> String[] {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar1"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux1"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar2"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux2"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar3"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux3"</span>,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bar4"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"qux4"</span>,
};
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(KafkaPartitionProducerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@InboundChannelAdapter(channel = Source.OUTPUT, poller = @Poller(fixedRate = "5000"))</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Message&lt;?&gt; generate() {
String value = data[RANDOM.nextInt(data.length)];
System.out.println(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Sending: "</span> + value);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> MessageBuilder.withPayload(value)
.setHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"partitionKey"</span>, value)
.build();
}
}</pre><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> output</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.topic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> producer</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partitioned</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-key-expression</span>: headers[<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'partitionKey'</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> partition-count</span>: <span class="hl-number">12</span></pre><p>
</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>The topic must be provisioned to have enough partitions to achieve the desired concurrency for all consumer groups.
The above configuration supports up to 12 consumer instances (6 if their <code class="literal">concurrency</code> is 2, 4 if their concurrency is 3, and so on).
It is generally best to <span class="quote">&#8220;<span class="quote">over-provision</span>&#8221;</span> the partitions to allow for future increases in consumers or concurrency.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The preceding configuration uses the default partitioning (<code class="literal">key.hashCode() % partitionCount</code>).
This may or may not provide a suitably balanced algorithm, depending on the key values.
You can override this default by using the <code class="literal">partitionSelectorExpression</code> or <code class="literal">partitionSelectorClass</code> properties.</p></td></tr></table></div><p>Since partitions are natively handled by Kafka, no special configuration is needed on the consumer side.
Kafka allocates partitions across the instances.</p><p>The following Spring Boot application listens to a Kafka stream and prints (to the console) the partition ID to which each message goes:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableBinding(Sink.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> KafkaPartitionConsumerApplication {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpringApplicationBuilder(KafkaPartitionConsumerApplication.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>)
.web(false)
.run(args);
}
<em><span class="hl-annotation" style="color: gray">@StreamListener(Sink.INPUT)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> listen(<em><span class="hl-annotation" style="color: gray">@Payload</span></em> String in, <em><span class="hl-annotation" style="color: gray">@Header(KafkaHeaders.RECEIVED_PARTITION_ID)</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">int</span> partition) {
System.out.println(in + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" received from partition "</span> + partition);
}
}</pre><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stream</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bindings</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> input</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> destination</span>: partitioned.topic
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> group</span>: myGroup</pre><p>
</p><p>You can add instances as needed.
Kafka rebalances the partition allocations.
If the instance count (or <code class="literal">instance count * concurrency</code>) exceeds the number of partitions, some consumers are idle.</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__binder_implementations.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__binder_implementations.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__apache_kafka_streams_binder.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Part&nbsp;VI.&nbsp;Binder Implementations&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;38.&nbsp;Apache Kafka Streams Binder</td></tr></table></div></body></html>

View File

@@ -0,0 +1,246 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>38.&nbsp;Apache Kafka Streams Binder</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__binder_implementations.html" title="Part&nbsp;VI.&nbsp;Binder Implementations"><link rel="prev" href="multi__apache_kafka_binder.html" title="37.&nbsp;Apache Kafka Binder"><link rel="next" href="multi__rabbitmq_binder.html" title="39.&nbsp;RabbitMQ Binder"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">38.&nbsp;Apache Kafka Streams Binder</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__apache_kafka_binder.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VI.&nbsp;Binder Implementations</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__rabbitmq_binder.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_apache_kafka_streams_binder" href="#_apache_kafka_streams_binder"></a>38.&nbsp;Apache Kafka Streams Binder</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_usage_2" href="#_usage_2"></a>38.1&nbsp;Usage</h2></div></div></div><p>For using the Kafka Streams binder, you just need to add it to your Spring Cloud Stream application, using the following
Maven coordinates:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-stream-binder-kafka-streams<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_kafka_streams_binder_overview" href="#_kafka_streams_binder_overview"></a>38.2&nbsp;Kafka Streams Binder Overview</h2></div></div></div><p>Spring Cloud Stream&#8217;s Apache Kafka support also includes a binder implementation designed explicitly for Apache Kafka
Streams binding. With this native integration, a Spring Cloud Stream "processor" application can directly use the
<a class="link" href="https://kafka.apache.org/documentation/streams/developer-guide" target="_top">Apache Kafka Streams</a> APIs in the core business logic.</p><p>Kafka Streams binder implementation builds on the foundation provided by the <a class="link" href="https://docs.spring.io/spring-kafka/reference/html/_reference.html#kafka-streams" target="_top">Kafka Streams in Spring Kafka</a>
project.</p><p>As part of this native integration, the high-level <a class="link" href="https://docs.confluent.io/current/streams/developer-guide/dsl-api.html" target="_top">Streams DSL</a>
provided by the Kafka Streams API is available for use in the business logic, too.</p><p>An early version of the <a class="link" href="https://docs.confluent.io/current/streams/developer-guide/processor-api.html" target="_top">Processor API</a>
support is available as well.</p><p>As noted early-on, Kafka Streams support in Spring Cloud Stream strictly only available for use in the Processor model.
A model in which the messages read from an inbound topic, business processing can be applied, and the transformed messages
can be written to an outbound topic. It can also be used in Processor applications with a no-outbound destination.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_streams_dsl" href="#_streams_dsl"></a>38.2.1&nbsp;Streams DSL</h3></div></div></div><p>This application consumes data from a Kafka topic (e.g., <code class="literal">words</code>), computes word count for each unique word in a 5 seconds
time window, and the computed results are sent to a downstream topic (e.g., <code class="literal">counts</code>) for further processing.</p><pre class="screen">@SpringBootApplication
@EnableBinding(KStreamProcessor.class)
public class WordCountProcessorApplication {
@StreamListener("input")
@SendTo("output")
public KStream&lt;?, WordCount&gt; process(KStream&lt;?, String&gt; input) {
return input
.flatMapValues(value -&gt; Arrays.asList(value.toLowerCase().split("\\W+")))
.groupBy((key, value) -&gt; value)
.windowedBy(TimeWindows.of(5000))
.count(Materialized.as("WordCounts-multi"))
.toStream()
.map((key, value) -&gt; new KeyValue&lt;&gt;(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))));
}
public static void main(String[] args) {
SpringApplication.run(WordCountProcessorApplication.class, args);
}</pre><p>Once built as a uber-jar (e.g., <code class="literal">wordcount-processor.jar</code>), you can run the above example like the following.</p><pre class="screen">java -jar wordcount-processor.jar --spring.cloud.stream.bindings.input.destination=words --spring.cloud.stream.bindings.output.destination=counts</pre><p>This application will consume messages from the Kafka topic <code class="literal">words</code> and the computed results are published to an output
topic <code class="literal">counts</code>.</p><p>Spring Cloud Stream will ensure that the messages from both the incoming and outgoing topics are automatically bound as
KStream objects. As a developer, you can exclusively focus on the business aspects of the code, i.e. writing the logic
required in the processor. Setting up the Streams DSL specific configuration required by the Kafka Streams infrastructure
is automatically handled by the framework.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_configuration_options_3" href="#_configuration_options_3"></a>38.3&nbsp;Configuration Options</h2></div></div></div><p>This section contains the configuration options used by the Kafka Streams binder.</p><p>For common configuration options and properties pertaining to binder, refer to the <a class="link" href="multi__configuration_options.html#binding-properties" title="29.2&nbsp;Binding Properties">core documentation</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_kafka_streams_properties" href="#_kafka_streams_properties"></a>38.3.1&nbsp;Kafka Streams Properties</h3></div></div></div><p>The following properties are available at the binder level and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.binder.</code>
literal.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">configuration</span></dt><dd> Map with a key/value pair containing properties pertaining to Apache Kafka Streams API.
This property must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.binder.</code>.
Following are some examples of using this property.</dd></dl></div><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde=org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms=1000</pre><p>For more information about all the properties that may go into streams configuration, see StreamsConfig JavaDocs in
Apache Kafka Streams docs.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">brokers</span></dt><dd><p class="simpara">Broker URL</p><p class="simpara">Default: <code class="literal">localhost</code></p></dd><dt><span class="term">zkNodes</span></dt><dd><p class="simpara">Zookeeper URL</p><p class="simpara">Default: <code class="literal">localhost</code></p></dd><dt><span class="term">serdeError</span></dt><dd><p class="simpara">Deserialization error handler type.
Possible values are - <code class="literal">logAndContinue</code>, <code class="literal">logAndFail</code> or <code class="literal">sendToDlq</code></p><p class="simpara">Default: <code class="literal">logAndFail</code></p></dd><dt><span class="term">applicationId</span></dt><dd><p class="simpara">Application ID for all the stream configurations in the current application context.
You can override the application id for an individual <code class="literal">StreamListener</code> method using the <code class="literal">group</code> property on the binding.
You have to ensure that you are using the same group name for all input bindings in the case of multiple inputs on the same methods.</p><p class="simpara">Default: <code class="literal">default</code></p></dd></dl></div><p>The following properties are <span class="emphasis"><em>only</em></span> available for Kafka Streams producers and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.bindings.&lt;binding name&gt;.producer.</code>
literal.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">keySerde</span></dt><dd><p class="simpara">key serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">valueSerde</span></dt><dd><p class="simpara">value serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">useNativeEncoding</span></dt><dd><p class="simpara">flag to enable native encoding</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div><p>The following properties are <span class="emphasis"><em>only</em></span> available for Kafka Streams consumers and must be prefixed with <code class="literal">spring.cloud.stream.kafka.streams.bindings.&lt;binding name&gt;.consumer.</code>
literal.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">keySerde</span></dt><dd><p class="simpara">key serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">valueSerde</span></dt><dd><p class="simpara">value serde to use</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">materializedAs</span></dt><dd><p class="simpara">state store to materialize when using incoming KTable types</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">useNativeDecoding</span></dt><dd><p class="simpara">flag to enable native decoding</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">dlqName</span></dt><dd><p class="simpara">DLQ topic name.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_timewindow_properties" href="#_timewindow_properties"></a>38.3.2&nbsp;TimeWindow properties:</h3></div></div></div><p>Windowing is an important concept in stream processing applications. Following properties are available to configure
time-window computations.</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.kafka.streams.timeWindow.length</span></dt><dd><p class="simpara">When this property is given, you can autowire a <code class="literal">TimeWindows</code> bean into the application.
The value is expressed in milliseconds.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd><dt><span class="term">spring.cloud.stream.kafka.streams.timeWindow.advanceBy</span></dt><dd><p class="simpara">Value is given in milliseconds.</p><p class="simpara">Default: <code class="literal">none</code>.</p></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_input_bindings" href="#_multiple_input_bindings"></a>38.4&nbsp;Multiple Input Bindings</h2></div></div></div><p>For use cases that requires multiple incoming KStream objects or a combination of KStream and KTable objects, the Kafka
Streams binder provides multiple bindings support.</p><p>Let&#8217;s see it in action.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_multiple_input_bindings_as_a_sink" href="#_multiple_input_bindings_as_a_sink"></a>38.4.1&nbsp;Multiple Input Bindings as a Sink</h3></div></div></div><pre class="screen">@EnableBinding(KStreamKTableBinding.class)
.....
.....
@StreamListener
public void process(@Input("inputStream") KStream&lt;String, PlayEvent&gt; playEvents,
@Input("inputTable") KTable&lt;Long, Song&gt; songTable) {
....
....
}
interface KStreamKTableBinding {
@Input("inputStream")
KStream&lt;?, ?&gt; inputStream();
@Input("inputTable")
KTable&lt;?, ?&gt; inputTable();
}</pre><p>In the above example, the application is written as a sink, i.e. there are no output bindings and the application has to
decide concerning downstream processing. When you write applications in this style, you might want to send the information
downstream or store them in a state store (See below for Queryable State Stores).</p><p>In the case of incoming KTable, if you want to materialize the computations to a state store, you have to express it
through the following property.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.inputTable.consumer.materializedAs: all-songs</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_multiple_input_bindings_as_a_processor" href="#_multiple_input_bindings_as_a_processor"></a>38.4.2&nbsp;Multiple Input Bindings as a Processor</h3></div></div></div><pre class="screen">@EnableBinding(KStreamKTableBinding.class)
....
....
@StreamListener
@SendTo("output")
public KStream&lt;String, Long&gt; process(@Input("input") KStream&lt;String, Long&gt; userClicksStream,
@Input("inputTable") KTable&lt;String, String&gt; userRegionsTable) {
....
....
}
interface KStreamKTableBinding extends KafkaStreamsProcessor {
@Input("inputX")
KTable&lt;?, ?&gt; inputTable();
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_multiple_output_bindings_aka_branching" href="#_multiple_output_bindings_aka_branching"></a>38.5&nbsp;Multiple Output Bindings (aka Branching)</h2></div></div></div><p>Kafka Streams allow outbound data to be split into multiple topics based on some predicates. The Kafka Streams binder provides
support for this feature without compromising the programming model exposed through <code class="literal">StreamListener</code> in the end user application.</p><p>You can write the application in the usual way as demonstrated above in the word count example. However, when using the
branching feature, you are required to do a few things. First, you need to make sure that your return type is <code class="literal">KStream[]</code>
instead of a regular <code class="literal">KStream</code>. Second, you need to use the <code class="literal">SendTo</code> annotation containing the output bindings in the order
(see example below). For each of these output bindings, you need to configure destination, content-type etc., complying with
the standard Spring Cloud Stream expectations.</p><p>Here is an example:</p><pre class="screen">@EnableBinding(KStreamProcessorWithBranches.class)
@EnableAutoConfiguration
public static class WordCountProcessorApplication {
@Autowired
private TimeWindows timeWindows;
@StreamListener("input")
@SendTo({"output1","output2","output3})
public KStream&lt;?, WordCount&gt;[] process(KStream&lt;Object, String&gt; input) {
Predicate&lt;Object, WordCount&gt; isEnglish = (k, v) -&gt; v.word.equals("english");
Predicate&lt;Object, WordCount&gt; isFrench = (k, v) -&gt; v.word.equals("french");
Predicate&lt;Object, WordCount&gt; isSpanish = (k, v) -&gt; v.word.equals("spanish");
return input
.flatMapValues(value -&gt; Arrays.asList(value.toLowerCase().split("\\W+")))
.groupBy((key, value) -&gt; value)
.windowedBy(timeWindows)
.count(Materialized.as("WordCounts-1"))
.toStream()
.map((key, value) -&gt; new KeyValue&lt;&gt;(null, new WordCount(key.key(), value, new Date(key.window().start()), new Date(key.window().end()))))
.branch(isEnglish, isFrench, isSpanish);
}
interface KStreamProcessorWithBranches {
@Input("input")
KStream&lt;?, ?&gt; input();
@Output("output1")
KStream&lt;?, ?&gt; output1();
@Output("output2")
KStream&lt;?, ?&gt; output2();
@Output("output3")
KStream&lt;?, ?&gt; output3();
}
}</pre><p>Properties:</p><pre class="screen">spring.cloud.stream.bindings.output1.contentType: application/json
spring.cloud.stream.bindings.output2.contentType: application/json
spring.cloud.stream.bindings.output3.contentType: application/json
spring.cloud.stream.kafka.streams.binder.configuration.commit.interval.ms: 1000
spring.cloud.stream.kafka.streams.binder.configuration:
default.key.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
default.value.serde: org.apache.kafka.common.serialization.Serdes$StringSerde
spring.cloud.stream.bindings.output1:
destination: foo
producer:
headerMode: raw
spring.cloud.stream.bindings.output2:
destination: bar
producer:
headerMode: raw
spring.cloud.stream.bindings.output3:
destination: fox
producer:
headerMode: raw
spring.cloud.stream.bindings.input:
destination: words
consumer:
headerMode: raw</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_message_conversion" href="#_message_conversion"></a>38.6&nbsp;Message Conversion</h2></div></div></div><p>Similar to message-channel based binder applications, the Kafka Streams binder adapts to the out-of-the-box content-type
conversions without any compromise.</p><p>It is typical for Kafka Streams operations to know the type of SerDe&#8217;s used to transform the key and value correctly.
Therefore, it may be more natural to rely on the SerDe facilities provided by the Apache Kafka Streams library itself at
the inbound and outbound conversions rather than using the content-type conversions offered by the framework.
On the other hand, you might be already familiar with the content-type conversion patterns provided by the framework, and
that, you&#8217;d like to continue using for inbound and outbound conversions.</p><p>Both the options are supported in the Kafka Streams binder implementation.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_outbound_serialization" href="#_outbound_serialization"></a>38.6.1&nbsp;Outbound serialization</h3></div></div></div><p>If native encoding is disabled (which is the default), then the framework will convert the message using the contentType
set by the user (otherwise, the default <code class="literal">application/json</code> will be applied). It will ignore any SerDe set on the outbound
in this case for outbound serialization.</p><p>Here is the property to set the contentType on the outbound.</p><pre class="screen">spring.cloud.stream.bindings.output.contentType: application/json</pre><p>Here is the property to enable native encoding.</p><pre class="screen">spring.cloud.stream.bindings.output.nativeEncoding: true</pre><p>If native encoding is enabled on the output binding (user has to enable it as above explicitly), then the framework will
skip any form of automatic message conversion on the outbound. In that case, it will switch to the Serde set by the user.
The <code class="literal">valueSerde</code> property set on the actual output binding will be used. Here is an example.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output.producer.valueSerde: org.apache.kafka.common.serialization.Serdes$StringSerde</pre><p>If this property is not set, then it will use the "default" SerDe: <code class="literal">spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde</code>.</p><p>It is worth to mention that Kafka Streams binder does not serialize the keys on outbound - it simply relies on Kafka itself.
Therefore, you either have to specify the <code class="literal">keySerde</code> property on the binding or it will default to the application-wide common
<code class="literal">keySerde</code>.</p><p>Binding level key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output.producer.keySerde</pre><p>Common Key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde</pre><p>If branching is used, then you need to use multiple output bindings. For example,</p><pre class="screen">interface KStreamProcessorWithBranches {
@Input("input")
KStream&lt;?, ?&gt; input();
@Output("output1")
KStream&lt;?, ?&gt; output1();
@Output("output2")
KStream&lt;?, ?&gt; output2();
@Output("output3")
KStream&lt;?, ?&gt; output3();
}</pre><p>If <code class="literal">nativeEncoding</code> is set, then you can set different SerDe&#8217;s on individual output bindings as below.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.output1.producer.valueSerde=IntegerSerde
spring.cloud.stream.kafka.streams.bindings.output2.producer.valueSerde=StringSerde
spring.cloud.stream.kafka.streams.bindings.output3.producer.valueSerde=JsonSerde</pre><p>Then if you have <code class="literal">SendTo</code> like this, @SendTo({"output1", "output2", "output3"}), the <code class="literal">KStream[]</code> from the branches are
applied with proper SerDe objects as defined above. If you are not enabling <code class="literal">nativeEncoding</code>, you can then set different
contentType values on the output bindings as below. In that case, the framework will use the appropriate message converter
to convert the messages before sending to Kafka.</p><pre class="screen">spring.cloud.stream.bindings.output1.contentType: application/json
spring.cloud.stream.bindings.output2.contentType: application/java-serialzied-object
spring.cloud.stream.bindings.output3.contentType: application/octet-stream</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_inbound_deserialization" href="#_inbound_deserialization"></a>38.6.2&nbsp;Inbound Deserialization</h3></div></div></div><p>Similar rules apply to data deserialization on the inbound.</p><p>If native decoding is disabled (which is the default), then the framework will convert the message using the contentType
set by the user (otherwise, the default <code class="literal">application/json</code> will be applied). It will ignore any SerDe set on the inbound
in this case for inbound deserialization.</p><p>Here is the property to set the contentType on the inbound.</p><pre class="screen">spring.cloud.stream.bindings.input.contentType: application/json</pre><p>Here is the property to enable native decoding.</p><pre class="screen">spring.cloud.stream.bindings.input.nativeDecoding: true</pre><p>If native decoding is enabled on the input binding (user has to enable it as above explicitly), then the framework will
skip doing any message conversion on the inbound. In that case, it will switch to the SerDe set by the user. The <code class="literal">valueSerde</code>
property set on the actual output binding will be used. Here is an example.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.valueSerde: org.apache.kafka.common.serialization.Serdes$StringSerde</pre><p>If this property is not set, it will use the default SerDe: <code class="literal">spring.cloud.stream.kafka.streams.binder.configuration.default.value.serde</code>.</p><p>It is worth to mention that Kafka Streams binder does not deserialize the keys on inbound - it simply relies on Kafka itself.
Therefore, you either have to specify the <code class="literal">keySerde</code> property on the binding or it will default to the application-wide common
<code class="literal">keySerde</code>.</p><p>Binding level key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.keySerde</pre><p>Common Key serde:</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.configuration.default.key.serde</pre><p>As in the case of KStream branching on the outbound, the benefit of setting value SerDe per binding is that if you have
multiple input bindings (multiple KStreams object) and they all require separate value SerDe&#8217;s, then you can configure
them individually. If you use the common configuration approach, then this feature won&#8217;t be applicable.</p></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_error_handling" href="#_error_handling"></a>38.7&nbsp;Error Handling</h2></div></div></div><p>Apache Kafka Streams provide the capability for natively handling exceptions from deserialization errors.
For details on this support, please see <a class="link" href="https://cwiki.apache.org/confluence/display/KAFKA/KIP-161%3A+streams+deserialization+exception+handlers" target="_top">this</a>
Out of the box, Apache Kafka Streams provide two kinds of deserialization exception handlers - <code class="literal">logAndContinue</code> and <code class="literal">logAndFail</code>.
As the name indicates, the former will log the error and continue processing the next records and the latter will log the
error and fail. <code class="literal">LogAndFail</code> is the default deserialization exception handler.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handling_deserialization_exceptions" href="#_handling_deserialization_exceptions"></a>38.7.1&nbsp;Handling Deserialization Exceptions</h3></div></div></div><p>Kafka Streams binder supports a selection of exception handlers through the following properties.</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.serdeError: logAndContinue</pre><p>In addition to the above two deserialization exception handlers, the binder also provides a third one for sending the erroneous
records (poison pills) to a DLQ topic. Here is how you enable this DLQ exception handler.</p><pre class="screen">spring.cloud.stream.kafka.streams.binder.serdeError: sendToDlq</pre><p>When the above property is set, all the deserialization error records are automatically sent to the DLQ topic.</p><pre class="screen">spring.cloud.stream.kafka.streams.bindings.input.consumer.dlqName: foo-dlq</pre><p>If this is set, then the error records are sent to the topic <code class="literal">foo-dlq</code>. If this is not set, then it will create a DLQ
topic with the name <code class="literal">error.&lt;input-topic-name&gt;.&lt;group-name&gt;</code>.</p><p>A couple of things to keep in mind when using the exception handling feature in Kafka Streams binder.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">The property <code class="literal">spring.cloud.stream.kafka.streams.binder.serdeError</code> is applicable for the entire application. This implies
that if there are multiple <code class="literal">StreamListener</code> methods in the same application, this property is applied to all of them.</li><li class="listitem">The exception handling for deserialization works consistently with native deserialization and framework provided message
conversion.</li></ul></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_handling_non_deserialization_exceptions" href="#_handling_non_deserialization_exceptions"></a>38.7.2&nbsp;Handling Non-Deserialization Exceptions</h3></div></div></div><p>For general error handling in Kafka Streams binder, it is up to the end user applications to handle application level errors.
As a side effect of providing a DLQ for deserialization exception handlers, Kafka Streams binder provides a way to get
access to the DLQ sending bean directly from your application.
Once you get access to that bean, you can programmatically send any exception records from your application to the DLQ.</p><p>It continues to remain hard to robust error handling using the high-level DSL; Kafka Streams doesn&#8217;t natively support error
handling yet.</p><p>However, when you use the low-level Processor API in your application, there are options to control this behavior. See
below.</p><pre class="screen">@Autowired
private SendToDlqAndContinue dlqHandler;
@StreamListener("input")
@SendTo("output")
public KStream&lt;?, WordCount&gt; process(KStream&lt;Object, String&gt; input) {
input.process(() -&gt; new Processor() {
ProcessorContext context;
@Override
public void init(ProcessorContext context) {
this.context = context;
}
@Override
public void process(Object o, Object o2) {
try {
.....
.....
}
catch(Exception e) {
//explicitly provide the kafka topic corresponding to the input binding as the first argument.
//DLQ handler will correctly map to the dlq topic from the actual incoming destination.
dlqHandler.sendToDlq("topic-name", (byte[]) o1, (byte[]) o2, context.partition());
}
}
.....
.....
});
}</pre></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_interactive_queries" href="#_interactive_queries"></a>38.8&nbsp;Interactive Queries</h2></div></div></div><p>As part of the public Kafka Streams binder API, we expose a class called <code class="literal">QueryableStoreRegistry</code>. You can access this
as a Spring bean in your application. An easy way to get access to this bean from your application is to "autowire" the bean
in your application.</p><pre class="screen">@Autowired
private QueryableStoreRegistry queryableStoreRegistry;</pre><p>Once you gain access to this bean, then you can query for the particular state-store that you are interested. See below.</p><pre class="screen">ReadOnlyKeyValueStore&lt;Object, Object&gt; keyValueStore =
queryableStoreRegistry.getQueryableStoreType("my-store", QueryableStoreTypes.keyValueStore());</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_accessing_the_underlying_kafkastreams_object" href="#_accessing_the_underlying_kafkastreams_object"></a>38.9&nbsp;Accessing the underlying KafkaStreams object</h2></div></div></div><p><code class="literal">StreamBuilderFactoryBean</code> from spring-kafka that is responsible for constructing the <code class="literal">KafkaStreams</code> object can be accessed programmatically.
Each <code class="literal">StreamBuilderFactoryBean</code> is registered as <code class="literal">stream-builder</code> and appended with the <code class="literal">StreamListener</code> method name.
If your <code class="literal">StreamListener</code> method is named as <code class="literal">process</code> for example, the stream builder bean is named as <code class="literal">stream-builder-process</code>.
Since this is a factory bean, it should be accessed by prepending an ampersand (<code class="literal">&amp;</code>) when accessing it programmatically.
Following is an example and it assumes the <code class="literal">StreamListener</code> method is named as <code class="literal">process</code></p><pre class="screen">StreamsBuilderFactoryBean streamsBuilderFactoryBean = context.getBean("&amp;stream-builder-process", StreamsBuilderFactoryBean.class);
KafkaStreams kafkaStreams = streamsBuilderFactoryBean.getKafkaStreams();</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__apache_kafka_binder.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__binder_implementations.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__rabbitmq_binder.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">37.&nbsp;Apache Kafka Binder&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;39.&nbsp;RabbitMQ Binder</td></tr></table></div></body></html>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,3 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Part&nbsp;VI.&nbsp;Binder Implementations</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="prev" href="multi__samples.html" title="36.&nbsp;Samples"><link rel="next" href="multi__apache_kafka_binder.html" title="37.&nbsp;Apache Kafka Binder"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Part&nbsp;VI.&nbsp;Binder Implementations</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__samples.html">Prev</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__apache_kafka_binder.html">Next</a></td></tr></table><hr></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_binder_implementations" href="#_binder_implementations"></a>Part&nbsp;VI.&nbsp;Binder Implementations</h1></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__samples.html">Prev</a>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__apache_kafka_binder.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">36.&nbsp;Samples&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;37.&nbsp;Apache Kafka Binder</td></tr></table></div></body></html>

View File

@@ -0,0 +1,34 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>47.&nbsp;Broadcasting Your Own Events</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_bus.html" title="Part&nbsp;VII.&nbsp;Spring Cloud Bus"><link rel="prev" href="multi__tracing_bus_events.html" title="46.&nbsp;Tracing Bus Events"><link rel="next" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">47.&nbsp;Broadcasting Your Own Events</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__tracing_bus_events.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VII.&nbsp;Spring Cloud Bus</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_sleuth.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_broadcasting_your_own_events" href="#_broadcasting_your_own_events"></a>47.&nbsp;Broadcasting Your Own Events</h2></div></div></div><p>The Bus can carry any event of type <code class="literal">RemoteApplicationEvent</code>. The default transport is
JSON, and the deserializer needs to know which types are going to be used ahead of time.
To register a new type, you must put it in a subpackage of
<code class="literal">org.springframework.cloud.bus.event</code>.</p><p>To customise the event name, you can use <code class="literal">@JsonTypeName</code> on your custom class or rely on
the default strategy, which is to use the simple name of the class.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Both the producer and the consumer need access to the class definition.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_registering_events_in_custom_packages" href="#_registering_events_in_custom_packages"></a>47.1&nbsp;Registering events in custom packages</h2></div></div></div><p>If you cannot or do not want to use a subpackage of <code class="literal">org.springframework.cloud.bus.event</code>
for your custom events, you must specify which packages to scan for events of type
<code class="literal">RemoteApplicationEvent</code> by using the <code class="literal">@RemoteApplicationEventScan</code> annotation. Packages
specified with <code class="literal">@RemoteApplicationEventScan</code> include subpackages.</p><p>For example, consider the following custom event, called <code class="literal">MyEvent</code>:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyEvent <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> RemoteApplicationEvent {
...
}</pre><p>You can register that event with the deserializer in the following way:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<em><span class="hl-annotation" style="color: gray">@RemoteApplicationEventScan</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BusConfiguration {
...
}</pre><p>Without specifying a value, the package of the class where <code class="literal">@RemoteApplicationEventScan</code>
is used is registered. In this example, <code class="literal">com.acme</code> is registered by using the package of
<code class="literal">BusConfiguration</code>.</p><p>You can also explicitly specify the packages to scan by using the <code class="literal">value</code>, <code class="literal">basePackages</code>
or <code class="literal">basePackageClasses</code> properties on <code class="literal">@RemoteApplicationEventScan</code>, as shown in the
following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.acme;
<em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//@RemoteApplicationEventScan({"com.acme", "foo.bar"})</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//@RemoteApplicationEventScan(basePackages = {"com.acme", "foo.bar", "fizz.buzz"})</span>
<em><span class="hl-annotation" style="color: gray">@RemoteApplicationEventScan(basePackageClasses = BusConfiguration.class)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> BusConfiguration {
...
}</pre><p>All of the preceding examples of <code class="literal">@RemoteApplicationEventScan</code> are equivalent, in that the
<code class="literal">com.acme</code> package is registered by explicitly specifying the packages on
<code class="literal">@RemoteApplicationEventScan</code>.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>You can specify multiple base packages to scan.</p></td></tr></table></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__tracing_bus_events.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_bus.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_sleuth.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">46.&nbsp;Tracing Bus Events&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</td></tr></table></div></body></html>

View File

@@ -0,0 +1,31 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>118.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_gateway.html" title="Part&nbsp;XV.&nbsp;Spring Cloud Gateway"><link rel="prev" href="multi__developer_guide.html" title="117.&nbsp;Developer Guide"><link rel="next" href="multi__spring_cloud_function.html" title="Part&nbsp;XVI.&nbsp;Spring Cloud Function"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">118.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__developer_guide.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XV.&nbsp;Spring Cloud Gateway</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_function.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_building_a_simple_gateway_using_spring_mvc_or_webflux" href="#_building_a_simple_gateway_using_spring_mvc_or_webflux"></a>118.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux</h2></div></div></div><p>Spring Cloud Gateway provides a utility object called <code class="literal">ProxyExchange</code> which you can use inside a regular Spring web handler as a method parameter. It supports basic downstream HTTP exchanges via methods that mirror the HTTP verbs. With MVC it also supports forwarding to a local handler via the <code class="literal">forward()</code> method. To use the <code class="literal">ProxyExchange</code> just include the right module in your classpath (either <code class="literal">spring-cloud-gateway-mvc</code> or <code class="literal">spring-cloud-gateway-webflux</code>).</p><p>MVC example (proxying a request to "/test" downstream to a remote server):</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity&lt;?&gt; proxy(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
}
}</pre><p>The same thing with Webflux:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> GatewaySampleApplication {
<em><span class="hl-annotation" style="color: gray">@Value("${remote.home}")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> URI home;
<em><span class="hl-annotation" style="color: gray">@GetMapping("/test")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> Mono&lt;ResponseEntity&lt;?&gt;&gt; proxy(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>).get();
}
}</pre><p>There are convenience methods on the <code class="literal">ProxyExchange</code> to enable the handler method to discover and enhance the URI path of the incoming request. For example you might want to extract the trailing elements of a path to pass them downstream:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@GetMapping("/proxy/path/**")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ResponseEntity&lt;?&gt; proxyPath(ProxyExchange&lt;<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">byte</span>[]&gt; proxy) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> Exception {
String path = proxy.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/proxy/path/"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> proxy.uri(home.toString() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/foos/"</span> + path).get();
}</pre><p>All the features of Spring MVC or Webflux are available to Gateway handler methods. So you can inject request headers and query parameters, for instance, and you can constrain the incoming requests with declarations in the mapping annotation. See the documentation for <code class="literal">@RequestMapping</code> in Spring MVC for more details of those features.</p><p>Headers can be added to the downstream response using the <code class="literal">header()</code> methods on <code class="literal">ProxyExchange</code>.</p><p>You can also manipulate response headers (and anything else you like in the response) by adding a mapper to the <code class="literal">get()</code> etc. method. The mapper is a <code class="literal">Function</code> that takes the incoming <code class="literal">ResponseEntity</code> and converts it to an outgoing one.</p><p>First class support is provided for "sensitive" headers ("cookie" and "authorization" by default) which are not passed downstream, and for "proxy" headers (<code class="literal">x-forwarded-*</code>).</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__developer_guide.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_gateway.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_function.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">117.&nbsp;Developer Guide&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;Part&nbsp;XVI.&nbsp;Spring Cloud Function</td></tr></table></div></body></html>

View File

@@ -0,0 +1,19 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>121.&nbsp;Building and Running a Function</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_function.html" title="Part&nbsp;XVI.&nbsp;Spring Cloud Function"><link rel="prev" href="multi__getting_started.html" title="120.&nbsp;Getting Started"><link rel="next" href="multi__function_catalog_and_flexible_function_signatures.html" title="122.&nbsp;Function Catalog and Flexible Function Signatures"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">121.&nbsp;Building and Running a Function</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__getting_started.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XVI.&nbsp;Spring Cloud Function</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__function_catalog_and_flexible_function_signatures.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_building_and_running_a_function" href="#_building_and_running_a_function"></a>121.&nbsp;Building and Running a Function</h2></div></div></div><p>The sample <code class="literal">@SpringBootApplication</code> above has a function that can be
decorated at runtime by Spring Cloud Function to be an HTTP endpoint,
or a Stream processor, for instance with RabbitMQ, Apache Kafka or
JMS.</p><p>The <code class="literal">@Beans</code> can be <code class="literal">Function</code>, <code class="literal">Consumer</code> or <code class="literal">Supplier</code> (all from
<code class="literal">java.util</code>), and their parametric types can be String or POJO. A
<code class="literal">Function</code> is exposed as a Spring Cloud Stream <code class="literal">Processor</code> if
<code class="literal">spring-cloud-function-stream</code> is on the classpath.
A <code class="literal">Consumer</code> is also exposed as a Stream
<code class="literal">Sink</code> and a <code class="literal">Supplier</code> translates to a Stream <code class="literal">Source</code>.
HTTP endpoints are exposed if the Stream binder is <code class="literal">spring-cloud-stream-binder-servlet</code>.</p><p>Functions can be of <code class="literal">Flux&lt;String&gt;</code> or <code class="literal">Flux&lt;Pojo&gt;</code> and Spring Cloud
Function takes care of converting the data to and from the desired
types, as long as it comes in as plain text or (in the case of the
POJO) JSON. TBD: support for <code class="literal">Flux&lt;Message&lt;Pojo&gt;&gt;</code> and maybe plain
<code class="literal">Pojo</code> types (Fluxes implied and implemented by the framework).</p><p>Functions can be grouped together in a single application, or deployed
one-per-jar. It&#8217;s up to the developer to choose. An app with multiple
functions can be deployed multiple times in different "personalities",
exposing different functions over different physical transports.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__getting_started.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_function.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__function_catalog_and_flexible_function_signatures.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">120.&nbsp;Getting Started&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;122.&nbsp;Function Catalog and Flexible Function Signatures</td></tr></table></div></body></html>

View File

@@ -0,0 +1,13 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>41.&nbsp;Bus Endpoints</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_bus.html" title="Part&nbsp;VII.&nbsp;Spring Cloud Bus"><link rel="prev" href="multi__quick_start_3.html" title="40.&nbsp;Quick Start"><link rel="next" href="multi__addressing_an_instance.html" title="42.&nbsp;Addressing an Instance"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">41.&nbsp;Bus Endpoints</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__quick_start_3.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VII.&nbsp;Spring Cloud Bus</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__addressing_an_instance.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_bus_endpoints" href="#_bus_endpoints"></a>41.&nbsp;Bus Endpoints</h2></div></div></div><p>Spring Cloud Bus provides two endpoints, <code class="literal">/actuator/bus-refresh</code> and <code class="literal">/actuator/bus-env</code>
that correspond to individual actuator endpoints in Spring Cloud Commons,
<code class="literal">/actuator/refresh</code> and <code class="literal">/actuator/env</code> respectively.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_bus_refresh_endpoint" href="#_bus_refresh_endpoint"></a>41.1&nbsp;Bus Refresh Endpoint</h2></div></div></div><p>The <code class="literal">/actuator/bus-refresh</code> endpoint clears the <code class="literal">RefreshScope</code> cache and rebinds
<code class="literal">@ConfigurationProperties</code>. See the <a class="link" href="multi__spring_cloud_context_application_context_services.html#refresh-scope" title="2.9&nbsp;Refresh Scope">Refresh Scope</a> documentation for
more information.</p><p>To expose the <code class="literal">/actuator/bus-refresh</code> endpoint, you need to add following configuration to your
application:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">management.endpoints.web.exposure.include</span>=bus-refresh</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_bus_env_endpoint" href="#_bus_env_endpoint"></a>41.2&nbsp;Bus Env Endpoint</h2></div></div></div><p>The <code class="literal">/actuator/bus-env</code> endpoint updates each instances environment with the specified
key/value pair across multiple instances.</p><p>To expose the <code class="literal">/actuator/bus-env</code> endpoint, you need to add following configuration to your
application:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">management.endpoints.web.exposure.include</span>=bus-env</pre><p>The <code class="literal">/actuator/bus-env</code> endpoint accepts <code class="literal">POST</code> requests with the following shape:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"name"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"key1"</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"value1"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__quick_start_3.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_bus.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__addressing_an_instance.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">40.&nbsp;Quick Start&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;42.&nbsp;Addressing an Instance</td></tr></table></div></body></html>

View File

@@ -0,0 +1,61 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>13.&nbsp;Circuit Breaker: Hystrix Clients</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_netflix.html" title="Part&nbsp;III.&nbsp;Spring Cloud Netflix"><link rel="prev" href="multi_spring-cloud-eureka-server.html" title="12.&nbsp;Service Discovery: Eureka Server"><link rel="next" href="multi__circuit_breaker_hystrix_dashboard.html" title="14.&nbsp;Circuit Breaker: Hystrix Dashboard"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">13.&nbsp;Circuit Breaker: Hystrix Clients</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi_spring-cloud-eureka-server.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;III.&nbsp;Spring Cloud Netflix</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__circuit_breaker_hystrix_dashboard.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_circuit_breaker_hystrix_clients" href="#_circuit_breaker_hystrix_clients"></a>13.&nbsp;Circuit Breaker: Hystrix Clients</h2></div></div></div><p>Netflix has created a library called <a class="link" href="https://github.com/Netflix/Hystrix" target="_top">Hystrix</a> that implements the <a class="link" href="https://martinfowler.com/bliki/CircuitBreaker.html" target="_top">circuit breaker pattern</a>.
In a microservice architecture, it is common to have multiple layers of service calls, as shown in the following example:</p><div class="figure"><a name="d0e4232" href="#d0e4232"></a><p class="title"><b>Figure&nbsp;13.1.&nbsp;Microservice Graph</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/Hystrix.png" alt="Hystrix"></div></div></div><br class="figure-break"><p>A service failure in the lower level of services can cause cascading failure all the way up to the user.
When calls to a particular service exceed <code class="literal">circuitBreaker.requestVolumeThreshold</code> (default: 20 requests) and the failure percentage is greater than <code class="literal">circuitBreaker.errorThresholdPercentage</code> (default: &gt;50%) in a rolling window defined by <code class="literal">metrics.rollingStats.timeInMilliseconds</code> (default: 10 seconds), the circuit opens and the call is not made.
In cases of error and an open circuit, a fallback can be provided by the developer.</p><div class="figure"><a name="d0e4252" href="#d0e4252"></a><p class="title"><b>Figure&nbsp;13.2.&nbsp;Hystrix fallback prevents cascading failures</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/HystrixFallback.png" alt="HystrixFallback"></div></div></div><br class="figure-break"><p>Having an open circuit stops cascading failures and allows overwhelmed or failing services time to recover.
The fallback can be another Hystrix protected call, static data, or a sensible empty value.
Fallbacks may be chained so that the first fallback makes some other business call, which in turn falls back to static data.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_how_to_include_hystrix" href="#_how_to_include_hystrix"></a>13.1&nbsp;How to Include Hystrix</h2></div></div></div><p>To include Hystrix in your project, use the starter with a group ID of <code class="literal">org.springframework.cloud</code>
and a artifact ID of <code class="literal">spring-cloud-starter-netflix-hystrix</code>.
See the <a class="link" href="https://projects.spring.io/spring-cloud/" target="_top">Spring Cloud Project page</a> for details on setting up your build system with the current Spring Cloud Release Train.</p><p>The following example shows a minimal Eureka server with a Hystrix circuit breaker:</p><pre class="screen">@SpringBootApplication
@EnableCircuitBreaker
public class Application {
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
@Component
public class StoreIntegration {
@HystrixCommand(fallbackMethod = "defaultStores")
public Object getStores(Map&lt;String, Object&gt; parameters) {
//do stuff that might fail
}
public Object defaultStores(Map&lt;String, Object&gt; parameters) {
return /* something useful */;
}
}</pre><p>The <code class="literal">@HystrixCommand</code> is provided by a Netflix contrib library called <a class="link" href="https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica" target="_top"><span class="quote">&#8220;<span class="quote">javanica</span>&#8221;</span></a>.
Spring Cloud automatically wraps Spring beans with that annotation in a proxy that is connected to the Hystrix circuit breaker.
The circuit breaker calculates when to open and close the circuit and what to do in case of a failure.</p><p>To configure the <code class="literal">@HystrixCommand</code> you can use the <code class="literal">commandProperties</code>
attribute with a list of <code class="literal">@HystrixProperty</code> annotations. See
<a class="link" href="https://github.com/Netflix/Hystrix/tree/master/hystrix-contrib/hystrix-javanica#configuration" target="_top">here</a>
for more details. See the <a class="link" href="https://github.com/Netflix/Hystrix/wiki/Configuration" target="_top">Hystrix wiki</a>
for details on the properties available.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="netflix-hystrix-starter" href="#netflix-hystrix-starter"></a>13.2&nbsp;Propagating the Security Context or Using Spring Scopes</h2></div></div></div><p>If you want some thread local context to propagate into a <code class="literal">@HystrixCommand</code>, the default declaration does not work, because it executes the command in a thread pool (in case of timeouts).
You can switch Hystrix to use the same thread as the caller through configuration or directly in the annotation, by asking it to use a different <span class="quote">&#8220;<span class="quote">Isolation Strategy</span>&#8221;</span>.
The following example demonstrates setting the thread in the annotation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@HystrixCommand(fallbackMethod = "stubMyService",
commandProperties = {
@HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
}
)</span></em>
...</pre><p>The same thing applies if you are using <code class="literal">@SessionScope</code> or <code class="literal">@RequestScope</code>.
If you encounter a runtime exception that says it cannot find the scoped context, you need to use the same thread.</p><p>You also have the option to set the <code class="literal">hystrix.shareSecurityContext</code> property to <code class="literal">true</code>.
Doing so auto-configures a Hystrix concurrency strategy plugin hook to transfer the <code class="literal">SecurityContext</code> from your main thread to the one used by the Hystrix command.
Hystrix does not let multiple Hystrix concurrency strategy be registered so an extension mechanism is available by declaring your own <code class="literal">HystrixConcurrencyStrategy</code> as a Spring bean.
Spring Cloud looks for your implementation within the Spring context and wrap it inside its own plugin.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_health_indicator_4" href="#_health_indicator_4"></a>13.3&nbsp;Health Indicator</h2></div></div></div><p>The state of the connected circuit breakers are also exposed in the <code class="literal">/health</code> endpoint of the calling application, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"hystrix"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">{</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"openCircuitBreakers"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">[</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"StoreIntegration::getStoresByLocationLink"</span>
]<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CIRCUIT_OPEN"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">},</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"UP"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">}</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_hystrix_metrics_stream" href="#_hystrix_metrics_stream"></a>13.4&nbsp;Hystrix Metrics Stream</h2></div></div></div><p>To enable the Hystrix metrics stream, include a dependency on <code class="literal">spring-boot-starter-actuator</code> and set
<code class="literal">management.endpoints.web.exposure.include: hystrix.stream</code>.
Doing so exposes the <code class="literal">/actuator/hystrix.stream</code> as a management endpoint, as shown in the following example:</p><pre class="programlisting"> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-actuator<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi_spring-cloud-eureka-server.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_netflix.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__circuit_breaker_hystrix_dashboard.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">12.&nbsp;Service Discovery: Eureka Server&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;14.&nbsp;Circuit Breaker: Hystrix Dashboard</td></tr></table></div></body></html>

View File

@@ -0,0 +1,4 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>14.&nbsp;Circuit Breaker: Hystrix Dashboard</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_netflix.html" title="Part&nbsp;III.&nbsp;Spring Cloud Netflix"><link rel="prev" href="multi__circuit_breaker_hystrix_clients.html" title="13.&nbsp;Circuit Breaker: Hystrix Clients"><link rel="next" href="multi__hystrix_timeouts_and_ribbon_clients.html" title="15.&nbsp;Hystrix Timeouts And Ribbon Clients"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">14.&nbsp;Circuit Breaker: Hystrix Dashboard</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__circuit_breaker_hystrix_clients.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;III.&nbsp;Spring Cloud Netflix</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__hystrix_timeouts_and_ribbon_clients.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_circuit_breaker_hystrix_dashboard" href="#_circuit_breaker_hystrix_dashboard"></a>14.&nbsp;Circuit Breaker: Hystrix Dashboard</h2></div></div></div><p>One of the main benefits of Hystrix is the set of metrics it gathers about each HystrixCommand.
The Hystrix Dashboard displays the health of each circuit breaker in an efficient manner.</p><div class="figure"><a name="d0e4373" href="#d0e4373"></a><p class="title"><b>Figure&nbsp;14.1.&nbsp;Hystrix Dashboard</b></p><div class="figure-contents"><div class="mediaobject"><img src="https://raw.githubusercontent.com/spring-cloud/spring-cloud-netflix/master/docs/src/main/asciidoc/images/Hystrix.png" alt="Hystrix"></div></div></div><br class="figure-break"></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__circuit_breaker_hystrix_clients.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_netflix.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__hystrix_timeouts_and_ribbon_clients.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">13.&nbsp;Circuit Breaker: Hystrix Clients&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;15.&nbsp;Hystrix Timeouts And Ribbon Clients</td></tr></table></div></body></html>

View File

@@ -0,0 +1,66 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>98.&nbsp;Client Side Usage</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_vault.html" title="Part&nbsp;XIV.&nbsp;Spring Cloud Vault"><link rel="prev" href="multi__quick_start_4.html" title="97.&nbsp;Quick Start"><link rel="next" href="multi_vault.config.authentication.html" title="99.&nbsp;Authentication methods"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">98.&nbsp;Client Side Usage</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__quick_start_4.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XIV.&nbsp;Spring Cloud Vault</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi_vault.config.authentication.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_client_side_usage_2" href="#_client_side_usage_2"></a>98.&nbsp;Client Side Usage</h2></div></div></div><p>To use these features in an application, just build it as a Spring
Boot application that depends on <code class="literal">spring-cloud-vault-config</code> (e.g. see
the test cases). Example Maven configuration:</p><div class="example"><a name="d0e28409" href="#d0e28409"></a><p class="title"><b>Example&nbsp;98.1.&nbsp;pom.xml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-parent<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>2.0.0.RELEASE<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;relativePath /&gt;</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- lookup parent from repository --&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/parent&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-starter-vault-config<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>1.0.0.BUILD-SNAPSHOT<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-starter-test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.boot<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-boot-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugins&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/build&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">&lt;!-- repositories also needed for snapshots and milestones --&gt;</span></pre></div></div><br class="example-break"><p>Then you can create a standard Spring Boot application, like this simple HTTP server:</p><div class="informalexample"><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@SpringBootApplication</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@RequestMapping("/")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Hello World!"</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> main(String[] args) {
SpringApplication.run(Application.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>, args);
}
}</pre></div><p>When it runs it will pick up the external configuration from the
default local Vault server on port <code class="literal">8200</code> if it is running. To modify
the startup behavior you can change the location of the Vault server
using <code class="literal">bootstrap.properties</code> (like <code class="literal">application.properties</code> but for
the bootstrap phase of an application context), e.g.</p><div class="example"><a name="d0e28430" href="#d0e28430"></a><p class="title"><b>Example&nbsp;98.2.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> host</span>: localhost
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> port</span>: <span class="hl-number">8200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> scheme</span>: https
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://localhost:<span class="hl-number">8200</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> connection-timeout</span>: <span class="hl-number">5000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> read-timeout</span>: <span class="hl-number">15000</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> order</span>: -<span class="hl-number">10</span></pre></div></div><br class="example-break"><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><code class="literal">host</code> sets the hostname of the Vault host. The host name will be used
for SSL certificate validation</li><li class="listitem"><code class="literal">port</code> sets the Vault port</li><li class="listitem"><code class="literal">scheme</code> setting the scheme to <code class="literal">http</code> will use plain HTTP.
Supported schemes are <code class="literal">http</code> and <code class="literal">https</code>.</li><li class="listitem"><code class="literal">uri</code> configure the Vault endpoint with an URI. Takes precedence over host/port/scheme configuration</li><li class="listitem"><code class="literal">connection-timeout</code> sets the connection timeout in milliseconds</li><li class="listitem"><code class="literal">read-timeout</code> sets the read timeout in milliseconds</li><li class="listitem"><code class="literal">config.order</code> sets the order for the property source</li></ul></div><p>Enabling further integrations requires additional dependencies and
configuration. Depending on how you have set up Vault you might need
additional configuration like
<a class="link" href="https://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.ssl" target="_top">SSL</a> and
<a class="link" href="https://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.authentication" target="_top">authentication</a>.</p><p>If the application imports the <code class="literal">spring-boot-starter-actuator</code> project, the
status of the vault server will be available via the <code class="literal">/health</code> endpoint.</p><p>The vault health indicator can be enabled or disabled through the property <code class="literal">management.health.vault.enabled</code> (default to <code class="literal">true</code>).</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_authentication_2" href="#_authentication_2"></a>98.1&nbsp;Authentication</h2></div></div></div><p>Vault requires an <a class="link" href="https://www.vaultproject.io/docs/concepts/auth.html" target="_top">authentication mechanism</a> to <a class="link" href="https://www.vaultproject.io/docs/concepts/tokens.html" target="_top">authorize client requests</a>.</p><p>Spring Cloud Vault supports multiple <a class="link" href="https://cloud.spring.io/spring-cloud-vault/spring-cloud-vault.html#vault.config.authentication" target="_top">authentication mechanisms</a> to authenticate applications with Vault.</p><p>For a quickstart, use the root token printed by the <a class="link" href="multi__quick_start_4.html#quickstart.vault.start">Vault initialization</a>.</p><div class="example"><a name="d0e28525" href="#d0e28525"></a><p class="title"><b>Example&nbsp;98.3.&nbsp;bootstrap.yml</b></p><div class="example-contents"><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.cloud.vault</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> token</span>: <span class="hl-number">19</span>aefa97-cccc-bbbb-aaaa-<span class="hl-number">225940e63d</span>76</pre></div></div><br class="example-break"><div class="warning" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Warning"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Warning]" src="images/warning.png"></td><th align="left">Warning</th></tr><tr><td align="left" valign="top"><p>Consider carefully your security requirements. Static token authentication is fine if you want quickly get started with Vault, but a static token is not protected any further. Any disclosure to unintended parties allows Vault use with the associated token roles.</p></td></tr></table></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__quick_start_4.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_vault.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi_vault.config.authentication.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">97.&nbsp;Quick Start&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;99.&nbsp;Authentication methods</td></tr></table></div></body></html>

View File

@@ -0,0 +1,9 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Part&nbsp;I.&nbsp;Cloud Native Applications</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="prev" href="multi__features.html" title="1.&nbsp;Features"><link rel="next" href="multi__spring_cloud_context_application_context_services.html" title="2.&nbsp;Spring Cloud Context: Application Context Services"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">Part&nbsp;I.&nbsp;Cloud Native Applications</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__features.html">Prev</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_context_application_context_services.html">Next</a></td></tr></table><hr></div><div class="part"><div class="titlepage"><div><div><h1 class="title"><a name="_cloud_native_applications" href="#_cloud_native_applications"></a>Part&nbsp;I.&nbsp;Cloud Native Applications</h1></div></div></div><div class="partintro"><div></div><p><a class="link" href="https://pivotal.io/platform-as-a-service/migrating-to-cloud-native-application-architectures-ebook" target="_top">Cloud Native</a> is a style of application development that encourages easy adoption of best practices in the areas of continuous delivery and value-driven development.
A related discipline is that of building <a class="link" href="https://12factor.net/" target="_top">12-factor Applications</a>, in which development practices are aligned with delivery and operations goals&#8201;&#8212;&#8201;for instance, by using declarative programming and management and monitoring.
Spring Cloud facilitates these styles of development in a number of specific ways.
The starting point is a set of features to which all components in a distributed system need easy access.</p><p>Many of those features are covered by <a class="link" href="https://projects.spring.io/spring-boot" target="_top">Spring Boot</a>, on which Spring Cloud builds. Some more features are delivered by Spring Cloud as two libraries: Spring Cloud Context and Spring Cloud Commons.
Spring Cloud Context provides utilities and special services for the <code class="literal">ApplicationContext</code> of a Spring Cloud application (bootstrap context, encryption, refresh scope, and environment endpoints). Spring Cloud Commons is a set of abstractions and common classes used in different Spring Cloud implementations (such as Spring Cloud Netflix and Spring Cloud Consul).</p><p>If you get an exception due to "Illegal key size" and you use Sun&#8217;s JDK, you need to install the Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files.
See the following links for more information:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><a class="link" href="https://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html" target="_top">Java 6 JCE</a></li><li class="listitem"><a class="link" href="https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html" target="_top">Java 7 JCE</a></li><li class="listitem"><a class="link" href="https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html" target="_top">Java 8 JCE</a></li></ul></div><p>Extract the files into the JDK/jre/lib/security folder for whichever version of JRE/JDK x64/x86 you use.</p><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>Spring Cloud is released under the non-restrictive Apache 2.0 license.
If you would like to contribute to this section of the documentation or if you find an error, you can find the source code and issue trackers for the project at <a class="link" href="https://github.com/spring-cloud/spring-cloud-commons/tree/master/docs/src/main/asciidoc" target="_top">github</a>.</p></td></tr></table></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__features.html">Prev</a>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_context_application_context_services.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">1.&nbsp;Features&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;2.&nbsp;Spring Cloud Context: Application Context Services</td></tr></table></div></body></html>

View File

@@ -0,0 +1,63 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>114.&nbsp;Configuration</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_gateway.html" title="Part&nbsp;XV.&nbsp;Spring Cloud Gateway"><link rel="prev" href="multi__tls_ssl.html" title="113.&nbsp;TLS / SSL"><link rel="next" href="multi__cors_configuration.html" title="115.&nbsp;CORS Configuration"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">114.&nbsp;Configuration</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__tls_ssl.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XV.&nbsp;Spring Cloud Gateway</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__cors_configuration.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuration_2" href="#_configuration_2"></a>114.&nbsp;Configuration</h2></div></div></div><p>Configuration for Spring Cloud Gateway is driven by a collection of `RouteDefinitionLocator`s.</p><p><b>RouteDefinitionLocator.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> RouteDefinitionLocator {
Flux&lt;RouteDefinition&gt; getRouteDefinitions();
}</pre><p>
</p><p>By default, a <code class="literal">PropertiesRouteDefinitionLocator</code> loads properties using Spring Boot&#8217;s <code class="literal">@ConfigurationProperties</code> mechanism.</p><p>The configuration examples above all use a shortcut notation that uses positional arguments rather than named ones. The two examples below are equivalent:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatus_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - name</span>: SetStatus
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> args</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> status</span>: <span class="hl-number">401</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - id</span>: setstatusshortcut_route
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> uri</span>: https://example.org
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> filters</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - SetStatus</span>=<span class="hl-number">401</span></pre><p>
</p><p>For some usages of the gateway, properties will be adequate, but some production use cases will benefit from loading configuration from an external source, such as a database. Future milestone versions will have <code class="literal">RouteDefinitionLocator</code> implementations based off of Spring Data Repositories such as: Redis, MongoDB and Cassandra.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_fluent_java_routes_api" href="#_fluent_java_routes_api"></a>114.1&nbsp;Fluent Java Routes API</h2></div></div></div><p>To allow for simple configuration in Java, there is a fluent API defined in the <code class="literal">RouteLocatorBuilder</code> bean.</p><p><b>GatewaySampleApplication.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// static imports from GatewayFilters and RoutePredicates</span>
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> RouteLocator customRouteLocator(RouteLocatorBuilder builder, ThrottleGatewayFilterFactory throttle) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> builder.routes()
.route(r -&gt; r.host(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**.abc.org"</span>).and().path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/png"</span>)
.filters(f -&gt;
f.addResponseHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-TestHeader"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foobar"</span>))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.route(r -&gt; r.path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/image/webp"</span>)
.filters(f -&gt;
f.addResponseHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"X-AnotherHeader"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"baz"</span>))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.route(r -&gt; r.order(-<span class="hl-number">1</span>)
.host(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"**.throttle.org"</span>).and().path(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/get"</span>)
.filters(f -&gt; f.filter(throttle.apply(<span class="hl-number">1</span>,
<span class="hl-number">1</span>,
<span class="hl-number">10</span>,
TimeUnit.SECONDS)))
.uri(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"http://httpbin.org:80"</span>)
)
.build();
}</pre><p>
</p><p>This style also allows for more custom predicate assertions. The predicates defined by <code class="literal">RouteDefinitionLocator</code> beans are combined using logical <code class="literal">and</code>. By using the fluent Java API, you can use the <code class="literal">and()</code>, <code class="literal">or()</code> and <code class="literal">negate()</code> operators on the <code class="literal">Predicate</code> class.</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_discoveryclient_route_definition_locator" href="#_discoveryclient_route_definition_locator"></a>114.2&nbsp;DiscoveryClient Route Definition Locator</h2></div></div></div><p>The Gateway can be configured to create routes based on services registered with a <code class="literal">DiscoveryClient</code> compatible service registry.</p><p>To enable this, set <code class="literal">spring.cloud.gateway.discovery.locator.enabled=true</code> and make sure a <code class="literal">DiscoveryClient</code> implementation is on the classpath and enabled (such as Netflix Eureka, Consul or Zookeeper).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_configuring_predicates_and_filters_for_discoveryclient_routes" href="#_configuring_predicates_and_filters_for_discoveryclient_routes"></a>114.2.1&nbsp;Configuring Predicates and Filters For DiscoveryClient Routes</h3></div></div></div><p>By default the Gateway defines a single predicate and filter for routes created via a <code class="literal">DiscoveryClient</code>.</p><p>The default predicate is a path predicate defined with the pattern <code class="literal">/serviceId/**</code>, where <code class="literal">serviceId</code> is
the id of the service from the <code class="literal">DiscoveryClient</code>.</p><p>The default filter is rewrite path filter with the regex <code class="literal">/serviceId/(?&lt;remaining&gt;.*)</code> and the replacement
<code class="literal">/${remaining}</code>. This just strips the service id from the path before the request is sent
downstream.</p><p>If you would like to customize the predicates and/or filters used by the <code class="literal">DiscoveryClient</code> routes you can do so
by setting <code class="literal">spring.cloud.gateway.discovery.locator.predicates[x]</code> and <code class="literal">spring.cloud.gateway.discovery.locator.filters[y]</code>.
When doing so you need to make sure to include the default predicate and filter above, if you want to retain
that functionality. Below is an example of what this looks like.</p><p><b>application.properties.&nbsp;</b>
</p><pre class="screen">spring.cloud.gateway.discovery.locator.predicates[0].name: Path
spring.cloud.gateway.discovery.locator.predicates[0].args[pattern]: "'/'+serviceId+'/**'"
spring.cloud.gateway.discovery.locator.predicates[1].name: Host
spring.cloud.gateway.discovery.locator.predicates[1].args[pattern]: "'**.foo.com'"
spring.cloud.gateway.discovery.locator.filters[0].name: Hystrix
spring.cloud.gateway.discovery.locator.filters[0].args[name]: serviceId
spring.cloud.gateway.discovery.locator.filters[1].name: RewritePath
spring.cloud.gateway.discovery.locator.filters[1].args[regexp]: "'/' + serviceId + '/(?&lt;remaining&gt;.*)'"
spring.cloud.gateway.discovery.locator.filters[1].args[replacement]: "'/${remaining}'"</pre><p>
</p></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__tls_ssl.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_gateway.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__cors_configuration.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">113.&nbsp;TLS / SSL&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;115.&nbsp;CORS Configuration</td></tr></table></div></body></html>

View File

@@ -0,0 +1,124 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>29.&nbsp;Configuration Options</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_stream.html" title="Part&nbsp;V.&nbsp;Spring Cloud Stream"><link rel="prev" href="multi_spring-cloud-stream-overview-binders.html" title="28.&nbsp;Binders"><link rel="next" href="multi_content-type-management.html" title="30.&nbsp;Content Type Negotiation"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">29.&nbsp;Configuration Options</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi_spring-cloud-stream-overview-binders.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;V.&nbsp;Spring Cloud Stream</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi_content-type-management.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuration_options" href="#_configuration_options"></a>29.&nbsp;Configuration Options</h2></div></div></div><p>Spring Cloud Stream supports general configuration options as well as configuration for bindings and binders.
Some binders let additional binding properties support middleware-specific features.</p><p>Configuration options can be provided to Spring Cloud Stream applications through any mechanism supported by Spring Boot.
This includes application arguments, environment variables, and YAML or .properties files.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_binding_service_properties" href="#_binding_service_properties"></a>29.1&nbsp;Binding Service Properties</h2></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.config.BindingServiceProperties</code></p><div class="variablelist"><dl class="variablelist"><dt><span class="term">spring.cloud.stream.instanceCount</span></dt><dd><p class="simpara">The number of deployed instances of an application.
Must be set for partitioning on the producer side. Must be set on the consumer side when using RabbitMQ and with Kafka if <code class="literal">autoRebalanceEnabled=false</code>.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">spring.cloud.stream.instanceIndex</span></dt><dd>The instance index of the application: A number from <code class="literal">0</code> to <code class="literal">instanceCount - 1</code>.
Used for partitioning with RabbitMQ and with Kafka if <code class="literal">autoRebalanceEnabled=false</code>.
Automatically set in Cloud Foundry to match the application&#8217;s instance index.</dd><dt><span class="term">spring.cloud.stream.dynamicDestinations</span></dt><dd><p class="simpara">A list of destinations that can be bound dynamically (for example, in a dynamic routing scenario).
If set, only listed destinations can be bound.</p><p class="simpara">Default: empty (letting any destination be bound).</p></dd><dt><span class="term">spring.cloud.stream.defaultBinder</span></dt><dd><p class="simpara">The default binder to use, if multiple binders are configured.
See <a class="link" href="multi_spring-cloud-stream-overview-binders.html#multiple-binders" title="28.4&nbsp;Multiple Binders on the Classpath">Multiple Binders on the Classpath</a>.</p><p class="simpara">Default: empty.</p></dd><dt><span class="term">spring.cloud.stream.overrideCloudConnectors</span></dt><dd><p class="simpara">This property is only applicable when the <code class="literal">cloud</code> profile is active and Spring Cloud Connectors are provided with the application.
If the property is <code class="literal">false</code> (the default), the binder detects a suitable bound service (for example, a RabbitMQ service bound in Cloud Foundry for the RabbitMQ binder) and uses it for creating connections (usually through Spring Cloud Connectors).
When set to <code class="literal">true</code>, this property instructs binders to completely ignore the bound services and rely on Spring Boot properties (for example, relying on the <code class="literal">spring.rabbitmq.*</code> properties provided in the environment for the RabbitMQ binder).
The typical usage of this property is to be nested in a customized environment <a class="link" href="multi_spring-cloud-stream-overview-binders.html#multiple-systems" title="28.5&nbsp;Connecting to Multiple Systems">when connecting to multiple systems</a>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">spring.cloud.stream.bindingRetryInterval</span></dt><dd><p class="simpara">The interval (in seconds) between retrying binding creation when, for example, the binder does not support late binding and the broker (for example, Apache Kafka) is down.
Set it to zero to treat such conditions as fatal, preventing the application from starting.</p><p class="simpara">Default: <code class="literal">30</code></p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="binding-properties" href="#binding-properties"></a>29.2&nbsp;Binding Properties</h2></div></div></div><p>Binding properties are supplied by using the format of <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.&lt;property&gt;=&lt;value&gt;</code>.
The <code class="literal">&lt;channelName&gt;</code> represents the name of the channel being configured (for example, <code class="literal">output</code> for a <code class="literal">Source</code>).</p><p>To avoid repetition, Spring Cloud Stream supports setting values for all channels, in the format of <code class="literal">spring.cloud.stream.default.&lt;property&gt;=&lt;value&gt;</code>.</p><p>In what follows, we indicate where we have omitted the <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.</code> prefix and focus just on the property name, with the understanding that the prefix ise included at runtime.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_binding_properties" href="#_common_binding_properties"></a>29.2.1&nbsp;Common Binding Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.config.BindingProperties</code></p><p>The following binding properties are available for both input and output bindings and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.destination=ticktock</code>).</p><p>Default values can be set by using the <code class="literal">spring.cloud.stream.default</code> prefix (for example`spring.cloud.stream.default.contentType=application/json`).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">destination</span></dt><dd>The target destination of a channel on the bound middleware (for example, the RabbitMQ exchange or Kafka topic).
If the channel is bound as a consumer, it could be bound to multiple destinations, and the destination names can be specified as comma-separated <code class="literal">String</code> values.
If not set, the channel name is used instead.
The default value of this property cannot be overridden.</dd><dt><span class="term">group</span></dt><dd><p class="simpara">The consumer group of the channel.
Applies only to inbound bindings.
See <a class="link" href="multi__main_concepts.html#consumer-groups" title="26.4&nbsp;Consumer Groups">Consumer Groups</a>.</p><p class="simpara">Default: <code class="literal">null</code> (indicating an anonymous consumer).</p></dd><dt><span class="term">contentType</span></dt><dd><p class="simpara">The content type of the channel.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi_content-type-management.html" title="30.&nbsp;Content Type Negotiation">Chapter&nbsp;30, <i>Content Type Negotiation</i></a></span>&#8221;</span>.</p><p class="simpara">Default: <code class="literal">application/json</code>.</p></dd><dt><span class="term">binder</span></dt><dd><p class="simpara">The binder used by this binding.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi_spring-cloud-stream-overview-binders.html#multiple-binders" title="28.4&nbsp;Multiple Binders on the Classpath">Section&nbsp;28.4, &#8220;Multiple Binders on the Classpath&#8221;</a></span>&#8221;</span> for details.</p><p class="simpara">Default: <code class="literal">null</code> (the default binder is used, if it exists).</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_consumer_properties" href="#_consumer_properties"></a>29.2.2&nbsp;Consumer Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.binder.ConsumerProperties</code></p><p>The following binding properties are available for input bindings only and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.consumer.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.consumer.concurrency=3</code>).</p><p>Default values can be set by using the <code class="literal">spring.cloud.stream.default.consumer</code> prefix (for example, <code class="literal">spring.cloud.stream.default.consumer.headerMode=none</code>).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">concurrency</span></dt><dd><p class="simpara">The concurrency of the inbound consumer.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">partitioned</span></dt><dd><p class="simpara">Whether the consumer receives data from a partitioned producer.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">headerMode</span></dt><dd><p class="simpara">When set to <code class="literal">none</code>, disables header parsing on input.
Effective only for messaging middleware that does not support message headers natively and requires header embedding.
This option is useful when consuming data from non-Spring Cloud Stream applications when native headers are not supported.
When set to <code class="literal">headers</code>, it uses the middleware&#8217;s native header mechanism.
When set to <code class="literal">embeddedHeaders</code>, it embeds headers into the message payload.</p><p class="simpara">Default: depends on the binder implementation.</p></dd><dt><span class="term">maxAttempts</span></dt><dd><p class="simpara">If processing fails, the number of attempts to process the message (including the first).
Set to <code class="literal">1</code> to disable retry.</p><p class="simpara">Default: <code class="literal">3</code>.</p></dd><dt><span class="term">backOffInitialInterval</span></dt><dd><p class="simpara">The backoff initial interval on retry.</p><p class="simpara">Default: <code class="literal">1000</code>.</p></dd><dt><span class="term">backOffMaxInterval</span></dt><dd><p class="simpara">The maximum backoff interval.</p><p class="simpara">Default: <code class="literal">10000</code>.</p></dd><dt><span class="term">backOffMultiplier</span></dt><dd><p class="simpara">The backoff multiplier.</p><p class="simpara">Default: <code class="literal">2.0</code>.</p></dd><dt><span class="term">instanceIndex</span></dt><dd><p class="simpara">When set to a value greater than equal to zero, it allows customizing the instance index of this consumer (if different from <code class="literal">spring.cloud.stream.instanceIndex</code>).
When set to a negative value, it defaults to <code class="literal">spring.cloud.stream.instanceIndex</code>.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi__inter_application_communication.html#spring-cloud-stream-overview-instance-index-instance-count" title="32.2&nbsp;Instance Index and Instance Count">Section&nbsp;32.2, &#8220;Instance Index and Instance Count&#8221;</a></span>&#8221;</span> for more information.</p><p class="simpara">Default: <code class="literal">-1</code>.</p></dd><dt><span class="term">instanceCount</span></dt><dd><p class="simpara">When set to a value greater than equal to zero, it allows customizing the instance count of this consumer (if different from <code class="literal">spring.cloud.stream.instanceCount</code>).
When set to a negative value, it defaults to <code class="literal">spring.cloud.stream.instanceCount</code>.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi__inter_application_communication.html#spring-cloud-stream-overview-instance-index-instance-count" title="32.2&nbsp;Instance Index and Instance Count">Section&nbsp;32.2, &#8220;Instance Index and Instance Count&#8221;</a></span>&#8221;</span> for more information.</p><p class="simpara">Default: <code class="literal">-1</code>.</p></dd><dt><span class="term">useNativeDecoding</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, the inbound message is deserialized directly by the client library, which must be configured correspondingly (for example, setting an appropriate Kafka producer value deserializer).
When this configuration is being used, the inbound message unmarshalling is not based on the <code class="literal">contentType</code> of the binding.
When native decoding is used, it is the responsibility of the producer to use an appropriate encoder (for example, the Kafka producer value serializer) to serialize the outbound message.
Also, when native encoding and decoding is used, the <code class="literal">headerMode=embeddedHeaders</code> property is ignored and headers are not embedded in the message.
See the producer property <code class="literal">useNativeEncoding</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_producer_properties" href="#_producer_properties"></a>29.2.3&nbsp;Producer Properties</h3></div></div></div><p>These properties are exposed via <code class="literal">org.springframework.cloud.stream.binder.ProducerProperties</code></p><p>The following binding properties are available for output bindings only and must be prefixed with <code class="literal">spring.cloud.stream.bindings.&lt;channelName&gt;.producer.</code> (for example, <code class="literal">spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id</code>).</p><p>Default values can be set by using the prefix <code class="literal">spring.cloud.stream.default.producer</code> (for example, <code class="literal">spring.cloud.stream.default.producer.partitionKeyExpression=payload.id</code>).</p><div class="variablelist"><dl class="variablelist"><dt><span class="term">partitionKeyExpression</span></dt><dd><p class="simpara">A SpEL expression that determines how to partition outbound data.
If set, or if <code class="literal">partitionKeyExtractorClass</code> is set, outbound data on this channel is partitioned. <code class="literal">partitionCount</code> must be set to a value greater than 1 to be effective.
Mutually exclusive with <code class="literal">partitionKeyExtractorClass</code>.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi__main_concepts.html#partitioning" title="26.6&nbsp;Partitioning Support">Section&nbsp;26.6, &#8220;Partitioning Support&#8221;</a></span>&#8221;</span>.</p><p class="simpara">Default: null.</p></dd><dt><span class="term">partitionKeyExtractorClass</span></dt><dd><p class="simpara">A <code class="literal">PartitionKeyExtractorStrategy</code> implementation.
If set, or if <code class="literal">partitionKeyExpression</code> is set, outbound data on this channel is partitioned. <code class="literal">partitionCount</code> must be set to a value greater than 1 to be effective.
Mutually exclusive with <code class="literal">partitionKeyExpression</code>.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="multi__main_concepts.html#partitioning" title="26.6&nbsp;Partitioning Support">Section&nbsp;26.6, &#8220;Partitioning Support&#8221;</a></span>&#8221;</span>.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionSelectorClass</span></dt><dd><p class="simpara"> A <code class="literal">PartitionSelectorStrategy</code> implementation.
Mutually exclusive with <code class="literal">partitionSelectorExpression</code>.
If neither is set, the partition is selected as the <code class="literal">hashCode(key) % partitionCount</code>, where <code class="literal">key</code> is computed through either <code class="literal">partitionKeyExpression</code> or <code class="literal">partitionKeyExtractorClass</code>.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionSelectorExpression</span></dt><dd><p class="simpara">A SpEL expression for customizing partition selection.
Mutually exclusive with <code class="literal">partitionSelectorClass</code>.
If neither is set, the partition is selected as the <code class="literal">hashCode(key) % partitionCount</code>, where <code class="literal">key</code> is computed through either <code class="literal">partitionKeyExpression</code> or <code class="literal">partitionKeyExtractorClass</code>.</p><p class="simpara">Default: <code class="literal">null</code>.</p></dd><dt><span class="term">partitionCount</span></dt><dd><p class="simpara">The number of target partitions for the data, if partitioning is enabled.
Must be set to a value greater than 1 if the producer is partitioned.
On Kafka, it is interpreted as a hint. The larger of this and the partition count of the target topic is used instead.</p><p class="simpara">Default: <code class="literal">1</code>.</p></dd><dt><span class="term">requiredGroups</span></dt><dd>A comma-separated list of groups to which the producer must ensure message delivery even if they start after it has been created (for example, by pre-creating durable queues in RabbitMQ).</dd><dt><span class="term">headerMode</span></dt><dd><p class="simpara">When set to <code class="literal">none</code>, it disables header embedding on output.
It is effective only for messaging middleware that does not support message headers natively and requires header embedding.
This option is useful when producing data for non-Spring Cloud Stream applications when native headers are not supported.
When set to <code class="literal">headers</code>, it uses the middleware&#8217;s native header mechanism.
When set to <code class="literal">embeddedHeaders</code>, it embeds headers into the message payload.</p><p class="simpara">Default: Depends on the binder implementation.</p></dd><dt><span class="term">useNativeEncoding</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, the outbound message is serialized directly by the client library, which must be configured correspondingly (for example, setting an appropriate Kafka producer value serializer).
When this configuration is being used, the outbound message marshalling is not based on the <code class="literal">contentType</code> of the binding.
When native encoding is used, it is the responsibility of the consumer to use an appropriate decoder (for example, the Kafka consumer value de-serializer) to deserialize the inbound message.
Also, when native encoding and decoding is used, the <code class="literal">headerMode=embeddedHeaders</code> property is ignored and headers are not embedded in the message.
See the consumer property <code class="literal">useNativeDecoding</code>.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd><dt><span class="term">errorChannelEnabled</span></dt><dd><p class="simpara">When set to <code class="literal">true</code>, if the binder supports asynchroous send results, send failures are sent to an error channel for the destination.
See <span class="quote">&#8220;<span class="quote"><a class="xref" href="">???</a></span>&#8221;</span> for more information.</p><p class="simpara">Default: <code class="literal">false</code>.</p></dd></dl></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="dynamicdestination" href="#dynamicdestination"></a>29.3&nbsp;Using Dynamically Bound Destinations</h2></div></div></div><p>Besides the channels defined by using <code class="literal">@EnableBinding</code>, Spring Cloud Stream lets applications send messages to dynamically bound destinations.
This is useful, for example, when the target destination needs to be determined at runtime.
Applications can do so by using the <code class="literal">BinderAwareChannelResolver</code> bean, registered automatically by the <code class="literal">@EnableBinding</code> annotation.</p><p>The 'spring.cloud.stream.dynamicDestinations' property can be used for restricting the dynamic destination names to a known set (whitelisting).
If this property is not set, any destination can be bound dynamically.</p><p>The <code class="literal">BinderAwareChannelResolver</code> can be used directly, as shown in the following example of a REST controller using a path variable to decide the target channel:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding</span></em>
<em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SourceWithDynamicDestination {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> BinderAwareChannelResolver resolver;
<em><span class="hl-annotation" style="color: gray">@RequestMapping(path = "/{target}", method = POST, consumes = "*/*")</span></em>
<em><span class="hl-annotation" style="color: gray">@ResponseStatus(HttpStatus.ACCEPTED)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handleRequest(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> String body, <em><span class="hl-annotation" style="color: gray">@PathVariable("target")</span></em> target,
<em><span class="hl-annotation" style="color: gray">@RequestHeader(HttpHeaders.CONTENT_TYPE)</span></em> Object contentType) {
sendMessage(body, target, contentType);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sendMessage(String body, String target, Object contentType) {
resolver.resolveDestination(target).send(MessageBuilder.createMessage(body,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
}</pre><p>Now consider what happens when we start the application on the default port (8080) and make the following requests with CURL:</p><pre class="screen">curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhost:8080/customers
curl -H "Content-Type: application/json" -X POST -d "order-1" http://localhost:8080/orders</pre><p>The destinations, 'customers' and 'orders', are created in the broker (in the exchange for Rabbit or in the topic for Kafka) with names of 'customers' and 'orders', and the data is published to the appropriate destinations.</p><p>The <code class="literal">BinderAwareChannelResolver</code> is a general-purpose Spring Integration <code class="literal">DestinationResolver</code> and can be injected in other components&#8201;&#8212;&#8201;for example, in a router using a SpEL expression based on the <code class="literal">target</code> field of an incoming JSON message. The following example includes a router that reads SpEL expressions:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@EnableBinding</span></em>
<em><span class="hl-annotation" style="color: gray">@Controller</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> SourceWithDynamicDestination {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> BinderAwareChannelResolver resolver;
<em><span class="hl-annotation" style="color: gray">@RequestMapping(path = "/", method = POST, consumes = "application/json")</span></em>
<em><span class="hl-annotation" style="color: gray">@ResponseStatus(HttpStatus.ACCEPTED)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> handleRequest(<em><span class="hl-annotation" style="color: gray">@RequestBody</span></em> String body, <em><span class="hl-annotation" style="color: gray">@RequestHeader(HttpHeaders.CONTENT_TYPE)</span></em> Object contentType) {
sendMessage(body, contentType);
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> sendMessage(Object body, Object contentType) {
routerChannel().send(MessageBuilder.createMessage(body,
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
}
<em><span class="hl-annotation" style="color: gray">@Bean(name = "routerChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> MessageChannel routerChannel() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> DirectChannel();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<em><span class="hl-annotation" style="color: gray">@ServiceActivator(inputChannel = "routerChannel")</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> ExpressionEvaluatingRouter router() {
ExpressionEvaluatingRouter router =
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ExpressionEvaluatingRouter(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> SpelExpressionParser().parseExpression(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"payload.target"</span>));
router.setDefaultOutputChannelName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"default-output"</span>);
router.setChannelResolver(resolver);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> router;
}
}</pre><p>The <a class="link" href="https://github.com/spring-cloud-stream-app-starters/router" target="_top">Router Sink Application</a> uses this technique to create the destinations on-demand.</p><p>If the channel names are known in advance, you can configure the producer properties as with any other destination.
Alternatively, if you register a <code class="literal">NewBindingCallback&lt;&gt;</code> bean, it is invoked just before the binding is created.
The callback takes the generic type of the extended producer properties used by the binder.
It has one method:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> configure(String channelName, MessageChannel channel, ProducerProperties producerProperties,
T extendedProducerProperties);</pre><p>The following example shows how to use the RabbitMQ binder:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> NewBindingCallback&lt;RabbitProducerProperties&gt; dynamicConfigurer() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (name, channel, props, extended) -&gt; {
props.setRequiredGroups(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"bindThisQueue"</span>);
extended.setQueueNameGroupOnly(true);
extended.setAutoBindDlq(true);
extended.setDeadLetterQueueName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myDLQ"</span>);
};
}</pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you need to support dynamic destinations with multiple binder types, use <code class="literal">Object</code> for the generic type and cast the <code class="literal">extended</code> argument as needed.</p></td></tr></table></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi_spring-cloud-stream-overview-binders.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_stream.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi_content-type-management.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">28.&nbsp;Binders&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;30.&nbsp;Content Type Negotiation</td></tr></table></div></body></html>

View File

@@ -0,0 +1,17 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>81.&nbsp;Configuring Authentication Downstream of a Zuul Proxy</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_security.html" title="Part&nbsp;XI.&nbsp;Spring Cloud Security"><link rel="prev" href="multi__more_detail.html" title="80.&nbsp;More Detail"><link rel="next" href="multi__spring_cloud_for_cloud_foundry.html" title="Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">81.&nbsp;Configuring Authentication Downstream of a Zuul Proxy</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__more_detail.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XI.&nbsp;Spring Cloud Security</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_for_cloud_foundry.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_configuring_authentication_downstream_of_a_zuul_proxy" href="#_configuring_authentication_downstream_of_a_zuul_proxy"></a>81.&nbsp;Configuring Authentication Downstream of a Zuul Proxy</h2></div></div></div><p>You can control the authorization behaviour downstream of an
<code class="literal">@EnableZuulProxy</code> through the <code class="literal">proxy.auth.*</code> settings. Example:</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">proxy</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> auth</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> routes</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> customers</span>: oauth2
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> stores</span>: passthru
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> recommendations</span>: none</pre><p>
</p><p>In this example the "customers" service gets an OAuth2 token relay,
the "stores" service gets a passthrough (the authorization header is
just passed downstream), and the "recommendations" service has its
authorization header removed. The default behaviour is to do a token
relay if there is a token available, and passthru otherwise.</p><p>See
<a class="link" href="https://github.com/spring-cloud/spring-cloud-security/tree/master/src/main/java/org/springframework/cloud/security/oauth2/proxy/ProxyAuthenticationProperties" target="_top">
ProxyAuthenticationProperties</a> for full details.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__more_detail.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_security.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__spring_cloud_for_cloud_foundry.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">80.&nbsp;More Detail&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry</td></tr></table></div></body></html>

View File

@@ -0,0 +1,13 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>115.&nbsp;CORS Configuration</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_gateway.html" title="Part&nbsp;XV.&nbsp;Spring Cloud Gateway"><link rel="prev" href="multi__configuration_2.html" title="114.&nbsp;Configuration"><link rel="next" href="multi__actuator_api.html" title="116.&nbsp;Actuator API"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">115.&nbsp;CORS Configuration</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__configuration_2.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XV.&nbsp;Spring Cloud Gateway</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__actuator_api.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_cors_configuration" href="#_cors_configuration"></a>115.&nbsp;CORS Configuration</h2></div></div></div><p>The gateway can be configured to control CORS behavior. The "global" CORS configuration is a map of URL patterns to <a class="link" href="https://docs.spring.io/spring/docs/5.0.x/javadoc-api/org/springframework/web/cors/CorsConfiguration.html" target="_top">Spring Framework <code class="literal">CorsConfiguration</code></a>.</p><p><b>application.yml.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> gateway</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> globalcors</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> corsConfigurations</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> '[/**]'</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> allowedOrigins</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"https://docs.spring.io"</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> allowedMethods</span>:
- GET</pre><p>
</p><p>In the example above, CORS requests will be allowed from requests that originate from docs.spring.io for all GET requested paths.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__configuration_2.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_gateway.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__actuator_api.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">114.&nbsp;Configuration&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;116.&nbsp;Actuator API</td></tr></table></div></body></html>

View File

@@ -0,0 +1,19 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>54.&nbsp;Current Span</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"><link rel="prev" href="multi__current_tracing_component.html" title="53.&nbsp;Current Tracing Component"><link rel="next" href="multi__instrumentation.html" title="55.&nbsp;Instrumentation"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">54.&nbsp;Current Span</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__current_tracing_component.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__instrumentation.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_current_span" href="#_current_span"></a>54.&nbsp;Current Span</h2></div></div></div><p>Brave supports a <span class="quote">&#8220;<span class="quote">current span</span>&#8221;</span> concept which represents the in-flight operation.
You can use <code class="literal">Tracer.currentSpan()</code> to add custom tags to a span and <code class="literal">Tracer.nextSpan()</code> to create a child of whatever is in-flight.</p><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In Sleuth, you can autowire the <code class="literal">Tracer</code> bean to retrieve the current span via
<code class="literal">tracer.currentSpan()</code> method. To retrieve the current context just call
<code class="literal">tracer.currentSpan().context()</code>. To get the current trace id as String
you can use the <code class="literal">traceIdString()</code> method like this: <code class="literal">tracer.currentSpan().context().traceIdString()</code>.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_setting_a_span_in_scope_manually" href="#_setting_a_span_in_scope_manually"></a>54.1&nbsp;Setting a span in scope manually</h2></div></div></div><p>When writing new instrumentation, it is important to place a span you created in scope as the current span.
Not only does doing so let users access it with <code class="literal">Tracer.currentSpan()</code>, but it also allows customizations such as SLF4J MDC to see the current trace IDs.</p><p><code class="literal">Tracer.withSpanInScope(Span)</code> facilitates this and is most conveniently employed by using the try-with-resources idiom.
Whenever external code might be invoked (such as proceeding an interceptor or otherwise), place the span in scope, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> inboundRequest.invoke();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> { <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span</span>
span.finish();
}</pre><p>In edge cases, you may need to clear the current span temporarily (for example, launching a task that should not be associated with the current request). To do tso, pass null to <code class="literal">withSpanInScope</code>, as shown in the following example:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope cleared = tracer.withSpanInScope(null)) {
startBackgroundThread();
}</pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__current_tracing_component.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_sleuth.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__instrumentation.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">53.&nbsp;Current Tracing Component&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;55.&nbsp;Instrumentation</td></tr></table></div></body></html>

View File

@@ -0,0 +1,7 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>53.&nbsp;Current Tracing Component</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"><link rel="prev" href="multi__propagation.html" title="52.&nbsp;Propagation"><link rel="next" href="multi__current_span.html" title="54.&nbsp;Current Span"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">53.&nbsp;Current Tracing Component</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__propagation.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__current_span.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_current_tracing_component" href="#_current_tracing_component"></a>53.&nbsp;Current Tracing Component</h2></div></div></div><p>Brave supports a <span class="quote">&#8220;<span class="quote">current tracing component</span>&#8221;</span> concept, which should only be used when you have no other way to get a reference.
This was made for JDBC connections, as they often initialize prior to the tracing component.</p><p>The most recent tracing component instantiated is available through <code class="literal">Tracing.current()</code>.
You can also use <code class="literal">Tracing.currentTracer()</code> to get only the tracer.
If you use either of these methods, do not cache the result.
Instead, look them up each time you need them.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__propagation.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_sleuth.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__current_span.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">52.&nbsp;Propagation&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;54.&nbsp;Current Span</td></tr></table></div></body></html>

View File

@@ -0,0 +1,217 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>92.&nbsp;Customization</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_contract.html" title="Part&nbsp;XIII.&nbsp;Spring Cloud Contract"><link rel="prev" href="multi_contract-dsl.html" title="91.&nbsp;Contract DSL"><link rel="next" href="multi__using_the_pluggable_architecture.html" title="93.&nbsp;Using the Pluggable Architecture"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">92.&nbsp;Customization</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi_contract-dsl.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XIII.&nbsp;Spring Cloud Contract</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__using_the_pluggable_architecture.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customization" href="#_customization"></a>92.&nbsp;Customization</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is valid only for Groovy DSL</p></td></tr></table></div><p>You can customize the Spring Cloud Contract Verifier by extending the DSL, as shown in
the remainder of this section.</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_extending_the_dsl" href="#_extending_the_dsl"></a>92.1&nbsp;Extending the DSL</h2></div></div></div><p>You can provide your own functions to the DSL. The key requirement for this feature is to
maintain the static compatibility. Later in this document, you can see examples of:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Creating a JAR with reusable classes.</li><li class="listitem">Referencing of these classes in the DSLs.</li></ul></div><p>You can find the full example
<a class="link" href="https://github.com/spring-cloud-samples/spring-cloud-contract-samples" target="_top">here</a>.</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_common_jar" href="#_common_jar"></a>92.1.1&nbsp;Common JAR</h3></div></div></div><p>The following examples show three classes that can be reused in the DSLs.</p><p><span class="strong"><strong>PatternUtils</strong></span> contains functions used by both the <span class="strong"><strong>consumer</strong></span> and the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> java.util.regex.Pattern;
<strong class="hl-tag" style="color: blue">/**
* If you want to use {@link Pattern} directly in your tests
* then you can create a class resembling this one. It can
* contain all the {@link Pattern} you want to use in the DSL.
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(c(PatternUtils.oldEnough()))]
* )
* }
* &lt;/pre&gt;
*
* Notice that we're using both {@code $()} for dynamic values
* and {@code c()} for the consumer side.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PatternUtils {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> String tooYoung() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[0-1][0-9]"</span>;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"[2-9][0-9]"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
<strong class="hl-tag" style="color: blue">/**
* Makes little sense but it's just an example ;)
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> Pattern ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> Pattern.compile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ConsumerUtils</strong></span> contains functions used by the <span class="strong"><strong>consumer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ClientDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the consumer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you can have a regular expression.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you have to have a concrete value.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ConsumerUtils {
<strong class="hl-tag" style="color: blue">/**
* Consumer side property. By using the {@link ClientDslProperty}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* request {
* body(
* [ age: $(ConsumerUtils.oldEnough())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ClientDslProperty oldEnough() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::start[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ClientDslProperty(PatternUtils.oldEnough(), <span class="hl-number">40</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//remove::end[return]</span>
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre><p><span class="strong"><strong>ProducerUtils</strong></span> contains functions used by the <span class="strong"><strong>producer</strong></span>.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> com.example;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.internal.ServerDslProperty;
<strong class="hl-tag" style="color: blue">/**
* DSL Properties passed to the DSL from the producer's perspective.
* That means that on the input side {@code Request} for HTTP
* or {@code Input} for messaging you have to have a concrete value.
* On the {@code Response} for HTTP or {@code Output} for messaging
* you can have a regular expression.
*
* @author Marcin Grzejszczak
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//tag::impl[]</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ProducerUtils {
<strong class="hl-tag" style="color: blue">/**
* Producer side property. By using the {@link ProducerUtils}
* you can omit most of boilerplate code from the perspective
* of dynamic values. Example
*
* &lt;pre&gt;
* {@code
* response {
* body(
* [ status: $(ProducerUtils.ok())]
* )
* }
* &lt;/pre&gt;
*
* That way it's in the implementation that we decide what value we will pass to the consumer
* and which one to the producer.
*/</strong>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> ServerDslProperty ok() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// this example is not the best one and</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// theoretically you could just pass the regex instead of `ServerDslProperty` but</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// it's just to show some new tricks :)</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> ServerDslProperty( PatternUtils.ok(), <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"OK"</span>);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::impl[]</span></pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_adding_the_dependency_to_the_project" href="#_adding_the_dependency_to_the_project"></a>92.1.2&nbsp;Adding the Dependency to the Project</h3></div></div></div><p>In order for the plugins and IDE to be able to reference the common JAR classes, you need
to pass the dependency to your project.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_test_the_dependency_in_the_projects_dependencies" href="#_test_the_dependency_in_the_projects_dependencies"></a>92.1.3&nbsp;Test the Dependency in the Project&#8217;s Dependencies</h3></div></div></div><p>First, add the common jar dependency as a test dependency. Because your contracts files
are available on the test resources path, the common jar classes automatically become
visible in your Groovy files. The following examples show how to test the dependency:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>test<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">testCompile(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1.BUILD-SNAPSHOT"</span>)</pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_test_a_dependency_in_the_plugins_dependencies" href="#_test_a_dependency_in_the_plugins_dependencies"></a>92.1.4&nbsp;Test a Dependency in the Plugin&#8217;s Dependencies</h3></div></div></div><p>Now, you must add the dependency for the plugin to reuse at runtime, as shown in the
following example:</p><p class="primary"><b>Maven.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;plugin&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>org.springframework.cloud<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>spring-cloud-contract-maven-plugin<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${spring-cloud-contract.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;extensions&gt;</span>true<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/extensions&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;packageWithBaseClasses&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/packageWithBaseClasses&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;contractPackageRegex&gt;</span>.*intoxication.*<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/contractPackageRegex&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;baseClassFQN&gt;</span>com.example.intoxication.BeerIntoxicationBase<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassFQN&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMapping&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/baseClassMappings&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/configuration&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;groupId&gt;</span>com.example<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/groupId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;artifactId&gt;</span>beer-common<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/artifactId&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;version&gt;</span>${project.version}<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/version&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;scope&gt;</span>compile<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/scope&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependency&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/dependencies&gt;</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-tag">&lt;/plugin&gt;</span></pre><p class="primary">
</p><p class="secondary"><b>Gradle.&nbsp;</b>
</p><pre class="programlisting">classpath <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"com.example:beer-common:0.0.1.BUILD-SNAPSHOT"</span></pre><p class="secondary">
</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_referencing_classes_in_dsls" href="#_referencing_classes_in_dsls"></a>92.1.5&nbsp;Referencing classes in DSLs</h3></div></div></div><p>You can now reference your classes in your DSL, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">package</span> contracts.beer.rest
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ConsumerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> com.example.ProducerUtils
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">import</span> org.springframework.cloud.contract.spec.Contract
Contract.make {
description(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span>Represents a successful scenario of getting a beer
```
given:
client is old enough
when:
he applies <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> a beer
then:
we<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'ll grant him the beer
</span>```
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> request {
method <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'POST'</span>
url <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'/check'</span>
body(
age: $(ConsumerUtils.oldEnough())
)
headers {
contentType(applicationJson())
}
}
response {
status <span class="hl-number">200</span>
body(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"
</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"status"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"${value(ProducerUtils.ok())}"</span>
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">""</span><span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">")
</span> headers {
contentType(applicationJson())
}
}
}</pre></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi_contract-dsl.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_contract.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__using_the_pluggable_architecture.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">91.&nbsp;Contract DSL&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;93.&nbsp;Using the Pluggable Architecture</td></tr></table></div></body></html>

View File

@@ -0,0 +1,72 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>59.&nbsp;Customizations</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"><link rel="prev" href="multi__managing_spans_with_annotations.html" title="58.&nbsp;Managing Spans with Annotations"><link rel="next" href="multi__sending_spans_to_zipkin.html" title="60.&nbsp;Sending Spans to Zipkin"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">59.&nbsp;Customizations</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__managing_spans_with_annotations.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__sending_spans_to_zipkin.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customizations" href="#_customizations"></a>59.&nbsp;Customizations</h2></div></div></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_http" href="#_http"></a>59.1&nbsp;HTTP</h2></div></div></div><p>If a customization of client / server parsing of the HTTP related spans is required,
just register a bean of type <code class="literal">brave.http.HttpClientParser</code> or
<code class="literal">brave.http.HttpServerParser</code>. If client /server sampling is required, just
register a bean of type <code class="literal">brave.http.HttpSampler</code> and name the bean
<code class="literal">sleuthClientSampler</code> for client sampler and <code class="literal">sleuthServerSampler</code> for server sampler.
For your convenience the <code class="literal">@ClientSampler</code> and <code class="literal">@ServerSampler</code>
annotations can be used to inject the proper beans or to
reference the bean names via their static String <code class="literal">NAME</code> fields.</p><p>Check out Brave&#8217;s code to see an example of how to make a path-based sampler
<a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy" target="_top">https://github.com/openzipkin/brave/tree/master/instrumentation/http#sampling-policy</a></p><p>If you want to completely rewrite the <code class="literal">HttpTracing</code> bean you can use the <code class="literal">SkipPatternProvider</code>
interface to retrieve the URL <code class="literal">Pattern</code> for spans that should be not sampled. Below you can see
an example of usage of <code class="literal">SkipPatternProvider</code> inside a server side, <code class="literal">HttpSampler</code>.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Configuration</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<em><span class="hl-annotation" style="color: gray">@Bean(name = ServerSampler.NAME)</span></em>
HttpSampler myHttpSampler(SkipPatternProvider provider) {
Pattern pattern = provider.skipPattern();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">new</span> HttpSampler() {
<em><span class="hl-annotation" style="color: gray">@Override</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> &lt;Req&gt; Boolean trySample(HttpAdapter&lt;Req, ?&gt; adapter, Req request) {
String url = adapter.path(request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">boolean</span> shouldSkip = pattern.matcher(url).matches();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (shouldSkip) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> false;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> null;
}
};
}
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_tracingfilter" href="#_tracingfilter"></a>59.2&nbsp;<code class="literal">TracingFilter</code></h2></div></div></div><p>You can also modify the behavior of the <code class="literal">TracingFilter</code>, which is the component that is responsible for processing the input HTTP request and adding tags basing on the HTTP response.
You can customize the tags or modify the response headers by registering your own instance of the <code class="literal">TracingFilter</code> bean.</p><p>In the following example, we register the <code class="literal">TracingFilter</code> bean, add the <code class="literal">ZIPKIN-TRACE-ID</code> response header containing the current Span&#8217;s trace id, and add a tag with key <code class="literal">custom</code> and a value <code class="literal">tag</code> to the span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Component</span></em>
<em><span class="hl-annotation" style="color: gray">@Order(TraceWebServletAutoConfiguration.TRACING_FILTER_ORDER + 1)</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyFilter <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> GenericFilterBean {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
MyFilter(Tracer tracer) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throws</span> IOException, ServletException {
Span currentSpan = <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer.currentSpan();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">if</span> (currentSpan == null) {
chain.doFilter(request, response);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span>;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// for readability we're returning trace id in a hex form</span>
((HttpServletResponse) response)
.addHeader(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"ZIPKIN-TRACE-ID"</span>,
currentSpan.context().traceIdString());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// we can also add some custom tags</span>
currentSpan.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"custom"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tag"</span>);
chain.doFilter(request, response);
}
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//end::response_headers[]</span></pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_custom_service_name" href="#_custom_service_name"></a>59.3&nbsp;Custom service name</h2></div></div></div><p>By default, Sleuth assumes that, when you send a span to Zipkin, you want the span&#8217;s service name to be equal to the value of the <code class="literal">spring.application.name</code> property.
That is not always the case, though.
There are situations in which you want to explicitly provide a different service name for all spans coming from your application.
To achieve that, you can pass the following property to your application to override that value (the example is for a service named <code class="literal">myService</code>):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.service.name</span>: myService</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_customization_of_reported_spans" href="#_customization_of_reported_spans"></a>59.4&nbsp;Customization of Reported Spans</h2></div></div></div><p>Before reporting spans (for example, to Zipkin) you may want to modify that span in some way.
You can do so by using the <code class="literal">SpanAdjuster</code> interface.</p><p>In Sleuth, we generate spans with a fixed name.
Some users want to modify the name depending on values of tags.
You can implement the <code class="literal">SpanAdjuster</code> interface to alter that name.</p><p>The following example shows how to register two beans that implement <code class="literal">SpanAdjuster</code>:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em> SpanAdjuster adjusterOne() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> span -&gt; span.toBuilder().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"foo"</span>).build();
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em> SpanAdjuster adjusterTwo() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> span -&gt; span.toBuilder().name(span.name() + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">" bar"</span>).build();
}</pre><p>The preceding example results in changing the name of the reported span to <code class="literal">foo bar</code>, just before it gets reported (for example, to Zipkin).</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_host_locator" href="#_host_locator"></a>59.5&nbsp;Host Locator</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>This section is about defining <span class="strong"><strong>host</strong></span> from service discovery.
It is <span class="strong"><strong>NOT</strong></span> about finding Zipkin through service discovery.</p></td></tr></table></div><p>To define the host that corresponds to a particular span, we need to resolve the host name and port.
The default approach is to take these values from server properties.
If those are not set, we try to retrieve the host name from the network interfaces.</p><p>If you have the discovery client enabled and prefer to retrieve the host address from the registered instance in a service registry, you have to set the <code class="literal">spring.zipkin.locator.discovery.enabled</code> property (it is applicable for both HTTP-based and Stream-based span reporting), as follows:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring.zipkin.locator.discovery.enabled</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__managing_spans_with_annotations.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_sleuth.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__sending_spans_to_zipkin.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">58.&nbsp;Managing Spans with Annotations&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;60.&nbsp;Sending Spans to Zipkin</td></tr></table></div></body></html>

View File

@@ -0,0 +1,13 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>45.&nbsp;Customizing the Message Broker</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_bus.html" title="Part&nbsp;VII.&nbsp;Spring Cloud Bus"><link rel="prev" href="multi__service_id_must_be_unique.html" title="44.&nbsp;Service ID Must Be Unique"><link rel="next" href="multi__tracing_bus_events.html" title="46.&nbsp;Tracing Bus Events"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">45.&nbsp;Customizing the Message Broker</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__service_id_must_be_unique.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VII.&nbsp;Spring Cloud Bus</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__tracing_bus_events.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_customizing_the_message_broker" href="#_customizing_the_message_broker"></a>45.&nbsp;Customizing the Message Broker</h2></div></div></div><p>Spring Cloud Bus uses <a class="link" href="https://cloud.spring.io/spring-cloud-stream" target="_top">Spring Cloud Stream</a> to
broadcast the messages. So, to get messages to flow, you need only include the binder
implementation of your choice in the classpath. There are convenient starters for the bus
with AMQP (RabbitMQ) and Kafka (<code class="literal">spring-cloud-starter-bus-[amqp|kafka]</code>). Generally
speaking, Spring Cloud Stream relies on Spring Boot autoconfiguration conventions for
configuring middleware. For instance, the AMQP broker address can be changed with
<code class="literal">spring.rabbitmq.*</code> configuration properties. Spring Cloud Bus has a handful of
native configuration properties in <code class="literal">spring.cloud.bus.*</code> (for example,
<code class="literal">spring.cloud.bus.destination</code> is the name of the topic to use as the external
middleware). Normally, the defaults suffice.</p><p>To learn more about how to customize the message broker settings, consult the Spring Cloud
Stream documentation.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__service_id_must_be_unique.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_bus.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__tracing_bus_events.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">44.&nbsp;Service ID Must Be Unique&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;46.&nbsp;Tracing Bus Events</td></tr></table></div></body></html>

View File

@@ -0,0 +1,3 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>125.&nbsp;Deploying a Packaged Function</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_function.html" title="Part&nbsp;XVI.&nbsp;Spring Cloud Function"><link rel="prev" href="multi__standalone_streaming_applications.html" title="124.&nbsp;Standalone Streaming Applications"><link rel="next" href="multi__dynamic_compilation.html" title="126.&nbsp;Dynamic Compilation"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">125.&nbsp;Deploying a Packaged Function</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__standalone_streaming_applications.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XVI.&nbsp;Spring Cloud Function</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__dynamic_compilation.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_deploying_a_packaged_function" href="#_deploying_a_packaged_function"></a>125.&nbsp;Deploying a Packaged Function</h2></div></div></div><p>Spring Cloud Function provides a "deployer" library that allows you to launch a jar file (or exploded archive, or set of jar files) with an isolated class loader and expose the functions defined in it. This is quite a powerful tool that would allow you to, for instance, adapt a function to a range of different input-output adapters without changing the target jar file. Serverless platforms often have this kind of feature built in, so you could see it as a building block for a function invoker in such a platform (indeed the <a class="link" href="https://projectriff.io" target="_top">Riff</a> Java function invoker uses this library).</p><p>The standard entry point of the API is the Spring configuration annotation <code class="literal">@EnableFunctionDeployer</code>. If that is used in a Spring Boot application the deployer kicks in and looks for some configuration to tell it where to find the function jar. At a minimum the user has to provide a <code class="literal">function.location</code> which is a URL or resource location for the archive containing the functions. It can optionally use a <code class="literal">maven:</code> prefix to locate the artifact via a dependency lookup (see <code class="literal">FunctionProperties</code> for complete details). A Spring Boot application is bootstrapped from the jar file, using the <code class="literal">MANIFEST.MF</code> to locate a start class, so that a standard Spring Boot fat jar works well, for example. If the target jar can be launched successfully then the result is a function registered in the main application&#8217;s <code class="literal">FunctionCatalog</code>. The registered function can be applied by code in the main application, even though it was created in an isolated class loader (by deault).</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__standalone_streaming_applications.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_function.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__dynamic_compilation.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">124.&nbsp;Standalone Streaming Applications&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;126.&nbsp;Dynamic Compilation</td></tr></table></div></body></html>

View File

@@ -0,0 +1,74 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>117.&nbsp;Developer Guide</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_gateway.html" title="Part&nbsp;XV.&nbsp;Spring Cloud Gateway"><link rel="prev" href="multi__actuator_api.html" title="116.&nbsp;Actuator API"><link rel="next" href="multi__building_a_simple_gateway_using_spring_mvc_or_webflux.html" title="118.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">117.&nbsp;Developer Guide</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__actuator_api.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XV.&nbsp;Spring Cloud Gateway</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__building_a_simple_gateway_using_spring_mvc_or_webflux.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_developer_guide" href="#_developer_guide"></a>117.&nbsp;Developer Guide</h2></div></div></div><p>TODO: overview of writing custom integrations</p><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_route_predicate_factories" href="#_writing_custom_route_predicate_factories"></a>117.1&nbsp;Writing Custom Route Predicate Factories</h2></div></div></div><p>TODO: document writing Custom Route Predicate Factories</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_gatewayfilter_factories" href="#_writing_custom_gatewayfilter_factories"></a>117.2&nbsp;Writing Custom GatewayFilter Factories</h2></div></div></div><p>In order to write a GatewayFilter you will need to implement <code class="literal">GatewayFilterFactory</code>. There is an abstract class called <code class="literal">AbstractGatewayFilterFactory</code> which you can extend.</p><p><b>PreGatewayFilterFactory.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PreGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory&lt;PreGatewayFilterFactory.Config&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PreGatewayFilterFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//If you want to build a "pre" filter you need to manipulate the</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//request before calling change.filter</span>
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//use builder to manipulate the request</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange.mutate().request(request).build());
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
}
}</pre><p>
</p><p><b>PostGatewayFilterFactory.java.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> PostGatewayFilterFactory <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">extends</span> AbstractGatewayFilterFactory&lt;PostGatewayFilterFactory.Config&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> PostGatewayFilterFactory() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">super</span>(Config.<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span>);
}
<em><span class="hl-annotation" style="color: gray">@Override</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GatewayFilter apply(Config config) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// grab configuration from Config object</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> chain.filter(exchange).then(Mono.fromRunnable(() -&gt; {
ServerHttpResponse response = exchange.getResponse();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Manipulate the response in some way</span>
}));
};
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">static</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Config {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//Put the configuration properties for your filter here</span>
}
}</pre><p>
</p></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_global_filters" href="#_writing_custom_global_filters"></a>117.3&nbsp;Writing Custom Global Filters</h2></div></div></div><p>In order to write a custom global filter, you will need to implement <code class="literal">GlobalFilter</code> interface. This will apply the filter to all requests.</p><p>Example of how to set up a Global Pre and Post filter, respectively</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter customGlobalFilter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; exchange.getPrincipal()
.map(Principal::getName)
.defaultIfEmpty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"Default User"</span>)
.map(userName -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//adds header to proxied request</span>
exchange.getRequest().mutate().header(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOM-REQUEST-HEADER"</span>, userName).build();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> exchange;
})
.flatMap(chain::filter);
}
<em><span class="hl-annotation" style="color: gray">@Bean</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">public</span> GlobalFilter customGlobalPostFilter() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> (exchange, chain) -&gt; chain.filter(exchange)
.then(Mono.just(exchange))
.map(serverWebExchange -&gt; {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">//adds header to response</span>
serverWebExchange.getResponse().getHeaders().set(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"CUSTOM-RESPONSE-HEADER"</span>,
HttpStatus.OK.equals(serverWebExchange.getResponse().getStatusCode()) ? <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"It worked"</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"It did not work"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> serverWebExchange;
})
.then();
}</pre></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_writing_custom_route_locators_and_writers" href="#_writing_custom_route_locators_and_writers"></a>117.4&nbsp;Writing Custom Route Locators and Writers</h2></div></div></div><p>TODO: document writing Custom Route Locators and Writers</p></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__actuator_api.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_gateway.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__building_a_simple_gateway_using_spring_mvc_or_webflux.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">116.&nbsp;Actuator API&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;118.&nbsp;Building a Simple Gateway Using Spring MVC or Webflux</td></tr></table></div></body></html>

View File

@@ -0,0 +1,22 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>82.&nbsp;Discovery</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_for_cloud_foundry.html" title="Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry"><link rel="prev" href="multi__spring_cloud_for_cloud_foundry.html" title="Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry"><link rel="next" href="multi__single_sign_on_2.html" title="83.&nbsp;Single Sign On"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">82.&nbsp;Discovery</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__spring_cloud_for_cloud_foundry.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__single_sign_on_2.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_discovery" href="#_discovery"></a>82.&nbsp;Discovery</h2></div></div></div><p>Here&#8217;s a Spring Cloud app with Cloud Foundry discovery:</p><p><b>app.groovy.&nbsp;</b>
</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Grab('org.springframework.cloud:spring-cloud-cloudfoundry')</span></em>
<em><span class="hl-annotation" style="color: gray">@RestController</span></em>
<em><span class="hl-annotation" style="color: gray">@EnableDiscoveryClient</span></em>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> Application {
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em>
DiscoveryClient client
<em><span class="hl-annotation" style="color: gray">@RequestMapping('/')</span></em>
String home() {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">'Hello from '</span> + client.getLocalServiceInstance()
}
}</pre><p>
</p><p>If you run it without any service bindings:</p><pre class="screen">$ spring jar app.jar app.groovy
$ cf push -p app.jar</pre><p>It will show its app name in the home page.</p><p>The <code class="literal">DiscoveryClient</code> can lists all the apps in a space, according to
the credentials it is authenticated with, where the space defaults to
the one the client is running in (if any). If neither org nor space
are configured, they default per the user&#8217;s profile in Cloud Foundry.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__spring_cloud_for_cloud_foundry.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_for_cloud_foundry.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__single_sign_on_2.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">Part&nbsp;XII.&nbsp;Spring Cloud for Cloud Foundry&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;83.&nbsp;Single Sign On</td></tr></table></div></body></html>

View File

@@ -0,0 +1,23 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>126.&nbsp;Dynamic Compilation</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_function.html" title="Part&nbsp;XVI.&nbsp;Spring Cloud Function"><link rel="prev" href="multi__deploying_a_packaged_function.html" title="125.&nbsp;Deploying a Packaged Function"><link rel="next" href="multi__serverless_platform_adapters.html" title="127.&nbsp;Serverless Platform Adapters"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">126.&nbsp;Dynamic Compilation</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__deploying_a_packaged_function.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XVI.&nbsp;Spring Cloud Function</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__serverless_platform_adapters.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_dynamic_compilation" href="#_dynamic_compilation"></a>126.&nbsp;Dynamic Compilation</h2></div></div></div><p>There is a sample app that uses the function compiler to create a
function from a configuration property. The vanilla "function-sample"
also has that feature. And there are some scripts that you can run to
see the compilation happening at run time. To run these examples,
change into the <code class="literal">scripts</code> directory:</p><pre class="screen">cd scripts</pre><p>Also, start a RabbitMQ server locally (e.g. execute <code class="literal">rabbitmq-server</code>).</p><p>Start the Function Registry Service:</p><pre class="screen">./function-registry.sh</pre><p>Register a Function:</p><pre class="screen">./registerFunction.sh -n uppercase -f "f-&gt;f.map(s-&gt;s.toString().toUpperCase())"</pre><p>Run a REST Microservice using that Function:</p><pre class="screen">./web.sh -f uppercase -p 9000
curl -H "Content-Type: text/plain" -H "Accept: text/plain" localhost:9000/uppercase -d foo</pre><p>Register a Supplier:</p><pre class="screen">./registerSupplier.sh -n words -f "()-&gt;Flux.just(\"foo\",\"bar\")"</pre><p>Run a REST Microservice using that Supplier:</p><pre class="screen">./web.sh -s words -p 9001
curl -H "Accept: application/json" localhost:9001/words</pre><p>Register a Consumer:</p><pre class="screen">./registerConsumer.sh -n print -t String -f "System.out::println"</pre><p>Run a REST Microservice using that Consumer:</p><pre class="screen">./web.sh -c print -p 9002
curl -X POST -H "Content-Type: text/plain" -d foo localhost:9002/print</pre><p>Run Stream Processing Microservices:</p><p>First register a streaming words supplier:</p><pre class="screen">./registerSupplier.sh -n wordstream -f "()-&gt;Flux.interval(Duration.ofMillis(1000)).map(i-&gt;\"message-\"+i)"</pre><p>Then start the source (supplier), processor (function), and sink (consumer) apps
(in reverse order):</p><pre class="screen">./stream.sh -p 9103 -i uppercaseWords -c print
./stream.sh -p 9102 -i words -f uppercase -o uppercaseWords
./stream.sh -p 9101 -s wordstream -o words</pre><p>The output will appear in the console of the sink app (one message per second, converted to uppercase):</p><pre class="screen">MESSAGE-0
MESSAGE-1
MESSAGE-2
MESSAGE-3
MESSAGE-4
MESSAGE-5
MESSAGE-6
MESSAGE-7
MESSAGE-8
MESSAGE-9
...</pre></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__deploying_a_packaged_function.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_function.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__serverless_platform_adapters.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">125.&nbsp;Deploying a Packaged Function&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;127.&nbsp;Serverless Platform Adapters</td></tr></table></div></body></html>

View File

@@ -0,0 +1,26 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>8.&nbsp;Embedding the Config Server</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_config.html" title="Part&nbsp;II.&nbsp;Spring Cloud Config"><link rel="prev" href="multi__serving_plain_text.html" title="7.&nbsp;Serving Plain Text"><link rel="next" href="multi__push_notifications_and_spring_cloud_bus.html" title="9.&nbsp;Push Notifications and Spring Cloud Bus"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">8.&nbsp;Embedding the Config Server</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__serving_plain_text.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;II.&nbsp;Spring Cloud Config</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__push_notifications_and_spring_cloud_bus.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_embedding_the_config_server" href="#_embedding_the_config_server"></a>8.&nbsp;Embedding the Config Server</h2></div></div></div><p>The Config Server runs best as a standalone application.
However, if need be, you can embed it in another application.
To do so, use the <code class="literal">@EnableConfigServer</code> annotation.
An optional property named <code class="literal">spring.cloud.config.server.bootstrap</code> can be useful in this case is.
It is a flag to indicate whether the server should configure itself from its own remote repository.
By default, the flag is off, because it can delay startup.
However, when embedded in another application, it makes sense to initialize the same way as any other application.
When setting <code class="literal">spring.cloud.config.server.bootstrap</code> to <code class="literal">true</code> you must also use a <a class="link" href="multi__spring_cloud_config_server.html#composite-environment-repositories" title="5.1.8&nbsp;Composite Environment Repositories">composite environment repository configuration</a>.
For example</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute">spring</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> application</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> name</span>: configserver
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> profiles</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> active</span>: composite
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> cloud</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> config</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> server</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> composite</span>:
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> - type</span>: native
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> search-locations</span>: ${HOME}/Desktop/config
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-attribute"> bootstrap</span>: <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">true</span></pre><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>If you use the bootstrap flag, the config server needs to have its name and repository URI configured in <code class="literal">bootstrap.yml</code>.</p></td></tr></table></div><p>To change the location of the server endpoints, you can (optionally) set <code class="literal">spring.cloud.config.server.prefix</code> (for example, <code class="literal">/config</code>), to serve the resources under a prefix.
The prefix should start but not end with a <code class="literal">/</code>.
It is applied to the <code class="literal">@RequestMappings</code> in the Config Server (that is, underneath the Spring Boot <code class="literal">server.servletPath</code> and <code class="literal">server.contextPath</code> prefixes).</p><p>If you want to read the configuration for an application directly from the backend repository (instead of from the config server), you
basically wat an embedded config server with no endpoints.
You can switch off the endpoints entirely by not using the <code class="literal">@EnableConfigServer</code> annotation (set <code class="literal">spring.cloud.config.server.bootstrap=true</code>).</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__serving_plain_text.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_config.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__push_notifications_and_spring_cloud_bus.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">7.&nbsp;Serving Plain Text&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;9.&nbsp;Push Notifications and Spring Cloud Bus</td></tr></table></div></body></html>

View File

@@ -0,0 +1,20 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>17.&nbsp;External Configuration: Archaius</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_netflix.html" title="Part&nbsp;III.&nbsp;Spring Cloud Netflix"><link rel="prev" href="multi_spring-cloud-ribbon.html" title="16.&nbsp;Client Side Load Balancer: Ribbon"><link rel="next" href="multi__router_and_filter_zuul.html" title="18.&nbsp;Router and Filter: Zuul"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">17.&nbsp;External Configuration: Archaius</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi_spring-cloud-ribbon.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;III.&nbsp;Spring Cloud Netflix</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__router_and_filter_zuul.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_external_configuration_archaius" href="#_external_configuration_archaius"></a>17.&nbsp;External Configuration: Archaius</h2></div></div></div><p><a class="link" href="https://github.com/Netflix/archaius" target="_top">Archaius</a> is the Netflix client-side configuration library.
It is the library used by all of the Netflix OSS components for configuration.
Archaius is an extension of the <a class="link" href="https://commons.apache.org/proper/commons-configuration" target="_top">Apache Commons Configuration</a> project.
It allows updates to configuration by either polling a source for changes or by letting a source push changes to the client.
Archaius uses Dynamic&lt;Type&gt;Property classes as handles to properties, as shown in the following example:</p><p><b>Archaius Example.&nbsp;</b>
</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> ArchaiusTest {
DynamicStringProperty myprop = DynamicPropertyFactory
.getInstance()
.getStringProperty(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"my.prop"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSomething() {
OtherClass.someMethod(myprop.get());
}
}</pre><p>
</p><p>Archaius has its own set of configuration files and loading priorities.
Spring applications should generally not use Archaius directly, but the need to configure the Netflix tools natively remains.
Spring Cloud has a Spring Environment Bridge so that Archaius can read properties from the Spring Environment.
This bridge allows Spring Boot projects to use the normal configuration toolchain while letting them configure the Netflix tools as documented (for the most part).</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi_spring-cloud-ribbon.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_netflix.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__router_and_filter_zuul.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">16.&nbsp;Client Side Load Balancer: Ribbon&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;18.&nbsp;Router and Filter: Zuul</td></tr></table></div></body></html>

View File

@@ -0,0 +1,4 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>1.&nbsp;Features</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="prev" href="multi_pr01.html" title=""><link rel="next" href="multi__cloud_native_applications.html" title="Part&nbsp;I.&nbsp;Cloud Native Applications"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">1.&nbsp;Features</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi_pr01.html">Prev</a>&nbsp;</td><th width="60%" align="center">&nbsp;</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__cloud_native_applications.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h1 class="title"><a name="_features" href="#_features"></a>1.&nbsp;Features</h1></div></div></div><p>Spring Cloud focuses on providing good out of box experience for typical use cases
and extensibility mechanism to cover others.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Distributed/versioned configuration</li><li class="listitem">Service registration and discovery</li><li class="listitem">Routing</li><li class="listitem">Service-to-service calls</li><li class="listitem">Load balancing</li><li class="listitem">Circuit Breakers</li><li class="listitem">Distributed messaging</li></ul></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi_pr01.html">Prev</a>&nbsp;</td><td width="20%" align="center">&nbsp;</td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__cloud_native_applications.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;Part&nbsp;I.&nbsp;Cloud Native Applications</td></tr></table></div></body></html>

View File

@@ -0,0 +1,139 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>50.&nbsp;Features</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_sleuth.html" title="Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth"><link rel="prev" href="multi__additional_resources.html" title="49.&nbsp;Additional Resources"><link rel="next" href="multi__sampling.html" title="51.&nbsp;Sampling"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">50.&nbsp;Features</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__additional_resources.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;VIII.&nbsp;Spring Cloud Sleuth</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__sampling.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_features_2" href="#_features_2"></a>50.&nbsp;Features</h2></div></div></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem"><p class="simpara">Adds trace and span IDs to the Slf4J MDC, so you can extract all the logs from a given trace or span in a log aggregator, as shown in the following example logs:</p><pre class="screen">2016-02-02 15:30:57.902 INFO [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
2016-02-02 15:30:58.372 ERROR [bar,6bfd228dc00d216b,6bfd228dc00d216b,false] 23030 --- [nio-8081-exec-3] ...
2016-02-02 15:31:01.936 INFO [bar,46ab0d418373cbc9,46ab0d418373cbc9,false] 23030 --- [nio-8081-exec-4] ...</pre><p class="simpara">Notice the <code class="literal">[appname,traceId,spanId,exportable]</code> entries from the MDC:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem"><span class="strong"><strong><code class="literal">spanId</code></strong></span>: The ID of a specific operation that took place.</li><li class="listitem"><span class="strong"><strong><code class="literal">appname</code></strong></span>: The name of the application that logged the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">traceId</code></strong></span>: The ID of the latency graph that contains the span.</li><li class="listitem"><span class="strong"><strong><code class="literal">exportable</code></strong></span>: Whether the log should be exported to Zipkin.
When would you like the span not to be exportable?
When you want to wrap some operation in a Span and have it written to the logs only.</li></ul></div></li><li class="listitem">Provides an abstraction over common distributed tracing data models: traces, spans (forming a DAG), annotations, and key-value annotations.
Spring Cloud Sleuth is loosely based on HTrace but is compatible with Zipkin (Dapper).</li><li class="listitem">Sleuth records timing information to aid in latency analysis.
By using sleuth, you can pinpoint causes of latency in your applications.</li><li class="listitem"><p class="simpara">Sleuth is written to not log too much and to not cause your production application to crash.
To that end, Sleuth:</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">Propagates structural data about your call graph in-band and the rest out-of-band.</li><li class="listitem">Includes opinionated instrumentation of layers such as HTTP.</li><li class="listitem">Includes a sampling policy to manage volume.</li><li class="listitem">Can report to a Zipkin system for query and visualization.</li></ul></div></li><li class="listitem">Instruments common ingress and egress points from Spring applications (servlet filter, async endpoints, rest template, scheduled actions, message channels, Zuul filters, and Feign client).</li><li class="listitem">Sleuth includes default logic to join a trace across HTTP or messaging boundaries.
For example, HTTP propagation works over Zipkin-compatible request headers.</li><li class="listitem">Sleuth can propagate context (also known as baggage) between processes.
Consequently, if you set a baggage element on a Span, it is sent downstream to other processes over either HTTP or messaging.</li><li class="listitem">Provides a way to create or continue spans and add tags and logs through annotations.</li><li class="listitem"><p class="simpara">If <code class="literal">spring-cloud-sleuth-zipkin</code> is on the classpath, the app generates and collects Zipkin-compatible traces.
By default, it sends them over HTTP to a Zipkin server on localhost (port 9411).
You can configure the location of the service by setting <code class="literal">spring.zipkin.baseUrl</code>.</p><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: circle; "><li class="listitem">If you depend on <code class="literal">spring-rabbit</code>, your app sends traces to a RabbitMQ broker instead of HTTP.</li><li class="listitem">If you depend on <code class="literal">spring-kafka</code>, and set <code class="literal">spring.zipkin.sender.type: kafka</code>, your app sends traces to a Kafka broker instead of HTTP.</li></ul></div></li></ul></div><div class="caution" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Caution"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Caution]" src="images/caution.png"></td><th align="left">Caution</th></tr><tr><td align="left" valign="top"><p><code class="literal">spring-cloud-sleuth-stream</code> is deprecated and should no longer be used.</p></td></tr></table></div><div class="itemizedlist"><ul class="itemizedlist" style="list-style-type: disc; "><li class="listitem">Spring Cloud Sleuth is <a class="link" href="https://opentracing.io/" target="_top">OpenTracing</a> compatible.</li></ul></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If you use Zipkin, configure the probability of spans exported by setting <code class="literal">spring.sleuth.sampler.probability</code>
(default: 0.1, which is 10 percent). Otherwise, you might think that Sleuth is not working be cause it omits some spans.</p></td></tr></table></div><div class="note" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Note"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Note]" src="images/note.png"></td><th align="left">Note</th></tr><tr><td align="left" valign="top"><p>The SLF4J MDC is always set and logback users immediately see the trace and span IDs in logs per the example
shown earlier.
Other logging systems have to configure their own formatter to get the same result.
The default is as follows:
<code class="literal">logging.pattern.level</code> set to <code class="literal">%5p [${spring.zipkin.service.name:${spring.application.name:-}},%X{X-B3-TraceId:-},%X{X-B3-SpanId:-},%X{X-Span-Export:-}]</code>
(this is a Spring Boot feature for logback users).
If you do not use SLF4J, this pattern is NOT automatically applied.</p></td></tr></table></div><div class="section"><div class="titlepage"><div><div><h2 class="title" style="clear: both"><a name="_introduction_to_brave" href="#_introduction_to_brave"></a>50.1&nbsp;Introduction to Brave</h2></div></div></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>Starting with version <code class="literal">2.0.0</code>, Spring Cloud Sleuth uses
<a class="link" href="https://github.com/openzipkin/brave" target="_top">Brave</a> as the tracing library.
For your convenience, we embed part of the Brave&#8217;s docs here.</p></td></tr></table></div><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>In the vast majority of cases you need to just use the <code class="literal">Tracer</code>
or <code class="literal">SpanCustomizer</code> beans from Brave that Sleuth provides. The documentation below contains
a high overview of what Brave is and how it works.</p></td></tr></table></div><p>Brave is a library used to capture and report latency information about distributed operations to Zipkin.
Most users do not use Brave directly. They use libraries or frameworks rather than employ Brave on their behalf.</p><p>This module includes a tracer that creates and joins spans that model the latency of potentially distributed work.
It also includes libraries to propagate the trace context over network boundaries (for example, with HTTP headers).</p><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_tracing" href="#_tracing"></a>50.1.1&nbsp;Tracing</h3></div></div></div><p>Most importantly, you need a <code class="literal">brave.Tracer</code>, configured to <a class="link" href="https://github.com/openzipkin/zipkin-reporter-java" target="_top">report to Zipkin</a>.</p><p>The following example setup sends trace data (spans) to Zipkin over HTTP (as opposed to Kafka):</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">class</span> MyClass {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">private</span> <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">final</span> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Tracer will be autowired</span>
MyClass(Tracer tracer) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">this</span>.tracer = tracer;
}
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> doSth() {
Span span = tracer.newTrace().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// ...</span>
}
}</pre><div class="important" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Important"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Important]" src="images/important.png"></td><th align="left">Important</th></tr><tr><td align="left" valign="top"><p>If your span contains a name longer than 50 chars, then that name is truncated to 50 chars.
Your names have to be explicit and concrete.
Big names lead to latency issues and sometimes even thrown exceptions.</p></td></tr></table></div><p>The tracer creates and joins spans that model the latency of potentially distributed work.
It can employ sampling to reduce overhead during the process, to reduce the amount of data sent to Zipkin, or both.</p><p>Spans returned by a tracer report data to Zipkin when finished or do nothing if unsampled.
After starting a span, you can annotate events of interest or add tags containing details or lookup keys.</p><p>Spans have a context that includes trace identifiers that place the span at the correct spot in the tree representing the distributed operation.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_local_tracing" href="#_local_tracing"></a>50.1.2&nbsp;Local Tracing</h3></div></div></div><p>When tracing code that never leaves your process, run it inside a scoped span.</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
ScopedSpan span = tracer.startScopedSpan(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The span is in "scope" meaning downstream code such as loggers can see trace IDs</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// always finish the span</span>
}</pre><p>When you need more features, or finer control, use the <code class="literal">Span</code> type:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Start a new trace or a span within an existing trace representing an operation</span>
Span span = tracer.nextSpan().name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"encode"</span>).start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Put the span in "scope" so that downstream code such as loggers can see trace IDs</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">try</span> (SpanInScope ws = tracer.withSpanInScope(span)) {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">return</span> encoder.encode();
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">catch</span> (RuntimeException | Error e) {
span.error(e); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Unless you handle exceptions, you might not know the operation failed!</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">throw</span> e;
} <span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">finally</span> {
span.finish(); <span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// note the scope is independent of the span. Always finish a span.</span>
}</pre><p>Both of the above examples report the exact same span on finish!</p><p>In the above example, the span will be either a new root span or the
next child in an existing trace.</p></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_customizing_spans" href="#_customizing_spans"></a>50.1.3&nbsp;Customizing Spans</h3></div></div></div><p>Once you have a span, you can add tags to it.
The tags can be used as lookup keys or details.
For example, you might add a tag with your runtime version, as shown in the following example:</p><pre class="programlisting">span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"clnt/finagle.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"6.36.0"</span>);</pre><p>When exposing the ability to customize spans to third parties, prefer <code class="literal">brave.SpanCustomizer</code> as opposed to <code class="literal">brave.Span</code>.
The former is simpler to understand and test and does not tempt users with span lifecycle hooks.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">interface</span> MyTraceCallback {
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> request(Request request, SpanCustomizer customizer);
}</pre><p>Since <code class="literal">brave.Span</code> implements <code class="literal">brave.SpanCustomizer</code>, you can pass it to users, as shown in the following example:</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">for</span> (MyTraceCallback callback : userCallbacks) {
callback.request(request, span);
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_implicitly_looking_up_the_current_span" href="#_implicitly_looking_up_the_current_span"></a>50.1.4&nbsp;Implicitly Looking up the Current Span</h3></div></div></div><p>Sometimes, you do not know if a trace is in progress or not, and you do not want users to do null checks.
<code class="literal">brave.CurrentSpanCustomizer</code> handles this problem by adding data to any span that&#8217;s in progress or drops, as shown in the following example:</p><p>Ex.</p><pre class="programlisting"><span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// The user code can then inject this without a chance of it being null.</span>
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> SpanCustomizer span;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-keyword">void</span> userCode() {
span.annotate(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"tx.started"</span>);
...
}</pre></div><div class="section"><div class="titlepage"><div><div><h3 class="title"><a name="_rpc_tracing" href="#_rpc_tracing"></a>50.1.5&nbsp;RPC tracing</h3></div></div></div><div class="tip" style="margin-left: 0.5in; margin-right: 0.5in;"><table border="0" summary="Tip"><tr><td rowspan="2" align="center" valign="top" width="25"><img alt="[Tip]" src="images/tip.png"></td><th align="left">Tip</th></tr><tr><td align="left" valign="top"><p>Check for <a class="link" href="https://github.com/openzipkin/brave/tree/master/instrumentation" target="_top">instrumentation written here</a> and <a class="link" href="https://zipkin.io/pages/existing_instrumentations.html" target="_top">Zipkin&#8217;s list</a> before rolling your own RPC instrumentation.</p></td></tr></table></div><p>RPC tracing is often done automatically by interceptors. Behind the scenes, they add tags and events that relate to their role in an RPC operation.</p><p>The following example shows how to add a client span:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// before you send a request, add metadata that describes the operation</span>
span = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"myrpc.version"</span>, <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"1.0.0"</span>);
span.remoteServiceName(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"backend"</span>);
span.remoteIpAndPort(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"172.3.4.1"</span>, <span class="hl-number">8108</span>);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
tracing.propagation().injector(Request::addHeader)
.inject(span.context(), request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the request is scheduled, start the span</span>
span.start();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// if there is an error, tag the span</span>
span.tag(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"error"</span>, error.getCode());
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// or if there is an exception</span>
span.error(exception);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// when the response is complete, finish the span</span>
span.finish();</pre><div class="section"><div class="titlepage"><div><div><h4 class="title"><a name="_one_way_tracing" href="#_one_way_tracing"></a>One-Way tracing</h4></div></div></div><p>Sometimes, you need to model an asynchronous operation where there is a
request but no response. In normal RPC tracing, you use <code class="literal">span.finish()</code>
to indicate that the response was received. In one-way tracing, you use
<code class="literal">span.flush()</code> instead, as you do not expect a response.</p><p>The following example shows how a client might model a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start a new span representing a client request</span>
oneWaySend = tracer.nextSpan().name(service + <span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"/"</span> + method).kind(CLIENT);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// Add the trace context to the request, so it can be propagated in-band</span>
tracing.propagation().injector(Request::addHeader)
.inject(oneWaySend.context(), request);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// fire off the request asynchronously, totally dropping any response</span>
request.execute();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the client side and flush instead of finish</span>
oneWaySend.start().flush();</pre><p>The following example shows how a server might handle a one-way operation:</p><pre class="programlisting"><em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracing tracing;
<em><span class="hl-annotation" style="color: gray">@Autowired</span></em> Tracer tracer;
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// pull the context out of the incoming request</span>
extractor = tracing.propagation().extractor(Request::getHeader);
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// convert that context to a span which you can name and add tags to</span>
oneWayReceive = nextSpan(tracer, extractor.extract(request))
.name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"process-request"</span>)
.kind(SERVER)
... add tags etc.
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// start the server side and flush instead of finish</span>
oneWayReceive.start().flush();
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you should not modify this span anymore as it is complete. However,</span>
<span xmlns:d="http://docbook.org/ns/docbook" class="hl-comment">// you can create children to represent follow-up work.</span>
next = tracer.newSpan(oneWayReceive.context()).name(<span xmlns:d="http://docbook.org/ns/docbook" class="hl-string">"step2"</span>).start();</pre></div></div></div></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__additional_resources.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_sleuth.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__sampling.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">49.&nbsp;Additional Resources&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;51.&nbsp;Sampling</td></tr></table></div></body></html>

View File

@@ -0,0 +1,26 @@
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>122.&nbsp;Function Catalog and Flexible Function Signatures</title><link rel="stylesheet" type="text/css" href="css/manual-multipage.css"><meta name="generator" content="DocBook XSL Stylesheets V1.79.1"><link rel="home" href="multi_spring-cloud.html" title="Spring Cloud"><link rel="up" href="multi__spring_cloud_function.html" title="Part&nbsp;XVI.&nbsp;Spring Cloud Function"><link rel="prev" href="multi__building_and_running_a_function.html" title="121.&nbsp;Building and Running a Function"><link rel="next" href="multi__standalone_web_applications.html" title="123.&nbsp;Standalone Web Applications"></head><body bgcolor="white" text="black" link="#0000FF" vlink="#840084" alink="#0000FF"><div class="navheader"><table width="100%" summary="Navigation header"><tr><th colspan="3" align="center">122.&nbsp;Function Catalog and Flexible Function Signatures</th></tr><tr><td width="20%" align="left"><a accesskey="p" href="multi__building_and_running_a_function.html">Prev</a>&nbsp;</td><th width="60%" align="center">Part&nbsp;XVI.&nbsp;Spring Cloud Function</th><td width="20%" align="right">&nbsp;<a accesskey="n" href="multi__standalone_web_applications.html">Next</a></td></tr></table><hr></div><div class="chapter"><div class="titlepage"><div><div><h2 class="title"><a name="_function_catalog_and_flexible_function_signatures" href="#_function_catalog_and_flexible_function_signatures"></a>122.&nbsp;Function Catalog and Flexible Function Signatures</h2></div></div></div><p>One of the main features of Spring Cloud Function is to adapt and
support a range of type signatures for user-defined functions. So
users can supply a bean of type <code class="literal">Function&lt;String,String&gt;</code>, for
instance, and the <code class="literal">FunctionCatalog</code> will wrap it into a
<code class="literal">Function&lt;Flux&lt;String&gt;,Flux&lt;String&gt;&gt;</code>. Users don&#8217;t normally have to
care about the <code class="literal">FunctionCatalog</code> at all, but it is useful to know what
kind of functions are supported in user code.</p><p>Generally speaking users can expect that if they write a function for
a plain old Java type (or primitive wrapper), then the function
catalog will wrap it to a <code class="literal">Flux</code> of the same type. If the user writes
a function using <code class="literal">Message</code> (from spring-messaging) it will receive and
transmit headers from any adapter that supports key-value metadata
(e.g. HTTP headers). Here are the details.</p><div class="informaltable"><table class="informaltable" style="border-collapse: collapse;border-top: 1px solid ; border-bottom: 1px solid ; "><colgroup><col class="col_1"><col class="col_2"><col class="col_3"></colgroup><thead><tr><th style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top">User Function</th><th style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top">Catalog Registration</th><th style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</th></tr></thead><tbody><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;S,T&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Message&lt;S&gt;,Message&lt;T&gt;&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;Message&lt;S&gt;&gt;, Flux&lt;Message&lt;T&gt;&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;S&gt;, Flux&lt;T&gt;&gt;</code> (pass through)</p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;T&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Supplier&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;T&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;T&gt;, Mono&lt;Void&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Message&lt;T&gt;&gt;</code></p></td><td style="border-right: 1px solid ; border-bottom: 1px solid ; " align="left" valign="top"><p><code class="literal">Function&lt;Flux&lt;Message&lt;T&gt;&gt;, Mono&lt;Void&gt;&gt;</code></p></td><td style="border-bottom: 1px solid ; " align="left" valign="top">&nbsp;</td></tr><tr><td style="border-right: 1px solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="border-right: 1px solid ; " align="left" valign="top"><p><code class="literal">Consumer&lt;Flux&lt;T&gt;&gt;</code></p></td><td style="" align="left" valign="top">&nbsp;</td></tr></tbody></table></div><p>Consumer is a little bit special because it has a <code class="literal">void</code> return type,
which implies blocking, at least potentially. Most likely you will not
need to write <code class="literal">Consumer&lt;Flux&lt;?&gt;&gt;</code>, but if you do need to do that,
remember to subscribe to the input flux. If you declare a <code class="literal">Consumer</code>
of a non publisher type (which is normal), it will be converted to a
function that returns a publisher, so that it can be subscribed to in
a controlled way.</p><p>A function catalog can contain a <code class="literal">Supplier</code> and a <code class="literal">Function</code> (or
<code class="literal">Consumer</code>) with the same name (like a GET and a POST to the same
resource). It can even contain a <code class="literal">Consumer&lt;Flux&lt;&gt;&gt;</code> with the same name
as a <code class="literal">Function</code>, but it cannot contain a <code class="literal">Consumer&lt;T&gt;</code> and a
<code class="literal">Function&lt;T,S&gt;</code> with the same name when <code class="literal">T</code> is not a <code class="literal">Publisher</code>
because the consumer would be converted to a <code class="literal">Function</code> and only one
of them can be registered.</p></div><div class="navfooter"><hr><table width="100%" summary="Navigation footer"><tr><td width="40%" align="left"><a accesskey="p" href="multi__building_and_running_a_function.html">Prev</a>&nbsp;</td><td width="20%" align="center"><a accesskey="u" href="multi__spring_cloud_function.html">Up</a></td><td width="40%" align="right">&nbsp;<a accesskey="n" href="multi__standalone_web_applications.html">Next</a></td></tr><tr><td width="40%" align="left" valign="top">121.&nbsp;Building and Running a Function&nbsp;</td><td width="20%" align="center"><a accesskey="h" href="multi_spring-cloud.html">Home</a></td><td width="40%" align="right" valign="top">&nbsp;123.&nbsp;Standalone Web Applications</td></tr></table></div></body></html>

Some files were not shown because too many files have changed in this diff Show More