秋招面经——结合各方面试经验

Spring

maven中版本 版本冲突

maven依赖中不允许存在两个不同版本的同名依赖。(添加<exclusion>标签来解决冲突)

Spring处理客户端(浏览器)的请求

当客户端进行某些操作时,会生成一个HTTP报文,将该报文发送到目标服务器进行处理

SpringBoot运行在Servlet容器(如Tomcat、Jetty)中,它通过底层网络通信协议(如HTTP、HTTPS)接收和发送HTTP报文。

Servlet容器负责接收来自客户端的HTTP请求报文,SpringBoot内部使用Servlet API提供的HttpServletRequest对象来表示HTTP请求报文,当报文到达时,Servlet容器会解析HTTP报文,将报文中的信息(请求方法、URL、请求头、请求参数等)填充到HttpServletRequest对象中。

SpringMVC的核心组件是DispatcherServlet,作为应用程序的前置控制器,当请求到达服务器时,DispatcherServlet是第一个处理请求的组件。DispatcherServlet负责调度请求并根据请求的URL路径来决定要调用的控制器方法。
HandlerMapping负责将URL映射到对应的控制器类和方法。
HandlerAdapter负责将请求中的参数绑定到控制器方法中的参数上。使用HandlerMethodArgumentResolver用于处理不同类型的参数。
HandlerReturnValueHandler处理控制器方法的返回结果,将返回结果转换为最终的Http响应。
ViewResulver用于解析视图名并转换为视图。视图最终被渲染为HTML

Spring的事务

通过事务管理器Transaction Manager和AOP机制完成

声明式事务管理:
通过AOP方式实现,允许使用@Transactional注解声明事务属性
Spring的事务管理器会在方法调用前后拦截并进行相应的事务处理
方法调用前事务管理器会开启一个新的事务,方法调用结束时,事务管理器会根据方法执行的结果决定是否提交事务还是回滚事务,如果方法调用过程中发生异常,事务管理器会回滚事务,确保数据的一致性。
可以通过配置事务的rollbackFor或noRollbackFor属性指定具体的异常类型

IOC的作用与好处

  1. 解耦
    将对象的创建和依赖关系的管理交给容器去执行,开发者只需要关注业务逻辑,而不需要关注创建和组装过程。
  2. 管理对象的声明周期
    IOC确保对象在需要时被正确地创建、初始化、使用和销毁
  3. 提供依赖注入
    IOC通过DI的方式,自动将对象需要的依赖注入到对象中,不需要手动编写繁琐的依赖查找和创建代码
  4. 实现可替换性和可扩展性
    由于对象的创建和依赖关系由容器管理,可以轻松替换或扩展对象的实现。方便进行单元测试,模块替换和功能扩展
  5. 提高代码的可读性和可维护性
    由于对象创建和依赖关系的管理集中在容器中,使得应用程序的代码更加简洁、清晰和可读

AOP的适用场景

可以使用JoinPoint获取切入点的相关信息

  1. 日志记录
    可以在方法执行前后或异常抛出时记录日志。
    通过将日志记录的逻辑与业务逻辑分离,可以避免在每个方法中都编写重复的日志代码。
  2. 安全性控制
    可以进行身份验证,权限检查等。确保只有授权用户能够访问敏感资源
  3. 事务管理
    AOP可以用于实现事务管理,将事务的开启、提交、回滚等操作与业务逻辑分离。
  4. 性能监控
    AOP可以用于监控方法的执行时间、资源消耗等性能指标。
  5. 异常处理
    可以用于统一处理方法中的异常。将异常处理与业务逻辑分离
  6. 缓存管理
    AOP可以用于实现缓存管理,将缓存的读取、写入和失效等操作与业务逻辑分离。可以减少重复的数据访问,提高系统的性能和响应速度

消息队列

消息队列对比

