spring-cloud / spring-cloud-netflix

Integration with Netflix OSS components
http://cloud.spring.io/spring-cloud-netflix/
Apache License 2.0
4.87k stars 2.44k forks source link

General questions about Zuul/Ribbon/Eureka #139

Closed ccit-spence closed 9 years ago

ccit-spence commented 9 years ago

Just wanting to be sure this is how it is supposed to work.

Setup: Service A -> instructor-service -> localhost:8080 Service B -> course-service -> localhost:8081 Zuul -> localhost:8765

Zuul route config

zuul: routes: instructor-service: /instructors/** course-service: /courses/**

Calling localhost:8765/instructors will result in the following response

{
    "_links": {
       "instructors": {
          "href": "http://localhost:8765/instructors/instructors{?page,size,sort}",
          "templated": true
       },
        "profile": {
           "href": "http://localhost:8765/instructors/alps"
        }
    }
}

Note the double /instructors/instructors is this normal? Do you need to change the context of each of the services to be at root if you want /instructors

Next question which I assume is normal operation due to Eureka and the Ribbon/Discovery. If you follow the rel "instructors" this is the result:

{
    "_links": {
        "self": {
            "href": "http://localhost:8765/arm/instructors/instructors{?page,size,sort}",
           "templated": true
        }
    },
    "_embedded": {
        "instructors": [
            {
                "id": 1,
                "createdDate": "2015-05-29",
                "modifiedDate": "2015-01-29",
                "_links": {
                    "self": {
                        "href": "http://localhost:8765/instructors/instructors/1"
                    },
                    "courses": {
                        "href": "http://localhost:8081/courses/search/findByRelated?relType=instructors&relatedId=1"
                        }
                    }
                }
            }
        }
    }
}

Note the url for courses is the actual url of the course service localhost:8081. My assumption is that since this is based on HAL the link is intended to be followed and not ever directly accessed? We are in an AWS environment and the host could change at anytime.

spencergibb commented 9 years ago

So for given this configuration: instructor-service: /instructors/**

Your spring data rest app produces this url: http://localhost:8765/instructors/instructors{?page,size,sort} The first :8765/instructors is the mapping you gave for zuul to match on. The second: /instructors{?page,size,sort} comes from spring data rest. It looks funny because they match.

We do something similar in our spring-cloud-samples/customer-stores.

This issue tracked some of our conversation.

Spring should find the appropriate headers and replace the host:port/prefix when creating the links: https://jira.spring.io/browse/SPR-12500

We had to pass headers to Traverson (if your using that) here and here

ccit-spence commented 9 years ago

I am using Traverson, the immediate concern is this would cause problems with CORS and with SSL. Correct?

I am using an old sample of the customer/stores example. Will update to the newer version.

spencergibb commented 9 years ago

If the javascript tries to access urls on different domains it would be a problem, but the scheme (http[s]), domain and port should be replaced properly

ccit-spence commented 9 years ago

Sounds good, will update the services with the headers fix. Thanks for the answers!

ccit-spence commented 9 years ago

I have tried the headers now and seeing the same behavior with showing the direct URL of the integrated service. Is this actually fixed? I am using the BUILD-SNAPSHOT.

spencergibb commented 9 years ago

@ccit-spence when I hit the customer-stores-ui proxy here: http://ui.local.spring.io:9900/customers/1

I get the following response


{
  "id" : 1,
  "firstname" : "Oliver",
  "lastname" : "Gierke",
  "address" : {
    "street" : "625 Avenue of the Americas",
    "zipCode" : "10011",
    "city" : "New York",
    "location" : {
      "latitude" : 40.740337,
      "longitude" : -73.995146
    }
  },
  "_links" : {
    "self" : {
      "href" : "http://ui.local.spring.io:9900/customers/1"
    },
    "stores-nearby" : {
      "href" : "http://ui.local.spring.io:9900/stores/search/findByAddressLocationNear?location=40.740337,-73.995146&distance=50"
    }
  }
}

You'll see both hrefs have the host and port of the proxy.

If I hit the customers service directly here: http://customers.local.spring.io:9000/customers/1 I get


{
  "id" : 1,
  "firstname" : "Oliver",
  "lastname" : "Gierke",
  "address" : {
    "street" : "625 Avenue of the Americas",
    "zipCode" : "10011",
    "city" : "New York",
    "location" : {
      "latitude" : 40.740337,
      "longitude" : -73.995146
    }
  },
  "_links" : {
    "self" : {
      "href" : "http://customers.local.spring.io:9000/customers/1"
    },
    "stores-nearby" : {
      "href" : "http://sgibb-mbp.local:8081/stores/search/findByAddressLocationNear?location=40.740337,-73.995146&distance=50"
    }
  }
}

Which has the wrong hosts and ports.

Whithout seeing your project, not sure what I can do besides the customer-stores examples.

ccit-spence commented 9 years ago

It will be a little later today, I will put a project up on github.

ccit-spence commented 9 years ago

Finally, got around to getting this uploaded. The example is at: https://github.com/ccit-spence/instructor-crouse-demo I did include my version of Zuul and Eureka incase that makes a difference. They are pretty much straight out of the examples.

spencergibb commented 9 years ago

Since you are using traverson, you need to configure zuul exactly like the customer-stores demo. You have:

zuul:
  routes:
    instructor-service: /instructors/**
    course-service: /courses/**

Through zuul, you end up with URLs with /instructors/instructors and traverson doesn't know how to traverse the extra /instructors.

Customer stores ui application.yml has:

zuul:
  routes:
    stores:
      path: /stores/**
      strip-prefix: false
    customers:
      path: /customers/**
      strip-prefix: false

You also need @EnableDiscoveryClient on the zuul Application.java

Here is what I did to get your app to work

diff --git a/instructors/src/main/java/com/demo/integration/CourseIntegration.java b/instructors/src/main/java/com/demo/integration/CourseIntegration.java
index 50293ba..8289047 100644
--- a/instructors/src/main/java/com/demo/integration/CourseIntegration.java
+++ b/instructors/src/main/java/com/demo/integration/CourseIntegration.java
@@ -45,17 +45,22 @@ public class CourseIntegration {
             coursesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
         }
         catch (RuntimeException e) {
-            // Eureka not available
+            e.printStackTrace();
         }

         logger.info("Trying to access the course service at {}…", coursesUri);

-        Traverson traverson = new Traverson(coursesUri, MediaTypes.HAL_JSON);
-        Link link = traverson.follow("courses", "search", "by-related")
-                .withHeaders(headers)
-                .withTemplateParameters(parameters).asLink();
-
-        logger.info("Found courses link pointing to {}.", link.getHref());
+        Link link = null;
+        try {
+            Traverson traverson = new Traverson(coursesUri, MediaTypes.HAL_JSON);
+            link = traverson.follow("courses", "search", "by-related")
+                    .withHeaders(headers)
+                    .withTemplateParameters(parameters).asLink();
+
+            logger.info("Found courses link pointing to {}.", link.getHref());
+        } catch (Exception e) {
+            e.printStackTrace();
+        }

         return link;

diff --git a/zuul/src/main/java/com/demo/Application.java b/zuul/src/main/java/com/demo/Application.java
index a6823f1..8bf176d 100644
--- a/zuul/src/main/java/com/demo/Application.java
+++ b/zuul/src/main/java/com/demo/Application.java
@@ -2,6 +2,7 @@ package com.demo;

 import org.springframework.boot.SpringApplication;
 import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
+import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
 import org.springframework.cloud.netflix.zuul.EnableZuulProxy;
 import org.springframework.context.annotation.ComponentScan;
 import org.springframework.context.annotation.Configuration;
@@ -12,6 +13,7 @@ import org.springframework.stereotype.Controller;
 @EnableAutoConfiguration
 @Controller
 @EnableZuulProxy
+@EnableDiscoveryClient
 public class Application {

     public static void main(String[] args) {
diff --git a/zuul/src/main/resources/application.yml b/zuul/src/main/resources/application.yml
index 274d103..b3d907f 100644
--- a/zuul/src/main/resources/application.yml
+++ b/zuul/src/main/resources/application.yml
@@ -11,8 +11,12 @@ endpoints:

 zuul:
   routes:
-    instructor-service: /instructors/**
-    course-service: /courses/**
+    instructor-service:
+      path: /instructors/**
+      strip-prefix: false
+    course-service:
+      path: /courses/**
+      strip-prefix: false

 #remove when spring-boot 1.2.1 is out
 security:
ccit-spence commented 9 years ago

Just to be sure I am following this properly. It appears you can't use any thing other than a one level URL.

localhost:8765/instructors -> works localhost:8765/instructor-service/instructors -> fails to proxy findby edit did a clean and restart and it worked (on Zuul)

changing to path: /xyz/instructors/** produces a 404

spencergibb commented 9 years ago

I don't see why it wouldn't work, in fact I made a test for it: 8fb85f26eac790195dfc04243b157bac10ccb379

I think the difficulty may come with spring-data-rest and strip-prefix: false which means send the prefix to the service you are calling, so /xyz/instructors/** doesn't work because the instructors service doesn't respond to /xyz/instructors, just /instructors. The proxy is not throwing the 404.

ccit-spence commented 9 years ago

In order for it to work with spring-data-rest what would a solution look like? Is it possible? One thing that seems strange is if I hit the link /instructor-service/instructors it will display data. If I hit /xyz/instructors I get the 404. Should I not get it for both?

spencergibb commented 9 years ago

My diff above is how to get it to work. The /instructor-service/instructors works because it is automatically added because of eureka (see /routes) and /instructor-service is stripped off before forwarding.