7.1 Gateway服务网关的作用
- 统一的入口来路由转发各个微服务
- 安全认证、鉴权、统一拦截接口请求与响应、限流
现在比较主流的网关组件是Spring Cloud Gateway。
7.2 Spring Cloud Gateway入门使用
需求: 通过访问api网关,将请求路由转发到device设备微服务。
7.2.1 创建smart-gateway网关工程并添加依赖
添加Spring Cloud Gateway网关依赖、Nacos依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
7.2.2 创建启动类并添加注解以启动Nacos客户端
@SpringBootApplication
@EnableDiscoveryClient
public class SmartGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(SmartGatewayApplication.class, args);
}
}
7.2.3 添加配置
主要配置:
-
Nacos地址,这样Gateway网关可以从Nacos中获取微服务
-
path路径转发到微服务的路由
server:
port: 8000
spring:
application:
name: smart-gateway
cloud:
nacos:
discovery:
server-addr: 192.168.56.200:8848
gateway:
discovery:
locator:
enabled: true # gateway发现nacos中的微服务
routes:
- id: device_route
uri: lb://smart-device # 从nacos中获取微服务,并遵循负载均衡策略
predicates:
- Path=/device-serv/**
filters:
- StripPrefix=1
最后启动gateway网关服务和device设备服务,访问api地址:http://127.0.0.1:8000/device-serv/device/get/a1
网关路由转发到device服务
7.3 全局过滤器
Spring Cloud Gateway中的全局过滤器,常用来统一对请求进行token认证。下面简单的例子说明下。
全局过滤器具体的处理逻辑:对请求的token进行认证,如果认证成功则转发到下游服务;认证失败,则直接返回异常响应。
/**
* @description: token认证过滤器
* @author:xg
* @date: 2025/1/5
* @Copyright:
*/
@Component
@Slf4j
public class TokenAuthGlobalFilter implements GlobalFilter, Ordered {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getHeaders().getFirst("token");
if (StringUtils.isBlank(token)) {
log.info("认证失败");
// 认证失败,返回异常响应
exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
return exchange.getResponse().setComplete();
}
// 调用chain.filter继续转发到下游服务
return chain.filter(exchange);
}
@Override
public int getOrder() {
return 0;
}
}
header头中没有token认证失败:
header头中没有token认证失败
token认证成功:
当然这个只是非常粗糙的对token认证,实际项目中一般会对token的有效性、时效性等进行认证,这个根据实际的业务来实现。
7.4 网关限流
所有请求都会经过网关,因此可以利用Sentinel组件在网关处对请求进行限流。
7.4.1 网关工程引入Sentinel限流依赖
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
7.4.2 配置过滤器以及限流规则
/**
* @description: 限流配置
* @author:xg
* @date: 2025/1/5
* @Copyright:
*/
@Configuration
public class FlowConfiguration {
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public FlowConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
/**
* 初始化限流的过滤器
* @return
*/
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public GlobalFilter sentinelGatewayFilter() {
return new SentinelGatewayFilter();
}
/**
* 初始化限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(
new GatewayFlowRule("device_route") // 资源名称,对应路由id
.setCount(1) // 限流阈值
.setIntervalSec(1) // 统计时间窗口,默认是1秒
);
GatewayRuleManager.loadRules(rules);
}
/**
* 配置限流的异常处理器
* @return
*/
@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);
}
}
最后,我们在1秒之内,多次访问接口:http://127.0.0.1:8000/device-serv/device/get/a1
1秒内多次调用被限流
我们看到接口被限流了。
7.4.3 针对api限流
在上面的配置中,添加针对具体的api限流,控制粒度更加的精确。
/**
* 初始化的限流参数
*/
@PostConstruct
public void initGatewayRules() {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule("device_api1").setCount(1).setIntervalSec(1));
rules.add(new GatewayFlowRule("device_api2").setCount(1).setIntervalSec(1));
GatewayRuleManager.loadRules(rules);
}
/**
* 自定义API分组
*/
@PostConstruct
private void initCustomizedApis() {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api1 = new ApiDefinition("device_api1")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 以/device-serv/device/get 开头的请求
add(new ApiPathPredicateItem().setPattern("/device-serv/device/get/**").
setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}});
ApiDefinition api2 = new ApiDefinition("device_api2")
.setPredicateItems(new HashSet<ApiPredicateItem>() {{
// 以/device-serv/device/hello 完整的url路径匹配
add(new ApiPathPredicateItem().setPattern("/device-serv/device/hello"));
}});
definitions.add(api1);
definitions.add(api2);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
我们看到,对于/device/hello接口,在1秒内访问多次被限流了。
api接口被限流