对比KafkaRocketMQRabbitMQ
优先级队列不支持通过建立不同的队列通过建立不同的队列
延迟队列不支持基于队列的延迟基于队列的延迟
死信队列不支持支持支持
消费模式pullpull/pushpull/push
广播模式发布订阅发布订阅点对点
(但可以由交换机实现发布订阅模式)
消息回溯offset和timestamp按时间回溯不支持
消息堆积&持久化磁盘堆积:所有消息都存在磁盘
每个partition对应一个或多个segment file
基于磁盘存储
使用commit Log存储消息(顺序写到文章末尾)
后台异步线程同步到consumerQueue
使用内存映射文件加速消息读取
内存堆积(换页操作存储到磁盘)
(或使用惰性队列将消息持久化到磁盘)
流量控制支持client和user级别多种维度的流量控制流量控制基于credit-base算法,是内部被动触发的保护机制,作用于生产者层面
顺序性消息同分区内有序Broker消息队列锁(分布式锁)
Consumer消息队列锁(本地锁)
Consumer消息处理队列消费锁(本地锁)
无法保证全局有序
性能最快中等最慢
高可用和容错包含Leaer和Follower
Leader失效后随机选举Leader
Master和Slavecluster(集群),federation(联盟),shovel
定时消息不支持支持支持
负载均衡
(三者都是软件负载均衡)
consumer端实现
每个消费者组都有指定一个broker为coordinator(群组协调器)
consumer端实现
所有的consumer都能得到consumer的订阅表,每个consumer自己做负载均衡
设置Prefetch count来限制Queue每次发送给每个消费者的消息数
刷盘策略异步刷盘
每3s钟调用1次fsync
支持同步刷盘
CommitRealTimeService 异步刷盘 && 开启内存字节缓冲区 第一
FLushRealTimeService 异步刷盘 && 关闭内存字节缓冲区 第二
GroupCommitService 同步刷盘 第三
优先内存存储,Buffer不够再刷盘
消息中间件KafkaRocketMQRabbitMQ
特点高吞吐量 持久性
分布式 发布订阅
高可用性 顺序消息
分布式事务 高扩展性
灵活路由
点对点+发布订阅+请求响应
可靠性 插件扩展
适用场景流式处理(日志收集 实时分析)
大数据集成
可靠性(持久化)要求高
异步消息处理 顺序消息
分布式事务
复杂路由 灵活消息模式 异步任务处理
技术支持零拷贝 批量处理
顺序读写 副本机制
消息严格顺序性交换机:Fanout、Ditect、Topic

Kafka高可用

一个Kafka集群由多个Broker组成,如果其中一个Broker宕机,其他机器上的Broker也依然能够对外提供服务

并且Kafka使用副本机制提供数据的冗余备份。
每个topic的分区可以配置多个副本,其中一个副本被称为领导者副本,其他副本被成为追随者副本
ISR机制:ISR是一个追随者副本的集合,与领导副本保持相对较新的数据同步,只有处于ISR中的追随者副本才能被选为新的领导副本。

Kafka零拷贝技术

零拷贝并不是不需要拷贝,而是减少不必要的拷贝次数。通常是说在IO读写过程中。

传统IO流程

  1. 第一次:将磁盘文件,读取到操作系统内核缓冲区;
  2. 第二次:将内核缓冲区的数据,copy到application应用程序的buffer;
  3. 第三次:将application应用程序buffer中的数据,copy到socket网络发送缓冲区(属于操作系统内核的缓冲区);
  4. 第四次:将socket buffer的数据,copy到网卡,由网卡进行网络传输。

实际IO读写,需要进行IO中断,需要CPU响应中断(带来上下文切换)
而kafka读取磁盘文件后,不需要做其他处理,直接用网络发送出去。减少第二次和第三次拷贝操作

Kafka消息传输过程中的特点

  1. 顺序读写
    磁盘顺序读写的速度远大于磁盘随机读写的速度
    kafka将topic划分为多个分区,每个分区都是一个有序且独立的日志队列,消息被顺序写入每个分区的日志队列中,保证消息在分区内的顺序性。
    来自Producer的数据,顺序追加在分区(partition)末尾,partition就是一个文件,以此实现顺序写入。
    Consumer从broker读取数据时,因为自带了偏移量,接着上次读取的位置继续读,以此实现顺序读。

偏移量维护机制:当消费者成功处理一批消息后,会向kafka提交自己的偏移量,告知kafka已经消费了这些消息,kafka会将提交的偏移量持久化并保存在特定的主体consumer_offsets中。Kafka会记录每个消费者组的偏移量信息,并在新的消费者加入时将最新的偏移量提供给它

  1. 零拷贝
    Kafka可以直接将文件读到操作系统内核,然后从内核空间的页缓存中的数据发送到网络,而无需将数据先拷贝到应用层缓存再拷贝到Socket缓存。同样的,也是直接将网络接收到的数据直接存到操作系统内核,然后完成落盘。
    • Memory Mapped Files:将磁盘文件映射到内存, 用户通过修改内存就能修改磁盘文件。工作原理是直接利用操作系统的Page来实现文件到物理内存的直接映射,完成映射之后对物理内存的操作会被同步到硬盘上(操作系统在适当的时候)。使用这种方式可以获取很大的I/O提升,省去了用户空间到内核空间复制的开销。
    • Java NIO对文件映射的支持:Java NIO,提供了一个 MappedByteBuffer 类可以用来实现内存映射。MappedByteBuffer只能通过调用FileChannel的map()取得。底层就是调用了Linux内核的mmap的API。
  2. 消费者预读机制
    消费者在从Kafka进行消息读取时,并不是一次性只读一条消息,而是从Kafka取一定量的消息存在消费者的缓存队列中,等需要再次读取消息时,从缓存队列中读取即可。当消费者消息完缓存队列中的消息,再异步从Kafka中读取。可以减少IO操作等待的时间

RocketMQ的事务

RocketMQ提供了事务消息机制,允许应用程序在发送消息时执行本地事务,并根据事务的结果决定消息是提交还是回滚。事务消息在分布式系统中具有较强的一致性和可靠性。

  1. 发送事务消息:
    应用程序发送事务消息时,首先将消息发送到指定的Broker,但是消息的状态比较为"Half Message",表示事务消息的半消息
  2. 执行本地事务:
    发送事务消息后,应用程序需要执行本地事务(例如:对数据库修改、文件本地操作等)。本地事务的执行结果需要记录下来,以便在后续步骤中进行判断。
  3. 提交或回滚
    本地事务执行完成后,应用程序根据事务的结果决定是提交事务消息还是回滚事务消息。如果本地事务成功完成,应用程序发送提交请求,将事务消息标记为"Commit Message",表示事务的提交;如果本地事务失败或遇到异常,应用程序发送回滚请求,将事务标记为"Rollback Message",表示事务消息回滚
  4. 消费事务消息:消费者消费事务消息时,会根据事务消息的状态进行消费,只有已提交的事务才会被消费,而未提交或已回滚的事务消息会被忽略。

Redis

缓存穿透、击穿、雪崩

缓存穿透

指访问一个缓存和数据库中都不存在的key,由于这个key在缓存中不存在,则会到数据库中查询,数据库中也不存在该key,无法将数据添加到缓存中,所以每次都会访问数据库导致数据库压力增大。

解决办法
将访问过的key设置为空key,加入到缓存中

缓存击穿

指大量请求访问缓存中的一个key时,该key过期了,导致这些请求都去直接访问数据库,短时间大量的请求可能会将数据库击垮。

解决办法
添加互斥锁或分布式锁,让一个线程去访问数据库,将数据添加到缓存中后,其他线程直接从缓存中获取。
热点数据key不过期,定时更新缓存

缓存雪崩

指在系统运行过程中,缓存服务宕机或大量的key值同时过期,导致所有请求都直接访问数据库导致数据库压力增大

解决办法
将key的过期时间打散,避免大量key同时过期。
对缓存服务做高可用处理。
加互斥锁,同一key值只允许一个线程去访问数据库,其余线程等待写入后直接从缓存中获取。

缓存不一致

数据库中的数据和Redis缓存中的数据不一致的问题

出现原因:
缓存和数据库中都需要更新数据,对二者的操作无法保证原子性的情况下,就会出现不一致问题

解决办法:

  • 重试机制:使用消息队列暂存要操作的数据,操作失败再从消息队列取回
  • 延迟双删:先删除缓存数据 ->再执行update更新数据表 ->最后(延迟N秒)再删除缓存

