SpringCloud微服务 之Zuul(二)

本文深入探讨Zuul路由网关的过滤器机制,包括预过滤、路由、后置及错误处理过滤器的使用。通过实例展示如何实现身份验证、请求路由、响应修改及异常处理等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

Zuul的基本使用FallBack+Filter。

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

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

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

  • 禁用指定的Filter

    zuul:
    	FormBodyWrapperFilter:
    		pre:
    			disable: true
    

案例

  • Eureka Server端编写(参考前例)。
    在这里插入图片描述

  • Eureka Client端服务调用方编写。

    • 项目结构
      在这里插入图片描述

    • CoreCode

      <?xml version="1.0" encoding="UTF-8"?>
      <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
      	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      	<modelVersion>4.0.0</modelVersion>
      
      	<artifactId>microservice-deal-gateway-zuul-filter</artifactId>
      	<packaging>jar</packaging>
      
      	<name>microservice-deal-gateway-zuul-filter</name>
      	<description>Demo project for Spring Boot</description>
      
      	<parent>
      	<groupId>com.example</groupId>
      	<artifactId>microservice-deal-parent</artifactId>
      	<version>0.0.1-SNAPSHOT</version>
      	</parent>
      
      	<dependencies>
      		<!-- Zuul -->
      		<dependency>
                  <groupId>org.springframework.cloud</groupId>
                  <artifactId>spring-cloud-starter-netflix-zuul</artifactId>
              </dependency>
      	</dependencies>
      
      </project>
      
      
      @Component
      public class MyFallbackProvider implements FallbackProvider {
      	@Override
      	public String getRoute() {
      		// 表明是为哪个微服务提供回退,*表示为所有微服务提供回退
      		return "*";
      	}
      
      	@Override
      	public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
      		return new ClientHttpResponse() {
      			@Override
      			public HttpStatus getStatusCode() throws IOException {
      				return HttpStatus.OK; // 请求网关成功了,所以是ok
      			}
      
      			@Override
      			public int getRawStatusCode() throws IOException {
      				return HttpStatus.OK.value();
      			}
      
      			@Override
      			public String getStatusText() throws IOException {
      				return HttpStatus.OK.getReasonPhrase();
      			}
      
      			@Override
      			public void close() {
      
      			}
      
      			@Override
      			public InputStream getBody() throws IOException {
      				JSONObject json = new JSONObject();
      				json.put("state", "501");
      				json.put("msg", "后台接口错误");
      				return new ByteArrayInputStream(json.toJSONString().getBytes("UTF-8")); // 返回前端的内容
      			}
      
      			@Override
      			public HttpHeaders getHeaders() {
      				HttpHeaders httpHeaders = new HttpHeaders();
      				httpHeaders.setContentType(MediaType.APPLICATION_JSON_UTF8); // 设置头
      				return httpHeaders;
      			}
      		};
      	}
      }
      
      @Component
      public class ErrorFilter extends ZuulFilter {
      
      	private static Logger log = LoggerFactory.getLogger(ErrorFilter.class);
      
      	@Override
      	public String filterType() {
      		// 异常过滤器
      		return "error";
      	}
      
      	@Override
      	public int filterOrder() {
      		// 优先级,数字越大,优先级越低
      		return 0;
      	}
      
      	@Override
      	public boolean shouldFilter() {
      		// 是否执行该过滤器,true代表需要过滤
      		return true;
      	}
      
      	@Override
          public Object run() {
              RequestContext ctx = RequestContext.getCurrentContext();
       
              log.info("进入异常过滤器");
       
              System.out.println(ctx.getResponseBody());
       
              ctx.setResponseBody("出现异常");
       
              return null;
       
          }
      
      @Component
      public class AccessUserNameFilter extends ZuulFilter {
      	@Override
      	public Object run() {
      		RequestContext ctx = RequestContext.getCurrentContext();
      		HttpServletRequest request = ctx.getRequest();
      
      		System.out.println(String.format("%s AccessUserNameFilter request to %s", request.getMethod(),
      				request.getRequestURL().toString()));
      
      		String username = request.getParameter("username");// 获取请求的参数
      		if (null != username && username.equals("Dustyone")) {// 如果请求的参数不为空,且值为chhliu时,则通过
      			ctx.setSendZuulResponse(true);// 对该请求进行路由
      			ctx.setResponseStatusCode(200);
      			ctx.set("isSuccess", true);// 设值,让下一个Filter看到上一个Filter的状态
      			return null;
      		} else {
      			ctx.setSendZuulResponse(false);// 过滤该请求,不对其进行路由
      			ctx.setResponseStatusCode(401);// 返回错误码
      			ctx.setResponseBody("{\"result\":\"username is not correct!\"}");// 返回错误内容
      			ctx.set("isSuccess", false);
      			return null;
      		}
      	}
      
      	@Override
      	public boolean shouldFilter() {
      		return true;// 是否执行该过滤器,此处为true,说明需要过滤
      	}
      
      	@Override
      	public int filterOrder() {
      		return 0;// 优先级为0,数字越大,优先级越低
      	}
      
      	@Override
      	public String filterType() {
      		return "pre";// 前置过滤器
      	}
      
      @Component
      public class AccessPasswordFilter extends ZuulFilter {
      
      	@Override
      	public Object run() {
      		RequestContext ctx = RequestContext.getCurrentContext();
      		HttpServletRequest request = ctx.getRequest();
      
      		System.out.println(String.format("%s AccessPasswordFilter request to %s", request.getMethod(),
      				request.getRequestURL().toString()));
      
      		String username = request.getParameter("password");
      		if (null != username && username.equals("123456")) {
      			ctx.setSendZuulResponse(true);
      			ctx.setResponseStatusCode(200);
      			ctx.set("isSuccess", true);
      			return null;
      		} else {
      			ctx.setSendZuulResponse(false);
      			ctx.setResponseStatusCode(401);
      			ctx.setResponseBody("{\"result\":\"password is not correct!\"}");
      			ctx.set("isSuccess", false);
      			return null;
      		}
      	}
      
      	@Override
      	public boolean shouldFilter() {
      		RequestContext ctx = RequestContext.getCurrentContext();
      		return (boolean) ctx.get("isSuccess");// 如果前一个过滤器的结果为true,则说明上一个过滤器成功了,需要进入当前的过滤,如果前一个过滤器的结果为false,则说明上一个过滤器没有成功,则无需进行下面的过滤动作了,直接跳过后面的所有过滤器并返回结果
      	}
      
      	@Override
      	public int filterOrder() {
      		return 1; // 优先级设置为1
      	}
      
      	@Override
      	public String filterType() {
      		return "pre";
      	}
      
      public class AccessTokenFilter extends ZuulFilter {
      	@Override
      	public Object run() {
      		RequestContext ctx = RequestContext.getCurrentContext();
      		HttpServletRequest request = ctx.getRequest();
      
      		System.out.println(String.format("%s AccessTokenFilter request to %s", request.getMethod(),
      				request.getRequestURL().toString()));
      
      		ctx.setSendZuulResponse(true);
      		ctx.setResponseStatusCode(200);
      		ctx.setResponseBody("{\"name\":\"Dustyone\"}");// 输出最终结果
      		return null;
      	}
      
      	@Override
      	public boolean shouldFilter() {
      		return true;
      	}
      
      	@Override
      	public int filterOrder() {
      		return 0;
      	}
      
      	@Override
      	public String filterType() {
      		return "post";// 在请求被处理之后,会进入该过滤器
      	}
      
      @SpringBootApplication
      @EnableDiscoveryClient
      @EnableZuulProxy
      public class MicroserviceDealGatewayZuulFilterApplication {
      	public static void main(String[] args) {
      		SpringApplication.run(MicroserviceDealGatewayZuulFilterApplication.class, args);
      	}
      
      	/**
      	 * 该示例是将userservice-v1映射到/v1/uservice/中,常用于版本管理中,如APP端调用的API带有版本信息(服务-版本),
      	 * Zuul为这些不同版本的微服务应用生成以版本代号作为路由前缀定义的路由规则。
      	 * 通过具有版本号前缀的URL路径,可以很容易通过路径表达式来归类和管理这些具有版本信息的微服务
      	 * @return
      	 */
      	@Bean
      	public PatternServiceRouteMapper serviceRouteMapper() {
      		// 调用构造函数PatternServiceRouteMapper(String servicePattern, String
      		// routePattern)
      		// servicePattern指定微服务的正则
      		// routePattern指定路由正则
      		return new PatternServiceRouteMapper("(?<name>^.+)-(?<version>v.+$)", "${version}/${name}");
      	}
      
      server:
        port: 8040
      spring:
        application:
          name: microservice-deal-gateway-zuul-fallback
      eureka:
        client:
          service-url:
            #defaultZone: http://localhost:8080/eureka/
            defaultZone: http://Dustyone:bai5331359@localhost:8080/eureka/
        instance:
          prefer-ip-address: true
      #zuul:routes:deal-route: path: /deal/** service-id: microservice-deal-cloud
      # 这样访问Zuul的/deal/1路径,请求将会被转发到microservice-deal-cloud的/deal/1,可查看日志打印,有助于理解。
      zuul:
        routes:
          microservice-deal-cloud: 
            path: /api-deal/**
            strip-prefix: true
      
      #设置feign的hystrix响应超时时间(必须)
      hystrix:
        command:
            default:
              execution:
                isolation:
                  thread:
                    timeoutInMilliseconds: 5000
          
      ribbon:
        eureka:
          enabled: true    # 禁用掉ribbon的eureka使用。详见:http://cloud.spring.io/spring-cloud-static/Camden.SR3/#_example_disable_eureka_use_in_ribbon
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule #Ribbon均衡负载策略
        
      management:
        security:
          enabled: false
          
      
  • Eureka Client端服务提供方编写

  • 访问:http://localhost:8040/api-deal/deal/1
    在这里插入图片描述

  • 访问:http://localhost:8040/api-deal/deal/1?username=Dustyone
    在这里插入图片描述

  • 访问:
    在这里插入图片描述

  • Hystrix熔断机制:

    @GetMapping("/deal/{id}")
    	public Deal findById(@PathVariable Integer id) throws InterruptedException {
    		Thread.sleep(3000);
    		Deal deal = new  Deal(id, "Dustyone", "Heyt", 22, 18,port);
    		return deal;
    	}
    
  • 重启microservice-deal-cloud服务节点,访问:http://localhost:8040/api-deal/deal/1?username=Dustyone&password=123456:
    在这里插入图片描述

小结

  • 本案小节使用的案例:microservice-deal-eureka-authentication、microservice-deal-cloud、microservice-deal-gateway-zuul-filter。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值