Friday, July 8, 2016

Microservices based Cloud Native Application - Part III

Preview:

This is the third post in the series of Microservices based application development.

The entire series could be found here:

Microservices based Cloud Native Application - Part I

Microservices based Cloud Native Application - Part II


Microservices based Cloud Native Application - Part III


Overview:


Continuing from previous posts, in this post, I'm going to write about a few challenges which I faced while implementing the Microservices and how did I address them. This might hopefully help other folks who might run into similar issues.


Challenges faced while implementing Microservices:


Issue 1:


While using Zuul API, I was getting the following exception, when the angular JS application, invoked the Zuul service.

com.netflix.zuul.exception.ZuulException: Forwarding error

at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.forward(RibbonRoutingFilter.java:132)
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.handleException(RibbonRoutingFilter.java:157) 
at org.springframework.cloud.netflix.zuul.filters.route.RibbonRoutingFilter.run(RibbonRoutingFilter.java:78)

We also got the following exception:
com.netflix.discovery.shared.transport.TransportException: Cannot execute request on any known server

Root Cause:

The root cause of both of the above exceptions were same:

The zuul server was failing to register with Eureka server as an Eureka Client.
After analyzing the logs, we found that while communicating with the Eureka Server, there was a mismatch in the API interface signatures. Looked like a version mismatch between Eureka client in Zuul and the Eureka server!

And it was indeed. 

Solution:

In the pom.xml of individual microservices, we were using Eureka clients as:

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.0.6.RELEASE</version>
</dependency>
Clearly, this might lead to confusions and issues, if different microservices define different versions of Eureka client, than the one defined in Eureka server.

To fix this and to bring in consistency in Eureka versions in all microservices, we removed the versioning from individual pom's and introduced dependency management in the parent pom (the pom of the parent project for all microservices).

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>Brixton.RELEASE</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>
As mentioned above, we used Brixton release and it automatically pulls the correct version of the dependencies defined in the child poms.

So the pom.xml of individual microservices for Eureka client will look like the below. Notice there is no version!

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>



Issue 2:

While inovking the Zuul APIs from the Angular JS application, the API calls were failing with CORS (Cross Origin Resource Sharing)issue as below:
"No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:63342' is therefore not allowed access".


Root Cause:

The reason for above issue is that, the angular app was running on a domain 'http://pronet-profile-web.cfapps.io/' and the Zuul App was running on a domain 'http://pronet-edge.cfapps.io/'. Notice that the sub domains are different. This was causing the CORS issue. 

Solution:

Add CORS filter in the Zuul server. Generally Spring recommends adding of an annotation "@CrossOrigin" on the spring boot microservice. However, this solution somehow did not work on the Zuul application (having @EnableZuulProxy annotation).

As an alternate fix, we added the the below filter to the Zuul application to enable CORS:

@Bean
  public CorsFilter corsFilter() {
      final UrlBasedCorsConfigurationSource urlBasedCorsConfigurationSource= new UrlBasedCorsConfigurationSource();
      final CorsConfiguration corsConfig = new CorsConfiguration();
      corsConfig.setAllowCredentials(true);
      corsConfig.addAllowedOrigin("*");
      corsConfig.addAllowedHeader("*");
      corsConfig.addAllowedMethod("OPTIONS");
      corsConfig.addAllowedMethod("HEAD");
      corsConfig.addAllowedMethod("GET");
      corsConfig.addAllowedMethod("PUT");
      corsConfig.addAllowedMethod("POST");
      corsConfig.addAllowedMethod("DELETE");
      corsConfig.addAllowedMethod("PATCH");
      urlBasedCorsConfigurationSource.registerCorsConfiguration("/**", corsConfig);
      return new CorsFilter(urlBasedCorsConfigurationSource);
  }
Note that you need to add the "OPTIONS" method as well.


Issue 3:

While inovking the Zuul APIs from the Angular JS application, the API calls were failing with the below error:
The 'Access-Control-Allow-Origin' header contains multiple values 'http://localhost:63342, http://localhost:63342', but only one is allowed.

Root Cause:
On analyzing this, we found that the individual microservices had a "@CrossOrigin" annotation. We already have a CORS filter on the Zuul server (Fix for Issue 2 above.)
 Adding another filter on the microservice layer, duplicates the CORS filter and hence we were gettting that error.

Solution:

 Removing the @CrossOrigin annotation from the individual microservices solved the issue.

No comments:

Post a Comment