优势

数据存在内存中,直接与内存相连,读写速度很快。
使用单线程模型,无多线程 竞争 锁 等问题
支持数据持久化
支持数据备份 master-slave模式数据备份(需要多个redis实例)
操作均为原子性的

持久化

  • AOF:采用日志的形式来记录每个写操作,追加到AOF文件的末尾(默认情况是不开启AOF)重启时再重新执行AOF文件中的命令来恢复数据(AOF是执行完命令后才记录日志的)
  • RDB:把内存数据以快照的形式保存到磁盘上。RDB持久化,是指在指定的时间间隔内,执行指定次数的写操作

内存淘汰机制

当Redis中内存不足时,为了保证系统的稳定性,Redis有多种内存淘汰机制选择要删除的键

  1. LRU(默认):优先淘汰最近最少访问的键
  2. LFU:最不经常使用
  3. Random
  4. TTL:过期时间。

过期键的处理

当Redis中键过期后其实并不会直接删除键,而是采用惰性删除的方式,等用户再次访问时,会返回不存在的结果给用户,并删除这个键。
或是用户在对这个键进行其他操作,例如SET、HSET、DEL时,Redis会检查键是否过期,如果键过期也会删除这个键。

一致性hash算法

一致性hash算法主要应用于分布式存储系统中,可以有效地解决分布式存储结构下普通余数Hash算法带来的伸缩性差的问题,可以保证在动态增加和删除节点的情况下尽量有多的请求命中原来的机器节点。可用于redis集群中。

将服务器和key全部放到hash环中(hash值的计算方式是对2^32取余),key顺时针选取服务器进行存储。

如果服务器宕机了,redis会基于主从复制(每个主节点都有一个或多个从节点)中的备份服务器中挑选一个(一般根据备份服务器中获取到的数据最多的从节点选举为主节点),并为它分配槽slot
Redis将整个数据集分为0-16383总共2^14个槽位(这是由消息头大小来决定的,保证心跳包不能太大),由主节点负责管理分配槽位。当节点加入或退出时,redis各节点间相互通信,告知彼此负责的槽位。

如果在该hash环上由于服务器数量少,导致数据倾斜,就引入虚拟节点的概念;Redis中可以使用自动分片工具完成槽位的自动迁移;也可以使用虚拟槽位,将一个物理槽位映射到多个虚拟槽位,更好的均衡数据分布,减少数据倾斜的可能性
在这里插入图片描述

面对大量访问时的处理办法

Redis是单线程服务,所有指令都是顺序执行,当某一指令耗时很长时,就会阻塞后续的指令执行。当被积压的指令越来越多时,Redis服务占用CPU将不断升高,最终导致Redis实例崩溃甚至服务器宕机。

处理办法:

  • 使用Redis连接池
  • 批量操作,使用Redis管道机制
  • 使用合适的数据结构和命令
  • 使用Reids集群

面对大量数据存储

  • 数据分片,将数据存储到多个Redis节点上
  • 使用合适的数据结构
  • 合理设置过期时间
  • 使用持久化机制

键的设计

  • 遵循基本格式:[业务名称]:[数据名]:[id]
    长度不超过44字节
  • 拒绝BigKey
  • 恰当的数据格式
    对象格式的数据可以采用json存储,但是更好的是采用hash格式存储
  • 将集合类型中数据量大的进行拆分

各类型的底层结构

String:简单动态字符串

List:双向链表或压缩链表(ziplist)

  • 双向链表:列表长度较长或包含较多元素
  • 压缩链表:在一定程度上减少内存使用,存储在连续的内存区域中

Set:哈希表(Hash Table)和跳跃表(Skip List)

  • 哈希表:集合元素较多或元素大。将集合的元素作为哈希表的键,值被设为固定的空值
  • 跳跃表:有序的数据结构,支持快速查找、插入和删除(因此也被用于ZSet)

ZSet:使用跳跃表存储元素,使用哈希表存储 元素:分值

Hash:哈希表和压缩链表

  • 哈希表:hash类型的字段数量较多,或字段大小较大时
  • 压缩链表:字段的顺序是按照插入的先后顺序进行存储的

