generator-jhipster
generator-jhipster copied to clipboard
Add support for Spring Cloud Gateway MVC
Overview of the feature request
When we first integrated Spring Cloud Gateway, it only supported WebFlux. Now it supports Spring MVC, so we should try to support it too.
https://docs.spring.io/spring-cloud-gateway/reference/spring-cloud-gateway-server-mvc.html
Motivation for or Use Case
If you're developing microservices with Spring MVC, you might prefer MVC in your gateway too.
Related issues or PR
- [x] Checking this box is mandatory (this is just to show you read everything)
I will take a look.
There is no support for service discovery yet.
I was able to get service discovery working when I wrote this blog post back in December.
I was able to get service discovery working when I wrote this blog post back in December.
There is no support for spring.cloud.gateway.discovery.locator.enabled=true
So this does not work:
https://github.com/jhipster/generator-jhipster/blob/1d0a1c2b47499ecb43b294e87a531a982e7776e4/generators/server/templates/src/main/resources/config/application.yml.ejs#L194-L206
Routes needs to be manually configured.
Confirmed: https://github.com/spring-cloud/spring-cloud-gateway/issues/3332
Gateway is reactive by default it’s possible to force it imperative by setting reactive false or --no-reactive.
Issues:
- openapi generated server url is wrong in microservices.
- JwtTokenRelay needs to be implemented.
Maybe we should require --experimental flag.
Yes, we should add the --experimental flag.
Yes, we should add the --experimental flag.
Done
I tried using the following JDL with 8.3.0:
application {
config {
baseName gateway
packageName org.jhipster.gateway
applicationType gateway
authenticationType oauth2
buildTool gradle
clientFramework react
prodDatabaseType postgresql
serviceDiscoveryType consul
testFrameworks [cypress]
microfrontends [blog, store]
}
}
application {
config {
baseName blog
packageName org.jhipster.blog
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType neo4j
enableHibernateCache false
serverPort 8081
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Blog, Post, Tag
}
application {
config {
baseName store
packageName org.jhipster.store
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType mongodb
enableHibernateCache false
serverPort 8082
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Product
}
entity Blog {
name String required minlength(3)
handle String required minlength(2)
}
entity Post {
title String required
content TextBlob required
date Instant required
}
entity Tag {
name String required minlength(2)
}
entity Product {
title String required
price BigDecimal required min(0)
image ImageBlob
}
relationship ManyToOne {
Blog{user(login)} to User with builtInEntity
Post{blog(name)} to Blog
}
relationship ManyToMany {
Post{tag(name)} to Tag{post}
}
paginate Post, Tag with infinite-scroll
paginate Product with pagination
deployment {
deploymentType docker-compose
serviceDiscoveryType consul
appsFolders [gateway, blog, store]
dockerRepositoryName "mraible"
}
deployment {
deploymentType kubernetes
appsFolders [gateway, blog, store]
clusteredDbApps [store]
kubernetesNamespace demo
kubernetesUseDynamicStorage true
kubernetesStorageClassName ""
serviceDiscoveryType consul
dockerRepositoryName "mraible"
}
The command I used to create my apps:
jhipster --experimental jdl app.jdl
If I look for webflux in the gateway's build file, it's still there:
$ cat gateway/build.gradle | grep webflux
implementation "org.springframework.boot:spring-boot-starter-webflux"
implementation libs.springdoc.openapi.starter.webflux.api
Do I have to specify reactive false for the gateway?
Do I have to specify
reactive falsefor the gateway?
Yes
I used the following JDL to create a set of apps:
application {
config {
baseName gateway
reactive false
packageName org.jhipster.gateway
applicationType gateway
authenticationType oauth2
buildTool gradle
clientFramework react
prodDatabaseType postgresql
serviceDiscoveryType consul
testFrameworks [cypress]
microfrontends [blog, store]
}
}
application {
config {
baseName blog
reactive false
packageName org.jhipster.blog
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType neo4j
enableHibernateCache false
serverPort 8081
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Blog, Post, Tag
}
application {
config {
baseName store
reactive false
packageName org.jhipster.store
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType mongodb
enableHibernateCache false
serverPort 8082
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Product
}
entity Blog {
name String required minlength(3)
handle String required minlength(2)
}
entity Post {
title String required
content TextBlob required
date Instant required
}
entity Tag {
name String required minlength(2)
}
entity Product {
title String required
price BigDecimal required min(0)
image ImageBlob
}
relationship ManyToOne {
Blog{user(login)} to User with builtInEntity
Post{blog(name)} to Blog
}
relationship ManyToMany {
Post{tag(name)} to Tag{post}
}
paginate Post, Tag with infinite-scroll
paginate Product with pagination
deployment {
deploymentType docker-compose
serviceDiscoveryType consul
appsFolders [gateway, blog, store]
dockerRepositoryName "mraible"
}
deployment {
deploymentType kubernetes
appsFolders [gateway, blog, store]
clusteredDbApps [store]
kubernetesNamespace demo
kubernetesUseDynamicStorage true
kubernetesStorageClassName ""
serviceDiscoveryType consul
dockerRepositoryName "mraible"
}
I used the following command:
jhipster jdl reactive-mf.jdl --monorepository --workspaces --experimental
When I start everything, the gateway is not able to load the microservice apps and the menu says "Error loading component".
The logs show a 404 when trying to connect to downstream services.
2024-04-10T10:23:02.247-04:00 WARN 28572 --- [ XNIO-1 task-4] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.servlet.resource.NoResourceFoundException: No static resource services/store/remoteEntry.js.]
2024-04-10T10:23:02.248-04:00 WARN 28572 --- [ XNIO-1 task-7] .m.m.a.ExceptionHandlerExceptionResolver : Resolved [org.springframework.web.servlet.resource.NoResourceFoundException: No static resource services/blog/remoteEntry.js.]
Everything works as expected when I change to use reactive true in the JDL.
The logs show a 404 when trying to connect to downstream services.
Since there is no service discovery integration, due to https://github.com/jhipster/generator-jhipster/issues/25715#issuecomment-2037578525, that's expected. Routes needs to be manually configured.
@mraible following https://github.com/jhipster/generator-jhipster/pull/25817 the routes option accepts "route", "route:host" or "route:host:port".
Updated jdl with routes option:
application {
config {
baseName gateway
reactive false
packageName org.jhipster.gateway
applicationType gateway
authenticationType oauth2
buildTool gradle
clientFramework react
prodDatabaseType postgresql
serviceDiscoveryType consul
testFrameworks [cypress]
microfrontends [blog, store]
routes ["blog:blog:8081", "store:store:8082"]
}
}
application {
config {
baseName blog
reactive false
packageName org.jhipster.blog
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType neo4j
enableHibernateCache false
serverPort 8081
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Blog, Post, Tag
}
application {
config {
baseName store
reactive false
packageName org.jhipster.store
applicationType microservice
authenticationType oauth2
buildTool gradle
clientFramework react
databaseType mongodb
enableHibernateCache false
serverPort 8082
serviceDiscoveryType consul
testFrameworks [cypress]
}
entities Product
}
entity Blog {
name String required minlength(3)
handle String required minlength(2)
}
entity Post {
title String required
content TextBlob required
date Instant required
}
entity Tag {
name String required minlength(2)
}
entity Product {
title String required
price BigDecimal required min(0)
image ImageBlob
}
relationship ManyToOne {
Blog{user(login)} to User with builtInEntity
Post{blog(name)} to Blog
}
relationship ManyToMany {
Post{tag(name)} to Tag{post}
}
paginate Post, Tag with infinite-scroll
paginate Product with pagination
deployment {
deploymentType docker-compose
serviceDiscoveryType consul
appsFolders [gateway, blog, store]
dockerRepositoryName "mraible"
}
deployment {
deploymentType kubernetes
appsFolders [gateway, blog, store]
clusteredDbApps [store]
kubernetesNamespace demo
kubernetesUseDynamicStorage true
kubernetesStorageClassName ""
serviceDiscoveryType consul
dockerRepositoryName "mraible"
}
@mshima This seems like quite a few changes for a feature that might go away once Spring Cloud Gateway MVC supports it. However, I talked with the Gateway project lead and he said service discovery won't be GA until November, so we should probably implement this workaround until then.
This could be reused for https://github.com/jhipster/generator-jhipster/issues/21012, otherwise we should just close it.
@egvimo Would this solution work to solve #21012?
Yes, this would work, I think.
Upstream issues:
- https://github.com/spring-cloud/spring-cloud-gateway/issues/3332: support manually added routes.
- https://github.com/spring-cloud/spring-cloud-gateway/issues/3382: duplicate required filters in every route
- https://github.com/spring-cloud/spring-cloud-gateway/issues/3354: manually set X-Forwarded-Prefix.
- https://github.com/spring-cloud/spring-cloud-gateway/issues/3233: retrieve routes based on https://github.com/spring-cloud/spring-cloud-gateway/issues/3233#issuecomment-1989318818