Generator-jhipster: Communication between two microservices

Created on 26 May 2016  路  42Comments  路  Source: jhipster/generator-jhipster

How to make Comunication between two microservices (microservices are generated with JWT security)

Hi,
_In the first, I note that hhis is not a bug, but I did not know where to make my request_

Used JHipster (v3.1.0) to generate 2 microservices (2 gateway and 2 microservices, with JWT security):

1st composant) Reference component "ref_gw" gateway app and "ref_ms" : microservice

2nd composant) "Reception_gw" : gateway app, and "reception_ms" microservice

Our need: the microservice "reception_ms" has to verify certain data which are quoted ref_ms, (by REST GET or POST Method)

I saw some documentations (theory only) speaking of discovery server-side (or the passage by the registery, other on spring cloud and AMQP,...), but how to implement it (registery solution) I found nothing.

Thank you

area area microservice

All 42 comments

@xetys wrote an article which had a section dealing with this check this http://stytex.de/blog/2016/03/25/jhipster3-microservice-tutorial/

ping @sdoxsee @PierreBesson

Thanks @deepu105
I hope that that is going to work.
I'll test and I inform you

Note that there is a module that can help you generate your clients. It's not based on RestTemplate but on Netflix's Feign so it should be easy to plug Ribbon/Eureka.

Thanks @cbornet

Great article @xetys ! Ping me on Twitter next time, so I know and can RT you!

Hi @MBenali, maybe you can take a look of what I did here: https://github.com/PierreBesson/jhipster-circuit-breaker-demo using Feign as a Rest client for microservices.
Also have a look at this article about Feign and Spring Cloud: https://dzone.com/articles/the-netflix-stack-using-spring-boot-part-3-feign

Thanks to all

MBenali

I didn't code yet the swagger template for microservice using @FeignClient but I think the current swagger-cli module should work well with spring cloud's autoconfiguration of Ribbon.
For an eureka service named "foo", something like

    @Bean
    public FeignApiClient FooRibbonApiClient() {
        FeignApiClient apiClient = createApiClient(FooApiClientProperties, feignEncoder, feignDecoder);
        apiClient.getFeignBuilder().client(feign.ribbon.RibbonClient.create()); // or better: create a RibbonClient bean
        apiClient.setBasePath("http://foo");
        return apiClient;
    }

i am currently stuck in upcomming-deadline-phases.....I have the following in mind...

  • PR to docs of how to use OAuth2 and the new JHipster UAA
  • PR to JHipster for a proper entityservice generator

i was trying Netflix Feign yesterday but i was really stuck on configuring the oauth2 behaviour.
There is an OAuth2RestTemplate, which is not that declarative as Feign is, but it actually has OAuth2 handling.

Since I am not familar with feign so far, I would appreciate some help from anyone who knows it in detail.

Otherwise I could share my dirtyhacking workaround to passs access tokens to feign...

or simply use OAuth2RestTemplate

@xetys You can see here how it is done in swagger-codegen.

thanks @cbornet, I will try that out

@xetys, I think Feign integration with OAuth2 is already merged here: https://github.com/spring-cloud/spring-cloud-security/commit/2718a044d30912eafb03f7a21e409afac8f1387f

I was thinking that we should a similar Feign Request Interceptor for JWT.

@PierreBesson, i am thinking of finding an hour, maybe together with @cbornet, to wire things in a right way. i believe this would be quite fast, because it would take me some more time to figure out the best practice (I started using spring just some months ago :laughing:)

ok, couldn't hold myself and figured out....thanks to @PierreBesson hinting me there is already a spring side interceptor....so all you need is actually define a OAuth2ProtectedResourceDetails an declare the interceptor bean like this:

@Bean
    public RequestInterceptor getOAuth2RequestInterceptor() throws IOException {
        log.info("installing OAuth2 request interceptor to feign");
        return new OAuth2FeignRequestInterceptor(new DefaultOAuth2ClientContext(), geOAuth2ProtectedResourceDetails());
    }

and that's it!

I PR all what is needed the next days...

Hi @xetys
I tried to follow your article "http://stytex.de/blog/2016/03/25/jhipster3-microservice-tutorial/ " (I have to have forgotten some things ...),

I want to find an information for the microservice reception_ms by REST calling the microservice reference_ms

The modifications in my code:

in microservice : reception_ms i add the classes :

  • AbstractMicroserviceClient
  • ReferentielClient who extends AbstractMicroserviceClient and i give the name of the microservice to the constructor like this: [public ReferentielClient() {super("referentiel_ms");}]
  • RestTemplateConfiguration
  • Referentiel (POJO)
    in this micro service, i test a rest call (Collection findAll = client.findAll();)