Elasticsearch

es的数据格式怎么规定的

通过mapping进行定义,可以查看我的相关文档 ElasticSearch 其中的mapping映射属性

query和filter的区别

query主要关注文档与查询条件的相关性匹配度,并根据相关性对文档进行排序。

filter主要关注筛选文档集合,并根据条件返回匹配的文档。

为什么使用es

  1. 分布式实时文件存储,可将每一个字段存入索引,使其可以被检索到
  2. 实时分析的分布式搜索引擎
    • 索引分拆成多个分片,每个分片可以有多个副本存储在不同的节点上,负载均衡等
  3. 可以扩展到上百台服务器
  4. 支持插件机制,分词插件、同步插件、Hadoop插件、可视化插件等

Docker

Docker镜像

Docker参考文章
Docker 容器的运行是基于宿主机的内核,通过linux的namespaces来实现隔离,相对于虚拟机而言降低了硬件资源的性能损耗,且具备一定程度上的应用隔离效果。

镜像是一种轻量级,可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

Java基础

类的访问权限

  • private: Java语言中对访问权限限制的最窄的修饰符,一般称之为私有的。被其修饰的类、属性以及方法只能被该类的对象访问,其子类不能访问,更不能允许跨包访问。
  • default:即不加任何访问修饰符,通常称为默认访问模式。该模式下,只允许在同一个包中进行访问。
  • protect: 介于public 和 private 之间的一种访问修饰符,一般称之为保护形。被其修饰的类、属性以及方法只能被类本身的方法及子类访问,即使子类在不同的包中也可以访问。
  • public: Java语言中访问限制最宽的修饰符,一般称之为公共的。被其修饰的类、属性以及方法不仅可以跨类访问,而且允许跨包(package)访问。

String不可变

String创建的对象是不可变的,因为String底层使用的字符数组实现,而该字符数组使用final修饰,代表该字符数组不可修改。
在使用String str = new String(“a”)创建对象后,再想使用str = str + “b” 实际上是使用了StringBuilder构建新的字符串,将str和"b"拼接后使用toString()返回一个新的对象,将str指向该对象,原来的"a"并没有被修改。
而在上述例子中,"a"存在堆内存中,"b"存在常量池中,"ab"存在堆内存中。因为新声明的对象是存在于堆内存中的;而直接使用""的字符串会现在常量池中查,没有的话就在常量池中创建一个该对象;连接后的对象也存在堆内存中,可以理解为是由StringBuilder.toString返回的对象

StringBuilder和StringBuffer

区别StringBuilderStringBuffer
线程安全非线程安全线程安全,使用synchronized同步
性能没有同步操作,单线程环境下性能更好同步会带来性能开销,多线程环境下性能略差

二者共有的方法
append(str):在字符串末尾添加指定字符串
insert(index, str):在指定位置插入字符串
delete(start, end):删除指定范围内的字符串
deleteCharAt(index):删除指定位置的字符
replace(start, end, str):用指定的字符串替换指定范围内的字符
substring(start):返回从指定位置开始到字符串末尾的子字符串
substring(start, end):返回指定范围内的子字符串
length():返回字符串的长度
charAt(index):返回指定位置的字符
setCharAt(index, ch):将指定位置的字符设置为指定的字符
indexOf(str):返回指定字符串在字符串中第一次出现的位置
toString():转为String对象

StringBuilder特有的方法:
capacity():返回当前容量(内部字符数组的长度)
ensureCapacity(minimumCapacity):确保容量至少为指定值(减少自动扩容的次数)
trimToSize():将容量调整为字符串的长度(节省内存空间)
setLength(newLength):设置字符串的长度(用于截断或扩展字符串)

List中的线程安全和非线程安全的List

ArrayList和LinkedList是非线程安全的
Collections.synchronizedList:线程安全,将传入的普通List包装成同步的List
CopyOnWriteArrayList:写时复制策略,当列表进行修改时,会创建一个底层数组的副本,确保修改操作不会影响正在进行的其他线程的读操作。

