<!--gateway不兼容springboot—-web,必须引入webflux.-->
<!--入了个大坑,搞了我几个小时-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.0.4.RELEASE</version>
</dependency>
<!--熔断-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.0.1.RELEASE</version>
</dependency>
<!-- 限流 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j</artifactId>
<version>2.10.0</version>
<type>pom</type>
</dependency>
server:
port: 9967
spring:
application:
name: gateway-service
redis:
host: 127.0.0.1
password: 123456
port: 6379
database: 0
cloud:
gateway:
discovery:
locator:
enabled: true
# 服务名小写
lower-case-service-id: true
routes:
- id: consumer-service
# lb代表从注册中心获取服务,且已负载均衡方式转发
uri: lb://consumer-service #注册中心服务Id,serviceId
predicates:
- Path=/consumer/** #*/#网关拦截的访问路径。(针对服务拦截)
# 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
filters:
# 进行token过滤
- TokenAuthenticationFilter #加上这个时候接口需要鉴权 token
- StripPrefix=1 #否则转发到后端服务时会带上consumer前缀
# 熔断降级配置
- name: Hystrix #不会配置看看源码
args:
name: default
fallbackUri: 'forward:/defaultfallback' #服务超时的回调,及时响应给前端。
# redis限流 , filter名称必须是RequestRateLimiter
- name: RequestRateLimiter
args:
# 与使用SpEL名称引用Bean,上面新建的RateLimiterConfig类中的bean的name相同
key-resolver: '#{@remoteAddrKeyResolver}' #是一个简单的获取用户请求参数 我这里以主机地址为key来作限流
redis-rate-limiter.replenishRate: 20 # 允许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
redis-rate-limiter.burstCapacity: 20 # 是一秒钟内允许用户执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值设置为零将阻止所有请求
- id: consumer-service
# lb代表从注册中心获取服务,且已负载均衡方式转发
uri: lb://consumer-service
predicates:
- Path=/a/**
#*/# 加上StripPrefix=1,否则转发到后端服务时会带上consumer前缀
filters:
# 进行token过滤
# - TokenAuthenticationFilter
- StripPrefix=1
# 熔断降级配置
- name: Hystrix
args:
name: default
fallbackUri: 'forward:/defaultfallback' #服务超时的回调,及时响应给前端。
# hystrix 信号量隔离,3秒后自动超时
hystrix:
command:
default:
execution:
isolation:
strategy: SEMAPHORE
thread:
timeoutInMilliseconds: 10000 #指定超时时间
shareSecurityContext: true
# 注册中心
eureka:
instance:
prefer-ip-address: true
client:
service-url:
defaultZone: http://localhost:8899/eureka/
# 暴露监控端点
management:
endpoints:
web:
exposure:
include: '*'
endpoint:
health:
show-details: always
配置文件加上了鉴权,因此我们在没加token的情况下,访问401了。
加上我们的token,就访问成功了。
在这里就要说一下我们的过滤器。TokenAuthenticationFilter 继承了AbstractGatewayFilterFactory,代码如下,
@Component
public class TokenAuthenticationFilter extends AbstractGatewayFilterFactory {
private static final String Bearer_ = "Bearer ";
@Override
public GatewayFilter apply(Object config) {
return (exchange, chain) -> {
ServerHttpRequest request = exchange.getRequest();
ServerHttpRequest.Builder mutate = request.mutate();
ServerHttpResponse response = exchange.getResponse();
try {
//1.获取header中的Authorization
String header = request.getHeaders().getFirst("Authorization");
if (header == null || !header.startsWith(Bearer_)) {
throw new RuntimeException("请求头中Authorization信息为空");
}
//2.截取Authorization Bearer
String token = header.substring(7);
//可以配合redis来提高性能
if (!StringUtils.isEmpty(token)) {
System.out.println("成功");
//3.有token,把token设置到header中,传递给后端服务
mutate.header("token", token).build();
} else {
//4.token无效
System.out.println("token无效");
DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), "无效的请求");
return response.writeWith(Mono.just(bodyDataBuffer));
}
} catch (Exception e) {
//没有token
DataBuffer bodyDataBuffer = responseErrorInfo(response, HttpStatus.UNAUTHORIZED.toString(), e.getMessage());
return response.writeWith(Mono.just(bodyDataBuffer));
}
ServerHttpRequest build = mutate.build();
return chain.filter(exchange.mutate().request(build).build());
};
}
/**
* 自定义返回错误信息
*
*/
public DataBuffer responseErrorInfo(ServerHttpResponse response, String status, String message) {
HttpHeaders httpHeaders = response.getHeaders();
httpHeaders.add("Content-Type", "application/json; charset=UTF-8");
httpHeaders.add("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0");
response.setStatusCode(HttpStatus.UNAUTHORIZED);
Map<String, String> map = new HashMap<>();
map.put("code", status);
map.put("msg", message);
DataBuffer bodyDataBuffer = response.bufferFactory().wrap(map.toString().getBytes());
return bodyDataBuffer;
}
}
卖了个关子,加上过滤器就ok了。
还有一个限流的配置类,如下
@Configuration
public class RateLimiterConfig {
/**
* Ip限流
*
* @return
*/
@Bean(value = "remoteAddrKeyResolver")
public KeyResolver remoteAddrKeyResolver() {
return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
}
//
// /**
// * 用户限流,使用这种方式限流,请求路径中必须携带userId参数
// *
// * @return
// */
// @Bean
// KeyResolver userKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("userId"));
// }
//
//
// /**
// * 接口限流,获取请求地址的uri作为限流key
// * @return
// */
// @Bean
// KeyResolver apiKeyResolver() {
// return exchange -> Mono.just(exchange.getRequest().getPath().value());
// }
访问a下的hello,可以看到接口成功返回了数据
我们将token去掉试试,也是成功的,因为我们在a下并没有配置token鉴权。
在访问一个不存在的路由吧,不出所料404了。
看来网关还是起到了,关键性作用。
明天接着把限流也给测一下。拜拜了
起到限流作用的配置是:
# 与使用SpEL名称引用Bean,上面新建的RateLimiterConfig类中的bean的name相同
key-resolver: '#{@remoteAddrKeyResolver}' #是一个简单的获取用户请求参数 我这里以主机地址为key来作限流
redis-rate-limiter.replenishRate: 2 # 允许用户每秒执行多少请求,而不会丢弃任何请求。这是令牌桶填充的速率。
redis-rate-limiter.burstCapacity: 10 # 是一秒钟内允许用户执行的最大请求数。这是令牌桶可以容纳的令牌数。将此值
使用压测工具jmeter,测试接口。
不会安装就jmeter工具的参考下面的博客:
https://blog.csdn.net/a13124837937/article/details/79628838
https://blog.csdn.net/fangfeixinlingamp/article/details/79982215
https://blog.csdn.net/cbzcbzcbzcbz/article/details/78023327
100个线程 ,1s内执行完,执行3次。看一下限流情况。
可以看到部分接口访问成功,部分被阻止掉了。
此时看一下redis限流情况。可以看到是本机ip被限流了。
其他的限流方式还没有尝试。暂且先不做了。基本上这个层面上已经实现。