面试官您好,我是钱超,来自湖南大学信息科学与工程学院软件系,目前是研二在读,我的研究方向是视频流传输协议。研究生期间,我的学分绩点3.82,位列专业第一,科研方面发表了一篇 CCF-B 会议论文,并有一项发明专利进入实审。英语水平通过了CET6 568分。
专业技能方面熟悉 计网、操作系统 等计算机基础知识,了解 Java并发、JVM、Spring框架 等技术。项目经历的话有一个自学的基于 SpringBoot 的开发项目,使用 MyBatisPlus 完成对数据库的CRUD,使用阿里云OSS实现图片的上传,实现 MySQL 的主从同步,并且使用 Redis 实现了购物车缓存以加快数据访问等。最近我也在学习微服务相关知识,尝试把这个单体项目改造成基于 SpringCloud 的微服务项目。大致的情况就是这样。
小林奶茶店是专门为奶茶饮品店定制的一款软件产品,包括系统管理后台和移动端应用两部分。其中系统管理后台主要提供给餐饮内部员工使用,可以对餐厅的菜品、套餐、订单进行管理和维护。移动端应用主要提供给消费者使用,可以在线浏览菜品、添加购物车、下单等。
项目总体基于 SpringBoot + MybatisPlus 进行开发,使用 MySQL 数据库进行数据存储,使用 Redis 进行项目优化,使用 git 和 maven 进行版本控制和项目管理。
当用户数量较多时,系统访问量大,频繁的访问数据库,数据库压力大,系统的性能下降,用户体验感差。因此使用Redis对数据进行缓存,从而减小数据库的压力,有效提高系统的性能和访问速度。
具体操作:导入Spring Cache和Redis依赖坐标,ttl过期时间设为30分钟,@EnableCaching 开启缓存,在对应的控制器方法上标注 @Cacheable 注解对结果缓存。在 save 和 delete 方法上标注 @CacheEvict 标注,对数据库做修改时删除缓存,保证数据一致性。
数据库设计共为11张表,分为员工、用户、地址、饮品、订单、套餐等等。套餐为饮品的各类组合,套餐和饮品的关系由 单独一张 表来维系,饮品包括不同口味,由dish_flavor 表来维系,一个订单可能包括多个内容(即饮品或套餐),这些内容由 order_detail 表来维系,order表则记录每次付钱后的订单
正向代理代理客户端,反向代理代理服务器。
冒泡 O(n), 选择 O(n^2), 插入 O(n^2), 归并 O(nlogn), 快排 O(nlogn), 堆排 O(nlogn)
顺序查找、二分查找、哈希查找、树表排序树(二叉排序树、平衡树、红黑树)
满二叉树:每一层的节点数都达到最大值,即 2 h − 1 2^{h-1} 2h−1个,其中h是树的高度。满二叉树的总节点数是 2 h − 1 2^h-1 2h−1个。
完全二叉树:除了最后一层外,其他层的节点数都达到最大值,且最后一层的节点都连续集中在最左边。总节点数在 2 h − 1 2^{h-1} 2h−1到 2 h − 1 2^h-1 2h−1之间。
满二叉树一定是完全二叉树,但完全二叉树不一定是满二叉树。
物 数 网 传 应。其中只有应用层在用户空间,其它层都在OS内核中。
IP地址类似住址门牌号,住在不同地方就有不同门牌号,快递根据门牌号找到所在位置。而MAC地址类似身份证号,设备出厂就固定写死了,但是知道身份证号是没法找到人的,身份证号和地理位置无关。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nwCwQz8G-1679660470777)(.assets/三次握手.png)]
过程:
为什么需要三次握手:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RqVUrIBG-1679660470779)(.assets/四次挥手.png)]
过程:
为什么需要四次挥手?
也就是要等待服务端把手上的数据处理发送完再关闭。
为什么需要 TIME_WAIT 状态?
超时重传:
在发送数据时,设定一个定时器,当超过指定的时间后,没有收到对方的 ACK 确认应答报文(数据包丢失/确认应答丢失),就会重发该数据。
问题:超时重传时间 RTO 的设定,理论上应略大于 RTT。但RTT无法准确计算,Linux采用加权移动平均,采样估计。
快速重传:
当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段。
问题:重传时是重传一个还是重传所有已经发出去的数据包,只重传一个可能造成延迟重传,重传所有可能浪费网络资源
SACK: Selective ACK
在 TCP 头部「选项」字段里加一个 SACK,将已收到的数据信息发给「发送方」,这样发送方就可以只重传丢失的数据。
Duplicate SACK:
使用 SACK 来告诉「发送方」有哪些数据被重复接收了,这样「发送方」就知道数据没有丢,是「接收方」的 ACK 确认报文丢了。
滑动窗口:
为了提高网络的传输效率,引入滑动窗口,双方各自维护发送窗口和接收窗口。
在发送窗口范围内的数据都可以发送出去,窗口后延之外的数据都是暂时不能发送的数据。随着接收到的数据,窗口会向后移动。
一般由接收方告诉发送方自己还有多少缓冲区可以用于接收数据,发送方根据接收方的处理能力调整窗口大小。但双方窗口只是近似相等,传输过程存在延迟,窗口也时刻在变化。发送窗口 = min(拥塞窗口,接收窗口)
为了防止资源紧张时,操作系统减小缓存,同时收缩窗口,导致丢包和窗口变负值的情况,TCP规定必须先收缩窗口,再减少缓存。
窗口缩减为0后,需要定时发送窗口探测报文,确认当前窗口大小,避免双方持久等待。
糊涂窗口综合征:发送方为了填满接收方几个字节的窗口,发送一整个TCP报文,而TCP+IP首部就有40字节,会造成极大资源浪费。
为了解决这一问题,接收方在窗口小于min(MSS, 0.5*cache)时,就发送0窗口通告;而发送发采用Nagle延迟处理,必须等窗口>=MSS且数据大小>=MSS,或者收到之前发送数据的ACK,才会继续发送。即接收方得满足「不通告小窗口给发送方」+ 发送方开启 Nagle 算法,才能避免糊涂窗口综合症。
流量控制是为了避免「发送方」的数据填满「接收方」的缓存,但是并不知道网络中发生了什么。而拥塞控制是为了避免「发送方」的数据填满整个网络。
拥塞窗口:由发送方维护,根据网络的拥塞程度动态变化的。
慢启动
TCP刚建立完成后,需要一点一点提高发包速度。
每当发送方收到一个ACK,拥塞窗口的大小就会加1,发包数呈指数增长,直到 cwnd >= ssthresh 慢启动门限。
拥塞避免
cwnd超过慢启动门限后,进入拥塞避免算法。
每收到一个ACK报文,拥塞窗口cwnd增加1/cwnd。发包数呈线性增长。
拥塞发生
一旦发生丢包、延时导致重传,进入拥塞发生阶段。
超时重传:ssthresh = 0.5 * cwnd,cwnd重置为1 (Linux初始值为10)
快重传
当接收方发现一个乱序到达的报文段,就会发送三次前一个包的 ACK。于是发送端收到三个相同的ACK报文,就会快速重传,不必等到超时再重传。
这时 cwnd /= 2,ssthresh = cwnd,进入快恢复。
快恢复
快速恢复算法是认为,你还能收到 3 个重复 ACK 说明网络也不那么糟糕,所以没有必要像 RTO 超时那么强烈。
快重传阶段更新完成后,cwnd = ssthresh + 3,重传丢失数据包。
如果再收到重复的ACK,cwnd + 1;如果收到新数据的ACK,cwnd设为原始 ssthresh(+3前的),再次进入拥塞避免。
如果TCP层不进行分片,仅在IP层分片,那么当一个TCP报文段的某个IP分片丢失,接收方的IP层无法组装成一个完整的TCP报文,也就不会响应ACK给发送方。于是发送方就会一直等待直到超时重传,因此由IP层进行分片传输效率很低。
所以,为了达到最佳的传输效能 TCP 协议在建立连接的时候通常要协商双方的 MSS 值,使得IP包长度不会大于MTU,也就不用IP分片了。这样即使一个IP分片丢失,进行重发时也是以 MSS 为单位。
对于UDP,OS不会对消息进行拆分,每个UDP报文就是一个完整的用户消息。
而对于TCP,消息可能会被操作系统分组成多个 TCP 报文,因此接收方的程序如果不知道发送方发送的消息长度/边界,就无法读出一个有效的用户消息。因此我们说 TCP 是面向字节流的协议。
当两个消息的某个部分内容被分到同一个 TCP 报文时,即 TCP 粘包问题,这时接收方不知道消息的边界的话,无法读出有效的消息。
解决方法
由应用程序负责:
DNS 域名解析协议
作用:域名 -> IP地址
查询顺序:
ARP 地址解析解析
作用:IP -> MAC
原理:广播 ARP 请求 -> 解包匹配 -> 单播 ARP 响应 -> 有限缓存
RARP:MAC -> IP,用于打印机服务器等小型嵌入式设备接入网络
DHCP 动态主机配置协议
作用:动态获取IP地址
原理(全程 UDP广播):
NAT 网络地址转换协议
作用:私有IP -> 公有IP (NAPT:网络地址和端口转化协议)
原理:转换表
缺点:外网无法主动连接、性能开销、重启连接断开
解决:IPv6、NAT穿透(应用程序主动建立端口映射)
ICMP 互联网控制报文协议
作用:确认 IP 包是否成功送达目标地址、报告发送过程中 IP 包被废弃的原因和改善网络设置等。
分类:查询报文(如 Ping 命令)、差错报文(如 Traceroute 命令)
IGMP 因特网组管理协议
作用:维护 IGMP 路由表,管理加入、离开组播组的主机
超文本传输协议,超文本指文字、图片、视频等等信息,传输指在多个设备之间,协议指规定了通信的方式。
状态码
字段
Content-Type: text/html; Charset=utf-8
Accept: */*
表示任意缓存
HTTP/1.1
优点:简单、灵活、易于扩展、应用广泛、跨平台。相比于1.0,HTTP/1.1 使用长连接(减少连接建立释放的开销),支持管道化传输(一次发起多个请求)
缺点:
HTTP/2
HTTP/3
使用基于UDP的 QUIC (一个在 UDP 之上的伪 TCP + TLS + HTTP/2 的多路复用的协议)协议。特点:
TODO
HTTP 请求都是明文,因此GET/POST都不安全,此外GET没有规定不能携带请求体,POST请求URL也可以有参数。
运算器、控制器、存储器、输入设备、输出设备
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZRQ49Wgm-1679660470780)(.assets/%E5%86%AF%E8%AF%BA%E4%BE%9D%E6%9B%BC%E6%A8%A1%E5%9E%8B.webp)]
指令周期的四级流水线:Fetch -> Decode -> Execute -> Store
硬件32/64位指CPU位宽,软件32/64位指指令的位宽。
64位相比32位的优势:
每个存储器只和相邻的一层存储器设备进行交互,形成缓存体系。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Umuap03-1679660470781)(.assets/%E4%B8%89%E7%BA%A7%E7%BC%93%E5%AD%98.webp)]
CPU Cache 是由很多个 Cache Line
组成的,Cache Line
是 CPU 从内存读取数据的基本单位(例如一次载入 64Byte),由各种标志(Tag)+ 数据块(Data Block)组成。
直接映射
因此,一个内存的访问地址由 组标记、Cache Line 索引、偏移量 三者共同定位。其它映射例如全相联、组相联等类似。
提升缓存命中率
提升缓存命中率,也就可以提升程序执行速度,优化方法例如:
写入数据
缓存一致性问题
由于 L1/L2 Cache 是多个核心各自独有的,因此可能带来多核心的缓存一致性问题。因此需要一种同步机制,能够实现:
Modified
:已修改。Cache 数据和内存中数据不一致,可以自由写入,被替换时需要写回内存。Exclusive
:独占。仅一个核心存储该 Cache Line,可以自由写入,不需要通知其它核心。如果其它核心从内存读取了该 Cache Line,那么将转为共享状态Shared
:共享。修改时需要向所有其它核心广播请求,要求把该 Cache Line 标记为无效,然后再更新Invalidated
:已失效。数据不一致,不可以读取该状态数据。伪共享
由于多个线程同时读写同一个 Cache Line 的不同变量,而导致 CPU Cache 失效的现象。
解决方案:
Linux 内核中,无论是进程还是线程,调度的都是 task_struct
结构体,区别在于线程的task_struct
部分资源是共享了进程已创建的资源,如内存地址空间、代码段、文件描述符等。
调度器
调度优先级从上到下,dl_rq -> rt_rq -> cfs_rq,也即实时任务总是先于普通任务被执行。
进程优先级可以通过 nice | renice
设置/更改,范围 -20~19,是一个修正值,nice 值越小优先级越高。
中断是系统用来响应硬件设备请求的一种机制,操作系统收到硬件的中断请求,会打断正在执行的进程,然后调用内核中的中断处理程序来响应请求。中断是一种异步的事件处理机制,可以提高系统的并发处理能力。
Linux 为了解决中断处理程序执行过长和中断丢失的问题,将中断处理程序分成了两个阶段:
另外,硬中断会打断 CPU 正在执行的任