腾讯四面面经

说明

是的,没听错,确实是腾讯四面,而且是技术面。先声明下,这个面经是帮朋友整理的,都是真实的面经,不得不说,四面确实是有强的的,接下来让我们一起看下

面试部门:s3,投递渠道:腾讯官网

IOC控制反转

分析:

控制反转(将控制权反转给容器),通过 DI 依赖注入实现

回答:

Spring IOC(Inversion of control) 即 控制反转,时 spring 框架的核心概念之一。IOC是一种思想,它通过依赖注入 DI(Dependency Injection) 实现,IOC 将对象的控制权(创建和管理)交给容器负责,而不是由程序员来控制

控制:指的是控制对象的创建, 反转是:将创建对象的工作反转给容器,让容器来帮我们创建

依赖注入 DI 是 IOC 的实现方式,比如我们之前想要创建对象,需要 new ,传参... DI 就是,提前将 对象的属性和依赖配置在配置文件中,在我们需要的地方直接注入对象即可,不需要我们手动去new,传参,Spring容器直接帮我们干好了这个事儿

依赖注入的方式有:构造器注入,setter注入,接口注入

Redis怎么实现分布式锁

分析:

使用 set ex nx + lua 脚本使用

回答:

在 Redis 中实现分布式锁的常见方法是通过 set ex nx 命令 + lua 脚本组合使用。确保多个客户端不会获得同一个锁的同时,也保证了安全解锁和意外情况下锁的自动释放

ex:设置过期时间,单位是 s nx:仅当键不存在时才设置

加锁:SET lock_key uniqueValue EX expire_time NX

解锁:使用 lua 脚本,先通过 get 获取 key 的 value 判断锁是否是自己加的,如果是则 del

之所以要有 uniqueValue 是为了防止被别的客户端给释放了

Redis 单点加锁故障问题 和 RedLock 红锁解决方案

1.redis 如何实现分布式锁 -> setnx

2.setnx 有什么缺点,替代方案 -> redisson

缺点就是锁的续期问题,只能设置一个固定时间,如果未完成逻辑锁就过期了,数据就可能产生不一致

所以需要一个看门狗机制,redission 就是一个好用的方式

看门狗机制

业界出了一个看门狗机制来防止这种情况的产生。

理论很简单,在抢到锁之后,后台会有一个任务,定时向 redis 进行锁的续期。比如锁的过期时间是 30s,可以每过三分之一时长(30/3)10s 后就去 redis 重新设置过期时间为 30s。

在锁被释放的时候,就移除这个定时任务。

redission

redission是一个类库,封装了很多redis操作,便于我们使用。

其实现的分布式锁就引入了看门狗机制,具体原理和上面所述的一致,

3.setnx 和 redisson 有什么缺点 单点故障 -> redlock

单点故障 问题

单台 Redis 实现分布式锁存在单点故障问题,如果采用主从读写分离架构,如果一个客户端在主节点上锁成功,但是主节点突然宕机,由于主从延迟导致从节点还未同步到这个锁,此时可能有另一个客户端抢到新晋升的主节点上的锁,此时会导致两个客户端抢到锁,产生了数据不一致。

基于这个情况,Redis 推出了 Redlock。

4.redlock 有什么缺点 实现复杂 同步问题 -> zk

Redission 是怎么实现分布式锁的

分析:

锁的获取(setnx(加锁成功) + Lua脚本(加锁失败)),锁的续期,锁的释放(Lua脚本),可重入锁

锁的获取:setnx 成功就 setnx,不成功用 lua,因为 Redission 是可重入锁

回答:

分布式锁是Redission做的, redission 实现分布式锁 加锁(lock),解锁(unlock),续期(lock不指定时间自动续期,默认锁时间30s,每隔10s检查一下 ),防止无限续期(lock时加时间,看门狗就不会生效)

Redission 加解锁的底层是,set ex nx 和 lua 脚本

Redission 加锁 lock,解锁 unlock

Redisson是基于Redis实现的 分布式锁,实际上是使用Redis的原子操作来确保多线程、多进程或多节点系统中,只有一个线程能获得锁,避免并发操作导致的数据不一致问题。

1.锁的获取:

  • Redisson使用Lua脚本,利用exists + hexists + hincrby命令来保证只有一个线程能成功设置键(表示获得锁)。

  • 同时,Redisson会通过pexpire命令为锁设置过期时间,防止因宕机等原因导致锁无法释放(即死锁问题)。

2.锁的续期:

  • 为了防止锁在持有过程中过期导致其他线程抢占锁,Redisson实现了锁自动续期的功能。持有锁的线程会定期续期,即更新锁的过期时间,确保任务没有完成时锁不会失效。

