一、原理:
令牌桶算法是一种常用的流量控制算法,用于限制请求或事件的发生速率,以防止系统过载。它的原理是基于令牌桶的数据结构和一定的令牌产生规则。
在令牌桶算法中,可以将令牌桶看作是一个具有固定容量的桶,以一定的速率产生令牌,并按照一定的规则进行存放。每当有请求或事件发生时,首先需要从桶中获取一个令牌,如果桶中没有可用的令牌,则需要等待或丢弃请求。当桶中的令牌数达到最大容量时,产生的令牌也不会继续增加。
具体工作原理如下:
- 令牌桶中有两个关键参数:令牌产生速率(token generation rate)和令牌容量(token bucket capacity)。
- 在每个固定时间间隔(例如,每秒),系统会向令牌桶中添加一定数量的令牌(即产生令牌),直到桶的容量达到最大值。
- 当有请求或事件发生时,需要先从令牌桶中获取一个令牌。
- 如果桶中有可用的令牌,则允许请求通过,并移除一个令牌。
- 如果桶中没有令牌,则需要等待,或者直接拒绝请求,这取决于具体的限流策略。
令牌桶算法的优点在于可以对请求的速率进行平滑的控制,且具备较好的适应性,可以应对突发流量。由于令牌的产生速率是固定的,因此可以精确控制系统的请求处理速率,防止系统的过载和资源耗尽。
在分布式系统中,令牌桶算法也常被用于实现分布式限流,保护后端服务免受过多请求的影响,确保系统的稳定性和可靠性。
二、基于redis实现令牌桶算法
这个算法,设计的时候主要是考虑到要支持分布式系统的令牌桶资源共享,因此这样设计,下面就是具体的实战代码
首先是配置:
application.yml:
yaml复制代码server:
port: 8081
spring:
#数据库连接配置
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://ip:3306/study?characterEncoding=utf-8&useSSL=false
username:
password:
redis:
# 地址
host:
# 端口,默认为6379
port: 6379
# 数据库索引
database: 8
# 密码
password:
# 连接超时时间
timeout: 10s
#mybatis的相关配置
mybatis:
#mapper配置文件
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.zhg.demo.mybatis.entity
#开启驼峰命名
configuration:
map-underscore-to-camel-case: true
maven依赖:
xml复制代码<!-- 引入了AspectJ的运行时库-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.7</version>
</dependency>
<!-- AspectJ编译器,用于编译切面 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjtools</artifactId>
<version>1.9.7</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<optional>true</optional>
</dependency>
<!-- redis 缓存操作 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.5.14</version>
</dependency>
redis配置:
kotlin复制代码package com.jlstest.springbootdemo.config;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* redis配置
*
* @author admin
*/
@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport
{
@Bean
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory)
{
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(connectionFactory);
FastJson2JsonRedisSerializer serializer = new FastJson2JsonRedisSerializer(Object.class);
// 使用StringRedisSerializer来序列化和反序列化redis的key值
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
// Hash的key也采用StringRedisSerializer的序列化方式
template.setHashKeySerializer(new StringRedisSerializer());