@Atuowired与@Resource区别:
1.类包不同:@Atuowired是Spring自带的,@Resource是jdk自带的
2.注入方式不同:@Atuowired按照类型注入,@Resource先按照名称,如果找不到在按照类型
SPI
概念:SPI全名Service Provider Interface,翻译过来就是服务提供接口
Spring增强的部分:Spring中的SPI相比于原生JDK,他的功能更为强大,因为他可以替换的类型不仅仅局限接口/抽象类,他可以是任何一个类,接口,注解;
-
到预先定义的地方(META-INF/service/+类名)去找配置文件
-
解析配置文件拿到全类名
-
通过反射获取实现类
SpringBoot的优势:
1.开箱即用 2.约定优于配置 3.内置tomcat 4.内置很多start
Mybatis中#与$符的区别
# 占位符,预编译
$ 字符串拼接,sql注入
ThreadLocal作用:作用:为每个线程提供独立的变量副本,使得每个线程在访问变量时获取到的都是自己线程独有的变量副本。可以有效解决线程并发安全问题,避免了不同线程间的互相干扰。
通俗的说:可以跨类跨方法传递变量。
内存溢出:太多垃圾对象。解决方案:remove()
登录流程:
session与cookie的区别:
1.存储位置不同:cookie存储在客服端浏览器,session存储在服务器
2.安全性:cookie存储在客户端安全性较低,session存储在服务器,安全性较高
3.性能方面:cookie每次请求会增加数据,性能较低,session相对性能较高
4.存储大小:cookie存储大小一般为4K,session的存储大小与内存有关
5.数据类型:cookie主要存储字符串类型的数据,而session可以存储任意类型数据
6.生命周期:cookie的生命周期可以设置,而session的生命周期一般为30分钟
7.使用场景:cookie存储在客户端,安全性能低,一般存储不重要的信息,如用户名等,session主要用来存储较重要的信息。
Session共享:
1.粘滞会话:会把用户请求记录到一个服务器上,并记录session ID ,之后该用户的每个请求都会到该服务器上,扩展受限制,负载不均衡
2.会话复制:内部的服务器同步session数据,一台服务器更新了session数据,其他副本服务器也会更新数据,时效性差,资源浪费
3.Session数据集中存储:将数据放到数据库进行集中处理
4.基于token认证(如:JWT)
面试题:乐观锁与悲观锁
悲观锁:认为每次对数据操作都会发声冲突,因此,在读取数据前就会锁定该资源,synchronized
关键字、ReentrantLock(重入锁)
等机制实现的就是悲观锁,它们通过互斥的方式来保证同一时间只有一个线程能执行特定代码块或方法。
乐观锁:假设很少发生冲突,因此在读取数据时,不立即加锁,但一个线程要更新数据时,才会检查在此期间数据是否已被其他线程修改过。常见的实现方式是使用CAS(Compare and Swap/Compare and Set)原子指令,或者通过版本号、时间戳等机制来判断数据是否有冲突。
面试题:库存表
字段:ioId入库单Id,productId商品Id,qty商品数量,productionDate生产日期,expirationDay保质期天数,expirationDate过期日期,version版本号等。
面试题:做过哪些SQL优化
在做商品入库单时,有一个过期日期的字段,需要计算得出,这是就会运算到整张表,造成索引失效,降低效率,我们就冗余了一个过期日期的字段,通过计算,得出这个字段的数据,这样大大提高了效率。
面试题:事务的传播行为
1.REQUIRED 有就用,没有就开新 √
2.SUPPORTS 有就用,没有就以非事务执行X
3.MANDATORY 必须有事务,没有就抛异常X
4.REQUIRES_NEW 永开新√
5.NOT_SUPPORTED 以非事务执行方法X
6.NEVER 非事务执行方法,如果已经存在事务就抛异常X
7.NESTED 如果有事务,则在嵌套事务中执行,如果没有事务,则执行√
面试题:jdbc的执行流程
JBDC的底层主要是三个接口对象,Connection、Statement、ResultSet。
Connection用于建立与数据库的连接,Statement用于向数据库发送sql语句,ResultSet用于封装sql查询语句的结果。
1.注册驱动(使用 Class.forName() 方法加载数据库驱动程序类)
2.获取连接对象(JDBC的底层其实是使用Socket进行连接数据库的。打开Connection)
3.执行SQL语句,返回执行结果(通过获取Statement实例执行SQL语句)
4.处理结果集(最后返回的结果集是ResultSet)
5.释放资源
面试题:mybatis的执行流程
了解:
(Executor执行器
Executor 执行器则是 MyBatis 框架中的 SQL 执行工具,它负责执行 MappedStatement 中定义的 SQL 语句,并根据配置决定是否开启缓存、是否处理批量操作等。当调用 SqlSession 的 CRUD 方法时,SqlSession 会委托给它的内部 Executor 执行器去完成具体的 SQL 执行工作。
Executor 是 MyBatis 执行 SQL 的入口,它是一个策略模式的体现,有多种实现(如 SimpleExecutor、ReuseExecutor、BatchExecutor 等),分别适用于不同的场景(例如是否启用缓存、是否进行批处理等)。
当用户通过 SqlSession 执行某个数据库操作时,Executor 会根据指定的方法名和参数找到相应的 MappedStatement,并负责执行该 SQL 语句。
1.SimpleExecutor – SIMPLE 就是普通的执行器。
2.ReuseExecutor-执行器会重用预处理语句(PreparedStatements)
3.BatchExecutor --它是批处理执行器
MappedStatement 对象
MappedStatement 对象是 MyBatis 核心配置文件中每一个 <select>、<insert>、<update>、<delete>
等标签的映射,它封装了 SQL 语句以及其参数类型、结果集映射等信息。在 MyBatis 初始化阶段,会将 XML 映射文件或注解解析成一个个 MappedStatement 对象存储起来。
StatementHandler 语句处理器
StatementHandler 是 Executor 与 JDBC Statement 接口交互的桥梁,它负责对 JDBC Statement 进行进一步的封装和控制。在 Executor 执行 SQL 的过程中,它会通过 StatementHandler 去真正操作数据库,StatementHandler 会根据 MappedStatement 中的信息设置 SQL 参数、预编译 SQL 语句以及处理查询结果集的映射等细节工作。)
三者之间的关系可以用以下步骤概括:
-
用户通过 SqlSession 调用一个方法,SqlSession 通过 Executor 找到对应的 MappedStatement。
-
Executor 使用找到的 MappedStatement,结合请求参数,通过 StatementHandler 创建并配置 JDBC Statement。
-
StatementHandler 根据 MappedStatement 中的 SQL 语句及参数映射信息,设置 SQL 参数并执行 SQL。
-
执行 SQL 后,StatementHandler 再次发挥作用,处理数据库返回的结果集,并将其按照 MappedStatement 中定义的结果映射转换为 Java 对象,最终返回给客户端。
面试题:mybatis的四种拦截器
1. Executor(执行器拦截器):
- 用途:拦截MyBatis执行器方法的执行。
2. StatementHandler(语句拦截器):
-- 用途:拦截SQL语句的执行。
3. ParameterHandler(参数拦截器):
- 用途:拦截SQL语句的参数设置。
4. ResultHandler(结果集拦截器):
- 用途:拦截从SQL语句返回的结果集的处理
面试题:反射应用场景
mybatis拦截器处理参数
使用hutool工具类中的ReflectUtil的getField()方法获得参数对象,使用setFieldValue对象获得的对象处理
面试题:mybatis拦截器执行流程
①创建自定义拦截器类,实现ibatis下的interceptor接口,重写interceptor()方法
②使用@Interceptor注解,并同时使用@Signature指定拦截器的接口类型,方法名与参数
③就把那个当前类放入IOC容器中加@Compontent注解
面试题:spring security的执行流程
spring security的执行流程,客户端到过滤器,再到DelegatingFilterProxy,到SecurityFilterChain,里面包含一些过滤器链,到filter2,再到servlet,到intercepter.
为什么在intercepter解析过token,为什么在ScecurityFilterChain中还要解析token,因为securityFilterChain作用在过滤器1和过滤器2之间,还没有执行到intercepter时,Security需要解析token进行验证。
Redis面试题:
Redis的特性(为什么要用Redis?):
1.内存数据库,速度比较快。
2.工作单线程,串行化,原子操作。IO是多线程的,避免上下文切换
3.IO模型(epoll),支持高并发
4.kv模型,v具有类型结构
5.具有本地方法,计算向数据移动。(a,b) => 交集
6.二进制安全,Value最大512M
Redis的数据结构:
set常用命令:应用:微信共同好友
zset(有序集合)常用命令 :应用:热搜
hash 应用:购物车
list:
BitMap:用于签到
Redis到底是多线程还是单线程的?
Redis6.0之前是单线程,6.0之后IO线程是多线程,工作线程是单线程
Redis数据持久化
Redis提供俩种持久化的方式:
RDB:存储数据结果,关注的是数据(快照)
AOF:存储操作过程,关注点在操作数据过程(命令)
RDB:优点:1.恢复数据比较快
2.备份的数据就是原始数据的大小,不会额外增加数据占用
缺点:1.快照时间有间隔,不能实时备份,丢失数据可能较多
2.开启子进程数据备份,在数据集比较庞大时,fork()子进程会非常耗时,造成服务器宕机。
AOF:优点:1.数据安全性高,不易丢失数据
2.AOF文件保存了所有写的操作,可读性强
缺点:1.AOF生成文件数据变大
2.恢复数据比RDB慢
在百万keys里的Redis里面。如何模糊查询某个key?
可以通过scan和keys *去查找,但是keys * 容易造成堵塞,还会消耗大量的资源。而scan是逐批次的返回key,避免堵塞和资源浪费
Redis面试题-缓存穿透,缓存击穿,缓存雪崩
1 穿透: 两边都不存在,既Redis和数据库中都没有该数据,C端用户访问Redis时,Redis中没有该数据,又向数据库的发送请求,数据库又没有该数据,又返回给Redis,再返回给C端用户,如果同一时间有大量用户访问该数据,就会造成缓存穿透。(皇帝的新装)解决办法: (黑名单) (布隆过滤器)
2 .击穿:一个热点的key失效了,这时大量的并发请求直接到达数据库.(对于某个热点数据(即经常被访问的数据),在缓存失效的一刻,大量并发请求同时到达,这些请求会直接穿透缓存层直达数据库,对数据库造成瞬时压力增大,可能导致数据库崩溃)(提前预热)
3 雪崩:大量key同时失效,导致所有请求都直接到达数据库,使得数据库短时间内承受过高负载,甚至宕机。 (避免大量的key同一时间失效,错峰)
MySQL 里有 2000w 数据,Redis 中只存 20w 的数据,如何保证 redis 中的数据都是热点数据?
1.Redis 过期删除策略
1)惰性删除:放任键过期不管,但是每次从键空间中获取键时,都检查取得的键是否过期,如果过期的话,就删除该键;如果没有过期,就返回该键。
缺陷:极端情况下会出现大量的key没有被再次访问,从而不会被清除,占用大量的内存
2)定期删除:每隔一段时间程序就对数据库进行一次检查,删除里面的过期键。至于要删除多少过期键,以及要检查多少个数据库,则由算法决定。
2.内存淘汰策略
Redis 内存数据集大小上升到一定大小的时候,就会施行数据淘汰策略。
Redis 提供 8 种数据淘汰策略:
LRU全称Least recently used,意思为淘汰掉最久未使用(即最老)的一条数据;
LFU全称Least-frequently used,意思为淘汰掉过去被访问次数最少的一条数据
淘汰策略名称 | 策略含义 | 人话 |
noeviction | 默认策略,不淘汰数据;大部分写命令都将返回错误 | 不删除任意数据(但redis还会根据引用计数器进行释放),这时如果内存不够时,会直接返回错误 |
volatile-lru | 从设置了过期时间的数据中根据 LRU 算法挑选数据淘汰(只针对设置过期的keys) | 从设置了过期时间的数据集中,选择最近最久未使用的数据释放 ,最老的删掉 |
allkeys-lru这个是最常用的 | 从所有数据中根据 LRU 算法挑选数据淘汰(所有keys) | 从数据集中(包括设置过期时间以及未设置过期时间的数据集中),选择最近最久未使用的数据释放 最老的删掉 |
allkeys-random | 从所有数据中随机挑选数据淘汰 | 随机选择一个数据进行释放; |
volatile-random | 从设置了过期时间的数据中,挑选越早过期的数据进行删除 | 从设置了过期时间的数据集中,随机 |
volatile-ttl | 从设置了过期时间的数据中,挑选越早过期的数据进行删除 | 从设置了过期时间的数据集中,选择马上就要过期的数据进行释放操作 |
allkeys-lfu | 从所有数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用) | 淘汰掉过去被访问次数最少的一条数据 |
volatile-lfu | 从设置了过期时间的数据中根据 LFU 算法挑选数据淘汰(4.0及以上版本可用 | 淘汰掉设置了过期时间的key过去被访问次数最少的一条数据 |
3.Redis主从同步机制
步骤如下:(全量)
1.从服务器向主服务器发送同步命令 sync;
2.主数据库接收到同步命令后,会执行 bgsave 命令,在后台生成一个 rdb 文件,并使用一个缓冲区记录从现在开始执行的所有写命令;
3.当主服务器执行完 bgsave 命令后,主服务器会将 bgsave 命令生成的 rdb 文件发送给从服务器;
4.从服务器接收到这个 rdb 文件,然后加载到内存 ;之后主服务器会把刚刚在缓存区的命令同步过来,从服务器就会执行这些命名。(两边就一致了)
5.以上处理完之后,之后主数据库每执行一个写命令,都会将被执行的写命令发送给从数据库。
4.Redis是单线程,但为什么快?
1. 纯内存操作
2. 单线程操作,避免了频繁的上下文切换
3. 合理高效的数据结构
4. 采用了非阻塞I/O多路复用机制 epool
5.Redis 和 Mysql 数据库数据如何保持一致性
-
1 先删缓存,再更新数据库
-
2 先更新数据库,再删除缓存
-
3 普通双删
-
4 延迟双删
5.3 图解
5.3.1 先删缓存,再更新数据库
1、线程A删除缓存数据,此时还没更新数据库
2、线程B查询缓存没有数据,查询数据库还是旧数据,放入缓存
3、线程C及其他线程使用旧缓存数据,缓存和数据库不一致
5.3.2 先更新数据库,再删除缓存
1、线程A更新数据库,此时还没有删除缓存
2、线程B及其他线程此时使用的还是旧缓存数据,和数据库内容不一致
5.3.3 普通双删
1、线程A先删除缓存,再更新数据库,再删除缓存
2、线程B查询缓存没有数据,在线程A更新数据库之前,查询到旧数据,此时系统时间片切换到线程A执行删除缓存,之后又轮到线程B放入缓存旧数据
3、线程C针对于线程A,查询缓存没有数据,查询到旧数据,放入缓存旧数据
都不能满足缓存和数据一致性。
5.3.4 延迟双删
1、线程A先删除缓存,之后更新数据库
2、线程B和线程C发现缓存没数据,查询数据库。线程B查询到的是旧数据,线程C查询到的是新数据。之后纷纷放入缓存
3、线程A延时3-5秒(时间一般要大于SQL执行时间+线程切换执行时间100ms足够),再将缓存删除。之后其他线程再查询缓存,发现没数据,再次查询数据库及放入缓存都是新数据
极端情况就是线程D,所以延时双删还是不一定能保证缓存及数据一致。
为什么要使用canal?
解决数据不一致问题
Canal 主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费,工作原理如下:
Canal 模拟 MySQL slave 的交互协议,伪装自己为 MySQL slave ,向 MySQL master 发送 dump 协议
MySQL master 收到 dump 请求,开始推送 binary log 给 slave (即 Canal )
Canal 解析 binary log 对象(原始为 byte 流)
使用场景:
-
数据库镜像
-
数据库实时备份
-
索引构建和实时维护(拆分异构索引、倒排索引等)
-
业务 cache 刷新
-
带业务逻辑的增量数据处理
原理:
6.Redis集群
Redis提供了多种集群模式以适应不同场景下的高可用性和水平扩展需求。以下是Redis集群模式:
-
主从复制(Master-Slave)模式:在此模式下,有一个主节点负责处理写入请求,而从节点则复制主节点的数据并提供读取服务。优点:实现简单,能实现数据冗余,通过读写分离提高系统性能。缺点:需要手动进行故障转移,无法自动处理主节点故障;不支持自动的数据分区(sharding),难以做到水平扩展。
-
哨兵(Sentinel)模式:Sentinel是Redis提供的一个高可用性解决方案,它能监控主从节点状态,并在主节点出现故障时自动完成故障转移。优点:解决了主从模式下手动故障转移的问题,提供了自动化监控和故障恢复机制。缺点:虽然比主从模式增加了自动化,但仍不支持自动的数据分区,且随着节点数量增加,管理和配置的复杂性也会增大。
-
Redis Cluster模式:Redis Cluster是官方正式支持的分布式解决方案,它采用了数据分片(sharding)技术,将数据分散在多个节点上。优点:真正实现了分布式存储,每个节点都可以处理读写请求,具备良好的水平扩展能力;内置了数据自动分割、故障检测与转移功能。缺点:相比其他模式更复杂,需要更多的网络资源和配置管理;客户端需要支持集群特性;跨slot的数据操作可能涉及多个节点,有一定复杂度。
MQ面试题
MQ有哪几种?RabbitMQ、RocketMQ、Kafka
你们用的是那种MQ?RabbitMQ,
为什么用MQ?
1.异步处理
场景说明:用户注册后,需要发注册邮件和注册短信,传统的做法有两种 1.串行的方式 2.并行的方式
串行方式: 将注册信息写入数据库后,发送注册邮件,再发送注册短信,以上三个任务全部完成后才返回给客户端。 这有一个问题是,邮件,短信并不是必须的,它只是一个通知,而这种做法让客户端等待没有必要等待的东西.
并行方式:将注册信息写入数据库后,发送邮件的同时,发送短信,以上三个任务完成后,返回给客户端,并行的方式能提高处理的时间。
消息队列:假设三个业务节点分别使用50ms,串行方式使用时间150ms,并行使用时间100ms。虽然并行已经提高的处理时间,但是,前面说过,邮件和短信对正常的使用网站没有任何影响,客户端没有必要等着其发送完成才显示注册成功,应该是写入数据库后就返回.
引入消息队列后,把发送邮件,短信不是必须的业务逻辑异步处理
由此可以看出,引入消息队列后,用户的响应时间就等于写入数据库的时间+写入消息队列的时间(可以忽略不计),引入消息队列后处理后,响应时间是串行的3倍,是并行的2倍。
2.应用解耦
场景:双11是购物狂节,用户下单后,订单系统需要通知库存系统,传统的做法就是订单系统调用库存系统的接口.
这种做法有一个缺点:
当库存系统出现故障时,订单就会失败。 订单系统和库存系统高耦合. 引入消息队列
订单系统
:
用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
库存系统
:
订阅下单的消息,获取下单消息,进行拆订单操作。 就算库存系统出现故障,消息队列也能保证消息的可靠投递,不会导致消息丢失.
3.流量削峰
场景: 秒杀活动,一般会因为流量过大,导致应用挂掉,为了解决这个问题,一般在应用前端加入消息队列。
作用:
1.可以控制活动人数,超过此一定阀值的订单直接丢弃.
2.可以缓解短时间的高流量压垮应用(应用程序按自己的最大处理能力获取订单)
常用的消息队列有哪些?
RabbitMQ(为什么要用RabbitMQ?是spring内置的MQ,选型用的MQ,社区比较活跃,有良好的UI界面)、RocketMQ、ActiveMQ、Kafka、ZeroMQ、MetaMq
交换机类型?
1、Direct Exchange
直连型交换机,根据消息携带的路由键将消息投递给对应队列。
大致流程,有一个队列绑定到一个直连交换机上,同时赋予一个路由键 routing key 。
然后当一个消息携带着路由值为abc,这个消息通过生产者发送给交换机时,交换机就会根据这个路由值abc去寻找绑定值的队列。
2、Fanout Exchange
扇型(广播)交换机,这个交换机没有路由键概念,就算你绑了路由键也是无视的。 这个交换机在接收到消息后,会直接转发到绑定到它上面的所有队列。
3、Topic Exchange
主题交换机,这个交换机其实跟直连交换机流程差不多,但是它的特点就是在它的路由键和绑定键之间是有规则的。
简单地介绍下规则:
* (星号) 用来表示一个单词 (必须出现的)
# (井号) 用来表示任意数量(零个或多个)单词
// * 代表两点之间一个占位单词 // # 代表后面所有,匹配所有
通配的绑定键是跟队列进行绑定的,举个小例子
队列Q1 绑定键为 *.TT.* 队列Q2绑定键为 TT.#
如果一条消息携带的路由键为 A.TT.B,那么队列Q1将会收到;
如果一条消息携带的路由键为TT.AA.BB,那么队列Q2将会收到;
当一个队列的绑定键为 "#"(井号) 的时候,这个队列将会无视消息的路由键,接收所有的消息。
当 * (星号) 和 # (井号) 这两个特殊字符都未在绑定键中出现的时候,此时主题交换机就拥有的直连交换机的行为。
如果只有 # ,它就实现了扇形交换机的功能。
所以主题交换机也就实现了扇形交换机的功能,和直连交换机的功能
如何解决死循环?重试机制
死信队列
面试题:你们是如何保证消息不丢失的?
答:我们创建死信队列,首先死信是消息在特定场景的表现形式,这些场景包括:
1.消息被拒绝访问,即RabbitMQ返回basicNack信号或被拒绝时basicReject。
2.消费者发生异常,超过最大尝试次数。其实Spring底层调用的就是basicNack。
3.消息的Expiration和队列的ttl过期时间。
4.消息队列达到最大长度
死信队列就是存储死信的消息队列
延迟队列三种形式:队列延迟、消息的延迟、使用延迟插件。
怎样使用死信队列?首先创建死信队列和死信交换机,让业务队列与死信交换机进行连接。
问:如何处理死信队列的消息? 答:进行人工干预
怎样进行人工干预?首先进行业务代码检查,然后对死信队列绑定一个消费者。
RabbitMQ如果有100万消息堆积在MQ,如何解决(消息堆积怎么解决?)
解决消息堆积有三种思路:
1.增加更多消费者,提高消费速度
2.在消费者内开启线程池加快消息处理速度
3.扩大队列容积,提高堆积上限,采用惰性队列
3.1在声明队列的时候可以设置属性x-queue-mode为lazy,即为惰性队列
3.2基于磁盘存储,消息上限高
3.3性能比较稳定,但基于磁盘存储,受限于磁盘IO,时效性会降低
Spring Bean的生命周期
1、单例
[容器启动]---->构造方法(实例化)----->set方法(依赖注入)----->init方法(初始化)---->使用---->[容器关闭]---->destroy方法(销毁)
2、多例
[使用对象]---->构造方法(实例化)----->set方法(依赖注入)----->init方法(初始化)---->[jvm垃圾回收]--->destroy方法(销毁)
Spring事务原理
Spring提供了事务管理的API,而它的实现依赖于AOP技术。在启动程序后,spring会扫描当前路径下所有包及子包,同时在执行含有@Transitional方法时创建代理,代理会提前开启事务,再去执行方法。当方法执行无异常时,则进行事务提交,有异常时,则进行回滚。
为什么使用延迟队列?
使用rabbitmq的延时队列插件,实现同一个队列中有多个不同超时时间的消息,并按时间超时顺序出队。应用场景:餐厅订餐,提前半小时电话或短息通知,订单30分钟支付等。
RabbitMQ如何保证消息的可靠性?
1.消费端保证消息的可靠性:
1.1消息确认
消费者从队列中 接收到消息时,消费者手动发送确认(baskAck).如果消费者在处理过程中发生异常时,那么消息在超时时间内将不会被删除,会再次被RabbitMQ投递给其他消费者。
1.2.死信队列(Dead Letter Queue)
当消息不能被正常消费时(比如达到最大重试次数),可以通过设置TTL(Time To Live)或者死信交换机(Dead Letter Exchange)将消息路由至死信队列,从而有机会后续分析和处理这些无法正常消费的消息。
2.生产端消息可靠性保证:
2.1消息持久化:
当生产者发布消息时,可以选择将其标记为持久化(persistent).这意味着即使RabbitMQ 服务器重启,消息也不会丢失,因为它们会被存储在磁盘上。
2.2.确认(Confirm)机制:
开启confirm回调模式后,消息至少被一个交换器接受后,向生产者发送一个确认信号.生产者可以根据这些确认信号判断,采取相应的重试策略。
RabbitMQ作为消息中间件并启用publisher confirms(发布者确认)与publisher returns(发布者退回)机制时,可以确保消息从生产者到交换机的投递过程得到更准确的状态反馈
如何保证消息的幂等性?
在RabbitMQ中,保证消费者的幂等性主要依赖于业务设计和实现
幂等性保证的基本原则
生产端:在消息发送前,先查询数据库,确认此消息是否已被处理过。如果是,则直接忽略;否则,继续处理,并在处理完成后更新消息状态为已处理。
消费端:
唯一标识:每个消息都携带一个全局唯一的ID或业务ID(BizId),如订单号、交易流水号等,以便在消费端能够识别重复的消息
命令reset master
专属于MySQL和MariaDB数据库管理系统,在复制(replication)环境中使用。该命令用于重置作为复制拓扑中主服务器(primary)的角色的复制状态。
如何保证消息的有序性
只设置一个消费者
分布式调度器的使用场景?
1.订单仓储系统,商品进入仓库更改状态。2.消息队列。3.删除过期路线。4.闹钟等lua脚本的使用场景:
在Redis中使用lua脚本,主要是其能够使多条redis语句具有原子性,在处理订单模块具有重要作用
JDK自带的线程池有哪些?
newCachedThreadPool():可缓存线程池
newFixedThreadPool():固定大小线程池
newSingleThreadExector():单线程线程池
newScheduledThreadPool():定时及周期任务线程池
线程池中核心线程数与最大线程数与缓冲任务队列的关系?(线程池的核心参数)
在java的线程池中,核心线程数(corePoolSize)、最大线程数(maximumPoolSize)以及缓冲队列(workQueue)之间存在着密切的关系,它们共同决定了线程池如何管理和调度任务:
1.核心线程(corePoolSize)是线程池中最基本的线程数,会一直存在线程池中,当提交新任务时,线程池会创建或保持多个核心线程来处理任务。
2.最大线程数(maximumPoolSize):当核心线程全都处于活跃状态且有新的任务提交时,线程池将创建额外的线程(临时工),直至线程总数达到maximumPoolSize。当线程数达到maximumPoolSize时,线程池不会创建新的线程。
3.缓冲队列 (workQueue):
-
只要有任务,新提交的任务会被放入缓冲队列中等待执行,当有长工不忙的时候,长工领任务执行.
-
当所有核心线程都在忙,并且缓冲队列尚未满时,新提交的任务也会被放入队列中排队(等待长工去执行)。
-
若队列已满(即有界队列的情况),并且线程数未达到maximumPoolSize,线程池会创建新的非核心线程(临时工)来执行任务。
-
当线程数已经达到maximumPoolSize,而缓冲队列也已经满了,这时线程池将依据预先设定的拒绝策略来处理无法接受的任务,例如丢弃任务、抛出异常或调用用户自定义的拒绝处理器。
综上所述,线程池的工作机制是这样的:
-
先使用核心线程执行任务。
-
当核心线程不足时,新任务入队列等待。
-
当队列满且线程数未达最大值时,增加非核心线程执行任务。
-
当队列满且线程数已达最大值时,触发拒绝策略处理新来的任务。
拒绝策略:丢失任务,抛出异常,使用自定义的拒绝处理器
为什么阿里巴巴不让使用JDK自带的线程池?
1.fixedThreadPool和singleThreadPool中,阻塞队列长度是Inter.MAX_VALUE,一旦请求量增加会造成内存溢出问题。
2.CachedThreadPool和ScheduledThreadPool最大线程数量是inter.MAX-value,一旦请求量增加会造成宕机的问题。
3.缺乏灵活性与可控性,新手误用风险
你们是怎样使用线程的?
hutool工具里面提供的ExecutorBuilder进行创建线程:
ExecutorService executor = ExecutorBuilder.create()
.setCorePoolSize(5)
.setMaxPoolSize(10)
.setWorkQueue(new LinkedBlockingQueue<>(100))
.build();
项目中有没有用过调度器?
监控消息队列的消息大小(定期检查队列消息)、蚂蚁组队的出行单(定时检查,超过当期那时间的不进行显示),redis定期检查key的数量
Spring自带的task与xxl-Job的区别?
Timer是JDK自带,单线程执行,task是单体应用,同步执行器执行,异步线程。xxl是分布式的,能保证高可用
MongoDB与MySQL的区别?
MongoDB是非关系型、文档数据库,使用BSON存储数据,用集合和文档来存储数据,使用灵活,可以存储不同结构的数据,天生支持分布式的。MySQL是关系型数据库,具有表结构,使用表和行来存储数据。
MongoDB在哪些场景使用?
游戏开发,物流电商,网站数据存储,大数据分析等。
ES是什么?分词器?
ES是分布式搜索和分析引擎,是非关系型数据库,天生支持分布式。我们常用的分词器有ik_smart和ik_max_word,ik_smart: 这种模式更侧重于保持语义完整性,尽量进行较少的、更有意义的拆分。ik_max_word: 此模式致力于最大化地拆分文本,即尽可能多地生成可能的词语组合,包括单字、双字直至整个短语。
ES使用的场景?
全文搜索:电商搜索、日志监控与分析、数据分析、地理信息系统、搜索推荐。
你们项目日志如何处理?EFK,他们的区别
使用ELK日志采集系统,Elasticsearch (ES)分布式搜索分析引擎,Logstash 收集日志后交给ES,Kibana 是一个开源的数据可视化工具。
现用EFK,F:FileBeat更轻量级,资源消耗较小。
流程:系统生成日志文件------->FileBeat采集指定文件夹中的日志文件------->存储到ES中--------->通过Kibana可视化界面进行实时监控。
什么是MDC,原理?
MDC映射调试上下文,是 log4j 和 logback 提供的一种方便在多线程条件下记录日志的功能。
原理:当请求来时生成一个traceId放在InheritableThreadLocal里,然后打印时去取就行了。
解决:阿里开元的ttl获取父线程的上下文。
有没有做过微信登录?微信公众号开发?微信小程序开发?
做过,微信登录首先是获取前端传的code,根据code获取asscource-token,获取openId,根据openId获取用户的头像,昵称等。我们做了个蚂蚁组队微信公众号开发,模拟滴滴出行,使用腾讯地图工具,获取线路,输入出发点,途经点,目的地,能够出现真实的线路,将数据保存在数据库中,用户可查看线路。 微信小程序开发与公众号开发类似,只不过,没有获取openId。
分布式与微服务区别?
1.概念层面:分布式系统:是一组网络协议相互协作,提高系统的可用性,可扩展性和稳定性
微服务架构:是一种特殊的分布式实现方式,强调将应用程序拆分成小型,互相独立的服务。
2.粒度划分:分布式系统中的服务划分粒度可大可小,微服务更倾向于细粒度的服务划分。
3.目标:分布式系统旨在通过负载均衡,提高系统的稳定性,微服务架构旨在简化开发流程,适应快速变化的业务需求。
Nacos配置中心动态刷新原理
1.pull模式:客服端去拉取,频繁的向服务器发送请求,请求间隔难以设置,时效性差。
2.push模式:服务端主动向客服端推送,但是消耗大量资源去维持心跳,造成资源浪费。
3.nocas设计:客服端向服务端发送请求,但是会携带长轮询的时间,默认30秒,服务端接收到请求时,会挂起一段时间,在这段时间内,如果服务端发生变化,会立即响应给客服端。本质就是服务端来控制客服端的请求响应时间,来减少客服端无效的请求。
目前主流的负载方案分为哪几种?
1.服务端负载均衡:在消费者和服务提供方中间使用独立的代理方式进行负载,有硬件的(比如 F5),也有软件的(比如 Nginx,openResty
2.客服端负载均衡:客户端根据自己的请求情况做负衡,Ribbon 就属于客户端自己做负载均衡
饥饿加载(使用Ribbon负载均衡,第一次请求时会超时,你们如何处理)
Ribbon默认是采用懒加载,既第一次访问时才会去创建LoadBalanceClient,请求时间会很长
饥饿加载会在项目启动时创建,降低第一次访问的耗时。
Feign和OpenFeign的区别:
OpenFeign支持spring mvc 注解,整合了更多的扩展(请求重试策略、超时控制、请求拦截器)
OpenFeign原理:(面试不做要求,自己理解即可)
1.在启动类开启@EnableFeignClients,注解开启Feign客户端支持,此时Spring会扫描并处理带有@FeignClient注解的接口,利用Java动态代理技术,在运行时创建接口的代理对象。当调用这个代理对象的方法时,实际上是触发了一个HTTP请求到远程服务
2.开发者只需要定义一个接口并使用OpenFeign提供的注解来描述HTTP请求的方法、URL、请求头、参数等信息。例如,使用@RequestMapping
、@GetMapping
、@PostMapping
等注解来指示HTTP方法和路径。
http://oeder-server-app/api/order/prdering
| 结合Nacos(restTemplate,加注解@LoadBalanced)
http://192.168.11.45:8888/api/order/ordering
| httpClient
json或String
| 对象
反序列化
如何防止黑客恶意攻击
如果一个ip用户对我们应用一秒钟访问超过十次,就对其进行拦截,使用lua脚本,设置key的值为时间戳+ip,v设置为访问次数,ttl设置为俩秒,如果一秒内访问的次数超过10秒就将其移除。·
流量网管与服务网管的区别:
流量网关:(如Nignx,OpenResty,Kong)是指提供全局性的,如例如 HTTPS证书认证、Web防火墙、全局流量监控,黑白名单等。
服务网关:(如Spring Cloud Gateway)与业务紧耦合的,提供单个业务级别的策略,如服务治理、身份认证,负载均衡等
事务的四大特性:
原子性(atomicity):一个事务是一个不可分割的工作单位,事务中包括的诸操作要么都做,要么都不做。
一致性(consistency):事务执行的状态前后是一致的,事务的中间状态不能被观察到的。
隔离性(isolation):一个事务的执行不能被其他事务干扰。即一个事务内部的操作及使用的数据对并发的其他事务是隔离的,并发执行的各个事务之间不能互相干扰。隔离性又分为四个级别:读未提交(read uncommitted)、读已提交(read committed,解决脏读)、可重复读(repeatable read,解决虚读)、串行化(serializable,解决幻读)。
持久性(durability):持久性也称永久性(permanence),指一个事务一旦提交,它对数据库中数据的改变就应该是永久性的。接下来的其他操作或故障不应该对其有任何影响。
面试题:两阶段提交与三阶段提交的区别?
1.增加了一个询问阶段,问了下,你能不不能行?
2.加入了超时机制
分布式事务:它分为二阶段和三阶段提交,三阶段相比于二阶段增加了一个询问阶段,就是你行不行?还有加入了超时机制。我们用的seata事务,它属于二阶段提交事务,原理是:将长事务分成多个短事务,锁资源短,效率高,它的三大角色:TC:事务协调者(带头大哥),TM (Transaction Manager) - 事务管理器 (鸡妈妈),RM (Resource Manager) - 资源管理器(小鸡),涉及的表global_table 全局表, branch_table 分支表,lock_table 此时此刻锁的表,及业务库的undo_log表:记录业务sql操作之前和之后的镜像数据,如果需要回滚,执行镜像数据进行恢复,正常成功后 异步删除undo_log无用数据。
线程newCachedThreadPool与newFixedThreadPool的区别?
newCachedThreadPool,当有任务时就会启动线程去执行,newFixedThreadPool,指定固定线程去执行。
GitFlow执行流程
主分支:master/main
业务分支:develop
功能分支:feature
维护分支:hotfix
发布分支:release
我们会在主分支上创建一个业务分支,如果还有其他的子模块,比如购物车模块,我们会在业务分支develop分支上创建feature功能分支,等功能模块完成之后,合并到develop分支,快到发布日期的时候,会在develop分支上创建一个release分支进行预发布,预发布成功后会合并到main主分支上,然后再将realease分支合并到develop业务分支上,如果我们线上出现问题,会在main分支上创建一个hotfix维护分支,等维护好之后,我们会进行测试,等测试没有问题之后,在合并到main分支上,然后再将hotfix分支合并到develop分支上,这样就能保证我们的业务分支永远都是最新的。
你了解过的RPC有哪些?
RPC既远程调用服务
openFeign是基于http协议的远程调用服务,作用于应用层。
Dubbo是基于dubbo协议的远程调用服务,作用于运输层(TCP)
你们项目中有没有用过Dubbo?
自从我加入公司,我们老大选型就行的springcouldalibb,但是我私下了解过,Dubbo提供了三大核心功能:面向接口的远程过程调用(RPC)、服务自动注册与发现、以及服务治理。它需要声明一个api接口,接口提供方(provider)和接口消费方(comsumber),接口提供方,实现类需要加@DubboServcie,启动类上加入@EnableDubbo注解,接口消费方,引入接口需要加@DubboRefence,启动类上加@EnableDubbo注解,先启动提供方,他就会注册到zookeeper下的dubbo节点上,当启动消费者时,他就会去zookeeper注册中心上寻找这个提供者,进而实现功能。
在Nacos中使用bootatrap.properties的作用?
使用application.properties时,应用启动会先读取配置文件的内容,而bootstrap.properties配置文件会在应用启动时,先获取Nacos中的配置信息,再被读取,相当于一个bean初始化之前,一个初始化之后
什么是服务熔断?什么是服务降级?
熔断机制是应对雪崩效应的一种微服务保护机制。当某个微服务不可用或者想用时间太长时间就会熔断该节点微服务调用。
服务降级,一般是从整体负荷考虑,就是当某个服务熔断后,服务不在被调用,此时可以自己准备一个fallback回调,返回一个默认值,虽然水平下降,单好歹可用。
事务失效的八种情况?
1.非public修饰的方法
@Transactional注解只能在在public修饰的方法下使用
2.类内部访问
类内部非直接访问带注解标记的方法 B,而是通过类普通方法 A,然后由 A 调用 B。 自己玩自己
3.数据库不支持事务
MySQL中,MyISAM引擎不支持事物,InnoDB 支持事物
4.异常类型不匹配
@Transactional 注解默认只处理运行时异常( RuntimeException 和 error)
5.传播属性设置问题
6.捕获异常未抛出
7.Bean没有纳入Spring IOC容器管理
8.事务方法内启动新线程进行异步操作
Docker中,CMD 和 ENTRYPOINT 的区别?
1.cmd易被覆盖,entrypoint不易被覆盖。
2.当cmd和entrypoint命令行格式的命令在一起时,entrypoint命令无条件替换cmd命令
3.当cmd和entrypoint键值对格式的命令在一起时,cmd命令是entrypoint命令的参数
mvcc是什么?
也就是多版本并发控制,是为了在读取数据时不加锁来提高读取效率和并发性的一种手段。InnoDB 实现MVCC,mvcc解决的就是读写时的线程安全问题,线程不用去争抢读写锁。
设计模式:
双token的原理:
MySQL主从同步
MySQL的主从同步(也称为复制)是一种数据库管理技术,允许将数据从一个MySQL服务器(称为主服务器或Master)自动复制到一个或多个其他MySQL服务器(称为从服务器或Slave),以此来实现数据冗余、提高读取性能、以及提供故障切换和恢复的机制。以下是MySQL主从同步的基本原理和工作流程:
-
二进制日志(Binlog)机制:MySQL主从同步的核心基于主服务器上的二进制日志。每当主服务器上的数据发生变化(如执行INSERT、UPDATE、DELETE或某些DDL语句时),这些操作会被记录到二进制日志文件中。二进制日志可以保存为Statement格式(记录原始SQL语句)、Row格式(记录每行数据的实际变化)或Mixed格式(根据情况自动选择Statement或Row格式)。
-
初始化同步(全量同步):在初次配置从服务器或从服务器数据丢失需要重建时,需要进行全量同步。这通常涉及在从服务器上执行一次全库备份的恢复,或者使用如mysqldump这样的工具导出主服务器的数据,再导入到从服务器中。
-
增量同步:一旦从服务器的数据与主服务器同步,从服务器的I/O线程会连接到主服务器,并请求从某个binlog文件和位置开始发送后续的binlog事件。主服务器通过其I/O线程将这些事件发送给从服务器,从服务器将这些事件写入其本地的中继日志(Relay Log)。
openFeign原理:
启动类上添加EnableFeignClients注解,openFeign会自动扫描是否标记有@FeignClient的注解,扫描到有改注解的接口,openFeign会创建一个动态代理,从nacos中获取配置信息,拼接成完整的url,进行远程调用。
全链路跟踪流程:
前端会有一个埋点,每次请求的时候都会携带上ThreadID到后端,然后后端会在过滤器中从请求头中将ThreadID给取出来放进MDC中,并且当我们使用openfeign时也需要在openfeign拦截器中从MDC中将ThreadID取出来,放进openfeign调用时的请求头中
数据脱敏原理:
自定义了一个脱敏注解@Desensitization里面封装了一个@JsonSerialize(using = Desensitizationserialize.class)注解,这个注解指定使用自定义的脱敏序列化工类,Desensitizationserialize线承Jsonserializer类重写seralize万法并不直接提供数据脱敏功能,它要是根据注解里的枚举类型找到具体脱影方法(就是对hutool里的脱敏工具进行了封装)将数据转换为可传输的字符串格式,实现接口ContextualSerializer重写createContextual方法,方法中扫描属性上是否有自定义的注解,有则创建定义的序列化类的实例并且返回,根据自定义的脱敏规则实现数据脱敏,没有注解不做处理
springSecurity的执行流程:
SpringSecurity的执行原理:当我们服务端接收到请求后,首先通过DelegatingFiterProxy代理对象交互,转发给springsecurity的执行链,由于他自带的执行链有16条,我们将不用的过滤器进行了排除,同时加入了我们自定义的过滤器,token全局过滤器,先判断token是否被篡改或过期,然后对token进行解析,将负载中的角色存储在SecuritvContext上下文中,对加有@PreAuthorize注解的方法进行判断,如果我们上下文中的角色包含注解中的角色,则执行请求,否则抛出异常,返回403权限不足。
谈谈对WebSocket、WebRTC等实时通讯协议的了解?
WebSocket是一种在单个TCP连接上进行全双工通信的协议。它的特点:①全双工通信:WebSocket连接建立后,客户端和服务器可以同时发送数据,无需等待对方响应。②低延迟:由于不需要频繁的HTTP请求,WebSocket的通信延迟较低。③持久连接:连接一旦建立,就会保持到一方主动关闭为止,这与HTTP的短连接形成对比。④消息格式灵活:可以发送文本或二进制数据,包括任意格式的消息体。WebRTC(Web Real-Time Communication)是一个支持网页浏览器进行实时通信(RTC)的技术。①P2P通信:WebRTC使用的是点对点技术,数据直接在两个浏览器之间传输,减少了服务器的带宽压力。②加密通信:WebRTC使用DTLS-SRTP对音视频数据进行加密,确保了通信的安全性。③媒体捕获和流处理:WebRTC支持直接从摄像头和麦克风捕获媒体流,以及对音频和视频进行实时处理。④数据通道:除了音视频,WebRTC还提供了数据通道API,允许在浏览器间直接传输任意数据。
谈谈对docker的理解,以及他的使用场景?
docker是一个虚拟化的容器,容器之间相互隔离互不影响,他里面有很多基础镜像,我们也可以自定义镜像。Docker的核心组成部分:①docker client:客户端,为用户提供一系列可执行的命令,用户用这些命令实现跟docker daemon交互。②docker daemon:守护进程一般在宿主主机后台运行等待接收来自客户端的请求信息。③docker image:镜像,镜像run之后就生成docker容器。④docker container:容器,一个系统级别的服务拥有自己的ip和系统目录结构运行容器前需要在本地存在对应的镜像,如果本地本地不存在该镜像就去镜像仓库下载。使用场景:①在CI/CD中,Docker可以用于构建和打包应用程序,以及自动化测试和部署。②在微服务架构中,Docker可以帮助实现服务的隔离、部署和扩展
Spring Boot自动装配原理
手写start的原理
你们的项目是怎样对接物流的
你们扣积分是异步的,扣的时候出现问题(队列出现问题),用户再次用积分下单怎么办
怎么防止大量的空商品请求导致黑名单变大
第一次为什么要用xxl-job去推es?
如何设置猜你喜欢功能?
什么时候触发MySQL的行锁与表锁
行锁 (Row Lock)
行锁是在InnoDB存储引擎中最常见的锁类型,它允许并发访问同一表的不同行。行锁可以减少锁争用,提高并发性能。在InnoDB中,行锁通常在以下情况下被触发:
- 事务开始时:当一个事务开始执行更新操作(如INSERT、UPDATE、DELETE)时,InnoDB会自动为涉及的行加锁。
- SELECT ... FOR UPDATE:当你使用
SELECT ... FOR UPDATE
语句时,即使没有更新操作,InnoDB也会为所选行加上行锁。 - SELECT ... LOCK IN SHARE MODE:这个语句也会为所选行加上共享锁(S锁)。
表锁 (Table Lock)
表锁是在MyISAM存储引擎中最常见的锁类型,它锁定整个表,阻止其他事务对表进行任何修改。虽然InnoDB也支持表锁,但主要还是以行锁为主。表锁通常在以下情况下被触发:
- 表级别的操作:在MyISAM存储引擎中,当执行如
ALTER TABLE
、REPAIR TABLE
、CHECK TABLE
等操作时,会锁定整个表。 - 显式锁定:在某些存储引擎中,如MyISAM,使用
LOCK TABLES
语句可以显式地锁定表。 - InnoDB的表锁:虽然InnoDB主要使用行锁,但在某些情况下也会使用表锁,例如:
- 当执行
TRUNCATE TABLE
操作时,InnoDB会锁定整个表。 - 当使用
ALTER TABLE
进行表结构更改时,InnoDB也可能锁定整个表。
- 当执行
锁模式
InnoDB支持两种主要的锁模式:
- 排他锁(X锁):排他锁阻止其他事务对该行进行任何类型的锁定。
- 共享锁(S锁):共享锁允许多个事务同时读取一行,但不允许其他事务对该行进行写操作。
总结
- 行锁:在InnoDB中,通常用于事务处理,可以提高并发性能,尤其是在多行更新的情况下。
- 表锁:在MyISAM中常见,在InnoDB中较少使用,但在某些操作中仍然适用,如表结构修改或表级操作。