in the Referentiel_Gateway, i add @EnableCircuitBreaker to the ReferentielGwApp (_I have to make the same thing on the second Gateway: Reception_GW ?_ )

in the microservice : Referentiel_ms, i modify MicroserviceSecurityConfiguration (.antMatchers("/api/**") //FOR TESTING!, ... )

here is the exception which I obtained

2016-05-30 18:56:46.740  WARN 10735 --- [nio-9081-exec-5] s.c.a.AnnotationConfigApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ribbonLoadBalancerContext' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Unsatisfied dependency expressed through constructor argument with index 0 of type [com.netflix.loadbalancer.ILoadBalancer]: Error creating bean with name 'ribbonLoadBalancer' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.ILoadBalancer]: Factory method 'ribbonLoadBalancer' threw exception; nested exception is java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2fb93200 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@17159506[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 8]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ribbonLoadBalancer' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.ILoadBalancer]: Factory method 'ribbonLoadBalancer' threw exception; nested exception is java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@2fb93200 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@17159506[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 8]





org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'ribbonLoadBalancerContext' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Unsatisfied dependency expressed through constructor argument with index 0 of type [com.netflix.loadbalancer.ILoadBalancer]: Error creating bean with name 'ribbonLoadBalancer' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.ILoadBalancer]: Factory method 'ribbonLoadBalancer' threw exception; nested exception is java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@763bd34 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@17159506[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 8]; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'ribbonLoadBalancer' defined in org.springframework.cloud.netflix.ribbon.RibbonClientConfiguration: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.netflix.loadbalancer.ILoadBalancer]: Factory method 'ribbonLoadBalancer' threw exception; nested exception is java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask@763bd34 rejected from java.util.concurrent.ScheduledThreadPoolExecutor@17159506[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 8]
    at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:749)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:464)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1123)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1018)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
    at org.springframework.cloud.context.named.NamedContextFactory.createContext(NamedContextFactory.java:109)
    at org.springframework.cloud.context.named.NamedContextFactory.getContext(NamedContextFactory.java:75)
    at org.springframework.cloud.context.named.NamedContextFactory.getInstance(NamedContextFactory.java:114)
    at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getInstance(SpringClientFactory.java:103)
    at org.springframework.cloud.netflix.ribbon.SpringClientFactory.getLoadBalancer(SpringClientFactory.java:55)
    at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.getLoadBalancer(RibbonLoadBalancerClient.java:126)
    at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.getServer(RibbonLoadBalancerClient.java:115)
    at org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient.choose(RibbonLoadBalancerClient.java:65)
    at com.any.appli.applii.ms.client.AbstractMicroserviceClient.getUrl(AbstractMicroserviceClient.java:68)
    at com.any.appli.applii.ms.client.ReferentielClient.findAll(ReferentielClient.java:56)

@MBenali is "referentiel_ms" the name appearing in eureka?

@xetys yes referentiel_ms is the name appearing in eureka,

i add the @EnableCircuitBreaker to the second Gateway (ReceptionGwApp) , and now i retrieve (discover) my microsservice, but i obtain this exception:

2016-05-30 20:19:33.353 DEBUG 12056 --- [nio-9081-exec-1] s.n.www.protocol.http.HttpURLConnection  : sun.net.www.MessageHeader@1e41ce56 pairs: {null: HTTP/1.1 404 Not Found}{Server: Apache-Coyote/1.1}{X-Application-Context: referentiel_ms:dev:8081}{Content-Type: application/json;charset=UTF-8}{Transfer-Encoding: chunked}{Date: Mon, 30 May 2016 18:19:33 GMT}
org.springframework.web.client.HttpClientErrorException: 2016-05-30 20:19:33.353 DEBUG 12056 --- [nio-9081-exec-1] s.n.www.protocol.http.HttpURLConnection  : 
sun.net.www.MessageHeader@1e41ce56 pairs: {null: HTTP/1.1 404 Not Found}{Server: Apache-Coyote/1.1}{X-Application-Context: referentiel_ms:dev:8081}{Content-Type: application/json;charset=UTF-8}{Transfer-Encoding: chunked}{Date: Mon, 30 May 2016 18:19:33 GMT}
org.springframework.web.client.**HttpClientErrorException: 404 Not Found**
    at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
    at org.springframework.web.client.RestTemplate.handleResponse(RestTemplate.java:641)
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:597)
    at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:557)
    at org.springframework.web.client.RestTemplate.getForEntity(RestTemplate.java:289)
    at com.rte.appli.applii.ms.client.ReferentielClient.findAll(ReferentielClient.java:56)

