【springCloud基础篇-16】Spring Cloud Zuul之自定义Filter及路由熔断及路由重试

接上篇文章:https://blog.csdn.net/qq_33333654/article/details/103269348

参考文献:http://www.ityouknow.com/springcloud/2018/01/20/spring-cloud-zuul.html

demo代码地址:https://download.csdn.net/download/qq_33333654/12014918

 

Zuul的核心

Filter是Zuul的核心,用来实现对外服务的控制。Filter的生命周期有4个,分别是“PRE”、“ROUTING”、“POST”、“ERROR”,整个生命周期可以用下图来表示。

Zuul大部分功能都是通过过滤器来实现的,这些过滤器类型对应于请求的典型生命周期。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。
  • POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。
  • ERROR:在其他阶段发生错误时执行该过滤器。 除了默认的过滤器类型,Zuul还允许我们创建自定义的过滤器类型。例如,我们可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。

Zuul中默认实现的Filter

类型顺序过滤器功能
pre-3ServletDetectionFilter标记处理Servlet的类型
pre-2Servlet30WrapperFilter包装HttpServletRequest请求
pre-1FormBodyWrapperFilter包装请求体
route1DebugFilter标记调试标志
route5PreDecorationFilter处理请求上下文供后续使用
route10RibbonRoutingFilterserviceId请求转发
route100SimpleHostRoutingFilterurl请求转发
route500SendForwardFilterforward请求转发
post0SendErrorFilter处理有错误的请求响应
post1000SendResponseFilter处理正常的请求响应

禁用指定的Filter

可以在application.yml中配置需要禁用的filter,格式:

zuul:
	FormBodyWrapperFilter:
		pre:
			disable: true

自定义Filter

在zuul项目中创建MyFilter继承ZuulFilter

package com.example.gatewayservicezuulsimple.zuulFilter;

import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;

/**
 * @ProjectName: gateway-service-zuul-simple
 * @Package: com.example.gatewayservicezuulsimple.zuulFilter
 * @ClassName: MyFilter
 * @Author: MC
 * @Description: ${description}
 * @Date: 2019/12/4 0004 15:17
 * @Version: 1.0
 */
public class MyFilter extends ZuulFilter {
    private final Logger logger = LoggerFactory.getLogger(MyFilter.class);
    @Override
    public String filterType() {
        return "pre"; //定义filter的类型,有pre、route、post、error四种
    }

    @Override
    public int filterOrder() {
        return 0; //定义filter的顺序,数字越小表示顺序越高,越先执行
    }

    @Override
    public boolean shouldFilter() {
        return true; //表示是否需要执行该filter,true表示执行,false表示不执行
    }

    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();

        logger.info("--->>> TokenFilter {},{}", request.getMethod(), request.getRequestURL().toString());

        String token = request.getParameter("token");// 获取请求的参数

        if (StringUtils.isNotBlank(token)) {
            ctx.setSendZuulResponse(true); //对请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);
            return null;
        } else {
            ctx.setSendZuulResponse(false); //不对其进行路由
            ctx.setResponseStatusCode(400);
            ctx.setResponseBody("token is empty");
            ctx.set("isSuccess", false);
            return null;
        }
    }
} 

zuul项目yml文件调整: 

zuul:
  routes:
    api-a:
      path: /client/**
      serviceId: spring-cloud-config-client

 

zuul项目启动类添加:

@SpringBootApplication
@EnableZuulProxy
public class GatewayServiceZuulSimpleApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayServiceZuulSimpleApplication.class, args);
    }


    @Bean
    public MyFilter myFilter(){
        return new MyFilter();
    }
}

启动:注册中心、rabbitmq、server项目、client项目、zuul项目

浏览器访问:http://localhost:8888/client/hello

返回的是:

token is empty

再次请求:http://localhost:8888/client/hello?token=xxx

返回的是:

hello_i_im_mysql_update1

 

路由熔断

zuul项目创建myFallback类:

package com.example.gatewayservicezuulsimple.fallback;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.netflix.zuul.filters.route.ZuulFallbackProvider;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * @ProjectName: spring-cloud-config-server
 * @Package: fallback
 * @ClassName: myFallback
 * @Author: MC
 * @Description: ${description}
 * @Date: 2019/12/4 0004 15:38
 * @Version: 1.0
 */
