关于java:非常全面的讲解SpringCloud中Zuul网关原理及其配置看它就够了

41次阅读

共计 10999 个字符,预计需要花费 28 分钟才能阅读完成。

Zuul 是 spring cloud 中的微服务网关。网关:是一个网络整体零碎中的前置门户入口。申请首先通过网关,进行门路的路由,定位到具体的服务节点上。

Zuul 是一个微服务网关,首先是一个微服务。也是会在 Eureka 注册核心中进行服务的注册和发现。也是一个网关,申请应该通过 Zuul 来进行路由。

Zuul 网关不是必要的。是举荐应用的。

应用 Zuul,个别在微服务数量较多(多于 10 个)的时候举荐应用,对服务的治理有严格要求的时候举荐应用,当微服务权限要求严格的时候举荐应用。

一、Zuul 网关的作用

网关有以下几个作用:

  • 对立入口:未全副为服务提供一个惟一的入口,网关起到内部和外部隔离的作用,保障了后盾服务的安全性。
  • 鉴权校验:辨认每个申请的权限,回绝不符合要求的申请。
  • 动静路由:动静的将申请路由到不同的后端集群中。
  • 缩小客户端与服务端的耦合:服务能够独立倒退,通过网关层来做映射。

二、Zuul 网关的利用

  • 1、网关拜访形式

通过 zuul 拜访服务的,URL 地址默认格局为:http://zuulHostIp:port/ 要拜访的服务名称 / 服务中的 URL

服务名称:properties 配置文件中的 spring.application.name。

服务的 URL:就是对应的服务对外提供的 URL 门路监听。

  • 2、网关依赖注入
<!-- spring cloud Eureka Client 启动器 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<!-- zuul 网关的重试机制,不是应用 ribbon 内置的重试机制
   是借助 spring-retry 组件实现的重试
   开启 zuul 网关重试机制须要减少下述依赖
 -->
<dependency>
   <groupId>org.springframework.retry</groupId>
   <artifactId>spring-retry</artifactId>
</dependency>
  • 3、网关启动器
/**
 * @EnableZuulProxy - 开启 Zuul 网关。*  以后利用是一个 Zuul 微服务网关。会在 Eureka 注册核心中注册以后服务。并发现其余的服务。*  Zuul 须要的必要依赖是 spring-cloud-starter-zuul。*/
@SpringBootApplication
@EnableZuulProxy
public class ZuulApplication {public static void main(String[] args) {SpringApplication.run(ZuulApplication.class, args);
    }
}
  • 4、网关全局变量配置

4.1 URL 门路匹配