3.锁的释放:

  • 锁释放时,Redisson也是通过Lua脚本保证释放操作的原子性。利用hexists + del确保只有持有锁的线程才能释放锁,防止误释放锁的情况。

  • Lua脚本同时利用publish命令,广播唤醒其它等待的线程。

  1. 可重入锁:
  • Redisson支持可重入锁,持有锁的线程可以多次获取同一把锁而不会被阻塞。具体是利用Redis中的哈希结构,哈希中的key为线程ID,如果重入则value +1,如果释放则value -1,减到0说明锁被释放了,则del锁。

lua脚本是如何保证的

我感觉这里应该是让答:Redis 是单线程,lua 脚本并不具有原子性,是 Redis 核心操作都是单线程,所以保证了 lua 脚本的原子性

底层原理应该是调用命令(源码我也没看过 -- 四面问源码很正常,这个真没办法,上强度了):

Lua 脚本中执行复杂的逻辑,超过了单个 Redis 命令的能力,例如常见基于 Redis 实现分布式锁就需要结合 Lua 脚本来实现(释放锁先get获取key的value判断锁是不是自己加的,如果是则 del)

Lua 如何使用:redis.call 调用 redis 命令,也有 if-else 这种逻辑可用

Redis 实现分布式锁,用 lua 脚本如下:

Lua 复制代码
if redis.call("GET",KEYS[1]) == ARGV[1]
then
    return redis.call("DEL",KEYS[1])
else
    return 0
end

IO 多路复用

I/O多路复用:也常称为事件驱动I/O。在此模式中,应用程序可以同时监控多个I/O描述符(比如,socket),当任何一个I/O描述符准备好数据时,应用程序就可以对其进行处理。这可以在一个单独的进程或线程中同时处理多个I/O操作,并且不需要阻塞或轮询。select、poll、epoll都是这种模型的实现。

Select,poll,epoll 的区别

分析:

select,poll,epoll 是 IO 多路复用机制,大概是怎么工作的,扯出都是同步IO,然后具体分析原理和优缺点(特点) 然后渐进式回答,select 和 poll 一起说,然后说有什么问题,epoll 通过什么解决了这两个问题

回答:

select,poll,epoll 都是 IO 多路复用的机制,可以监听多个文件描述符,一旦某个文件描述符就绪(读就绪或者写就绪),就能够通知程序进行相应的读写操作,但他们本质上都是同步IO,因为它们都需要在事件就绪后自己进行读写操作,这个读写操作是阻塞的,而异步 IO 无需自己进行读写,异步IO的实现会负责将数据从内核空间拷贝到用户空间

  • select 和 poll 内部都是使用线性结构来存储进程关注的 socket 集合,在使用的时候,首先需要把关注的 socket 集合通过 select/poll 系统调用从用户态拷贝到内核态,然后由内核检测事件,当有网络事件发生时,内核需要遍历进程关注的 socket 集合,找到对应的 socket,并设置状态为 可读/可写,然后把整个 socket 集合从内核态拷贝到用户态,用户态还要继续便利整个 socket 集合找到可读/可写的socket,然后对其进行处理,select和poll的区别在于 scoket 集合的大小,select 限制了 socket 集合有限制,通常为 1024 ,可以调整。poll 没有限制 socket 集合的大小,可以支持任何数量 socket

  • 很明显发现,select 和 poll 的缺陷在于,当客户端越多,也就是 socket 集合越大,socket 集合的遍历和拷贝会带来很大的开销,epoll 通过两个方面解决了 select/poll 的问题

  • epoll 在内核里使用 红黑树 来关注进程所有待检测的 socket,红黑树是个高效的数据结构,增删改一般时间复杂度都是 O(logn),通过对这颗红黑树的管理,不需要像 select/poll 在每次操作时都需要传入整个 socket 集合,减少了内核和用户空间大量的数据拷贝和内存分配(需要在内核中为这些 socket 分配临时的存储空间)

  • epoll 采用事件驱动机制,内核中维护着一个链表用于记录就绪事件。它只需要把有事件发生的 socket 集合传递给应用程序,而不必像 select 和 poll 那样去轮询扫描整个 socket 集合(其中包含有事件和无事件的 socket),这极大地提高了检测效率

  • epoll 提供了 水平触发 LT,边缘触发 ET 两种模式,调用 epoll_wait 拿发生事件的 socket 时,水平触发是,每次都会返回有事件处理的 socket,边缘触发是 只会返回一次 有事件处理的 socket,如果这次返回没有处理,下次就不会返回了

epoll事件驱动,红黑树和就序列表(事件回调机制)

NIO 是怎么实现的

