jhipster / generator-jhipster

JHipster is a development platform to quickly generate, develop, & deploy modern web applications & microservice architectures.
https://www.jhipster.tech
Apache License 2.0
21.4k stars 4.02k forks source link

Add support for Spring Cloud Gateway MVC #25715

Closed mraible closed 1 month ago

mraible commented 4 months ago
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
mshima commented 4 months ago

I will take a look.

mshima commented 4 months ago

There is no support for service discovery yet.

mraible commented 4 months ago

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

mshima commented 4 months ago

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.

mraible commented 4 months ago

Confirmed: https://github.com/spring-cloud/spring-cloud-gateway/issues/3332

mshima commented 4 months ago

Gateway is reactive by default it’s possible to force it imperative by setting reactive false or --no-reactive.

Issues:

Maybe we should require --experimental flag.

DanielFran commented 4 months ago

Yes, we should add the --experimental flag.

mshima commented 4 months ago

Yes, we should add the --experimental flag.

Done

mraible commented 4 months ago

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 commented 4 months ago

Do I have to specify reactive false for the gateway?

Yes

mraible commented 4 months ago

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 commented 4 months ago

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.

mshima commented 4 months ago

@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"
}
mraible commented 3 months ago

@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 commented 3 months ago

This could be reused for https://github.com/jhipster/generator-jhipster/issues/21012, otherwise we should just close it.

mraible commented 3 months ago

@egvimo Would this solution work to solve #21012?

egvimo commented 3 months ago

Yes, this would work, I think.

mshima commented 1 month ago

Upstream issues: