2024-08-29



import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
 
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
 
    @Bean
    public JwtAuthenticationFilter jwtAuthenticationFilter() {
        return new JwtAuthenticationFilter();
    }
 
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("user").password("password").roles("USER");
    }
 
    @Bean
    public PasswordEncoder passwordEncoder() {
        return NoOpPasswordEncoder.getInstance(); // 仅用于示例,生产环境应使用更安全的编码器
    }
 
    @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }
 
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
                .and()
                .authorizeRequests()
                .antMatchers("/login").permitAll()
                .anyRequest().authenticated()
                .and()
                .addFilterBefore(jwtAuthenticationFilter(), UsernamePasswordAuthenticationFilter.class);
    }
}

这个代码示例展示了如何在Spring Boot应用中使用Spring Security和JWT进行鉴权。它定义了一个SecurityConfig类,继承自WebSecurityConfigurerAdapter,并配置了HTTP安全策略,包括创建无状态会话、禁用C

2024-08-29

Redis 是一个开源的使用 C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 数据库,并提供多种语言的 API。

Redis 的数据结构:

  1. 字符串(String)
  2. 列表(List)
  3. 集合(Set)
  4. 有序集合(Sorted Set)
  5. 哈希(Hash)
  6. 位图(Bitmap)
  7. HyperLogLog
  8. Stream

解决方案和实例代码:

  1. 字符串(String)



# 设置键值
redis.set('key', 'value')
# 获取键值
redis.get('key')
  1. 列表(List)



# 在列表左侧插入元素
redis.lpush('list', 'element')
# 在列表右侧插入元素
redis.rpush('list', 'element')
# 获取列表所有元素
redis.lrange('list', 0, -1)
  1. 集合(Set)



# 添加元素到集合
redis.sadd('set', 'element')
# 获取集合所有元素
redis.smembers('set')
  1. 有序集合(Sorted Set)



# 添加元素到有序集合
redis.zadd('sortedset', {'element': score})
# 获取有序集合所有元素
redis.zrange('sortedset', 0, -1)
  1. 哈希(Hash)



# 设置哈希字段
redis.hset('hash', 'field', 'value')
# 获取哈希字段值
redis.hget('hash', 'field')
# 获取哈希所有字段值
redis.hgetall('hash')
  1. 位图(Bitmap)



# 设置位图的位值
redis.setbit('bitmap', offset, value)
# 获取位图的位值
redis.getbit('bitmap', offset)
  1. HyperLogLog



# 添加元素到 HyperLogLog
redis.pfadd('hyperloglog', 'element')
# 估算 HyperLogLog 的基数
redis.pfcount('hyperloglog')
  1. Stream



# 添加消息到 Stream
redis.xadd('stream', {'field': 'value'})
# 获取 Stream 中的消息
redis.xrange('stream', '-', '-', count=10)

注意:以上代码示例使用的是 Python redis 库。在实际应用中,需要先安装 redis 库:pip install redis。以上代码仅为数据操作的示例,实际使用时需要创建 Redis 对象并连接到 Redis 服务器。

2024-08-29

在使用Redis进行数据重复提交拦截时,可以通过设置一个与提交操作相关联的唯一标识(如用户ID、会话ID、表单Token等)作为Redis的key,并设置一个合理的过期时间来控制。如果key已经存在于Redis中,则表示该操作已经执行过,可以阻止重复提交。

以下是一个简单的Python示例,使用redis-py库和拦截器来实现数据的重复提交拦截:




import redis
 
# 初始化Redis客户端
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
 
def repeat_submit_interceptor(unique_key, expire_time=3600):
    """
    重复提交拦截器
    :param unique_key: 用于标识操作的唯一键
    :param expire_time: key的过期时间,防止无限期占用内存
    :return: True 允许提交, False 拒绝提交
    """
    if redis_client.exists(unique_key):
        # 如果key已经存在,表示操作已执行
        return False
    else:
        # 如果key不存在,设置key,表示操作执行中
        redis_client.set(unique_key, "1", ex=expire_time)
        return True
 
# 假设用户ID为唯一标识
user_id = "user123"
 
# 进行提交前检查
if repeat_submit_interceptor(user_id):
    # 允许提交操作
    print("Operation allowed. Proceed with submission.")
