Spring-cloud-netflix: Zuul Routes with serviceId + path

Created on 1 Jun 2016  路  24Comments  路  Source: spring-cloud/spring-cloud-netflix

I'm using Zuul + Eureka with 2 micro-services register as Eureka client:
customer-service: v2/plans
pricing-service: v2/plans

I want to have different url path for both of them in Zuul routes

zuul:
    ignoredServices: "*"
    routes:
        customer-services:
          path: /v2/billing-plans/**
          serviceId: customer-service/plans
          stripPrefix: false
        pricing-services:
          path: /v2/pricing-plans/**
          serviceId: pricing-service/plans
          stripPrefix: false

Is this possible?

wontfix

Most helpful comment

It would be a very useful feature, eg. to prohibit access to other paths in the remote service. It can be achieved using stripPrefix but that might require the remote service to change its paths as well to match. Given that Zuul server is often mapped to a customer facing URL, it would be useful to change internal paths transparently.

My suggestion is to add a "servicePath" property and not mix it with the serviceId.

pricing-services:
          path: /v2/pricing-plans/**
          serviceId: pricing-service
          servicePath: /plans

Could it be as easy as prepending/replacing prefix when calling service?

All 24 comments

Hi

sure it's possible, we use it like that every days.
You can also have many routes (with different path) on same serviceId (in this case I guess the first that is matching win)
Christophe

So you mean serviceid can combine with it own url?just to clarify again, both [customer-service] and [pricing-service] is the eureka id.

Just give a try and it won't work

Error from Zuul:

Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: pricing-service/plans

Can you post a sample zuul application.yml for reference? thanks.

Anyone have any thought?!

We use it like that:

    route1:
      serviceId: id1
      path: /api/foo/**
      stripPrefix: false

    route2:
      serviceId: id1
      path: /api/bar/**
      stripPrefix: false

    route3:
      serviceId: id2
      path: /api/foobar/**
      stripPrefix: false

With your sample routes, id1 must always contain /api/bar or /api/foo
what if i want to expose different path name to public, example api/barcounter and it actually map /api/bar of id1.

route1:
      serviceId: id1/bar  -- map to id1
      path: /api/barcounter/** -- this exposed to public
      stripPrefix: false

Facing the same problem. Trying to create a frontend for an old service that uses a different ressource layout...

EdgeService (*new*)
http://<zuul>/api/service_a/people

Service_A (*old*)
http://<service_a>/api/v1/service_a/people

So I want to map the ressource people of Service_A to ressource people on the edge. It麓s no problem to achive what I want with using the url feature of zuul. But I am struggeling to get it to work with serviceId.

zuul:
  ignored-services: "*"
  routes:
    service_a:
      path: /service_a/**
      url: http://localhost:9005/api/v1/service_a/

But I want to use service discovery... Any idea?

@codymccain please use the 'add your reaction' github feature rather than '+1' as a comment, thanks.

i have similar issue

zuul:
   ignoredServices: ' * '
   routes:
     greeting-v1:
       path: /v1/**
       service-id: GREETING-V1

Url http://zuul/v1/greeting is returning success response but http://zuul/v1/api/greeting is not throwing 404.
Please advise.

@crenav I dont see how your issue is related, please open a separate issue with more details

@huhuhong can you tell us the value of spring.application.name for customer-services and pricing-services?

It would be a very useful feature, eg. to prohibit access to other paths in the remote service. It can be achieved using stripPrefix but that might require the remote service to change its paths as well to match. Given that Zuul server is often mapped to a customer facing URL, it would be useful to change internal paths transparently.

My suggestion is to add a "servicePath" property and not mix it with the serviceId.

pricing-services:
          path: /v2/pricing-plans/**
          serviceId: pricing-service
          servicePath: /plans

Could it be as easy as prepending/replacing prefix when calling service?

BTW, this looks similar to #308.

Yes it's similar

Is there some workaround available? Using the url works but then I do not have hystrix stream as a service is not used. Thanks

We override ZuulProxyConfiguration to change the "locateRoutes" function of DiscoveryClientRouteLocator. It seems to work but not fully tested.

-- EDIT: Overriding locateRoutes causes instability with preDecorator. Therefore we override getMatchingRoute instead!

/*
* Do not forget to remove EnableZuulProxy annotation 
* since it imports default ZuulProxyConfiguration
*/

@Configuration
@EnableCircuitBreaker
public class FuzyZuulProxyConfiguration extends ZuulProxyConfiguration implements FLogger {

    public static class FuzyDiscoveryRouteLocator extends DiscoveryClientRouteLocator {

        public FuzyDiscoveryRouteLocator(String servletPath, DiscoveryClient discovery, ZuulProperties properties, ServiceRouteMapper serviceRouteMapper) {
            super(servletPath, discovery, properties, serviceRouteMapper);
        }

        @Override
        public Route getMatchingRoute(String path) {
            Route route  = super.getMatchingRoute(path);

            ZuulRoute zuulRoute = null;
            for (Entry entry : locateRoutes().entrySet()) {
                String pattern = entry.getKey();
                if (this.pathMatcher.match(pattern, path)) {
                    zuulRoute = entry.getValue();
                    break;
                }
            }
            if (zuulRoute!=null && StringUtils.isNoneBlank(zuulRoute.getServiceId()) && StringUtils.isNoneBlank(zuulRoute.getUrl())){
                String toReplace = StringUtils.removeEnd(zuulRoute.getPath(), "/**");
                route.setPath(zuulRoute.getUrl() + StringUtils.removeStart(path, toReplace));
                route.setLocation(zuulRoute.getServiceId());
            }
            return route;
        }

    }

    @Autowired
    private DiscoveryClient discovery;

    @Autowired
    private ServiceRouteMapper serviceRouteMapper;

    @Bean
    @Primary
    public RouteLocator routeFuzyLocator() {
        return new FuzyDiscoveryRouteLocator(this.server.getServletPrefix(), this.discovery, this.zuulProperties, this.serviceRouteMapper);
    }

}

@kaplanke on stackoverflow @spencergibb helped with a work around that works: http://stackoverflow.com/questions/41624601/zuul-hystrix-stream-without-using-serviceid/41640974#41640974

However the servicePath question here is also important to solve which I do not have a workaround for so far.

@rahulaga thanks for the link. We actually somehow trick the router to use hystrix by providing serviceId while override the route path with url after the zuul routes are located. By this, we managed to use serviceId and url at the same time with hystrix support. (I.e. we can use a different url value than the path value). However I am not sure if this is a safe way to do this and I totally agree with the need of a servicePath property.

Is this enhancement being considered or is there a different "official" way to solve this now?

Not currently. Spring Cloud Gateway will include path rewrite capabilities.

No serviceId no hystrix锛孨o servicePath no route

Should we expect that there will be no more enhancements (extended functionality) on ZuulProxy in favour of Spring Cloud Gateway now?

@taasjord I've marked it to talk about it.

@ray

@huhuhong can you tell us the value of spring.application.name for customer-services and pricing-services?

Hello ,I have an old project, and now i am dividing it to some micro-services. but there is a problem . i have some routes such as

http://localhost:8080/send_code
http://localhost:8080/login
http://localhost:8080/message

they have been divided into there modules, so how can i route a specific path . Is that possible? thank you very much , I have searched a lot , but i still can't find ways to solve the problem.

This module has entered maintenance mode. This means that the Spring Cloud team will no longer be adding new features to the module. We will fix blocker bugs and security issues, and we will also consider and review small pull requests from the community.

Was this page helpful?
0 / 5 - 0 ratings