Skip to content

Commit

Permalink
add: rust study note
Browse files Browse the repository at this point in the history
  • Loading branch information
gongna-au committed Oct 10, 2023
1 parent 809f673 commit 7c33ad6
Show file tree
Hide file tree
Showing 2 changed files with 583 additions and 0 deletions.
186 changes: 186 additions & 0 deletions _posts/2023-11-15-test-markdown.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,186 @@
---
layout: post
title: 测开
subtitle:
tags: [测试/开发]
comments: true
---

## 性能和压力测试:

测试流程
初始设定:模拟多个用户(例如500、1000、5000用户)进行并发操作。
关注点:响应时间、系统吞吐量、错误率。

排查和分析性能问题
工具:使用性能监控工具如Grafana, Prometheus等。
排查:定位瓶颈可能发生的地方,如数据库、网络、CPU等。
分析:通过日志、监控数据来确定瓶颈原因。

优化
根据瓶颈分析结果进行相应的优化。
重复压力测试,验证是否解决了性能问题。



> 测试策略?
按照测试金字塔的模式进行,主要包括单元测试、集成测试和端到端测试。

单元测试: 为每个模块、函数或类编写了单元测试,以确保其单一职责得到满足。
集成测试: 在单元测试的基础上,进一步进行了API和数据库的集成测试。
端到端测试: 我们使用Selenium进行UI测试,确保整个流程可以顺利完成。
我们还使用了持续集成(CI)系统,每次代码提交都会触发测试流程,只有全部测试通过才能继续后续的部署流程。

> 如何在有限的时间和资源内优先选择测试哪些功能?
通常会使用风险基础的测试策略来确定测试的优先级。首先,识别关键的业务流程和高风险区域。
业务影响: 功能对业务的影响程度是一个考虑因素,例如,支付功能出现问题会直接影响到公司的收入。
用户流量: 高用户访问量的功能会优先进行测试。
代码复杂性: 如果代码逻辑复杂或者有大量的条件分支,那么这部分功能也会被优先考虑。
在时间非常有限的情况下,我可能会采用探索性测试,通过这种不完全结构化的方式快速找出潜在的高风险问题。


> 使用自动化测试工具或框架的经验?
主要使用了Go的内置测试库以及一些第三方库如Testify来进行自动化测试。对于API测试,使用Postman和JMeter的经验。因为主要关注数据库中间件,所以也使用了数据库的压力测试工具,例如Sysbench。

> 描述一下如何从零开始设置一个自动化测试环境。
以Go语言和数据库中间件为例,我通常会按照以下步骤设置自动化测试环境:

环境准备: 首先,确保Go语言的运行环境和必要的依赖库都已经安装。

项目结构: 在项目目录下创建一个独立的tests文件夹,用于存放所有的测试代码。

单元测试: 使用Go的内置测试库编写单元测试,并通过go test命令进行运行。

集成测试: 由于我们测试的是数据库中间件,所以需要启动一个模拟的数据库环境,这可以通过Docker来完成。然后,使用Go编写针对这个环境的集成测试。

测试数据准备: 使用Go的sql包或者专门的数据库驱动来插入必要的测试数据。

持续集成: 使用CI工具(例如Jenkins或GitHub Actions)来自动化测试流程。每次代码提交都会触发测试,并将结果报告给团队。

压力测试: 使用Sysbench或者自定义的Go程序来对中间件进行压力测试,观察性能瓶颈和系统的稳定性。

通过这样的设置,我们可以确保数据库中间件在各种条件下都能正常工作,并及时发现潜在的问题。


持续集成/持续部署(CI/CD)

> 将测试集成到CI/CD流程中的经验?
GitLab-CI:就是一套配合GitLab使用的持续集成系统
GitLab-Runner:是配合GitLab-CI进行使用的。用来自动化执行软件集成脚本的进程。当这个工程的仓库代码发生变动时,比如有人push了代码,GitLab就会将这个变动通知GitLab-CI。这时GitLab-CI会找出与这个工程相关联的Runner,并通知这些Runner把代码更新到本地并执行预定义好的执行脚本。

为了利用gitlab的持续集成能力,我们可以通过在项目根目录写一个.gitlab-ci.yml配置文件来开启gitlab pipeline功能


> 在CI/CD中,如何管理测试数据和测试环境的?
测试数据:对于需要特定状态的测试,我们使用数据库迁移脚本来准备测试数据。这些脚本在每次运行测试前都会执行,确保数据的一致性。

测试环境:我们使用Docker容器来模拟生产环境。每次CI/CD流程触发时,都会生成一个新的容器环境来运行测试。这确保了测试环境的一致性,并且与其他任务隔离。

并行测试:为了加速整个测试过程,我们还利用了Jenkins的并行执行功能,使得多个测试任务可以同时进行。

结果反馈:测试结果


> 你有没有进行过性能、负载或压力测试?使用了哪些工具?

