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

Add support for Spring Cloud Gateway MVC #25715

Open
1 task done
mraible opened this issue Apr 2, 2024 · 17 comments
Open
1 task done

Add support for Spring Cloud Gateway MVC #25715

mraible opened this issue Apr 2, 2024 · 17 comments
Assignees
Labels
area: enhancement 🔧 area: feature request 💡 $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ theme: gateway $500 https://www.jhipster.tech/bug-bounties/

Comments

@mraible
Copy link
Contributor

mraible commented Apr 2, 2024

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
  • Checking this box is mandatory (this is just to show you read everything)
@mshima
Copy link
Member

mshima commented Apr 2, 2024

I will take a look.

@mshima
Copy link
Member

mshima commented Apr 4, 2024

There is no support for service discovery yet.

@mshima mshima self-assigned this Apr 4, 2024
@mraible
Copy link
Contributor Author

mraible commented Apr 4, 2024

I was able to get service discovery working when I wrote this blog post back in December.

@mshima
Copy link
Member

mshima commented Apr 4, 2024

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:

discovery:
locator:
enabled: true
lower-case-service-id: true
predicates:
- name: Path
args:
pattern: "'/services/'+serviceId.toLowerCase()+'/**'"
filters:
- name: RewritePath
args:
regexp: "'/services/' + serviceId.toLowerCase() + '/(?<remaining>.*)'"
replacement: "'/${remaining}'"

Routes needs to be manually configured.

@mraible
Copy link
Contributor Author

mraible commented Apr 4, 2024

Confirmed: spring-cloud/spring-cloud-gateway#3332

@mshima
Copy link
Member

mshima commented Apr 5, 2024

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.

@DanielFran
Copy link
Member

Yes, we should add the --experimental flag.

@mshima
Copy link
Member

mshima commented Apr 6, 2024

Yes, we should add the --experimental flag.

Done

@mraible
Copy link
Contributor Author

mraible commented Apr 9, 2024

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?

@mshima
Copy link
Member

mshima commented Apr 9, 2024

Do I have to specify reactive false for the gateway?

Yes

@mraible
Copy link
Contributor Author

mraible commented Apr 10, 2024

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".

Screenshot 2024-04-10 at 10 09 19 AM

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.

@mshima
Copy link
Member

mshima commented Apr 10, 2024

The logs show a 404 when trying to connect to downstream services.

Since there is no service discovery integration, due to #25715 (comment), that's expected.
Routes needs to be manually configured.

@mshima
Copy link
Member

mshima commented Apr 11, 2024

@mraible following #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"
}

@mraible
Copy link
Contributor Author

mraible commented Apr 25, 2024

@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.

@mshima
Copy link
Member

mshima commented Apr 25, 2024

This could be reused for #21012, otherwise we should just close it.

@mraible
Copy link
Contributor Author

mraible commented Apr 25, 2024

@egvimo Would this solution work to solve #21012?

@egvimo
Copy link
Contributor

egvimo commented Apr 25, 2024

Yes, this would work, I think.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: enhancement 🔧 area: feature request 💡 $$ bug-bounty $$ https://www.jhipster.tech/bug-bounties/ theme: gateway $500 https://www.jhipster.tech/bug-bounties/
Projects
None yet
Development

No branches or pull requests

4 participants