「训」 笔记(13):Q & A

本文最后更新于:40 分钟前

思考和总结的一些问题。

Go 语言

Q:make 和 new 方法有什么区别

  • make 用于初始化内置的数据结构,如数组、切片和 channel 等。
  • new 用于分配并创建一个指向对应类型的指针。

Q:Go 当中同步锁有什么特点,作用是什么

Go 语言中的同步锁是一种机制,用于控制对共享资源的访问,以确保在同一时间只有一个协程(goroutine)可以访问该资源,从而避免数据竞争和不一致的结果。

Q:Map 是线程安全(并发安全)的吗

不是,多个 goroutine 在并发读写同一个 Map 可能会导致数据竞争和不确定的结果。

  • 读写锁:使用 sync.RWMutex 来保护对 Map 的读写操作。
  • 分片锁:定义一个长度为 N 的 map 数组和一个长度为 N 的锁数组。使用 hash 函数来计算 key 应该存储在哪个 map 中。然后再对这个 map 进行读写操作。
  • sync.Map:Go 语言第三方库实现了并发安全的 Map,可以在多个 goroutine 中并发读写,无需额外的锁保护。

Q:使用 channel 时需要注意什么

  • 当尝试从一个 nil 的通道接收数据时,会导致程序的阻塞。
  • 如果向 nil 的通道发送数据,会导致程序运行时恐慌(panic)。
  • 当从一个已关闭的通道接收数据时,会获得通道中的零值,如果 buffer 内还有元素未读,则会读到通道内的值。
  • 如果向已经关闭的通道发送数据,会导致程序运行时恐慌(panic)。

Q:Go 语言当中 channel 缓冲有什么特点

无缓冲的 channel 是同步的,而有缓冲的 channel 是非同步的。

Q:goroutine 在什么情况下会被挂起

channel 堵塞、垃圾回收、sleep 休眠、锁等待、抢占、IO 阻塞。

Q:goroutine 的通信方式

可以通过共享内存(变量)加锁的方式来进行通信,但是维护成本较⾼。官方推荐通过 channel 进行通信:

  • 作为队列存储数据
  • 阻塞和唤醒 goroutine

select 搭配 channel 使用,其机制是监听多个 channel,每⼀个 case 都是⼀个事件,⼀旦某个事件就绪(chan 没有堵塞),就会从这些就绪事件中随机选择⼀个去执行,default 用于所有 chan 都堵塞的情况执行。

Q:Printf(),Sprintf(),和 Fprintf() 有什么不同

  • Printf() 函数用于将格式化的字符串输出到标准输出(控制台)。
  • Sprintf() 函数将格式化的字符串保存到一个字符串变量中,而不是输出到标准输出。
  • Fprintf() 函数用于将格式化的字符串输出到指定的 io.Writer 接口(文件)。

Q:数组和切片的区别

  • 固定长度 vs 可变长度:数组具有固定长度,定义时需要指定其长度,而且长度在数组的整个生命周期中保持不变。切片则是对数组的一个动态窗口或视图,长度可以动态改变。

  • 值类型 vs 引用类型:数组是值类型,直接存储数据本身。对数组进行赋值或传递给函数时,会进行值的拷贝。切片是引用类型,实际上是对底层数组的引用。对切片进行赋值或传递给函数时,会共享底层数组,而不进行拷贝。

    • 值传递只会把参数的值复制一份放进对应的函数,两个变量的地址不同,不可相互修改。
    • 地址传递(引用传递)会将变量本身传入对应的函数,在函数中可以对该变量进行值内容的修改。
  • 内存管理:数组在内存中是连续存储的,大小固定,不会随着元素的增加而改变。切片通过指针引用底层数组,可以动态增长和缩减,当切片的长度超过底层数组的容量时,会重新分配更大的底层数组。

Q:Golang 引用类型有哪些

引用类型是指那些存储了底层数据结构的地址的数据类型。这些类型在赋值或传递参数时不会进行数据复制,而是共享底层数据,因此被称为引用类型。

包含以下几种:切片、映射(map)、通道。

Q:slice 深拷贝和浅拷贝

  • 浅拷贝只复制了 slice 结构的部分,而没有复制底层数组。当对原始 slice 或拷贝后的 slice 进行修改时,会影响到所有相关的 slice。这是因为它们共享同一个底层数组。

  • 深拷贝会创建一个新的 slice 和一个新的底层数组,将原始数据的所有元素复制到新的底层数组中。原始 slice 和拷贝后的 slice 将不再共享底层数组,它们是完全独立的。

Q:拷贝大切片一定比小切片代价大吗

并不是,所有切片的大小相同:三个字段(一个 uintptr,两个int)。切片中的第一个字是指向切片底层数组的指针,这是切片的存储空间,第二个字段是切片的长度,第三个字段是容量。将一个 slice 变量分配给另一个变量只会复制三个机器字。所以拷贝大切片跟小切片的代价应该是一样的。

Q:defer 的作用和特点是什么

defer 是一个用于延迟执行函数调用的关键字。它可以将一个函数或方法的执行推迟到当前函数返回之前或所在的代码块执行结束之前。

defer 常用于资源的释放和清理操作,如关闭文件、释放锁、释放数据库连接等。通过将资源释放操作延迟执行,可以确保在函数退出之前进行必要的清理,无论函数是正常返回还是异常退出。

当有多个 defer 语句出现时,它们的执行顺序类似于栈(先进后出)的方式。也就是说,最后一个推迟的函数调用将最先执行,而第一个推迟的函数调用将最后执行。

Q:为什么 defer 要按照定义的顺序逆序执行

后⾯定义的函数可能会依赖前⾯的资源,所以要先执行。如果前⾯先执行,释放掉这个依赖,那后⾯的函数就找不到它的依赖了。

Q:扩容前后的切片是否相同

切片的容量足够则相同。

当切片的容量不足以容纳新增的元素时,Go 语言会自动扩容切片。扩容的过程中,Go 语言会重新分配一个更大的底层数组,并将原始切片中的元素复制到新的底层数组中。由于底层数组的改变,扩容前后的切片实际引用了不同的底层数组,因此它们是不相同的。

Q:GC 的触发机制

  1. 内存分配量达到阈值:每次内存分配前都会检查当前内存分配量是否达到阈值,如果达到则触发GC,阈值 = 上次 GC 时的内存分配量 * 内存增长率
  2. 定时触发 GC:默认情况下两分钟触发⼀次 GC,可由 runtime 中的参数声明
  3. ⼿动触发 GC:可以在代码中通过使用 runtime.GC() 来⼿动触发

Q:什么是 M:N 模型

Go runtime 会负责 goroutine 的生老病死,从创建到销毁,都一手包办。Runtime 会在程序启动的时候,创建 M 个线程(CPU 执行调度的单位),之后创建的 N 个 goroutine 都会依附在这 M 个线程上执行,这就是 M:N 模型。在同一时刻,一个线程上只能跑一个 goroutine。当 goroutine 发生阻塞时,runtime 会把当前 goroutine 调度走,让其他 goroutine 来执行。