但大部分情况下还是使用其他同步操作,例如锁来确保线程安全

Hash冲突的解决方式

  1. 链表法(HashMap)+ 红黑树(默认链表长度超过8)
  2. 再哈希法
  3. 开放定址法
  4. 建立公共溢出区

HashMap和HashTable的对比

  1. 线程安全
    HashMap不支持线程安全;HashTable通过使用synchronized锁住整个表来进行同步

  2. 空值和空键
    HashTable不支持空值和空键;HashMap支持空键(但仅一个),也支持空值

  3. 有序性
    迭代时HashTable元素的顺序与插入顺序相同,HashMap迭代时不保证元素的顺序

HashMap和CurrentHashMap的对比

HashMap线程不同步,底层使用链表和红黑树的方式处理冲突
CurrentHashMap:

  • JDK 1.7 :使用segment数组,每个segment中存储一定量的HashEntry,HashEntry用链表处理冲突;线程安全通过锁被操作的segment
  • JDK 1.8 :直接使用Node结点,使用Synchronized和CAS的方式实现线程安全

红黑树的特性,以及保持平衡的方式

  1. 每个结点不是红色就是黑色
  2. 根节点是黑色的
  3. 如果一个节点是红色的,则它的子结点必须是黑色的(没有连续的红色节点)
  4. 对于每个结点,从该结点到其所有可到达的叶结点的路径中,均包含相同数目的黑色结点
  5. 每个 NIL 叶子结点都是黑色的(此处的叶子结点指的是空结点)

保持平衡:

  • 插入:
    • 插入新节点涂红色
    • 如果父节点为黑色不用操作
    • 如果父节点为红色
      • 父节点无兄弟节点或父节点的兄弟节点为黑色,直接旋转操作
      • 父节点有兄弟结点,且兄弟结点也为黑色,直接上溢:将父节点和叔都涂为黑色,将祖父节点涂为红色,然后在上溢部分继续判断是否需要处理
  • 删除:
    • 删除结点都在B树的最后一层,可以理解为都在红黑树的最下面2层
    • 删除最后一层的红色结点没有影响
    • 删除最后一层的黑色节点:
      • 如果黑色节点有2个子节点,不允许删除
      • 如果黑色结点只有一个子节点,删除黑色结点,并用子节点取代该位置,同时染成黑色
      • 如果黑色结点为叶子节点:
        • 该节点没有兄弟节点(只能是根节点),直接删除
        • 该节点的兄弟结点为黑色,旋转兄弟结点
        • 该节点的兄弟结点为红色,旋转兄弟和父节点

Java快速失败机制

用于在并发环境下检测到在遍历或操作集合时的并发修改,并迅速抛出异常,以防止出现不确定的行为或损坏的数据。
在集合对象被多个线程访问时,如果其中一个线程修改了集合的结构,其他正在遍历或修改该集合的线程会立即感知到集合的结构发生了变化,并会抛出ConcurrentModificationException(运行时异常)。
主要是在使用Iterator和并发集合类时发生

解决方式:

  • 使用CopyOnWriteArrayList,由于使用复制操作,不会抛出上述异常。
  • 使用锁对ArrayList进行操作时同样可以防止快速失败

JVM

JVM的调优

  1. 调整内存大小
  2. 调整线程池大小
  3. 类加载调优
  4. 并发调优

类加载机制

java源码经过javac编译后形成class字节码文件,jvm将字节码文件加载进内存,经过字节码验证器验证,为类变量进行内存分配和初始化零值,字节码解释器解释执行字节码指令转换为底层机器代码并执行。

双亲委派

如果一个类加载器收到了类加载的请求,他首先不会自己去尝试加载这个类,而是把这个请求委派父类加载器去完成。每一个层次的类加载器都是如此,因此所有的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个请求时,子加载器才会尝试自己去加载。
当发现在某一步该类已经被加载过时,会直接返回已加载的类,避免重复加载

反射

反射通过类加载器加载class字节码文件实现,可通过一个类的字节码获取该类的所有信息,包括属性、方法等