# URL pattern
# 应用门路形式匹配路由规定。# 参数 key 构造:zuul.routes.customName.path=xxx
# 用于配置门路匹配规定。# 其中 customName 自定义。通常应用要调用的服务名称,不便前期治理
# 可应用的通配符有:* ** ?
# ? 单个字符
# * 任意多个字符,不蕴含多级门路
# ** 任意多个字符,蕴含多级门路
zuul.routes.eureka-application-service.path=/api/**
# 参数 key 构造:zuul.routes.customName.url=xxx
# url 用于配置合乎 path 的申请门路路由到的服务地址。zuul.routes.eureka-application-service.url=http://127.0.0.1:8080/

4.2 服务名称匹配

# service id pattern 通过服务名称路由
# key 构造:zuul.routes.customName.path=xxx
# 门路匹配规定
zuul.routes.eureka-application-service.path=/api/**
# key 构造:zuul.routes.customName.serviceId=xxx
# serviceId 用于配置合乎 path 的申请门路路由到的服务名称。zuul.routes.eureka-application-service.serviceId=eureka-application-service

服务名称匹配也能够应用简化的配置:

# simple service id pattern 简化配置计划
# 如果只配置 path,不配置 serviceId。则 customName 相当于服务名称。# 合乎 path 的申请门路间接路由到 customName 对应的服务上。zuul.routes.eureka-application-service.path=/api/**

4.3 路由排除配置

# ignored service id pattern
# 配置不被 zuul 治理的服务列表。多个服务名称应用逗号 ',' 分隔。# 配置的服务将不被 zuul 代理。zuul.ignored-services=eureka-application-service
# 此形式相当于给所有新发现的服务默认排除 zuul 网关拜访形式,只有配置了路由网关的服务才能够通过 zuul 网关拜访
# 通配形式配置排除列表。zuul.ignored-services=*
# 应用服务名称匹配规定配置路由列表,相当于只对已配置的服务提供网关代理。zuul.routes.eureka-application-service.path=/api/**

# 通配形式配置排除网关代理门路。所有合乎 ignored-patterns 的申请门路都不被 zuul 网关代理。zuul.ignored-patterns=/**/test/**
zuul.routes.eureka-application-service.path=/api/**

4.4 路由前缀配置

# prefix URL pattern 前缀路由匹配
# 配置申请门路前缀,所有基于此前缀的申请都由 zuul 网关提供代理。zuul.prefix=/api
# 应用服务名称匹配形式配置申请门路规定。# 这里的配置将为:http://ip:port/api/appservice/** 的申请提供 zuul 网关代理,能够将要拜访服务进行前缀分类。# 并将申请路由到服务 eureka-application-service 中。zuul.routes.eureka-application-service.path=/appservice/**
  • 5 Zuul 网关配置总结

网关配置形式有多种,默认、URL、服务名称、排除 | 疏忽、前缀。

网关配置没有优劣好坏,应该在不同的状况下抉择适合的配置计划。

zuul 网关其底层应用 ribbon 来实现申请的路由,并内置 Hystrix,可选择性提供网关 fallback 逻辑。应用 zuul 的时候,并不举荐应用 Feign 作为 application client 端的开发实现。毕竟 Feign 技术是对 ribbon 的再封装,应用 Feign 自身会进步通信耗费,升高通信效率,只在服务互相调用的时候应用 Feign 来简化代码开发就够了。而且商业开发中,应用 Ribbon+RestTemplate 来开发的比例更高。

三、Zuul 网关过滤器

Zuul 中提供了过滤器定义,能够用来过滤代理申请,提供额定性能逻辑。如:权限验证,日志记录等。

Zuul 提供的过滤器是一个父类。父类是 ZuulFilter。通过父类中定义的形象办法 filterType,来决定以后的 Filter 品种是什么。有前置过滤、路由后过滤、后置过滤、异样过滤。

  • 前置过滤:是申请进入 Zuul 之后,立即执行的过滤逻辑。
  • 路由后过滤:是申请进入 Zuul 之后,并 Zuul 实现了申请路由后执行的过滤逻辑,路由后过滤,是在近程服务调用之前过滤的逻辑。
  • 后置过滤:近程服务调用完结后执行的过滤逻辑。
  • 异样过滤:是任意一个过滤器产生异样或近程服务调用无后果反馈的时候执行的过滤逻辑。无后果反馈,就是近程服务调用超时。

3.1 过滤器实现形式

继承父类 ZuulFilter。在父类中提供了 4 个形象办法,别离是:filterType, filterOrder, shouldFilter, run。其性能别离是:

filterType:办法返回字符串数据,代表以后过滤器的类型。可选值有 -pre, route, post, error。

  • pre – 前置过滤器,在申请被路由前执行,通常用于解决身份认证,日志记录等;
  • route – 在路由执行后,服务调用前被调用;
  • error – 任意一个 filter 产生异样的时候执行或近程服务调用没有反馈的时候执行(超时),通常用于解决异样;
  • post – 在 route 或 error 执行后被调用,个别用于收集服务信息,统计服务性能指标等,也能够对 response 后果做非凡解决。

filterOrder:返回 int 数据,用于为同 filterType 的多个过滤器定制执行程序,返回值越小,执行程序越优先。

shouldFilter:返回 boolean 数据,代表以后 filter 是否失效。

run:具体的过滤执行逻辑。如 pre 类型的过滤器,能够通过对申请的验证来决定是否将申请路由到服务上;如 post 类型的过滤器,能够对服务响应后果做加工解决(如为每个响应减少 footer 数据)。Java 知音公众号内回复“后端面试”,送你一份 Java 面试题宝典

3.2 过滤器的生命周期

3.3 代码示例

/**
 * Zuul 过滤器,必须继承 ZuulFilter 父类。* 以后类型的对象必须交由 Spring 容器治理。应用 @Component 注解形容。* 继承父类后,必须实现父类中定义的 4 个形象办法。* shouldFilter、run、filterType、filterOrder
 */