@Component
public class myFallback implements ZuulFallbackProvider {
    private final Logger logger = LoggerFactory.getLogger(myFallback.class);
    //指定要处理的 service。
    @Override
    public String getRoute() {
        return "spring-cloud-config-client";
    }

    @Override
    public ClientHttpResponse fallbackResponse() {

        return new ClientHttpResponse(){
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return HttpStatus.OK;
            }

            @Override
            public int getRawStatusCode() throws IOException {
                return 200;
            }

            @Override
            public String getStatusText() throws IOException {
                return "OK";
            }

            @Override
            public void close() {

            }

            @Override
            public InputStream getBody() throws IOException {
                return new ByteArrayInputStream("The service is unavailable.".getBytes());
            }

            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

 关闭zuul项目、server项目、client项目,打开client32项目(另外一个clent项目,内容与client项目一样)。

依次启动server项目、client项目、client32项目、zuul项目。

浏览器访问:http://localhost:8888/client/hello?token=xxx

获取信息同上,一切正常。

然后手动关闭client32项目,浏览器再多次请求http://localhost:8888/client/hello?token=xxx

发现输出 

The service is unavailable.

 

路由重试

 关闭zuul项目、server项目、client项目、client32项目

分别在client项目与client32项目中创建:

@RequestMapping("/testZuul")
public String testZuul() {
    return "is 9031 client";
}

 

 

@RequestMapping("/testZuul")
public String testZuul() {
    System.out.println("请求了32的client。。。。。");
    try{
        Thread.sleep(1000000);
    }catch ( Exception e){
        System.out.println(e);
    }
    return "is 9032 client";
}

 在zuul项目的pom中添加核心依赖:

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

 

zuul项目yml文件调整:

server:
  port: 8888
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8000/eureka/,http://peer2:8001/eureka/,http://peer3:8002/eureka/
spring:
  application:
    name: gateway-service-zuul
#  cloud:
#    loadbalancer:
#      retry:
#        enabled: true # 开启重试
zuul:
  routes:
    api-a:
      path: /client/**
      serviceId: spring-cloud-config-client
  retryable: true # 开启重试
ribbon:
#  connectTimeout: 2000 # 请求连接的超时时间
#  readTimeout: 5000 # 请求处理的超时时间
  maxAutoRetries: 2 # 对当前实例的重试次数
  maxAutoRetriesNextServer: 0 # 切换实例的重试次数
#  okToRetryOnAllOperations: false # 对所有操作请求都进行重试



#    hello:
#      path: /hello/**
#      url: http://localhost:9031/

#    baidu:
#      path: /it/**
#      url: https://blog.csdn.net/qq_33333654/category_9436879.html

 

 

依次启动server项目、client项目、client32项目、zuul项目。

 

浏览器访问:

http://localhost:8888/client/testZuul?token=xxx

 

放回的是

hello_i_im_mysql_update1

手动关闭client项目,再次访问http://localhost:8888/client/testZuul?token=xxx

浏览器返回:

The service is unavailable.

查看client32项目输出:请求了32的client。。。。。

 

Zuul高可用

我们实际使用Zuul的方式如上图,不同的客户端使用不同的负载将请求分发到后端的Zuul,Zuul在通过Eureka调用后端服务,最后对外输出。因此为了保证Zuul的高可用性,前端可以同时启动多个Zuul实例进行负载,在Zuul的前端使用Nginx或者F5进行负载转发以达到高可用性。

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冒菜-码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值