查漏补缺第十期(网易实习一面)
前言
目前正在出一个查漏补缺专题
系列教程, 篇幅会较多, 喜欢的话,给个关注❤️ ~
本专题主要以Java
语言为主, 好了, 废话不多说直接开整吧~
Session过期怎么处理
处理会话过期可以依赖于底层的Servlet
容器(例如Tomcat)或使用Spring Session
框架来管理会话。
以下是两种处理会话过期的常见方法:
- 使用
Servlet
容器的会话过期配置:Servlet
容器提供了一些配置选项来管理会话过期。你可以在application.properties
(或application.yml
)文件中添加以下配置:
# 设置会话过期时间为30分钟(单位:秒)
server.servlet.session.timeout=1800
上述配置将会话过期时间设置为30分钟。可以根据需要进行调整。
- 使用
Spring Session
框架:Spring Session
是一个用于管理会话的框架,它提供了对会话存储的抽象和灵活的配置选项。你可以将Spring Session
集成到Spring Boot
项目中,以更高级的方式管理会话过期。
添加Spring Session
依赖:在pom.xml
文件中添加Spring Session
的依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-session</artifactId>
</dependency>
配置会话过期时间:在application.properties
(或application.yml
)文件中添加以下配置:
# 设置会话过期时间为30分钟(单位:秒)
server.servlet.session.timeout=1800
启用Spring Session
:在启动类上添加@EnableRedisHttpSession
注解,以启用Spring Session
并指定会话存储方式(如Redis
):
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
@SpringBootApplication
@EnableRedisHttpSession
public class YourApplication {
// ...
}
上述配置示例中使用了Redis作为会话存储方式。你可以根据需要选择其他支持的存储方式。
无论你选择哪种方法,一旦会话过期,你可以在控制器或其他相关组件中处理会话过期的情况。例如,你可以通过捕获SessionDestroyedEvent
事件来执行一些特定的逻辑:
import org.springframework.context.ApplicationListener;
import org.springframework.security.web.session.HttpSessionDestroyedEvent;
public class SessionDestroyedListener implements ApplicationListener<HttpSessionDestroyedEvent> {
@Override
public void onApplicationEvent(HttpSessionDestroyedEvent event) {
// 处理会话过期的逻辑
}
}
上述代码是一个会话销毁事件的监听器示例,你可以在onApplicationEvent
方法中编写处理过期会话的逻辑。
-
也可以利用
iframe
自动刷新空页面来保持session
的续期 -
也可以存到数据库中比如
Mysql
如果你想将会话数据存储到MySQL数据库中,可以使用Spring Session
框架结合JDBC
来实现。
下面是使用Spring Boot和Spring Session将会话数据存储到MySQL数据库的步骤:
- 添加依赖:在
pom.xml
文件中添加Spring Session
和MySQL
数据库的依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-session</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
- 配置数据库连接:在
application.properties
(或application.yml
)文件中配置MySQL数据库连接信息:
spring.datasource.url=jdbc:mysql://localhost:3306/your_database_name
spring.datasource.username=your_username
spring.datasource.password=your_password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
- 创建会话实体
import javax.persistence.Entity;
import javax.persistence.Id;
@Entity
public class SessionEntity {
@Id
private String sessionId;
private byte[] sessionData;
// getters and setters
}
- 创建会话存储库:创建一个继承自
JpaRepository
的接口,用于访问和操作会话实体。
import org.springframework.data.jpa.repository.JpaRepository;
public interface SessionRepository extends JpaRepository<SessionEntity, String> {
}
- 配置会话存储:创建一个配置类,配置会话存储的方式为JDBC,并指定会话实体和存储库。
import org.springframework.context.annotation.Configuration;
import org.springframework.session.jdbc.config.annotation.web.http.EnableJdbcHttpSession;
import org.springframework.session.jdbc.config.annotation.web.http.JdbcHttpSessionConfiguration;
@Configuration
@EnableJdbcHttpSession
public class SessionConfig extends JdbcHttpSessionConfiguration {
public SessionConfig() {
super();
}
}
redis过期时间怎么设置,redis怎么续期
在Redis
中,你可以使用EXPIRE
命令来设置键的过期时间,并使用TTL
命令来获取键的剩余过期时间。要续期(延长)键的过期时间,你可以使用EXPIREAT
命令或PEXPIRE
命令。
下面是一些常见的Redis命令示例来设置和续期键的过期时间:
-
设置键的过期时间(以秒为单位):
EXPIRE key_name seconds
例如,将名为
mykey
的键的过期时间设置为60秒:EXPIRE mykey 60
-
获取键的剩余过期时间(以秒为单位):
TTL key_name
例如,获取名为
mykey
的键的剩余过期时间:TTL mykey
如果键已经过期或不存在,
TTL
命令将返回-2或-1。 -
续期(延长)键的过期时间:
-
EXPIREAT
命令:以Unix时间戳作为参数来设置键的过期时间。EXPIREAT key_name timestamp
例如,将名为
mykey
的键的过期时间延长到Unix时间戳为1735689600的日期和时间:EXPIREAT mykey 1735689600
-
PEXPIRE
命令:以毫秒为单位设置键的过期时间。PEXPIRE key_name milliseconds
例如,将名为
mykey
的键的过期时间延长500毫秒:PEXPIRE mykey 500
-
rabbitmq可以多个消费者订阅一个消费者吗,实现步骤
在RabbitMQ
中,多个消费者可以同时订阅一个消费者(也称为工作队列模式或任务队列模式)。这种模式下,多个消费者会从同一个队列中接收消息,并按照一定的策略来共享和处理这些消息。
下面是实现多个消费者订阅一个消费者的步骤:
-
创建一个消息队列:首先,在
RabbitMQ
中创建一个消息队列,作为消息的缓冲区。 -
发布消息:将消息发布到消息队列中。这可以通过
RabbitMQ
的生产者(发布者)完成。 -
创建多个消费者:创建多个消费者应用程序,它们将订阅同一个消息队列。
-
消费消息:在每个消费者应用程序中,设置消息接收逻辑,使其从消息队列中接收并处理消息。消费者可以使用
RabbitMQ
的消费者(订阅者)来实现。
以下是一个简单的Java
代码示例,展示了如何使用RabbitMQ
的Java
客户端实现多个消费者订阅一个消费者:
import com.rabbitmq.client.*;
public class Consumer {
private static final String QUEUE_NAME = "my_queue";
public static void main(String[] args) throws Exception {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明队列
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 设置每个消费者同时只能处理一条消息
channel.basicQos(1);
// 创建多个消费者
for (int i = 0; i < 3; i++) {
// 创建消费者实例
String consumerName = "Consumer " + (i + 1);
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(consumerName + " received: " + message);
// 模拟耗时操作
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 手动发送消息确认信号
channel.basicAck(envelope.getDeliveryTag(), false);
}
};
// 启动消费者
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
}
在上述代码中,我们创建了3个消费者,并且它们都订阅了同一个名为my_queue
的消息队列。每个消费者在接收到消息后会进行一些处理,并手动发送消息确认信号(basicAck
)来告知RabbitMQ该消息已被处理。注意,通过调整basicQos
方法的参数,我们限制了每个消费者同时处理的消息数量为1,以实现公平分发。
通过运行多个这样的消费者应用程序,它们将同时从队列中接收消息并处理,实现多个消费者订阅一个消费者的效果。
rabbitmq遇到重复下单怎么处理
当使用RabbitMQ
处理重复下单问题时,可以采取以下几种策略:
-
去重处理
:在消费者端进行去重处理,以确保同一订单只被处理一次。可以维护一个订单ID的集合,记录已经处理过的订单ID,当新的订单到达时,先检查该订单ID是否已存在于集合中,如果存在则不处理,否则进行处理并将订单ID加入到集合中。 -
幂等性处理
:确保消费者处理逻辑具备幂等性,即使同一消息被消费多次,最终的结果保持一致。这样即使发生重复消息,重复的处理操作也不会引起实际业务上的影响。例如,在处理订单下单消息时,可以在数据库中添加唯一约束或使用订单号作为主键,当重复的订单号到达时,数据库操作将失败,但不会产生实际的影响。
-
消息去重
:在消息发布端进行去重处理,以避免发送重复的消息到队列中。可以在发送消息前,先查询数据库或缓存中是否已存在相同的订单,如果存在则不发送消息,否则发送消息到队列。这种方法可以减少不必要的重复消息,但仍然需要保证消费者的处理逻辑具备幂等性。
-
消息超时处理
:在消息发送后设置一定的超时时间,在消费者端处理消息时,检查订单是否已经处理过。如果订单已经处理过,则忽略该消息;如果订单未处理,但超过了设定的超时时间,则重新处理该消息。
这种方法可以解决消息在网络传输过程中的重复问题,但仍然需要保证消费者的处理逻辑具备幂等性。
需要根据具体的业务场景和需求选择适合的处理策略。通常结合多种策略可以提高处理重复下单问题的可靠性和效率。
当处理重复下单问题时,以下是一个示例代码,结合了幂等性处理和消息超时处理:
import com.rabbitmq.client.*;
import java.io.IOException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.TimeoutException;
public class OrderConsumer {
private static final String QUEUE_NAME = "order_queue";
private static final String EXCHANGE_NAME = "order_exchange";
private static final String ROUTING_KEY = "order";
private static Set<String> processedOrders = new HashSet<>();
public static void main(String[] args) throws IOException, TimeoutException {
// 创建连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("localhost");
// 创建连接
Connection connection = factory.newConnection();
// 创建通道
Channel channel = connection.createChannel();
// 声明交换机和队列
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, true);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY);
// 设置每个消费者同时只能处理一条消息
channel.basicQos(1);
// 创建消费者实例
Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
String orderId = properties.getMessageId();
if (processedOrders.contains(orderId)) {
System.out.println("Order already processed: " + orderId);
// 手动发送消息确认信号
channel.basicAck(envelope.getDeliveryTag(), false);
} else {
// 模拟订单处理耗时
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 处理订单逻辑
System.out.println("Processing order: " + message);
// 标记订单已处理
processedOrders.add(orderId);
// 手动发送消息确认信号
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
};
// 启动消费者
channel.basicConsume(QUEUE_NAME, false, consumer);
}
}
在上述代码中,我们创建了一个订单消费者,它会从名为order_queue
的队列中接收订单消息。消费者在处理订单之前,会检查订单是否已经处理过,如果已处理则忽略该消息,否则进行订单处理,并将订单ID加入到已处理订单的集合中。
这样,即使同一个订单消息被重复发送到队列中,只有第一次会被处理,后续的重复消息会被忽略。
redis数据类型,zset实现原理
在Redis
中,有多种数据类型可供使用,其中之一是有序集合(Sorted Set)
,也称为有序列表(Sorted List)
,其实现原理如下:
有序集合是一个包含了成员(member)和分数(score)的数据结构,其中每个成员都关联着一个分数,用于进行排序。有序集合在Redis内部使用了一种称为跳跃表(Skip List)的数据结构来实现。
跳跃表是一种基于链表的数据结构,可以提供快速的插入、删除和查找操作,同时保持元素有序。它通过维护多层级的指针来实现高效的查找和范围查询。
当在Redis中创建一个有序集合(ZSET)时,Redis会为每个成员分配一个唯一的标识符,同时根据成员的分数构建跳跃表的结构。每个跳跃表节点都包含一个成员和分数,以及指向其他节点的指针。
通过使用跳跃表,有序集合能够在O(log(N))的时间复杂度内执行插入、删除和查找操作。它允许按照成员的分数进行排序,并支持根据分数范围进行范围查询,例如获取分数在指定范围内的成员列表。
有序集合还提供了一些其他的操作,例如根据成员获取其分数、根据成员进行排名(从小到大或从大到小)、计算成员之间的分数差值等。
由于有序集合的实现使用了跳跃表,这使得它成为Redis中非常高效和灵活的数据类型,适用于许多场景,例如排行榜、计数器、范围查询等。
总结起来,有序集合通过使用跳跃表作为底层数据结构,实现了成员有序、快速的插入、删除和查找操作。这使得有序集合成为Redis中强大且高效的数据类型之一。
redis淘汰策略
Redis
采用了多种淘汰策略(Eviction Policy
)来管理内存,以确保在内存使用达到限制时仍能保持数据的可用性。以下是Redis
中常见的淘汰策略:
-
LRU(Least Recently Used)
:选择最近最少使用的键进行淘汰。即当内存不足时,优先淘汰最近最少访问的键。 -
LFU(Least Frequently Used)
:选择最不经常使用的键进行淘汰。即当内存不足时,优先淘汰被访问次数最少的键。 -
Random(随机)
:随机选择要淘汰的键。这种策略是完全随机的,没有根据访问模式或其他指标进行考虑。 -
LRU-TTL(Least Recently Used with Time to Live)
:在最近最少使用的键中,优先淘汰剩余生存时间(TTL)较短的键。这种策略适用于有设置过期时间的键。 -
Allkeys-LRU
:在所有键中采用LRU策略进行淘汰,包括设置过期时间和未设置过期时间的键。 -
Volatile-LRU
:在设置了过期时间的键中采用LRU策略进行淘汰,优先淘汰具有较短剩余生存时间(TTL)的键。 -
Volatile-TTL
:在设置了过期时间的键中,优先淘汰具有较短剩余生存时间(TTL)的键。
Redis
的淘汰策略可以通过配置文件(redis.conf
)中的maxmemory-policy
选项进行设置。默认的淘汰策略是volatile-lru
,即优先淘汰具有较短剩余生存时间(TTL)的键。
需要注意的是,淘汰策略仅在Redis的内存使用达到maxmemory
配置限制时才会触发。如果Redis的内存使用未超过限制,即使设置了淘汰策略,也不会主动淘汰数据。
通过选择适合应用场景的淘汰策略,可以在保持数据可用性的前提下,合理管理Redis
的内存使用,避免内存溢出问题。
redis如果一个key特别大,如果要删除掉会有什么问题,比如删除一个特别大的 ZSet,怎么删
当要删除一个特别大的数据结构(如一个特别大的ZSet)时,可能会面临以下几个问题:
-
删除操作可能会导致
Redis
在删除过程中出现阻塞,从而影响其他操作的性能。当执行删除操作时,Redis
会一次性将整个数据结构加载到内存中,然后进行删除操作。如果数据结构非常大,可能会占用大量的内存和CPU资源,并导致Redis
在删除期间变得不可响应。 -
删除操作可能会导致
Redis
的内存占用过高。当删除一个特别大的ZSet
时,Redis需要将整个ZSet
的数据加载到内存中进行删除操作,这可能导致Redis
的内存占用达到或接近内存限制。如果内存不足,可能会触发Redis
的淘汰策略,导致其他键被淘汰。
针对以上问题,可以考虑采取以下措施来删除一个特别大的ZSet
:
-
分批删除:将删除操作分批进行,每次删除一部分数据。可以使用
ZSCAN
命令遍历ZSet,并使用ZREM
命令逐个删除ZSet的成员。这样可以减少单次删除操作的数据量,减轻Redis的负担,并降低对其他操作的影响。ZSCAN key cursor [MATCH pattern] [COUNT count] ZREM key member [member ...]
注意,分批删除可能需要多次执行,直到将整个ZSet删除完毕。
-
使用后台线程删除:如果对实时性要求不高,可以将删除操作放入后台线程进行处理,以减少对
Redis
主线程的影响。可以使用Redis
的Lua
脚本或编写自定义的后台任务来异步删除特别大的ZSet
。例如,可以编写一个异步任务,使用
ZSCAN
命令遍历ZSet,并使用ZREM
命令逐个删除ZSet的成员。然后将该任务放入一个异步任务队列中,由后台线程逐个执行删除操作。 -
谨慎处理删除过程中的阻塞情况:如果删除操作无法避免对
Redis
主线程的阻塞,可以在删除期间暂时停止对Redis
的其他操作,以避免阻塞影响其他请求的性能。
结束语
大家可以针对自己薄弱的地方进行复习, 然后多总结,形成自己的理解,不要去背~
本着把自己知道的都告诉大家,如果本文对您有所帮助,点赞+关注
鼓励一下呗~
相关文章
- 查漏补缺第一期(Redis相关)
- 查漏补缺第二期(synchronized & 锁升级)
- 查漏补缺第三期(分布式事务相关)
- 查漏补缺第四期(Mysql相关)
- 查漏补缺第五期(HashMap & ConcurrentHashMap)
- 查漏补缺第六期(京东一面)
- 查漏补缺第七期(美团到店一面)
- 查漏补缺第八期(阿里一面)
- 查漏补缺第九期(阿里二面)
项目源码(源码已更新 欢迎star⭐️)
往期设计模式相关文章
- 一起来学设计模式之认识设计模式
- 一起来学设计模式之单例模式
- 一起来学设计模式之工厂模式
- 一起来学设计模式之建造者模式
- 一起来学设计模式之原型模式
- 一起来学设计模式之适配器模式
- 一起来学设计模式之桥接模式
- 一起来学设计模式之组合模式
- 一起来学设计模式之装饰器模式
- 一起来学设计模式之外观模式
- 一起来学设计模式之享元模式
- 一起来学设计模式之代理模式
- 一起来学设计模式之责任链模式
- 一起来学设计模式之命令模式
- 一起来学设计模式之解释器模式
- 一起来学设计模式之迭代器模式
- 一起来学设计模式之中介者模式
- 一起来学设计模式之备忘录模式
- 一起来学设计模式之观察者模式
- 一起来学设计模式之状态模式
- 一起来学设计模式之策略模式
- 一起来学设计模式之模板方法模式
- 一起来学设计模式之访问者模式
- 一起来学设计模式之依赖注入模式
设计模式项目源码(源码已更新 欢迎star⭐️)
Kafka 专题学习
- 一起来学kafka之Kafka集群搭建
- 一起来学kafka之整合SpringBoot基本使用
- 一起来学kafka之整合SpringBoot深入使用(一)
- 一起来学kafka之整合SpringBoot深入使用(二)
- 一起来学kafka之整合SpringBoot深入使用(三)
项目源码(源码已更新 欢迎star⭐️)
ElasticSearch 专题学习
项目源码(源码已更新 欢迎star⭐️)
往期并发编程内容推荐
- Java多线程专题之线程与进程概述
- Java多线程专题之线程类和接口入门
- Java多线程专题之进阶学习Thread(含源码分析)
- Java多线程专题之Callable、Future与FutureTask(含源码分析)
- 面试官: 有了解过线程组和线程优先级吗
- 面试官: 说一下线程的生命周期过程
- 面试官: 说一下线程间的通信
- 面试官: 说一下Java的共享内存模型
- 面试官: 有了解过指令重排吗,什么是happens-before
- 面试官: 有了解过volatile关键字吗 说说看
- 面试官: 有了解过Synchronized吗 说说看
- Java多线程专题之Lock锁的使用
- 面试官: 有了解过ReentrantLock的底层实现吗?说说看
- 面试官: 有了解过CAS和原子操作吗?说说看
- Java多线程专题之线程池的基本使用
- 面试官: 有了解过线程池的工作原理吗?说说看
- 面试官: 线程池是如何做到线程复用的?有了解过吗,说说看
- 面试官: 阻塞队列有了解过吗?说说看
- 面试官: 阻塞队列的底层实现有了解过吗? 说说看
- 面试官: 同步容器和并发容器有用过吗? 说说看
- 面试官: CopyOnWrite容器有了解过吗? 说说看
- 面试官: Semaphore在项目中有使用过吗?说说看(源码剖析)
- 面试官: Exchanger在项目中有使用过吗?说说看(源码剖析)
- 面试官: CountDownLatch有了解过吗?说说看(源码剖析)
- 面试官: CyclicBarrier有了解过吗?说说看(源码剖析)
- 面试官: Phaser有了解过吗?说说看
- 面试官: Fork/Join 有了解过吗?说说看(含源码分析)
- 面试官: Stream并行流有了解过吗?说说看
推荐 SpringBoot & SpringCloud (源码已更新 欢迎star⭐️)
博客(阅读体验较佳)
转载自:https://juejin.cn/post/7241714570674765882