面经分享--金山软件开发一面
一、介绍一下TCP/IP四层模型和OSI七层模型
OSI七层模型就是我们网络传输的一整个流程吧,从上至下是 应用层-表示层-会话层-传输层-网络层-数据链路层-物理层。
每一层的职责:
应用层:给应用程序提供了一套统一的接口
表示层:把数据转化成兼容另一个系统能识别的格式
会话层:负责建立,管理表示层实体之间的通信会话
传输层:将数据进行端到端的传输
网络层:负责网络的路由,分片等
数据链路层:负责数据的封帧和差错检测,MAC寻址
物理层:在物理网络中传输数据帧
TCP/IP四层模型是将七层模型给封装了
应用层:包括应用层,表示层,会话层。支持 HTTP、SMTP 等最终用户进程
传输层:处理主机到主机的通信(TCP、UDP)
网络层:寻址和路由数据包(IP 协议)
网络接口层:通过网络的物理电线、电缆或无线信道移动比特
二、访问一个网站的全过程是什么样的
1.解析URL:分析URL所需要使用的传输协议和请求的资源路径,搜索引擎会对URL进行检查,判断是否合法
2.解析URL,分别从本地浏览器缓存,操作系统缓存,本地域名服务器,根服务器,顶级服务器,权威服务器(递归的方式进行查询),返回结果
3.获取MAC地址:浏览器得到返回回来的IP地址之后,还需要知道目的主机的MAC地址,因为数据链路层的传输需要知道MAC地址。通过IP地址与本机的子网掩码相结合,可以判断是否与请求主机在同一个子网中,如果在同一个子网中就会使用ARP协议获取目的主机的MAC地址
4.建立TCP连接:通过三次握手的方式进行连接
5.发送HTTP请求:请求中包含用户需要获取的资源信息,例如网页的URL,请求方式等
6.服务器处理请求并返回响应:
三、说说TCP的三次握手
1.客户端向服务器发送一个syn报文,随机初始化序号,将这个序号放到报文里面的“序号”字段中,然后将syn标志位改为1,表示是syn报文,之后状态变为syn_sent状态
2.服务端监听的端口发生变化后,收到这个报文进行判断,这个报文是否合法,是否有效,判断之后发送ack+syn的报文,也是随机初始化序号放到“序号”字段中,将syn,ack的标志位都改为1,然后发送给客户端,之后进入syn_rcvd状态
3.客户端收到报文之后,还要向服务端发送最后一个ack报文,表示收到了请求,之后进入established状态。
注意:前两次握手不能携带具体的数据,第三次可以
四、为什么要四次挥手
1.一般是客户端先发送一个FIN报文,也是一样将FIN标志位改为1,代表是FIN报文,表示客户端不会再发送任何的数据了,进入fin_wait_1状态
2.服务端收到FIN报文之后,立马恢复一个ACK,表示收到了此消息,进入CLOSE_WAIT状态,在收到FIN报文之后,TCP会创建一个EOF文件结束符到接收缓冲区中,放在接收缓冲区的最后,服务端必须处理完这个EOF之前的数据才能read到这个EOF结束符。
3.服务端read到这个EOF之后,代表数据已经被全部处理了,然后服务端就会发送fin报文给客户端,此时服务端进入LAST_ACK状态
4.客户端收到服务端的FIN报文之后,返回ACK确认包,然后进入TIME_WAIT状态
5.服务端收到ACK之后,进入最后的CLOSE状态
6.客户端进入2MSL时间之后,也会进入CLOSE状态
五、系统中有很多TIME_WAIT怎么处理
1.HTTP没有使用长连接:根据大多数 Web 服务的实现,不管哪一方禁用了 HTTP Keep-Alive,都是由服务端主动关闭连接,那么此时服务端上就会出现 TIME_WAIT 状态的连接。
2.HTTP长连接超时:如果客户端在完后一个 HTTP 请求后,在 60 秒内都没有再发起新的请求,定时器的时间一到,nginx 就会触发回调函数来关闭该连接,那么此时服务端上就会出现 TIME_WAIT 状态的连接。可以往网络问题的方向排查,比如是否是因为网络问题,导致客户端发送的数据一直没有被服务端接收到,以至于 HTTP 长连接超时。
3.HTTP长连接的请求数量达到上限:如果达到这个参数设置的最大值时,则 nginx 会主动关闭这个长连接,那么此时服务端上就会出现 TIME_WAIT 状态的连接。 调大nginx的keepalive_requests参数
六、什么是syn泛洪攻击,如何防范
syn泛洪攻击是耗尽服务器的半连接队列资源,导致正常用户无法与服务器建立正常的连接。
在建立3次握手的时候,服务器返回了syn+ack之后,会将这个未完成的连接放入一个专门的缓冲区中,等到客户端的ack,但是客户端一直不返回ack,导致大量的半连接出现在这个缓冲区中占领资源,导致新的连接无法连接。
解决方法:1.调大这个队列
2.开启net.ipv4.tcp_syncookies:
当 「 SYN 队列」满之后,后续服务端收到 SYN 包,不会丢弃,而是根据算法,计算出一个
cookie
值;将 cookie 值放到第二次握手报文的「序列号」里,然后服务端回第二次握手给客户端;
服务端接收到客户端的应答报文时,服务端会检查这个 ACK 包的合法性。如果合法,将该连接对象放入到「 Accept 队列」。
最后应用程序通过调用
accpet()
接口,从「 Accept 队列」取出的连接。
3.减少syn+ack重传次数:那么针对 SYN 攻击的场景,我们可以减少 SYN-ACK 的重传次数,以加快处于 SYN_REVC 状态的 TCP 连接断开。
七、Redis的布隆过滤器
主要是用于解决缓存穿透这样的问题,我们给缓存和数据之前加一层布隆过滤器,布隆过滤器是一组存储二进制数据的数组,通过设定几个哈希函数,将一个值进行几次哈希函数得到的值,将这个位置的数组改为1,之后要查询某个数值的时候,先用哈希函数进行判断,这些位置上的值是不是1,如果是再去缓存或者数据库中进行查找,如果不都是1,代表缓存和数据库中不存在该值,就不走查询。注意:因为当数据多的时候会出现哈希函数映射大集合映射到小集合,所以布隆过滤器中存在数据库中不一定存在,布隆过滤器不存在代表数据库一定不存在。
八、Redis如何保证数据一致性
Redis保证的是最终一致性,不能保证强一致性,如果一定要保证强一致性那么可以通过加锁的方式,但是都加锁了其实redis就没什么必要了,就直接去数据库中查比较好
最终一致性:读操作先去缓存中读,有就返回,没有就去数据库中查
写操作:先更新数据库,再更新缓存。
这时候就会出现问题也就是
当已经把Redis中的数据删了之后,写Mysql延迟了,这时候又有客户端去读redis,发现redis中没有,从mysql查到了之前没有修改的数据,返回并存放到redis中,导致之后的所有请求都是从redis这里获取,导致数据的不一致性。
使用延迟双删的方式:
针对删除缓存异常的情况:
1.删除缓存重试机制(消息队列实现)
如果应用删除缓存失败,可以从消息队列中重新读取数据,然后再次删除缓存,这个就是重试机制。当然,如果重试超过的一定次数,还是没有成功,我们就需要向业务层发送报错信息了。
如果删除缓存成功,就要把数据从消息队列中移除,避免重复操作,否则就继续重试。
2.使用bin log,再操作缓存
使用阿里巴巴的Canal组件,Canal 模拟 MySQL 主从复制的交互协议,把自己伪装成一个 MySQL 的从节点,向 MySQL 主节点发送 dump 请求,MySQL 收到请求后,就会开始推送 Binlog 给 Canal,Canal 解析 Binlog 字节流之后,转换为便于读取的结构化数据,供下游程序订阅使用。
将binlog日志采集发送到MQ队列里面,然后编写一个简单的缓存删除消息者订阅binlog日志,根据更新log删除缓存,并且通过ACK机制确认处理这条更新log,保证数据缓存一致性。
九、说说网络IO
BIO:阻塞型IO,当发送IO操作的时候,线程中其他所有操作都被阻塞了,效率较低
NIO:非阻塞型IO,一个线程中可以进行多个IO操作,这时候会有一个监听器如select,poll,epoll去监听这样的IO操作,一旦有IO操作准备好之后,应用程序会被通知
AIO:异步的IO模型:应用程序发起IO操作之后会做其他事情,通过异步的方式由操作系统内核完成,应用程序只需要等待通知即可。
十、IO多路复用的实现
一个线程中可以同时处理多个IO操作,能够资源复用,防止创建过多的线程导致上下文切换的开销。
具体实现:
select/poll/epoll 是如何获取网络事件的呢?在获取事件时,先把所有连接(文件描述符)传给内核,再由内核返回产生了事件的连接,然后在用户态中再处理这些连接对应的请求即可。
select:将所有socket连接放到一个文件描述符集合中,然后将这些连接拷贝到内核中,通过遍历的方式去判断是否由网络事件产生,如果有事件产生将此socket标记为可读或可写,然后再拷贝回用户态,用户态再通过遍历的方式找到这样的socket,然后处理
select使用的是bitsmap,而poll使用的是动态数组,以链表的形式来组织。
epoll:
epoll 在内核里使用红黑树来跟踪进程所有待检测的文件描述字,所以只需要传入一个待检测的 socket,减少了内核和用户空间大量的数据拷贝和内存分配。
epoll 使用事件驱动的机制,内核里维护了一个链表来记录就绪事件,当某个 socket 有事件发生时,通过回调函数内核会将其加入到这个就绪事件列表中,当用户调用 epoll_wait() 函数时,只会返回有事件发生的文件描述符的个数,不需要像 select/poll 那样轮询扫描整个 socket 集合,大大提高了检测的效率。
十一、Mysql 的InnoDB为什么要使用B+树,而不是B树
1.B+树的叶子节点只存储数据+主键索引,非叶子节点存储其他索引信息,这样就使得在相同数据量的情况下,相比B树的非叶子节点,B+树的叶子节点可以存放更多的索引,B+树的高度就会下降,使得IO磁盘的次数也较少
2.B+树有很多冗余节点,所有非叶子节点都是冗余索引),这些冗余索引让 B+ 树在插入、删除的效率都更高,比如删除根节点的时候,不会像 B 树那样会发生复杂的树的变化;
3.B+ 树叶子节点之间用链表连接了起来,有利于范围查询,而 B 树要实现范围查询,因此只能通过树的遍历来完成范围查询,这会涉及多个节点的磁盘 I/O 操作,范围查询效率不如 B+ 树。
十二、什么是CAP
分布式系统中,C:一致性,A:可用性,P:分区容错性,三者不可兼得
一致性(C) : 在分布式系统中的所有数据备份, 在同一时刻是否同样的值(等同于所有节点访问同一份最新的数据副本)
可用性(A): 在集群中一部分节点故障后, 集群整体是否还能响应客户端的读写请求(对数据更新具备高可用性)
分区容忍性(P): 以实际效果而言, 分区相当于对通信的时限要求. 系统如果不能在时限内达成数据一致性, 就意味着发生了分区的情况, 必须就当前操作在 C 和 A 之间做出选择
十三、算法
只出现一次的数字:异或操作