Spring Cloud Gateway实验

Spring Cloud Gateway学习笔记

一、网关的HelloWorld

1.新建SpringBoot项目,添加gateway依赖和eureka-client依赖:

1
2
3
4
5
6
7
8
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

2.在application.yml中配置网关地址自动映射和服务发现地址:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
spring:
application:
name: gatewaydemo
cloud:
gateway:
discovery:
locator:
enabled: true # 开启网关自动映射处理逻辑,如:
# 有微服务名称是WEATHER-FORECAST,系统内部请求地址是:
# http://微服务实例IP:微服务端口/api/v1/weather/forecast?location=Shanghai&time=today
# 则配置enabled=true后将自动映射到:
# http://网关地址:9999/weather-forecast/api/v1/weather/forecast?location=Shanghai&time=today
lower-case-service-id: true # 开启服务名称小写转换(Eureka对服务命名管理默认全大写)
server:
port: 9999

eureka:
client:
service-url:
defaultZone: http://inspiron-7537:8761/eureka

启动好微服务、注册中心和网关后,就可以通过访问网关:

1
curl "http://localhost:9999/weather-forecast/api/v1/weather/forecast?location=Shanghai&time=today"

获取微服务结果了。

实验过程可以感觉到“约定大于配置”的思想,不用改代码,仅仅是配置就能完成基本功能。

二、手工配置服务映射

在前述实验基础上,将application.yml中spring配置项修改为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
spring:
application:
name: gatewaydemo
cloud:
gateway:
discovery:
locator:
enabled: false
lower-case-service-id: true
routes: # 配置网关中的一个完整路由,包括:命名、地址、谓词(规则)集合、过滤器集合
- id: first # 命名唯一即可,命名规则符合Java变量命名即可
uri: http://weather-forecast #当前路由定义对应的微服务转发地址
# 定义一个谓词,格式:
predicates: # 谓词名字=参数,或者name:谓词名 args:参数名
- Path=/api/** # 谓词命名是 GatewayPredicate接口实现的命名前缀,XxxRoutePredicateFactory
#value:
# 配置过滤器集合,格式与谓词定义类似
filters: # 过滤器命名是GatewayFilter接口实现的命名前缀,XxxGatewayFilterFactory
- StripPrefix=1 # 过滤请求地址前缀,过滤掉第1节

routes配置一个数组,使用-表达数组中一个对象,其含义是配置一个指向 http://weather-forecast 的路由,将请求中带有/api/**的请求路径去除/api后转发过去。

启动网关服务和天气预报服务后,等待服务发现完毕,发送报文:

1
2
curl "http://localhost:8025/v1/weather/forecast?location=Shanghai&time=today"
curl "http://localhost:9999/api/v1/weather/forecast?location=Shanghai&time=today"

均可成功。

三、配置令牌桶限流过滤器

1.在 application.yml 中新增filters配置:

1
2
3
4
5
6
7
8
9
10
filters: # 过滤器命名是GatewayFilter接口实现的命名前缀,XxxGatewayFilterFactory
- StripPrefix=1 # 过滤请求地址前缀,过滤掉第1节,如:
# 请求地址为:http://localhost:9999/api/v1/weather/forecast?location=Shanghai&time=today
# 过滤掉: /api,转发地址是:lb://weather-forecast/v1/weather/forecast?location=Shanghai&time=today
- name: RequestRateLimiter # 限流过滤器 RequestRateLimiterGatewayFilterFactory,改配置不支持简化配置
args:
keyResolver: ‘#{@tokenBucketAlgorithm}’ # 使用Spring EL表达式从容器中找对象并赋值
redis-rate-limiter:
replenishRate: 1 # 每秒生成1个令牌
burstCapacity: 5 # 令牌桶的上限容量

keyResolver是令牌桶工厂,需要继承KeyResolver类来实现。
redis-rate-limiter是已实现的方法,需要配置依赖和启动redis。

2.创建令牌桶工厂实现类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package edu.ustb.gatewaydemo.keyresolver;

import reactor.core.publisher.Mono;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;

@Component
public class TokenBucketAlgorithm implements KeyResolver {

/**
* 令牌桶绑定客户端IP
* 结合yml配置,每个客户端平均限流1次/s,峰值流量5次/s
* @param exchange
* @return
*/
@Override
public Mono<String> resolve(ServerWebExchange exchange) {
String hostAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
return Mono.just(hostAddress);
}
}

3.在pom.xml中添加依赖:

1
2
3
4
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

4.启动redis,进入redis-cli,可以查看缓存情况:
Empty keys with no requests

5.模拟请求,再次查看缓存情况:
先用curl试一下:

1
curl "http://localhost:9999/api/v1/weather/forecast?location=Beijing&time=today"

没有问题后打开JMeter,模拟一次10个线程的访问:
too-many-requests.png

可以看到被拒绝的多余请求,返回的是状态码429。(正常请求返回状态码是200)

redis-keys-available.png

使用ab进行并发测试:

1
ab -n 10 -c 5 "http://localhost:9999/api/v1/weather/forecast?location=Beijing&time=tomorrow"

从ab返回的结果中含有5个 “Non-2xx responses” 也能验证JMeter的结果:
ab-result.png

Spring Boot Appication 监控记录可视化 Spring Cloud Hystrix 实例学习

评论

You forgot to set the shortname for Disqus. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×