赞
踩
在微服务架构里,服务的粒度被进一步细分,各个业务服务可以被独立的设计、开发、测试、部署和管理。这时,各个独立部署单元可以用不同的开发测试团队维护,可以使用不同的编程语言和技术平台进行设计,这就要求必须使用一种语言和平台无关的服务协议作为各个单元间的通讯方式。
网关的角色是作为一个 API 架构,用来保护、增强和控制对于 API 服务的访问。
API 网关是一个处于应用程序或服务(提供 REST API 接口服务)之前的系统,用来管理授权、访问控制和流量限制等,这样 REST API 接口服务就被 API 网关保护起来,对所有的调用者透明。因此,隐藏在 API 网关后面的业务系统就可以专注于创建和管理服务,而不用去处理这些策略性的基础设施。
Spring Cloud Gateway是Spring官方基于Spring 5.0,Spring Boot 2.0和Project
Reactor等技术开发的网关,Spring Cloud
Gateway旨在为微服务架构提供一种简单而有效的统一的API路由管理方式。Spring Cloud Gateway作为Spring
Cloud生态系中的网关,目标是替代ZUUL,其不仅提供统一的路由方式,并且基于Filter链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。
Spring Cloud Gateway 可以看做是一个 Zuul 1.x 的升级版和代替品,比 Zuul 2 更早的使用 Netty
实现异步 IO,从而实现了一个简单、比 Zuul 1.x 更高效的、与 Spring Cloud 紧密配合的 API 网关。
Spring Cloud Gateway 里明确的区分了 Router 和
Filter,并且一个很大的特点是内置了非常多的开箱即用功能,并且都可以通过 SpringBoot 配置或者手工编码链式调用来使用。
比如内置了 10 种 Router,使得我们可以直接配置一下就可以随心所欲的根据 Header、或者 Path、或者 Host、或者
Query 来做路由。
比如区分了一般的 Filter 和全局 Filter,内置了 20 种 Filter 和 9 种全局
Filter,也都可以直接用。当然自定义 Filter 也非常方便。
<!--gateway 此模块不能引入starter-web-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
server: port: 7000 spring: application: name: api-gateway cloud: gateway: # 路由数组 (路由:请求满足什么样的条件的时候转发到哪个微服务上) routes: #当前路由发的标识,要求唯一,默认是UUID - id: product_route #请求最终要转发到的地址 uri: http://localhost:8081 #路由的优先级,数字越小,优先级越高 order: 1 #断言(条件判断,返回值是boolean,转发请求要满足的条件) predicates: # 当请求路径满足Path指定的规则时,此路由信息才正常转发 - Path=/service-product/** #过滤器(在请求传递过程中 对请求做点手脚) filters: #在请求转发前去掉一层路径 否则请求路径 localhost:7000/product_serv/product/1 --> http://localhost:8081/product_serv/product/1 - StripPrefix=1
<!--nacos 服务发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
@SpringBootApplication
@EnableDiscoveryClient
public class ApiGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ApiGatewayApplication.class);
}
}
server: port: 7000 spring: application: name: api-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: #让gateway从nacos中获取服务信息 discovery: locator: enabled: true # 路由数组 (路由:请求满足什么样的条件的时候转发到哪个微服务上) routes: #当前路由发的标识,要求唯一,默认是UUID - id: product_route #请求最终要转发到的地址 #lb 从nacos中按照名称获取服务,并遵循负载均衡策略 uri: lb://service-product #路由的优先级,数字越小,优先级越高 order: 1 #断言(条件判断,返回值是boolean,转发请求要满足的条件) predicates: # 当请求路径满足Path指定的规则时,此路由信息才正常转发 - Path=/product_serv/** #过滤器(在请求传递过程中 对请求做点手脚) filters: #在请求转发前去掉一层路径 否则请求路径 localhost:7000/product_serv/product/1 --> http://localhost:8081/product_serv/product/1 - StripPrefix=1
server:
port: 7000
spring:
application:
name: api-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
#让gateway从nacos中获取服务信息
discovery:
locator:
enabled: true
用于进行条件判断,只有断言都返回真,才会真正的执行路由
-After=2021-12-31T23:59:59.789+08:00[Asia/Shanghai]
-RemoteAddr=192.168.1.1/24
CookieRoutePredicateFactory:接收两个参数,cookie名字和一个正则表达式,判断请求cookie是否具有给定名称且值与正则表达式匹配
-Cookie=chocolate,ch.
HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。判断请求Header是否具有给定名称且值与正则表达式匹配
-Header=X-Request-Id,\d+
HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=**.testhost.org
MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配
-Method=GET
PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则
-Path=/foo/{segment}
QueryRoutePredicateFactory:接收两个参数,请求param和正则表达式,判断请求参数是否具有给定名称且值与正则表达式匹配
-Query=baz,ba.
WeightRoutePredicateFactory:接收一个【组名,权重】,然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1
uri: host1
predicates:
-Path=/product/**
-Weight=group3,1
-id: weight_route2
uri: host2
predicates:
-Path=/product/**
-Weight=group3,9
让age在(min,max)之间的人访问
server: port: 7000 spring: application: name: api-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: #让gateway从nacos中获取服务信息 discovery: locator: enabled: true # 路由数组 (路由:请求满足什么样的条件的时候转发到哪个微服务上) routes: #当前路由发的标识,要求唯一,默认是UUID - id: product_route #请求最终要转发到的地址 #lb 从nacos中按照名称获取服务,并遵循负载均衡策略 uri: lb://service-product #路由的优先级,数字越小,优先级越高 order: 1 #断言(条件判断,返回值是boolean,转发请求要满足的条件) predicates: # 当请求路径满足Path指定的规则时,此路由信息才正常转发 - Path=/product_serv/** # 自定义断言,限制年龄在18-60之间的人才能访问 - Age=18,60 #过滤器(在请求传递过程中 对请求做点手脚) filters: #在请求转发前去掉一层路径 否则请求路径 localhost:7000/product_serv/product/1 --> http://localhost:8081/product_serv/product/1 - StripPrefix=1
@Component public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> { public AgeRoutePredicateFactory() { super(AgeRoutePredicateFactory.Config.class); } /** * 读取配置文件中的参赛者,负载到配置类的属性上 */ @Override public List<String> shortcutFieldOrder() { //这个为准的顺序必须和配置文件中的值顺序对应 return Arrays.asList("minAge", "maxAge"); } @Override public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) { return new Predicate<ServerWebExchange>() { @Override public boolean test(ServerWebExchange serverWebExchange) { String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age"); if (StringUtils.isNotBlank(ageStr)) { int age = Integer.parseInt(ageStr); if (age > config.getMinAge() && age < config.getMaxAge()) { return true; } return false; } return false; } }; } /** * 配置类,用于接收配置文件中对应的参数 */ @Data @NoArgsConstructor public static class Config { private int minAge; private int maxAge; } }
在请求的传递过程中,对请求和响应做一些微调
在Gateway中,Filter的生命周期只有两个:“pre”和“post"
应用到单个路由或者一个分组的路由上
server: port: 7000 spring: application: name: api-gateway cloud: nacos: discovery: server-addr: localhost:8848 gateway: #让gateway从nacos中获取服务信息 discovery: locator: enabled: true # 路由数组 (路由:请求满足什么样的条件的时候转发到哪个微服务上) routes: #当前路由发的标识,要求唯一,默认是UUID - id: product_route #请求最终要转发到的地址 #lb 从nacos中按照名称获取服务,并遵循负载均衡策略 uri: lb://service-product #路由的优先级,数字越小,优先级越高 order: 1 #断言(条件判断,返回值是boolean,转发请求要满足的条件) predicates: # 当请求路径满足Path指定的规则时,此路由信息才正常转发 - Path=/product_serv/** # 自定义断言,限制年龄在18-60之间的人才能访问 - Age=18,60 #过滤器(在请求传递过程中 对请求做点手脚) filters: #在请求转发前去掉一层路径 否则请求路径 localhost:7000/product_serv/product/1 --> http://localhost:8081/product_serv/product/1 - StripPrefix=1 - Log=true,true
@Component public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> { public LogGatewayFilterFactory() { super(LogGatewayFilterFactory.Config.class); } /** * 读取配置文件中参数,赋值到配置类 */ @Override public List<String> shortcutFieldOrder() { return Arrays.asList("consoleLog", "cacheLog"); } @Override public GatewayFilter apply(Config config) { return new GatewayFilter() { @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { if (config.isCacheLog()) { System.out.println("cacheLog---开启"); } if (config.isConsoleLog()) { System.out.println("consoleLog---开启"); //请求结束 return exchange.getResponse().setComplete(); } //继续向下执行 return chain.filter(exchange); } }; } @Data @NoArgsConstructor public static class Config { private boolean consoleLog; private boolean cacheLog; } }
应用到所有的路由上
实现鉴权
@Component public class AuthGlobalFilter implements GlobalFilter, Ordered { /** *过滤器逻辑 */ @Override public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token = exchange.getRequest().getQueryParams().getFirst("token"); if (!StringUtils.equals("admin",token)){ System.out.println("认证失败----"); exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } return chain.filter(exchange); } /** *标识当前过滤器的优先级,返回值越小,优先级越高 */ @Override public int getOrder() { return 0; } }
<!--限流-->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
@Configuration public class GatewayConfiguration { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer){ this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } /** *初始化一个限流的过滤器 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter(){ return new SentinelGatewayFilter(); } /** *配置初始化限流参数 */ @PostConstruct public void initGatewayRules(){ Set<GatewayFlowRule> rules = new HashSet<>(); //资源名称,对应路由id rules.add(new GatewayFlowRule("product_route") //限流阈值 .setCount(1) //统计时间窗口,单位秒,默认1秒 .setIntervalSec(1)); GatewayRuleManager.loadRules(rules); } /** *配置限流异常处理器 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(){ return new SentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer); } /** *自定义限流异常页面 */ @PostConstruct public void initBlockHandlers(){ BlockRequestHandler blockRequestHandler = new BlockRequestHandler() { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { Map map = new HashMap<>(); map.put("code",0); map.put("message","接口被限流了"); return ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(BodyInserters.fromObject(map)); } }; GatewayCallbackManager.setBlockHandler(blockRequestHandler); } }
@Configuration public class GatewayConfiguration { private final List<ViewResolver> viewResolvers; private final ServerCodecConfigurer serverCodecConfigurer; public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider, ServerCodecConfigurer serverCodecConfigurer) { this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList); this.serverCodecConfigurer = serverCodecConfigurer; } /** * 初始化一个限流的过滤器 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public GlobalFilter sentinelGatewayFilter() { return new SentinelGatewayFilter(); } /** * 配置初始化限流参数 */ @PostConstruct public void initGatewayRules() { Set<GatewayFlowRule> rules = new HashSet<>(); rules.add(new GatewayFlowRule("product_api1").setCount(1).setIntervalSec(1)); rules.add(new GatewayFlowRule("product_api2").setCount(1).setIntervalSec(1)); GatewayRuleManager.loadRules(rules); } /** * 配置限流异常处理器 */ @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() { return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer); } /** * 自定义限流异常页面 */ @PostConstruct public void initBlockHandlers() { BlockRequestHandler blockRequestHandler = new BlockRequestHandler() { @Override public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { Map map = new HashMap<>(); map.put("code", 0); map.put("message", "接口被限流了"); return ServerResponse.status(HttpStatus.OK) .contentType(MediaType.APPLICATION_JSON_UTF8) .body(BodyInserters.fromObject(map)); } }; GatewayCallbackManager.setBlockHandler(blockRequestHandler); } /** * 自定义api分组 */ @PostConstruct private void initCustomizedApis() { Set<ApiDefinition> definitions = new HashSet<>(); ApiDefinition api1 = new ApiDefinition("product_api1") .setPredicateItems(new HashSet<ApiPredicateItem>() { { // 以/product_serv/product/api1 开头的请求 add(new ApiPathPredicateItem().setPattern("/product_serv/product/api1/**") .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); }}); ApiDefinition api2 = new ApiDefinition("product_api2") .setPredicateItems(new HashSet<ApiPredicateItem>() { { // 以/product_serv/product/api2/demo1 完成的url路径匹配 add(new ApiPathPredicateItem().setPattern("/product_serv/product/api2/demo1")); }}); definitions.add(api1); definitions.add(api2); GatewayApiDefinitionManager.loadApiDefinitions(definitions); } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。