重新定义cloud 第17章 spring cloud gateway上篇

gateway 概述

  • 基于 spring 5.0

  • spring boot 2.0

  • project reactor

  • 提供简单,有效且统一, api路由 管理方式

  • 基于 filter 链的方式 提供了 网关基本的 功能

    • 安全
    • 监控/埋点
    • 限流
    • 协议适配
    • 协议转发
    • 安全策略 waf
    • 防刷
    • 流量
    • 监控日志
  • 网关的 核心 Filter 以及filter chain (filter 责任链)

  • 路由 route(包含)

    • id
    • 目的 url
    • 断言工厂
    • 一组filter
    • 断言为真,则 说明url和 配置的路由匹配
  • 断言 predicate

    • spring 5.0 的 serverWebExchange
    • 允许开发者 去定义匹配 来自于 http Request中的任何信息(请求头,参数)
  • 过滤器 filter

    • 一个标准的spring filter
    • gateWay filter
    • globbal filter
    • 过滤器 filter 将会 对 请求和响应 进行 修改处理。

工作原理

  • 默认端口,80,https 443

  • 启动容器只支持 netty

  • gateway client

  • spring cloud gateway

    • HttpWebHandlerAdapter 组装网关上下文
    • DispatcheerHandler 分发处理器,循环遍历 Mapping,获取 Handler
    • Route Predicate Handler Mapping 断言处理器,判断是否可用
    • 断言成功
    • Filtering Web Handler 创建过滤器,调用过滤链
      • pre filter
      • post filter
  • proxied service 代理服务器

入门案例

  • 协议适配

    • 路由信息转发
  • 协议转发

  • 本案例演示,Path路由断言工厂 实现 url 直接转发

xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--Spring Cloud Gateway的Starter-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>

主启动类

@SpringBootApplication
public class SpringCloudGatewayApplication {

	/**
	 * 基本的转发
	 * 当访问http://localhost:8080/jd
	 * 转发到http://jd.com
	 * @param builder
	 * @return
	 */
	@Bean // Locator n. 定位器,探测器
	public RouteLocator custom Route Locator(RouteLocatorBuilder builder) {
		return builder.routes()
				//basic proxy
				.route(r ->r.path("/jd")
						.uri("http://jd.com:80/").id("jd_route")
				).build();
	} //第一种配置

	public static void main(String[] args) {
		SpringApplication.run(SpringCloudGatewayApplication.class, args);
	}
}
  • 第二种配置。application.yml
spring:
  cloud:
    gateway:
      routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
        - id: baidu_route
          uri: http://baidu.com:80/
          predicates:
            - Path=/baidu

application.yml

server:
  port: 8080
  
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
        - id: baidu_route
          uri: http://baidu.com:80/
          predicates:
            - Path=/baidu

logging: ## Spring Cloud Gateway的日志配置
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG


management:
  endpoints:
    web:
      exposure:
        include: '*'
  security:
    enabled: false
trace 
英 /treɪs/  美 /treɪs/  
v. (通过调查)找到;追溯;追踪;沿(特定路径)走;映描;(尤指用手指、脚趾)画;勾画出轮廓
n. 痕迹;踏出来的小路;(大脑上的学习或记忆)痕迹;语迹;跟踪;微量;丝毫;描记线;交线;缰绳

开启端点

  • 如上配置

  • 提供了 gateway actuator

  • 关于 filter和 routes 信息查询,制定 route信息更新的 Rest API

  • http://localhost:8080/actuator/gateway/routes

    [{
    	"route_id": "jd_route", //jd的转发 断言是这个类
    	"route_object": {
    		"predicate": "org.springframework.cloud.gateway.support.ServerWebExchangeUtils$$Lambda$293/70528019@27137286"
    	},
    	"order": 0
    }, {
    	"route_id": "baidu_route",
    	"route_definition": {
    		"id": "baidu_route",
    		"predicaates": [{
    			"name": "Path",
    			"args": {
    				"_genkey_0": "/baidu" //本地的请求
    			}
    		}],
    		"filters": [],
    		"uri": "http://baidu.com:80/", //转发的路径
    		"order": 0
    	},
    	"order": 0
    }]
    

路由 断言

  • 路由匹配 是 spring webFlux的 Handler Mapping 为 基础实现的
  • 有许多的 路由断言 工厂
  • 路由断言工厂会根据配置的路由规则,对 请求进行 断言匹配
  • 匹配成功进行下一步处理,否则 断言失败,返回错误信息

after路由器断言工厂

  • after route predicate factory

  • 取UTC 时间格式 UTC一般指协调世界时。协调世界时,又称世界统一时间、世界标准时间、国际协调时间。

  • 当请求进来的当前时间 在配置的 UTF 时间之后,会匹配成功。否则失败。

  • 当前请求在,配置时间之后。才会成功、