计算机网络

Q:HTTP 协议

HTTP 协议工作于客户端-服务端架构之上。浏览器作为 HTTP 客户端通过 URL,向 HTTP 服务端,即 web 服务器发送所有请求。web 服务器根据接收到的请求,向客户端发送响应信息。

是一个基于请求与响应模式的、无状态的、应用层的协议。

是一个基于 TCP/IP 通信协议来传递数据(HTML 文件,图片文件,查询结果等)的协议。

Q:HTTP 协议特点

  1. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。
  2. 灵活:HTTP 允许传输任意类型的数据对象。
  3. 无状态:协议对于事务处理没有记忆能力,如果后续处理需要前面的信息,则它必须重传。
  4. 无连接的:限制每次连接只处理一个请求,服务器处理完客户的请求,并收到客户的应答后,即断开连接。
  5. 支持 B/S 及 C/S 模式。
  6. 默认端口 80。
  7. 基于 TCP/IP 通信协议。

Q:Cookie 和 Session 的区别

  • Cookie 是浏览器支持的一种本地存储机制。一般由服务端设置生成,在响应请求时被自动存储在浏览器中,用于辨别用户身份。

  • 创建 Session 的时候, 服务端会在 http 协议中告诉客户端,需要在 Cookie 里记录一个 sessionid,以后每次请求把这个 id 发送到服务器,就知道用户身份了。

  • Token 是一种基于令牌的认证机制,常见于无状态(Stateless)的 Web 应用中(移动端也推荐用 Token)。在使用 Token 认证时,当用户登录成功后,服务器会生成一个加密的 Token,并将其返回给客户端。客户端在后续的请求中需要携带这个 Token 作为身份凭证,通常通过请求头(如 Authorization 头)或查询参数来传递。

    区别

  • 存储位置:Cookie 是存储在客户端(浏览器)中的小型文本文件,而 Session 是存储在服务器上的数据结构。

  • 数据存储:Cookie 通常包含少量的用户数据,以键值对的形式存储在客户端。Session 可以存储更多的用户数据,可以包含复杂的对象和数据结构。

  • 安全性:Cookie 存储在客户端,可以被客户端修改或者删除。Session 数据存储在服务器端,相对来说更安全,但是会占用服务器资源。

  • 生命周期:Cookie 可以设置过期时间,可以在客户端存储一段时间,并在之后仍然有效。Session 通常在用户关闭浏览器或一段时间不活动后过期,或者通过在服务器上设置超时时间来控制。

  • 存储方式:Cookie 通过在 HTTP 响应头中的 Set-Cookie 字段发送给客户端,客户端将其存储在本地。之后,每次请求中都会通过 Cookie 头字段将 Cookie 发送回服务器。Session 通过在服务器上创建唯一的标识符(通常是会话 ID)来跟踪用户会话,并将该标识符存储在 Cookie 中或通过 URL 重写传递给客户端。

Q:什么是跨站脚本攻击 XSS 和 HttpOnly

XSS 是跨站脚本攻击(Cross Site Scripting)。恶意攻击者往 Web 页面里插入 Script 代码,当用户浏览时,Script 代码会被执行从而达到攻击用户的目的。如盗取用户 Cookie、破坏页面结构、重定向到其它网站(流量劫持)等。

HttpOnly 是一个标记,用于设置 HTTP Cookie 的属性。当设置了 HttpOnly 属性后,浏览器将限制该 Cookie 只能通过 HTTP 或 HTTPS 协议进行传输,禁止通过脚本(如 JavaScript)访问该 Cookie。

HttpOnly 属性的作用是增加 web 应用程序的安全性,是防止跨站脚本攻击(XSS)的一种方式。

Q:GET 和 POST 的区别

  • GET 重点在从服务器上获取资源,一般用于获取/查询资源信息。是无副作用的,是幂等的,且可缓存。
  • POST 重点在向服务器发送数据,一般用于更新资源信息。有副作用,非幂等,不可缓存。
  • POST 比 GET 安全性更高。Get 通过 URL 来传输数据,因为 URL 是可见的,可能会泄露私密信息,如密码等。POST 通过 URL 和请求体(requrest body)传输数据,将字段与对应值封存在请求体中发送给服务器。在请求体中的数据,我们是无法直接观测到的。

Q:浏览器输入一个 URL 之后,网络各层发生了什么

  1. 浏览器输入 URL,若协议为 HTTP:
  2. 应用层 DNS 解析,返回对应的 ip 地址(DNS 协议,DNS 服务器是基于 UDP 的)
  3. 应用层客户端发送 HTTP 请求(HTTP 协议)
  4. 传输层传输报文建立 TCP 连接三次握手(TCP 协议)
  5. 网络层 IP 查询 MAC 地址(IP 协议,ARP 协议)
    1. 源 IP 地址和⽬标 IP 地址在传输过程中是不会变的,而源 MAC 地址和⽬标 MAC ⼀直在变化
    2. MAC 只负责某一个区间之间的通信传输
  6. 服务器发送 HTTP 响应报文
  7. 关闭连接,TCP 四次挥手
  8. 客户端解析 HTTP 响应报文,浏览器开始显示 HTML

Q:HTTP 和 HTTPS 的区别

  • HTTP 协议是以明文的方式在网络中传输数据,而 HTTPS 协议传输的数据则是使用密钥进行加密(经过 SSL/TLS 加密)的,所以 HTTPS 具有更高的安全性。
  • HTTPS 部署成本高,HTTPS 协议需要使用证书来验证自身的安全性,所以需要购买 CA 证书。
  • HTTPS 握手阶段延时较高:HTTPS 在 TCP 三次握手阶段之后,还需要进行 SSL/TLS 的握手。
  • 由于采用 HTTPS 协议需要进行加解密的计算,占用 CPU 资源较多。

Q:长连接与短连接的区别

长连接,也叫持久连接,在 TCP 层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息交互,直至连接的任意一方(客户端 OR 服务端)主动断开连接,此过程称为一次完整的长连接。在 HTTP 请求和响应中,通过设置 Connection 头字段来指示是否使用长连接 Connection: keep-alive

早期 HTTP/1.0 性能上的一个很大的问题,那就是每发起一个请求,都要新建一次 TCP 连接(三次握手),而且是串行请求,增加了通信开销。HTTP/1.1 最重要的新特性就是引入了长连接。

短连接,客户端收到服务端的响应后,立刻发送 FIN 消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互之后立刻断开连接的情况都称为短连接。

Q:Socket 套接字

Socket 套接字是在应用层和传输层之间的一个抽象层,用于实现应用程序之间的网络通信。它提供了一种编程接口,允许应用程序通过网络进行数据传输。

在网络通信中,Socket 套接字通过使用网络协议栈中的传输层协议(如 TCP 或 UDP)来实现数据的可靠传输或不可靠传输。只需要指定主机的 IP 地址,和一个端口号。

Socket 套接字是在操作系统内核中实现的,它负责处理网络通信的细节,如建立连接、数据的发送和接收、错误处理等。应用程序可以使用 Socket API(如套接字函数)来创建、配置和控制 Socket 套接字,从而实现网络通信。

Q:IPv4 和 IPv6

  • IPv4 是目前广泛使用的 IP 协议版本。它使用 32 位地址,通常表示为点分十进制格式(例如,192.168.0.1)。

  • IPv6 是为了解决 IPv4 地址空间不足的问题而设计的下一代 IP 协议。IPv6 使用 128 位地址,通常表示为冒号分隔的十六进制格式(例如,2001:0db8:85a3:0000:0000:8a2e:0370:7334)。

Q:负载均衡

负载均衡是一种在计算机网络中分配工作负载(请求或流量)到多个服务器的技术。它旨在提高系统的性能、可扩展性和可靠性,通过均衡分配负载来避免单个服务器过载或故障引起的性能下降。

实现负载均衡的一种常见方法是通过负载均衡器(Load Balancer)。负载均衡器是位于客户端和服务器之间的网络设备或软件,它接收来自客户端的请求,并将这些请求分发到一组后端服务器上,以实现负载的均衡。

  • Nginx 是七层(即应用层)负载均衡器 ,这意味着如果它要转发流量首先得和 client 建立一个 TCP 连接,并且转发的时候也要与转发到的上游 server 建立一个 TCP 连接,而我们知道建立 TCP 连接其实是需要耗费内存(TCP Socket,接收/发送缓存区等需要占用内存)的。所以 Nginx 的负载能力受限于机器 I/O,CPU 内存等一系列配置,一旦连接很多(比如达到百万)的话,Nginx 抗负载能力就会急剧下降。

  • LVS 是四层(传输层)负载均衡器,LVS 只是单纯地转发包,不需要和上下游建立连接,相比于 Nginx 它的抗负载能力强、性能高,对内存和 cpu 资源消耗比较低。负载均衡设备在接收到第一个来自客户端的 SYN 请求时,即通过负载均衡算法选择一个最佳的服务器,并对报文中目标 IP 地址进行修改(改为后端服务器 IP),直接转发给该服务器。TCP 的连接建立,即三次握手是客户端和服务器直接建立的,负载均衡设备只是起到一个类似路由器的转发动作。

Q:负载均衡算法

  • 轮询(Round Robin):每个请求按照轮询的顺序依次分发给服务器。

  • 权重轮询(Weighted Round Robin):给每个服务器分配一个权重值,根据权重值的比例来分配请求。

  • 随机(Random):随机选择一台服务器来处理每个请求。

  • 最少连接(Least Connections):选择当前连接数最少的服务器来处理请求。

  • IP 哈希(IP Hash):根据请求的源 IP 地址来进行哈希计算,并将请求分配给对应的服务器。同一 IP 地址的请求总是被分配到相同的服务器,适用于保持会话的应用。

  • 哈希(Hash):根据请求的特定信息(如 URL、Cookie 等)进行哈希计算,并将请求分配给对应的服务器。

  • 最少响应时间(Least Response Time):根据服务器的响应时间来进行分配。将请求发送到响应时间最短的服务器,以提供更快的响应。

  • 最少资源(Least Resources):根据服务器的资源使用情况(如 CPU、内存等)进行分配。将请求发送到资源使用最少的服务器,以提供更好的性能。

Q:什么是 SYN 攻击

在 TCP 协议中,建立连接的过程涉及三次握手:客户端发送一个 SYN 包(同步请求)给服务器,服务器回复一个 SYN-ACK 包(同步应答),最后客户端发送一个 ACK 包(确认)。这个过程用于确保客户端和服务器之间的连接正常建立。

SYN 攻击利用了这个连接建立的过程中的漏洞。攻击者发送大量的伪造的 SYN 包给目标服务器,但是攻击者并不回复服务器的 SYN-ACK 包。服务器在等待超时后会关闭这个半连接状态,释放资源。攻击者不断发送大量的伪造 SYN 包,占用服务器的连接资源,导致服务器无法处理正常的连接请求。

Q:常见的网络劫持

  • DNS 劫持:攻击者篡改 DNS 的解析过程,使得用户在输入网址时被重定向到恶意网站,从而窃取用户的信息或进行其他恶意活动。

  • ARP 劫持:攻击者在本地网络中伪造或篡改 ARP 响应,将目标主机的 IP 地址映射到攻击者的 MAC 地址,从而导致网络通信被重定向到攻击者控制的主机。

  • BGP 劫持:攻击者通过篡改 BGP 路由表信息,使得网络流量被重定向到攻击者控制的路径,从而可以窃取、拦截或篡改网络通信。

  • HTTP 劫持:攻击者劫持 HTTP 通信过程,例如通过中间人攻击(Man-in-the-Middle Attack)获取或篡改 HTTP 请求和响应,从而窃取用户的敏感信息或操纵通信内容。

  • WiFi 劫持:攻击者在公共 WiFi 网络中进行劫持,例如通过伪造热点或中间人攻击来窃取用户的网络流量和敏感信息。

操作系统

Q:操作系统的功能

操作系统位于硬件资源之上,管理硬件资源;应用程序之下,为应用程序提供服务,同时管理应用程序。

  • 资源分配,资源回收
  • 为应用程序提供服务
  • 管理应用程序,控制进程的生命周期
  • 操作系统内核的功能:进程调度、内存管理、硬件通信、系统调用

Q:用户程序与操作系统的关系

相互调用。

计算机启动后启动的第⼀个软件就是操作系统,随后启动的所有进程都运行在操作系统之上,使用操作系统提供的服务,同时被操作系统监控,进程结束后也由操作系统回收。

编译的代码可执行文件只是储存在硬盘的静态文件,运行时被加载到内存,CPU 执行内存中指令,这个运行的程序被称为进程。用户进程调用操作系统提供的服务,实现自己的功能。

Q:进程、线程和协程

  • 进程(Process):进程是操作系统分配资源的最小单位,是一个正在执行中的程序的实例。每个进程都有独立的内存空间和执行环境,它们之间相互隔离。进程之间通常通过进程间通信(IPC)机制进行通信,如管道、共享内存、消息队列等。多进程可以同时执行不同的任务,实现并发处理。

  • 线程(Thread):线程是进程内的执行单元,是 CPU 调度的最小单位。一个进程可以包含多个线程,共享相同的内存空间和上下文环境。不同线程之间可以并发执行,通过共享内存来进行数据通信,但也需要注意同步和互斥问题。每个线程都有自己独立的堆栈空间,线程之间的切换开销相对较小,适合执行密集的计算和并发任务。

  • 协程(Coroutine):协程是一种用户态的轻量级线程,一个线程中可以包含多个协程,在同一时刻只有一个协程运行,通过协程的切换来实现并发任务的处理。协程在执行过程中可以主动挂起和恢复,控制权的切换由程序自身控制,通常不涉及内核态的切换,因此协程的创建和切换开销较小。协程通常使用更少的内存,因为多个协程可以共享同一个线程的堆栈空间。