首先要说明,NIO 是 Java 中创建 IO 的一种方式

我的理解(如果让我回答的话):

New IO ,大概就是 进行系统调用,然后内核创建IO线程,在堆外开辟直接内存,减少拷贝次数...

具体我也不太清楚是怎么实现的,不过我感觉只要说出来点儿沾边的,问题就不大

零拷贝原理是什么

分析:

说出传统方式:2 次系统调用(read,write),4 次用户态和内核态切换,4 次数据拷贝

零拷贝技术:一次系统调用(sendfile),减少了两次内核/用户态切换,加上 SG-DMA 网卡技术,两次数据拷贝都不需要 CPU,都是通过 DMA 来搬运

回答:

传统文件传输的方式,需要涉及 2 次系统调用,write 和 read 函数,期间会发生 4 次用户态和内核态上下文转换和 4 次数据拷贝。而零拷贝技术的文件传输方式只需要一次系统调用,就是 sendfile,这样相比传统文件传输的方式,减少了 2 次用户态和内核态上下文切换,再加上网卡支持 SG-DMA 技术的话,数据拷贝次数只需要 2 次,就可以完成文件的传输,而且 2 次的数据拷贝过程,都不需要通过 CPU,2 次都是由 DMA 来搬运

所以零拷贝技术主要是为了提升文件传输的效率,kafka 消息队列 I/O 的吞吐量高的原因,也是因为使用了零拷贝技术(可提可不提,别给自己挖坑)。

DMA Direct Memory Access ,直接 内存 访问) 是一种硬件机制,用于在外设(如磁盘、网卡)和内存之间直接传输数据,而无需 CPU 的干预。

零拷贝这么好,为什么不全用零拷贝?

零拷贝只适用于单纯的发送文件,没办法对文件数据进行额外的加工,比如压缩,再发送之类的。

零拷贝是将数据发送到网络中的场景

CA 证书 和 签名证书,从安全性上有什么区别

这...,真没办法 我也第一次遇到

但我理解大概就是:

CA 证书,服务器私钥加密,本地服务器公钥解密,重要数据是加密传输的,涉及加解密,更安全(但是 HTTPS 的四次握手 CA 证书这里,比 JWT 多做一次加加密操作,更安全

其他的 CA 证书可以选择对传输的数据也进行加密(这里不太了解 HTTPS 实现细节),我记得是没有的)

签名证书,类似 JWT 实现,没有涉及到加密解密,只是设计哈希函数的计算,重要数据是明文传输的,可能会被盗取

JWT Token 能说一说吗?

分析:

Json Web Token 回答:

JWT(JSON Web Token)是一种用于在各方之间传递安全信息的紧凑、URL安全的令牌格式。

在用户登录后,服务器生成JWT并返回给客户端。客户端在后续请求中通过HTTP头部(通常是Authorization头)发送该JWT,服务器则验证该JWT的有效性以进行用户身份验证。

因为它的无状态性,常用于分布式系统和微服务架构中。

其结构主要包括三个部分:Header、Payload和Signature。

JWT的工作原理可以总结为以下几个步骤:

  1. Header:描述令牌的元数据,通常包含令牌的类型(即JWT)和所使用的签名算法(如HMAC SHA256)。

  2. Payload:包含声明(Claims),即传递的数据。这些数据通常包括用户信息和其他相关数据。常见的声明类型有iss(发行者)、exp(到期时间)、sub(主题)等。

  3. Signature:将Header和Payload用指定的算法进行签名,用以验证JWT的真实性和完整性。签名确保了令牌内容在传输过程中未被篡改。

JWT的优点:

  • 自包含:JWT中包含了所有必要的信息,因此在验证时不需要查询数据库,提升了性能。

  • 跨语言:由于JWT是基于JSON的,几乎所有编程语言都支持它的生成和解析。

TLS 四次握手说一下

分析:

我们就回答 HTTPS RSA 算法握手流程就好了

四次握手(中间加一个校验证书):

  1. 客户端问候(TLS 版本,支持的加密算法套件,随机数)

  2. 服务端问候(确认的 TLS 版本,确认的加密算法套件-挑了其中一个,随机数,CA签名的证书)

  • 校验证书
  1. 客户端密钥交互(发送第三个随机数,用三个随机数来生成密钥) + 开始使用加密(做摘要) + 客户端完成

  2. 服务端发送开始使用加密(做摘要) + 服务器完成

回答:

