Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebFlux app can't start if management.server.port is customized #2980

Conversation

matsumana
Copy link
Member

@matsumana matsumana commented Aug 8, 2020

Hi.
If users customize WebFlux app's management.server.port setting as follows, the app can't start.

# e.g.
management:
  server:
    port: 18080

Because if management.server.port is not equals to server.port, DifferentManagementContextConfiguration will be enabled then ArmeriaReactiveWebServerFactory#getWebServer(HttpHandler) will be called twice.

The process flow is the following.

1st call of ArmeriaReactiveWebServerFactory#getWebServer(HttpHandler):

Then, 2nd time ApplicationContext refreshing will be started by DifferentManagementContextConfiguration#onApplicationEvent(WebServerInitializedEvent).

I investigated whether I can stop ApplicationContext refreshing by DifferentManagementContextConfiguration#onApplicationEvent(WebServerInitializedEvent).
But I couldn't find a way to customize.
So I added ArmeriaWebServer field into ArmeriaReactiveWebServerFactory to cache it.

@codecov
Copy link

codecov bot commented Aug 8, 2020

Codecov Report

Merging #2980 into master will increase coverage by 0.01%.
The diff coverage is 100.00%.

Impacted file tree graph

@@             Coverage Diff              @@
##             master    #2980      +/-   ##
============================================
+ Coverage     73.22%   73.23%   +0.01%     
- Complexity    12280    12292      +12     
============================================
  Files          1060     1060              
  Lines         47681    47695      +14     
  Branches       6021     6027       +6     
============================================
+ Hits          34914    34930      +16     
+ Misses         9715     9714       -1     
+ Partials       3052     3051       -1     
Impacted Files Coverage Δ Complexity Δ
.../web/reactive/ArmeriaReactiveWebServerFactory.java 78.87% <100.00%> (+3.09%) 33.00 <16.00> (+10.00)
...eriaReactiveWebServerFactoryAutoConfiguration.java 100.00% <100.00%> (ø) 2.00 <1.00> (ø)
...orp/armeria/client/eureka/EurekaEndpointGroup.java 60.71% <0.00%> (-3.58%) 23.00% <0.00%> (-1.00%)
...rp/armeria/common/stream/DefaultStreamMessage.java 84.69% <0.00%> (-2.05%) 66.00% <0.00%> (-1.00%)
.../com/linecorp/armeria/server/RoutingPredicate.java 69.35% <0.00%> (-1.62%) 20.00% <0.00%> (-1.00%)
...inecorp/armeria/server/HttpResponseSubscriber.java 78.94% <0.00%> (-0.81%) 56.00% <0.00%> (-1.00%)
...armeria/server/HttpServerPipelineConfigurator.java 76.63% <0.00%> (-0.73%) 13.00% <0.00%> (ø%)
...va/com/linecorp/armeria/client/HAProxyHandler.java 72.54% <0.00%> (-0.53%) 14.00% <0.00%> (ø%)
...armeria/server/healthcheck/HealthCheckService.java 83.77% <0.00%> (-0.44%) 52.00% <0.00%> (-1.00%)
...om/linecorp/armeria/client/HttpSessionHandler.java 71.59% <0.00%> (+0.59%) 49.00% <0.00%> (+1.00%)
... and 8 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 03becb6...ea7317c. Read the comment docs.

@minwoox minwoox added the defect label Aug 10, 2020
@minwoox minwoox added this to the 1.0.0 milestone Aug 10, 2020