系统开销:由于在创建或撤消进程时,系统都要为之分配或回收资源,如内存空间、I/O 设备等。在进行进程切换时,涉及到整个 CPU 环境的保存以及新 CPU 环境的设置。而线程切换只须保存和设置少量寄存器的内容,所以进程切换的开销也远大于线程切换的开销。

通信开销:由于同一进程中的多个线程具有相同的地址空间,致使它们之间的同步和通信的实现变得比较容易,可以直接读写进程数据段(如全局变量)来进行通信,也需要进行同步和互斥来保证数据的一致性。

进程间相互独立,不会相互影响;而一个线程挂掉可能会导致整个进程挂掉。

Q:进程间通信的方式

  • 管道(Pipe):管道是一种半双工的通信方式,分为匿名管道和命名管道。匿名管道用于父子进程之间的通信,而命名管道则可以用于无关进程之间的通信。

  • 命名管道(Named Pipe):命名管道是一种具有名称的管道,可以用于不相关的进程之间进行通信。命名管道可以在文件系统中创建,并且可以由多个进程共享。

  • 共享内存(Shared Memory):共享内存是一种进程间共享内存空间的方式。多个进程可以访问同一块内存区域,从而实现高效的数据交换。需要进行同步和互斥来保证数据的一致性。

  • 消息队列(Message Queue):消息队列是一种进程间传递消息的方式。进程可以将消息发送到消息队列,其他进程可以从队列中接收消息。消息队列提供了一种异步的通信方式。

  • 信号量(Semaphore):信号量是一种用于进程同步和互斥的机制。多个进程可以通过信号量来进行临界区的互斥访问,以及进程之间的同步操作。

  • 套接字(Socket):套接字是一种网络通信的方式,用于在不同的主机之间进行进程间通信。套接字提供了一种通用的、可移植的通信机制,可以在不同计算机之间进行通信。

Q:进程间同步的方式

  • 临界区(Critical Section):通过对多线程的串行化来访问公共资源或一段代码,速度快,适合控制数据访问。每个进程中访问临界资源的那段程序称为临界区,一次仅允许一个进程使用的资源称为临界资源。

  • 互斥量(Mutex):在某个进程需要访问共享资源时,它必须先获得互斥锁,其他进程在同一时间内无法获得该锁,从而保证了共享资源在同一时刻只被一个进程访问。在完成操作后,进程释放互斥锁,其他进程才能继续竞争获取该锁。

  • 信号量(Semaphore):信号量是一种计数器,用于管理对共享资源的访问。它允许多个进程同时访问共享资源,但通过控制信号量的计数值,限制能够同时访问共享资源的进程数量。

  • 事件(Event):事件用于实现进程间的通信和同步。进程可以等待一个事件的发生,在事件发生时,其他进程可以通过触发事件来通知等待的进程。

Q:线程/进程的五种状态

  1. 新建:当创建线程对象时,线程处于新建状态。此时线程还没有开始执行,尚未分配系统资源。

  2. 就绪:线程已经分配了系统资源,但尚未获得 CPU 的执行时间片。

  3. 运行:获得 CPU 的执行时间片后,进入运行状态,开始执行线程的任务代码。

  4. 阻塞:线程可能由于某些原因而暂时停止执行,进入阻塞状态。例如等待某个资源。

  5. 终止:线程的执行任务结束后,或者由于异常等原因导致线程终止,线程进入终止状态。终止状态的线程不再执行,且无法转换到其他状态。

Q:中断的处理过程

  • 保护现场:将当前执行程序的相关数据保存在寄存器中,然后入栈。
  • 开中断:以便执行中断时能响应较高级别的中断请求。
  • 中断处理
  • 关中断:保证恢复现场时不被新中断打扰
  • 恢复现场:从堆栈中按序取出程序数据,恢复中断前的执行状态。

Q:进程调度策略

  • 先来先服务
  • 短作业优先
  • 最短剩余时间优先
  • 优先级调度
  • 时间片轮转
  • 高响应比优先
  • 多级反馈队列:每个队列的时间片不同,优先级也不同

Q:消息队列的作用

  • 异步通信:消息队列允许发送者发送消息后立即返回,而无需等待接收者的响应。这样可以实现异步通信,提高系统的响应性能和并发性。

  • 解耦应用组件:通过消息队列,不同的应用组件可以解耦并独立演化。发送者和接收者之间不需要直接的依赖关系,它们只需要通过共享的消息队列来进行通信。这样,应用组件可以独立开发、部署和升级,而不会对其他组件产生影响。

  • 缓冲和削峰填谷:消息队列可以作为缓冲区,当消息发送的速度超过接收的速度时,消息可以暂时存储在队列中,避免消息丢失或系统过载。同时,在高负载时,消息队列可以平滑处理峰值流量,保护系统免受过高的请求压力。

  • 可靠性和持久化:许多消息队列提供持久化机制,确保即使在消息传递过程中出现故障或重启,消息也不会丢失。消息队列还可以提供消息的投递确认和重试机制,确保消息的可靠传递。

  • 扩展性和可伸缩性:消息队列可以用于构建分布式系统,并支持水平扩展。通过增加消息队列的实例或增加消费者的数量,可以提高系统的处理能力和可伸缩性。

Q:什么是交换空间

交换空间通常是在硬盘上预留出来的一块空间,操作系统在需要时将内存中暂时不用的数据写入到交换空间,这样可以释放物理内存用于其他需要更紧急的任务。当需要读取这些数据时,操作系统再将它们从交换空间读取回物理内存中。

Q:分页和分段

分页和分段是计算机内存管理中的两种重要技术,解决了以前操作系统连续分配内存管理方案带来的碎片问题。

  • 分段是将进程的逻辑地址空间划分成多个逻辑段(Segment),不同段可以放入内存的不同位置。每个段表示一个独立的逻辑单元,如代码段、数据段、栈段等。每个段的大小可以不同,并且段的长度可以根据进程的需求动态增长或缩小,减少外部碎片。

  • 分页是将进程的逻辑地址空间划分成大小固定的页(Page),同时将内存划分成与页大小相同的块,称为页框,每个进程的页表记录了页到页框的映射关系。分页消除了外部碎片,但会出现内部碎片。

