C++面试速通宝典——17

283. Nginx负载均衡算法

‌‌‌‌  Nginx支持多种负载均衡算法。

  1. 轮询(Round Robin):默认算法,按顺序逐个分配请求到后端服务器。
  2. 加权轮询(Weighted Round Robin):与轮询类似,但可以指定权重,权重高的服务器接受更多请求。
  3. 最少连接数(Least Connections):结合最少连接数和权重,选择最优服务器。
  4. 加权最少连接数(Weighted Least Connections):结合最少连接数和权重,选择最优服务器。
  5. IP哈希(IP Hash):根据请求的IP地址的哈希结果来分配请求,保证同一IP的请求总是发送到同一台服务器。
  6. 通用哈希(Generic Hash):自定义键值的哈希方法进行分配,更灵活。
  7. 随机(Random):随机选择后端服务器。

解释

‌‌‌‌  Nginx 是一个高性能的反向代理服务器、负载均衡器和 HTTP 服务器。它最初由 Igor Sysoev 开发,现在由 Nginx, Inc. 维护并广泛用于处理高并发请求和提供稳定的服务。

Nginx 是什么?

‌‌‌‌  Nginx 是一种轻量级、模块化、高效能的服务器,具有以下主要功能:

  1. 反向代理:接收客户端请求并将其转发给后端服务器。
  2. 负载均衡:分配客户端请求到多台后端服务器,均衡服务器负载。
  3. HTTP 服务器:可以直接提供静态内容,如 HTML、CSS、JavaScript 文件等。
  4. SSL/TLS 终止:处理加密连接,将解密后的流量传递给后端服务器。
  5. 缓存:提供内容缓存,提高响应速度和减少后端服务器负载。
  6. 压缩:对内容进行压缩,减少带宽使用,提高传输效率。

Nginx 是为了解决什么问题?

  1. 高并发处理:Nginx 设计为异步非阻塞架构,能够高效处理大量并发连接。
  2. 性能优化:通过缓存、压缩等手段,提高网页加载速度和用户体验。
  3. 负载均衡:将请求均匀分配到多台服务器,防止单一服务器过载,提高系统可靠性和可用性。
  4. 反向代理和安全性:隐藏后端服务器的真实 IP 地址,提高安全性;同时处理 SSL/TLS 加密,减少后端服务器负载。
  5. 灵活的配置:模块化设计和配置文件的灵活性,适应不同场景和需求。

负载均衡算法是为了解决什么问题的?

负载均衡算法的主要目的是将客户端请求均匀分配到多台后端服务器,以实现以下目标:

  1. 均衡负载:避免某台服务器过载,从而提高系统的整体性能和稳定性。
  2. 提高可用性:如果一台服务器故障,负载均衡器可以将请求重新分配到其他可用服务器,提供高可用性。
  3. 优化资源利用:通过合理分配请求,充分利用所有服务器的资源,提高效率。
  4. 会话粘性:在某些情况下(如用户登录会话),需要保证同一用户的请求总是分配到同一台服务器。

负载均衡算法的具体解决方案

  1. 轮询(Round Robin)

    • 目标:均匀分配请求,简单且适用于性能均衡的服务器。
  2. 加权轮询(Weighted Round Robin)

    • 目标:考虑服务器性能差异,权重高的服务器处理更多请求。
  3. 最少连接数(Least Connections)

    • 目标:将新请求分配给当前负载较轻的服务器,适用于处理时间较长的请求。
  4. 加权最少连接数(Weighted Least Connections)

    • 目标:结合服务器性能和当前负载情况,选择最优服务器。
  5. IP哈希(IP Hash)

    • 目标:保证同一IP地址的请求总是分配到同一台服务器,适用于需要会话粘性的场景。
  6. 通用哈希(Generic Hash)

    • 目标:提供灵活的分配策略,可以根据自定义键值进行哈希分配。
  7. 随机(Random)

    • 目标:简单且快速的负载均衡,适用于不需要会话粘性的场景。

这些负载均衡算法通过不同的策略,解决了如何高效、均衡地分配请求,提高系统的性能、稳定性和可用性的问题。

284. 为什么有权值轮询?

‌‌‌‌  权重轮询 (Weighted Round Robin)是轮询算法的一种改进,主要是为了应对不同服务器的机型、配置、负载能力等各不相同的情况

‌‌‌‌  在负载均衡系统中,如果后端都是不同性能的服务器,但却平等的对待,那么性能强的服务器的能力会被浪费,性能弱的服务器可能会被压垮。为了充分利用每台服务器的性能,使服务器尽可能的高效运行,因此引入了权值的概念。权重高的服务器会接收到更多的请求,权重低的则相对较少。

285. Redis的持久化方式

Redis支持两种主要的持久化方式:

  1. RDB(Redis Database):在指定的时间间隔内,将内存中的数据集快照保存到硬盘中,适用于灾难恢复。
  2. AOF(Append Only File)将所有写操作命令追加到AOF文件中,实现数据的实时持久化,适用于数据不丢失的场景。

286. 网络5层模型以及相关协议

网络的5层模型及相关协议如下:

  1. 物理层 :负责传输原始比特流,相关协议包括:Ethernet,USB,Bluetooth。
  2. 数据链路层 :负责在相邻节点间传输,协议有:Ethernet,PPP,Switching。
  3. 网络层 :处理数据包在网络中的传输,协议有:IP,ICMP,ARP,RARP,Routing Reotocols。
  4. 传输层 :负责提供端到端的通信服务,协议有:TCP,UDP。
  5. 应用层 :为应用程序提供网络服务,协议有:HTTP,FTP,SMTP,DNS.SSH。

287. 应用层还有哪些协议?

  1. TELNET:远程登录服务协议
  2. SNMP:简单网络管理协议
  3. IMAP:互联网消息存取协议
  4. POP3:邮局协议版本3
  5. SIP:会话发起协议
  6. XMPP:可扩展消息和存在协议(用于即时通讯)
  7. LDAP:轻量级目录访问协议
  8. RTSP:实时流协议
  9. DHCP:动态主机配置协议
  10. RTP:实时传输协议
  11. HTTPS:超文本传输协议
  12. MQTT:消息队列遥测传输
  13. WebDAV:基于HTTP的网页分布式作者和版本控制协议

288. 介绍HTTP的状态码

主要有以下几类:

  1. 1XX(消息响应):接受的请求正在处理,如100(Continue)
  2. 2XX(成功):请求已成功处理,如200(OK),201(Created),204(No Content)
  3. 3XX(重定向):需要i进行附加操作以完成请求,如301(Moved Permanently),302(Found),304(Not Found)。
  4. 4XX(客户端错误):请求包含语法错误或无法完成请求,如400(Bad Request),403(Forbidden),404(Not Found)
  5. 5XX(服务器错误):服务器在处理请求的过程中发生错误,如500(Internal Server Error),502(Bad Gateway),503(Service Unavailable)

289. TCP和UDP的区别

  1. 从格式上看:
    1. TCP的首部字节通常有20-60字节,UDP首部字节比较小,只有8个字节。
    2. TCO是基于字节流的,其可以被拆包和粘包,而UDP是基于数据段报文,其一个消息就是一个报文。
  2. 从功能上看:
    1. TCP是面向连接的,是一对一通信;而UDP不是面向连接的,可以实现一对一、一对多和多对多的通信。
    2. TCP通过三次握手、四次挥手、序列号确认应答号和重传机制,滑动窗口等保证了传输的可靠性,而UDP是不可靠的。
  3. 从效率上看:
    1. TCP所需资源多,传输效率慢;UDP所需资源少,传输效率高。
  4. 应用场景:
    1. TCP适合对数据准确性要求比较高的场合,如数据库连接、电子邮件等。
    2. UDP适合对速度要求比较高,可以容忍一定的数据丢失的场合,如在线视频、qq聊天等。

290. UDP适合的场景

  1. 实时应用:如视频会议,实时游戏,因为他们需要快速且连续的数据流。
  2. 简单查询响应通信:如DNS查找。
  3. 广播或多播通信:UDP支持发送单个数据包给多个接收者(例如,IPTV)。
  4. 限制环境下的通信:如VoIP电话,其中网络带宽可能受限。
  5. 无需复杂交互的服务:如SNMP(简单网络管理协议)等
  6. 忽略数据丢失可接受的情况:若偶尔丢失数据对应用影响不大时使用UDP。

291. select,poll,epoll的区别

  1. select:
    1. 文件描述符数量受限于FD_SETSIZE
    2. 使用位图跟踪每个文件描述符,每次调用都需要复制整个位图
    3. 性能 随监听的文件描述符增长而降低,时间复杂度O(n)
  2. poll:
    1. 不受文件描述符数量的限制,因为他是基于链表
    2. 使用指针数组跟踪文件描述符和事件,每次调用还是需要复制整个数组
    3. 性能同样随监听文件描述符的数量增长而降低,时间复杂度O(n)
  3. epoll:
    1. 可以处理大量的文件描述符,没有实际数量的限制
    2. 使用事件通知机制,当状态改变时,只返回有事件发生的文件描述符
    3. 不需要每次调用都复制文件描述符集合,较少开销
    4. 性能不会随着文件描述符的数量增长而显著下降,事件复杂度接近O(1)

