天天看点

spring cloud原理_SpringCloud学习笔记——旧服务网关Zuul原理入门实例Zuul原理——过滤器路由配置规则

spring cloud原理_SpringCloud学习笔记——旧服务网关Zuul原理入门实例Zuul原理——过滤器路由配置规则

入门实例

项目依赖

org.springframework.cloud            spring-cloud-starter-netflix-zuul                            org.springframework.cloud            spring-cloud-starter-netflix-eureka-client        
           

驱动网关工作

@[email protected](scanBasePackages ="com.spring.cloud.zuul.filter")public class DemoApplication {    public static void main(String[] args) {        SpringApplication.run(DemoApplication.class, args);    }}
           

在网关应用入口处使用@EnableZuulProxy注解驱动Zuul网关工作。

@EnableZuulProxy注解

@EnableCircuitBreaker//驱动Hystrix熔断器工作@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Import({ZuulProxyMarkerConfiguration.class})public @interface EnableZuulProxy {}
           

之所以使用@EnableZuulProxy驱动Zuul网关工作后,需要驱动Hystrix熔断器开启工作,因为服务网关可以在高并发场景下请求流量过大时保护服务网关,使服务网关应用不至于瘫痪。默认情况下,Zuul会使用信号量隔离的方式使用Hystrix熔断器,并非使用线程池的方式进行隔离。

网关配置

application.properties

# Zuul应用名称spring.application.name=zuulDemo#Zuul应用端口server.port=8040#将服务网关应用注册至服务注册中心,并从服务注册中心获取服务实例列表(包含服务实例名称,服务实例IP,服务实例端口号)eureka.client.service-url.defaultZone=http://server1:8701/eureka/,http://server2:8702/eureka/#配置通过path方式进行服务路由zuul.routes.user-router.path=/user/**#配置通过url方式进行服务路由zuul.routes.user-router.url=http://localhost:8100/#配置服务路由service-idzuul.router.user-router.service-id=user-service-provider
           

上述配置文件中,zuul.routes.*用来配置路由信息,本身是一个Map对象,后续的配置是为了配置路由信息。zuul.routes.user-router.path用来配置用户微服务匹配路径;zuul.routes.user-router.url用来配置用户微服务实际路径;如果用户微服务存在多个服务实例,通过配置微服务实例Id的方式进行路由配置(zuul.router.user-router.service-id=服务实例Id)。

Zuul原理——过滤器

Zuul的本质是一套Servlet的API。其中ZuulServlet是核心Servlet,接收各类服务请求。此外,Zuul还提供了ZuulServletFilter拦截器,可以拦截各类请求。为了更加方便和快捷的增加和删除拦截逻辑,在ZuulServlet和ZuulServletFilter基础上,定义了自己的过滤器ZuulFilter。

过滤器设计

ZuulFilter是一个抽象类,实现了接口IZuulFilter。Netflix Zuul提供了许多ZuulFilter实现类。在Spring Cloud Zuul中实现的过滤器必须包含4个特征:过滤类型、执行顺序、执行条件、具体操作。在ZuulFilter抽象类中对4个过滤器特性进行了定义:

public abstract class ZuulFilter implements IZuulFilter, Comparable {    public abstract String filterType();    public abstract int filterOrder();    boolean shouldFilter();    Object run() throws ZuulException;}
           

方法的含义和功能总结如下:

方法名 描述
filterType 返回过滤器类型字符串(pre,routing,post,error)
filterOrder 返回该过滤器执行顺序,数值越小执行顺序越靠前
shouldFilter 返回布尔值决定该过滤器是否需要执行
run 过滤器执行的具体逻辑,可在该方法中实现自定义的业务逻辑,返回null则进行后续的正常逻辑

执行原理

在ZuulFilter抽象类中,定义的4个方法均没有参数,如何执行过滤器相关操作?

过滤器拦截的均为Http请求,在Zuul中提供了以下核心类来完成过滤器相关工作。

核心类 描述
ZuulServlet 处理http请求入口,用于处理服务网关Http请求
ZuulRunner 用于对不同类型的过滤器进行串联
FilterProcessor 执行具体的过滤器相关操作
RequestContext 请求上下文,用于获取请求参数和设置响应信息

RequestContext

每一个新的http请求都是由一个独立的线程进行处理(tomcat线程),本次请求的所有参数均在一个独立的线程中进行处理,故将每次的请求上下文存储于ThreadLocal(线程本地变量,每次处理过程中存储RequestContex实例副本,避免参数误操作)中。在initialValue方法中,每个Http请求只能实例化自己的请求上下文(RequestContext)实例。

