本篇文章主要讲解 zuul-ratelimit 组件如何来作为服务限流的。并且只讲解他的默认存储类型,因为我想后期能力允许,我会单独讲解利用 redis 来做限流。
本文 Demo 摘自《重新定义》
首先简单说一下 spring cloud zuul-ratelimit,他是外国人专门针对 zuul 编写的限流库,提供来4种限流策略,如下。
限流粒度/类型 | 说明 |
---|---|
User | 针对请求的用户进行限流 |
Origin | 针对请求的Origin进行限流 |
URL | 针对URL/接口进行限流 |
ServiceId | 针对服务进行限流,如果没有配置限流类型,则此类型生效 |
多种粒度临时变量储存方式
存储方式 | 说明 |
---|---|
IN_MEMORY | 基于本地内存,底层是ConcurrentHashMap,默认的。 |
REDIS | 基于redis存储,使用时必须搭建redis |
CONSUL | consul 的kv存储 |
JPA | spring data jpa,基于数据库 |
BUKET4J | 使用一个Java编写的基于令牌桶算法的限流库 |
这里重点说一下,如果 zuul 需要多节点部署,那就不能用 IN_MEMORY 存储方式,比较常用的就是用REDIS。
本篇Demo环境
框架 | 版本 |
---|---|
Spring Boot | 2.0.3.RELEASE |
Spring Cloud | Finchley.RELEASE |
zuul-ratelimit | 2.0.6.RELEASE |
JDK | 1.8.x |
本篇案例一共涉及到一个父 pom 和三个工程,eureka-server client-a zuul-server
父pom
<modules>
<module>ch8-3-eureka-server</module>
<module>ch8-3-zuul-server</module>
<module>ch8-3-client-a</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<!-- 利用传递依赖,公共部分 -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- springboot web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!-- 管理依赖 -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<!--注意: 这里必须要添加,否则各种依赖有问题 -->
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/libs-milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
eureka-server
下面我把eureka-server 所有代码贴在一起
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
server:
port: 8888
eureka:
instance:
hostname: localhost
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>
</dependencies>
client-a
由于当前客户端代码简单,我会把所有代码贴在一起
@SpringBootApplication
@EnableDiscoveryClient
public class ClientAApplication {
public static void main(String[] args) {
SpringApplication.run(ClientAApplication.class, args);
}
}
@RestController
public class TestController {
@GetMapping("/add")
public Integer add(Integer a, Integer b){
return a + b;
}
@GetMapping("/a/add")
public Integer aadd(Integer a, Integer b){
return a + b;
}
@GetMapping("/sub")
public Integer sub(Integer a, Integer b){
return a - b;
}
@GetMapping("/mul")
public String mul(Integer a, Integer b){
System.out.println("进入client-a!");
return "client-a-" + a * b;
}
@GetMapping("/div")
public Integer div(Integer a, Integer b){
return a / b;
}
}
server:
port: 7070
spring:
application:
name: client-a
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8888}/eureka/
instance:
prefer-ip-address: true
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
</dependencies>
zuul-server
下面重点讲解 网关这个工程
启动类
@SpringBootApplication
@EnableDiscoveryClient
@EnableZuulProxy
public class ZuulServerApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulServerApplication.class, args);
}
}
pom依赖
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.marcosbarbero.cloud</groupId>
<artifactId>spring-cloud-zuul-ratelimit</artifactId>
<version>2.0.6.RELEASE</version>
</dependency>
</dependencies>
yml文件
spring:
application:
name: zuul-server
server:
port: 5555
eureka:
client:
serviceUrl:
defaultZone: http://${eureka.host:127.0.0.1}:${eureka.port:8888}/eureka/
instance:
prefer-ip-address: true
zuul:
routes:
client-a:
path: /client/**
serviceId: client-a
ratelimit:
# key-prefix: springcloud-book #按粒度拆分的临时变量key前缀
enabled: true #启用开关
# repository: IN_MEMORY #key存储类型,默认是IN_MEMORY本地内存,必须大些
behind-proxy: true #表示代理之后
default-policy: #全局限流策略,可单独细化到服务粒度
limit: 2 #在一个单位时间窗口的请求数量
quota: 1 #在一个单位时间窗口的请求时间限制
refresh-interval: 3 #单位时间窗口
# type:
# - user #可指定用户粒度
# - origin #可指定客户端地址粒度
# - url #可指定url粒度
代码解释:
我用 # 注释的代码可以不用写,除了上面注释的内容,我必须要整体说一下该配 置的含义,他表示对全局开启了限流,策略是,3秒内访问不允许超过 2 次,并且这 2 次请求要小于 1 秒。这些参数大家根据你对需要自己修改。
上面是对全局配置限流,下面我对其中一个服务进行限流,只需增加几行配置。
ratelimit:
key-prefix: springcloud-book #按粒度拆分的临时变量key前缀
enabled: true #启用开关
repository: IN_MEMORY #key存储类型,默认是IN_MEMORY本地内存,此外还有多种形式
behind-proxy: true #表示代理之后
default-policy: #全局限流策略,可单独细化到服务粒度
limit: 2 #在一个单位时间窗口的请求数量
quota: 1 #在一个单位时间窗口的请求时间限制
refresh-interval: 3 #单位时间窗口
type:
- user #可指定用户粒度
- origin #可指定客户端地址粒度
- url #可指定url粒度
policies:
client-a:
limit: 5
quota: 5
efresh-interval: 10
代码解释:
我们增加了 policies 配置,含义是我们对 client-a 服务进行特殊限流配置,10秒内请求数量不得大于 5 次,这 5 次请求总时长不能大于 5秒,其他服务对限流策略还是按照 上面默认的,不冲突。
测试
下面我们分别启动 eureka服务,客户端,zuul,因为我两种方法都已经试验过了,结果是一样的,比如 要求是 10 秒内请求 5 次,那么一定是第6次会报错,后台也会抛错。
代码结构
需要第可以加微信