Q:死锁产生的条件

  1. 互斥条件:一个资源在任意时刻只能被一个进程持有。

  2. 请求与保持条件:当进程在等待其他资源时,它可以继续持有自己已经获得的资源。

  3. 不可剥夺条件:资源只能由占有它的进程主动释放,而不能被其他进程抢占。

  4. 环路等待条件:每个进程都在等待下一个进程所占有的资源,形成一个循环等待的环路。

Q:如何处理死锁问题

  • 预防死锁:消除死锁产生的四个必要条件之一,如对资源进行排序,让进程按照相同的顺序请求资源。

  • 避免死锁:在资源分配过程中,通过检查系统状态来判断是否会产生死锁,并采取相应的措施来避免死锁的发生。

  • 检测与恢复:一旦检测到死锁,可以通过抢占资源或回滚进程状态来恢复系统到一个安全的状态,解除死锁。

  • 忽略死锁:如果死锁发生的概率非常低,并且解决死锁问题的代价很高,可以选择忽略死锁并在发生死锁时进行系统重启或人工介入来解决问题。

Linux

Q:Linux 常用命令

  1. 文件和目录管理命令:
  • ls: 列出目录中的文件和子目录。
  • pwd: 显示当前工作目录的路径。
  • cd: 切换工作目录。
  • mkdir: 创建新目录。
  • rm: 删除文件或目录。
  • cp: 复制文件或目录。
  • mv: 移动文件或目录,也可用于重命名文件。
  • cat: 显示文件内容。
  • more 或 less: 分页查看文件内容。
  • touch: 创建空文件或更新文件的访问和修改时间。
  1. 系统管理命令:
  • ps: 显示当前运行进程的状态。
  • top: 实时显示系统的进程状态和资源使用情况。
  • kill: 终止进程。
  • ifconfig 或 ip: 显示网络接口信息。
  • ping: 测试网络连接。
  • netstat: 显示网络连接、路由表和网络接口信息。
  • df: 显示磁盘空间使用情况。
  • du: 显示目录或文件的磁盘使用情况。
  1. 用户管理命令:
  • who: 显示当前登录的用户。
  • whoami: 显示当前用户名。
  • useradd: 创建新用户。
  • userdel: 删除用户。
  • passwd: 设置用户密码。
  1. 权限管理命令:
  • chmod: 修改文件或目录的权限。
  • chown: 修改文件或目录的所有者。
  • chgrp: 修改文件或目录的所属组。
  1. 压缩和解压命令:
  • tar: 打包和解包文件。
  • gzip 或 gunzip: 压缩和解压文件。
  1. 搜索命令:
  • grep: 在文件中搜索指定的文本模式。
  • find: 在文件系统中搜索文件或目录。
  1. 管道和重定向:
  • | : 管道操作符,用于将一个命令的输出作为另一个命令的输入。
  • > : 重定向操作符,将命令的输出保存到文件中。
  • >> : 追加重定向操作符,将命令的输出追加到文件末尾。

Q:Linux 如何查看系统日志

  • 使用 journalctl 命令
  • 查看特定日志文件:/var/log 目录下的不同文件中

Q:用户态和内核态有什么区别

通过系统调用将 Linux 整个体系分为用户态和内核态(或者说内核空间和用户空间)。

  • 内核态是操作系统内核运行的一种模式,其中操作系统内核具有最高的特权级别和完全访问系统资源的权限。例如协调 CPU 资源,分配内存资源,并且提供稳定的环境供应用程序运行。
  • 用户态是应用程序运行的一种模式,其中应用程序以及用户编写的代码在用户空间运行,无法直接访问或操作底层硬件设备和操作系统内核。

用户态的应用程序运行在操作系统提供的虚拟地址空间中,通过系统调用向内核请求服务和资源。

从用户态到内核态切换可以通过三种方式:

  1. 系统调用:其实系统调用本身就是中断,但是软件中断,跟硬中断不同。
  2. 异常:如果当前进程运行在用户态,如果这个时候发生了异常事件,就会触发切换。例如:缺页异常。
  3. 外设中断:当外设完成用户的请求时,会向 CPU 发送中断信号。

Q:kill -15 和 kill -9 的区别

  • kill -9 PID 是操作系统从内核级别强制杀死一个进程。
  • kill -15 PID 可以理解为操作系统发送一个通知告诉应用主动关闭。

因为 kill -15 信号只是通知对应的进程要进行”安全、干净的退出”,程序接到信号之后,退出前一般会进行一些”准备工作”,如资源释放、临时文件清理等等,如果准备工作做完了,再进行程序的终止。但是,如果在”准备工作”进行过程中,遇到阻塞或者其他问题导致无法成功,那么应用程序可以选择忽略该终止信号。

Q:前台进程和后台进程区别

  • 前台进程:是在终端中运行的命令,那么该终端就为进程的控制终端,一旦这个终端关闭,这个进程也随之消失。
  • 后台进程:也叫守护进程(Daemon),是运行在后台的一种特殊进程,不受终端控制,它不需要终端的交互;Linux 的大多数服务器就是使用守护进程实现的。

Q:scp 命令

在 linux 下 scp 命令主要用来在不同主机之间做数据的安全拷贝的。scp 命令使用安全加密的协议,所以在远程拷贝数据的时候会比较安全,不会被黑客截取。

Q:shell 脚本

第一行一定是 #!/bin/bash,它代表该文件使用的是 bash 语法。如果不设置该行,那么你的 shell 脚本就不能被执行。

数学计算要用 ‘[]’ 括起来并且外头要带一个 $

  • $v:用于获取变量的值。例如 name=”John”,那么 $name 将会返回 “John”
  • $0:表示当前脚本的名称
  • $1:表示脚本的参数。$1 表示第一个参数,$2 表示第二个参数,以此类推
  • $#:表示传递给脚本的参数个数
  • $@:表示所有的命令行参数列表
  • $?:表示上一个命令的退出状态。通常如果上一个命令执行成功,则其值为 0,否则为非零值
  • $$:表示当前脚本的进程 ID
  • $!:表示最后一个在后台运行的进程的 ID
  • $RANDOM:表示一个随机数
  • $() 或者 ``:用于命令替换,可以将命令的输出作为一个值赋给变量。例如:result=$(ls) 将 ls 命令的输出赋给 result