else:
    # 拒绝重复提交
    print("Operation rejected. Submission already made.")

在实际应用中,你需要根据你的应用逻辑来设计唯一键的生成方式,并确保它能够在不同的上下文中区分开不同的操作。

2024-08-29



from channels.generic.websocket import AsyncWebsocketConsumer
from channels.routing import route
from asgiref.sync import async_to_sync
import redis
 
# 连接到Redis
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
 
class ChatConsumer(AsyncWebsocketConsumer):
    async def connect(self):
        # 连接WebSocket时被调用
        await self.accept()
        await redis_client.hset('chat_consumers', self.channel_name, '')
 
    async def disconnect(self, close_code):
        # 断开WebSocket连接时被调用
        await redis_client.hdel('chat_consumers', self.channel_name)
 
    async def receive(self, text_data):
        # 接收到消息时被调用
        await redis_client.lpush('chat_messages', text_data)
        await self.send_chat_message(text_data)
 
    async def send_chat_message(self, message):
        # 发送消息给所有连接的客户端
        await self.send(text_data=message)
 
    @staticmethod
    async def broadcast_chat_message(message):
        # 广播消息给所有连接的客户端
        for consumer in async_to_sync(redis_client.hkeys)('chat_consumers'):
            await consumer.send_chat_message(message)
 
# 路由定义
application = route('websocket.connect', ChatConsumer.connect) \
              .route('websocket.disconnect', ChatConsumer.disconnect) \
              .route('websocket.receive', ChatConsumer.receive)

这个示例代码展示了如何使用Django Channels和Redis来创建一个简单的WebSocket聊天服务。它包括连接处理、断开连接处理、接收消息处理以及广播消息给所有连接的客户端。这个例子是一个很好的起点,展示了如何将Redis用作存储活跃的WebSocket连接和消息队列。

2024-08-29

Redisson 提供了分布式锁和相关同步器,但是它也提供了 RateLimiter 限流器,用于控制Redis中的资源访问速率。RateLimiter 基于 Lua 脚本和 Redis 的排序集合(Sorted Set)实现。

RateLimiter 主要有两种模式:

  1. 普通模式(Default Mode):控制单位时间内的通过请求数。
  2. 固定窗口模式(Fixed Window Mode):类似于普通模式,但是算法更复杂,允许在时间窗口中进行配额重置。

下面是使用 Redisson 创建 RateLimiter 的示例代码:




Config config = new Config();
// 配置Redisson的相关配置信息
// ...
RedissonClient redisson = Redisson.create(config);
 
// 创建一个名为"myRateLimiter"的 RateLimiter 对象,设置每秒钟通过请求数不超过 10 个
RateLimiter rateLimiter = redisson.getRateLimiter("myRateLimiter");
rateLimiter.trySetRate(RateType.OVERALL, 10, 1, RateIntervalUnit.SECONDS);
 
// 尝试获取许可证
if (rateLimiter.tryAcquire()) {
    // 如果获取到许可证,则执行业务逻辑
    // ...
} else {
    // 如果无法获取到许可证,则进行限流处理或者等待
    // ...
}

在上述代码中,我们首先配置了 Redisson 客户端,然后创建了一个名为 "myRateLimiter" 的 RateLimiter 对象,并设置了每秒钟通过请求数不超过 10 个的限制。在实际业务中,我们通过调用 tryAcquire 方法来尝试获取许可证,如果获取到许可证,则可以执行后续的业务逻辑;如果无法获取到许可证,则可以根据实际需求进行限流处理或等待。

2024-08-29

在搭建Redis 6.x版本的调试环境时,通常需要以下几个步骤:

  1. 下载Redis 6.x的源码。
  2. 编译Redis源码。
  3. 配置Redis,可能需要创建配置文件。
  4. 启动Redis服务器。
  5. 验证Redis服务是否正常运行。

以下是在Linux环境下搭建Redis 6.x调试环境的示例步骤:




# 1. 下载Redis 6.x源码
wget http://download.redis.io/releases/redis-6.2.5.tar.gz
 
# 2. 解压源码
tar xzf redis-6.2.5.tar.gz
 
# 3. 编译Redis
cd redis-6.2.5
make
 
# 4. 创建配置文件(如果需要)
cp redis.conf /etc/redis.conf
 
# 5. 启动Redis服务器
src/redis-server /etc/redis.conf
 
# 6. 验证Redis服务
redis-cli ping

确保你的系统已经安装了makewget。如果系统是Ubuntu或Debian,可以使用以下命令安装:




sudo apt-get update
sudo apt-get install build-essential wget

如果是CentOS或者Red Hat,可以使用以下命令安装:




sudo yum groupinstall 'Development Tools'
sudo yum install wget

以上步骤仅供参考,具体的Redis版本和配置可能会有所不同。

2024-08-29

为了让Redis在systemctl下启动,你需要创建一个systemd服务单元文件。以下是一个基本的例子:

  1. 创建Redis服务文件 /etc/systemd/system/redis.service



[Unit]
Description=Redis In-Memory Data Store
After=network.target
 
[Service]
User=redis
Group=redis
ExecStart=/usr/local/bin/redis-server /etc/redis/redis.conf
ExecStop=/usr/local/bin/redis-cli shutdown
Restart=always
 
[Install]
WantedBy=multi-user.target

确保ExecStart中的路径是你的Redis服务器程序的正确路径,同样redis.conf配置文件的路径也要正确。

  1. 重新加载systemd管理器配置:



sudo systemctl daemon-reload
  1. 启动Redis服务:



sudo systemctl start redis
  1. 设置Redis服务开机自启:



sudo systemctl enable redis

确保你已经根据你的系统配置修改了服务文件中的用户和组。如果你没有为Redis创建特定的用户和组,你可以使用root用户运行Redis。

注意:如果你的Redis不是安装在默认路径或者使用了非默认的配置文件路径,你需要相应地修改上述服务文件中的路径。

2024-08-29



import com.google.common.hash.BloomFilter;
import com.google.common.hash.Funnels;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class BloomFilterService {
 
    private final RedissonClient redissonClient;
    private final RBloomFilter<String> bloomFilter;
 
    @Autowired
    public BloomFilterService(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
 
        // 创建布隆过滤器,假设预计放入元素10000个,希望的误判率为0.01%
        bloomFilter = redissonClient.getBloomFilter("myBloomFilter");
        long expectedInsertions = 10000;
        double falseProbability = 0.01;
        BloomFilter<String> bloomFilterGuava = BloomFilter.create(Funnels.stringFunnel(Charsets.UTF_8), expectedInsertions, falseProbability);
        bloomFilter.tryInit(expectedInsertions, falseProbability);
 
        // 将Guava Bloom Filter的数据迁移到Redisson的Bloom Filter
        for (String element : bloomFilterGuava.asSet()) {
            bloomFilter.add(element);
        }
    }
 
    public boolean mightContain(String element) {
        // 检查元素是否可能存在于布隆过滤器中
        return bloomFilter.contains(element);
    }
 
    public void add(String element) {
        // 添加元素到布隆过滤器
        bloomFilter.add(element);
    }
}

这段代码首先创建了一个BloomFilterService类,它使用了Redisson提供的RBloomFilter接口。在构造函数中,它初始化了布隆过滤器的容量和期望的误判率,并且尝试从Guava的BloomFilter中迁移数据。mightContain方法用于检查元素是否可能存在于过滤器中,而add方法用于将元素添加到过滤器中。这个例子展示了如何将Redisson的布隆过滤器功能集成到Spring Boot应用程序中。

2024-08-29

Redis是一种开源的内存中数据结构存储系统,可以用作数据库、缓存和消息中间件。以下是Redis高级用法的一些概述:

  1. 发布/订阅模式:Redis的发布/订阅模式可以实现消息的传输,适用于解耦系统和实现消息队列。

    
    
    
    # 订阅者
    redis_client.subscribe('channel')
    for message in redis_client.listen():
        print(message)
     
    # 发布者
    redis_client.publish('channel', 'message')
  2. 事务:Redis的事务可以确保一系列操作的原子性。

    
    
    
    pipeline = redis_client.pipeline()
    pipeline.multi()
    pipeline.set('key1', 'value1')
    pipeline.set('key2', 'value2')
    pipeline.execute()
  3. 键空间通知:可以监控Redis中的特定事件,如键的过期或者键的更改等。

    
    
    
    redis_client.config_set('notify-keyspace-events', 'KEA')
    # 然后订阅__keyevent@0__:set
  4. 键的过期和内存淘汰策略:可以设置键的过期时间和内存淘汰策略。

    
    
    
    redis_client.setex('key', 60, 'value')  # 设置键的过期时间为60秒
    redis_client.config_set('maxmemory-policy', 'allkeys-lru')  # 设置内存淘汰策略
  5. Lua脚本:可以在Redis服务器端执行复杂的操作,以保证操作的原子性。

    
    
    
    redis.call('set', 'key', 'value')
  6. 分布式锁:Redlock算法可以实现分布式锁,确保在多个节点上的数据一致性。

    
    
    
    def acquire_lock(lock_name, acquire_timeout=10, lock_timeout=10):
        identifier = str(uuid.uuid4())
        end = time.time() + acquire_timeout
        while time.time() < end:
            if redis_client.set(lock_name, identifier, ex=lock_timeout, nx=True):
                return identifier
            time.sleep(0.001)
        return False
     
    def release_lock(lock_name, identifier):
        pipe = redis_client.pipeline()
        while True:
            try:
                pipe.watch(lock_name)
                if pipe.get(lock_name) == identifier:
                    pipe.multi()
                    pipe.delete(lock_name)
                    pipe.execute()
                    return True
                pipe.unwatch()
                break
            except redis.exceptions.WatchError:
                pass
        return False
  7. 增量式反序列化:可以在客户端进行增量式反序列化,减少网络IO。

    
    
    
    for chunk in redis_client.scan_iter('key*'):
        print(chunk)
  8. 快照和持久化:可以定期保存数据库快照到磁盘,实现数据的持久化。

    
    
    
    save 900 1  # 900秒内至少1个键被修改
    save 300 10  # 300秒内至少10个键被修改
    stop-writes-on-bgsave-error yes  # 后台保存出错时停止写操作
    rdbcompression yes  # 使用LZF压缩
    dbfilename dump.rdb  # 文件名
    dir ./  # 文件路径
  9. 复制:Redis的复制功能可以实现数据的多
2024-08-29



from redis_cluster import RedisCluster
 
# 连接Redis哨兵模式集群
startup_nodes = [
    {"host": "127.0.0.1", "port": "26379"},
    {"host": "127.0.0.1", "port": "26380"},
    {"host": "127.0.0.1", "port": "26381"}
]
rc = RedisCluster(startup_nodes=startup_nodes, decode_responses=True)
 
# 使用Redis哨兵模式集群进行数据操作
rc.set("foo", "bar")
print(rc.get("foo"))  # 输出: 'bar'
 
# 持久化操作
# Redis的RDB持久化: 在指定的时间间隔内将内存中的数据集快照写入磁盘
# 在redis.conf中配置
# save 900 1      # 900秒内至少1个键被修改则触发保存
# save 300 10     # 300秒内至少10个键被修改则触发保存
# save 60 10000   # 60秒内至少10000个键被修改则触发保存
 
# Redis的AOF持久化: 记录每次写操作, 在重启时重新执行这些命令来恢复数据
# 在redis.conf中配置
# appendonly yes   # 开启AOF持久化
# appendfsync everysec  # 每秒同步一次至磁盘
 
# 去中心化集群配置
# 首先需要在每台服务器的redis.conf中配置
# cluster-enabled yes
# cluster-config-file nodes-6379.conf
# cluster-node-timeout 5000
# 然后使用Redis的`redis-cli`工具创建集群
# redis-cli --cluster create 127.0.0.1:6379 127.0.0.1:6380 127.0.0.1:6381 --cluster-replicas 1
 
# 使用Redis去中心化集群进行数据操作
# 需要使用Redis-py-cluster库来连接和操作去中心化集群

这个代码实例展示了如何使用redis-py-cluster库来连接和操作Redis的哨兵模式集群和去中心化集群。同时,也简单地展示了如何配置RDB和AOF两种持久化方式。在实际应用中,你需要根据具体的Redis服务器配置和业务需求来调整配置文件中的参数。