路由配置代码

@SpringBootApplication
public class SCGatewayApplication {

	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		//生成比当前时间早一个小时的UTC时间
		ZonedDateTime minusTime = LocalDateTime.now().minusHours(1).atZone(ZoneId.systemDefault());
		return builder.routes()
				.route("after_route", r -> r.after(minusTime)
						.uri("http://baidu.com"))
				.build();
	}
	public static void main(String[] args) {
		SpringApplication.run(SCGatewayApplication.class, args);
	}
}

如果:.plusHours(1) 就会失败,因为当前请求的时间,在之前了。
  • 或者
spring:
  cloud:
    gateway:
      routes: #当访问http://localhost:8080/baidu,直接转发到https://www.baidu.com/
        - id: baidu_route
          uri: http://baidu.com:80/
          predicates:
            - Path=/baidu #要加百度的访问
            - After=2020-11-13T14:26:38.584+08:00[Asia/Shanghai]

UTC工具类

/**
 * 生成UTC时间
 */
public class UtcTimeUtil {

    public static void main(String[] args) {
        ZonedDateTime time=  ZonedDateTime.now();
        System.out.println("zonedDateTime:"+time);

        String  maxTime=ZonedDateTime.now().plusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
        System.out.println("maxTime:"+maxTime);
        //用这个会出现:Handler Terminated 处理程序终止 无法跳转

        String  minTime=ZonedDateTime.now().minusHours(1).format(DateTimeFormatter.ISO_ZONED_DATE_TIME);
        System.out.println("minTime:"+minTime);


        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        String str=time.format(formatter);
        System.out.println(str);
    }

}

测试

  • 如果使用类配置,直接访问:http://localhost:8081

  • 如果用类配置,不会配置请求http://localhost:8081/baidu

  • r.path("/baidu")
          .uri("http://baidu.com").id("aaa") 
    

before 路由器断言 工厂

  • 当请求进来的时间,在 路由器断言工厂之前 ,会匹配成功

    	@Bean
    	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    		//加一天,保证当前的请求,是在 之前的。
            //Handler Terminated(如果当前的请求在 之后)
    		ZonedDateTime datetime = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
    		return builder.routes()
    				.route("before_route", r -> r.before(datetime)
    						.uri("http://baidu.com"))
    
    				.build();
    	}
    
  • http://localhost:8080 会转发到baidu

Between断言工厂

代码控制
@SpringBootApplication
public class SCGatewayApplication {

	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {

		ZonedDateTime datetime1 = LocalDateTime.now().minusDays(1).atZone(ZoneId.systemDefault());
		ZonedDateTime datetime2 = LocalDateTime.now().plusDays(1).atZone(ZoneId.systemDefault());
		return builder.routes()
				.route("between_route", r -> r.between(datetime1,datetime2)
						.uri("http://baidu.com"))

				.build();
	}
	public static void main(String[] args) {
		SpringApplication.run(SCGatewayApplication.class, args);
	}
}
yaml控制
spring:
  cloud:
    gateway:
      routes:
      - id: between_route
        uri: http://xujin.org
        predicates:
        - name: Between
          args:
            datetime1: 2018-03-15T00:02:48.513+08:00[Asia/Shanghai]
            datetime2: 2018-03-15T02:02:48.516+08:00[Asia/Shanghai]

cookie 断言工厂

  • 会取, key 和 value
  • 当请求携带的 cookie 和 cookied 断言工厂中配置的 cookie 一致,匹配成功

代码控制

	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("cookie_route", r -> r.cookie("chocolate", "ch.p")
						.uri("http://localhost:8071/test/cookie"))
				.build();
	}

yaml 控制

server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      routes:
        - id: between_route
          uri: http://xujin.org
          predicates:
            - Cookie=chocolate, ch.p

请求到的代码

	@GetMapping("/test/cookie")
	public String testGateway(HttpServletRequest request, HttpServletResponse response){
		Cookie[] cookies = request.getCookies();
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				System.out.println(cookie.getName()+":"+cookie.getValue());
			}
		}
		return "Spring Cloud Gateway,Hello world!";
	}

测试

  • http://localhost:8080/
  • header 增加:key为:Cookie ,value: chocolate=ch.p
  • 只有用这个headr头 才能访问

header 路由断言工厂

header 断言的两种方法

  • 根据 路由的 header 信息断言
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("header_route", r -> r.header("X-Request-Id", "xujin")
						.uri("http://localhost:8071/test/head"))
				.build();
	}
  • 或者
spring:
  cloud:
    gateway:
      routes:
      - id: header_route
        uri: http://localhost:8071/test/head
        predicates:
        - Header=X-Request-Id, xujin

