Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
S
spring-cloud-netflix
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
openSource
spring-cloud-netflix
Commits
6e2238f2
Commit
6e2238f2
authored
Dec 09, 2017
by
Fahim Farook
Committed by
Ryan Baxter
Dec 08, 2017
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Update Eureka Server's service-urls upon EnvironmentChangeEvent. (#2455)
Fixes gh-2421
parent
f640ffd2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
325 additions
and
2 deletions
+325
-2
EurekaServerAutoConfiguration.java
.../netflix/eureka/server/EurekaServerAutoConfiguration.java
+64
-2
RefreshablePeerEurekaNodesTests.java
...etflix/eureka/server/RefreshablePeerEurekaNodesTests.java
+261
-0
No files found.
spring-cloud-netflix-eureka-server/src/main/java/org/springframework/cloud/netflix/eureka/server/EurekaServerAutoConfiguration.java
View file @
6e2238f2
/*
* Copyright 2013-201
5
the original author or authors.
* Copyright 2013-201
7
the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
...
...
@@ -35,7 +35,9 @@ import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import
org.springframework.boot.context.properties.EnableConfigurationProperties
;
import
org.springframework.boot.web.servlet.FilterRegistrationBean
;
import
org.springframework.cloud.client.actuator.HasFeatures
;
import
org.springframework.cloud.context.environment.EnvironmentChangeEvent
;
import
org.springframework.cloud.netflix.eureka.EurekaConstants
;
import
org.springframework.context.ApplicationListener
;
import
org.springframework.context.annotation.Bean
;
import
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
;
import
org.springframework.context.annotation.Configuration
;
...
...
@@ -67,6 +69,7 @@ import com.sun.jersey.spi.container.servlet.ServletContainer;
/**
* @author Gunnar Hillert
* @author Biju Kunjummen
* @author Fahim Farook
*/
@Configuration
@Import
(
EurekaServerInitializerConfiguration
.
class
)
...
...
@@ -169,9 +172,68 @@ public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
@ConditionalOnMissingBean
public
PeerEurekaNodes
peerEurekaNodes
(
PeerAwareInstanceRegistry
registry
,
ServerCodecs
serverCodecs
)
{
return
new
PeerEurekaNodes
(
registry
,
this
.
eurekaServerConfig
,
return
new
Refreshable
PeerEurekaNodes
(
registry
,
this
.
eurekaServerConfig
,
this
.
eurekaClientConfig
,
serverCodecs
,
this
.
applicationInfoManager
);
}
/**
* {@link PeerEurekaNodes} which updates peers when /refresh is invoked.
* Peers are updated only if
* <code>eureka.client.use-dns-for-fetching-service-urls</code> is
* <code>false</code> and one of following properties have changed.
* </p>
* <ul>
* <li><code>eureka.client.availability-zones</code></li>
* <li><code>eureka.client.region</code></li>
* <li><code>eureka.client.service-url.<zone></code></li>
* </ul>
*/
static
class
RefreshablePeerEurekaNodes
extends
PeerEurekaNodes
implements
ApplicationListener
<
EnvironmentChangeEvent
>
{
public
RefreshablePeerEurekaNodes
(
final
PeerAwareInstanceRegistry
registry
,
final
EurekaServerConfig
serverConfig
,
final
EurekaClientConfig
clientConfig
,
final
ServerCodecs
serverCodecs
,
final
ApplicationInfoManager
applicationInfoManager
)
{
super
(
registry
,
serverConfig
,
clientConfig
,
serverCodecs
,
applicationInfoManager
);
}
@Override
public
void
onApplicationEvent
(
final
EnvironmentChangeEvent
event
)
{
if
(
shouldUpdate
(
event
.
getKeys
()))
{
updatePeerEurekaNodes
(
resolvePeerUrls
());
}
}
/*
* Check whether specific properties have changed.
*/
protected
boolean
shouldUpdate
(
final
Set
<
String
>
changedKeys
)
{
assert
changedKeys
!=
null
;
// if eureka.client.use-dns-for-fetching-service-urls is true, then
// service-url will not be fetched from environment.
if
(
clientConfig
.
shouldUseDnsForFetchingServiceUrls
())
{
return
false
;
}
if
(
changedKeys
.
contains
(
"eureka.client.region"
))
{
return
true
;
}
for
(
final
String
key
:
changedKeys
)
{
// property keys are not expected to be null.
if
(
key
.
startsWith
(
"eureka.client.service-url."
)
||
key
.
startsWith
(
"eureka.client.availability-zones."
))
{
return
true
;
}
}
return
false
;
}
}
@Bean
public
EurekaServerContext
eurekaServerContext
(
ServerCodecs
serverCodecs
,
...
...
spring-cloud-netflix-eureka-server/src/test/java/org/springframework/cloud/netflix/eureka/server/RefreshablePeerEurekaNodesTests.java
0 → 100644
View file @
6e2238f2
/*
* Copyright 2013-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package
org
.
springframework
.
cloud
.
netflix
.
eureka
.
server
;
import
static
org
.
hamcrest
.
CoreMatchers
.
is
;
import
static
org
.
junit
.
Assert
.
assertEquals
;
import
static
org
.
junit
.
Assert
.
assertFalse
;
import
static
org
.
junit
.
Assert
.
assertNotNull
;
import
static
org
.
junit
.
Assert
.
assertThat
;
import
static
org
.
junit
.
Assert
.
assertTrue
;
import
static
org
.
mockito
.
Matchers
.
anyListOf
;
import
static
org
.
mockito
.
Mockito
.
mock
;
import
static
org
.
mockito
.
Mockito
.
never
;
import
static
org
.
mockito
.
Mockito
.
spy
;
import
static
org
.
mockito
.
Mockito
.
verify
;
import
static
org
.
mockito
.
Mockito
.
when
;
import
java.util.Arrays
;
import
java.util.Collections
;
import
java.util.HashSet
;
import
java.util.List
;
import
org.junit.Test
;
import
org.junit.runner.RunWith
;
import
org.springframework.beans.factory.annotation.Autowired
;
import
org.springframework.beans.factory.annotation.Value
;
import
org.springframework.boot.autoconfigure.EnableAutoConfiguration
;
import
org.springframework.boot.builder.SpringApplicationBuilder
;
import
org.springframework.boot.test.context.SpringBootTest
;
import
org.springframework.boot.test.util.EnvironmentTestUtils
;
import
org.springframework.boot.test.web.client.TestRestTemplate
;
import
org.springframework.cloud.context.environment.EnvironmentChangeEvent
;
import
org.springframework.cloud.netflix.eureka.EurekaClientConfigBean
;
import
org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration.RefreshablePeerEurekaNodes
;
import
org.springframework.context.ConfigurableApplicationContext
;
import
org.springframework.context.annotation.Configuration
;
import
org.springframework.http.HttpStatus
;
import
org.springframework.http.ResponseEntity
;
import
org.springframework.test.context.junit4.SpringRunner
;
import
com.netflix.appinfo.ApplicationInfoManager
;
import
com.netflix.discovery.EurekaClientConfig
;
import
com.netflix.eureka.EurekaServerConfig
;
import
com.netflix.eureka.cluster.PeerEurekaNodes
;
import
com.netflix.eureka.registry.PeerAwareInstanceRegistry
;
import
com.netflix.eureka.resources.ServerCodecs
;
/**
* @author Fahim Farook
*/
@RunWith
(
SpringRunner
.
class
)
@SpringBootTest
(
classes
=
RefreshablePeerEurekaNodesTests
.
Application
.
class
,
webEnvironment
=
SpringBootTest
.
WebEnvironment
.
RANDOM_PORT
,
value
=
{
"spring.application.name=eureka-server"
,
"eureka.client.service-url.defaultZone=http://localhost:8678/eureka/"
})
public
class
RefreshablePeerEurekaNodesTests
{
@Autowired
private
ConfigurableApplicationContext
context
;
@Autowired
private
PeerEurekaNodes
peerEurekaNodes
;
@Value
(
"${local.server.port}"
)
private
int
port
=
0
;
private
static
final
String
DEFAULT_ZONE
=
"eureka.client.service-url.defaultZone"
;
private
static
final
String
REGION
=
"eureka.client.region"
;
private
static
final
String
USE_DNS
=
"eureka.client.use-dns-for-fetching-service-urls"
;
@Test
public
void
notUpdatedWhenDnsIsTrue
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=true"
,
"eureka.client.region=unavailable-region"
,
// to force defaultZone
"eureka.client.service-url.defaultZone=http://default-host1:8678/eureka/"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
new
HashSet
<
String
>(
Arrays
.
asList
(
USE_DNS
,
DEFAULT_ZONE
))));
assertFalse
(
"PeerEurekaNodes' are updated when eureka.client.use-dns-for-fetching-service-urls is true"
,
serviceUrlMatches
(
"http://default-host1:8678/eureka/"
));
}
@Test
public
void
updatedWhenDnsIsFalse
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=false"
,
"eureka.client.region=unavailable-region"
,
// to force defaultZone
"eureka.client.service-url.defaultZone=http://default-host2:8678/eureka/"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
new
HashSet
<
String
>(
Arrays
.
asList
(
USE_DNS
,
DEFAULT_ZONE
))));
assertTrue
(
"PeerEurekaNodes' are not updated when eureka.client.use-dns-for-fetching-service-urls is false"
,
serviceUrlMatches
(
"http://default-host2:8678/eureka/"
));
}
@Test
public
void
updatedWhenRegionChanged
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=false"
,
"eureka.client.region=region1"
,
"eureka.client.availability-zones.region1=region1-zone"
,
"eureka.client.availability-zones.region2=region2-zone"
,
"eureka.client.service-url.region1-zone=http://region1-zone-host:8678/eureka/"
,
"eureka.client.service-url.region2-zone=http://region2-zone-host:8678/eureka/"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
REGION
)));
assertTrue
(
"PeerEurekaNodes' are not updated when eureka.client.region is changed"
,
serviceUrlMatches
(
"http://region1-zone-host:8678/eureka/"
));
changeProperty
(
"eureka.client.region=region2"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
REGION
)));
assertTrue
(
"PeerEurekaNodes' are not updated when eureka.client.region is changed"
,
serviceUrlMatches
(
"http://region2-zone-host:8678/eureka/"
));
}
@Test
public
void
updatedWhenAvailabilityZoneChanged
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=false"
,
"eureka.client.region=region4"
,
"eureka.client.availability-zones.region3=region3-zone"
,
"eureka.client.service-url.region4-zone=http://region4-zone-host:8678/eureka/"
,
"eureka.client.service-url.defaultZone=http://default-host3:8678/eureka/"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
"eureka.client.availability-zones.region3"
)));
assertTrue
(
this
.
peerEurekaNodes
.
getPeerEurekaNodes
().
get
(
0
).
getServiceUrl
().
equals
(
"http://default-host3:8678/eureka/"
));
changeProperty
(
"eureka.client.availability-zones.region4=region4-zone"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
"eureka.client.availability-zones.region4"
)));
assertTrue
(
"PeerEurekaNodes' are not updated when eureka.client.availability-zones are changed"
,
serviceUrlMatches
(
"http://region4-zone-host:8678/eureka/"
));
}
@Test
public
void
notUpdatedWhenIrrelevantPropertiesChanged
()
{
// Only way to test this is verifying whether updatePeerEurekaNodes() is invoked.
// PeerEurekaNodes.updatePeerEurekaNodes() is not public, hence cannot verify with Mockito.
class
VerifyablePeerEurekNode
extends
RefreshablePeerEurekaNodes
{
public
VerifyablePeerEurekNode
(
PeerAwareInstanceRegistry
registry
,
EurekaServerConfig
serverConfig
,
EurekaClientConfig
clientConfig
,
ServerCodecs
serverCodecs
,
ApplicationInfoManager
applicationInfoManager
)
{
super
(
registry
,
serverConfig
,
clientConfig
,
serverCodecs
,
applicationInfoManager
);
}
protected
void
updatePeerEurekaNodes
(
List
<
String
>
newPeerUrls
)
{
super
.
updatePeerEurekaNodes
(
newPeerUrls
);
}
}
// Create stubs.
final
EurekaClientConfigBean
configClientBean
=
mock
(
EurekaClientConfigBean
.
class
);
when
(
configClientBean
.
isUseDnsForFetchingServiceUrls
()).
thenReturn
(
false
);
final
VerifyablePeerEurekNode
mock
=
spy
(
new
VerifyablePeerEurekNode
(
null
,
null
,
configClientBean
,
null
,
null
));
mock
.
onApplicationEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
"some.irrelevant.property"
)));
verify
(
mock
,
never
()).
updatePeerEurekaNodes
(
anyListOf
(
String
.
class
));
}
@Test
public
void
peerEurekaNodesIsRefreshablePeerEurekaNodes
()
{
assertNotNull
(
this
.
peerEurekaNodes
);
assertTrue
(
"PeerEurekaNodes should be an instance of RefreshablePeerEurekaNodes"
,
this
.
peerEurekaNodes
instanceof
RefreshablePeerEurekaNodes
);
}
@Test
public
void
serviceUrlsCountAsSoonAsRefreshed
()
{
changeProperty
(
"eureka.client.service-url.defaultZone=http://defaul-host3:8678/eureka/,http://defaul-host4:8678/eureka/"
);
forceUpdate
();
assertThat
(
"PeerEurekaNodes' peer count is incorrect."
,
this
.
peerEurekaNodes
.
getPeerEurekaNodes
().
size
(),
is
(
2
));
}
@Test
public
void
serviceUrlsValueAsSoonAsRefreshed
()
{
changeProperty
(
"eureka.client.service-url.defaultZone=http://defaul-host4:8678/eureka/"
);
forceUpdate
();
assertTrue
(
"PeerEurekaNodes' new peer[0] is incorrect"
,
serviceUrlMatches
(
"http://defaul-host4:8678/eureka/"
));
}
@Test
public
void
dashboardUpdatedAsSoonAsRefreshed
()
{
changeProperty
(
"eureka.client.service-url.defaultZone=http://defaul-host5:8678/eureka/"
);
forceUpdate
();
final
ResponseEntity
<
String
>
entity
=
new
TestRestTemplate
()
.
getForEntity
(
"http://localhost:"
+
this
.
port
+
"/"
,
String
.
class
);
assertEquals
(
HttpStatus
.
OK
,
entity
.
getStatusCode
());
final
String
body
=
entity
.
getBody
();
assertNotNull
(
body
);
assertTrue
(
"DS Replicas not updated in the Eureka Server dashboard"
,
body
.
contains
(
"http://defaul-host5:8678/eureka/"
));
}
@Test
public
void
notUpdatedForRelaxedKeys
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=false"
,
"eureka.client.region=unavailable-region"
,
// to force defaultZone
"eureka.client.service-url.defaultZone=http://defaul-host6:8678/eureka/"
);
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
"eureka.client.serviceUrl.defaultZone"
)));
assertFalse
(
"PeerEurekaNodes' are updated for keys with relaxed binding"
,
serviceUrlMatches
(
"http://defaul-host6:8678/eureka/"
));
}
@EnableEurekaServer
@Configuration
@EnableAutoConfiguration
(
exclude
=
{
org
.
springframework
.
boot
.
autoconfigure
.
security
.
SecurityAutoConfiguration
.
class
,
org
.
springframework
.
boot
.
actuate
.
autoconfigure
.
ManagementWebSecurityAutoConfiguration
.
class
})
protected
static
class
Application
{
public
static
void
main
(
String
[]
args
)
{
new
SpringApplicationBuilder
(
RefreshablePeerEurekaNodesTests
.
Application
.
class
).
run
(
args
);
}
}
/*
* Changes the value of given key in the environment.
*/
private
void
changeProperty
(
final
String
...
pairs
)
{
EnvironmentTestUtils
.
addEnvironment
(
this
.
context
,
pairs
);
}
/*
* Refreshes the context with properties satisfying to invoke update.
*/
private
void
forceUpdate
()
{
changeProperty
(
"eureka.client.use-dns-for-fetching-service-urls=false"
,
"eureka.client.region=unavailable-region"
);
// to force defaultZone
this
.
context
.
publishEvent
(
new
EnvironmentChangeEvent
(
Collections
.
singleton
(
"eureka.client.service-url.defaultZone"
)));
}
/*
* Whether the first element in PeerEurekaNodes matches the given url.
*/
private
boolean
serviceUrlMatches
(
final
String
serviceUrl
)
{
return
this
.
peerEurekaNodes
.
getPeerEurekaNodes
().
get
(
0
).
getServiceUrl
().
equals
(
serviceUrl
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment