2024-08-29



# 安装依赖
sudo apt-get update
sudo apt-get install build-essential tcl
 
# 下载Redis源码
wget http://download.redis.io/releases/redis-6.2.6.tar.gz
tar xzf redis-6.2.6.tar.gz
cd redis-6.2.6
 
# 编译Redis
make
 
# 运行Redis
make install
 
# 运行Redis服务端
redis-server
 
# 另开一个终端,运行Redis客户端
redis-cli
 
# 编写多实例配置文件
mkdir /etc/redis
mkdir /var/redis
mkdir /var/redis/6379
cp redis.conf /etc/redis/redis-6379.conf
 
# 修改配置文件
sed -i 's/port 6379/port 6380/' /etc/redis/redis-6379.conf
sed -i 's/dir \./dir \/var\/redis\/6380/' /etc/redis/redis-6379.conf
sed -i 's/daemonize no/daemonize yes/' /etc/redis/redis-6379.conf
sed -i 's/logfile ""/logfile "\/var\/log\/redis_6380.log"/g' /etc/redis/redis-6379.conf
 
# 启动第二个Redis实例
redis-server /etc/redis/redis-6379.conf

这段代码演示了如何在Ubuntu系统上安装和配置Redis的基本步骤,并展示了如何创建一个新的实例。注意,这只是一个示例,实际部署时应根据具体环境调整配置。

2024-08-29

在实现点赞功能时,我们通常需要考虑点赞者无法重复点赞,以及需要记录每个用户的点赞数量。以下是使用Redis实现点赞功能的一个简单示例:




import redis
 
# 连接Redis
r = redis.Redis(host='localhost', port=6379, db=0)
 
# 点赞
def like_post(user_id, post_id):
    # 使用用户ID和帖子ID作为key,用于检查用户是否已点赞
    like_key = f"like:{post_id}:{user_id}"
    
    # 检查用户是否已点赞
    if r.exists(like_key):
        return False  # 用户已点赞,返回False
    
    # 用户未点赞,执行点赞操作
    r.set(like_key, "1")  # 设置点赞标识
    r.incr(f"like_count:{post_id}")  # 增加帖子的点赞数
    return True  # 点赞成功,返回True
 
# 取消点赞
def unlike_post(user_id, post_id):
    like_key = f"like:{post_id}:{user_id}"
    
    # 检查用户是否已点赞
    if not r.exists(like_key):
        return False  # 用户未点赞,返回False
    
    # 用户已点赞,执行取消点赞操作
    r.delete(like_key)  # 删除点赞标识
    r.decr(f"like_count:{post_id}")  # 减少帖子的点赞数
    return True  # 取消点赞成功,返回True
 
# 检查用户是否点赞
def is_liked(user_id, post_id):
    like_key = f"like:{post_id}:{user_id}"
    return r.exists(like_key)  # 返回用户是否点赞的布尔值
 
# 获取帖子的点赞数
def get_like_count(post_id):
    return r.get(f"like_count:{post_id}") or 0  # 返回点赞数,如果不存在则默认为0

在这个示例中,我们使用了Redis的SET数据结构来记录每个用户是否对帖子进行了点赞,使用INCRDECR命令来增加或减少点赞数。每个帖子的点赞数通过like_count:{post_id}来记录。这样的实现方式确保了每个用户只能点赞一次,且点赞数量正确地增加或减少。

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应用程序中。