public class RequestContext extends ConcurrentHashMap {    private static final Logger LOG = LoggerFactory.getLogger(RequestContext.class);    protected static Class extends RequestContext> contextClass = RequestContext.class;    private static RequestContext testContext = null;    protected static final ThreadLocal extends RequestContext> threadLocal = new ThreadLocal() {        protected RequestContext initialValue() {            try {                return (RequestContext)RequestContext.contextClass.newInstance();            } catch (Throwable var2) {                throw new RuntimeException(var2);            }        }    };    public static RequestContext getCurrentContext() {        if (testContext != null) {            return testContext;        } else {            RequestContext context = (RequestContext)threadLocal.get();            return context;        }    }    public boolean getBoolean(String key) {        return this.getBoolean(key, false);    }    public boolean getZuulEngineRan() {        return this.getBoolean("zuulEngineRan");    }    public void setZuulEngineRan() {        this.put("zuulEngineRan", true);    }    public HttpServletRequest getRequest() {        return (HttpServletRequest)this.get("request");    }    public void setRequest(HttpServletRequest request) {        this.put("request", request);    }    public HttpServletResponse getResponse() {        return (HttpServletResponse)this.get("response");    }    public void setResponse(HttpServletResponse response) {        this.set("response", response);    }    public void unset() {        threadLocal.remove();    }}
           
方法 描述
getCurrentContext 获取当前请求上下文(RequestContext)
get(set)Request 获取(设置)请求信息
get(set)Response 获取(设置)响应信息 
get(set)ZuulEngineRan 设置Zuul引擎(ZuulRunner)
get(set)Boolean 获取(设置)Boolean值
unset 从ThreadLocal中移除当前请求上下文副本

ZuulServlet

