Spring Cloud Gateway的应用场景

网关在微服务架构里相当于是客户端请求的一个总入口,有解耦和隐藏后端服务的作用。除此之外,网关还有多种功能,本篇文章我们用Spring Cloud Gateway来模拟实现网关的各种应用场景。

1. 流控(金丝雀发布,蓝绿发布)

目前很多公司都实现了生产不停机发布版本,在生产中同时存在新老版本代码,很多是通过ngnix来做流量的分流。发版时让流量都往老版本的服务走,等新版本发布完后再把流量慢慢切到新版本去验证,这就是所谓的金丝雀发布,也叫做蓝绿发布。下面我们用Spring Cloud Gateway来模拟实现这种分流

    @Bean
	public RouteLocator blueGreenRoute(RouteLocatorBuilder builder){
		return builder.routes()
				// 80%流量到老版本服务
				.route(p -> p.weight("group1",8).and().path("/**")
						.uri("http://httpbin.org:80"))
				// 20%流量到新版本服务
				.route(p -> p.weight("group1",2).and().path("/**")
						.filters(f -> f.stripPrefix(1))
						.uri("https://www.baidu.com"))
				.build();

	}

命令行测试:curl http://localhost:8080/get

2.熔断

熔断发生在请求报错时返回友好的提示信息给到用户,而不是异常堆栈信息。下面测试代码,是让头部带有name的请求路由到错误的地址h
ttp://httpbin.org33:80,异常后重定向到/fallback,输出提示信息

    // fallback,返回提示:请稍后再试
	@Bean
	public RouterFunction<ServerResponse> testFunRouterFunction() {
		RouterFunction<ServerResponse> route = RouterFunctions.route(
				RequestPredicates.path("/fallback"),
				request -> ServerResponse.ok().body(BodyInserters.fromValue("请稍后再试")));
		return route;
	}

	@Bean
	public RouteLocator hystrixRoute(RouteLocatorBuilder builder){
		return builder.routes()
				.route(p -> p.header("name")
						.filters(f -> f.hystrix((config -> config
								.setName("mytest")
						.setFallbackUri("forward:/fallback")))) // 熔断,如果访问报错则跳到/fallback
				.uri("http://httpbin.org33:80"))
				.build();
	}

命令行测试:curl -H ‘name:dd’ http://localhost:8080/get

3.黑名单控制

首先我们自定义一个过滤器,检测请求头里的id是否为黑名单,如果为黑名单,则直接返回,不做路由转发。

public class BlackFilter implements GatewayFilter, Ordered {
	// 黑名单列表,也可以放在缓存数据库里
	List<String> blackList = Arrays.asList("123","456");

	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// 用户id
		String id = exchange.getRequest().getHeaders().getFirst("id");
		if(blackList.contains(id)){
			// 如果是黑名单就直接返回,不再往目标服务器转发
			exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE);
			return exchange.getResponse().setComplete();
		}

		return chain.filter(exchange);
	}

	@Override
	public int getOrder() {
		return 0;
	}
}

@Bean
	public RouteLocator blackRoute(RouteLocatorBuilder builder){
		return builder.routes()
				.route(p -> p.path("/**")
						.uri("http://httpbin.org:80").filters(new BlackFilter()))
				.build();
	}

命令测试:curl -H ‘id:123’ http://localhost:8080/get

4.日志记录

请求的路径、入参和耗时日志是排查问题的重要信息,可以在网关上做统一的处理。

public class LogFilter implements GatewayFilter, Ordered {
	private static final String START_TIME = "startTime";
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		// 下面3行代码在前过滤器pre filter执行
		String url = exchange.getRequest().getURI().getRawPath();
		System.out.println("请求地址:" + url + ",入参:" + exchange.getRequest().getQueryParams().toString());
		exchange.getAttributes().put(START_TIME, System.currentTimeMillis());

		// chain.filter里面的逻辑相当于后过滤器post filter
		return chain.filter(exchange).then(
				Mono.fromRunnable(() -> {
					Long startTime = exchange.getAttribute(START_TIME);
					if(startTime != null){
						System.out.println(url+ "耗时:" +
								(System.currentTimeMillis() - startTime) + "ms");
					}
				})
		);
	}

	@Override
	public int getOrder() {
		return 0;
	}
}

@Bean
	public RouteLocator logRoute(RouteLocatorBuilder builder) {
		return builder.routes()
				.route(p -> p.path("/**")
						.uri("http://httpbin.org:80").filters(new LogFilter()))
				.build();
	}

命令测试:curl -H ‘id:123’ http://localhost:8080/get

5.用户鉴权

用户统一鉴权控制,是网关常见的应用场景,下面模拟的情况是从Cookie中获取token,然后用token验证用户是否已经登录。如果用户没有登录,则重定向到登录页面。

public class MyAuthFilter implements GatewayFilter, Ordered {
	@Override
	public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
		String token = null;
		HttpCookie tokenCookie = exchange.getRequest().getCookies().getFirst("token");
		if (tokenCookie != null) {
			token = tokenCookie.getValue();
		}
		// 拿到token判断是否已经登录
		if(!isLogin(token)){
			// 如果没有登录则重定向都登录页面
			exchange.getResponse().setStatusCode(HttpStatus.FOUND);
			exchange.getResponse().getHeaders().set("Location", "/login");
			return exchange.getResponse().setComplete();
		}
		return chain.filter(exchange);
	}

	private boolean isLogin(String token){
		return false;
	}
	@Override
	public int getOrder() {
		return 0;
	}
	
	@Bean
	public RouterFunction<ServerResponse> testFunRouterFunction() {
		RouterFunction<ServerResponse> route = RouterFunctions.route(
				RequestPredicates.path("/login"),
				request -> ServerResponse.ok().body(BodyInserters.fromValue("请登录系统")));
		return route;
	}

	@Bean
	public RouteLocator authRoute(RouteLocatorBuilder builder) {
		return builder.routes()
				.route(p -> p.path("/get")
						.filters(f -> f.filter(new MyAuthFilter()))
						.uri("http://httpbin.org:80"))
				.build();
	}

浏览器访问:http://localhost:8080/get

6.总结

Spring Cloud Gateway的基本使用方法暂时就写到这里了,还有很多功能后面做研究。下一步进入源码,学习整体的架构设计以及运作原理。

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值