被请求的项目

	/**
	 * 测试Head路由断言工厂
	 * @param request
	 * @param response
	 * @return
	 */
	@GetMapping("/test/head")
	public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
		String head=request.getHeader("X-Request-Id");
		return "return head info:"+head;
	}

Host 路由断言工厂

  • 对请求中的 Host 进行断言处理,断言成功则进行路由转发。

	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("host_route", r -> r.host("**.baidu.com:8080")
						.uri("http://jd.com"))
				.build();
	}

server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: http://jd.com
          predicates:
          - Host=**.baidu.com:8080
  • 更改host文件,把vip.baidu.com映射到:127.0.0.1
  • 访问:http://vip.baidu.com:8080 即可跳转到京东

Method 断言工厂

  • 根据路由信息配置的 method对 请求方法 是 Get 或者 Post 等 进行断言
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("method_route", r -> r.method("GET")
						.uri("http://jd.com"))
				.build();
	}

server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: http://jd.com
          predicates:
            - Method=GET
  • 访问:http://localhost:8080/ 即可。

Query 断言工厂

  • 从请求中 获取两个参数,和 Query 断言路由器中的配置 进行匹配
  • http://localhost:8080/?foo=baz 和 r.query(“foo”,“baz”) 配置对比
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("query_route", r -> r.query("foo","baz")
						.uri("http://baidu.com"))
				.build();
	}
server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: http://jd.com
          predicates:
            - Query=foo, baz

RemoteAddr 路由器断言工厂

  • ipv4 或 v6 网段,或者IP
  • 当请求的 IP 在网段内,匹配成功,否则失败
	@Bean
	public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("remoteaddr_route", r -> r.remoteAddr("127.0.0.1")
						.uri("http://baidu.com"))
				.build();
	}
spring:
  cloud:
    gateway:
      routes:
      - id: remoteaddr_route
        uri: http://baidu.com
        predicates:
        - RemoteAddr=127.0.0.1
  • 访问:http://localhost:8080/ 不行。
  • 访问:http://127.0.0.1:8080/ 转发成功
  • 或者 配本地的IP,也是可以测试的。

Spring cloud gateway 的 内置 filter

  • 路由过滤器 允许 以某种方式 修改请求进来的 http请求
  • 或 返回的 http 响应
  • 主要作用:处理 特定路由
  • 过滤器 将近20多个
  • 分为7类
    • Header
    • Paramter
    • Path
    • Status
    • Redirect跳转
    • Hystrix熔断
    • RateLimiter

AddRequestHeader 过滤器工厂

  • 对匹配的请求 加上 Header
	@Bean
	public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("add_request_header_route", r ->
						r.path("/test").filters(f -> 
                                                f.addRequestHeader
                                                ("X-Request-Acme", "ValueB"))
								.uri("http://localhost:8071/test/head"))
				.build();
	}
	/**
	 * @param request
	 * @param response
	 * @return
	 */
	@GetMapping("/test/head")
	public String testGatewayHead(HttpServletRequest request, HttpServletResponse response){
		String head=request.getHeader("X-Request-Acme");
		return "return head info:"+head;
	}
  • 访问:http://localhost:8080/test
  • 返回:return head info:ValueB

AddRequestParameter过滤器

  • 对 匹配上的请求路由 添加 请求参数
	@Bean
	public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("add_request_parameter_route", r ->
						r.path("/addRequestParameter").filters(f -> f.addRequestParameter("example", "ValueB"))
								.uri("http://localhost:8071/test/addRequestParameter"))
				.build();
	}
	@GetMapping("/test/addRequestParameter")
	public String addRequestParameter(HttpServletRequest request, HttpServletResponse response){
		String parameter=request.getParameter("example");
		return "return addRequestParameter info:"+parameter;
	}
  • 访问:http://localhost:8080/addRequestParameter
  • 返回:return addRequestParameter info:ValueB

