前面介绍的微服务方式,不适用于微服务多和复杂的场景。前面的架构存在诸多问题:
以上问题可以借助API网关来解决。
所谓的API网关,就是系统的统一入口,封装了应用程序内部结构,为客户端提供统一服务,一些与业务本身无关的公共逻辑可以在这里实现,如认证授权、监控、路由转发等。
SpringCloud Gateway是一个旨在为微服务架构提供一种简单有效的统一api路由管理方式,目标替代zuul网关。还提供了统一的对方式,且基于filter链的方式提供了网关基本的功能。eg:安全、监控、限流。
流程:
<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">
<parent>
<artifactId>cloud2_parentartifactId>
<groupId>com.hxgroupId>
<version>1.0version>
parent>
<modelVersion>4.0.0modelVersion>
<artifactId>cloud2-gatewayartifactId>
<name>cloud2-gatewayname>
<properties>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<maven.compiler.source>1.7maven.compiler.source>
<maven.compiler.target>1.7maven.compiler.target>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloudgroupId>
<artifactId>spring-cloud-starter-gatewayartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
<version>${spring-cloud-alibaba.version}version>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
dependencies>
project>
server:
port: 8871
spring:
application:
name: cloud2-gateway
cloud:
nacos:
discovery:
server-addr: localhost:8848
gateway:
discovery:
locator:
enabled: true # 让gateway可以发现nacos中的服务
@SpringBootApplication
@EnableDiscoveryClient
public class Cloud2GatewayApp {
public static void main(String[] args) {
SpringApplication.run(Cloud2GatewayApp.class,args);
}
}
spring:
cloud:
gateway:
routes:
- id: cloud2-order # 路由id,唯一
uri: http://localhost:8882 # 跳转地址,固定地址
predicates:
- Path=/od/** # 断言,路径匹配处理条件
filters:
- StripPrefix=1 # 网关局部过滤器:对通过网关请求进行拦截与加工。不同拦截器有不同逻辑。
- id: cloud2-product
uri: lb://cloud2-product-server # 跳转地址:负载均衡
predicates:
- Path=/prod/** # 拦截规则,拦截以/prod开头的路径
filters:
- StripPrefix=1 # StripPrefix表示过滤url第一节
过滤器就是在请求传递过程中,对请求与响应做一些手脚。
在Gateway中,filter的生命周期只有俩个:pre和post
在gateway中,filter的作用范围:
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
@Order(0)
class TimeGatewayFilter implements GatewayFilter{
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long begin = System.currentTimeMillis();
System.out.println("开始时间 = " + begin);
return chain.filter(exchange).then(Mono.fromRunnable(()->{
long end = System.currentTimeMillis();
System.out.println("结束时间 = " + end);
System.out.println("消耗时间 = " + (end - begin) + " 毫秒");
}));
}
}
/**
* 将自定义的局部filter注册到网关
*/
@Override
public GatewayFilter apply(Object config) {
return new TimeGatewayFilter();
}
}
spring:
cloud:
gateway:
routes:
- id: cloud2-order # 路由id,唯一
uri: http://localhost:8882 # 跳转地址,固定地址
predicates:
- Path=/od/** # 断言,路径匹配处理条件
filters:
- Time
spring:
cloud:
gateway:
routes:
- id: cloud2-order # 路由id,唯一
uri: http://localhost:8882 # 跳转地址,固定地址
predicates:
- Path=/od/** # 断言,路径匹配处理条件
filters:
- Time=111,2222
@Component
public class TimeGatewayFilterFactory extends AbstractGatewayFilterFactory<TimeGatewayFilterFactory.Config> {
// 指定获取配置参数之后,要封装成什么对象
public TimeGatewayFilterFactory() {
super(Config.class);
}
// 将数据添加到哪个属性上面
@Override
public List<String> shortcutFieldOrder() {
return Arrays.asList("val1","val2");
}
@Data
public static class Config {
private int val1;
private String val2;
}
/**
* 将自定义的局部filter注册到网关
*/
@Override
public GatewayFilter apply(TimeGatewayFilterFactory.Config config) {
return new TimeGatewayFilter(config);
}
@Order(0)
@NoArgsConstructor
class TimeGatewayFilter implements GatewayFilter {
private TimeGatewayFilterFactory.Config timeGatewayParam;
public TimeGatewayFilter(TimeGatewayFilterFactory.Config config) {
this.timeGatewayParam = config;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
long begin = System.currentTimeMillis();
System.out.println("开始时间 = " + begin);
System.out.println("timeGatewayParam.getVal1() = " + timeGatewayParam.getVal1());
System.out.println("timeGatewayParam.val2 = " + timeGatewayParam.getVal2());
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
long end = System.currentTimeMillis();
System.out.println("结束时间 = " + end);
System.out.println("消耗时间 = " + (end - begin) + " 毫秒");
}));
}
}
}
package com.hx.gateway;
import org.apache.commons.lang.StringUtils;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
import java.nio.charset.StandardCharsets;
/**
* @author Huathy
* @date 2023-04-05 16:51
* @description
*/
@Component
public class AuthGlobalFilter implements GlobalFilter {
@Override
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
String token = exchange.getRequest().getQueryParams().getFirst("token");
ServerHttpResponse response = exchange.getResponse();
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if(StringUtils.isBlank(token)){
System.out.println("该请求没有登录");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
// return response.setComplete(); // 仅返回状态码
String res = "{\"code\":401,\"msg\":\"未登录\"}";
DataBuffer wrap = response.bufferFactory().wrap(res.getBytes(StandardCharsets.UTF_8));
return response.writeWith(Mono.just(wrap));
}
return chain.filter(exchange);
}
}
网关是所有请求的公共入口,所以可以在网关进行限流,而限流的方式也很多,我们本次采用Sentinel组件来实现网关限流。Sentinel支持对SpringCloud Gateway、Zuul等主流网关进行限流。
Sentinel提供了SpringCloudGateway的适配模块,可以提供俩种资源维度的限流:
Sentinel中支持按照API分组进行限流,就是我们可以按照特定规则进行限流。
在控制台页面中提供了三种方式的api分组管理
<dependency>
<groupId>com.alibaba.cspgroupId>
<artifactId>sentinel-spring-cloud-gateway-adapterartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-starter-alibaba-sentinelartifactId>
dependency>
<dependency>
<groupId>com.alibaba.cloudgroupId>
<artifactId>spring-cloud-alibaba-sentinel-gatewayartifactId>
dependency>
spring:
cloud:
sentinel:
transport:
port: 18871
dashboard: localhost:8080
测试地址:http://localhost:8871/od/gs/t1?token=1
添加以下配置即可
@Component
public class GatewayConfig {
@PostConstruct
public void initBlockHandlers() {
BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
@Override
public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
Map<String, Object> map = new HashMap<>();
map.put("code", 500101);
map.put("msg", "接口限流了");
return ServerResponse.status(HttpStatus.OK)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromValue(map));
}
};
GatewayCallbackManager.setBlockHandler(blockRequestHandler);
}
}
路由转发 + 执行过滤器链