工具选择:
Vegeta: 一个用Go编写的HTTP负载测试工具,适用于API性能测试。
pprof: Go语言自带的性能分析工具,用于收集和分析CPU、内存、协程等方面的数据。

> 请描述一下你如何诊断和解决性能瓶颈

性能分析: 我会首先使用pprof来进行基准测试和性能分析,找出CPU或内存的热点。

数据库优化: 如果问题出在数据库交互上,我会使用SQL分析工具进行查询优化。

并发优化: Go语言的goroutine非常适合进行并发优化,我会通过改进代码的并发逻辑来提高性能。

资源缓存: 对于重复和高频的操作,我会使用缓存机制(如Redis)来减少不必要的计算和I/O操作。

负载测试: 在所有优化完成后,我会使用Vegeta来模拟不同的用户负载和请求速率,确保优化效果符合预期。


> 如何使用pprof来进行基准测试和性能分析,找出CPU或内存的热点?
```go
package main

import (
"log"
"net/http"
_ "net/http/pprof"
"runtime"
)

// 在main函数中或其他初始化代码中
func main() {
runtime.SetBlockProfileRate(1) // 开启对阻塞操作的跟踪,block
runtime.SetMutexProfileFraction(1) // 开启对锁调用的跟踪,mutex
go func() {
log.Println(http.ListenAndServe(":6060", nil))
}()
http.ListenAndServe(":8888", nil)

}

```

allocs:查看过去所有内存分配的样本(历史累计)。
block:查看导致阻塞同步的堆栈跟踪(历史累计)。
cmdline: 当前程序的命令行的完整调用路径(从程序一开始运行时决定)。
goroutine:查看当前所有运行的 goroutines 堆栈跟踪(实时变化)。
heap:查看活动对象的内存分配情况(实时变化)。
mutex:查看导致互斥锁的竞争持有者的堆栈跟踪(历史累计)。
profile: 默认进行 30s 的 CPU Profiling,得到一个分析用的 profile 文件(从开始分析,到分析结束)

> StringBuffer 和 StringBuilder
StringBuffer 和 StringBuilder 都是 Java 中用于字符串拼接的可变字符序列类。它们之间最大的区别在于 StringBuffer 是线程安全的(因为它使用了同步),而 StringBuilder 是非线程安全的。

这意味着,如果你的代码中有多个线程同时访问和修改同一个 StringBuffer 对象,那么 StringBuffer 可以保证在多线程环境下的正确性。而如果使用 StringBuilder,则需要你自己实现同步机制来保证在多线程环境下的正确性。

另外,因为 StringBuffer 使用了同步机制,所以它在单线程环境下会略微慢一些,而 StringBuilder 在单线程环境下会略微快一些。

在底层实现上,StringBuffer 和 StringBuilder 都使用了一个可变的字符数组来存储字符串。当需要扩展字符数组的容量时,它们都会创建一个新的字符数组,并将原来的字符串复制到新的字符数组中。

StringBuffer 的同步机制
StringBuffer 使用了同步机制来保证方法的原子性和可靠性。这意味着在同一时间内,只能有一个线程执行 StringBuffer 中的同步方法,从而避免了多个线程同时修改数据的情况。

以下是几个关键点:

同步方法: StringBuffer 中的方法,例如 append(), insert(), delete(), reverse() 等,都是同步方法。这意味着同一时刻只有一个线程可以调用这些方法,确保了方法的原子性。

锁机制: StringBuffer 内部使用一个锁对象来实现同步机制。这个锁对象会被方法调用所获取,其他线程在获取锁之前会被阻塞,从而实现线程间的同步。

性能影响: 尽管同步机制确保了多线程环境下的线程安全,但是也带来了性能上的开销。因为同一时间只能有一个线程访问 StringBuffer 的同步方法,其他线程必须等待锁的释放才能继续执行。

append()
append() 方法用于向字符串末尾添加字符序列,它的同步机制是通过锁来实现的。当一个线程调用 append() 方法时,它会获取 StringBuffer 实例对象的内部锁,然后执行添加操作,最后释放锁。这确保了同一时间只有一个线程能够执行 append() 操作,避免了多线程下数据不一致的问题。

```java
public synchronized StringBuffer append(String str)
```
```java
insert()
```
insert() 方法用于在指定位置插入字符序列,同样也使用了锁机制来实现线程安全。当一个线程调用 insert() 方法时,它会获取 StringBuffer 实例对象的内部锁,执行插入操作,最后释放锁。
```java
public synchronized StringBuffer insert(int offset, String str)
```
```java
delete()
```
delete() 方法用于删除指定范围内的字符,也是一个同步方法。它在执行时会获取 StringBuffer 实例对象的内部锁,然后进行删除操作,最后释放锁。
```java
public synchronized StringBuffer delete(int start, int end)
```

reverse()
reverse() 方法用于反转字符串,同样也使用了同步机制。它在执行时会获取 StringBuffer 实例对象的内部锁,执行反转操作,最后释放锁。
Loading

0 comments on commit 7c33ad6

Please sign in to comment.