TLS 四次握手过程如下:

  • 第一次 TLS 握手,客户端向服务端发送一条 Client Hello 消息,这其中包括,客户端使用的 TLS 版本号,支持的密码套件列表,客户端生成的随机数,这个随机数是用来后面生成密钥的元素之一

  • 第二次 TLS 握手,服务端向客户端发送一条 Server Hello 消息,这其中包括,服务端确认的版本号,确认的密码套件,服务端生成的随机数。接着为了服务端为了验证自己的身份,会发送 Server Certificate,CA 签发的服务器证书,随后,服务端发送 Server Hello Done,目的是告诉客户端,我已经把东西给你了,本此握手完毕

  • 校验证书:客户端收到服务端证书后,会用本地CA公钥解密证书,来验证证书是否合法,如果合法,客户端就能拿到服务端的公钥

  • 第三次 TLS 握手,客户端再生产一个随机数,用服务端公钥加密,通过 ClientKeyExchange 消息传给服务端。服务端接收到后用私钥解密得到第二个随机数。到这里,服务端和客户端双方都有 3 个随机数,双方根据这三个随机数,根据算法生成对称密钥。生成完之后,客户端发送消息告诉服务端使用对称加密方式发送数据,并且还会对之前所有发送的数据做个摘要,再用对称密钥加密一下,让服务器做个验证,验证对称密钥是否可用,以及之前握手信息是否有被中途修改

  • 第四次 TLS 握手,服务器也做同样的操作,发消息告诉客户端开始使用对称加密方式发送消息,并且也会对数据做个摘要,用对称密钥加密一下,让客户端做个校验,如果双方都验证加密和解密没问题,那么 TLS 四次握手就完成了

如何验证 CA 证书是否有效?

如何验证 CA 的证书是否有效的:

服务端发送:

这个证书中包含的信息有服务端公钥,用途等打成一个包,用hash计算得到一个值

然后CA用自己私钥对这个值进行加密,生成一个签名,也就是CA对证书做了签名

客户端验证:

客户端将拿到的证书信息进行hash运算得到h1,用本地CA公钥解密签名得到h2,如果h1==h2,说明证书有效

为什么要 3 个随机数?

因为前两个随机数都是明文传输,容易被其他人获取,如果就用两个,其他人会用这两个随机数生成对称密钥

第三个随机数使用服务端公钥来加密,就算被获取了,没有服务端私钥,也解析不出来,也就不能生成正确的对称密钥,所以三个随机数更安全

总结:

TLS 四次握手,对称加密和非对称加密都涉及到了,都是两次

HTTP 主要使用 TLS 协议握手,SSL 存在安全漏洞,并且 TLS 作为 SSL 的升级版,在安全性、兼容性和标准化等方面都有诸多优势,这是在 HTTPS 通信中主要使用 TLS 协议握手而不是 SSL 协议的重要原因。

所以我们主要关注 TLS 的握手过程,TLS 握手根据密钥交互算法的不同,可以分为两种:RSA 算法,ECDHE 算法

智力题:一个 3 升的水桶,一个 5 升的水桶,用这两个水桶从一个无边无际的壶里面舀出来4升,怎么做?

经典智力题,没有什么问题

前提:5L桶满,3L桶空

第一次:5L桶 ->(倒满) 3L桶,然后把 3L 桶中的全倒掉 现在:5L桶 -> 2L 3L 桶 -> 0L

第二次:5L桶剩余的2L ->(倒入) 3L桶 现在:5L桶 -> 0L 3L 桶 -> 2L

第三次:5L桶拉满,把3L桶倒满(这次3L桶只能倒1L) 现在:5L桶 -> 4L 3L 桶 -> 3L

相关推荐
李慕婉学姐3 小时前
【开题答辩过程】以《基于JAVA的校园即时配送系统的设计与实现》为例,不知道这个选题怎么做的,不知道这个选题怎么开题答辩的可以进来看看
java·开发语言·数据库
奋进的芋圆4 小时前
Java 延时任务实现方案详解(适用于 Spring Boot 3)
java·spring boot·redis·rabbitmq
sxlishaobin4 小时前
设计模式之桥接模式
java·设计模式·桥接模式
model20055 小时前
alibaba linux3 系统盘网站迁移数据盘
java·服务器·前端
荒诞硬汉5 小时前
JavaBean相关补充
java·开发语言
提笔忘字的帝国5 小时前
【教程】macOS 如何完全卸载 Java 开发环境
java·开发语言·macos
2501_941882485 小时前
从灰度发布到流量切分的互联网工程语法控制与多语言实现实践思路随笔分享
java·开发语言
華勳全栈6 小时前
两天开发完成智能体平台
java·spring·go
alonewolf_996 小时前
Spring MVC重点功能底层源码深度解析
java·spring·mvc
沛沛老爹6 小时前
Java泛型擦除:原理、实践与应对策略
java·开发语言·人工智能·企业开发·发展趋势·技术原理