@Component
public class LoggerFilter extends ZuulFilter {private static final Logger logger = LoggerFactory.getLogger(LoggerFilter.class);
    
    /**
     * 返回 boolean 类型。代表以后 filter 是否失效。* 默认值为 false。* 返回 true 代表开启 filter。*/
    @Override
    public boolean shouldFilter() {return true;}

    /**
     * run 办法就是过滤器的具体逻辑。* return 能够返回任意的对象,以后实现疏忽。(spring-cloud-zuul 官网解释)* 间接返回 null 即可。*/
    @Override
    public Object run() throws ZuulException {
        // 通过 zuul,获取申请上下文
        RequestContext rc = RequestContext.getCurrentContext();
        HttpServletRequest request = rc.getRequest();

        logger.info("LogFilter1.....method={},url={}",
                request.getMethod(),request.getRequestURL().toString());
        // 能够记录日志、鉴权,给保护人员记录提供定位帮助、统计性能
        return null;
    }

    /**
     * 过滤器的类型。可选值有:* pre - 前置过滤
     * route - 路由后过滤
     * error - 异样过滤
     * post - 近程服务调用后过滤
     */
    @Override
    public String filterType() {return "pre";}

    /**
     * 同品种的过滤器的执行程序。* 依照返回值的天然升序执行。*/
    @Override
    public int filterOrder() {return 0;}
}

四、Zuul 网关的容错(与 Hystrix 的无缝联合)

在 spring cloud 中,Zuul 启动器中蕴含了 Hystrix 相干依赖,在 Zuul 网关工程中,默认是提供了 Hystrix Dashboard 服务监控数据的(hystrix.stream),然而不会提供监控面板的界面展现。能够说,在 spring cloud 中,zuul 和 Hystrix 是无缝联合的。

4.1 Zuul 中的服务降级解决

在 Edgware 版本之前,Zuul 提供了接口 ZuulFallbackProvider 用于实现 fallback 解决。从 Edgware 版本开始,Zuul 提供了 ZuulFallbackProvider 的子接口 FallbackProvider 来提供 fallback 解决。

Zuul 的 fallback 容错解决逻辑,只针对 timeout 异样解决,当申请被 Zuul 路由后,只有服务有返回(包含异样),都不会触发 Zuul 的 fallback 容错逻辑。

因为对于 Zuul 网关来说,做申请路由散发的时候,后果由近程服务运算的。那么近程服务反馈了异样信息,Zuul 网关不会解决异样,因为无奈确定这个谬误是否是利用实在想要反馈给客户端的。Java 知音公众号内回复“后端面试”,送你一份 Java 面试题宝典

4.2 代码示例

/**
 * 如果须要在 Zuul 网关服务中减少容错解决 fallback,须要实现接口 ZuulFallbackProvider
 * spring-cloud 框架,在 Edgware 版本 (包含) 之后,申明接口 ZuulFallbackProvider 过期生效,* 提供了新的 ZuulFallbackProvider 的子接口 - FallbackProvider
 * 在老版本中提供的 ZuulFallbackProvider 中,定义了两个办法。*  - String getRoute()
 *    以后的 fallback 容错解决逻辑解决的是哪一个服务。能够应用通配符‘*’代表为全副的服务提供容错解决。*    如果只为某一个服务提供容错,返回对应服务的 spring.application.name 值。*  - ClientHttpResponse fallbackResponse()
 *    当服务产生谬误的时候,如何容错。* 新版本中提供的 FallbackProvider 提供了新的办法。*  - ClientHttpResponse fallbackResponse(Throwable cause)
 *    如果应用新版本中定义的接口来做容错解决,容错解决逻辑,只运行子接口中定义的新办法。也就是有参办法。*    是为近程服务产生异样的时候,通过异样的类型来运行不同的容错逻辑。*/
@Component
public class TestFallBbackProvider implements FallbackProvider {