Q:grep、sed、awk 的用法

  • grep 是一种用于在文本中查找匹配模式的工具。它根据给定的模式搜索文本行,并输出包含匹配项的行。
    基本语法:grep 'pattern' filename

    1
    2
    3
    4
    5
    6
    7
    8
    # 在文件 file.txt 中查找包含 "pattern" 的行并输出到标准输出
    grep "pattern" file.txt

    # 在文件 file.txt 中查找包含 "pattern" 的行,忽略大小写,并显示行号
    grep -i -n "pattern" file.txt

    # 在文件夹 dir 中递归查找所有文件中匹配正则表达式 "pattern" 的行,并打印匹配行所在的文件名和行号
    grep -r -n pattern dir/
  • sed 是一种流编辑器,它逐行处理文本并根据指定的命令进行操作。常用于查找和替换文本。
    基本语法:sed 's/pattern/replacement/' filename

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    # 将 testfile 文件中每行第一次出现的 oo 用字符串 kk 替换,然后将该文件内容输出到标准输出
    sed -e 's/oo/kk/' testfile

    # g 标识符表示全局查找替换,使 sed 对文件中所有符合的字符串都被替换,修改后内容会到标准输出,不会修改原文件
    sed -e 's/oo/kk/g' testfile

    # 选项 i 使 sed 修改文件
    sed -i 's/oo/kk/g' testfile

    # 在 testfile 文件的第四行后添加一行,并将结果输出到标准输出
    sed -e 4a\newLine testfile

    # 将 testfile 的内容列出并且列印行号,同时将第 2~5 行删除
    nl testfile | sed '2,5d'

    # 删除第 3 到最后一行
    sed '3,$d' testfile

    # 在第 2 行后新增上 drink tea
    sed '2a drink tea' testfile

    # 在第 2 行前插入上 drink tea
    sed '2i drink tea' testfile

    # 搜索 testfile 有 oo 关键字的行
    sed -n '/oo/p' testfile

    # 删除 testfile 所有包含 oo 的行
    sed '/oo/d' testfile
  • awk 是一种强大的文本处理工具,用于从输入文件逐行读取文本,并根据指定的规则进行处理和格式化。
    基本语法:awk 'pattern {action}' filename

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # 打印文件 file.txt 的第 2 列
    awk '{print $2}' file.txt

    # 根据分隔符 ":" 打印文件 file.txt 的第 1 列和第 3 列
    awk -F':' '{print $1,$3}' file.txt

    # 过滤第一列大于 2 的行
    awk '$1>2 {print $1,$3}' file.txt

    # 计算文件 file.txt 的第 2 列的总和
    awk '{sum += $2} END {print sum}' file.txt

Q:Linux 如何查看某个文件中某个关键字的后 5 行

1
grep -n "keyword" example.txt | tail -n 5
  • grep -n “keyword” example.txt: 使用 grep 命令查找文件中包含关键字 keyword 的所有行,并显示它们的行号。选项 -n 表示显示行号。
  • tail -n 5: 使用 tail 命令显示最后 5 行。因为前面已经使用 grep 命令找到了匹配的行并显示了行号,这里只需要用 tail 命令显示最后 5 行即可。

Q:Docker 常用命令

  1. 容器管理命令:
  • docker run: 创建并运行一个新的容器。
  • docker start: 启动一个已经被停止的容器。
  • docker stop: 停止一个正在运行的容器。
  • docker restart: 重启一个容器。
  • docker pause: 暂停一个容器中的所有进程。
  • docker unpause: 恢复一个容器中的所有进程。
  • docker kill: 强制停止一个容器。
  • docker rm: 删除一个或多个容器。
  • docker ps: 列出正在运行的容器。
  • docker ps -a: 列出所有容器,包括正在运行和已停止的。
  • docker exec: 在运行中的容器内执行命令。
  • docker logs: 查看容器的日志。
  • docker inspect: 显示容器的详细信息。
  1. 镜像管理命令:
  • docker images: 列出本地的镜像。
  • docker pull: 从 Docker 仓库拉取镜像。
  • docker build: 使用 Dockerfile 构建镜像。
  • docker push: 将镜像推送到 Docker 仓库。
  • docker rmi: 删除一个或多个镜像。
  1. Docker网络命令:
  • docker network ls: 列出所有 Docker 网络。
  • docker network create: 创建一个新的 Docker 网络。
  • docker network connect: 将容器连接到一个 Docker 网络。
  • docker network disconnect: 将容器从一个 Docker 网络断开连接。
  1. 数据管理命令:
  • docker volume ls: 列出所有 Docker 数据卷。
  • docker volume create: 创建一个新的 Docker 数据卷。
  • docker volume rm: 删除一个或多个 Docker 数据卷。
  1. 其他常用命令:
  • docker version: 显示 Docker 客户端和服务端的版本信息。
  • docker info: 显示 Docker 系统信息。
  • docker-compose: 使用 Docker Compose 来管理多个容器的应用。

Q:Dockerfile 中 COPY 和 ADD 有什么区别

  • COPY 只能复制本地主机的文件或目录,不能复制网络资源或从 URL 下载文件。
  • COPY 在复制文件时,不会自动解压缩压缩文件,即使文件是归档文件(例如 .tar, .gz 等),也会按照原样复制到镜像中。
  • 除了复制本地文件外,ADD 还支持复制网络资源和从 URL 下载文件。如果 <源路径> 是一个 URL,ADD 命令会自动下载文件并复制到镜像中。
  • ADD 在复制文件时,如果文件是归档文件(例如 .tar, .gz 等),会自动解压缩压缩文件并复制到镜像中。

Q:Docker 容器如何实现隔离的

Docker 容器实现隔离是通过一系列 Linux 内核特性和技术实现的。这些特性允许每个 Docker 容器拥有独立的文件系统、进程空间、网络和用户空间,从而使它们相互隔离。

每个容器运行在它自己的命名空间(Namespaces)中,但是,确实与其它运行中的容器共用相同的系统内核。隔离的产生是由于系统内核清楚地知道命名空间及其中的进程,且这些进程调用系统 API 时,内核保证进程只能访问属于其命名空间中的资源。

Q:什么是 Docker compose

用于定义和管理多个 Docker 容器的应用程序。它允许你使用简单的 YAML 文件来描述多个容器之间的关系和配置,从而实现快速、方便地部署和管理复杂的多容器应用。

数据库

Q:为什么说 B+ 树更适合数据库索引

  • B+ 树的磁盘读写代价更低:B+ 树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对 B 树更小,盘块所能容纳的关键字数量也越多,一次性读入内存的查找的关键字也就越多,相对 IO 读写次数就降低了。

  • B 树中每个节点中不仅包含数据的 key 值,还有 data 值。而每一个页的存储空间是有限的,如果 data 数据较大时将会导致每个节点(即一个页)能存储的 key 的数量很小,当存储的数据量很大时同样会导致 B 树的深度较大,增大查询时的磁盘 I/O 次数,进而影响查询效率。

  • B+ 树的查询效率更加稳定:由于中间结点并不指向文件内容,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,每一个数据的查询效率相当。

  • B+ 树的数据都存储在叶子结点中方便扫库,只需要扫一遍叶子结点即可,但是 B 树因为其分支结点同样存储着数据,需要进行一次中序遍历按序来扫,而且由于叶子节点之间有双向链表 B+ 树更加适合用于区间查询。