292. select、poll、epoll的适用场景

  1. select:适用于文件描述符数量较少 ,且跨平台兼容性要求较高的场景。
  2. poll:适用于文件描述符数量适中,但要求不受固定限制,同时更大数量文件描述符的场景。
  3. epoll:适用于需要处理大量并发文件描述符,且对系统性能要求较高的服务器端应用,特别是在Linux环境下。

293. select默认最大是多少?

在大多数系统上,select函数默认的最大文件描述符数量限制是由宏FD_SETSIZE定义的 ,它通常设置为1024.这意味着默认情况下,select可以监控的文件描述符的范围是0到1023。需要注意的是,某些系统允许修改这个值,但这通常需要重新编译系统的C库。

294. 进程和线程、协程的区别

‌‌‌‌

  • 进程:进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位。每个进程都有自己的独立内存空间,不同进程通过进程间通信来通信。由于进程比较重量,占据独立的内存,所以上下文进程间的切换开销(栈、寄存器、虚拟内存、文件句柄等)比较大,但相对比较稳定安全。

  • 线程:线程是进程的一个实体,是 CPU 调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源。线程间通信主要通过共享内存,上下文切换很快,资源开销较少,但相比进程不够稳定容易丢失数据。

  • 协程 :协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。

区别

进程与线程比较

  1. 地址空间:线程是进程内的一个执行单元,进程内至少有一个线程,它们共享进程的地址空间,而进程有自己独立的地址空间

  2. 资源拥有:进程是资源分配和拥有的单位,同一个进程内的线程共享进程的资源

  3. 线程是处理器调度的基本单位,但进程不是

  4. 二者均可并发执行

  5. 每个独立的线程有一个程序运行的入口、顺序执行序列和程序的出口,但是线程不能够独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制

协程与线程进行比较

  1. 一个线程可以多个协程,一个进程也可以单独拥有多个协程

  2. 线程进程都是同步机制,而协程则是异步

  3. 协程能保留上一次调用时的状态,每次过程重入时,就相当于进入上一次调用的状态

295. IO密集型适合多进程还是多线程?

‌‌‌‌  IO密集型任务通常更适合使用多线程 ,因为线程间共享内存和资源上下文切换开销小利于提高IO等待期间的CPU利用率

解释

‌‌‌‌  在处理 I/O 密集型任务时,多线程通常比多进程更适合。以下是一些原因和解释:

多线程优点

  1. 轻量级:线程比进程更轻量级,创建和销毁的开销更小。
  2. 共享内存:线程可以共享相同的内存空间,减少了进程间通信的开销。
  3. 上下文切换开销小:线程之间的上下文切换开销比进程之间小。

I/O 密集型任务特点

‌‌‌‌  I/O 密集型任务通常涉及大量的等待(如网络请求、磁盘读写等),这意味着大部分时间程序都在等待 I/O 操作完成,而不是使用 CPU。多线程模型能够更好地利用这种等待时间,允许其他线程在一个线程等待 I/O 操作完成时继续执行。

什么时候选择多进程

尽管多线程在 I/O 密集型任务中更常见,但在以下情况中,使用多进程可能会更有优势:

  1. 避免 GIL 限制:在 Python 中,由于全局解释器锁(GIL)的存在,多线程在 CPU 密集型任务中可能无法有效利用多核处理器。多进程可以绕过这个限制。
  2. 独立性和安全性:如果任务彼此之间需要高度隔离或存在内存泄漏风险,多进程可以提供更好的独立性和安全性。

结论

‌‌‌‌  对于 I/O 密集型任务,多线程通常是更好的选择,因为它们能够更高效地利用资源,减少上下文切换的开销。然而,具体选择还需要根据应用程序的具体需求和环境来决定。

296. http无状态,怎么保存状态?

‌‌‌‌  HTTP无状态可以通过使用Cookies、Sesion机制、Token以及HTTP参数等方式来保存状态。

297. git工具的rebase merge的区别

‌‌‌‌  git rebase主要用于将一个分支的修改重新应用到另一个分支上 ,他能创建更线性的项目历史;

‌‌‌‌  git merge则是将两个分支的历史合并成一条,可能会产生新的合并提交,保留了原始分支的历史。

‌‌‌‌  简而言之,rebase重写历史以线性顺序展示,merge保留了分支的并行历史。

解释

git rebase

‌‌‌‌  git rebase 的主要作用是将一个分支上的修改应用到另一个分支上。它通过重写提交历史,创建一个线性的项目历史。

举例:

‌‌‌‌  假设有两个分支:mainfeaturefeature 分支是从 main 分支上创建的,现在在 main 分支上有一些新的提交,feature 分支也有一些自己的提交。

原始历史:

c 复制代码
A---B---C (main)
     \
      D---E (feature)

‌‌‌‌  使用 git rebase mainfeature 分支上,会把 feature 分支的提交(D 和 E)重新应用到 main 分支的最新提交(C)之后:

