0

I've started the journey of learning more about this architecture using Spring Cloud and the Netflix's projects.

From a general standpoint, I do understand the architecture but now that I'm actually coding an app, a horizon of doubts has appeared about some minimal stuff that could have a big impact in the big approach of this architecture.

First, my app is based on this tutorial which I assume as valid since it's from Spring itself: https://spring.io/blog/2015/07/14/microservices-with-spring

And now, the problem: (ALL THIS USES EUREKA) let's say I've a microservice "accounts" which consist only in Spring Data REST for the persistence/HATEOAS and on top of that I've a client service which consumes those endpoints using RestTemplate (because I'm using Ribbon's load balancer). From my view, this is how it works: user -> frontend -> client service -> Spring Data REST endpoints (the microservice) -> DB.

This is nice and all but when I get the endpoint response (from the RestTemplate), it contains hypermedia going directly to the microservice, and per se, you now can drive your way through the microservice while ignoring the load balancer, which, for me, kills the purpose of it. And if I've like 5 microservices scaling to each other, it means the user is now focusing into just one of a broad way of 5 microservice that are supposedly managed by load balancer.

Q1: Is ok for an user to have that knowledge of the API (and then kinda killing part of the structure)?

Even if I add the links to the services detected by Eureka and delete the current endpoints to that microservice, those links aren't going to work and such means I'm chopping of the hypermedia links.

Q2: Is there another approach to this? Through some kind of proxy, external load balancer, dns or something like that?

P.D: I don't know if I'm being clear enough. Please let me know if something is misty.

P.D.2: Summary question: what to do with those media links? Let them be or use something else (please tell) to actually get them "right"?

Edit: Based on the advices below, I added Zuul which seems to be the solution to my problem. Now, about Zuul, where should I use to filter?

My app is like this right now: account-microservice (Spring Data REST) and account-web-service (client with the RestTemplate Load balancer). Those are the Eureka registered names.

Where must go the @EnabledZuulProxy? Inside the microservice or inside the client?

My current config is this one (currently, the @EnabledZuulProxy is in the client):

My microservice:

@SpringBootApplication
@EnableEurekaClient
public class AccountServer {

    public static void main(String[] args) {
        System.setProperty("spring.config.name", "AccountServerClient");
        SpringApplication.run(AccountServer.class, args);
    }
 }

 //Config:
 spring.application.name=account-microservice
 spring.application.freemarker.enabled=false
 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
 eureka.client.instance.leaseRenewalIntervalInSeconds=5

My client:

@SpringBootApplication
@EnableEurekaClient
@EnableZuulProxy
@EnableCircuitBreaker
public class AccountMicroService {

    public static void main(String[] args) {
        // Tell server to look for registration.properties or registration.yml
        System.setProperty("spring.config.name", "AccountMicroServiceClient");
        SpringApplication.run(AccountMicroService.class, args);
    }
 }

 //Config
 # Spring properties
 spring.application.name=account-web-service
 spring.freemarker.enabled=false
 eureka.instance.leaseRenewalIntervalInSeconds: 5
 eureka.client.serviceUrl.defaultZone=http://localhost:1111/eureka/
 zuul.routes.account-web-service.path=/accounts/**
 zuul.routes.account-web-service.serviceId=account-web-service
 zuul.routes.account-web-service.stripPrefix=false

Client code to consume SDR:

//@HystrixCommand(fallbackMethod="test")
public ResponseEntity<PagedResources<AccountResource>> findAll(Pageable pageable) throws RestClientException, URISyntaxException {

    return restTemplate.exchange(serviceUrl + "/accounts" + "?page={page}&size={size}&sort={sort}", HttpMethod.GET,
            null, new ParameterizedTypeReference<PagedResources<AccountResource>>() {
            }, pageable.getPageNumber(), pageable.getPageSize(), pageable.getSort());
}

Like this, it doesn't work at all. No filtering neither. If I change that and use it in the microservice, then it keeps sending everything directed to the microservice to the client, even the request from the client, which ends up with a loop and then an error (timeout, load balancer full, circuit broken...) which is generally this though: com.netflix.zuul.exception.ZuulException: Forwarding error.

Edit 2: I finally got Zuul to work and understood what it does. Now, when I access my new gateway app, it forwards the request to my microservice and the resulting hypermedia is now pointing to that gateway, which is what I wanted.

The problem was initially architectural and then turned into a coding problem, but now I think am clear about this technologies.

The cup of tea was understanding what Zuul does, when and how it does it. @Ryan Baxter guideline and this two articles: https://dzone.com/articles/microservice-architecture-with-spring-cloud-and-do and https://spring.io/guides/gs/routing-and-filtering/ where fundamentals to get this done. I'm gonna redact an answer for this with what I did and what I understood.

Manuel Páez
  • 121
  • 1
  • 4
  • 13

2 Answers2

1

If you use Zuul from Spring Cloud Netflix as your load balancer it will automatically add the X-Forwarded-Host header which Spring HATEOAS will respect. When that header is present HATEOAS will generate links using the host value in the header. If you are not using Zuul than you will have to configure your load balancer to add that header.

Ryan Baxter
  • 1,237
  • 2
  • 8
  • 16
  • I actually started to use Zuul but it doesn't seem to be doing anything, the hypermedia is the same as always and works as if it wasn't any extra routing by zuul. Mind to guide me a bit? – Manuel Páez Feb 24 '17 at 16:51
  • if you're making a call with `RestTemplate` you have to add the correct headers for the other service to understand. – spencergibb Feb 24 '17 at 17:24
  • Mind to tell me how? Just adding the header X-Forwarded-Host:serviceName to every single RestTemplate? – Manuel Páez Feb 24 '17 at 17:46
  • Ok, I kinda managed to get Zuul to work but now it's completely blocked. Can't access the microservice directly (keeps looping to the client service and then says load balancer is full) and when accessing through the client service, ends with an Hybrix runtime exception: timed-out and fallback failed. – Manuel Páez Feb 24 '17 at 22:43
  • I am not sure what you mean by Zuul is "blocked". We cant really help unless you provide more details. Samples that reproduce the problem are always helpful. – Ryan Baxter Feb 25 '17 at 00:45
  • Edited the question; please check it out. By "blocked" (hell, that was a bad way to spell it) I meant that it the both the client and the microservice stopped to show me responses because of errors. – Manuel Páez Feb 25 '17 at 01:37
0

Ok, what I did was simply to use Zuul (as @Ryan suggested). The thing is, initially, I kind of didn't get the approach for it. This was a petty architectural problem that, after interiorizing @Ryan's suggestion with the two articles I added to the bottom of my question, finally got them all right (hope so).

First: Now that my project is using Zuul (by creating the gateway app as both of my links suggested) and have both Hybrix and Ribbon in its classpath, Zuul must wire them all together and turn into a multifunctional proxy gateway with circuit breaker and load balancing characteristics.

Second: I got rid of that rest template load balancer because: a) the responses where getting the microservice hypermedia because I wasn't sending the port forward header, which pretty much nullify Zuul's proxying capabilities; and b) because since Zuul is acting as a gateway proxy load balancer, I do think is kinda boilerplate to use ribbon again with a rest template.

Now, my app is like this: user -> {[Zuul's gateway -> Spring Data REST microservice (forwarded by Zuul)] <- Eureka}.

I'm gonna take @Ryan's answer as the one since he pointed out exactly what was happening and what in the end I did to solve my problem. Still, I'm also answering since I think further explanation about what I ended up doing could help to another SO user.

If something is wrong about my solution, please comment.

Manuel Páez
  • 121
  • 1
  • 4
  • 13