获取类对象

  1. Class<?> clazz = MyClass.class;通过类字面常量获取类对象
  2. Class<?> clazz = Class.forName("com.example.MyClass");通过Class.forName()方法获取
  3. MyClass object = new MyClass(); Class<?> clazz = obj.getClass();通过对象的getClass()获取
  4. Class<?> clazz = getClass().getClassLoader().loadClass("com.example.MyClass");

根据类对象生成对象实例:Object object = (Object)clazz.newInstance();

根据类对象获取属性和方法:

  • getFields():获取所有public的属性
  • getDeclaredFields():获取所有(不限修饰符的)属性
    • getType:以Class形式返回类型
    • getName:返回属性名
    • setAccessible(true):使用
  • getMethods():获取所有pubic的方法
  • getDeclaredMethods():获取所有(不限修饰符的)方法
    • getReturnType:以Class形式获取返回类型
    • getName:返回方法名
    • getParameterTypes:以Class[]返回参数类型数组
    • invoke(obj, param1, ...):调用方法
  • getConstructors():获取所有public的构造方法
  • getDeclaredConstructors():获取所有(不限修饰符的)构造方法
    • getModifiers:以int形式返回修饰符.
    • getName:返回构造器名(全类名).
    • getParameterTypes:以Class[]返回参数类型数组

异常

异常的分类,以及有什么区别

Error(错误):是程序无法处理的错误,表示代码运行时JVM出现的问题。例如:OutOfMemoryErrorNullPointerException
Exception(异常):是程序本身可以处理的异常。

  • RuntimeException:运行时异常,不受检查异常,表示JVM常用操作引发的错误,编译时能通过,但会在后期代码的执行过程中暴露出来
  • 其他异常:受检查异常,在编译时不能被忽略,程序必须对它有相应的处理

计算机网络

Https为什么安全

  1. 浏览器发起 HTTPS 请求
  2. 服务端返回 HTTPS 证书
  3. 客户端验证证书是否合法,如果不合法则提示告警
  4. 当证书验证合法后,在本地生成随机数
  5. 通过公钥加密随机数,并把加密后的随机数传输到服务端
  6. 服务端通过私钥对随机数进行解密
  7. 服务端通过客户端传入的随机数构造对称加密算法,对返回结果内容进行加密后传输

数据传输是用对称加密:非对称加密的加解密效率非常低

  1. https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。
  2. http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议。
  3. http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
  4. http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。

TCP拥塞控制

  1. 慢启动
    • 最初拥塞控制窗口cwnd的初始值为一个小值,推荐为2个MSS(MSS通常为536字节或1460字节)
      • MSS:Maximum Segment Size ,最大报文段长度,TCP提交给IP层最大分段大小
    • 当收到一个ACK后,cwnd = cwnd*2
  2. 拥塞避免
    • 拥塞窗口cwnd值等于慢开始门限值后,cwnd = cwnd+1
  3. 快速恢复
    • 拥塞时,将慢开始门限修改为cwnd/2,并且将cwnd = cwnd/2
  4. 快速重传
    • 连续收到3条对某报文段的重传确认,就需要快速重传该报文段

地址框输入url后经历了什么

  1. DNS解析
    • 在自己的DNS高速缓存中查找
    • 权限域名服务器->顶级域名服务器->根域名服务器
  2. TCP连接
    • TCP三次握手
  3. 发送HTTP请求
  4. 服务端处理请求:根据路径查找对应的资源(服务),然后返回数据报
  5. 浏览器收到返回的数据,进行渲染,形成页面
  6. TCP断开
    • 四次挥手

场景问题

流量大对服务的影响

影响

  1. 性能下降:服务无法处理大量请求,导致响应时间延长或请求超时。
  2. 内存压力:大量并发请求导致服务消耗大量内存,如果内存资源不足,服务可能崩溃或不稳定。
  3. CPU负载过高:高流量会导致服务的CPU使用率增加,可能导致服务响应变慢甚至崩溃。
  4. 网络瓶颈:服务的网络带宽可能成为瓶颈,无法处理大量的进出流量,可能导致请求延迟增加或无法建立连接
  5. 数据库负载过高
  6. 安全风险:DDoS攻击或恶意请求,导致服务不可用或数据泄露