    /**
     * return - 返回 fallback 解决哪一个服务。返回的是服务的名称
     * 举荐 - 为指定的服务定义个性化的 fallback 逻辑。* 举荐 - 提供一个解决所有服务的 fallback 逻辑。* 益处 - 服务某个服务产生超时,那么指定的 fallback 逻辑执行。如果有新服务上线,未提供 fallback 逻辑,有一个通用的。*/
    @Override
    public String getRoute() {return "eureka-application-service";}

    /**
     * fallback 逻辑。在晚期版本中应用。* Edgware 版本之后,ZuulFallbackProvider 接口过期,提供了新的子接口 FallbackProvider
     * 子接口中提供了办法 ClientHttpResponse fallbackResponse(Throwable cause)。* 优先调用子接口新定义的 fallback 解决逻辑。*/
    @Override
    public ClientHttpResponse fallbackResponse() {System.out.println("ClientHttpResponse fallbackResponse()");
        
        List<Map<String, Object>> result = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        data.put("message", "服务正忙,请稍后重试");
        result.add(data);
        
        ObjectMapper mapper = new ObjectMapper();
        
        String msg = "";
        try {msg = mapper.writeValueAsString(result);
        } catch (JsonProcessingException e) {msg = "";}
        
        return this.executeFallback(HttpStatus.OK, msg, 
                "application", "json", "utf-8");
    }

    /**
     * fallback 逻辑。优先调用。能够依据异样类型动静决定解决形式。*/
    @Override
    public ClientHttpResponse fallbackResponse(Throwable cause) {System.out.println("ClientHttpResponse fallbackResponse(Throwable cause)");
        if(cause instanceof NullPointerException){List<Map<String, Object>> result = new ArrayList<>();
            Map<String, Object> data = new HashMap<>();
            data.put("message", "网关超时,请稍后重试");
            result.add(data);
            
            ObjectMapper mapper = new ObjectMapper();
            
            String msg = "";
            try {msg = mapper.writeValueAsString(result);
            } catch (JsonProcessingException e) {msg = "";}
            
            return this.executeFallback(HttpStatus.GATEWAY_TIMEOUT, 
                    msg, "application", "json", "utf-8");
        }else{return this.fallbackResponse();
        }
    }
    
    /**
     * 具体处理过程。* @param status 容错解决后的返回状态,如 200 失常 GET 申请后果,201 失常 POST 申请后果,404 资源找不到谬误等。*  应用 spring 提供的枚举类型对象实现。HttpStatus
     * @param contentMsg 自定义的响应内容。就是反馈给客户端的数据。* @param mediaType 响应类型,是响应的主类型,如:application、text、media。* @param subMediaType 响应类型,是响应的子类型,如:json、stream、html、plain、jpeg、png 等。* @param charsetName 响应后果的字符集。这里只传递字符集名称,如:utf-8、gbk、big5 等。* @return ClientHttpResponse 就是响应的具体内容。*  相当于一个 HttpServletResponse。*/
    private final ClientHttpResponse executeFallback(final HttpStatus status, 
            String contentMsg, String mediaType, String subMediaType, String charsetName) {return new ClientHttpResponse() {

            /**
             * 设置响应的头信息
             */
            @Override
            public HttpHeaders getHeaders() {HttpHeaders header = new HttpHeaders();
                MediaType mt = new MediaType(mediaType, subMediaType, Charset.forName(charsetName));
                header.setContentType(mt);
                return header;
            }

            /**
             * 设置响应体
             * zuul 会将本办法返回的输出流数据读取,并通过 HttpServletResponse 的输入流输入到客户端。*/
            @Override
            public InputStream getBody() throws IOException {
                String content = contentMsg;
                return new ByteArrayInputStream(content.getBytes());
            }

            /**
             * ClientHttpResponse 的 fallback 的状态码 返回 String
             */
            @Override
            public String getStatusText() throws IOException {return this.getStatusCode().getReasonPhrase();}

            /**
             * ClientHttpResponse 的 fallback 的状态码 返回 HttpStatus
             */
            @Override
            public HttpStatus getStatusCode() throws IOException {return status;}

            /**
             * ClientHttpResponse 的 fallback 的状态码 返回 int
             */
            @Override
            public int getRawStatusCode() throws IOException {return this.getStatusCode().value();}

            /**
             * 回收资源办法
             * 用于回收以后 fallback 逻辑开启的资源对象的。* 不要敞开 getBody 办法返回的那个输出流对象。*/
            @Override
            public void close() {}
        };
    }
}