Q:最左前缀原则和最左匹配原则

  • 最左前缀原则是指当数据库使用复合索引进行查询时,查询条件必须是复合索引的最左边的一个或多个字段,才能充分利用索引加速查询。

  • 最左匹配原则是最左前缀原则的一个扩展,它指的是数据库可以利用复合索引中的最左边的连续一段字段来加速查询。这意味着在满足最左前缀原则的前提下,数据库可以只使用复合索引中的前几个字段来匹配查询条件。

Q:数据库三大范式是什么

  • 第一范式:每个列都不可以再拆分。
  • 第二范式:在第一范式的基础上,非主键列完全依赖于主键,而不能是依赖于主键的一部分。
  • 第三范式:在第二范式的基础上,非主键列只依赖于主键,不依赖于其他非主键。

通过遵循数据库三大范式,可以有效地规范数据库的设计,消除数据冗余和不一致性,减少数据更新异常,使得数据库的结构更加清晰、高效,并提高数据库的性能和可维护性。

Q:MySQL 和 Redis 两种数据库有什么区别

  • MySQL 是关系型数据库,主要用于存放持久化数据,将数据存储在硬盘中,读取速度较慢。但是不受空间容量限制,性价比高。

  • Redis 是内存数据库,将数据存储在缓存中,用于存储使用频繁的数据,缓存的读取速度快,能够大大的提高运行效率,但是保存时间有限。

  • Redis 中没有索引,所有的数据都存储在内存中,不需要像传统数据库一样通过索引来查找。

Q:主从全量同步使用 RDB 而不使用 AOF 的原因

  • RDB 文件内容是经过压缩的⼆进制数据(不同数据类型数据做了针对性优化),文件很小。而 AOF 文件记录的是每⼀次写操作的命令,写操作多文件会变得很⼤,其中还包括对同⼀个 key 的多次冗余操作。

  • 打开 AOF 就要选择文件刷盘的策略,选择不当会严重影响 Redis 性能。而 RDB 只有在需要定时备份和主从全量同步数据时才会触发生成⼀次快照。

Q:跳跃表在 Redis 中主要用在哪些地方

zset 有序集合就是用跳表来实现的。可以实现范围查找,排行榜功能或者 topN 功能。

跳跃表是一种有序的数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节点的目的。以空间换时间的方式提升了查找速度。

Q:在 Redis 中找出已知的前缀开头的所有 key

使用 keys 指令可以扫出指定模式的 key 列表。但 Redis 的单线程的。keys 指令会导致线程阻塞一段时间,线上服务会停顿,直到指令执行完毕,服务才能恢复。这个时候可以使用 scan 指令,scan 指令可以无阻塞的提取出指定模式的 key 列表,但是会有一定的重复概率,在客户端做一次去重就可以了,整体所花费的时间会比直接用 keys 指令长。

Q:常见的缓存更新策略

  • Cache Aside(旁路缓存)策略

    Cache Aside(旁路缓存)策略是最常用的,应用程序直接与「数据库、缓存」交互,并负责对缓存的维护

    • 写策略的步骤:先更新数据库中的数据,再删除缓存中的数据。
    • 读策略的步骤:如果读取的数据命中了缓存,则直接返回数据;如果读取的数据没有命中缓存,则从数据库中读取数据,然后将数据写入到缓存,并且返回给用户。
  • Read/Write Through(读穿 / 写穿)策略

    应用程序只和缓存交互,不再和数据库交互,而是由缓存和数据库交互,相当于更新数据库的操作由缓存自己代理了。

  • Write Back(写回)策略

    更新数据的时候,只更新缓存,同时将缓存数据设置为脏的,然后立马返回,并不会更新数据库。对于数据库的更新,会通过批量异步更新的方式进行。

测试

Q:软件生命周期的六个阶段

计划、需求、设计、编码、测试、运行与维护。

  1. 问题定义及规划阶段:主要确定软件的开发目的及其可行性,制定开发计划。
  2. 需求分析/评审阶段:在确定软件开发可行的情况下,将对软件需要实现的每个功能进行详细分析。
  3. 软件设计阶段:在此阶段,将根据需求分析的结果来设计整个软件系统,例如系统框架设计,数据库设计等。软件设计一般分为总体设计和详细设计。
  4. 软件编码阶段:开发人员任务、程序员编码。
  5. 软件测试阶段:测试工程师的任务或开发的任务。
  6. 软件运行维护阶段:版本、产品上线(版本的升级改进)BUG 的修复。

Q:介绍一下单元测试、集成测试、系统测试、验收测试、回归测试

  • 单元测试:测试编码是否符合设计要求。软件中最小的测试单元,比如 go 中的一个函数。相关单元测试放在一起就是一个模块。
  • 集成测试:就是接口测试,对接口是否能够实现进行测试,对接口实现后的结果进行测试。在单元测试的基础上将所有模块按照要求设计组装。测试不同模块之间是否按照预期工作,比如不同模块之间的数据传输。
  • 系统测试:系统测试就是对可视化图形界面测试,对整个系统功能进行测。是基于系统整体需求说明书的黑盒类测试,应覆盖系统所有联合的部件。
  • 验收测试:验收测试就是模拟客户进行测试,确保软件各部分功能正常运行。确定产品是否能够满足合同或用户所规定需求的测试。验收测试包括 Alpha 测试和 Beta 测试。
  • 回归测试:在缺陷修复之后的检验测试,指修改了旧代码后,重新进行测试以确认修改没有引入新的错误或导致其他代码产生错误。

Q:黑盒测试和白盒测试

  • 白盒测试又称为结构测试、逻辑驱动测试或基于程序本身的测试,着重于程序的内部结构及算法,通常不关心功能与性能指标。

    • 逻辑覆盖测试:语句覆盖、判定覆盖、条件覆盖、判定-条件覆盖、条件组合覆盖、路径覆盖
    • 基本路径覆盖测试:选择足够的测试用例,使得运行这些测试用例时,被测程序的每条可能执行的路径都至少经过一次。
  • 黑盒测试又称为功能测试、数据驱动测试或基于规格说明的测试,实际上是站在最终用户的立场上,检验输入输出信息及系统性能指标是否符合规格说明书中有关功能需求及性能需求的规定。

    • 等价类划分法:主要解决穷举的问题
    • 边界值分析法:选取正好等于、刚好大于、刚好小于边界的值作为测试数据
  • 灰盒测试是综合运用黑盒测试和白盒测试技术的一种混合测试方法。

Q:冒烟测试

冒烟测试是一种快速的功能测试,验证核心功能是否正常,目的是确认软件是否值得进行更详细的测试。

Q:接口测试

接口测试其实最主要的验证接口逻辑,可用性,边界值,异常检查。

主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。

Q:为什么要做接口测试

  • 现在很多系统前后端架构是分离的,因为不同端的工作进度不一样,所以我们要针对最开始出来的接口,以及需要调用其他公司的一些接口进行接口测试及验证数据。
  • 如今系统越来越复杂,传统的靠前端测试已经大大降低了效率,而且现在都推崇测试前移也叫测试左移,希望测试能更早的介入,那接口测试就是一种及早介入的方式。