解决

  1. 横向扩展:增加服务器数量,添加服务实例的数量,负载均衡
  2. 缓存
  3. 异步处理
  4. 数据库优化
  5. 限流和熔断
  6. 监控和日志
  7. 优化算法和数据结构

数据有效期为半年

定时任务

30分钟延迟任务

  1. 使用RabbitMQ或RocketMQ的延迟队列
  2. 使用死信队列的方式
  3. 使用Redis的ZSet,根据当前时间去redis的zset中获取任务时间进行处理

秒杀系统

概念:举个例子,演唱会门票设置开抢时间,到时间后才能进行购买,而一般情况是,热门的演唱会门票会在几秒内销售一空。

问题:并发读、并发写的性能要求,高可用,高性能,数据一致性

解决

  1. 尽可能使用静态页面,将尽可能少的元素用于动态资源,例如:时间。
    缓存静态资源的方式:直接缓存整个响应体,这样发起请求时只需要返回对应的响应体就行,而不需要再次发起请求。
    缓存数据存放位置:最好的方式是存在CDN(内容分发网络)中,即离用户最近的网络边缘。用户发起请求时,DNS会直接导向CDN,从CDN的高速缓存中返回数据。
    缓存命中和失效的问题:节点选取在访问量大的地区,距离主站远的地区,节点与主站间网络质量良好的地区;选择CDN的二级缓存,内存大
  2. 热点
    • 热点操作:对热点操作进行限制,比如频繁刷新进行提示阻断,对秒杀按钮在秒杀前进行灰化
    • 热点数据:
      • 静态热点数据:大数据计算,预测热点数据,进行缓存等
      • 动态热点数据:无法预测,可异步采集各节点的热点信息并进行分析,及时返回给系统,系统根据信息进行处理,例如缓存或限流,实现热点保护,避免击穿等
    • 热点隔离:
      • 业务层:从业务层上提前通知即将到来的热点数据,进行缓存
      • 系统层:隔离部署,使用不同的域名或服务器
      • 数据层:使用不同的缓存集群或数据库服务组
    • 热点优化:缓存、限流
  3. 库存:
    常用的三种减库存方式:下单减库存,付款减库存,预扣库存;目前采用比较多的是预扣库存
  4. 高并发读写
    • 读:使用分布式缓存,甚至允许读一部分的脏数据,写的时候再保证一致性
    • 写:可以使用Redis,写入数据库时使用分布式锁,避免高并发
  5. 高可用
    • 流量削峰:
      • 答题:将秒杀时大量的流量,通过答题的方式延迟流量的到来时间,从<1s,延长到<10s
      • 排队:
        • 消息队列:可将同步的直接调用改为异步的间接推送缓冲瞬时流量
        • 线程池加锁等待
        • 文件序列化写,再顺序读
      • 过滤:
        • 读限流
        • 读缓存
        • 写限流
        • 写缓存

限流 削峰 熔断 服务降级

限流

对超出服务处理能力之外的请求进行拦截,对访问服务的流量进行限制。

限流算法

  1. 计数器(固定窗口):对服务请求计数,到达一定数量后拒绝或排队
  2. 滑动窗口
  3. 漏斗算法:漏斗满就拒绝
  4. 令牌桶算法:获得令牌则可以请求,否则不能请求

限流策略:

  1. 服务拒绝
  2. 延迟处理
  3. 请求分级(优先级)
  4. 动态限流:监控
  5. 监控预警,动态扩容

削峰

延缓用户请求,缓解高并发的压力

削峰方案

  1. 答题
  2. 消息队列
  3. 分层过滤:CDN->缓存->后台系统->DB

熔断

当某服务超时或不可用时,为了防止整个系统雪崩,暂时停止对某服务的调用
在SpringCloud框架里熔断机制通过Hystrix实现,Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内调用20次,如果失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand

服务降级

服务降级指的是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

方式

  1. 延迟服务:将同步的方式改为延迟的降级
  2. 关闭某些服务:关闭相应功能区,如推荐区等
  3. 写降级:高并发场景下先写入缓存,之后再异步写入DB,保证一致性即可
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值