五、Zuul 网关的限流爱护

Zuul 网关组件也提供了限流爱护。当申请并发达到阀值,主动触发限流爱护,返回谬误后果。只有提供 error 错误处理机制即可。

Zuul 的限流爱护须要额定依赖 spring-cloud-zuul-ratelimit 组件。

<dependency>
    <groupId>com.marcosbarbero.cloud</groupId>
    <artifactId>spring-cloud-zuul-ratelimit</artifactId>
    <version>1.3.4.RELEASE</version>
</dependency>

5.1 全局限流配置

应用全局限流配置,zuul 会对代理的所有服务提供限流爱护。

# 开启限流爱护
zuul.ratelimit.enabled=true
# 60s 内申请超过 3 次,服务端就抛出异样,60s 后能够恢复正常申请
zuul.ratelimit.default-policy.limit=3
zuul.ratelimit.default-policy.refresh-interval=60
# 针对 IP 进行限流,不影响其余 IP
zuul.ratelimit.default-policy.type=origin

5.2 部分限流配置

应用部分限流配置,zuul 仅针对配置的服务提供限流爱护。

# 开启限流爱护
zuul.ratelimit.enabled=true
# hystrix-application-client 服务 60s 内申请超过 3 次,服务抛出异样。zuul.ratelimit.policies.hystrix-application-client.limit=3
zuul.ratelimit.policies.hystrix-application-client.refresh-interval=60
# 针对 IP 限流。zuul.ratelimit.policies.hystrix-application-client.type=origin

5.3 限流参数简介

六、Zuul 网关性能调优:网关的两层超时调优

应用 Zuul 的 spring cloud 微服务结构图:

从上图中能够看出。整体申请逻辑还是比较复杂的,在没有 zuul 网关的状况下,app client 申请 app service 的时候,也有申请超时的可能。那么当减少了 zuul 网关的时候,申请超时的可能就更显著了。

当申请通过 zuul 网关路由到服务,并期待服务返回响应,这个过程中 zuul 也有超时管制。zuul 的底层应用的是 Hystrix+ribbon 来实现申请路由。构造如下:

zuul 中的 Hystrix 外部应用线程池隔离机制提供申请路由实现,其默认的超时时长为 1000 毫秒。ribbon 底层默认超时时长为 5000 毫秒。如果 Hystrix 超时,间接返回超时异样。如果 ribbon 超时,同时 Hystrix 未超时,ribbon 会主动进行服务集群轮询重试,直到 Hystrix 超时为止。如果 Hystrix 超时时长小于 ribbon 超时时长,ribbon 不会进行服务集群轮询重试。

那么在 zuul 中可配置的超时时长就有两个地位:Hystrix 和 ribbon。具体配置如下:

# 开启 zuul 网关重试
zuul.retryable=true
# hystrix 超时工夫设置
# 线程池隔离,默认超时工夫 1000ms
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=8000

# ribbon 超时工夫设置:倡议设置比 Hystrix 小
# 申请连贯的超时工夫: 默认 5000ms
ribbon.ConnectTimeout=5000
# 申请解决的超时工夫: 默认 5000ms
ribbon.ReadTimeout=5000
# 重试次数:MaxAutoRetries 示意拜访服务集群下原节点(同门路拜访);MaxAutoRetriesNextServer 示意拜访服务集群下其余节点(换台服务器)ribbon.MaxAutoRetries=1
ribbon.MaxAutoRetriesNextServer=1
# 开启重试
ribbon.OkToRetryOnAllOperations=true

Spring-cloud 中的 zuul 网关重试机制是应用 spring-retry 实现的。工程必须依赖下述资源:

<dependency>
  <groupId>org.springframework.retry</groupId>
  <artifactId>spring-retry</artifactId>
</dependency>

写在最初

欢送大家关注我的公众号【惊涛骇浪如码】,海量 Java 相干文章,学习材料都会在外面更新,整顿的材料也会放在外面。

感觉写的还不错的就点个赞,加个关注呗!点关注,不迷路,继续更新!!!

正文完
 0