Q:接口的组成

接口说明、调用 url(请求的地址)、请求方法(get/post)、请求参数、参数类型、参数说明。

Q:性能测试

在整个测试中,应用程序的性能在预期的或更高的负载下进行评估。评估系统的不同性能属性,如响应时间(速度)、吞吐量、可靠性、资源使用率、可扩展性等。监控系统的各项指标,是否符合需求,如果不符合,就发现了系统的性能瓶颈。

  • 负载测试:通过在被测系统上不断加压(如逐渐增加模拟用户的数量),直到性能指标达到极限,来观察不同负载下系统的响应时间和数据吞吐量、系统占用的资源(如 CPU、内存)等。
  • 压力测试:系统在强负载下的测试,例如 CPU、内存在饱和使用情况下,看系统在峰值使用情况下是否稳定,看系统处理会话的能力以及哪里会出问题。
  • 容量测试:通过测试预先分析出系统某项指标的极限值(如最大并发用户数、数据库记录数等)。
  • 并发测试:模拟多用户并发访问同一个应用、同一个模块或者数据记录时是否存在死锁或者其他性能问题。
  • 配置测试:通过对被测系统软硬件环境的调整,了解各种不同环境对系统性能影响的程度,从而找到系统各项资源的最优分配原则。
  • 可靠性测试(持久性测试):给系统施加一定的业务压力,让其持续运行一段时间,测试在这种条件下能否稳定运行。

Q:性能测试常用指标

吞吐量、资源利用率(CPU、内存、带宽)、响应时间、响应时间分布、并发数、错误率、瓶颈分析、平台和网络性能。

Q:测试用例八大要素

编号、标题、模块、前置条件、优先级、测试步骤、测试数据、预期结果。

Q:驱动模块和桩模块

驱动模块和桩模块的设计模式有助于实现模块间的解耦、模块的独立开发和测试,以及提高系统的可维护性和可测试性。

  • 驱动模块(Driver):
    驱动模块是指在软件系统中用于驱动或控制其他模块或组件的模块。它通常作为系统的入口点,负责协调和调用其他模块的功能。驱动模块可以是一个主程序、主服务或主类,它负责初始化系统,处理用户输入,协调各个模块之间的交互等。可以通过模拟一系列用户操作行为,比如选择用户界面上的某一个选项或者按下某个按钮等,自动调用被测试模块中的函数。

  • 桩模块(Stub):
    桩模块是指在软件开发中用于模拟或代替其他模块或组件的模块。它通常用于测试、仿真或替代系统中的某个模块,以便进行单元测试、集成测试或模块开发的并行工作。桩模块的设计目标是提供与实际模块相同的接口,但实现逻辑通常是简化的或者是空实现的。桩模块可以模拟其他模块的行为,返回预定义的数据或固定的结果,以便进行测试和验证。

Q:软件测试的关键点

  • 尽早发现缺陷,避免最后问题堆积导致无法解决或解决工程量巨大。
  • 用尽量少的测试用例发现尽可能多的缺陷。
  • 提升发现缺陷的效率。

Q:自动化测试的流程

  1. 分析自动化测试需求,一般在手工测试之后开始
  2. 根据项目的特点、选择合适的自动化测试工具,并搭建测试环境
  3. 测试用例设计和开发:设计测试用例;或提取手工测试的测试用例,转化为自动化测试用例
  4. 开发自动化软件测试框架和测试脚本
  5. 执行:通过工具、代码实现自动化的构造输入、自动检测输出结果是否满足预期
  6. 生成自动测试报告
  7. 持续改进、脚本优化

Q:登陆界面测试

  • 功能测试

    1. 输入正确的用户名和密码,点击提交按钮,验证是否能正确登录
    2. 输入错误的用户名或者密码,验证登录会失败,并且提示相应的错误信息
    3. 登录成功后能否能否跳转到正确的页面
    4. 用户名和密码,如果太短或者太长,应该怎么处理
    5. 用户名和密码,中有特殊字符(比如空格),和其他非英文的情况
    6. 记住用户名的功能
    7. 登陆失败后,不能记录密码的功能
    8. 用户名和密码前后有空格的处理
    9. 密码是否非明文显示显示,使用星号圆点等符号代替
    10. 登录页面中的注册、忘记密码,登出用另一帐号登陆等链接是否正确
    11. 输入密码的时候,大写键盘开启的时候要有提示信息
    12. 什么都不输入,点击提交按钮,检查提示信息
  • 性能测试

    1. 打开登录页面,需要的时间是否在需求要求的时间内
    2. 输入正确的用户名和密码后,检查登录成功跳转到新页面的时间是否在需求要求的时间内
    3. 模拟大量用户同时登陆,检查一定压力下能否正常登陆跳转
  • 兼容性测试

    1. 不同浏览器下能否显示正常且功能正常
    2. 同种浏览器不同版本下能否显示正常且功能正常
    3. 不同的平台是否能正常工作,比如 Windows,Mac
    4. 移动设备上是否正常工作,比如 Iphone,Andriod
    5. 不同的分辨率下显示是否正常
  • 界面测试

    1. 布局是否合理,testbox 和按钮是否整齐
    2. testbox 和按钮的长度,高度是否复合要求
    3. 界面的设计风格是否与 UI 的设计风格统一
    4. 界面中的文字简洁易懂,没有错别字
  • 可用性测试

    1. 是否可以全用键盘操作,是否有快捷键
    2. 输入用户名,密码后按回车,是否可以登陆
    3. 输入框能否可以以 Tab 键切换
  • 安全性测试

    1. 登录成功后生成的 Cookie,是否是 HttpOnly
    2. 用户名和密码是否通过加密的方式,发送给 web 服务器
    3. 用户名和密码的验证,应该是用服务器端验证, 而不能单单是在客户端用 javascript 验证
    4. 用户名和密码的输入框,应该屏蔽 SQL 注入攻击
    5. 用户名和密码的的输入框,应该禁止输入脚本
    6. 防止暴力破解,检测是否有错误登陆的次数限制
    7. 是否支持多用户在同一机器上登录
    8. 同一用户能否在多台机器上登录

其他

Q:为什么需要 DevOps

软件开发公司在软件新版本发布方面,多尝试通过发布一系列以小的特性改变集为目标的新软件版本,代替发布一个大特性改变集的新软件版本的方式。

  • 增加软件布署的频率
  • 降低新发布版本的失败率
  • 缩短修复缺陷的交付时间
  • 加快解决版本冲突的问题
  • 自动化运维流程

「训」 笔记(13):Q & A
https://qanlyma.github.io/ByteDance-13/
作者
Qanly
发布于
2023年7月15日