@MBenali this is the time where 404 means something good...it least you are connected to your service and the url is not correct. Maybe we proceed this talk in a chat like gitter? I am afraid of spamming this issue forum a bit :smile:

Ok, @xetys
we can exchange about this subject on gitter

@xetys @PierreBesson I added an option in swagger-codegen to generate a stub interface like what is described here. Would you help me test it before I PR ?
You can use the generated petstore sample or generate for your own swagger spec with java -jar swagger-codegen.jar generate -i swagger.json -l springboot -DinterfaceOnly=true (after packaging swagger-codegen from my branch)
After that, all is needed is to configure your clients the way you want (w/wo Eureka, w/wo Hystrix, ...) with @FeignClient annotations. Eg:

@FeignClient(name="pet", url="http://petstore.swagger.io/v2")
public interface PetClient extends PetApi {

}

@cbornet, It's a very interesting solution.

I did some testing with your branch but only on the petstore. I will try to use it on my Feign Circuit Breaker demo to see if it works in this case.
I noticed that some tests didn't pass when building swagger-codegen.jar but maybe it's not your fault...

Do you know if Hystrix fallbacks will be available even if not using Ribbon/Eureka ?

Yes it's my fault :smile: . I have fixed the tests now.

From what I understand Ribbon is always used with FeignClient (with or without Eureka).
Hystrix doesn't seem to have any relationship with Ribbon so I think you can use it without Eureka.

Just had it working on a monolith app. This is just awesome ! I'll upgrade my module asap as it's much more integrated and flexible than what I had done !

There is an issue with petstore endpoints that have multiple "produces" (https://github.com/spring-cloud/spring-cloud-netflix/issues/808). I'll see how this can be dealt.

@PierreBesson I confirm it works with Hystrix without Eureka. Hystrix is even enabled by default on my monolithic test app.

@cbornet I just did some more tests today with your branch codegen.jar and JHipster Microservices and its really promising as we only have to generate the @FeignClient class.
So in your module we should switch to using Spring Cloud Feign even for the monolith.
Once you have updated your module, I can help you add some options for microservices (requesting Eureka, etc...).

That's a really good news. Did you try to do a fallback using Hystrix using :
@FeignClient(name = "app", fallback = ClientFallback.class)

Arf ! I have this issue : https://github.com/spring-cloud/spring-cloud-netflix/issues/1023
Since there is a @RequestMapping generated on the interface, spring-MVC interprets it as a controller !!
So I get a first controller for the FeignClient interface and then another one for the Hystrix fallback class and it fails during startup...
I need to see how to remove this @RequestMapping from the generated interface.

mhm....i hadn't any problems in my setup where the feign client are also using @RequestMapping as well....

@cbornet I had noticed that the interface show up as a controller in the Swagger UI but same as @xetys, I didn't have problems before...

Does it still work when you add the Hystrix fallback ?

@cbornet, I confirm that it doesn't work with an interface generated by your swagger-codegen. As soon as I add the fallback it fails to launch.
As far as I understand, this is caused by having an @RequestMapping both on the interface and on the method. When I removed the one on the interface the app could start fine. But then we need to change the rest of the generated "interface client".

I fixed it (removed the RequestMapping on the interface and adjusted the path in the methods). Can you check ?

@cbornet, the https://github.com/spring-cloud/spring-cloud-netflix/issues/1023 issue is now fixed and released in Brixton SR1. I have made PR to upgrade. Please tell us if it helps with your problem.

I don't think it really fixes the issue. The problem is in spring-framework so I don't think it can be solved.

@PierreBesson @xetys I have updated the swagger-cli module to use the spring-cloud-feign codegen. Would you make some tests before I publish to npm ?

I've used the org.springframework.cloud.security.oauth2.client.feign.OAuth2FeignRequestInterceptor in the past and it has worked well for me. However, in my current project I'm not using OAuth2, just JWT. What would be nice would be to have the ability to use either and have JHipster generate the necessary classes and configuration to do that.

Any feelings about that?

Thanks,

Russ Baker

@cbornet yes, i got a pretty stack of things to do on this and on that but due to a tight schedule I just had not the time....I issued to return to our feign problems during the next week

@russTbaker For me its some conceptional problem, because there is no support of a pure maschine recognition for internal calls using that mechanism. The only thing you can do here is to pass the jwt from request if possible, so internal requests are threated as similar to your gateway requests.

This at least is limiting the security options with pure jwt, as well as centralizing to much responsibility on the gateway...as I see it...

if you still want to go this way, @PierreBesson already started an approach of using a JwtRequestInterceptor. I take a look as soon I solve my issues on that topic

Closing as I think the question is answered...

Was this page helpful?
0 / 5 - 0 ratings