private boolean isManagementPortEqualsToServerPort() {
final Integer managementPort = environment.getProperty("management.server.port", Integer.class);
if (managementPort != null && managementPort < 0) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about calling ensureValidPort(managementPort) so that an exception is raised if it's under 0?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, how about returning early if managementPort is null?

@@ -131,6 +140,11 @@ public ArmeriaReactiveWebServerFactory(ConfigurableListableBeanFactory beanFacto

@Override
public WebServer getWebServer(HttpHandler httpHandler) {
final int port = ensureValidPort(getPort());
if (armeriaWebServer != null && needsToReuseWebServer(port)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If armeriaWebServer is not null and the port is different, it seems like the previously configured settings to armeriaWebServer are gone. Isn't it right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previously configured settings remains.
I'll add some tests to check this.

@matsumana
Copy link
Member Author

updated.
I registered ArmeriaWebServer as bean instead of using static field.

Copy link
Member

@minwoox minwoox left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks a lot for fixing this. 👍

final ArmeriaWebServer armeriaWebServer = new ArmeriaWebServer(server, protocol, address, port,
beanFactory);
if (!isManagementPortEqualsToServerPort()) {
// Since this method will be called twice, need to reuse ArmeriaWebServer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about leaving a comment?
The management port is set to the Server in ArmeriaSpringActuatorAutoConfiguration.

@matsumana
Copy link
Member Author

Updated the comment.

Copy link
Collaborator

@trustin trustin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Excellent work, @matsumana 👍

Copy link
Contributor

@ikhoon ikhoon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! @matsumana

"65536",
})
void isManagementPortEqualsToServerPortThrows(String managementPort) {
assertThrows(IllegalArgumentException.class, () -> {
Copy link
Contributor

@ikhoon ikhoon Aug 12, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could use assertThatThrownBy(...)

.path(0)
.textValue())
.isEqualTo("/hello/foo");
} catch (JsonProcessingException e) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just throws the exception?

@trustin
Copy link
Collaborator

trustin commented Aug 12, 2020

@matsumana Let me merge once @ikhoon's comments are addressed. 😉

@matsumana
Copy link
Member Author

updated!

@trustin trustin merged commit 38d43de into line:master Aug 13, 2020
4 checks passed
@trustin
Copy link
Collaborator

trustin commented Aug 13, 2020

Thanks a lot, @matsumana 🙇

@matsumana
Copy link
Member Author

Thank you guys!

@matsumana matsumana deleted the fix-management-port-issue-for-boot2-webflux-autoconfigure branch August 13, 2020 03:55
fmguerreiro pushed a commit to fmguerreiro/armeria that referenced this pull request Sep 19, 2020
…#2980)

If users customize WebFlux app's `management.server.port` setting as follows, the app can't start.

```yaml
# e.g.
management:
  server:
    port: 18080
```

Because if `management.server.port` is not equal to `server.port`, [DifferentManagementContextConfiguration](https://github.com/spring-projects/spring-boot/blob/v2.3.2.RELEASE/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java#L115-L116) will be enabled then `ArmeriaReactiveWebServerFactory#getWebServer(HttpHandler)` will be called twice.

The process flow is the following.

1st call of `ArmeriaReactiveWebServerFactory#getWebServer(HttpHandler)`:
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L554
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java#L895
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java#L122
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java#L158
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java#L360
- https://github.com/spring-projects/spring-framework/blob/v5.2.8.RELEASE/spring-context/src/main/java/org/springframework/context/support/DefaultLifecycleProcessor.java#L182
- https://github.com/spring-projects/spring-boot/blob/v2.3.2.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerStartStopLifecycle.java#L40
- https://github.com/spring-projects/spring-boot/blob/v2.3.2.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerManager.java#L54
- https://github.com/line/armeria/blob/armeria-0.99.9/spring/boot2-webflux-autoconfigure/src/main/java/com/linecorp/armeria/spring/web/ArmeriaWebServer.java#L82

Then, 2nd time ApplicationContext refreshing will be started by `DifferentManagementContextConfiguration#onApplicationEvent(WebServerInitializedEvent)`.
- https://github.com/spring-projects/spring-boot/blob/v2.3.2.RELEASE/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/web/reactive/context/WebServerManager.java#L55-L56
- https://github.com/spring-projects/spring-boot/blob/v2.3.2.RELEASE/spring-boot-project/spring-boot-actuator-autoconfigure/src/main/java/org/springframework/boot/actuate/autoconfigure/web/server/ManagementContextAutoConfiguration.java#L131-L142

I investigated whether I can stop ApplicationContext refreshing by `DifferentManagementContextConfiguration#onApplicationEvent(WebServerInitializedEvent)`.
But I couldn't find a way to customize.
So I added ArmeriaWebServer field into ArmeriaReactiveWebServerFactory to cache it.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants