内容分类 | 详情 |
---|---|
Java高频面试题 | 汇总入口 |
JVM | JVM面试题 |
并发 | 并发面试题 |
Spring | Spring面试题 |
分布式 | 分布式面试题 |
SpringBoot | SpringBoot面试题 |
SpringCloud | SpringCloud面试题 |
Dubbo | Dubbo面试题 |
MySQL | MySQL面试题 |
Mybatis | Mybatis面试题 |
Redis | Redis面试题 |
RocketMQ | RocketMQ面试题 |
算法 | 算法面试题 |
遇到的问题 | 遇到的问题 |
面试官的其他问题 | 面试官的其他问题 |
Git | Git面试题 |
文章目录
缓存穿透
缓存穿透是指缓存和数据库中都没有的数据,而用户不断发起请求,如发起为id为“-1”的数据或id为特别大不存在的数据,黑客攻击。
缓存击穿
缓存击穿是指缓存中没有但数据库中有的数据(一般是缓存时间到期),这时由于并发用户特别多,同时读缓存没读到数据,又同时去数据库去取数据,引起数据库压力瞬间增大,造成过大压力。
缓存雪崩
缓存雪崩是指缓存中数据大批量到过期时间,而查询数据量巨大,引起数据库压力过大甚至down机。和缓存击穿不同的是,缓存击穿指并发查同一条数据,缓存雪崩是不同数据都过期了,很多数据都查不到从而查数据库。
redis的布隆过滤器的原理
SDK设计
- 自定义注解
请求方式,请求url - 泛型
请求实体和返回实体的限定 - 反射
获取自定义注解属性值 - 第三方工具类
fastjson,httpClient,google-guava
Oauth2协议原理
- RO (resource owner): 资源所有者,对资源具有授权能力的人。如上文中的用户Alice。
- RS (resource server): 资源服务器,它存储资源,并处理对资源的访问请求。如Google资源服务器,它所保管的资源就是用户Alice的照片。
- Client: 第三方应用,它获得RO的授权后便可以去访问RO的资源。如网易印像服务。
- AS (authorization server): 授权服务器,它认证RO的身份,为RO提供授权审批流程,并最终颁发授权令牌(Access Token)。读者请注意,为了便于协议的描述,这里只是在逻辑上把AS与RS区分开来;在物理上,AS与RS的功能可以由同一个服务器来提供服务。
授权码模式流程:
- web客户端通过组织一个URL向授权服务器发起认证流程,并有一个重定向的URI,授权成功后授权服务器将会重定向到这个URI。
- 授权服务器接收到URL请求会验证终端用户,是否许可这个客户端的请求认证。
- 假如终端用户许可了这次访问,授权服务器将会把user-agent重定向到之前提供的重定向URI,并在URI后面拼上一个授权码CODE值。
- 客户端通过验证并传入上一步取得的授权码从授权服务器请求一个访问令牌。(需要带上ClientId和Secret,ClientId和Secret是通过平台授予)。
- 授权服务器验证客户端私有证书和授权码的有效性并返回访问令牌。
access_token,expires
私钥和秘钥
- Bob将他的公开密钥传送给Alice。
- Alice用Bob的公开密钥加密她的消息,然后传送给Bob。
- Bob用他的私人密钥解密Alice的消息。
JWT
一个token分3部分,按顺序:
- 头部(header)
头部承载两部分信息:
声明类型,这里是jwt
声明加密的算法 通常直接使用 HMAC SHA256
base64加密(该加密是可以对称解密的),构成了第一部分
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
- 载荷(payload)
载荷就是存放有效信息的地方。这个名字像是特指飞机上承载的货品,这些有效信息包含三个部分
- 签证(signature)
jwt的第三部分是一个签证信息,这个签证信息由三部分组成:
header (base64后的)
payload (base64后的)
secret
这个部分需要base64加密后的header和base64加密后的payload使用.连接组成的字符串,然后通过header中声明的加密方式进行加盐secret组合加密,然后就构成了jwt的第三部分。
使用场景
JWT认证流程:
- 用户输入用户名/密码登录,服务端认证成功后,会返回给客户端一个 JWT
- 客户端将 token 保存到本地(通常使用 localstorage,也可以使用 cookie)
- 当用户希望访问一个受保护的路由或者资源的时候,需要请求头的 Authorization 字段中使用Bearer 模式添加 JWT
UUID
由128位十六进制数组成。根据网卡MAC地址、时间戳、命名空间、随机数等条件由机器自动生成
跨域解决方案
一级域名又称顶级域名,即: baidu.com
二级域名,即: www.baidu.com,另外 .baidu.com也属于二级域名,上可以填写任意内容,都属于二级域名。
三级域名,也就是二级域名的子目录,即: …baidu.com ,_上可以填写任意内容,都属于三级域名。
同源:如果两个 URL 的 protocol、port (en-US) (如果有指定的话)和 host 都相同的话,则这两个 URL 是同源
URL | 说明 | 是否允许通信 |
---|---|---|
http://www.a.com/a.js http://www.a.com/b.js | 同一域名下 | 允许 |
http://www.a.com/lab/a.js http://www.a.com/script/b.js | 同一域名下不同文件夹 | 允许 |
http://www.a.com:8000/a.js http://www.a.com/b.js | 同一域名,不同端口 | 不允许 |
http://www.a.com/a.js https://www.a.com/b.js | 同一域名,不同协议 | 不允许 |
http://www.a.com/a.js http://70.32.92.74/b.js | 域名和域名对应ip | 不允许 |
http://www.a.com/a.js http://script.a.com/b.js | 主域相同,子域不同 | 不允许 |
http://www.a.com/a.js http://a.com/b.js | 同一域名,不同二级域名(同上) | 不允许(cookie这种情况下也不允许访问) |
http://www.cnblogs.com/a.js http://www.a.com/b.js | 不同域名 | 不允许 |
cors跨域
- 类或者方法支持跨域
使用@CrossOrigin注解
- SpringBoot解决跨域:
全局方式:
@EnableWebMvc
@Configuration
public class MvcConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//每次调用registry.addMappin可以添加一个跨域配置,需要多个配置可以多次调用registry.addMapping
registry.addMapping("/**")
.allowedOrigins("*") //放行哪些原始域
.allowedMethods("PUT", "DELETE","POST", "GET") //放行哪些请求方式
.allowedHeaders("header1", "header2", "header3") //放行哪些原始请求头部信息
.exposedHeaders("header1", "header2") //暴露哪些头部信息
.allowCredentials(false) //是否发送 Cookie
.maxAge(3600);
// Add more mappings...
}
}
- 过滤器
//处理跨域的Filter
//1. 添加 CORS配置信息
CorsConfiguration config = new CorsConfiguration();
//放行哪些原始域
config.addAllowedOrigin("*");
//是否发送 Cookie
config.setAllowCredentials(false);
//放行哪些请求方式
config.addAllowedMethod("*");
//放行哪些原始请求头部信息
config.addAllowedHeader("*");
//暴露哪些头部信息
config.addExposedHeader("*");
//2. 添加映射路径
UrlBasedCorsConfigurationSource corsConfigurationSource = new UrlBasedCorsConfigurationSource();
corsConfigurationSource.registerCorsConfiguration("/**",config);
- nginx反向代理
现在我在fe.server.com对dev.server.com发起请求一定会出现跨域。
server {
listen 80;
server_name fe.server.com;
location / {
proxy_pass dev.server.com;
}
}
NIO和IO的区别
核心区别:
-
NIO是以块的方式处理数据,但是IO是以最基础的字节流的形式去写入和读出的。所以在效率上的话,肯定是NIO效率比IO效率会高出很多。
Channel是一个对象,可以通过它读取和写入数据。拿 NIO 与原来的 I/O 做个比较,通道就像是流。
-
NIO比传统的BIO核心区别就是,NIO采用的是多路复用的IO模型,普通的IO用的是阻塞的IO模型,两个之间的效率肯定是多路复用效率更高
IO为阻塞的,当一个线程调用read() 或 write()时,该线程被阻塞,直到有一些数据被读取,或数据完全写入。
Java NIO的非阻塞模式
redis布隆过滤器的原理
redis布隆过滤器原理
当使用布隆过滤器添加 key 时,会使用不同的 hash 函数对 key 存储的元素值进行哈希计算,从而会得到多个哈希值。根据哈希值计算出一个整数索引值,将该索引值与位数组长度做取余运算,最终得到一个位数组位置,并将该位置的值变为 1。每个 hash 函数都会计算出一个不同的位置,然后把数组中与之对应的位置变为 1。通过上述过程就完成了元素添加(add)操作。当我们需要判断一个元素是否存时,其流程如下:首先对给定元素再次执行哈希计算,得到与添加元素时相同的位数组位置,判断所得位置是否都为 1,如果其中有一个为 0,那么说明元素不存在,若都为 1,则说明元素有可能存在。
redis使用bitmap存储数据,Redis 的位图(bitmap)是由多个二进制位组成的数组
遇到的问题
接口幂等性问题
幂等性:
多次调用方法或者接口不会改变业务状态,可以保证重复调用的结果和单次调用的结果一致。
医生为患者购买药品生成订单的场景,在生成药品订单的时候出现药品的数量对不上的问题。测试进行接口压力测试。发现接口存在幂等性问题。
前端:
防重复提交
后端:
redis解决
- 前端提交订单表单时,后端根据参数和方法名称生成hash值,作为key,以 SETNX 的方式存入 redis 中,并设置过期时间,防止死锁。
- 如果设置成功,证明这是第一次请求,则执行后续的业务逻辑。
- 如果设置失败,则代表已经执行过当前请求,直接返回。
自定义注解
在需要防重复提交的方法上引入这个自定义注解
拦截器
获取方法上是否有该注解,如果有该注解,那么要进行防重复提交的业务逻辑。
防重复提交的业务逻辑
解决大数据量的手机MAC地址
问题描述:
统计某个商场的用户的数量,数据去重统计,对100G左右的zip压缩包文件中的txt记录的手机MAC地址去重,zip压缩包中的文件有多个txt文件,每个txt文件大概有10G。
-
使用多线程NIO遍历每个txt文件,读取每一行MAC地址
计算hash值存储到1000个文件中
hash(url) % 1000
取模计算,得到一个取模后的值,根据取模的值生成一个新的小文件,并把这个mac数据放到这个小文件中。可能相同的MAC字符串存放到同一个小文件中。每个小文件大概有100M到200M之间。 -
再用线程池核心线程数控制在10个,这个任务最大占用内存为2G去处理这1000个小文件,遍历每个小文件放到set中去重。
支付消息重复消费的问题
重复消息产生的场景:
- ACK
正常情况下在consumer真正消费完消息后应该发送ack,通知broker该消息已正常消费,从queue中剔除
当ack因为网络原因无法发送到broker,broker会认为词条消息没有被消费,此后会开启消息重投机制把消息再次投递到consumer
- 消费模式
在CLUSTERING模式下,消息在broker中会保证相同group的consumer消费一次,但是针对不同group的consumer会推送多次
解决:
redis
的 Incr
原子操作:key
自增,大于0
返回值大于0
则说明消费过,(key
可以是消息的md5
取值, 或者如果消息id
设计合理直接用id
做key
)
long num = redisTemplate.opsForValue().increment(key, delta)
if(num == 1L){
//消费
}else{
//忽略,重复消费
}
线上问题
接口出现卡顿想象,时好时坏。
- 分析CPU
-
通过 top 命令找到 CPU 消耗最高的进程,并记住进程 ID。
-
pid内最耗cpu的线程pid
top -Hp 3913
-
线程ID转换为16进制格式
printf "%x\n" tid
-
定位代码
jstack 3913 | grep f59 -A 50
查看RUNNABLE状态的线程
-
JVM内存溢出的问题
- 找到占用内存最大的PID
top命令 然后shift + m对MEM进行排序
- 生成hprof文件
jmap -dump:live,format=b,file=/usr/local/src/log/myheapdump.hprof 2217979
- 分析hprof文件
本地使用jhat命令
jhat myheapdump.hprof
点击Show instance counts for all classes (including platform)会展示包含平台的类的的信息
查看各个对象中包含的实例有多少个
接口并发的问题
订单系统有个促销的活动,系统用户量不到几万的客户量,商品促销活动,压力测试下单,qps每秒几万次请求,造成MySQL占用CPU飙升,影响其他接口正常响应数据。造成了缓存穿透。
因为促销活动商品有很多,所以这里把会员用户ID+商品ID作为过滤条件,通过Redis布隆过滤器,把那些购买过该商品的用户给过滤掉,减少请求接口的压力。
布隆过滤器的特点: 能够通过布隆过滤器过滤掉的一定在数据库中不存在,过滤不掉的不一定在数据库中存在。
订单ID相同了
生成订单号的逻辑:
OD + yyMMddHHmmssSSS + 5位数(商户ID3位+随机数2位) 22位
解决方法使用雪花算法,每秒可生成400万不重复的ID
树结构的遍历
方式一:全路径字段
方式二:队列遍历
导出大量数据生成Eexcel方案
接口幂等性设计
https://www.jianshu.com/p/5650032a874b