c 复制代码
A---B---C---D'---E' (feature)

‌‌‌‌  D'E' 是重新应用的提交,实际内容和 DE 一样,但历史记录被重新写过了。

git merge

‌‌‌‌  git merge 的作用是将两个分支的历史合并成一条。这通常会创建一个新的合并提交,保留了所有原始的提交历史。

举例:

‌‌‌‌  使用相同的原始历史,在 main 分支上使用 git merge feature 会创建一个新的合并提交:

c 复制代码
A---B---C---F (main)
     \     /
      D---E (feature)

‌‌‌‌  F 是合并提交,它包含了 main 分支和 feature 分支的所有历史记录。

对比与选择

  • rebase:重写历史,使项目历史看起来更线性。它可以使提交历史更干净、更易读,但会改变现有提交的哈希值,因此需要小心使用,尤其是在已经共享的分支上。
  • merge:保留所有分支的原始历史,不重写历史记录。它会生成一个新的合并提交,表示两个分支的合并点。虽然历史记录会更复杂,但可以清楚地看到每个分支的变动。

结论

  • 使用 rebase 可以让提交历史更简洁、更线性,但需要注意可能带来的冲突和历史重写的问题。
  • 使用 merge 可以保留完整的历史记录,适合在合作开发时使用,避免了潜在的历史重写风险。

298. 如果有一个错误的提交,如何把他回退到正确的,包括本地远端,都回退

‌‌‌‌  要回退一个错误的提交 ,可以在本地使用git revert <commit_hash> 创建一个新提交来撤销错误提交的改变,然后用git push推送到远端。

‌‌‌‌  如果需要彻底从历史中移除 ,可以使用 git reset--hard <commit_hash> 要回退到正确的提交状态,然后强制推送到远程 git push--force。

解释

‌‌‌‌  这段话解释了两种在 Git 中回退错误提交的方法,分别是通过 git revertgit reset 命令。让我们详细看看每种方法的使用场景和具体操作。

1. 使用 git revert

‌‌‌‌  git revert 用于撤销一个特定的提交。它不会删除该提交,而是创建一个新的提交,应用反向更改。

举例:

‌‌‌‌  假设提交历史如下:

c 复制代码
A---B---C---D (main)

‌‌‌‌  其中 D 是错误的提交。

‌‌‌‌  使用 git revert D 命令会创建一个新的提交 E,它的内容是 D 的反向更改:

c 复制代码
A---B---C---D---E (main)

‌‌‌‌  然后,将这个新提交推送到远端:

c 复制代码
git push origin main

2. 使用 git reset --hard

‌‌‌‌  git reset --hard 用于回退到一个特定的提交,并且会丢弃之后的所有提交。这个操作会重写历史,因此需要小心使用,特别是当这些提交已经推送到远端时。

举例:

‌‌‌‌  假设提交历史如下:

c 复制代码
A---B---C---D (main)

‌‌‌‌  其中 D 是错误的提交。

‌‌‌‌  使用 git reset --hard C 会回退到提交 C,并丢弃 D

c 复制代码
A---B---C (main)

然后,将这种变更强制推送到远端:

c 复制代码
git push origin main --force

对比与选择

  • git revert

    • 优点:不会删除历史记录,保留所有提交。适合用于已经推送到共享仓库的提交。
    • 缺点:会增加一个新的提交(反向提交),提交历史变长。
  • git reset --hard

    • 优点:彻底移除错误的提交,历史记录干净。
    • 缺点 :重写历史,可能导致协作开发中的问题,需要强制推送(--force),可能覆盖其他人的变更。

具体操作步骤

使用 git revert
  1. 找到错误提交的哈希值(例如 D)。
  2. 创建反向提交:
c 复制代码
git revert <commit_hash>
  1. 推送到远端:
c 复制代码
git push origin main
使用 git reset --hard
  1. 找到正确提交的哈希值(例如 C)。
  2. 回退到该提交:
c 复制代码
git reset --hard <commit_hash>
  1. 强制推送到远端:
c 复制代码
git push origin main --force

注意事项

  • git revert:适用于已经推送并分享的分支,避免影响他人工作。
  • git reset --hard :适用于单独工作或者确保所有协作者都同意重写历史的情况下。使用 --force 推送时要非常小心,确保不会覆盖其他人的工作。

总结来说,git revertgit reset --hard 是处理错误提交的两种有效方法,选择哪一种取决于具体情况和协作模式。

相关推荐
南东山人2 小时前
一文说清:C和C++混合编程
c语言·c++
Ysjt | 深5 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__5 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word5 小时前
c++基础语法
开发语言·c++·算法
一只小小汤圆6 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz6 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE6 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
ö Constancy7 小时前
c++ 笔记
开发语言·c++
fengbizhe7 小时前
笔试-笔记2
c++·笔记