RewritePath过滤器

  • RewritePath 替代 Zuul 的 StripPrefix

    path: /demo/**
    stripPrefix: true
    url: http://demo.com
    
  • 所有 /demo/xxxx的请求 转发给 http://demo.com/xxx ,去掉demo 前缀

  • gateway使用的是 RewritePath

	@Bean
	public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("rewritepath_route", r ->
						r.path("/foo/**").filters(f -> f.rewritePath("/foo/(?<segment>.*)","/$\\{segment}"))
								.uri("http://www.baidu.com"))
				.build();
	}

  • 访问:http://localhost:8080/foo/cache/sethelp/help.html
  • 将会去掉:/foo。http://www.baidu.com/cache/sethelp/help.html
  • /foo/(?.*) 替换为 /$\{segment}

Add Response Header过滤器

  • 对网关 返回的 响应添加 header
	@Bean
	public RouteLocator testRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("add_request_header_route", r ->
						r.path("/test").filters(f -> f.addResponseHeader("X-Response-Foo", "Bar"))
								.uri("http://www.baidu.com"))
				.build();
	}
//很神奇,去掉www,后转发成功了,之后加上www也可以了。
//好吧,确实不能加 www,缓存问题
  • http://localhost:8080/test

  • 转发的京东 请写:http://jd.com:80/

StripPrefix过滤器

  • StripPrefix Gateway Filter Factory
  • 针对请求 url前缀进行处理的 filter工厂,用于去除前缀
  • Prefix Path Gateway Filter Factory 是用于增加前缀
server:
  port: 8080
spring:
  application:
    name: spring-cloud-gateway

spring:
  cloud:
    gateway:
      routes:
      - id: baidu_route
        uri: http://www.baidu.com
        predicates:
        - Path=/baidu/test/**
        filters:
        - StripPrefix=2

logging:
  level:
    org.springframework.cloud.gateway: TRACE
    org.springframework.http.server.reactive: DEBUG
    org.springframework.web.reactive: DEBUG
    reactor.ipc.netty: DEBUG

  • 访问:http://localhost:8080/baidu/test
  • 跳转到: http://www.baidu.com 。去除:/baidu/test/

Retry过滤器

  • 对网络请求 进行重试
  • 使用 重试 filter 进行重试
	@Bean
	public RouteLocator retryRouteLocator(RouteLocatorBuilder builder) {
		return builder.routes()
				.route("retry_route", r -> r.path("/test/retry")
						.filters(f ->f.retry(config -> config.setRetries(2).setStatuses(HttpStatus.INTERNAL_SERVER_ERROR)))
						.uri("http://localhost:8071/retry?key=abc&count=2"))
				.build();
	}
  • 设置重试 次数为 两次
  • 代理服务 调用失败时,设置 返回的状态码 为 500 (服务器内部错误)

重试到 action的代码

	
	ConcurrentHashMap<String, AtomicInteger> map = new ConcurrentHashMap<>();

@GetMapping("/retry")
	public String testRetryByException(@RequestParam("key") String key, @RequestParam(name = "count") int count) {
        
		AtomicInteger num = map.computeIfAbsent(key, s -> new AtomicInteger());
        
		//对请求或重试次数计数
		int i = num.incrementAndGet();
		log.warn("重试次数: "+i);
		//计数i小于重试次数2抛出异常,让Spring Cloud Gateway进行重试
		if (i < count) {
			throw new RuntimeException("Deal with failure, please try again!");
		}
		//当重试两次时候,清空计数,返回重试两次成功
		map.clear();
		return "重试"+count+"次成功!";
	}

测试

  • http://localhost:8080/test/retry
  • 第一次:AtomicInteger num 为0,num.incrementAndGet(); 为1
  • 第二次:AtomicInteger num 为1,num.incrementAndGet(); 为2
  • key=abc 作为:ConcurrentHashMap 的 key 统计 计数

hystrix 过滤器

  • 熔断,自我保护,服务降级,快速失败
  • gateway调用后端服务,后端服务一致异常。对服务进行降级。

xml

    <dependencies>
        <!-- Spring Cloud Gateway Starter-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <!-- hystrix的Starter-->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>
    </dependencies>

yaml


server:
 port: 8080
spring:
 application:
   name: spring-cloud-gateway

spring:
 cloud:
   gateway:
     routes:
     - id: prefix_route
       uri: http://localhost:8071/test/Hystrix?isSleep=true
       predicates:
       - Path=/test/Hystrix
       filters:
       - name: Hystrix # Hystrix Filter的名称
         args: # Hystrix配置参数
           name: fallbackcmd #HystrixCommand的名字
           fallbackUri: forward:/fallback #fallback对应的uri

#Hystrix的fallbackcmd的时间
hystrix.command.fallbackcmd.execution.isolation.thread.timeoutInMilliseconds: 5000

logging:
 level:
   org.springframework.cloud.gateway: TRACE
   org.springframework.http.server.reactive: DEBUG
   org.springframework.web.reactive: DEBUG
   reactor.ipc.netty: DEBUG

本项目的回退

@RestController
public class FallbackController {

    @GetMapping("/fallback")
    public String fallback() {
        return "Spring Cloud Gateway Fallback!";
    }

}

远程请求的 接口

	@GetMapping("/test/Hystrix")
	public String index(@RequestParam("isSleep") boolean isSleep) throws InterruptedException {
		log.info("issleep is " + isSleep);
		//isSleep为true开始睡眠,睡眠时间大于Gateway中的fallback设置的时间
		if (isSleep) {
			TimeUnit.MINUTES.sleep(10); //这里怎么睡成分钟了
		}
		return "No Sleep";
	}

测试

  • http://localhost:8080/test/Hystrix
  • 返回:Spring Cloud Gateway Fallback!
  • 如果改成睡4秒,返回为:No Sleep
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值