package com.netflix.zuul.http;public class ZuulServlet extends HttpServlet {    private static final long serialVersionUID = -3374242278843351500L;    private ZuulRunner zuulRunner;    public ZuulServlet() {    }    public void init(ServletConfig config) throws ServletException {        super.init(config);        //buffer-requests:初始化Zuul引擎(ZuulRunner)必要参数(默认为false)        String bufferReqsStr = config.getInitParameter("buffer-requests");        boolean bufferReqs = bufferReqsStr != null && bufferReqsStr.equals("true");        this.zuulRunner = new ZuulRunner(bufferReqs);    }    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {        try {            //初始化Zuul引擎(ZuulRunner)            this.init((HttpServletRequest)servletRequest, (HttpServletResponse)servletResponse);            RequestContext context = RequestContext.getCurrentContext();            //设置buffer-requests参数为true            context.setZuulEngineRan();            try {                this.preRoute();            } catch (ZuulException var13) {                this.error(var13);                this.postRoute();                return;            }            try {                this.route();            } catch (ZuulException var12) {                this.error(var12);                this.postRoute();                return;            }            try {                this.postRoute();            } catch (ZuulException var11) {                this.error(var11);            }        } catch (Throwable var14) {            this.error(new ZuulException(var14, 500, "UNHANDLED_EXCEPTION_" + var14.getClass().getName()));        } finally {            RequestContext.getCurrentContext().unset();        }    }    void postRoute() throws ZuulException {        this.zuulRunner.postRoute();    }    void route() throws ZuulException {        this.zuulRunner.route();    }    void preRoute() throws ZuulException {        this.zuulRunner.preRoute();    }    void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {        this.zuulRunner.init(servletRequest, servletResponse);    }    void error(ZuulException e) {        RequestContext.getCurrentContext().setThrowable(e);        this.zuulRunner.error();    }}
           
方法 描述
init(ServletConfig) 初始化Zuul执行引擎(ZuulRunner)
init(HttpServletRequst,HttpServletResponse) 设置Zuul执行引擎(ZuulRunner)请求(request)响应(response)信息和参数
service() 执行过滤器相关操作
preRoute() 使用Zuul执行引擎执行前置过滤器(过滤器类型为pre)
route() 使用Zuul执行引擎执行路由过滤器(过滤器类型为routing)
postRoute() 使用Zuul执行引擎执行Post过滤器(过滤器类型为post)
error() 使用Zuul执行引擎执行异常过滤器(过滤器类型为error)

通过service方法,我们可以获知Zuul过滤器执行顺序如下:

  • 前置(pre)、路由(route)、post类型过滤器执行过程中均未发生异常,执行顺序为pre->route->post,error类型不执行
  • 前置(pre)类型过滤器执行过程中发生异常,执行顺序为pre->error->post
  • 路由(route)类型过滤器执行过程中发生异常,执行顺序为pre->route->error->post
  • post类型过滤器执行过程中发生异常,执行顺序为pre->route->post->error

service方法执行的过程中一旦发生异常,将抛出的Throwable实例设置到当前执行请求上下文的Throwable属性内。执行过程中无论是否有异常发生,从线程本地变量中移除请求上下文(ReqeustContext)副本。

ZuulRunner

public class ZuulRunner {    private boolean bufferRequests;    public ZuulRunner() {        this.bufferRequests = true;    }    public ZuulRunner(boolean bufferRequests) {        this.bufferRequests = bufferRequests;    }    public void init(HttpServletRequest servletRequest, HttpServletResponse servletResponse) {        RequestContext ctx = RequestContext.getCurrentContext();        if (this.bufferRequests) {            ctx.setRequest(new HttpServletRequestWrapper(servletRequest));        } else {            ctx.setRequest(servletRequest);        }        ctx.setResponse(new HttpServletResponseWrapper(servletResponse));    }    public void postRoute() throws ZuulException {        FilterProcessor.getInstance().postRoute();    }    public void route() throws ZuulException {        FilterProcessor.getInstance().route();    }    public void preRoute() throws ZuulException {        FilterProcessor.getInstance().preRoute();    }    public void error() {        FilterProcessor.getInstance().error();    }}
           
public class HttpServletResponseWrapper extends javax.servlet.http.HttpServletResponseWrapper {    private int status = 0;    public HttpServletResponseWrapper(HttpServletResponse response) {        super(response);    }    public void setStatus(int sc) {        this.status = sc;        super.setStatus(sc);    }    public void setStatus(int sc, String sm) {        this.status = sc;        super.setStatus(sc, sm);    }    public int getStatus() {        return this.status;    }}
           

ZuulRunner作为Zuul执行引擎,初始化时将请求上下文(ReqeustContext)中的响应属性设置为HttpServletResponseWrapper实例,该实例主要通过status属性设置http响应状态码。如果参数bufferRequests为true,将请求上下文中的请求属性设置为HttpServletRequestWrapper实例,HttpServletRequestWrapper类主要用于把请求的表单参数和请求体缓存(设置)在对应的属性中,无特殊要求传递bufferRequests默认值(false)。

方法 描述
init 初始化Zuul执行引擎
postRoute 使用FilterProcessor实例执行post类型过滤器
route 使用FilterProcessor实例执行路由(类型为route)过滤器
preRoute 使用FilterProcessor实例执行前置(类型为pre)过滤器
error 使用FilterProcessor实例执行错误(类型为error)过滤器

FilterProcessor

public class FilterProcessor {    static FilterProcessor INSTANCE = new FilterProcessor();    protected static final Logger logger = LoggerFactory.getLogger(FilterProcessor.class);    private FilterUsageNotifier usageNotifier = new FilterProcessor.BasicFilterUsageNotifier();    public FilterProcessor() {    }    public static FilterProcessor getInstance() {        return INSTANCE;    }    public static void setProcessor(FilterProcessor processor) {        INSTANCE = processor;    }    public void setFilterUsageNotifier(FilterUsageNotifier notifier) {        this.usageNotifier = notifier;    }    public void postRoute() throws ZuulException {        try {            this.runFilters("post");        } catch (ZuulException var2) {            throw var2;        } catch (Throwable var3) {            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_POST_FILTER_" + var3.getClass().getName());        }    }    public void error() {        try {            this.runFilters("error");        } catch (Throwable var2) {            logger.error(var2.getMessage(), var2);        }    }    public void route() throws ZuulException {        try {            this.runFilters("route");        } catch (ZuulException var2) {            throw var2;        } catch (Throwable var3) {            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_ROUTE_FILTER_" + var3.getClass().getName());        }    }    public void preRoute() throws ZuulException {        try {            this.runFilters("pre");        } catch (ZuulException var2) {            throw var2;        } catch (Throwable var3) {            throw new ZuulException(var3, 500, "UNCAUGHT_EXCEPTION_IN_PRE_FILTER_" + var3.getClass().getName());        }    }    public Object runFilters(String sType) throws Throwable {        if (RequestContext.getCurrentContext().debugRouting()) {            Debug.addRoutingDebug("Invoking {" + sType + "} type filters");        }        boolean bResult = false;        List list = FilterLoader.getInstance().getFiltersByType(sType);        if (list != null) {            for(int i = 0; i < list.size(); ++i) {                ZuulFilter zuulFilter = (ZuulFilter)list.get(i);                Object result = this.processZuulFilter(zuulFilter);                if (result != null && result instanceof Boolean) {                    bResult |= (Boolean)result;                }            }        }        return bResult;    }    public Object processZuulFilter(ZuulFilter filter) throws ZuulException {        RequestContext ctx = RequestContext.getCurrentContext();        boolean bDebug = ctx.debugRouting();        String metricPrefix = "zuul.filter-";        long execTime = 0L;        String filterName = "";        try {            long ltime = System.currentTimeMillis();            filterName = filter.getClass().getSimpleName();            RequestContext copy = null;            Object o = null;            Throwable t = null;            if (bDebug) {                Debug.addRoutingDebug("Filter " + filter.filterType() + " " + filter.filterOrder() + " " + filterName);                copy = ctx.copy();            }            ZuulFilterResult result = filter.runFilter();            ExecutionStatus s = result.getStatus();            execTime = System.currentTimeMillis() - ltime;            switch(s) {            case FAILED:                t = result.getException();                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);                break;            case SUCCESS:                o = result.getResult();                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.SUCCESS.name(), execTime);                if (bDebug) {                    Debug.addRoutingDebug("Filter {" + filterName + " TYPE:" + filter.filterType() + " ORDER:" + filter.filterOrder() + "} Execution time = " + execTime + "ms");                    Debug.compareContextState(filterName, copy);                }            }            if (t != null) {                throw t;            } else {                //计数器统计                this.usageNotifier.notify(filter, s);                return o;            }        } catch (Throwable var15) {            if (bDebug) {                Debug.addRoutingDebug("Running Filter failed " + filterName + " type:" + filter.filterType() + " order:" + filter.filterOrder() + " " + var15.getMessage());            }            //计数器统计            this.usageNotifier.notify(filter, ExecutionStatus.FAILED);            if (var15 instanceof ZuulException) {                throw (ZuulException)var15;            } else {                ZuulException ex = new ZuulException(var15, "Filter threw Exception", 500, filter.filterType() + ":" + filterName);                ctx.addFilterExecutionSummary(filterName, ExecutionStatus.FAILED.name(), execTime);                throw ex;            }        }    }}
           

在FilterProcessor类中,提供属性usageNotifier(类型为FilterUsageNotifier接口),默认实现类为FilterProcessor静态内部类BasicFilterUsageNotifier,该类实现接口FilterUsageNotifier的notify方法(方法参数为ZuulFilter和ExecutionStatus枚举),ExecutionStatus(处理状态)枚举中主要提供如下可选值

public enum ExecutionStatus {    SUCCESS(1),    SKIPPED(-1),    DISABLED(-2),    FAILED(-3);    private int status;    private ExecutionStatus(int status) {        this.status = status;    }}
           
可选值 描述
SUCCESS(1) 该过滤器处理成功
SKIPPED(-1) 该过滤器跳过处理
DISABLED(-2) 该过滤器为禁用状态
FAILED(-3) 该过滤器处理失败

在FilterProcesser类中,主要通过runFilter方法(参数为过滤器类型字符串)和processZuulFilter(参数为ZuulFilter对象)对过滤器进行执行并返回执行结果。runFilter方法中返回值为布尔类型(过滤器执行成功或失败),该返回值无实际意义,执行成功或失败都通过请求上下文(ReqeustContext).addFilterExecutionSummary(参数分别为过滤器名称,执行状态枚举值,执行时间)对过滤器执行结果进行记录。

方法 描述
postRoute 执行post类型过滤器(前缀为post)
route 执行路由类型过滤器(前缀为route)
preRoute 执行前置类型过滤器(前缀为pre)
error 执行错误类型过滤器(前缀为error)
runFilter 通过参数获取需要执行的过滤器类型,返回执行结果
processZuulFilter 对具体的过滤器执行操作,并将执行结果设置在请求上下文(RequestContext)中

自动装配的过滤器

过滤器类型 过滤器名 过滤器描述 顺序
pre ServletDetectionFilter 标记处理Servlet的类型 -3
Servlet30WrapperFilter 包装HttpServletRequest请求 -2
FromBodyWrapperFilter 包装请求体 -1
DebugFilter 标记调试状态 1
PreDecorationFilter 处理请求上下文供后续使用 5
route RibbonRoutingFilter serviceId请求转发 10
SimpleHostRoutingFilter url请求转发 100
SendForwardFilter forward请求转发 500
post SendErrorFilter 处理有错误的请求响应
SendResponseFilter 处理正常处理的请求响应 1000

开发过滤器

在本例中,我们将通过过滤器对http请求过程中的用户名、密码、token参数信息进行校验,校验成功通过过滤器为响应信息设置新的响应头(response-header)信息。

用户名校验过滤器(UserNameFilter)

import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.exception.ZuulException;import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;/** * 根据用户名进行校验 */@Componentpublic class UserNameFilter extends ZuulFilter {    @Override   //设置过滤器类型    public String filterType() {        return FilterConstants.PRE_TYPE;    }    //用户名校验最先执行    @Override    public int filterOrder() {        return FilterConstants.SERVLET_DETECTION_FILTER_ORDER-3;    }    @Override    public boolean shouldFilter() {        return true;    }    //执行具体的校验逻辑    @Override    public Object run() throws ZuulException {        RequestContext requestContext=RequestContext.getCurrentContext();        HttpServletRequest request=requestContext.getRequest();        if(null!=request.getParameter("userName"))        {            requestContext.setSendZuulResponse(true);            requestContext.setResponseStatusCode(200);            requestContext.set("isSuccess",true);        }        else{            requestContext.setSendZuulResponse(false);        throw new ZuulRuntimeException(new ZuulException(this.filterType()+":"+this.getClass().getSimpleName(), HttpStatus.UNAUTHORIZED.value(),"用户名不能为空"));        }        return null;    }}
           

用户密码校验(PassWordFilter)

import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.exception.ZuulException;import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Componentpublic class PassWordFilter extends ZuulFilter {    //设置过滤器类型    @Override    public String filterType() {        return FilterConstants.PRE_TYPE;    }    //设置过滤器执行顺序    @Override    public int filterOrder() {        return FilterConstants.SERVLET_DETECTION_FILTER_ORDER-2;    }    @Override    public boolean shouldFilter() {        return true;    }    //执行具体的校验逻辑    @Override    public Object run() throws ZuulException {        RequestContext requestContext=RequestContext.getCurrentContext();        HttpServletRequest request=requestContext.getRequest();        if(null!=request.getParameter("passWord"))        {            requestContext.setSendZuulResponse(true);            requestContext.setResponseStatusCode(200);            requestContext.set("isSuccess",true);        }        else        {            requestContext.setSendZuulResponse(false);            throw new ZuulRuntimeException(new ZuulException(this.filterType()+":"+this.getClass().getSimpleName(), HttpStatus.UNAUTHORIZED.value(),"密码不能为空"));        }        return null;    }}
           

Token校验器(AccessTokenFilter)

import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.exception.ZuulException;import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;import org.springframework.cloud.netflix.zuul.util.ZuulRuntimeException;import org.springframework.http.HttpStatus;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletRequest;@Componentpublic class AccessTokenFilter extends ZuulFilter {    @Override    public String filterType() {        return FilterConstants.PRE_TYPE;    }    @Override    public int filterOrder() {        return FilterConstants.SERVLET_DETECTION_FILTER_ORDER-1;    }    @Override    public boolean shouldFilter() {        return true;    }    @Override    public Object run() throws ZuulException {        RequestContext requestContext=RequestContext.getCurrentContext();        HttpServletRequest request=requestContext.getRequest();        if(null!=request.getParameter("accessToken"))        {            requestContext.setResponseStatusCode(200);            requestContext.setSendZuulResponse(true);            requestContext.set("isSuccess",true);        }        else{            requestContext.setSendZuulResponse(false);            throw new ZuulRuntimeException(new ZuulException(this.filterType()+":"+this.getClass().getSimpleName(), HttpStatus.UNAUTHORIZED.value(),"token不能为空"));        }        return null;    }}
           

设置响应头过滤器(ResponseHeaderFilter)

import com.netflix.zuul.ZuulFilter;import com.netflix.zuul.context.RequestContext;import com.netflix.zuul.exception.ZuulException;import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants;import org.springframework.stereotype.Component;import javax.servlet.http.HttpServletResponse;@Componentpublic class ResponseHeaderFilter extends ZuulFilter {    @Override    public String filterType() {        return FilterConstants.POST_TYPE;    }    @Override    public int filterOrder() {        return FilterConstants.SEND_RESPONSE_FILTER_ORDER-1;    }    @Override    public boolean shouldFilter() {        return true;    }    @Override    public Object run() throws ZuulException {        RequestContext requestContext=RequestContext.getCurrentContext();        HttpServletResponse response=requestContext.getResponse();        response.addHeader("response-header","response-header");        return null;    }}
           

用户名、密码、token信息参数均在前置类型过滤器,在ServletDetectionFilter前执行校验操作,校验通过后交由Zuul内置过滤器在前置类型过滤器进行处理。前置类型过滤器处理完成,通过路由类型过滤器路由到post过滤器,设置响应头过滤器为post类型过滤器,在正常处理响应请求(SendResponseFilter)前执行。

路由配置规则

默认路由访问规则

http://服务网关应用名称:服务网关应用端口/服务提供者实例名称
           

自定义访问URL

zuul.routes.服务实例ID=URL
           

如zuul.routes.user-service=/user/**就会将user-service微服务映射到/user/**访问路径。

忽略指定微服务

zuul.ignored-services=服务提供者实例名称
           

通过上述配置,可以配置需要忽略的服务实例,多个服务实例间用逗号(,)间隔

忽略所有微服务,只路由指定微服务

zuul.ignored-services=*zuul.routes.user-service=/user/**
           

通过上述配置,可以配置忽略所有服务实例,只路由到user-service实例

定义服务实例ID和对应路径

zuul.routes.user-router.service-id=user-service-providerzuul.routes.user-router.path=/user/**
           

通过上述配置,同时指定用户微服务实例ID为user-service-provider,对应路径为/user/**,user-router只是给路由一个有意义的名称,可以任意起名。

同时指定path和URL

zuul.routes.user-route.url=http://localhost:8100/user-servicezuul.routes.user-route.path=/user/**
           

通过上述配置,可以将访问地址为/user/**的路由到用户服务实例(localhost:8100为服务注册中心地址,user-service为服务提供者配置的服务实例名称)。

使用上述配置方式,不会使HystrixCommand执行,也不会使Ribbon负载均衡多个URL,破坏了Zuul的服务熔断和负载均衡特性。

同时指定path和URL并且不破坏服务熔断(Hystrix)负载均衡(Ribbon)特性

zuul.routes.user-route.service-id=user-service-providerzuul.routes.user-route.path=/user/**ribbon.eureka.enabled=false #为Ribbon禁用Eurekauser-service-provider.ribbon.listOfServers=localhost:8100,localhost:8101
           

通过上述配置,即可以指定path和URL,又保证Zuul的服务熔断和负载均衡特性可用。配置负载均衡的服务实例名须与服务实例ID(service-id)配置内容一致。

路由前缀

zuul.prefix=/apizuul.routes.user-router=/user/**#zuul.routes.user-router.strip-prefix=true#zuul.strip-prefix=false
           

通过上述配置,路由转发至用户微服务实例时去掉/api前缀;如果需要路由转发时带上路由前缀,通过zuul.routes.user-service-provider.strip-prefix=true进行指定路由移除代理前置配置即可(user-router只是为服务路由起了一个有意义的名字,可以任意起名),也可以通过zuul.strip-prefix=false对所有服务路由。

忽略某些路径

在前面的路由配置规则中,提到了如何忽略微服务,有时我们还需要更细粒度的路由控制,例如想让zuul路由到用户微服务实例,又想保护用户微服务的用户角色配置相关服务不被访问,可以进行如下配置:

zuul.ignoredPatterns:/**/RoleConfig/**zuul.routes.user-service-provider=/user/**
           

通过上述配置,可以讲用户微服务路由到/userRoleConfig/**)。

本地转发

在Zuul实现的API网关路由功能中,还支持forward形式的服务端转发配置。通过配置path和url相关信息就能够实现本地转发。

zuul.routes.user-route.url=http://localhost:8100/zuul.routes.user-route.path=/user/**zuul.routes.order-route.url=forward:/order-servicezuul.routes.user-route.path=/order/**
           

上述配置中,/user/**请求被转发到http://localhost:8100/,/order/**请求被转发到/order-service进行处理。在配置本地转发后,需要在服务提供者应用中提供请求路径为/order-service对应的接口进行服务调用处理,否则会因为本地转发无法找到对应处理接口而返回404错误。

继续阅读