diff --git a/daliy_note/.DS_Store b/daliy_note/.DS_Store new file mode 100644 index 0000000..c4b2554 Binary files /dev/null and b/daliy_note/.DS_Store differ diff --git a/daliy_note/10.12/Prometheus写入victoriaMetrics集群.md b/daliy_note/10.12/Prometheus写入victoriaMetrics集群.md new file mode 100644 index 0000000..66d6c75 --- /dev/null +++ b/daliy_note/10.12/Prometheus写入victoriaMetrics集群.md @@ -0,0 +1,44 @@ +将Prometheus数据写入VictoriaMetrics集群与单节点的配置略有不同,因为集群模式需要考虑多节点的协调和数据分片。VictoriaMetrics集群由多个组成部分,比如vminsert、vmstorage和vmselect,确保数据的高可用性和性能。以下是如何配置Prometheus以写入VictoriaMetrics集群的步骤: + +### VictoriaMetrics集群架构 + +1. **vminsert**:负责接收数据写入请求,并将数据分发到vmstorage节点。 +2. **vmstorage**:负责实际的数据存储。 +3. **vmselect**:负责处理查询请求,从vmstorage中读取数据。 + +### 配置Prometheus写入VictoriaMetrics集群 + +1. **配置Prometheus Remote Write**: + - 在Prometheus的配置文件`prometheus.yml`中,配置`remote_write`以指向vminsert节点。通常,你会有多个vminsert节点用于负载均衡。 + + ```yaml + remote_write: + - url: "http://vminsert-1.example.com:8480/insert/0/prometheus/" + - url: "http://vminsert-2.example.com:8480/insert/0/prometheus/" + # 添加更多的vminsert节点以实现负载均衡和高可用性 + ``` + +2. **配置负载均衡(可选)**: + - 你可以使用负载均衡器(如Nginx、HAProxy等)来分发到多个vminsert节点,以简化Prometheus的配置和提升集群的可用性。 + +3. **确保网络连接**: + - 确保Prometheus服务器可以访问到所有配置的vminsert节点,检查网络连接和防火墙设置。 + +4. **监控和日志检查**: + - 检查Prometheus和VictoriaMetrics集群的日志,确保数据正确写入。如果有错误日志,及时排查和解决。 + +### 其他注意事项 + +- **数据一致性和分片**: + - 在VictoriaMetrics集群中,vminsert会自动处理数据分片和一致性,你无需特别配置。 + +- **高可用性**: + - 确保每个组件(vminsert、vmstorage、vmselect)都有多个实例,以避免单点故障。 + +- **资源配置**: + - 监控每个节点的CPU、内存和磁盘使用情况,确保资源充足以处理高负载。 + +- **集群管理**: + - 定期更新和维护VictoriaMetrics集群,应用安全补丁和新特性。 + +通过上述配置,你可以实现将Prometheus的数据高效地写入VictoriaMetrics集群,并充分利用其高性能和高可用性的优势。根据你的具体负载和环境,调整节点数量和配置以实现最佳性能。 \ No newline at end of file diff --git a/daliy_note/10.12/VictoriaMetrics租户.md b/daliy_note/10.12/VictoriaMetrics租户.md new file mode 100644 index 0000000..72b2496 --- /dev/null +++ b/daliy_note/10.12/VictoriaMetrics租户.md @@ -0,0 +1,47 @@ +在VictoriaMetrics中,多租户支持是通过租户ID来实现的。VictoriaMetrics本身并不需要显式地“添加”或“创建”租户。相反,租户是通过在写入和查询请求中使用不同的租户ID来实现的。这使得租户管理非常灵活和简单。 + +### 如何使用多租户功能 + +1. **写入数据到指定租户** + + 当你将数据写入VictoriaMetrics时,可以通过在请求URL中指定租户ID来区分数据属于哪个租户。例如: + + - 默认租户(ID为0): + + ```plaintext + http://vminsert.example.com:8480/insert/0/prometheus/ + ``` + + - 自定义租户(ID为123): + + ```plaintext + http://vminsert.example.com:8480/insert/123/prometheus/ + ``` + + 在这个例子中,`123`是自定义的租户ID。你可以根据需要选择任意整数作为租户ID。 + +2. **查询特定租户的数据** + + 当查询数据时,也需要在查询请求中指定租户ID,以确保查询的是正确的数据集。对于Prometheus兼容的查询API,你可以这样指定: + + - 查询默认租户的数据: + + ```plaintext + http://vmselect.example.com:8481/select/0/prometheus/api/v1/query?query=... + ``` + + - 查询租户`123`的数据: + + ```plaintext + http://vmselect.example.com:8481/select/123/prometheus/api/v1/query?query=... + ``` + +### 重要注意事项 + +- **租户ID是逻辑概念**:VictoriaMetrics没有像传统数据库那样的租户“创建”过程。租户ID的使用仅仅是通过在请求中指定不同的ID来实现数据的逻辑隔离。 + +- **权限管理**:原生的VictoriaMetrics不包含复杂的权限管理系统。如果需要对租户进行权限或安全管理,通常需要在应用层或者通过其他工具(如反向代理或API网关)来实现。 + +- **数据隔离**:使用不同的租户ID确保数据的存储和查询是隔离的,但物理上数据仍然可能存储在同一个数据库集群中。 + +通过这种简单的机制,VictoriaMetrics可以轻松地支持多租户环境,而无需复杂的配置和管理。 \ No newline at end of file diff --git a/daliy_note/10.12/grafana读取VictoriaMetrics集群数据.md b/daliy_note/10.12/grafana读取VictoriaMetrics集群数据.md new file mode 100644 index 0000000..66d6c75 --- /dev/null +++ b/daliy_note/10.12/grafana读取VictoriaMetrics集群数据.md @@ -0,0 +1,44 @@ +将Prometheus数据写入VictoriaMetrics集群与单节点的配置略有不同,因为集群模式需要考虑多节点的协调和数据分片。VictoriaMetrics集群由多个组成部分,比如vminsert、vmstorage和vmselect,确保数据的高可用性和性能。以下是如何配置Prometheus以写入VictoriaMetrics集群的步骤: + +### VictoriaMetrics集群架构 + +1. **vminsert**:负责接收数据写入请求,并将数据分发到vmstorage节点。 +2. **vmstorage**:负责实际的数据存储。 +3. **vmselect**:负责处理查询请求,从vmstorage中读取数据。 + +### 配置Prometheus写入VictoriaMetrics集群 + +1. **配置Prometheus Remote Write**: + - 在Prometheus的配置文件`prometheus.yml`中,配置`remote_write`以指向vminsert节点。通常,你会有多个vminsert节点用于负载均衡。 + + ```yaml + remote_write: + - url: "http://vminsert-1.example.com:8480/insert/0/prometheus/" + - url: "http://vminsert-2.example.com:8480/insert/0/prometheus/" + # 添加更多的vminsert节点以实现负载均衡和高可用性 + ``` + +2. **配置负载均衡(可选)**: + - 你可以使用负载均衡器(如Nginx、HAProxy等)来分发到多个vminsert节点,以简化Prometheus的配置和提升集群的可用性。 + +3. **确保网络连接**: + - 确保Prometheus服务器可以访问到所有配置的vminsert节点,检查网络连接和防火墙设置。 + +4. **监控和日志检查**: + - 检查Prometheus和VictoriaMetrics集群的日志,确保数据正确写入。如果有错误日志,及时排查和解决。 + +### 其他注意事项 + +- **数据一致性和分片**: + - 在VictoriaMetrics集群中,vminsert会自动处理数据分片和一致性,你无需特别配置。 + +- **高可用性**: + - 确保每个组件(vminsert、vmstorage、vmselect)都有多个实例,以避免单点故障。 + +- **资源配置**: + - 监控每个节点的CPU、内存和磁盘使用情况,确保资源充足以处理高负载。 + +- **集群管理**: + - 定期更新和维护VictoriaMetrics集群,应用安全补丁和新特性。 + +通过上述配置,你可以实现将Prometheus的数据高效地写入VictoriaMetrics集群,并充分利用其高性能和高可用性的优势。根据你的具体负载和环境,调整节点数量和配置以实现最佳性能。 \ No newline at end of file diff --git a/daliy_note/10.14/linux&&macos查看文件夹大小.md b/daliy_note/10.14/linux&&macos查看文件夹大小.md new file mode 100644 index 0000000..ddbbb9a --- /dev/null +++ b/daliy_note/10.14/linux&&macos查看文件夹大小.md @@ -0,0 +1,49 @@ +在macOS上,你可以使用`du`命令来统计当前文件夹及其子文件夹的磁盘空间使用情况。这里有几个常用的命令选项可以帮助你实现这一目标: + +### 使用 `du` 命令 + +1. **显示当前文件夹及其子文件夹的磁盘使用情况** + + 打开“终端”应用程序,然后导航到你想要检查的目录,或者直接在终端中输入以下命令: + + ```bash + du -h + ``` + + 这个命令会递归地显示当前目录及其所有子目录的磁盘使用情况,`-h`选项将结果以人类可读的格式(如KB、MB、GB)显示。 + +2. **显示当前文件夹及其子文件夹的总磁盘使用情况** + + 如果你只想查看当前目录及其子目录的总磁盘使用情况,而不需要详细的目录列表,可以使用: + + ```bash + du -sh + ``` + + 这里,`-s`选项表示“summary”,即只显示总计的大小。 + +3. **显示指定层级的子目录磁盘使用情况** + + 如果你想限制显示的目录层级,可以使用`--max-depth`选项。例如: + + ```bash + du -h --max-depth=1 + ``` + + 这个命令会显示当前目录下的每个子目录的磁盘使用情况,但不再深入子目录的子目录。 + +### 示例 + +假设你在终端中导航到某个目录,并运行`du -sh`,输出可能类似于: + +```plaintext +1.2G . +``` + +这表示当前目录及其所有子目录总共占用了1.2GB的磁盘空间。 + +### 其他注意事项 + +- `du`命令可能需要一些时间来计算特别是对于包含大量文件或大文件的目录。 +- 权限问题可能会导致`du`无法访问某些目录。在这种情况下,可能需要使用`sudo`来提升权限,例如:`sudo du -sh`。 +- 如果你对某些特定文件类型的大小感兴趣,可能需要结合其他命令(如`find`)来实现更复杂的过滤和统计。 \ No newline at end of file diff --git a/daliy_note/10.15/mongo.md b/daliy_note/10.15/mongo.md new file mode 100644 index 0000000..60311bd --- /dev/null +++ b/daliy_note/10.15/mongo.md @@ -0,0 +1,35 @@ +### docker 部署mongoDB + +```bash +docker run -itd -p 27017:27017 -e MONGO_INITDB_ROOT_USERNAME=root -e MONGO_INITDB_ROOT_PASSWORD=123456 --name mongo mongo +``` + + + +### 用root用户登录 + +- 解决创建用户报错误问题 + + Command createUser requires authentication + +```bash +docker exec -it mongo bash # 进入到容器的terminal +mongosh --username root --password --authenticationDatabase admin # 认证admin账户 +``` + + + +### 给database创建用户’ + +```bash +use cmdb +db.createUser({ + user: "root", + pwd: "123456", + roles: [ + { role: "readWrite", db: "" }, + { role: "dbAdmin", db: "" } + ] +}) +``` + diff --git a/daliy_note/10.24/context.Context.md b/daliy_note/10.24/context.Context.md new file mode 100644 index 0000000..96f3778 --- /dev/null +++ b/daliy_note/10.24/context.Context.md @@ -0,0 +1,45 @@ +是的,在 Go 语言中,`context.Context` 是一个接口类型,而接口在 Go 中是引用类型。具体来说,`context.Context` 提供了一种管理请求范围内数据、取消信号和超时的方式,是用于处理并发编程中的上下文信息的常用工具。 + +### `context.Context` 基础 + +- **接口类型**: `context.Context` 是一个接口,这意味着它定义了一组方法,但不包含数据本身。实现 `context.Context` 接口的具体类型如 `context.Background()` 和 `context.TODO()` 都是引用类型。 + +- **常用方法**: + - `Deadline() (deadline time.Time, ok bool)`: 返回上下文被取消的时间。 + - `Done() <-chan struct{}`: 返回一个通道,当上下文被取消或到达截止时间时,该通道会被关闭。 + - `Err() error`: 返回上下文被取消的原因。 + - `Value(key interface{}) interface{}`: 从上下文中检索与键关联的值。 + +### 使用 `context.Context` + +`context.Context` 通常用于管理请求的生命周期,特别是在处理 HTTP 请求、RPC 调用或其他需要在一段时间内传递上下文信息的操作时。 + +```go +package main + +import ( + "context" + "fmt" + "time" +) + +func main() { + ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) + defer cancel() + + select { + case <-time.After(1 * time.Second): + fmt.Println("operation completed") + case <-ctx.Done(): + fmt.Println("timeout:", ctx.Err()) + } +} +``` + +### 传递和取消 + +- **传递引用**: 因为 `context.Context` 是引用类型,所以通常将它作为第一个参数传递给需要上下文信息的函数。这是因为引用类型允许在多个函数间共享同一个上下文实例。 + +- **取消操作**: 通过使用例如 `context.WithCancel` 和 `context.WithTimeout` 等函数,可以生成一个可取消的上下文,并在操作完成或超时时调用取消函数。 + +总之,`context.Context` 的设计使其适合用于并发编程中传递请求范围内的信息和取消信号,并且由于它是引用类型,传递它时不会产生额外的复制开销。 \ No newline at end of file diff --git a/daliy_note/10.24/vim打开文件乱码.md b/daliy_note/10.24/vim打开文件乱码.md new file mode 100644 index 0000000..4a4c56f --- /dev/null +++ b/daliy_note/10.24/vim打开文件乱码.md @@ -0,0 +1,66 @@ +当你使用 Vim 打开文件时遇到乱码问题,通常是由于文件编码和 Vim 的默认编码不匹配造成的。以下是一些常见的解决方案: + +### 1. 检查文件的实际编码 + +首先,你需要知道文件的实际编码是什么。你可以使用以下命令来检查文件的编码: + +#### 在 Linux 或 macOS 上 + +```bash +file -i filename +``` + +这将输出文件的 MIME 类型和编码,比如 `text/plain; charset=utf-8`。 + +### 2. 设置 Vim 的编码 + +一旦知道了文件的实际编码,你可以在 Vim 中设置相应的编码来正确显示文件内容。 + +#### 在 Vim 中设置编码 + +打开 Vim 后,输入以下命令来设置文件的编码: + +```vim +:e ++enc= +``` + +例如,如果文件是以 UTF-8 编码的,你可以输入: + +```vim +:e ++enc=utf-8 +``` + +### 3. 修改 Vim 配置文件 + +如果你经常遇到编码问题,可以考虑修改 Vim 的配置文件(通常是 `~/.vimrc`)来设置默认编码。 + +在 `~/.vimrc` 中添加以下行,可以设置 Vim 的默认编码为 UTF-8: + +```vim +set encoding=utf-8 +set fileencodings=utf-8,gbk,latin1 +``` + +这样,Vim 会优先尝试使用 UTF-8 打开文件,如果不匹配则尝试 GBK 和 Latin1。 + +### 4. 使用 iconv 转换文件编码 + +如果文件编码不常见,或者你需要将文件转换为另一种编码,可以使用 `iconv` 工具。 + +#### 转换文件编码 + +```bash +iconv -f -t -o +``` + +例如,将一个 GBK 编码的文件转换为 UTF-8: + +```bash +iconv -f gbk -t utf-8 input.txt -o output.txt +``` + +### 5. 确保终端支持相应编码 + +有时候问题可能出在终端模拟器上。确保你的终端设置为支持文件的编码,特别是 UTF-8,这是大多数现代终端模拟器的默认设置。 + +通过以上步骤,你应该能够解决 Vim 打开文件时的乱码问题。如果问题仍然存在,可能需要确认文件本身是否损坏或尝试其他工具来检查文件内容。 \ No newline at end of file diff --git a/daliy_note/10.24/waitgroup.md b/daliy_note/10.24/waitgroup.md new file mode 100644 index 0000000..ca2dac2 --- /dev/null +++ b/daliy_note/10.24/waitgroup.md @@ -0,0 +1,42 @@ +在 Go 语言中,你可以传递 `sync.WaitGroup` 的指针。这在并发编程中是一个常见的模式,因为 `sync.WaitGroup` 的方法(如 `Add`、`Done` 和 `Wait`)会修改 `WaitGroup` 的内部状态,所以通常需要通过指针传递以避免复制。 + +使用 `sync.WaitGroup` 时,通过指针传递的典型用法如下: + +```go +package main + +import ( + "fmt" + "sync" +) + +func worker(id int, wg *sync.WaitGroup) { + defer wg.Done() // 确保在 goroutine 完成时调用 Done + fmt.Printf("Worker %d starting\n", id) + // 模拟工作 + fmt.Printf("Worker %d done\n", id) +} + +func main() { + var wg sync.WaitGroup + + for i := 1; i <= 5; i++ { + wg.Add(1) // 增加 WaitGroup 的计数器 + go worker(i, &wg) // 传递 WaitGroup 的指针 + } + + wg.Wait() // 等待所有 goroutine 完成 + fmt.Println("All workers done") +} +``` + +### 说明 + +- **指针传递**: `worker` 函数接收一个 `*sync.WaitGroup` 类型的参数。这样,所有的 goroutine 都共享同一个 `WaitGroup` 实例,可以正确地递增和递减计数器。 + +- **Add、Done 和 Wait**: + - `Add(n int)`: 增加等待计数器的值。 + - `Done()`: 减少等待计数器的值(通常在 goroutine 完成时调用)。 + - `Wait()`: 阻塞直到等待计数器归零。 + +通过这种方式,可以确保主函数会等待所有启动的 goroutine 完成其任务。传递 `sync.WaitGroup` 的指针是 Go 中并发编程的一种惯用方法,确保在多个 goroutine 中正确地共享和管理等待组的状态。 \ No newline at end of file diff --git a/daliy_note/10.29/git.md b/daliy_note/10.29/git.md new file mode 100644 index 0000000..f44ee3a --- /dev/null +++ b/daliy_note/10.29/git.md @@ -0,0 +1,6 @@ +### git 获取当前分支名 + +```bash +git branch --show-current +``` + diff --git a/daliy_note/10.8/linux查看所有硬件设备型号.md b/daliy_note/10.8/linux查看所有硬件设备型号.md new file mode 100644 index 0000000..277d83c --- /dev/null +++ b/daliy_note/10.8/linux查看所有硬件设备型号.md @@ -0,0 +1,8 @@ +### lshw + +```bash +sudo apt install lshw +lshw +lshw -short +``` + diff --git a/daliy_note/10.9/kubernetes admission webhook.md b/daliy_note/10.9/kubernetes admission webhook.md new file mode 100644 index 0000000..d768ea2 --- /dev/null +++ b/daliy_note/10.9/kubernetes admission webhook.md @@ -0,0 +1,231 @@ +`AdmissionWebhook` 是 Kubernetes 中的一种动态准入控制器,用于在请求进入 Kubernetes API 服务器时对其进行修改或验证。准入控制器是 Kubernetes 安全模型的一部分,负责拦截请求并对其进行特定的检查或修改,以确保集群的安全性和策略的一致性。 + +### AdmissionWebhook 的作用 + +1. **验证(Validating Webhooks)**: + - 这些 Webhook 在资源被创建、更新或删除之前对请求进行检查,以确保请求符合某些策略或规则。 + - 如果验证失败,请求将被拒绝,并返回给用户一个错误信息。 + +2. **变更(Mutating Webhooks)**: + - 这些 Webhook 可以在请求到达 API 服务器时修改请求对象。 + - 例如,可以自动填充某些字段,或者根据预定义的规则调整资源配置。 + +### 使用场景 + +- **策略实施**:确保所有部署的应用程序都符合公司或组织的安全和合规策略。例如,确保所有 Pod 都具有指定的标签或资源限制。 +- **自动化变更**:自动为资源添加或修改某些属性,例如为每个 Pod 添加一个特定的环境变量。 +- **资源验证**:在资源被持久化之前,对其进行额外的验证,以防止错误配置。 +- **审计和日志**:记录和监控特定请求,以帮助进行审计和故障排查。 + +### 工作原理 + +1. **注册 Webhook**: + - 使用 `MutatingWebhookConfiguration` 和 `ValidatingWebhookConfiguration` 资源在 Kubernetes 中注册 Webhook。 + +2. **处理请求**: + - 当一个请求到达 Kubernetes API 服务器时,配置的 Webhook 会被调用。 + - Webhook 服务接收到请求对象,执行逻辑并返回一个响应,指示是否允许、拒绝或修改请求。 + +3. **响应处理**: + - 对于 Mutating Webhook,API 服务器将根据 Webhook 的响应修改请求对象。 + - 对于 Validating Webhook,API 服务器将检查 Webhook 的响应以决定是否执行请求。 + +### 实现示例 + +要实现一个 AdmissionWebhook,通常需要以下步骤: + +1. **编写 Webhook 服务器**: + - 使用编程语言(例如 Go)编写一个 HTTP 服务器,能够处理 Kubernetes 的 AdmissionReview 请求。 + +2. **部署 Webhook 服务**: + - 将 Webhook 服务器作为一个 Pod 部署到 Kubernetes 集群中。 + +3. **创建 Webhook 配置**: + - 编写并应用 `MutatingWebhookConfiguration` 或 `ValidatingWebhookConfiguration`,指向你的 Webhook 服务。 + +### 注意事项 + +- **安全性**:确保 Webhook 服务的安全性,使用 TLS 进行通信,确保只有经过身份验证的请求才能访问。 +- **性能影响**:Webhook 会影响 API 请求的延迟,因为请求需要经过 Webhook 的处理。尽量优化 Webhook 的处理逻辑以减少开销。 +- **故障处理**:设计 Webhook 时需要考虑故障处理,确保 Webhook 不会因为某些错误而导致集群无法正常工作。 + +通过使用 AdmissionWebhook,Kubernetes 提供了一个强大的机制来实施自定义策略和自动化配置,帮助运维人员更好地管理和控制集群行为。 + + + +### 举例 + +当然!下面是一个简单的示例,展示如何实现一个 Kubernetes Admission Webhook。这个示例将创建一个简单的 Mutating Admission Webhook,它会在每个 Pod 创建之前向其中添加一个环境变量。 + +### 步骤 1: 编写 Webhook 服务器 + +我们将用 Go 编写一个简单的 HTTP 服务器,作为我们的 Webhook 服务。 + +```go +package main + +import ( + "encoding/json" + "net/http" + "log" + + admissionv1 "k8s.io/api/admission/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + corev1 "k8s.io/api/core/v1" +) + +func handleAdmission(w http.ResponseWriter, r *http.Request) { + var admissionReviewReq admissionv1.AdmissionReview + if err := json.NewDecoder(r.Body).Decode(&admissionReviewReq); err != nil { + log.Printf("Could not decode request: %v", err) + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + podResource := metav1.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} + if admissionReviewReq.Request.Resource != podResource { + log.Printf("Expect resource to be %s", podResource) + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + raw := admissionReviewReq.Request.Object.Raw + pod := corev1.Pod{} + if err := json.Unmarshal(raw, &pod); err != nil { + log.Printf("Could not unmarshal raw object: %v", err) + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + // 添加环境变量 + env := corev1.EnvVar{Name: "ADDED_BY_WEBHOOK", Value: "true"} + pod.Spec.Containers[0].Env = append(pod.Spec.Containers[0].Env, env) + + // 创建补丁操作 + patchBytes, err := json.Marshal([]map[string]interface{}{ + {"op": "add", "path": "/spec/containers/0/env", "value": pod.Spec.Containers[0].Env}, + }) + if err != nil { + log.Printf("Could not marshal patch: %v", err) + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + admissionReviewResponse := admissionv1.AdmissionReview{ + Response: &admissionv1.AdmissionResponse{ + UID: admissionReviewReq.Request.UID, + Allowed: true, + Patch: patchBytes, + PatchType: func() *admissionv1.PatchType { + pt := admissionv1.PatchTypeJSONPatch + return &pt + }(), + }, + } + + respBytes, err := json.Marshal(admissionReviewResponse) + if err != nil { + log.Printf("Could not marshal response: %v", err) + http.Error(w, "Bad request", http.StatusBadRequest) + return + } + + w.Header().Set("Content-Type", "application/json") + w.Write(respBytes) +} + +func main() { + http.HandleFunc("/mutate", handleAdmission) + log.Println("Starting server on :8080") + if err := http.ListenAndServeTLS(":8080", "/path/to/tls.crt", "/path/to/tls.key", nil); err != nil { + log.Fatalf("Failed to start server: %v", err) + } +} +``` + +### 步骤 2: 部署 Webhook 服务 + +1. **编译和打包服务**: + - 将上述代码编译为二进制文件,创建一个 Docker 镜像,并将其推送到容器镜像仓库。 + +2. **创建 Kubernetes Deployment 和 Service**: + 编写一个 YAML 文件,将 Webhook 服务部署到 Kubernetes 集群中。 + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webhook-server +spec: + replicas: 1 + selector: + matchLabels: + app: webhook-server + template: + metadata: + labels: + app: webhook-server + spec: + containers: + - name: webhook-server + image: + ports: + - containerPort: 8080 + volumeMounts: + - name: webhook-certs + mountPath: "/etc/webhook/certs" + readOnly: true + volumes: + - name: webhook-certs + secret: + secretName: webhook-certs +--- +apiVersion: v1 +kind: Service +metadata: + name: webhook-server +spec: + ports: + - port: 443 + targetPort: 8080 + selector: + app: webhook-server +``` + +### 步骤 3: 创建 Webhook 配置 + +1. **创建 TLS 证书**: + - 为 Webhook 服务创建 TLS 证书,并将其存储在 Kubernetes Secret 中。 + +2. **创建 `MutatingWebhookConfiguration`**: + 编写一个 YAML 文件,为 Kubernetes 注册 Webhook。 + +```yaml +apiVersion: admissionregistration.k8s.io/v1 +kind: MutatingWebhookConfiguration +metadata: + name: pod-mutator +webhooks: + - name: pod-mutator.example.com + clientConfig: + service: + name: webhook-server + namespace: default + path: "/mutate" + caBundle: + rules: + - operations: ["CREATE"] + apiGroups: [""] + apiVersions: ["v1"] + resources: ["pods"] + admissionReviewVersions: ["v1"] + sideEffects: None +``` + +### 注意事项 + +- 确保 Webhook 服务的 TLS 证书正确配置,并与 `MutatingWebhookConfiguration` 中的 `caBundle` 匹配。 +- 此示例只是一个简单的实现,生产环境中需要考虑安全性、性能优化和错误处理等方面。 +- `caBundle` 应该是你的 CA 证书的 base64 编码。 + +通过上述步骤,你可以实现一个简单的 Admission Webhook,它在 Pod 创建时自动添加一个环境变量。这个示例可以作为实现更复杂逻辑的起点。 \ No newline at end of file diff --git a/daliy_note/11.12/泛域名证书自签发.md b/daliy_note/11.12/泛域名证书自签发.md new file mode 100644 index 0000000..5c0a147 --- /dev/null +++ b/daliy_note/11.12/泛域名证书自签发.md @@ -0,0 +1,48 @@ +### 一、自签证书 + +- 生成CA Key证书 + +```bash + openssl genrsa 2048 > ca-key.pem +``` + +- 生成CA Cert证书 + +```bash +openssl req -new -x509 -nodes -days 3650 -key ca-key.pem -out ca-cert.pem -subj=/C=CN/ST=Beijing/L=Beijing/O=ExampleO/OU=ExampleOU/CN=ExampleRootCA/emailAddress=admin@example.com +``` + +- 生成server证书签发请求和server private key证书;在此命令中修改CN(Common Name)用来创建域名证书请求 + +```bash +openssl req -newkey rsa:2048 -nodes -days 3650 -keyout server-key.pem -out server-req.pem -subj=/C=CN/ST=Beijing/L=Beijing/O=ExampleO/OU=ExampleOU/CN=*.example.com/emailAddress=admin@example.com +``` + +- 签发server证书请求,生成server cert证书;注意修改subjectAltName的值 + +```bash +openssl x509 -req -extfile <(printf "subjectAltName=DNS:*.example.com") -days 3650 -CAcreateserial -in server-req.pem -out server-cert.pem -CA ca-cert.pem -CAkey ca-key.pem +``` + +### 二、生成证书报错 + +``` bash +Can't load ./.rnd into RNG 10504:error:2406F079:random number generator:RAND_load_file:Cannot open file:crypto\rand\randfile.c:98:Filename=./.rnd +``` + +- 解决方案 + + ```bash + cd /root(你当前所使用的用户) + openssl rand -writerand .rnd + ``` + + + +### 三、Chrome On MacOS 信任证书 + +> https://segmentfault.com/a/1190000012394467 + +- chrome控制台,安全tab,查看证书 +- 查看证书详细信息,导出下载证书 +- 双击打开下载的证书,选择始终信任 \ No newline at end of file diff --git a/daliy_note/11.14/mongodb索引.md b/daliy_note/11.14/mongodb索引.md new file mode 100644 index 0000000..a50a416 --- /dev/null +++ b/daliy_note/11.14/mongodb索引.md @@ -0,0 +1 @@ +https://blog.51cto.com/u_16099356/11608804 \ No newline at end of file diff --git a/daliy_note/11.4/docker查看无法运行的镜像内的文件.md b/daliy_note/11.4/docker查看无法运行的镜像内的文件.md new file mode 100644 index 0000000..d55fb29 --- /dev/null +++ b/daliy_note/11.4/docker查看无法运行的镜像内的文件.md @@ -0,0 +1,6 @@ +```bash +docker run --rm -it --entrypoint sh manager +``` + + + diff --git a/daliy_note/11.4/kakfa-docker-compose.md b/daliy_note/11.4/kakfa-docker-compose.md new file mode 100644 index 0000000..642cadb --- /dev/null +++ b/daliy_note/11.4/kakfa-docker-compose.md @@ -0,0 +1,208 @@ +### 创建各种文件目录 + +```bash +mkdir -p /tmp/kafka/broker{1..3}/{data,logs} +mkdir -p /tmp/zookeeper/zookeeper/{data,datalog,logs,conf} +``` + +### zookeeper配置文件 + +- vi /tmp/zookeeper/zookeeper/conf/zoo.cfg + +```yaml +# The number of milliseconds of each tick +tickTime=2000 +# The number of ticks that the initial +# synchronization phase can take +initLimit=10 +# The number of ticks that can pass between +# sending a request and getting an acknowledgement +syncLimit=5 +# the directory where the snapshot is stored. +# do not use /tmp for storage, /tmp here is just +# example sakes. +dataDir=/data +dataLogDir=/datalog +# the port at which the clients will connect +clientPort=2181 +# the maximum number of client connections. +# increase this if you need to handle more clients +#maxClientCnxns=60 +# +# Be sure to read the maintenance section of the +# administrator guide before turning on autopurge. +# +# http://zookeeper.apache.org/doc/current/zookeeperAdmin.html#sc_maintenance +# +# The number of snapshots to retain in dataDir +autopurge.snapRetainCount=3 +# Purge task interval in hours +# Set to "0" to disable auto purge feature +autopurge.purgeInterval=1 +``` + +- vi /tmp/zookeeper/zookeeper/conf/log4j.properties + +```yaml +# Define some default values that can be overridden by system properties +zookeeper.root.logger=INFO, CONSOLE +zookeeper.console.threshold=INFO +zookeeper.log.dir=/logs +zookeeper.log.file=zookeeper.log +zookeeper.log.threshold=DEBUG +zookeeper.tracelog.dir=. +zookeeper.tracelog.file=zookeeper_trace.log + +# +# ZooKeeper Logging Configuration +# + +# Format is " (, )+ + +# DEFAULT: console appender only +log4j.rootLogger=${zookeeper.root.logger} + +# Example with rolling log file +#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE + +# Example with rolling log file and tracing +#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE + +# +# Log INFO level and above messages to the console +# +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Threshold=${zookeeper.console.threshold} +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n + +# +# Add ROLLINGFILE to rootLogger to get log file output +# Log DEBUG level and above messages to a log file +log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender +log4j.appender.ROLLINGFILE.Threshold=${zookeeper.log.threshold} +log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/${zookeeper.log.file} + +# Max log file size of 10MB +log4j.appender.ROLLINGFILE.MaxFileSize=10MB +# uncomment the next line to limit number of backup files +log4j.appender.ROLLINGFILE.MaxBackupIndex=10 + +log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout +log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L] - %m%n + + +# +# Add TRACEFILE to rootLogger to get log file output +# Log DEBUG level and above messages to a log file +log4j.appender.TRACEFILE=org.apache.log4j.FileAppender +log4j.appender.TRACEFILE.Threshold=TRACE +log4j.appender.TRACEFILE.File=${zookeeper.tracelog.dir}/${zookeeper.tracelog.file} + +log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout +### Notice we are including log4j's NDC here (%x) +log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} [myid:%X{myid}] - %-5p [%t:%C{1}@%L][%x] - %m%n +``` + + + +### docker-compose 配置文件 + +- vi docker-compose.yaml + +```yaml +version: '2' +services: + zookeeper: + container_name: zookeeper + image: wurstmeister/zookeeper:v1 + pull_policy: never + restart: unless-stopped + hostname: zoo1 + volumes: + - "/tmp/zookeeper/zookeeper/data:/data" + - "/tmp/zookeeper/zookeeper/datalog:/datalog" + - "/tmp/zookeeper/zookeeper/logs:/logs" + - "/tmp/zookeeper/zookeeper/conf:/opt/zookeeper-3.4.13/conf" + ports: + - "2181:2181" + networks: + - kafka + kafka1: + container_name: kafka1 + image: wurstmeister/kafka:v1 + pull_policy: never + ports: + - "8002:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 10.25.76.114 ## 修改:宿主机IP + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://10.25.76.114:8002 ## 修改:宿主机IP + KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181" + KAFKA_ADVERTISED_PORT: 8002 + KAFKA_BROKER_ID: 1 + KAFKA_LOG_DIRS: /kafka/data + volumes: + - /tmp/kafka/broker1/logs:/opt/kafka/logs + - /tmp/kafka/broker1/data:/kafka/data + depends_on: + - zookeeper + networks: + - kafka + kafka2: + container_name: kafka2 + image: wurstmeister/kafka:v1 + pull_policy: never + ports: + - "8003:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 10.25.76.114 ## 修改:宿主机IP + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://10.25.76.114:8003 ## 修改:宿主机IP + KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181" + KAFKA_ADVERTISED_PORT: 8003 + KAFKA_BROKER_ID: 2 + KAFKA_LOG_DIRS: /kafka/data + volumes: + - /tmp/kafka/broker2/logs:/opt/kafka/logs + - /tmp/kafka/broker2/data:/kafka/data + depends_on: + - zookeeper + networks: + - kafka + kafka3: + container_name: kafka3 + image: wurstmeister/kafka:v1 + pull_policy: never + ports: + - "8004:9092" + environment: + KAFKA_ADVERTISED_HOST_NAME: 10.25.76.114 ## 修改:宿主机IP + KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://10.25.76.114:8004 ## 修改:宿主机IP + KAFKA_ZOOKEEPER_CONNECT: "zoo1:2181" + KAFKA_ADVERTISED_PORT: 8004 + KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 3 + KAFKA_MIN_INSYNC_REPLICAS: 2 + KAFKA_BROKER_ID: 3 + KAFKA_LOG_DIRS: /kafka/data + volumes: + - /tmp/kafka/broker3/logs:/opt/kafka/logs + - /tmp/kafka/broker3/data:/kafka/data + depends_on: + - zookeeper + networks: + - kafka + kafka-ui: + image: provectuslabs/kafka-ui:v1 + pull_policy: never + environment: + DYNAMIC_CONFIG_ENABLED: 'true' + ports: + - "8001:8080" ## 暴露端口 + networks: + - kafka + depends_on: + - zookeeper +networks: + kafka: + driver: bridge +``` + diff --git a/daliy_note/11.5/go-alpine缺lib库.md b/daliy_note/11.5/go-alpine缺lib库.md new file mode 100644 index 0000000..8d4a706 --- /dev/null +++ b/daliy_note/11.5/go-alpine缺lib库.md @@ -0,0 +1,12 @@ +### 解决方案一 安装依赖库 + +```bash +apk add libc6-compat +``` + +### 关闭CGO + +```bash +CGO_ENABLE=0 go build +``` + diff --git a/daliy_note/11.6/安装python2.md b/daliy_note/11.6/安装python2.md new file mode 100644 index 0000000..fd32d30 --- /dev/null +++ b/daliy_note/11.6/安装python2.md @@ -0,0 +1,10 @@ +### mac 安装 + +```bash +brew install pyenv +pyenv install 2.7.18 +export PATH="$(pyenv root)/shims:${PATH}" +pyenv global 2.7.18 +python --version +``` + diff --git a/daliy_note/11.8/ssh异常解决.md b/daliy_note/11.8/ssh异常解决.md new file mode 100644 index 0000000..96dcfdf --- /dev/null +++ b/daliy_note/11.8/ssh异常解决.md @@ -0,0 +1,11 @@ +### ssh 报错 no key alg + +> 低版本ssh 连接 高版本 + +- ssh -Q kex 查看服务器支持的加密算法 +- sshd -T |grep kex 查看当前配置 +- 修改ssh配置文件 sudo vim /etc/ssh/sshd_config + - 末尾加入 KexAlgorithms=+diffie-hellman-group1-sha1 + - 末尾加入 HostKeyAlgorithms +ssh-rsa + +- 重启ssh 服务 service sshd restart \ No newline at end of file diff --git a/daliy_note/8月归档/8.21/8.21.md b/daliy_note/8月归档/8.21/8.21.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/8月归档/8.21/as-a-service.md b/daliy_note/8月归档/8.21/as-a-service.md new file mode 100644 index 0000000..dc4c7ba --- /dev/null +++ b/daliy_note/8月归档/8.21/as-a-service.md @@ -0,0 +1,175 @@ +### IaaS(Infrastructure as a Service,基础设施即服务) + +**IaaS** 是最基本的云计算服务模型,提供虚拟化的计算资源,包括服务器、存储和网络资源。用户可以根据需要配置和管理这些资源。 + +**特点**: + +- **灵活性**:用户可以根据需要动态调整计算资源。 +- **控制力**:用户拥有对操作系统、存储和网络等基础设施的控制权。 +- **成本效益**:按使用量付费,避免了前期资本支出。 + +**示例**: + +- **Amazon Web Services (AWS) EC2** +- **Microsoft Azure Virtual Machines** +- **Google Cloud Compute Engine** + +### PaaS(Platform as a Service,平台即服务) + +**PaaS** 提供一个开发和部署应用程序的平台,使开发者可以专注于代码和应用程序本身,而无需管理底层的基础设施。 + +**特点**: + +- **简化开发**:提供开发工具、数据库、操作系统等,简化了开发流程。 +- **自动化管理**:自动处理基础设施管理(如扩展、备份、安全性等)。 +- **协作支持**:通常支持团队协作,简化了开发、测试和部署过程。 + +**示例**: + +- **Google App Engine** +- **Microsoft Azure App Services** +- **Heroku** + +### SaaS(Software as a Service,软件即服务) + +**SaaS** 提供通过互联网访问的软件应用程序,通常是基于订阅模式。用户无需安装、配置或管理软件,只需使用浏览器或客户端访问。 + +**特点**: + +- **易于使用**:无需安装和维护,用户可以直接使用。 +- **按需付费**:通常基于订阅模式,按用户数量或使用量付费。 +- **自动更新**:软件提供商负责维护和更新,确保用户始终使用最新版本。 + +**示例**: + +- **Google Workspace(如 Gmail、Google Docs)** +- **Microsoft Office 365** +- **Salesforce** + +### MaaS(Monitoring as a Service,监控即服务) + +**MaaS** 是一种专注于监控和管理 IT 基础设施和应用程序性能的服务模型。它提供远程监控、告警和报告功能,帮助组织确保其系统的健康和性能。 + +**特点**: + +- **远程监控**:通过云平台远程监控基础设施和应用程序。 +- **实时告警**:提供实时告警和通知,帮助快速响应问题。 +- **报告和分析**:提供详细的性能报告和分析,帮助优化系统。 + +**示例**: + +- **New Relic** +- **Datadog** +- **Amazon CloudWatch** + +### BaaS(Backend as a Service,后端即服务) + +**BaaS** 提供全面的后端服务,帮助开发者快速构建和管理应用程序的后端功能,如数据库、身份验证、推送通知等。 + +**特点**: + +- **快速开发**:简化后端开发,专注于前端和业务逻辑。 +- **可扩展性**:自动处理扩展和负载平衡。 +- **安全性**:提供内置的安全功能。 + +**示例**: + +- **Firebase** +- **AWS Amplify** +- **Parse** + +### DaaS(Desktop as a Service,桌面即服务) + +**DaaS** 提供虚拟桌面环境,使用户可以通过互联网访问和使用桌面操作系统及应用程序。 + +**特点**: + +- **灵活性**:用户可以从任何设备访问虚拟桌面。 +- **简化管理**:集中管理桌面环境,简化 IT 管理任务。 +- **安全性**:提供数据隔离和安全访问控制。 + +**示例**: + +- **Amazon WorkSpaces** +- **Microsoft Windows Virtual Desktop** +- **Citrix Virtual Apps and Desktops** + +### DBaaS(Database as a Service,数据库即服务) + +**DBaaS** 提供托管的数据库服务,用户可以按需创建、管理和扩展数据库,而无需担心底层基础设施。 + +**特点**: + +- **自动化管理**:自动处理数据库的备份、恢复、升级和扩展。 +- **高可用性**:提供内置的高可用性和容灾功能。 +- **按需付费**:根据使用量付费,避免了前期资本支出。 + +**示例**: + +- **Amazon RDS** +- **Google Cloud SQL** +- **Microsoft Azure SQL Database** + +### FaaS(Function as a Service,函数即服务) + +**FaaS** 是一种事件驱动的计算服务,允许开发者上传并执行代码函数,而无需管理服务器。通常被称为 “无服务器计算”。 + +**特点**: + +- **无服务器**:无需管理底层服务器,专注于业务逻辑。 +- **按需执行**:按函数调用次数和执行时间付费。 +- **自动扩展**:根据负载自动扩展和收缩。 + +**示例**: + +- **AWS Lambda** +- **Google Cloud Functions** +- **Microsoft Azure Functions** + +### NaaS(Network as a Service,网络即服务) + +**NaaS** 提供基于云的网络服务,使用户可以按需配置和管理网络资源,如虚拟专用网(VPN)、负载均衡和防火墙。 + +**特点**: + +- **按需配置**:灵活配置网络资源,满足不同需求。 +- **可扩展性**:根据需求自动扩展网络容量。 +- **安全性**:提供内置的网络安全功能。 + +**示例**: + +- **Amazon VPC** +- **Microsoft Azure Virtual Network** +- **Google Cloud Virtual Private Cloud (VPC)** + +### STaaS(Storage as a Service,存储即服务) + +**STaaS** 提供基于云的存储服务,使用户可以按需存储和管理数据,通常提供对象存储、块存储和文件存储等类型。 + +**特点**: + +- **按需存储**:根据数据量付费,灵活扩展存储容量。 +- **高可用性**:提供高可用性和容灾功能。 +- **数据安全**:提供加密和访问控制,确保数据安全。 + +**示例**: + +- **Amazon S3** +- **Google Cloud Storage** +- **Microsoft Azure Blob Storage** + +### AIaaS(Artificial Intelligence as a Service,人工智能即服务) + +**AIaaS** 提供基于云的人工智能服务,使用户可以按需访问和使用 AI 模型和工具,如机器学习、自然语言处理和计算机视觉等。 + +**特点**: + +- **即用型 AI**:无需开发复杂的 AI 模型,直接使用现成的服务。 +- **可扩展性**:根据需求自动扩展计算资源。 +- **集成性**:易于集成到现有应用程序和工作流程中。 + +**示例**: + +- **IBM Watson** +- **Google AI Platform** +- **Microsoft Azure AI** \ No newline at end of file diff --git a/daliy_note/8月归档/8.21/servlet.md b/daliy_note/8月归档/8.21/servlet.md new file mode 100644 index 0000000..26c2192 --- /dev/null +++ b/daliy_note/8月归档/8.21/servlet.md @@ -0,0 +1,237 @@ +Servlet 容器是一个用于管理和执行 Java Servlet、JSP(JavaServer Pages)和其他基于 Java 的 web 组件的运行时环境。它是 Java EE(企业版)规范的一部分,负责处理客户端请求、管理 servlet 生命周期、提供网络服务和其他运行时环境支持。 + +### Servlet 容器的功能 + +1. **请求处理**: + - 接收客户端(通常是浏览器)发送的 HTTP 请求,并根据 URL 路径将请求分发到相应的 servlet 进行处理。 + - 处理 HTTP 请求和响应,包括 GET、POST、PUT、DELETE 等方法。 +2. **Servlet 生命周期管理**: + - 管理 servlet 的生命周期,包括加载、初始化、处理请求、销毁等。 + - 调用 servlet 的 init() 方法进行初始化,service() 方法处理请求,destroy() 方法进行销毁。 +3. **会话管理**: + - 提供会话管理功能,通过 cookies 或 URL 重写来跟踪用户会话。 + - 维护会话状态,确保多次请求之间的数据一致性。 +4. **安全性**: + - 提供认证和授权机制,确保只有授权用户才能访问受保护的资源。 + - 支持基于角色的访问控制(RBAC)。 +5. **并发处理**: + - 处理多个客户端请求的并发性,确保高效的资源使用和响应时间。 + - 提供线程管理和同步机制,确保数据一致性和线程安全。 +6. **资源管理**: + - 管理静态资源(如 HTML、CSS、JavaScript 文件)和动态资源(如 servlet、JSP 页面)。 + - 提供资源的加载和缓存功能,优化性能。 +7. **日志记录和监控**: + - 提供日志记录功能,记录请求、响应和错误信息,便于调试和监控。 + - 支持监控和管理工具,帮助管理员监控服务器性能和健康状态。 + +### 常见的 Servlet 容器 + +1. **Apache Tomcat**: + - 开源的 Servlet 容器,广泛用于开发和部署 Java web 应用。 + - 支持 Servlet、JSP 和 WebSocket 等规范。 +2. **Jetty**: + - 轻量级、高性能的 Servlet 容器,适用于嵌入式系统和微服务架构。 + - 支持 HTTP/2、WebSocket 等现代协议。 +3. **GlassFish**: + - 完整的 Java EE 应用服务器,支持所有 Java EE 规范。 + - 提供企业级功能,如事务管理、消息驱动 Bean 和 JCA 连接器等。 +4. **WildFly(原 JBoss AS)**: + - 另一个完整的 Java EE 应用服务器,支持所有 Java EE 规范。 + - 提供高性能和高可用性的企业级功能。 +5. **IBM WebSphere**: + - 企业级 Java EE 应用服务器,提供高可靠性和高性能。 + - 支持广泛的企业功能和集成能力。 +6. **Oracle WebLogic**: + - 企业级 Java EE 应用服务器,提供高可用性和可扩展性。 + - 支持广泛的企业功能和集成能力。 + +### Servlet 容器的工作原理 + +1. **启动**: + - Servlet 容器启动时,读取配置文件(如 `web.xml` 或注解配置),加载和初始化所有配置的 servlet 和其他组件。 +2. **请求处理**: + - 当客户端发送请求时,Servlet 容器接收请求并解析 URL 路径,将请求分发到相应的 servlet。 + - 调用 servlet 的 service() 方法,根据请求方法(GET、POST 等)调用 doGet() 或 doPost() 等具体方法处理请求。 +3. **响应生成**: + - Servlet 处理请求后,生成响应(如 HTML、JSON 数据)并通过 HttpServletResponse 对象发送回客户端。 +4. **会话管理**: + - Servlet 容器维护会话状态,通过 cookies 或 URL 重写跟踪用户会话。 + - 提供 HttpSession 对象,允许 servlet 存储和检索会话数据。 +5. **销毁**: + - 在应用程序关闭或 servlet 需要卸载时,Servlet 容器调用 servlet 的 destroy() 方法进行清理工作。 + +### 总结 + +Servlet 容器是 Java web 应用程序的核心组件,提供了请求处理、生命周期管理、会话管理、安全性、并发处理、资源管理和日志记录等功能。常见的 Servlet 容器包括 Apache Tomcat、Jetty、GlassFish、WildFly、IBM WebSphere 和 Oracle WebLogic 等。了解 Servlet 容器的工作原理和功能,有助于开发和部署高效、可靠的 Java web 应用程序。 + + + +--- + +部分方法 + +### `HttpServletRequest` 对象的主要用途 + +1. **获取请求参数**: + + - 使用 `getParameter(String name)` 方法获取单个请求参数的值。 + - 使用 `getParameterValues(String name)` 方法获取具有相同名称的多个请求参数的值(返回字符串数组)。 + - 使用 `getParameterMap()` 方法获取所有请求参数的键值对(返回 `Map`)。 + + ``` + String username = request.getParameter("username"); + String[] hobbies = request.getParameterValues("hobbies"); + Map parameterMap = request.getParameterMap(); + ``` + + + +2. **获取请求头信息**: + + - 使用 `getHeader(String name)` 方法获取指定请求头的值。 + - 使用 `getHeaders(String name)` 方法获取具有相同名称的多个请求头的值(返回 `Enumeration`)。 + - 使用 `getHeaderNames()` 方法获取所有请求头的名称(返回 `Enumeration`)。 + + ``` + String userAgent = request.getHeader("User-Agent"); + Enumeration headerNames = request.getHeaderNames(); + ``` + + + +3. **获取请求路径和 URL 信息**: + + - 使用 `getRequestURI()` 方法获取请求的 URI 部分。 + - 使用 `getRequestURL()` 方法获取请求的完整 URL。 + - 使用 `getContextPath()` 方法获取应用程序的上下文路径。 + - 使用 `getServletPath()` 方法获取 servlet 的路径。 + + ``` + String uri = request.getRequestURI(); + StringBuffer url = request.getRequestURL(); + String contextPath = request.getContextPath(); + String servletPath = request.getServletPath(); + ``` + + + +4. **获取请求方法**: + + - 使用 `getMethod()` 方法获取请求的方法(如 GET、POST、PUT、DELETE 等)。 + + ``` + String method = request.getMethod(); + ``` + + + +5. **获取请求体数据**: + + - 使用 `getReader()` 方法获取 `BufferedReader` 对象,从而读取请求体的数据(用于处理文本数据)。 + - 使用 `getInputStream()` 方法获取 `ServletInputStream` 对象,从而读取请求体的数据(用于处理二进制数据)。 + + ``` + BufferedReader reader = request.getReader(); + // 或 + ServletInputStream inputStream = request.getInputStream(); + ``` + + + +6. **会话管理**: + + - 使用 `getSession()` 方法获取当前会话(如果不存在则创建一个新的会话)。 + - 使用 `getSession(boolean create)` 方法获取当前会话(根据 `create` 参数决定是否创建新的会话)。 + + ``` + HttpSession session = request.getSession(); + ``` + + + +7. **处理请求属性**: + + - 使用 `getAttribute(String name)` 方法获取请求属性。 + - 使用 `setAttribute(String name, Object o)` 方法设置请求属性。 + - 使用 `removeAttribute(String name)` 方法移除请求属性。 + + ``` + Object attr = request.getAttribute("attrName"); + request.setAttribute("attrName", someObject); + request.removeAttribute("attrName"); + ``` + + + +8. **其他有用的方法**: + + - `getRemoteAddr()`:获取客户端的 IP 地址。 + - `getRemoteHost()`:获取客户端的主机名。 + - `getCookies()`:获取请求中的所有 cookies(返回 `Cookie[]`)。 + - `getLocale()`:获取客户端的首选语言环境。 + - `getQueryString()`:获取请求的查询字符串。 + + ``` + String remoteAddr = request.getRemoteAddr(); + String remoteHost = request.getRemoteHost(); + Cookie[] cookies = request.getCookies(); + Locale locale = request.getLocale(); + String queryString = request.getQueryString(); + ``` + + + +### 示例代码 + +下面是一个简单的示例 servlet,展示如何使用 `HttpServletRequest` 对象获取请求信息: + +``` +import javax.servlet.ServletException; +import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.io.PrintWriter; + +@WebServlet("/example") +public class ExampleServlet extends HttpServlet { + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + response.setContentType("text/html"); + PrintWriter out = response.getWriter(); + + // 获取请求参数 + String username = request.getParameter("username"); + + // 获取请求头 + String userAgent = request.getHeader("User-Agent"); + + // 获取请求路径和 URL + String uri = request.getRequestURI(); + StringBuffer url = request.getRequestURL(); + + // 获取请求方法 + String method = request.getMethod(); + + // 获取客户端 IP 地址 + String remoteAddr = request.getRemoteAddr(); + + // 输出信息到响应 + out.println(""); + out.println("

Request Information

"); + out.println("

Username: " + username + "

"); + out.println("

User-Agent: " + userAgent + "

"); + out.println("

Request URI: " + uri + "

"); + out.println("

Request URL: " + url.toString() + "

"); + out.println("

Request Method: " + method + "

"); + out.println("

Client IP: " + remoteAddr + "

"); + out.println(""); + } +} +``` + + + +### 总结 + +`HttpServletRequest` 对象是处理客户端 HTTP 请求的关键对象。它提供了各种方法来访问请求参数、请求头、请求路径、会话信息和请求体数据等。通过正确使用 `HttpServletRequest` 对象,开发者可以有效地处理和响应客户端请求,构建功能丰富的 web 应用程序。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.22/8.22.md b/daliy_note/8月归档/8.22/8.22.md new file mode 100644 index 0000000..65ae569 --- /dev/null +++ b/daliy_note/8月归档/8.22/8.22.md @@ -0,0 +1 @@ +SOP 是 Standard Operating Procedure(标准操作程序)的缩写。SOP 是指一组详细的、书面的指示,旨在帮助执行某些操作或任务,以确保一致性、效率和质量。SOP 常用于各种行业和领域,包括制造业、医疗保健、信息技术、制药、食品服务和政府机构等。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.22/alertmanager.md b/daliy_note/8月归档/8.22/alertmanager.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/8月归档/8.22/golang-runtime.md b/daliy_note/8月归档/8.22/golang-runtime.md new file mode 100644 index 0000000..533962e --- /dev/null +++ b/daliy_note/8月归档/8.22/golang-runtime.md @@ -0,0 +1,182 @@ +`runtime.SetBlockProfileRate` 是 Go 语言中的一个函数,用于控制阻塞操作的采样和分析。阻塞操作包括通道操作、锁的获取和等待等,这些操作可能会影响程序的性能和并发性。 + +## 用途 + +`runtime.SetBlockProfileRate` 的主要用途是开启和控制阻塞操作的采样,以便于对程序的性能进行分析和优化。通过设置采样率,可以捕获阻塞操作的样本,并生成阻塞分析报告(block profile)。这些报告可以帮助你识别和诊断程序中的性能瓶颈。 + +## 函数签名 + +``` +func SetBlockProfileRate(rate int) +``` + + + +- `rate`: 设置采样的阻塞事件数。当 `rate` 为正数时,每发生 `rate` 次阻塞事件,会采样一次。当 `rate` 为零时,禁用阻塞分析。 + +## 示例 + +以下是一个简单的示例,展示了如何使用 `runtime.SetBlockProfileRate` 进行阻塞分析: + +``` +package main + +import ( + "log" + "runtime" + "runtime/pprof" + "os" + "time" +) + +func main() { + // 设置阻塞分析的采样率,这里设置为 1 表示每次阻塞都会采样 + runtime.SetBlockProfileRate(1) + + // 创建一个通道用于测试阻塞操作 + ch := make(chan bool) + + // 启动一个 goroutine 并阻塞在接收操作上 + go func() { + <-ch + }() + + // 等待一段时间以产生一些阻塞事件 + time.Sleep(2 * time.Second) + + // 停止阻塞分析 + runtime.SetBlockProfileRate(0) + + // 创建一个文件保存阻塞分析报告 + f, err := os.Create("block_profile.out") + if err != nil { + log.Fatal("could not create block profile: ", err) + } + defer f.Close() + + // 写入阻塞分析报告 + if err := pprof.Lookup("block").WriteTo(f, 0); err != nil { + log.Fatal("could not write block profile: ", err) + } + + log.Println("Block profile saved to block_profile.out") +} +``` + + + +### 运行示例 + +1. 运行上述代码。 +2. 生成的阻塞分析报告会保存在当前目录下的 `block_profile.out` 文件中。 +3. 使用 `go tool pprof` 工具查看分析报告: + +``` +go tool pprof block_profile.out +``` + + + +在 `pprof` 交互模式下,你可以使用命令如 `top`、`list` 等来查看阻塞事件的详细信息。 + +## 总结 + +`runtime.SetBlockProfileRate` 是一个强大的工具,能够帮助开发者分析和优化 Go 程序中的阻塞操作。通过调整采样率和生成阻塞分析报告,你可以更好地理解程序的性能瓶颈,并做出相应的优化。 + + + +--- + +`runtime.SetMutexProfileFraction` 是 Go 语言中的一个函数,用于控制互斥锁(mutex)竞争事件的采样和分析。互斥锁竞争发生在多个 Goroutine 争用同一个锁的情况下,这可能会影响程序的并发性能。 + +## 用途 + +`runtime.SetMutexProfileFraction` 的主要用途是开启和控制互斥锁竞争事件的采样,以便于对程序的锁竞争情况进行分析和优化。通过设置采样率,可以捕获锁竞争事件的样本,并生成互斥锁竞争分析报告(mutex profile)。这些报告可以帮助你识别和诊断程序中的锁竞争瓶颈。 + +## 函数签名 + +``` +func SetMutexProfileFraction(rate int) int +``` + + + +- `rate`: 设置采样的互斥锁竞争事件数。当 `rate` 为正数时,每发生 `rate` 次竞争事件,会采样一次。当 `rate` 为零时,禁用互斥锁竞争分析。 +- 返回值:返回先前设置的采样率。 + +## 示例 + +以下是一个简单的示例,展示了如何使用 `runtime.SetMutexProfileFraction` 进行互斥锁竞争分析: + +``` +package main + +import ( + "log" + "os" + "runtime" + "runtime/pprof" + "sync" + "time" +) + +func main() { + // 设置互斥锁竞争分析的采样率,这里设置为 1 表示每次竞争都会采样 + prevRate := runtime.SetMutexProfileFraction(1) + log.Printf("Previous mutex profile rate: %d\n", prevRate) + + // 创建一个互斥锁用于测试竞争操作 + var mu sync.Mutex + + // 启动多个 goroutine 并竞争获取锁 + for i := 0; i < 10; i++ { + go func(i int) { + for j := 0; j < 1000; j++ { + mu.Lock() + time.Sleep(10 * time.Millisecond) // 模拟一些工作 + mu.Unlock() + } + }(i) + } + + // 等待一段时间以产生一些竞争事件 + time.Sleep(5 * time.Second) + + // 停止互斥锁竞争分析 + runtime.SetMutexProfileFraction(0) + + // 创建一个文件保存互斥锁竞争分析报告 + f, err := os.Create("mutex_profile.out") + if err != nil { + log.Fatal("could not create mutex profile: ", err) + } + defer f.Close() + + // 写入互斥锁竞争分析报告 + if err := pprof.Lookup("mutex").WriteTo(f, 0); err != nil { + log.Fatal("could not write mutex profile: ", err) + } + + log.Println("Mutex profile saved to mutex_profile.out") +} +``` + + + +### 运行示例 + +1. 运行上述代码。 +2. 生成的互斥锁竞争分析报告会保存在当前目录下的 `mutex_profile.out` 文件中。 +3. 使用 `go tool pprof` 工具查看分析报告: + +``` +go tool pprof mutex_profile.out +``` + + + +在 `pprof` 交互模式下,你可以使用命令如 `top`、`list` 等来查看互斥锁竞争事件的详细信息。 + +## 总结 + +`runtime.SetMutexProfileFraction` 是一个有用的工具,能够帮助开发者分析和优化 Go 程序中的互斥锁竞争情况。通过调整采样率和生成互斥锁竞争分析报告,你可以更好地理解程序的锁竞争瓶颈,并做出相应的优化。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.22/linux$.md b/daliy_note/8月归档/8.22/linux$.md new file mode 100644 index 0000000..b97ed46 --- /dev/null +++ b/daliy_note/8月归档/8.22/linux$.md @@ -0,0 +1,90 @@ +在 Linux 上,通过命令行执行命令时,命令的退出状态码(exit status)存储在特殊的环境变量 `$?` 中。退出状态码为 0 表示命令成功执行,非 0 表示命令执行失败。 + +如果你希望在执行命令后查看其退出状态码,可以使用如下方法: + +1. **执行命令**: + + ``` + your_command_here + ``` + + + +2. **查看退出状态码**: + + ``` + echo $? + ``` + + + +例如: + +``` +ls /some/directory +echo $? +``` + + + +如果目录存在且命令成功,`echo $?` 将输出 `0`。如果目录不存在或发生错误,`echo $?` 将输出一个非零值,例如 `2`。 + +### 示例 + +假设你运行以下命令来列出一个不存在的目录: + +``` +ls /nonexistent_directory +``` + + + +然后你可以查看退出状态码: + +``` +echo $? +``` + + + +假设目录 `/nonexistent_directory` 不存在,`ls` 命令将失败,`echo $?` 输出: + +``` +2 +``` + + + +这表明 `ls` 命令未成功执行。 + +### 常见的退出状态码 + +- `0`:命令成功执行。 +- `1`:一般性错误,例如权限问题。 +- `2`:误用命令内建功能。 +- `127`:命令未找到。 + +### 示例脚本 + +你可以将这些命令放入一个脚本中,以便更方便地查看退出状态码: + +``` +#!/bin/bash + +your_command_here +status=$? + +if [ $status -eq 0 ]; then + echo "Command succeeded" +else + echo "Command failed with exit status $status" +fi +``` + + + +这个脚本首先执行 `your_command_here`,然后存储退出状态码,并根据状态码输出相应的消息。 + +### 总结 + +通过使用 `$?` 变量,你可以轻松地获取上一个命令的退出状态码,并根据该状态码执行相应的操作。这在编写脚本时特别有用,可以帮助你处理错误情况并进行调试。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.22/linux获取上一个命令的执行结果.md b/daliy_note/8月归档/8.22/linux获取上一个命令的执行结果.md new file mode 100644 index 0000000..44905e3 --- /dev/null +++ b/daliy_note/8月归档/8.22/linux获取上一个命令的执行结果.md @@ -0,0 +1,125 @@ +在 Linux 上,如果你想获取上一次命令的输出结果,有几种方法可以实现。这些方法包括使用命令替换、变量存储和管道重定向等技术。以下是几种常用的方法: + +### 方法一:使用命令替换 + +你可以使用命令替换(command substitution)将命令的输出存储到一个变量中。例如: + +``` +result=$(your_command_here) +echo "$result" +``` + + + +### 示例 + +假设你要获取 `ls` 命令的输出并存储在变量中: + +``` +result=$(ls /path/to/directory) +echo "$result" +``` + + + +### 方法二:使用临时文件 + +你可以将命令的输出重定向到一个临时文件,然后读取该文件的内容。例如: + +``` +your_command_here > temp_output.txt +cat temp_output.txt +``` + + + +### 示例 + +假设你要获取 `ls` 命令的输出并存储在临时文件中: + +``` +ls /path/to/directory > temp_output.txt +cat temp_output.txt +``` + + + +### 方法三:使用管道和 `tee` 命令 + +`tee` 命令可以将输入内容同时写入标准输出和一个文件中。你可以利用这个特性将命令的输出存储到一个文件,同时显示在终端上。例如: + +``` +your_command_here | tee temp_output.txt +``` + + + +### 示例 + +假设你要获取 `ls` 命令的输出并同时显示在终端和存储在文件中: + +``` +ls /path/to/directory | tee temp_output.txt +``` + + + +### 方法四:重定向标准输出和标准错误 + +你可以将命令的标准输出和标准错误重定向到一个文件,然后读取该文件。例如: + +``` +your_command_here > temp_output.txt 2>&1 +cat temp_output.txt +``` + + + +### 示例 + +假设你要获取 `ls` 命令的输出和错误信息并存储在一个文件中: + +``` +ls /path/to/directory > temp_output.txt 2>&1 +cat temp_output.txt +``` + + + +### 选择合适的方法 + +根据你的具体需求,可以选择以上方法之一。如果你只需要在脚本中临时存储并使用命令的输出,使用命令替换(方法一)通常是最方便的。如果你需要将输出内容保存在文件中供以后使用,可以选择方法二或方法三。 + +### 例子 + +假设你要获取当前目录下的所有文件和目录列表并存储在变量中: + +``` +result=$(ls) +echo "Current directory listing:" +echo "$result" +``` + + + +如果你要在脚本中存储并处理命令的输出: + +``` +#!/bin/bash + +# 获取当前目录下的所有文件和目录列表 +result=$(ls) + +# 打印输出 +echo "Current directory listing:" +echo "$result" + +# 进一步处理输出 +# 例如统计文件数 +file_count=$(echo "$result" | wc -l) +echo "Number of files and directories: $file_count" +``` + + + +通过这种方式,你可以灵活地获取并处理上一次命令的执行结果。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.22/makefile.md b/daliy_note/8月归档/8.22/makefile.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/8月归档/8.23/8.23.md b/daliy_note/8月归档/8.23/8.23.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/8月归档/8.23/runmaxpos.md b/daliy_note/8月归档/8.23/runmaxpos.md new file mode 100644 index 0000000..7ba7378 --- /dev/null +++ b/daliy_note/8月归档/8.23/runmaxpos.md @@ -0,0 +1,85 @@ +`runtime.GOMAXPROCS` 是 Go 语言标准库中的一个函数,用于设置和获取可以同时执行的最大 CPU 数量。它是 Go 运行时调度器的重要部分,影响 Go 程序的并发性能。 + +### 用法 + +`runtime.GOMAXPROCS` 的签名如下: + +``` +func GOMAXPROCS(n int) int +``` + + + +- `n`:要设置的最大 CPU 核数。如果 `n` 为 0 或负数,`GOMAXPROCS` 不会改变当前的设置,只返回当前的值。 +- 返回值:函数会返回之前的 `GOMAXPROCS` 设置值。 + +### 示例代码 + +以下是一个简单的示例展示如何使用 `runtime.GOMAXPROCS`: + +``` +package main + +import ( + "fmt" + "runtime" +) + +func main() { + // 获取当前的 GOMAXPROCS 设置 + currentProcs := runtime.GOMAXPROCS(0) + fmt.Printf("Current GOMAXPROCS: %d\n", currentProcs) + + // 设置 GOMAXPROCS 为 4 + previousProcs := runtime.GOMAXPROCS(4) + fmt.Printf("Previous GOMAXPROCS: %d\n", previousProcs) + + // 再次获取当前的 GOMAXPROCS 设置 + currentProcs = runtime.GOMAXPROCS(0) + fmt.Printf("New GOMAXPROCS: %d\n", currentProcs) +} +``` + + + +### 解释 + +1. **获取当前 `GOMAXPROCS` 设置**: + + ``` + currentProcs := runtime.GOMAXPROCS(0) + ``` + + + + 调用 `runtime.GOMAXPROCS(0)`,传入 0 表示不改变当前设置,只返回当前的 `GOMAXPROCS` 值。 + +2. **设置 `GOMAXPROCS`**: + + ``` + previousProcs := runtime.GOMAXPROCS(4) + ``` + + + + 调用 `runtime.GOMAXPROCS(4)`,将最大并发执行的 CPU 数量设置为 4。函数返回之前的 `GOMAXPROCS` 值。 + +3. **再次获取当前的 `GOMAXPROCS` 设置**: + + ``` + currentProcs = runtime.GOMAXPROCS(0) + ``` + + + + 再次获取当前的 `GOMAXPROCS` 值,验证新的设置是否生效。 + +### 注意事项 + +- **默认值**:在 Go 1.5 之前,`GOMAXPROCS` 的默认值是 1,这意味着默认情况下 Go 程序只能使用一个 CPU 核。在 Go 1.5 及以后,默认值是运行时机器上的 CPU 核数。 +- **性能影响**:设置过高的 `GOMAXPROCS` 值可能导致过多的上下文切换和资源争用,反而会降低程序性能。一般来说,`GOMAXPROCS` 的值设置为物理 CPU 核数是一个合理的选择。 +- **并发模型**:Go 的调度器使用的是 M:N 模型,其中 M 个 goroutine 映射到 N 个 OS 线程上。`GOMAXPROCS` 控制的是可以同时运行的 OS 线程数。 + +### 总结 + +`runtime.GOMAXPROCS` 是 Go 语言中的一个重要函数,用于设置和获取可以同时执行的最大 CPU 数量。合理设置 `GOMAXPROCS` 可以显著影响 Go 程序的并发性能。通过理解和使用 `GOMAXPROCS`,开发者可以更好地控制 Go 程序的调度行为,优化程序性能。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.23/struct_compare.md b/daliy_note/8月归档/8.23/struct_compare.md new file mode 100644 index 0000000..f3da20a --- /dev/null +++ b/daliy_note/8月归档/8.23/struct_compare.md @@ -0,0 +1,34 @@ +- 别名可以直接比较 type MyStudent1 = Student +- 属性字段一样,可以通过类型转换进行比较 + +```Go +package main + +import "fmt" + +type Student struct { + Name string +} + +type StudentAnother struct { + Name string +} + +type MyStudent Student +type MyStudent1 = Student + +func main() { + s1 := Student{Name: "1"} + s2 := MyStudent{Name: "1"} + s3 := MyStudent1{Name: "1"} + s4 := StudentAnother{Name: "1"} + fmt.Printf("s1: %T\n", s1) + fmt.Printf("s2: %T\n", s2) + fmt.Printf("s3: %T\n", s3) + fmt.Printf("s4: %T\n", s4) + // fmt.Printf("(s1 == s2): %v\n", (s1 == s2)) + fmt.Printf("(s1 == s3): %v\n", (s1 == s3)) + fmt.Printf("(s1 == Student(s4)): %v\n", (s1 == Student(s4))) +} +``` + diff --git a/daliy_note/8月归档/8.26/8.26.md b/daliy_note/8月归档/8.26/8.26.md new file mode 100644 index 0000000..fb5a924 --- /dev/null +++ b/daliy_note/8月归档/8.26/8.26.md @@ -0,0 +1 @@ +![image-20240826203425108](/Users/sunqi19/Library/Application Support/typora-user-images/image-20240826203425108.png) \ No newline at end of file diff --git a/daliy_note/8月归档/8.26/nfs.md b/daliy_note/8月归档/8.26/nfs.md new file mode 100644 index 0000000..883116b --- /dev/null +++ b/daliy_note/8月归档/8.26/nfs.md @@ -0,0 +1,35 @@ +### centos 挂载 + +#### 1. 安装 nfs-utils + +```bash +yum install nfs-utils -y +``` + +#### 2. 挂载目录 + +```bash +mount -t nfs 192.168.0.4:/export/share /mnt/nfs +``` + +#### 3. 检查挂载 + +```bash +df -h | grep nfs +``` + +#### 4. 卸载 + +```bash +umount /mnt/nfs +``` + +### ubuntu挂载 + +#### 1. 安装 nfs-common + +```bash +apt install nfs-common +``` + +#### 2. 剩余同上 diff --git a/daliy_note/8月归档/8.26/redis.md b/daliy_note/8月归档/8.26/redis.md new file mode 100644 index 0000000..5b7dac5 --- /dev/null +++ b/daliy_note/8月归档/8.26/redis.md @@ -0,0 +1,115 @@ +在 Redis 中,设置认证(AUTH)可以为你的 Redis 服务器增加一层安全性。通过设置密码,只有提供正确密码的客户端才能访问 Redis 服务器。以下是设置和使用 Redis 认证的步骤: + +### 1. 修改 Redis 配置文件 + +首先,找到你的 Redis 配置文件(通常是 `redis.conf`)。 + +在配置文件中,找到以下行(可能是被注释掉的): + +``` +# requirepass foobared +``` + + + +将其取消注释并设置一个强密码,例如: + +``` +requirepass YourStrongPasswordHere +``` + + + +保存配置文件并重启 Redis 服务器以使更改生效: + +``` +sudo systemctl restart redis-server +``` + + + +或者,如果你是手动启动 Redis 的,可以使用: + +``` +redis-server /path/to/your/redis.conf +``` + + + +### 2. 使用 AUTH 连接 Redis 服务器 + +当 Redis 设置了密码之后,客户端需要在连接时提供密码。以下是不同客户端的示例: + +#### 使用 redis-cli + +``` +redis-cli +> AUTH YourStrongPasswordHere +> PING +``` + + + +如果认证成功,`PING` 命令将返回 `PONG`。 + +#### 使用 Python 的 redis-py + +``` +import redis + +r = redis.Redis(host='localhost', port=6379, password='YourStrongPasswordHere') +print(r.ping()) # 如果认证成功,将返回 True +``` + + + +#### 使用 Node.js 的 ioredis + +``` +const Redis = require('ioredis'); +const redis = new Redis({ + host: 'localhost', + port: 6379, + password: 'YourStrongPasswordHere' +}); + +redis.ping().then(result => { + console.log(result); // 如果认证成功,将返回 'PONG' +}); +``` + + + +### 3. 在运行时设置密码 + +如果你需要在运行时设置或更改密码,可以使用 `CONFIG SET` 命令: + +``` +redis-cli +> CONFIG SET requirepass YourNewStrongPasswordHere +``` + + + +请注意,使用 `CONFIG SET` 命令设置的密码不会持久化到配置文件中。如果 Redis 服务器重启,设置将丢失。因此,最好在 `redis.conf` 中设置密码以确保持久性。 + +### 4. 移除密码 + +如果你决定移除密码保护,可以将 `requirepass` 设置为空字符串: + +``` +redis-cli +> CONFIG SET requirepass "" +``` + + + +或者在配置文件中注释掉 `requirepass` 行并重启 Redis 服务器。 + +### 5. 注意事项 + +1. **安全性**:确保使用强密码,并限制 Redis 服务器的网络访问权限。 +2. **持久化**:修改 `redis.conf` 文件确保密码在重启后仍然有效。 +3. **监控**:定期检查 Redis 日志和访问记录,确保未发生未经授权的访问。 + +通过这些步骤,你可以为 Redis 服务器增加一层安全保护,防止未经授权的访问。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.26/samba.md b/daliy_note/8月归档/8.26/samba.md new file mode 100644 index 0000000..fb40558 --- /dev/null +++ b/daliy_note/8月归档/8.26/samba.md @@ -0,0 +1,13 @@ +### 解决windows系统挂载omv的samba共享不能执行.exe文件的问题 + +- 打开:服务-->SMB/CIFS-->设置,在“高级设置”中的“扩展选项”中填入“acl allow execute always = True”,保存配置就可以 + + ![~](/Users/sunqi19/Library/Application Support/typora-user-images/image-20240826112255699.png) + +### 解决 Windows LTSC 版本无法连接 SMB 共享目录 + +- 因为在 LTSC 系统上默认禁止不安全的来宾的访问,需要手动配置组策略 +- 快捷键 Win + R 运行,输入 gpedit.msc 打开本地组策略编辑器 +- 依次展开「管理模版」->「网络」->「Lanman 工作站」标签 + +![image-20240826112527291](/Users/sunqi19/Library/Application Support/typora-user-images/image-20240826112527291.png) \ No newline at end of file diff --git a/daliy_note/8月归档/8.27/alertmanager.md b/daliy_note/8月归档/8.27/alertmanager.md new file mode 100644 index 0000000..21d760c --- /dev/null +++ b/daliy_note/8月归档/8.27/alertmanager.md @@ -0,0 +1,11 @@ +### dispatcher + +dispatch/dispatch.go + +### route + +dispatch/route.go + +### tool + +alertmanager/scripts/tools.go \ No newline at end of file diff --git a/daliy_note/8月归档/8.28/vim.md b/daliy_note/8月归档/8.28/vim.md new file mode 100644 index 0000000..6f1828f --- /dev/null +++ b/daliy_note/8月归档/8.28/vim.md @@ -0,0 +1,15 @@ +### Esc + +- ctrl + [ + +### jj映射为Esc + +- .vimrc + +```sh +inoremap jj +``` + +### 引用 + +- https://placeless.net/blog/map-jj-to-esc-for-zsh-vim-vscode-ipython-jupyterlab \ No newline at end of file diff --git a/daliy_note/8月归档/8.29/8.29.md b/daliy_note/8月归档/8.29/8.29.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/8月归档/8.29/mysql.md b/daliy_note/8月归档/8.29/mysql.md new file mode 100644 index 0000000..0f6585d --- /dev/null +++ b/daliy_note/8月归档/8.29/mysql.md @@ -0,0 +1,49 @@ +### mysql where 条件大小写 + +在 MySQL 中,`WHERE` 条件的大小写敏感性取决于所使用的字符集和排序规则(collation)。 + +### 1. 默认行为 + +- **大小写不敏感**:在大多数情况下,MySQL 的默认字符集 `latin1`和 `utf8` 使用的排序规则(如 `latin1_swedish_ci` 或 `utf8_general_ci`)都是不区分大小写的。这意味着在这些排序规则下,`WHERE` 子句将不会区分大小写。例如: + + ``` + SELECT * FROM users WHERE username = 'JohnDoe'; + ``` + + 该查询将返回 `johnDoe`、`JOHNDOE` 和 `JohnDoe` 等所有变体。 + +### 2. 大小写敏感 + +- **使用大小写敏感的排序规则**:如果你想要 `WHERE` 条件区分大小写,可以使用大小写敏感的排序规则,例如 `latin1_bin` 或 `utf8_bin`。例如: + + ``` + SELECT * FROM users WHERE BINARY username = 'JohnDoe'; + ``` + + + + 使用 `BINARY` 关键字会强制 MySQL 将 `username` 列的值视为二进制字符串,从而实现大小写敏感的比较。 + +- **指定排序规则**:你也可以在查询中显式指定排序规则。例如: + + ``` + SELECT * FROM users WHERE username COLLATE utf8_bin = 'JohnDoe'; + ``` + +### 3. 修改表字符集 + +- utf8_general_ci --不区分大小写 +- utf8_bin–区分大小写 + +```sql +ALTER TABLE temp_01 MODIFY COLUMN browser VARCHAR(50) CHARACTER SET utf8 COLLATE utf8_bin DEFAULT NULL; +Query OK, 180 rows affected (0.01 sec) +Records: 180 Duplicates: 0 Warnings: 0 +``` + + + +### 4. 总结 + +- 默认情况下,MySQL 的 `WHERE` 条件是大小写不敏感的,除非使用了二进制比较或指定了大小写敏感的排序规则。 +- 如果想要进行大小写敏感的比较,可以使用 `BINARY` 关键字或者指定适当的排序规则。 \ No newline at end of file diff --git a/daliy_note/8月归档/8.30/go_deep_copy.md b/daliy_note/8月归档/8.30/go_deep_copy.md new file mode 100644 index 0000000..c71cee4 --- /dev/null +++ b/daliy_note/8月归档/8.30/go_deep_copy.md @@ -0,0 +1,13 @@ +```go +func DeepCopy(dst, src interface{}) error { + var buf bytes.Buffer + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}(nil)) + gob.Register([]map[string]interface{}{}) + if err := gob.NewEncoder(&buf).Encode(src); err != nil { + return err + } + return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) +} +``` + diff --git a/daliy_note/8月归档/8.30/go_正态分布_指数分布.md b/daliy_note/8月归档/8.30/go_正态分布_指数分布.md new file mode 100644 index 0000000..22a29d3 --- /dev/null +++ b/daliy_note/8月归档/8.30/go_正态分布_指数分布.md @@ -0,0 +1,17 @@ +```go +func ExponentialRandom(lambda float64) float64 { + rand.Seed(time.Now().UnixNano()) + uniform := rand.Float64() + return -lambda * math.Log(1.0-uniform) +} + +func NormalDistribution(mean float64, stdDev float64) float64 { + rand.Seed(time.Now().UnixNano()) + uniform := rand.Float64() + exponent := -math.Pow((uniform-mean), 2) / (2 * math.Pow(stdDev, 2)) + coefficient := 1 / (stdDev * math.Sqrt(2*math.Pi)) + result := coefficient * math.Exp(exponent) + return result*stdDev + mean +} +``` + diff --git a/daliy_note/8月归档/8.30/k3s.md b/daliy_note/8月归档/8.30/k3s.md new file mode 100644 index 0000000..6772c0d --- /dev/null +++ b/daliy_note/8月归档/8.30/k3s.md @@ -0,0 +1,77 @@ +### /etc/pve/lxc/301.conf + +``` +lxc.apparmor.profile: unconfined +lxc.cgroup.devices.allow: a +lxc.cap.drop: +lxc.cgroup2.devices.allow: c 10:200 rwm +lxc.mount.entry: /dev/net/tun dev/net/tun none bind,create=file +lxc.mount.auto: proc:rw sys:rw +``` + + + +### /dev/kmsg + +```bash +cat <<'EOF' | tee /usr/local/bin/conf-kmsg.sh > /dev/null +#!/bin/sh -e +if [ ! -e /dev/kmsg ];then +ln -s /dev/console /dev/kmsg +fi +mount --make-rshared / + +EOF +``` + + + +### 开机自启 + +```bash +chmod +x /usr/local/bin/conf-kmsg.sh +chmod +x /etc/rc.local +chmod +x /etc/rc.d/rc.local +``` + + + +### /etc/rc.local + +``` +sh /usr/local/bin/conf-kmsg.sh +``` + + + +### ip forward + +```bash +echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf +sysctl --system +``` + + + +### K3s_token + +```bash +cat /var/lib/rancher/k3s/server/node-token +``` + +> K103ecfc393250507782a8275efe48fb3a27bfb010e14862ebfd7145448514b9f8b::server:0ef7e0ba1534843bcc8637dd8c9a31e9 + +### 主节点 + +```bash +curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_VERSION=v1.28.12+k3s1 INSTALL_K3S_MIRROR=cn sh - +``` + + + +### agent加入集群 + +```bash +curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_VERSION=v1.28.12+k3s1 INSTALL_K3S_MIRROR=cn K3S_URL=https://192.168.0.20:6443 K3S_TOKEN=K103ecfc393250507782a8275efe48fb3a27bfb010e14862ebfd7145448514b9f8b::server:0ef7e0ba1534843bcc8637dd8c9a31e9 sh - +``` + diff --git a/daliy_note/8月归档/8.30/带内故障带外故障.md b/daliy_note/8月归档/8.30/带内故障带外故障.md new file mode 100644 index 0000000..9b8a3cf --- /dev/null +++ b/daliy_note/8月归档/8.30/带内故障带外故障.md @@ -0,0 +1,6 @@ +**服务器故障可以分为带内故障和带外故障,这两种故障类型主要区别在于管理和维护的方式。**‌ + +- **带内故障**‌涉及通过服务器内部的接口进行管理和控制,这种方式可以对服务器进行实时监控和维护,但需要占用服务器的计算资源,可能会影响服务器的性能。带内管理的优点在于可以直接访问服务器的操作系统和硬件资源,进行详细的诊断和修复。然而,它的缺点是当服务器操作系统或硬件出现故障时,带内管理将无法进行,因为管理操作依赖于服务器本身的运行状态。 +- **带外故障**‌则是指通过专用的网卡或管理口实现对服务器的管理,这种方式不受操作系统和硬件故障的影响,提供更加稳定的管理方式。带外管理可以通过一些专用硬件设备(例如BMC、DRAC、iLO等)进行实现,也可以通过管理软件和云平台实现。带外管理的优点包括可以实现远程管理和监控,不需要物理接触服务器,同时也不会影响服务器的性能。这种管理方式非常适用于远程数据中心、云计算环境等场景,因为它可以在任何时间、任何地点对服务器进行全面的远程控制和管理。 + +综上所述,带内管理和带外管理都是服务器管理中重要的方式,各自具有自己的优势和适用范围。带内管理适用于对服务器的操作系统和硬件资源进行详细的诊断和修复,而带外管理则适用于远程管理和监控,不受操作系统和硬件故障的影响,提供更加稳定的管理方式‌。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.10/smartctl.md b/daliy_note/9月归档/9.10/smartctl.md new file mode 100644 index 0000000..7bf87b1 --- /dev/null +++ b/daliy_note/9月归档/9.10/smartctl.md @@ -0,0 +1,6 @@ +### 查看硬盘设备 sn + +```bash +smartctl -a /dev/nvme0 +``` + diff --git a/daliy_note/9月归档/9.11/alertmanager-报警写入.md b/daliy_note/9月归档/9.11/alertmanager-报警写入.md new file mode 100644 index 0000000..514075a --- /dev/null +++ b/daliy_note/9月归档/9.11/alertmanager-报警写入.md @@ -0,0 +1,83 @@ +### function 报警接收处理函数 + +```go +func (api *API) postAlertsHandler(params alert_ops.PostAlertsParams) middleware.Responder { + +} +``` + + + +### 报警指纹(唯一id)生成 + +```bash +func labelSetToFingerprint(ls LabelSet) Fingerprint { + if len(ls) == 0 { + return Fingerprint(emptyLabelSignature) + } + + labelNames := make(LabelNames, 0, len(ls)) + for labelName := range ls { + labelNames = append(labelNames, labelName) + } + sort.Sort(labelNames) + + sum := hashNew() + for _, labelName := range labelNames { + sum = hashAdd(sum, string(labelName)) + sum = hashAddByte(sum, SeparatorByte) + sum = hashAdd(sum, string(ls[labelName])) + sum = hashAddByte(sum, SeparatorByte) + } + return Fingerprint(sum) +} +``` + + + +### 接收报警 + +```go +func (o *PostAlerts) ServeHTTP(rw http.ResponseWriter, r *http.Request) { + route, rCtx, _ := o.Context.RouteInfo(r) + if rCtx != nil { + *r = *rCtx + } + var Params = NewPostAlertsParams() + if err := o.Context.BindValidRequest(r, route, &Params); err != nil { // bind params + o.Context.Respond(rw, r, route.Produces, route, err) + return + } + + res := o.Handler.Handle(Params) // actually handle the request + o.Context.Respond(rw, r, route.Produces, route, res) + +} +``` + +### NewAPI时注册 + +```go' +func NewAPI( + alerts provider.Alerts, + gf groupsFn, + sf getAlertStatusFn, + silences *silence.Silences, + peer cluster.ClusterPeer, + l log.Logger, + r prometheus.Registerer, +) (*API, error) { + openAPI.AlertGetAlertsHandler = alert_ops.GetAlertsHandlerFunc(api.getAlertsHandler) + openAPI.AlertPostAlertsHandler = alert_ops.PostAlertsHandlerFunc(api.postAlertsHandler) + openAPI.AlertgroupGetAlertGroupsHandler = alertgroup_ops.GetAlertGroupsHandlerFunc(api.getAlertGroupsHandler) + openAPI.GeneralGetStatusHandler = general_ops.GetStatusHandlerFunc(api.getStatusHandler) + openAPI.ReceiverGetReceiversHandler = receiver_ops.GetReceiversHandlerFunc(api.getReceiversHandler) + openAPI.SilenceDeleteSilenceHandler = silence_ops.DeleteSilenceHandlerFunc(api.deleteSilenceHandler) + openAPI.SilenceGetSilenceHandler = silence_ops.GetSilenceHandlerFunc(api.getSilenceHandler) + openAPI.SilenceGetSilencesHandler = silence_ops.GetSilencesHandlerFunc(api.getSilencesHandler) + openAPI.SilencePostSilencesHandler = silence_ops.PostSilencesHandlerFunc(api.postSilencesHandler) +} +``` + + + diff --git a/daliy_note/9月归档/9.11/alertmanager工作模式.md b/daliy_note/9月归档/9.11/alertmanager工作模式.md new file mode 100644 index 0000000..488e0e3 --- /dev/null +++ b/daliy_note/9月归档/9.11/alertmanager工作模式.md @@ -0,0 +1,17 @@ +Alertmanager 是一个用于处理 Prometheus 发送的警报的工具。它支持多种模式和配置,以便更好地管理和路由警报。你提到的 "fallbck mode" 可能是 "fallback mode" 的拼写错误。以下是相关模式和概念的解释: + +### 1. Fallback Mode +**Fallback 模式** 是一种在主要配置或路径失效时使用的备用机制。在 Alertmanager 中,fallback 模式的一种常见用途是在某些接收器不可用时,将警报发送到备用接收器。例如,如果主要的电子邮件服务器不可用,可以配置 Alertmanager 将警报发送到备用的电子邮件服务器或其他通知渠道(如 Slack 或 PagerDuty)。 + +### 2. UTF-8 Strict Mode +**UTF-8 严格模式** 通常指的是在处理字符串数据时,严格按照 UTF-8 编码进行解码和验证。这意味着任何不是合法 UTF-8 编码的字符串都会被拒绝或抛出错误。在 Alertmanager 的上下文中,UTF-8 严格模式可以确保接收到的警报信息(如标签和值)是按照 UTF-8 编码的,从而避免乱码或编码错误带来的问题。 + +### 3. Classic Mode +**Classic 模式** 可能指的是 Alertmanager 的默认操作模式或配置。在这种模式下,Alertmanager 按照最基本和标准的方式处理警报,并使用默认的路由规则和接收器。与其他自定义模式或高级配置相比,classic 模式通常不包含特殊的处理逻辑或备用机制。 + +### 区别总结 +- **Fallback Mode**: 在主要配置或路径失效时启用的备用机制,确保系统的高可用性和冗余。 +- **UTF-8 Strict Mode**: 严格按照 UTF-8 编码处理字符串数据,确保数据的正确性和一致性。 +- **Classic Mode**: 默认的操作模式或配置,使用标准的路由规则和接收器处理警报。 + +这些模式和概念的选择和使用取决于具体的应用场景和需求。合理配置和使用这些模式可以提高系统的可靠性和稳定性。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.11/gossip.md b/daliy_note/9月归档/9.11/gossip.md new file mode 100644 index 0000000..50b0dac --- /dev/null +++ b/daliy_note/9月归档/9.11/gossip.md @@ -0,0 +1,71 @@ +在计算机科学和分布式系统中,“Gossip” 通常指的是一种用于信息传播和一致性维护的协议或算法。这种协议模拟了人类社会中的八卦(gossip)传播方式,即通过节点之间随机地交流信息,使得整个网络逐渐达成共识或传播信息。以下是 Gossip 协议的详细介绍: + +### Gossip 协议的基本概念 + +1. **信息传播**: + 在 Gossip 协议中,每个节点会随机选择一个或多个其他节点,并将自己知道的信息传递给这些节点。接收到信息的节点再继续传播给其他节点,类似于病毒传播或人类口耳相传的八卦。 + +2. **随机选择**: + Gossip 协议依赖于节点之间的随机选择和通信,这种随机性确保了信息能够广泛而迅速地传播到整个网络。 + +3. **去中心化**: + Gossip 协议通常是去中心化的,没有单点故障。每个节点都可以独立地执行 Gossip 操作,这使得系统具有高可用性和容错性。 + +### Gossip 协议的特点 + +1. **鲁棒性**: + Gossip 协议对节点故障和网络分区具有较高的容错性。即使部分节点失效,信息仍然可以通过其他节点传播。 + +2. **可扩展性**: + 由于 Gossip 协议是去中心化的,它非常适合大规模分布式系统,能够在大量节点之间有效传播信息。 + +3. **一致性**: + Gossip 协议可以用于维护系统的一致性,例如分布式数据库中的数据一致性、分布式缓存中的缓存一致性等。 + +4. **最终一致性**: + Gossip 协议通常提供最终一致性(Eventual Consistency),即在没有新的更新的情况下,所有节点最终会收敛到相同的状态。 + +### 常见应用场景 + +1. **分布式数据库**: + Gossip 协议被广泛用于分布式数据库系统,如 Apache Cassandra 和 Amazon DynamoDB,用于节点间的元数据传播和一致性维护。 + +2. **分布式缓存**: + 在分布式缓存系统中,Gossip 协议用于同步缓存节点之间的缓存状态和失效信息。 + +3. **集群管理**: + 在分布式集群管理和监控系统中,Gossip 协议用于节点状态的传播和故障检测。 + +4. **区块链**: + 在一些区块链网络中,Gossip 协议用于区块和交易信息的传播。 + +### Gossip 协议的实现 + +Gossip 协议的实现通常包括以下步骤: + +1. **节点选择**: + 每个节点定期随机选择一个或多个其他节点进行通信。选择方式可以是完全随机的,也可以是基于某些策略的(例如最近最少交流)。 + +2. **信息交换**: + 选定的节点之间交换信息。这些信息可以是节点状态、数据更新、故障报告等。 + +3. **信息传播**: + 接收到信息的节点继续选择其他节点进行信息传播,直到信息在整个网络中传播开来。 + +4. **收敛性**: + 通过多轮 Gossip 操作,信息逐渐在整个网络中传播并收敛到一致的状态。 + +### 示例 + +假设有一个分布式系统包含四个节点 A、B、C 和 D,每个节点定期与其他节点进行 Gossip 操作: + +1. 节点 A 将自己知道的信息传递给节点 B。 +2. 节点 B 接收到信息后,又将其传递给节点 C。 +3. 节点 C 再将信息传递给节点 D。 +4. 节点 D 最后将信息传递给节点 A。 + +通过多次这样的操作,所有节点最终都会知道相同的信息。 + +### 总结 + +Gossip 协议是一种用于分布式系统中信息传播和一致性维护的去中心化协议。它通过节点之间的随机通信,确保信息能够有效地传播到整个网络。Gossip 协议具有鲁棒性、可扩展性和最终一致性的特点,广泛应用于分布式数据库、分布式缓存、集群管理和区块链等领域。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.11/openstack与k8s.md b/daliy_note/9月归档/9.11/openstack与k8s.md new file mode 100644 index 0000000..5e5caf5 --- /dev/null +++ b/daliy_note/9月归档/9.11/openstack与k8s.md @@ -0,0 +1,59 @@ +OpenStack 和 Kubernetes 是两种不同类型的云计算和容器编排平台,但它们可以相互协作,以提供更全面的云服务。以下是对 OpenStack 和 Kubernetes 的比较及其协作方式的简要介绍: + +### OpenStack + +**主要用途**: OpenStack 主要用于创建和管理虚拟化的基础设施。它提供计算、存储和网络资源,类似于基础设施即服务(IaaS)平台。 + +**核心组件**: +- **Nova**: 虚拟机管理 +- **Swift**: 对象存储 +- **Cinder**: 块存储 +- **Neutron**: 网络服务 +- **Keystone**: 身份认证 +- **Glance**: 镜像管理 +- **Horizon**: Web 仪表盘 + +### Kubernetes + +**主要用途**: Kubernetes 主要用于容器的编排和管理。它帮助部署、扩展和管理容器化应用,类似于容器即服务(CaaS)平台。 + +**核心组件**: +- **Pod**: 最小部署单元,包含一个或多个容器 +- **Service**: 为一组 Pod 提供网络访问 +- **Deployment**: 管理 Pod 的声明式更新 +- **ConfigMap 和 Secret**: 配置和敏感信息管理 +- **Ingress**: HTTP 和 HTTPS 路由 + +### 比较 + +1. **基础架构管理 vs 应用管理**: + - **OpenStack**: 专注于虚拟机、存储和网络等基础设施资源的管理。 + - **Kubernetes**: 专注于容器和应用的编排与管理。 + +2. **虚拟机 vs 容器**: + - **OpenStack**: 管理虚拟机和裸金属服务器。 + - **Kubernetes**: 管理容器和容器化应用。 + +3. **服务类型**: + - **OpenStack**: 提供 IaaS 服务。 + - **Kubernetes**: 提供 CaaS 服务。 + +### 协作 + +OpenStack 和 Kubernetes 可以相互集成,从而提供更强大的云解决方案。以下是一些常见的集成方式: + +1. **使用 OpenStack 作为 Kubernetes 的底层基础设施**: + - **计算资源**: Kubernetes 可以在 OpenStack 的虚拟机上运行,利用 Nova 提供的计算资源。 + - **存储资源**: Kubernetes 可以利用 OpenStack 的 Cinder 或 Swift 作为持久化存储解决方案。 + - **网络资源**: Kubernetes 可以使用 OpenStack 的 Neutron 提供的网络功能,管理 Pod 的网络配置。 + +2. **Kubernetes on OpenStack**: 这是一个常见的部署模式,其中 Kubernetes 集群运行在 OpenStack 提供的虚拟机上。OpenStack 提供底层基础设施,Kubernetes 管理容器化应用。 + +3. **OpenStack Magnum**: OpenStack 提供了一个名为 Magnum 的服务,用于在 OpenStack 上管理容器编排引擎(如 Kubernetes、Docker Swarm 和 Mesos)。Magnum 使得在 OpenStack 环境中部署和管理 Kubernetes 集群变得更加容易。 + +### 示例场景 + +- **企业私有云**: 企业可以使用 OpenStack 构建私有云基础设施,并在其上运行 Kubernetes,管理和编排容器化应用。 +- **混合云和多云部署**: 企业可以利用 OpenStack 提供的虚拟化资源,同时通过 Kubernetes 管理跨多个云环境的容器化应用,实现灵活的资源调度和高可用性。 + +总的来说,OpenStack 和 Kubernetes 各有侧重,但它们的结合可以提供从基础设施到应用的全栈云计算解决方案。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.13/alertmanager流程图.md b/daliy_note/9月归档/9.13/alertmanager流程图.md new file mode 100644 index 0000000..1085302 --- /dev/null +++ b/daliy_note/9月归档/9.13/alertmanager流程图.md @@ -0,0 +1 @@ +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/typora/image-20240913102921805.png) \ No newline at end of file diff --git a/daliy_note/9月归档/9.13/macos安装picgo.md b/daliy_note/9月归档/9.13/macos安装picgo.md new file mode 100644 index 0000000..d284837 --- /dev/null +++ b/daliy_note/9月归档/9.13/macos安装picgo.md @@ -0,0 +1,18 @@ +### 提示软件已经损坏,移到废纸篓 + +#### 允许mac安装和打开任何来源 + +- 回车后,输入电脑开机密码 + +```bash +sudo spctl --master-disable +``` + + + +#### 移除picgoapp的签名验证 + +```bash +xattr -cr /Applications/PicGo.app +``` + diff --git a/daliy_note/9月归档/9.18/gin框架.md b/daliy_note/9月归档/9.18/gin框架.md new file mode 100644 index 0000000..ed49b35 --- /dev/null +++ b/daliy_note/9月归档/9.18/gin框架.md @@ -0,0 +1,97 @@ +### 设置router时,传递多个HandlerFunc + +```go + +// HandlerFunc defines the handler used by gin middleware as return value. +type HandlerFunc func(*Context) +``` + + + +在 Go 语言的 Gin Web 框架中,设置路由时传递多个 `HandlerFunc` 的目的是为了实现中间件机制。中间件是指在处理请求的过程中,能够在请求到达最终处理函数之前或响应返回客户端之前,对请求和响应进行预处理或后处理的一些函数。 + +### 中间件的用途 + +中间件可以用于多种场景,包括但不限于: + +1. **日志记录**:记录每个请求的详细信息,如请求路径、方法、处理时间等。 +2. **认证与授权**:检查用户是否已登录,是否有权限访问某些资源。 +3. **错误处理**:捕获和处理请求处理过程中的错误,返回统一的错误响应。 +4. **请求修改**:在请求到达最终处理函数之前,对请求进行修改,如解析和验证请求参数。 +5. **响应处理**:在响应返回给客户端之前,对响应进行修改,如添加响应头、格式化响应数据等。 + +### 示例 + +例如,你可以定义一个简单的中间件来记录请求的处理时间: + +```go +package main + +import ( + "log" + "time" + + "github.com/gin-gonic/gin" +) + +// 定义一个中间件函数 +func LoggerMiddleware(c *gin.Context) { + start := time.Now() + c.Next() // 处理请求 + + // 请求处理完成后执行以下代码 + duration := time.Since(start) + log.Printf("Request processed in %v", duration) +} + +func main() { + r := gin.Default() + + // 使用中间件 + r.GET("/ping", LoggerMiddleware, func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "pong", + }) + }) + + r.Run() // 监听并在 0.0.0.0:8080 上启动服务 +} +``` + +在这个例子中,`LoggerMiddleware` 是一个中间件函数,它会记录每个请求的处理时间。`c.Next()` 用于调用下一个中间件或最终的处理函数。 + +### 使用多个中间件 + +你可以传递多个 `HandlerFunc` 来设置多个中间件。例如,假设你有两个中间件,一个用于日志记录,一个用于认证: + +```go +func AuthMiddleware(c *gin.Context) { + // 这里进行认证逻辑 + token := c.GetHeader("Authorization") + if token != "valid-token" { + c.JSON(401, gin.H{"error": "Unauthorized"}) + c.Abort() // 终止请求的进一步处理 + return + } + c.Next() +} + +func main() { + r := gin.Default() + + // 使用多个中间件 + r.GET("/secure", LoggerMiddleware, AuthMiddleware, func(c *gin.Context) { + c.JSON(200, gin.H{ + "message": "secure data", + }) + }) + + r.Run() // 监听并在 0.0.0.0:8080 上启动服务 +} +``` + +在这个例子中,`LoggerMiddleware` 和 `AuthMiddleware` 会按顺序执行,然后再执行最终的处理函数。如果认证失败,中间件会终止请求处理并返回 401 错误。 + +### 总结 + +在 Gin 中传递多个 `HandlerFunc` 是为了实现中间件机制,中间件可以在请求处理的不同阶段执行特定的逻辑,从而提供更好的代码组织和复用性。中间件在处理请求时按照传递的顺序依次执行,`c.Next()` 用于调用下一个中间件或最终处理函数。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.19/AIOps.md b/daliy_note/9月归档/9.19/AIOps.md new file mode 100644 index 0000000..3690b18 --- /dev/null +++ b/daliy_note/9月归档/9.19/AIOps.md @@ -0,0 +1,31 @@ +AIOps(Artificial Intelligence for IT Operations)的概念起源于将人工智能(AI)技术应用于IT运维(IT Operations)领域。其目的是通过自动化和智能化手段来提升IT运维的效率和效果。AIOps综合了大数据、机器学习、人工智能等技术,帮助企业更好地管理和优化其IT基础设施和应用服务。 + +### AIOps的核心概念 + +1. **数据收集与整合**:AIOps平台能够从多个数据源(如日志、监控系统、事件管理系统等)中收集数据,并对这些数据进行整合和预处理。 +2. **实时监控与分析**:通过实时监控IT环境中的各种指标,AIOps平台可以对数据进行实时分析,识别出潜在的问题和风险。 +3. **异常检测与告警**:利用机器学习算法,AIOps平台能够检测出异常行为,并在问题发生之前发出告警。 +4. **根因分析**:AIOps平台可以通过自动化分析和关联性算法,快速定位问题的根因,减少故障排除的时间。 +5. **自动化响应与修复**:在检测到问题后,AIOps平台可以自动化执行一些预定义的修复操作,减少人工干预。 +6. **持续优化**:通过不断学习和优化,AIOps平台可以逐渐提升其检测和响应的准确性和效率。 + +### AIOps的作用 + +1. **提高运维效率**:通过自动化监控、告警和修复,AIOps减少了人工干预,提高了运维效率。 +2. **提升服务稳定性**:AIOps可以实时监控和分析系统状态,提前发现和解决潜在问题,减少系统宕机时间。 +3. **加快问题解决速度**:利用根因分析功能,AIOps能够快速定位问题的根本原因,缩短故障排除时间。 +4. **降低运维成本**:通过自动化手段减少人工操作,AIOps可以降低运维成本,同时减少人为错误的发生。 +5. **数据驱动决策**:AIOps平台提供的数据分析和报告功能,可以帮助运维团队做出更为精准的数据驱动决策。 +6. **提升用户体验**:通过减少系统故障和提升系统性能,AIOps可以显著提升最终用户的体验。 + +### AIOps的应用场景 + +1. **故障检测与预测**:利用机器学习模型,AIOps可以预测可能的故障,并提前采取预防措施。 +2. **性能优化**:通过分析监控数据,AIOps可以识别性能瓶颈并提供优化建议。 +3. **事件关联与根因分析**:AIOps可以自动关联多个事件,帮助快速定位问题的根本原因。 +4. **自动化运维**:通过预定义的自动化脚本,AIOps可以在检测到问题后自动执行修复操作。 +5. **容量规划**:通过对历史数据的分析,AIOps能够帮助企业进行容量规划,确保资源的合理分配。 + +### 总结 + +AIOps是现代IT运维发展的重要方向,通过引入人工智能技术,AIOps能够大幅提升运维的自动化和智能化水平,从而提高效率、降低成本、提升系统稳定性和用户体验。随着大数据和AI技术的不断发展,AIOps必将在更多的企业和场景中得到广泛应用。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.2/9.2.md b/daliy_note/9月归档/9.2/9.2.md new file mode 100644 index 0000000..4753574 --- /dev/null +++ b/daliy_note/9月归档/9.2/9.2.md @@ -0,0 +1,6 @@ +### mysql 客户端中文 + +```sh +set names utf8; +``` + diff --git a/daliy_note/9月归档/9.20/OCI.md b/daliy_note/9月归档/9.20/OCI.md new file mode 100644 index 0000000..2130f87 --- /dev/null +++ b/daliy_note/9月归档/9.20/OCI.md @@ -0,0 +1,28 @@ +**开放容器倡议(Open Container Initiative,简称 OCI)** 是由 **Linux 基金会(Linux Foundation)** 在 2015 年 6 月发起的一个开源项目。其主要目标是为 **容器(Container)** 技术制定开放的行业标准,包括容器的 **规范化格式** 和 **运行时规范**,以促进不同容器平台之间的互操作性,避免供应商锁定,推动容器生态系统的健康发展。 + +### 产生背景: + +在容器技术快速发展的过程中,市场上出现了多种容器格式和运行时实现方式,如 Docker、rkt 等。由于缺乏统一的标准,导致不同容器技术之间缺乏兼容性,给开发者和运营者带来了挑战。为了解决这一问题,Docker 公司与其他行业领导者共同发起了 OCI,希望通过制定统一的标准,促进容器技术的广泛采用和互操作性。 + +### 主要工作: + +OCI 的主要工作是制定和维护以下两大规范: + +1. **容器运行时规范(Runtime Specification)**: + - 定义了容器运行时的标准,包括容器的创建、配置、执行和生命周期管理等方面。 + - 规范描述了容器运行时应该如何解读容器配置,如何设置容器的环境,以及如何启动应用程序等。 + - **`runc`** 是一个符合 OCI 运行时规范的参考实现,由 OCI 社区维护。 +2. **容器镜像规范(Image Specification)**: + - 定义了容器镜像的格式和结构,包括镜像的清单、层次结构、配置和元数据等。 + - 旨在确保容器镜像可以在不同的容器引擎和运行时之间互相兼容和传输。 + - **`umoci`** 是一个符合 OCI 镜像规范的参考工具,用于操作 OCI 镜像。 + +### 影响和意义: + +- **互操作性**:通过制定统一的规范,OCI 促进了不同容器技术和平台之间的兼容性,使开发者和运营者可以更加灵活地选择容器工具和服务。 +- **生态系统健康发展**:避免了由于标准缺失导致的供应商锁定,有利于容器技术的创新和生态系统的繁荣。 +- **社区合作**:汇集了行业内的主要参与者,包括技术公司、云服务提供商和开源社区,共同参与规范的制定和实施。 + +### 总结: + +开放容器倡议(OCI)在容器技术发展的关键时期,为容器格式和运行时制定了开放的行业标准。这些标准为容器技术的广泛应用奠定了基础,促进了容器生态系统的开放性和可移植性,对云计算、微服务和 DevOps 等领域的发展产生了深远的影响。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.20/helm-hook.md b/daliy_note/9月归档/9.20/helm-hook.md new file mode 100644 index 0000000..76385f5 --- /dev/null +++ b/daliy_note/9月归档/9.20/helm-hook.md @@ -0,0 +1,257 @@ +Helm 的 Hooks 是一种机制,允许用户在 Chart 的生命周期的某些关键点执行自定义操作。Hooks 可以用于执行初始化任务、清理任务、备份操作等。以下是 Helm 支持的一些常用 Hooks 以及它们的触发点: + +### 常用 Hooks + +#### 1. `pre-install` +- **触发点**:在 Helm 安装 Chart 之前执行。 +- **用途**:用于初始化或准备操作,如创建必要的资源。 + +#### 2. `post-install` +- **触发点**:在 Helm 安装 Chart 之后执行。 +- **用途**:用于执行后续操作,如配置或通知。 + +#### 3. `pre-delete` +- **触发点**:在 Helm 删除 Chart 之前执行。 +- **用途**:用于执行清理操作,确保资源正确释放。 + +#### 4. `post-delete` +- **触发点**:在 Helm 删除 Chart 之后执行。 +- **用途**:用于执行后续清理任务或通知。 + +#### 5. `pre-upgrade` +- **触发点**:在 Helm 升级 Chart 之前执行。 +- **用途**:用于准备升级操作,如备份数据。 + +#### 6. `post-upgrade` +- **触发点**:在 Helm 升级 Chart 之后执行。 +- **用途**:用于执行后续操作或验证升级结果。 + +#### 7. `pre-rollback` +- **触发点**:在 Helm 回滚 Chart 之前执行。 +- **用途**:用于准备回滚操作。 + +#### 8. `post-rollback` +- **触发点**:在 Helm 回滚 Chart 之后执行。 +- **用途**:用于执行回滚后的操作。 + +#### 9. `test` +- **触发点**:专用于测试操作。 +- **用途**:用于运行测试用例,验证 Chart 部署是否成功。 + +### 使用 Hook 的示例 + +在 Helm Chart 的模板文件中,使用注解来定义 Hook。例如,一个简单的 ConfigMap 资源可以在安装之前创建: + +```yaml +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config + annotations: + "helm.sh/hook": pre-install +data: + my-value: "This is a pre-install hook" +``` + +### Hook 的生命周期 + +1. **安装(Install)**: + - `pre-install` + - Chart 安装 + - `post-install` + +2. **升级(Upgrade)**: + - `pre-upgrade` + - Chart 升级 + - `post-upgrade` + +3. **删除(Delete)**: + - `pre-delete` + - Chart 删除 + - `post-delete` + +4. **回滚(Rollback)**: + - `pre-rollback` + - Chart 回滚 + - `post-rollback` + +### Hook 的策略 + +你还可以为 Hook 定义策略,以控制它们的行为。这些策略通过注解指定: + +- **`"helm.sh/hook-delete-policy"`**:定义 Hook 执行完毕后的删除策略。 + - `hook-succeeded`:当 Hook 成功时删除。 + - `hook-failed`:当 Hook 失败时删除。 + - `before-hook-creation`:在创建新的 Hook 资源之前删除旧的 Hook 资源。 + +示例: + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: test-job + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + spec: + containers: + - name: test-container + image: busybox + command: ['sh', '-c', 'echo Hello, Helm!'] + restartPolicy: Never +``` + +### 总结 + +Helm 的 Hooks 提供了在 Chart 生命周期的关键点上执行自定义操作的灵活性,使得 Chart 的管理和部署更加细粒度和可控。掌握和使用这些 Hooks,可以让你的 Helm Chart 更加健壮和自动化。 + +--- + +当然,以下是上边例子的详细解释,包括其执行过程和作用: + +### 示例解释 + +这个示例是一个 Kubernetes `Job` 资源,带有 Helm Hook 的注解。具体内容如下: + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: test-job + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + spec: + containers: + - name: test-container + image: busybox + command: ['sh', '-c', 'echo Hello, Helm!'] + restartPolicy: Never +``` + +### 细节解释 + +1. **资源类型**: + - `apiVersion: batch/v1` 和 `kind: Job` 表明这是一个 Kubernetes `Job` 资源。 + - `Job` 资源在 Kubernetes 中用于一次性的任务,通常会在完成后自动退出。 + +2. **元数据**: + - `metadata` 部分包含了资源的名称 `test-job`。 + - `annotations` 部分包含了 Helm Hook 的注解: + - `"helm.sh/hook": test` 表示这个 `Job` 是一个测试 Hook。 + - `"helm.sh/hook-delete-policy": hook-succeeded` 表示当这个 `Job` 成功完成后,Helm 会删除它。 + +3. **Job 规范**: + - `spec` 部分定义了 `Job` 的具体行为。 + - `template` 部分定义了 `Pod` 模板,这个 `Pod` 包含一个名为 `test-container` 的容器。 + - 容器使用 `busybox` 镜像,并且执行命令 `echo Hello, Helm!`。 + - `restartPolicy: Never` 表示这个 `Pod` 在完成任务后不会重新启动。 + +### 执行过程 + +1. **安装 Chart**: + - 当你执行 `helm install` 命令来安装 Chart 时: + ```sh + helm install + ``` + - Helm 会识别并执行这个 `Job`,因为它带有 `"helm.sh/hook": test` 注解。 + +2. **执行 Hook**: + - `Job` 资源会被创建并启动。 + - 容器会执行命令 `echo Hello, Helm!`,将 "Hello, Helm!" 输出到控制台。 + +3. **删除 Hook**: + - 因为这个 Hook 带有 `"helm.sh/hook-delete-policy": hook-succeeded` 注解,当 `Job` 成功完成(即输出 "Hello, Helm!" 并退出)后,Helm 会自动删除这个 `Job` 资源。 + +### 总结 + +这个示例展示了如何使用 Helm 的 `test` Hook 来运行一个一次性的任务,并在任务成功完成后自动清理资源。具体流程如下: + +1. Helm 识别到 `Job` 资源带有 `test` Hook 注解。 +2. Helm 在安装过程中执行这个 `Job`。 +3. `Job` 成功完成任务(输出 "Hello, Helm!")。 +4. Helm 根据 `hook-delete-policy` 注解自动删除 `Job` 资源。 + +通过这种方式,你可以在 Helm Chart 中定义各种自定义任务,并确保在任务完成后自动清理资源,从而保持集群的整洁和高效。 + +--- + +多个hook执行顺序 + +当然,以下是上边例子的详细解释,包括其执行过程和作用: + +### 示例解释 + +这个示例是一个 Kubernetes `Job` 资源,带有 Helm Hook 的注解。具体内容如下: + +```yaml +apiVersion: batch/v1 +kind: Job +metadata: + name: test-job + annotations: + "helm.sh/hook": test + "helm.sh/hook-delete-policy": hook-succeeded +spec: + template: + spec: + containers: + - name: test-container + image: busybox + command: ['sh', '-c', 'echo Hello, Helm!'] + restartPolicy: Never +``` + +### 细节解释 + +1. **资源类型**: + - `apiVersion: batch/v1` 和 `kind: Job` 表明这是一个 Kubernetes `Job` 资源。 + - `Job` 资源在 Kubernetes 中用于一次性的任务,通常会在完成后自动退出。 + +2. **元数据**: + - `metadata` 部分包含了资源的名称 `test-job`。 + - `annotations` 部分包含了 Helm Hook 的注解: + - `"helm.sh/hook": test` 表示这个 `Job` 是一个测试 Hook。 + - `"helm.sh/hook-delete-policy": hook-succeeded` 表示当这个 `Job` 成功完成后,Helm 会删除它。 + +3. **Job 规范**: + - `spec` 部分定义了 `Job` 的具体行为。 + - `template` 部分定义了 `Pod` 模板,这个 `Pod` 包含一个名为 `test-container` 的容器。 + - 容器使用 `busybox` 镜像,并且执行命令 `echo Hello, Helm!`。 + - `restartPolicy: Never` 表示这个 `Pod` 在完成任务后不会重新启动。 + +### 执行过程 + +1. **安装 Chart**: + - 当你执行 `helm install` 命令来安装 Chart 时: + ```sh + helm install + ``` + - Helm 会识别并执行这个 `Job`,因为它带有 `"helm.sh/hook": test` 注解。 + +2. **执行 Hook**: + - `Job` 资源会被创建并启动。 + - 容器会执行命令 `echo Hello, Helm!`,将 "Hello, Helm!" 输出到控制台。 + +3. **删除 Hook**: + - 因为这个 Hook 带有 `"helm.sh/hook-delete-policy": hook-succeeded` 注解,当 `Job` 成功完成(即输出 "Hello, Helm!" 并退出)后,Helm 会自动删除这个 `Job` 资源。 + +### 总结 + +这个示例展示了如何使用 Helm 的 `test` Hook 来运行一个一次性的任务,并在任务成功完成后自动清理资源。具体流程如下: + +1. Helm 识别到 `Job` 资源带有 `test` Hook 注解。 +2. Helm 在安装过程中执行这个 `Job`。 +3. `Job` 成功完成任务(输出 "Hello, Helm!")。 +4. Helm 根据 `hook-delete-policy` 注解自动删除 `Job` 资源。 + +通过这种方式,你可以在 Helm Chart 中定义各种自定义任务,并确保在任务完成后自动清理资源,从而保持集群的整洁和高效。 + +--- + +https://helm.sh/zh/docs/topics/charts_hooks/ \ No newline at end of file diff --git a/daliy_note/9月归档/9.20/helm.md b/daliy_note/9月归档/9.20/helm.md new file mode 100644 index 0000000..e62fd18 --- /dev/null +++ b/daliy_note/9月归档/9.20/helm.md @@ -0,0 +1,147 @@ +Helm 是 Kubernetes 的包管理器,用于管理 Kubernetes 应用程序。以下是一些常用的 Helm 命令,帮助你在 Kubernetes 集群中更有效地管理应用程序。 + +### 基本命令 + +#### 1. 安装 Chart +```sh +helm install +``` +- `release_name`:指定这次安装的名称。 +- `chart_name`:指定要安装的 Chart 名称。 + +#### 2. 升级 Release +```sh +helm upgrade +``` +- `release_name`:要升级的 Release 的名称。 +- `chart_name`:新的 Chart 名称或路径。 + +#### 3. 卸载 Release +```sh +helm uninstall +``` +- `release_name`:要卸载的 Release 的名称。 + +#### 4. 查看 Release +```sh +helm list +``` +- 列出所有已安装的 Release。 + +#### 5. 查看 Release 状态 +```sh +helm status +``` +- 查看指定 Release 的状态。 + +#### 6. 查看 Release 历史 +```sh +helm history +``` +- 查看指定 Release 的历史记录。 + +### Chart 管理 + +#### 7. 搜索 Chart +```sh +helm search hub +``` +- 在 Helm Hub 中搜索 Chart。 + +```sh +helm search repo +``` +- 在已添加的仓库中搜索 Chart。 + +#### 8. 添加仓库 +```sh +helm repo add +``` +- `repo_name`:仓库的名称。 +- `repo_url`:仓库的 URL。 + +#### 9. 更新仓库 +```sh +helm repo update +``` +- 更新所有已添加仓库的信息。 + +#### 10. 列出仓库 +```sh +helm repo list +``` +- 列出所有已添加的仓库。 + +#### 11. 移除仓库 +```sh +helm repo remove +``` +- 移除指定的仓库。 + +### Chart 开发 + +#### 12. 创建新的 Chart +```sh +helm create +``` +- 创建一个新的 Chart 目录结构。 + +#### 13. 打包 Chart +```sh +helm package +``` +- 将指定目录下的 Chart 打包成 `.tgz` 文件。 + +#### 14. 验证 Chart +```sh +helm lint +``` +- 检查 Chart 是否符合规范,有无错误。 + +### 配置管理 + +#### 15. 查看默认值 +```sh +helm show values +``` +- 查看 Chart 的默认值。 + +#### 16. 使用自定义值安装/升级 +```sh +helm install -f +``` +```sh +helm upgrade -f +``` +- 使用指定的自定义值文件进行安装或升级。 + +### Helm 插件 + +#### 17. 列出插件 +```sh +helm plugin list +``` +- 列出所有已安装的 Helm 插件。 + +#### 18. 安装插件 +```sh +helm plugin install +``` +- 从指定 URL 安装插件。 + +#### 19. 移除插件 +```sh +helm plugin uninstall +``` +- 移除指定的插件。 + +### 其他 + +#### 20. 获取 Helm 版本 +```sh +helm version +``` +- 查看当前 Helm 客户端和服务端的版本信息。 + +### 总结 +这些是 Helm 的一些常用命令,掌握这些命令可以帮助你在 Kubernetes 集群中更高效地管理应用程序。Helm 提供了强大且灵活的功能,适用于各种场景的应用管理。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.20/openelb.md b/daliy_note/9月归档/9.20/openelb.md new file mode 100644 index 0000000..2a835d0 --- /dev/null +++ b/daliy_note/9月归档/9.20/openelb.md @@ -0,0 +1,74 @@ +OpenELB(开放式负载均衡器)是一个开源项目,旨在为 Kubernetes 集群提供高性能的负载均衡和服务发现功能。OpenELB 支持多种负载均衡模式,包括 Layer 2 模式、BGP 模式和 VIP 模式。让我们详细了解这些模式的区别与工作原理。 + +### 1. Layer 2 模式 + +#### 工作原理: +- **Layer 2 模式**利用数据链路层(也称为 MAC 层)的广播功能,将请求分发到不同的后端节点。 +- 在这种模式下,OpenELB 会将负载均衡器的虚拟 IP(VIP)配置为一个浮动 IP 地址,并将其广播到同一子网内的所有节点。 +- 这些节点会响应这个浮动 IP 地址的 ARP 请求,最终实现请求的分发。 + +#### 优点: +- 配置简单,不需要额外的路由协议支持。 +- 适用于小型集群和简单网络拓扑。 + +#### 缺点: +- 不适用于跨子网或跨数据中心的场景。 +- 可能存在广播风暴的风险,对网络性能有一定影响。 + +### 2. BGP 模式 + +#### 工作原理: +- **BGP(Border Gateway Protocol)模式**利用 BGP 路由协议实现跨子网、跨数据中心的负载均衡。 +- OpenELB 充当一个 BGP 路由器,与集群内的其他 BGP 路由器建立 BGP 会话。 +- 通过 BGP 广播 VIP 地址,并将其路由到正确的后端节点。 +- 这种模式可以实现更复杂的路由策略和更高的网络可扩展性。 + +#### 优点: +- 适用于跨子网、跨数据中心的场景。 +- 支持复杂的路由策略和高可扩展性。 +- 更加稳定,适合大型集群和复杂网络拓扑。 + +#### 缺点: +- 配置复杂,需要一定的网络知识和 BGP 配置经验。 +- 需要支持 BGP 路由协议的网络设备或软件。 + +### 3. VIP 模式 + +#### 工作原理: +- **VIP(Virtual IP)模式**通过将一个虚拟 IP 地址(VIP)分配给服务,实现负载均衡。 +- OpenELB 将 VIP 地址绑定到一个特定的节点上,作为服务的入口。 +- 通过 IPVS(IP Virtual Server)或其他负载均衡技术,将请求分发到后端的实际服务实例。 + +#### 优点: +- 配置相对简单,适用于大多数场景。 +- 支持高可用性,通过监控和切换 VIP 地址,实现故障转移。 + +#### 缺点: +- 如果 VIP 地址绑定的节点发生故障,可能需要手动或通过高可用机制进行切换,存在一定的延迟。 + +- 在某些复杂网络环境下,可能需要额外的配置来确保 VIP 地址的可达性。 + +### 总结 + +#### 1. Layer 2 模式 + +- **适用场景**:适用于小型集群和简单网络拓扑的场景。通常用于单个子网内部的服务负载均衡。 +- **选择建议**:如果你的集群规模较小,且网络环境简单,Layer 2 模式是一个快速、简单的选择。 + +#### 2. BGP 模式 + +- **适用场景**:适用于跨子网、跨数据中心的场景,尤其是在需要复杂路由策略和高可扩展性的环境中。 +- **选择建议**:如果你的集群规模较大,网络拓扑复杂,或者需要跨数据中心进行负载均衡,BGP 模式是一个更合适的选择。 + +#### 3. VIP 模式 + +- **适用场景**:适用于大多数场景,包括需要高可用性和故障转移的环境。适合中型到大型集群,网络环境相对复杂,但不需要跨数据中心。 +- **选择建议**:如果你需要一个相对简单但功能强大的负载均衡解决方案,能够提供高可用性和故障转移能力,VIP 模式是一个不错的选择。 + +### 选择建议 + +- **简单和快速部署**:选择 Layer 2 模式。 +- **复杂网络环境和高可扩展性**:选择 BGP 模式。 +- **需要高可用性和故障转移**:选择 VIP 模式。 + +选择哪种模式应根据你的具体需求和网络环境来决定。如果你对网络配置和路由协议有一定的经验,可以考虑使用更复杂的 BGP 模式。如果你需要一个快速部署且相对简单的解决方案,可以选择 Layer 2 模式或 VIP 模式。无论选择哪种模式,都需要确保对相关配置和工作原理有充分的理解,以实现最佳的负载均衡效果。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.20/semver.md b/daliy_note/9月归档/9.20/semver.md new file mode 100644 index 0000000..c3046d8 --- /dev/null +++ b/daliy_note/9月归档/9.20/semver.md @@ -0,0 +1,24 @@ +SemVer 2,即 “Semantic Versioning 2.0.0”,是一种版本控制策略,用于管理应用程序、库和API的版本号。它是一种约定俗成的规范,通过版本号来清晰地表达软件变更的意义。 + +根据SemVer 2.0.0,版本号被定义为 `MAJOR.MINOR.PATCH`,具体含义如下: + +1. **MAJOR(主版本号)**:当你做了不兼容的 API 修改; +2. **MINOR(次版本号)**:当你做了向下兼容的功能性新增; +3. **PATCH(修订号)**:当你做了向下兼容的问题修正。 + +另外,还包括一些额外的标签用于标示预发布版本和构建元数据,这部分通常以 `-` 或 `+` 连接到版本号的后面: + +- **预发布版本标签**(如 `1.0.0-alpha`、`1.0.0-beta`):用于在开发过程中标示不稳定的版本,这些版本不一定是完全的。 +- **构建元数据**(如 `1.0.0+20130313144700`):用于标示构建的特定信息,通常用于区分不同的构建,而不影响版本排序。 + +一个完整的版本号实例可能是: `1.4.7-alpha.1+exp.sha.5114f85`。 + +### 具体规则和示例: + +- **初始开发阶段**:0.Y.Z 版本号,当软件还在开发初期阶段,不稳定,不应保证API稳定性。 +- **发布1.0.0版本后**: + - **MAJOR版本变更**:`1.0.0` -> `2.0.0`,表示不兼容API的重大修改。 + - **MINOR版本变更**:`1.1.0` -> `1.2.0`,表示新增了功能,但是保持向后兼容。 + - **PATCH版本变更**:`1.1.1` -> `1.1.2`,表示修复了错误,保持向后兼容。 + +通过采用 SemVer 规范,开发者和用户可以更清楚地理解每个发布版本的变更范围和潜在影响,有助于版本管理和依赖管理。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.23/nc.md b/daliy_note/9月归档/9.23/nc.md new file mode 100644 index 0000000..a159ad6 --- /dev/null +++ b/daliy_note/9月归档/9.23/nc.md @@ -0,0 +1,18 @@ +`nc`(或 `netcat`)是一个网络工具,用于读写网络连接。它可以用于执行多种网络操作,如端口扫描、数据传输等。下面是你提供的命令的具体解析: + +```bash +nc -zv -w 3 100.64.1.11 8009 +``` + +各个选项的含义如下: + +1. `nc`:调用 `netcat` 程序。 +2. `-z`:零输入/扫描模式。这个选项告诉 `netcat` 不要发送任何数据,只是去扫描指定的端口,以确定它是否打开。 +3. `-v`:详细模式。启用详细输出,会在执行过程中显示更多信息,例如连接的尝试和结果。 +4. `-w 3`:设置超时时间为3秒。这个选项指定了在尝试连接时的超时时间,即如果在3秒内没有成功连接,就放弃。 +5. `100.64.1.11`:目标IP地址。 +6. `8009`:目标端口。 + +综合起来,这个命令的功能是尝试连接到IP地址 `100.64.1.11` 的端口 `8009`,并在3秒内确定该端口是否打开(即是否有服务在监听这个端口)。它不会发送任何数据,只是用来检测端口的状态,并且会输出详细的连接结果。 + +这个命令常用于网络诊断和故障排除,帮助管理员确定某个服务是否在目标IP和端口上运行。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.23/prometheus.md b/daliy_note/9月归档/9.23/prometheus.md new file mode 100644 index 0000000..aa8a1d9 --- /dev/null +++ b/daliy_note/9月归档/9.23/prometheus.md @@ -0,0 +1,28 @@ +在Prometheus中,`scalar` 函数用于将一个时间序列的值转换为一个标量值。这意味着它可以将一个单值时间序列(也就是只有一个数据点的时间序列)转换为一个静态值,可以在计算中使用。 + +**函数语法**: +```promql +scalar(expr) +``` + +**参数**: +- `expr`: 任何有效的PromQL表达式,该表达式应返回一个或多个时间序列。通常情况下,表达式返回的时间序列应该只有一个样本点(单值)。 + +**返回值**: +- 返回一个单一的标量值,这个值来自于给定表达式计算后获得的时间序列的最后一个样本。如果表达式返回多个样本或没有样本,则`scalar`函数会产生错误。 + +### 使用场景 +- 当你需要在计算中使用一个查询的最后一个值,但你不关心时间序列的时间戳,只希望获取一个数值时,`scalar` 函数非常有用。 +- 例如,如果你想要通过计算某个时间序列的最后一个值来与另一个时间序列进行比较或做进一步的数学运算。 + +### 示例 +假设你有一个名为 `http_requests_total` 的计数器时间序列,你希望获取最后一次请求的总数并用这个值去做其他的计算: + +```promql +scalar(http_requests_total{job="my_service"}) +``` + +这会返回 `http_requests_total` 中最后一个样本的数值,而不关心它与时间的关系。 + +### 注意事项 +- 如果查询返回了多个时间序列数据,那么使用 `scalar` 函数将导致错误,因此确保你在使用该函数时,表达式的结果是一个单一的时间序列。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.24/KubeOnKube.md b/daliy_note/9月归档/9.24/KubeOnKube.md new file mode 100644 index 0000000..3f59611 --- /dev/null +++ b/daliy_note/9月归档/9.24/KubeOnKube.md @@ -0,0 +1,24 @@ +"Kubernetes on Kubernetes" (通常缩写为 "Kube-on-Kube") 是指使用一个 Kubernetes 集群来管理和运行另一个 Kubernetes 集群的架构或方法。这种方法通常用于提供 Kubernetes 集群的自动化管理、集群生命周期管理、集群监控等功能。 + +以下是对 "Kubernetes on Kubernetes" 的进一步解释: + +1. **管理集群和工作负载集群**: + - **管理集群(Management Cluster)**:这是第一个 Kubernetes 集群,用于管理其他 Kubernetes 集群的生命周期。管理集群中可能运行控制平面组件、集群管理工具和其他辅助服务。 + - **工作负载集群(Workload Cluster)**:这些是由管理集群创建和管理的 Kubernetes 集群,实际用于运行用户的应用和服务。 + +2. **使用 Helm Charts 或 Operators**:在管理集群中可以使用 Helm Charts 或 Kubernetes Operators 来部署和管理工作负载集群。这些工具可以帮助自动化集群的创建、配置、更新和监控。 + +3. **KubeVirt**:KubeVirt 是一个开源项目,允许在 Kubernetes 中管理和运行虚拟机。使用 KubeVirt,可以在 Kubernetes 集群上运行 VM,这些虚拟机可以用于运行另一个 Kubernetes 集群的控制平面组件,从而实现 Kubernetes on Kubernetes 的架构。 + +4. **Cluster API**:Cluster API 是一个 Kubernetes 项目,提供了一组标准化的 API 和控制器,用于在 Kubernetes 集群上声明性地创建、配置和管理另一个 Kubernetes 集群。Cluster API 使得集群的配置、升级和扩展变得更加自动化和一致。 + +5. **优势**: + - **一致性和标准化**:使用 Kubernetes 的原生 API 和工具管理集群,提高了集群管理的一致性和标准化。 + - **自动化**:通过自动化工具和控制器,可以简化集群的生命周期管理,减少手动操作的复杂性。 + - **弹性和扩展性**:可以根据需求动态地创建和删除工作负载集群,灵活应对不同的工作负载需求。 + +6. **挑战**: + - **复杂性**:管理多层次的 Kubernetes 集群会增加系统的复杂性,需要对 Kubernetes 有深入的理解。 + - **资源开销**:运行多个 Kubernetes 集群会占用更多的计算、存储和网络资源,需要合理规划和调度。 + +总的来说,Kubernetes on Kubernetes 是一种强大的架构方法,可以帮助实现高度自动化和可扩展的集群管理,但也需要解决复杂性和资源管理的问题。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.25/Cassandra.md b/daliy_note/9月归档/9.25/Cassandra.md new file mode 100644 index 0000000..c752538 --- /dev/null +++ b/daliy_note/9月归档/9.25/Cassandra.md @@ -0,0 +1,61 @@ +> https://wallenotes.github.io/categories/%E6%95%B0%E6%8D%AE%E5%BA%93/Cassandra/ + +Apache Cassandra 是一个高可用性、高扩展性、分布式的 NoSQL 数据库管理系统。它最初由 Facebook 开发,并在 2008 年开源,后来成为 Apache 软件基金会的顶级项目。Cassandra 采用了去中心化的架构设计,能够在大规模分布式系统中实现高性能和高可用性。 + +### Cassandra 的主要特点 + +1. **去中心化架构**:Cassandra 采用去中心化的对等节点架构(Peer-to-Peer),没有单点故障,每个节点在集群中地位平等。数据自动在集群中均匀分布,保证了高可用性和容错性。 + +2. **线性扩展性**:Cassandra 能够通过增加节点来线性扩展处理能力和存储容量。无论是写操作还是读操作,性能都可以通过增加节点来提升。 + +3. **高可用性和容错性**:数据在多个节点之间进行复制,确保即使某些节点故障,数据依然可用。Cassandra 支持多数据中心的部署,能够在地理上分布的多个数据中心之间进行数据复制。 + +4. **灵活的数据模型**:Cassandra 采用了宽列存储模型(Wide Column Store),允许用户定义灵活的表结构。每个表可以有多个列族,每个列族可以包含任意数量的列。 + +5. Cassandra 提供了强一致性和最终一致性之间的灵活选择。用户可以根据需求调整读写操作的一致性级别(例如 `ONE`、`QUORUM`、`ALL`),以在性能和数据一致性之间进行权衡。 + +6. **高吞吐量和低延迟**:Cassandra 设计用于处理大规模的写入和读取操作,能够在高并发环境下保持低延迟和高吞吐量。 +7. **支持 CQL(Cassandra Query Language)**:CQL 类似于 SQL,但专为 Cassandra 的数据模型设计,提供了简单易用的查询接口。 + +### Cassandra 的核心组件 + +1. **节点(Node)**:Cassandra 集群中的基本单位,每个节点存储部分数据。所有节点地位平等,可以独立处理读写请求。 + +2. **数据中心(Data Center)**:多个节点可以组成一个数据中心,Cassandra 支持跨数据中心的部署和数据复制,以提高容灾能力和数据可用性。 + +3. **键空间(Keyspace)**:键空间是一个逻辑容器,用于组织表,类似于关系数据库中的数据库。它定义了数据复制策略等元数据信息。 + +4. **表(Table)**:表是数据存储的基本单位,由行和列组成。每行有一个唯一的主键,通过主键进行数据分区和分布。 + +5. **列族(Column Family)**:列族是表的一个逻辑分组,用于组织列。每个列族可以包含任意数量的列。 + +### Cassandra 的常见使用场景 + +1. **实时大数据分析**:由于其高吞吐量和低延迟,Cassandra 适合用于实时大数据分析和处理。例如,日志记录、监控系统、点击流分析等。 + +2. **社交网络**:Cassandra 能够高效处理社交网络中的大量用户数据和交互数据,支持高并发的读写操作。例如,Facebook 在其收件箱搜索系统中使用了 Cassandra。 + +3. **物联网(IoT)**:Cassandra 适合处理物联网设备产生的大量数据,能够在分布式环境中实现数据收集和处理。 + +4. **电子商务**:Cassandra 能够高效处理电子商务平台中的商品目录、用户数据、订单记录等,支持高并发的交易处理。 + +5. **金融服务**:金融机构可以利用 Cassandra 存储和分析金融交易数据、用户行为数据等,支持实时风控和欺诈检测。 + +### Cassandra 的优缺点 + +#### 优点 + +1. **高可用性和无单点故障**:去中心化的架构设计确保了高可用性和无单点故障。 +2. **线性扩展性**:能够通过增加节点来线性提升性能和存储容量。 +3. **灵活的数据模型**:宽列存储模型允许用户定义灵活的数据结构。 +4. **高吞吐量和低延迟**:设计用于处理大规模的写入和读取操作,性能表现优异。 + +#### 缺点 + +1. **学习曲线**:Cassandra 的数据模型和查询语言与传统关系数据库不同,可能需要一定的学习成本。 +2. **事务支持有限**:Cassandra 主要提供最终一致性,事务支持不如传统关系数据库强大。 +3. **复杂的运维管理**:大规模的分布式系统需要专业的运维管理和监控,运维成本较高。 + +### 总结 + +Apache Cassandra 是一个强大的分布式 NoSQL 数据库,适用于需要高可用性、高扩展性和高性能的应用场景。尽管它的学习和运维成本较高,但其在处理大规模数据和高并发操作中的优势,使其成为诸多企业和应用的首选。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.25/时序数据存储方案.md b/daliy_note/9月归档/9.25/时序数据存储方案.md new file mode 100644 index 0000000..57d7d70 --- /dev/null +++ b/daliy_note/9月归档/9.25/时序数据存储方案.md @@ -0,0 +1,115 @@ +这些系统都是用于时间序列数据存储和监控的解决方案,但它们在架构、功能、性能和使用场景上各有不同。以下是 Prometheus、M3DB、VictoriaMetrics、Thanos 和 Cortex 的对比。 + +### Prometheus + +**概述**: +- Prometheus 是一个开源的系统监控和报警工具包,最初由 SoundCloud 开发,并于 2016 年加入 CNCF。 +- 主要用于监控和告警,设计目标是可靠性和快速查询。 + +**特点**: +- **数据模型**:时间序列数据库,数据按时间序列存储。 +- **查询语言**:PromQL,功能强大,适合复杂查询。 +- **架构**:单节点设计,但可以通过远程存储集成扩展。 +- **数据持久化**:本地存储,支持远程写入和读取。 +- **高可用性**:单节点设计有单点故障问题,但可以通过 Thanos 或 Cortex 实现高可用性。 + +**使用场景**: +- 适合中小规模的监控系统。 +- 需要复杂查询和告警功能的场景。 + +### M3DB + +**概述**: +- M3DB 是 Uber 开发的分布式时间序列数据库,设计用于大规模监控系统。 + +**特点**: +- **数据模型**:时间序列数据库,支持高吞吐量和低延迟。 +- **查询语言**:支持 PromQL 以及其他查询语言。 +- **架构**:分布式架构,支持水平扩展。 +- **数据持久化**:支持多种存储后端,如本地磁盘、S3 等。 +- **高可用性**:内置高可用性和数据复制 +- **数据压缩**:高效的数据压缩算法,适合大规模数据存储。 + +- **集成**:与 Prometheus 兼容,可以作为 Prometheus 的远程存储后端。 +- **运维**:需要较高的运维和管理成本,适合有专门运维团队的组织。 + +**使用场景**: +- 超大规模监控系统,需要高吞吐量和低延迟的场景。 +- 需要持久化存储和高可用性的场景,如金融服务、物联网等。 + +### VictoriaMetrics + +**概述**: +- VictoriaMetrics 是一个高性能、开源的时间序列数据库,设计用于高效存储和检索大规模时间序列数据。 + +**特点**: +- **数据模型**:时间序列数据库,支持高并发写入和查询。 +- **查询语言**:支持 PromQL。 +- **架构**:单节点和集群模式,集群模式支持水平扩展。 +- **数据持久化**:本地存储,支持远程存储(如 S3)。 +- **高可用性**:集群模式提供高可用性和数据复制。 + +**性能**: +- **高性能**:高效的存储和压缩算法,适合大规模数据的存储和查询。 +- **低资源消耗**:相比其他解决方案,资源消耗较低。 + +**使用场景**: +- 高性能、大规模数据存储和查询的场景。 +- 资源受限的环境,如边缘计算和嵌入式系统。 + +### Thanos + +**概述**: +- Thanos 是一个开源项目,扩展了 Prometheus 的功能,提供长时间存储、全局查询和高可用性。 + +**特点**: +- **数据模型**:基于 Prometheus 的时间序列数据模型。 +- **查询语言**:支持 PromQL。 +- **架构**:模块化架构,包括 Sidecar、Store Gateway、Query、Compactor 等组件。 +- **数据持久化**:与对象存储(如 S3、GCS)集成,实现长时间存储。 +- **高可用性**:支持 Prometheus 的高可用性,通过多 Prometheus 实例和全局查询实现。 + +**功能**: +- **长时间存储**:解决 Prometheus 本地存储的局限性。 +- **全局查询**:跨多个 Prometheus 实例进行全局查询。 +- **高可用性**:实现 Prometheus 的高可用性和数据冗余。 + +**使用场景**: +- 需要长时间数据存储和全局查询的场景。 +- 需要高可用性监控的场景,如大规模分布式系统。 + +### Cortex + +**概述**: +- Cortex 是一个开源项目,用于将 Prometheus 扩展到大规模、高可用性的环境,提供多租户支持。 + +**特点**: +- **数据模型**:基于 Prometheus 的时间序列数据模型。 +- **查询语言**:支持 PromQL。 +- **架构**:模块化架构,包括 Distributor、Ingester、Querier、Store Gateway 等组件。 +- **数据持久化**:支持多种存储后端,如 DynamoDB、Cassandra、S3 等。 +- **高可用性**:内置高可用性和数据分片、复制功能。 + +**功能**: +- **多租户支持**:适合多租户环境,如 SaaS 平台。 +- **水平扩展**:支持水平扩展,适合大规模监控系统。 +- **高可用性**:通过数据分片和复制实现高可用性。 + +**使用场景**: +- 多租户、大规模、高可用性监控的场景。 +- 需要 Prometheus 兼容性和扩展性的场景。 + +### 总结对比 + +| 特性 | Prometheus | M3DB | VictoriaMetrics | Thanos | Cortex | +| ---------- | ------------ | ---------------------- | ---------------------- | -------------------- | ------------------ | +| 数据模型 | 时间序列 | 时间序列 | 时间序列 | 时间序列 | 时间序列 | +| 查询语言 | PromQL | PromQL, 其他 | PromQL | PromQL | PromQL | +| 架构 | 单节点 | 分布式 | 单节点、集群 | 模块化扩展 | 模块化扩展 | +| 数据持久化 | 本地存储 | 多种存储后端 | 本地存储、远程存储 | 对象存储(如 S3) | 多种存储后端 | +| 高可用性 | 单节点故障 | 内置高可用性和数据复制 | 集群模式高可用性 | 支持高可用性 | 内置高可用性 | +| 主要优点 | 简单易用 | 高吞吐量、低延迟 | 高性能、低资源消耗 | 长时间存储、全局查询 | 多租户、高可用性 | +| 主要缺点 | 单点故障 | 运维成本较高 | 单节点模式有局限 | 需要额外配置和维护 | 复杂性较高 | +| 适用场景 | 中小规模监控 | 超大规模监控 | 高性能、大规模存储查询 | 长时间存储、高可用性 | 多租户、大规模监控 | + +选择合适的时间序列数据库和监控解决方案需要根据具体的需求、规模和资源情况来决定。Prometheus 适合中小规模的监控,M3DB 和 Cortex 适合大规模高性能的监控,而 Thanos 和 VictoriaMetrics 则提供了长时间存储和高可用性的解决方案。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.26/nodejs_promise.md b/daliy_note/9月归档/9.26/nodejs_promise.md new file mode 100644 index 0000000..9e600c3 --- /dev/null +++ b/daliy_note/9月归档/9.26/nodejs_promise.md @@ -0,0 +1,141 @@ +在 Node.js 中,`Promise` 是一种用于处理异步操作的方式。它可以帮助你简化嵌套的回调函数(即“回调地狱”),并提供更加直观的代码结构。在本回答中,我将介绍如何在 Node.js 中使用 `Promise`,包括创建和使用 `Promise`,以及如何将回调函数转换为 `Promise`。 + +### 基本概念 + +一个 `Promise` 有三种状态: + +1. **Pending**:初始状态,既没有被解决(fulfilled)也没有被拒绝(rejected)。 +2. **Fulfilled**:操作成功完成。 +3. **Rejected**:操作失败。 + +### 创建和使用 `Promise` + +#### 创建 `Promise` + +你可以通过 `new Promise` 来创建一个 `Promise` 对象。它接收一个执行函数,该函数有两个参数:`resolve` 和 `reject`,分别用于表示操作成功和失败。 + +```javascript +const myPromise = new Promise((resolve, reject) => { + // 异步操作 + setTimeout(() => { + const success = true; // 你可以根据实际情况设置成功或失败 + + if (success) { + resolve('Operation completed successfully!'); + } else { + reject('Operation failed!'); + } + }, 1000); +}); +``` + +#### 使用 `Promise` + +你可以使用 `then` 和 `catch` 方法来处理 `Promise` 的结果。 + +```javascript +myPromise + .then(result => { + console.log(result); // 输出: Operation completed successfully! + }) + .catch(error => { + console.error(error); // 输出: Operation failed! + }); +``` + +### 将回调函数转换为 `Promise` + +许多 Node.js 核心模块(如 `fs`)使用回调函数。你可以使用 `util.promisify` 将这些回调函数转换为返回 `Promise` 的函数。 + +#### 使用 `util.promisify` + +```javascript +const fs = require('fs'); +const util = require('util'); + +const readFile = util.promisify(fs.readFile); + +readFile('example.txt', 'utf8') + .then(data => { + console.log(data); + }) + .catch(error => { + console.error(error); + }); +``` + +#### 手动转换回调函数 + +你也可以手动将回调函数转换为 `Promise`。 + +```javascript +const fs = require('fs'); + +function readFilePromise(filePath, encoding) { + return new Promise((resolve, reject) => { + fs.readFile(filePath, encoding, (err, data) => { + if (err) { + reject(err); + } else { + resolve(data); + } + }); + }); +} + +readFilePromise('example.txt', 'utf8') + .then(data => { + console.log(data); + }) + .catch(error => { + console.error(error); + }); +``` + +### 使用 `async`/`await` + +`async` 和 `await` 是基于 `Promise` 的语法糖,可以让你的异步代码看起来像同步代码。它们使得代码更具可读性和可维护性。 + +```javascript +const fs = require('fs').promises; // 使用 fs.promises 提供的基于 Promise 的方法 + +async function readFileAsync(filePath) { + try { + const data = await fs.readFile(filePath, 'utf8'); + console.log(data); + } catch (error) { + console.error(error); + } +} + +readFileAsync('example.txt'); +``` + +### 链式调用和错误处理 + +你可以通过链式调用来处理多个异步操作,并在最后使用 `catch` 处理所有可能的错误。 + +```javascript +const fs = require('fs').promises; + +fs.readFile('example.txt', 'utf8') + .then(data => { + console.log('File content:', data); + return fs.writeFile('example_copy.txt', data); + }) + .then(() => { + console.log('File copied successfully'); + }) + .catch(error => { + console.error('Error:', error); + }); +``` + +### 总结 + +- `Promise` 为异步操作提供了一种更加直观的处理方式。 +- `then` 和 `catch` 方法用于处理 `Promise` 的结果和错误。 +- `util.promisify` 可以将回调函数转换为返回 `Promise` 的函数。 +- `async`/`await` 是基于 `Promise` 的语法糖,使得异步代码更具可读性。 + +通过这些方法,你可以在 Node.js 中更高效地处理异步操作。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.26/prometheus无数据报警.md b/daliy_note/9月归档/9.26/prometheus无数据报警.md new file mode 100644 index 0000000..95f5c0b --- /dev/null +++ b/daliy_note/9月归档/9.26/prometheus无数据报警.md @@ -0,0 +1,146 @@ +无数据报警(也称为“空数据报警”或“数据缺失报警”),指的是当预期的数据没有按时到达或数据源停止输出时触发报警。这在监控系统中非常重要,因为它可以帮助你及时发现数据采集系统或服务出现的问题。以下是一个实现无数据报警的方案,主要基于 Prometheus 和 Alertmanager。 + +### 方案步骤 + +#### 1. 配置 Prometheus 规则 +在 Prometheus 中配置报警规则,检测某个时间窗口内数据是否缺失。假设你想监控一个指标 `http_requests_total`,并在 5 分钟内没有任何数据时触发报警。 + +```yaml +groups: + - name: NoDataAlertGroup + rules: + - alert: NoDataForHttpRequests + expr: absent(http_requests_total) == 1 + for: 5m + labels: + severity: critical + annotations: + summary: "No data for http_requests_total" + description: "No data has been received for the metric http_requests_total in the last 5 minutes." +``` + +这个规则使用 `absent()` 函数来检测 `http_requests_total` 这个指标是否不存在。如果在 5 分钟内没有数据,这个报警会被触发。 + +#### 2. 部署 Alertmanager +确保你的 Alertmanager 已经部署并与 Prometheus 集成。你可以在 Prometheus 的配置文件 `prometheus.yml` 中添加 Alertmanager 的配置。 + +```yaml +alerting: + alertmanagers: + - static_configs: + - targets: + - 'alertmanager-service:9093' +``` + +#### 3. 配置 Alertmanager 接收器 + +在 Alertmanager 的配置文件 `alertmanager.yml` 中,配置接收器来处理报警通知。以下是一个示例,设置了 Slack 接收器: + +```yaml +route: + group_by: ['alertname'] + group_wait: 30s + group_interval: 5m + repeat_interval: 3h + receiver: 'slack-notifications' + +receivers: + - name: 'slack-notifications' + slack_configs: + - api_url: 'https://hooks.slack.com/services/T00000000/B00000000/XXXXXXXXXXXXXXXXXXXXXXXX' + channel: '#alerts' + text: "{{ .CommonAnnotations.description }}" +``` + +在这个配置中,`route` 部分定义了报警的路由规则,`receivers` 部分定义了接收器的配置。你可以根据需要替换为其他接收器,比如电子邮件、PagerDuty 甚至自定义的 Webhook。 + +#### 4. 测试报警配置 + +为了确保无数据报警配置正确,可以手动停止某些数据源或服务,然后观察是否能够及时收到报警通知。 + +1. **停止数据源**:例如,停止发送 `http_requests_total` 数据的服务。 +2. **等待报警**:根据配置的 `for` 时间等待一段时间(如 5 分钟)。 +3. **检查报警**:在 Slack 或其他接收器中查看是否收到了报警通知。 + +#### 5. 可视化和监控 + +使用 Grafana 来可视化和监控这些报警数据,可以帮助你更直观地了解系统的健康状况。 + +1. **添加 Prometheus 数据源**:在 Grafana 中添加 Prometheus 作为数据源。 +2. **创建仪表板**:创建一个仪表板来展示关键指标和报警状态。例如,你可以创建一个面板来展示 `http_requests_total` 指标的变化情况,以及当前的报警状态。 + +#### 6. 优化和调整 + +根据实际情况,不断优化和调整报警规则。例如,你可能需要: + +- **调整报警阈值**:根据业务需求调整报警触发的时间窗口和阈值。 +- **细化报警规则**:为不同的指标配置不同的报警规则,以便更精细地监控系统。 + +### 总结 + +通过上述步骤,你可以在云原生环境中实现无数据报警方案。核心思路是使用 Prometheus 配置 `absent()` 函数检测数据缺失,并通过 Alertmanager 发送报警通知。结合 Grafana,可以实现可视化监控和报警管理,从而确保系统的可靠性和稳定性。 + +### unless 关键字 + +在 Prometheus 中,`unless` 是用于执行集合差集运算的关键字。它用于过滤掉在一个集合中出现但在另一个集合中也出现的数据点。简单来说,`A unless B` 表示从集合 A 中剔除所有在集合 B 中也出现的数据点。 + +#### 基本语法 + +```promql +vector1 unless vector2 +``` + +- **vector1**:第一个向量表达式。 +- **vector2**:第二个向量表达式。 +- **结果**:返回在 `vector1` 中但不在 `vector2` 中的所有数据点。 + +#### 示例 + +假设你有两个指标:`up`(表示服务是否正常运行)和 `http_requests_total`(表示 HTTP 请求总数)。你希望找出那些有 HTTP 请求的服务,但这些服务没有运行: + +```promql +http_requests_total unless up +``` + +这个查询将返回那些有 HTTP 请求但服务不在运行状态的实例。 + +### 实现无数据报警的另一种方式 + +你可以结合 `unless` 关键字来实现无数据报警。假设你想监控一个名为 `http_requests_total` 的指标,如果某一实例在过去 5 分钟内没有数据,你希望触发报警。你可以使用以下 PromQL 表达式: + +```promql +up unless http_requests_total[5m] +``` + +这个表达式将返回那些在过去 5 分钟内没有 `http_requests_total` 数据的实例(前提是这些实例仍然在运行状态)。 + +结合报警规则: + +```yaml +groups: +- name: example-alerts + rules: + - alert: NoDataForHttpRequests + expr: up unless http_requests_total[5m] + for: 5m + labels: + severity: critical + annotations: + summary: "No data for http_requests_total" + description: "No data has been received for the metric http_requests_total in the last 5 minutes." +``` + +### 进一步优化和扩展 + +- **多指标组合**:你可以使用 `unless` 结合多个指标来实现更复杂的报警规则。例如,监控多个服务的健康状态和数据采集情况。 +- **细化标签**:通过在查询中添加标签,可以细化报警规则。例如,只监控特定服务或特定实例的数据缺失情况。 + +```promql +up{job="myservice"} unless http_requests_total{job="myservice"}[5m] +``` + +- **结合其他 PromQL 函数**:你可以结合其他 PromQL 函数(如 `rate`, `avg_over_time` 等)来实现更复杂的查询和报警规则。 + +### 总结 + +使用 Prometheus 的 `unless` 关键字,你可以轻松地实现无数据报警。通过编写合适的 PromQL 表达式并配置报警规则,你可以确保在数据缺失的情况下及时收到通知。这是 Prometheus 强大查询功能的一个具体应用,能够帮助你更好地监控和维护你的系统。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.4/git.md b/daliy_note/9月归档/9.4/git.md new file mode 100644 index 0000000..072a34e --- /dev/null +++ b/daliy_note/9月归档/9.4/git.md @@ -0,0 +1 @@ +https://cloud.tencent.com/developer/article/1590046 \ No newline at end of file diff --git a/daliy_note/9月归档/9.4/ssh问题.md b/daliy_note/9月归档/9.4/ssh问题.md new file mode 100644 index 0000000..8a99826 --- /dev/null +++ b/daliy_note/9月归档/9.4/ssh问题.md @@ -0,0 +1,3 @@ +Host * +HostkeyAlgorithms=+ssh-rsa,ssh-dss +PubkeyAcceptedAlgorithms=+ssh-rsa,ssh-dss \ No newline at end of file diff --git a/daliy_note/9月归档/9.4/uri和url.md b/daliy_note/9月归档/9.4/uri和url.md new file mode 100644 index 0000000..229e865 --- /dev/null +++ b/daliy_note/9月归档/9.4/uri和url.md @@ -0,0 +1,62 @@ +URI(Uniform Resource Identifier)和URL(Uniform Resource Locator)是两个常见的术语,它们用于标识和访问网络资源。尽管它们有许多相似之处,但它们并不是完全相同的概念。 + +### URI + +URI(统一资源标识符)是一个通用的标识符,用于标识任何资源。URI可以是一个URL,也可以是URN(Uniform Resource Name)。URI的语法由RFC 3986标准定义。 + +URI的基本结构如下: + +``` +scheme:[//[userinfo@]host[:port]]path[?query][#fragment] +``` + + + +- **scheme**:指定使用的协议(例如,http, https, ftp)。 +- **userinfo**:可选的用户信息,通常包含用户名和密码。 +- **host**:资源所在的主机名或IP地址。 +- **port**:可选的端口号。 +- **path**:资源的路径。 +- **query**:可选的查询字符串,用于传递参数。 +- **fragment**:可选的片段标识符,用于指定资源的某部分。 + +### URL + +URL(统一资源定位符)是URI的一个子集,专门用于标识网络资源的定位信息。URL不仅标识资源,而且提供了如何访问资源的具体方法。URL的基本结构也是由RFC 3986定义的。 + +URL的基本结构如下: + +``` +scheme://userinfo@host:port/path?query#fragment +``` + + + +例如: + +``` +https://www.example.com:8080/path/to/resource?search=query#section +``` + + + +在这个例子中: + +- `https` 是方案,表示使用HTTPS协议。 +- `www.example.com` 是主机名。 +- `8080` 是端口号。 +- `/path/to/resource` 是路径。 +- `?search=query` 是查询字符串。 +- `#section` 是片段标识符。 + +### 区别 + +- **URI 是一个更广泛的概念**:它可以标识任何资源,而不仅仅是通过网络协议访问的资源。 +- **URL 是 URI 的一个特例**:它不仅标识资源,还提供访问资源的具体方法。 + +### 总结 + +- **URI**:统一资源标识符,标识和命名资源。 +- **URL**:统一资源定位符,标识和定位资源。 + +在实践中,URL是最常见的URI形式,特别是在Web开发和互联网应用中。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.5/9.5.md b/daliy_note/9月归档/9.5/9.5.md new file mode 100644 index 0000000..e69de29 diff --git a/daliy_note/9月归档/9.5/helm.md b/daliy_note/9月归档/9.5/helm.md new file mode 100644 index 0000000..e777679 --- /dev/null +++ b/daliy_note/9月归档/9.5/helm.md @@ -0,0 +1,7 @@ +### 下载 && 安装 + +```bash +sudo -i +curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash +``` + diff --git a/daliy_note/9月归档/9.5/kubectl.md b/daliy_note/9月归档/9.5/kubectl.md new file mode 100644 index 0000000..7403f6f --- /dev/null +++ b/daliy_note/9月归档/9.5/kubectl.md @@ -0,0 +1,14 @@ +### 下载 + +```bash + curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +``` + + + +### 安装 + +```bash +sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl +``` + diff --git a/daliy_note/9月归档/9.6/docker.md b/daliy_note/9月归档/9.6/docker.md new file mode 100644 index 0000000..1bce70d --- /dev/null +++ b/daliy_note/9月归档/9.6/docker.md @@ -0,0 +1,4 @@ +### 空容器一直运行 + +docker run -d ubuntu /bin/bash -c "while true; do sleep 1; echo i_am_a_container;done" + diff --git a/daliy_note/9月归档/9.6/shell.md b/daliy_note/9月归档/9.6/shell.md new file mode 100644 index 0000000..8bee23b --- /dev/null +++ b/daliy_note/9月归档/9.6/shell.md @@ -0,0 +1,191 @@ +在 shell 脚本中,`if` 语句用于条件判断,有多种条件表达式可以使用,其中 `-z`、`-n` 和 `-x` 是用于字符串和文件的条件测试选项。以下是对这些选项的详细解释: + +### 字符串条件判断 + +#### `-z` + +`-z` 用于判断字符串是否为空。如果字符串的长度为零,则条件为真。 + +```sh +if [ -z "$string" ]; then + echo "String is empty" +else + echo "String is not empty" +fi +``` + +#### `-n` + +`-n` 用于判断字符串是否不为空。如果字符串的长度不为零,则条件为真。 + +```sh +if [ -n "$string" ]; then + echo "String is not empty" +else + echo "String is empty" +fi +``` + +### 文件条件判断 + +#### `-x` + +`-x` 用于判断文件是否有可执行权限。如果文件存在且有可执行权限,则条件为真。 + +```sh +if [ -x "$file" ]; then + echo "File is executable" +else + echo "File is not executable or does not exist" +fi +``` + +### 其他常见文件条件判断选项 + +除了 `-x`,还有其他一些常见的文件条件判断选项: + +- `-e`:判断文件是否存在。 +- `-f`:判断是否为普通文件。 +- `-d`:判断是否为目录。 +- `-r`:判断文件是否有读权限。 +- `-w`:判断文件是否有写权限。 + +### 综合示例 + +以下是一个综合示例,演示如何使用 `-z`、`-n` 和 `-x` 进行条件判断: + +```sh +#!/bin/bash + +file="/path/to/your/file" +string="Hello, World!" + +# 判断字符串是否为空 +if [ -z "$string" ]; then + echo "String is empty" +else + echo "String is not empty" +fi + +# 判断字符串是否不为空 +if [ -n "$string" ]; then + echo "String is not empty" +else + echo "String is empty" +fi + +# 判断文件是否有可执行权限 +if [ -x "$file" ]; then + echo "File is executable" +else + echo "File is not executable or does not exist" +fi +``` + +在 Shell 脚本中,`if` 条件判断可以使用多种标识符来检查文件属性、字符串内容、数值比较等。以下是一些常见的 `if` 判断标识符: + +### 文件条件判断标识符 + +- `-e FILE`:判断文件是否存在。 +- `-f FILE`:判断是否为普通文件。 +- `-d FILE`:判断是否为目录。 +- `-r FILE`:判断文件是否可读。 +- `-w FILE`:判断文件是否可写。 +- `-x FILE`:判断文件是否可执行。 +- `-s FILE`:判断文件是否非空(文件大小是否大于零)。 +- `-L FILE`:判断是否为符号链接。 +- `-p FILE`:判断是否为命名管道(FIFO)。 +- `-c FILE`:判断是否为字符设备文件。 +- `-b FILE`:判断是否为块设备文件。 + +### 字符串条件判断标识符 + +- `-z STRING`:判断字符串是否为空(长度为零)。 +- `-n STRING`:判断字符串是否不为空(长度不为零)。 +- `STRING1 = STRING2`:判断两个字符串是否相等。 +- `STRING1 != STRING2`:判断两个字符串是否不相等。 + +### 数值条件判断标识符 + +- `-eq`:等于(equal)。 +- `-ne`:不等于(not equal)。 +- `-lt`:小于(less than)。 +- `-le`:小于或等于(less than or equal)。 +- `-gt`:大于(greater than)。 +- `-ge`:大于或等于(greater than or equal)。 + +### 逻辑运算符 + +- `!`:逻辑非(not)。 +- `-a`:逻辑与(and)。 +- `-o`:逻辑或(or)。 + +### 组合示例 + +以下是一个示例脚本,演示如何使用这些条件判断标识符: + +```sh +#!/bin/bash + +file="/path/to/your/file" +string1="Hello" +string2="World" +num1=10 +num2=20 + +# 文件条件判断 +if [ -e "$file" ]; then + echo "File exists" +else + echo "File does not exist" +fi + +if [ -d "$file" ]; then + echo "File is a directory" +else + echo "File is not a directory" +fi + +# 字符串条件判断 +if [ -z "$string1" ]; then + echo "String1 is empty" +else + echo "String1 is not empty" +fi + +if [ "$string1" = "$string2" ]; then + echo "Strings are equal" +else + echo "Strings are not equal" +fi + +# 数值条件判断 +if [ $num1 -eq $num2 ]; then + echo "Numbers are equal" +else + echo "Numbers are not equal" +fi + +if [ $num1 -lt $num2 ]; then + echo "num1 is less than num2" +else + echo "num1 is not less than num2" +fi + +# 逻辑运算 +if [ -e "$file" -a -x "$file" ]; then + echo "File exists and is executable" +else + echo "File does not exist or is not executable" +fi + +if [ -n "$string1" -o -n "$string2" ]; then + echo "At least one of the strings is not empty" +else + echo "Both strings are empty" +fi +``` + +### 结论 + +在 shell 脚本中,有多种条件判断标识符可以使用,用于检查文件属性、字符串内容、数值比较和逻辑运算。这些标识符使得脚本能够根据不同条件执行不同的操作,从而增强脚本的灵活性和功能性。 \ No newline at end of file diff --git a/daliy_note/9月归档/9.9/git.md b/daliy_note/9月归档/9.9/git.md new file mode 100644 index 0000000..02d5fc3 --- /dev/null +++ b/daliy_note/9月归档/9.9/git.md @@ -0,0 +1,5 @@ +https://stackoverflow.com/questions/66366582/github-unexpected-disconnect-while-reading-sideband-packet + + + +git config --global http.postBuffer 157286400 \ No newline at end of file diff --git a/note/.gitignore b/note/.gitignore new file mode 100644 index 0000000..e43b0f9 --- /dev/null +++ b/note/.gitignore @@ -0,0 +1 @@ +.DS_Store diff --git a/note/Blog/chatgpt.md b/note/Blog/chatgpt.md new file mode 100644 index 0000000..54a3f20 --- /dev/null +++ b/note/Blog/chatgpt.md @@ -0,0 +1,14 @@ +### 部署容器 ChatGPT + +- PandoraNext + +```bash +docker run -d --restart always \ +--name PandoraNext \ +--net=internal \ +-p 8181:8181 \ +-v /root/data/pandora/data:/data \ +-v /root/data/pandora/sessions:/root/.cache/PandoraNext \ +pengzhile/pandora-next +``` + diff --git a/note/Blog/折腾wordpress.md b/note/Blog/折腾wordpress.md new file mode 100644 index 0000000..6cb149f --- /dev/null +++ b/note/Blog/折腾wordpress.md @@ -0,0 +1,82 @@ +### docker 部署 mysql + +```bash +docker run -d --privileged=true \ +--net=internal \ +--name mysql \ +-v /root/data/mysql:/var/lib/mysql \ +-e MYSQL_ROOT_PASSWORD=XXXXX \ +-p 3206:3306 \ +mysql:5.7 +``` + +### docker 部署 wordpress + +```bash +docker run -itd \ +--name wp \ +--net=internal \ +--link mysql \ +wordpress:latest +``` + + + +### Nginx 代理 wordpress + +```nginx +server{ + listen 443 ssl; + server_name test.heysq.com; + ssl_certificate /etc/nginx/ssls/heysq_com/cert.pem; + ssl_certificate_key /etc/nginx/ssls/heysq_com/key.pem; + ssl_session_timeout 5m; + client_max_body_size 500m; + location / { + proxy_pass http://wp:80; + proxy_redirect off; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Host $server_name; + proxy_set_header X-Forwarded-Proto https; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_read_timeout 86400; + } +} +``` + +### 访问test.heysq.com + +- 页面报错阻止加载混合活动内容,因为浏览器安全策略,禁止https网站内部加载http请求 + + ![image-20231220101825710](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/note/image-20231220101825710.png) + +### wordpress配置 + +- 在没有页面样式的情况下进行wordpress配置数据库和站点信息 + +- 配置完站点信息后,进入wordpress容器查看生成的`wp-config.php`文件 + + image-20231220102119917 + +- 按照wordpress官网的教程,在`wp-config.php`文件中添加以下代码 + +```php +define('FORCE_SSL_ADMIN', true); + +if (strpos($_SERVER['HTTP_X_FORWARDED_PROTO'], 'https') !== false){ + $_SERVER['HTTPS'] = 'on'; + $_SERVER['SERVER_PORT'] = 443; +} +if (isset($_SERVER['HTTP_X_FORWARDED_HOST'])) { + $_SERVER['HTTP_HOST'] = $_SERVER['HTTP_X_FORWARDED_HOST']; +} + +define('WP_HOME','https://test.heysq.com/'); // 替换成自己网站的域名 +define('WP_SITEURL','https://test.heysq.com/'); // 替换成自己网站的域名 +``` + +- 刷新站点页面,wordpress 样式恢复正常 + diff --git a/note/Go/GMP.md b/note/Go/GMP.md new file mode 100644 index 0000000..356c4b5 --- /dev/null +++ b/note/Go/GMP.md @@ -0,0 +1,69 @@ +### Go goroutine调度 +- 目的:将 Goroutine 按照一定算法放到不同的操作系统线程中去执行 +- 调度模型与算法几经演化,从最初的 G-M 模型、到 G-P-M 模型,从不支持抢占,到支持协作式抢占,再到支持基于信号的异步抢占 + +### GM模型 +- 每个 Goroutine 对应于运行时中的一个抽象结构:G(Goroutine) +- 被视作“物理 CPU”的操作系统线程,则被抽象为另外一个结构:M(machine) + +#### GM不足 +- 单一全局互斥锁(Sched.Lock) 和集中状态存储的存在,导致所有 Goroutine 相关操作,比如创建、重新调度等,都要上锁 +- Goroutine 传递问题:M 经常在 M 之间传递“可运行”的 Goroutine,这导致调度延迟增大,也增加了额外的性能损耗 +- 每个 M 都做内存缓存,导致内存占用过高,数据局部性较差 +- 由于系统调用(syscall)而形成的频繁的工作线程阻塞和解除阻塞,导致额外的性能损耗 +> 集中状态(centralized state),就是一把全局锁要保护的数据太多。这样无论访问哪个数据,都要锁这把全局锁。数据局部性差是因为每个m都会缓存它执行的代码或数据,但是如果在m之间频繁传递goroutine,那么这种局部缓存的意义就没有了。无法实现局部缓存带来的性能提升。 + + +### GMP模型 +- 在 Go 1.1 版本中实现了 G-P-M 调度模型和work stealing 算法,这个模型一直沿用至今 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/gmp.png) + + - P 是一个“逻辑 Proccessor”,每个 G(Goroutine)要想真正运行起来,首先需要被分配一个 P,也就是进入到 P 的本地运行队列(local runq)中 + - 对于 G 来说,P 就是运行它的“CPU”,可以说:在 G 的眼里只有 P + - 从 Go 调度器的视角来看,真正的“CPU”是 M,只有将 P 和 M 绑定,才能让 P 的 runq 中的 G 真正运行起来 + +- Go 1.1 模型不支持抢占式调度 +- GO 1.2 加入抢占式调度,原理就是,Go 编译器在每个函数或方法的入口处加上了一段额外的代码 (runtime.morestack_noctxt),让运行时有机会在这段代码中检查是否需要执行抢占调度,弊端就是只有在函数或方法不能能加入代码,纯算法循环代码无法加入调度功能(比如死循环) +- Go 1.14 版本中增加了对非协作的抢占式调度的支持,这种抢占式调度是基于系统信号的,也就是通过向线程发送信号的方式来抢占正在运行的 Goroutine +> 协作式:大家都按事先定义好的规则来,比如:一个goroutine执行完后,退出,让出p,然后下一个goroutine被调度到p上运行。这样做的缺点就在于 是否让出p的决定权在groutine自身。一旦某个g不主动让出p或执行时间较长,那么后面的goroutine只能等着,没有方法让前者让出p,导致延迟甚至饿死。而非协作: 就是由runtime来决定一个goroutine运行多长时间,如果你不主动让出,对不起,我有手段可以抢占你,把你踢出去,让后面的goroutine进来运行 +### G +- goroutine 缩写,每次go func()都代表一个G,无限制,而且 G 对象是可以重用的 +- 使用struct runtime.g,包含了当前goroutine的状态,堆栈和上下文 + +### M +- 工作线程(OS thread)也被称为Machine,使用 struct runtime.m,所有M是有线程栈的(1-8M) +- 执行流程是从 P 的本地运行队列以及全局队列中获取 G,切换到 G 的执行栈上并执行 G 的函数,调用 goexit 做清理工作并回到 M,如此反复 +- 如果不对该线程栈提供内存的话,系统会给该线程栈提供内存(不同操作系统提供的线程栈大小不同)。当指定了线程栈,则 M.stack→G.stack,M 的 PC 寄存器指向 G 提供的函数,然后去执行 + +### P +- Processor是一个抽象概念,并不是真正的物理CPU +- 代表了M所需的上下文环境,也是处理用户级代码逻辑的处理器。负责衔接M和G的调度上下文,将等待执行的G和M进行连接。当P有任务是需要创建或者唤醒一个M来执行 它队列里的任务,所以P/M需要进行绑定,构成一个执行单元 +- P决定了并发任务的数量,通过runtime.GOMAXPROCS来设定,Go1.5之后被默认设置为可用核数,之前默认设置为1 + +### GMP优化部分 +- netpoller,即便 G 发起网络 I/O 操作,也不会导致 M 被阻塞(仅阻塞 G),也就不会导致大量线程(M)被创建出来 +- io poller,这个功能可以像 netpoller 那样,在 G 操作那些支持监听(pollable)的文件描述符时,仅会阻塞 G,而不会阻塞 M。不支持对常规文件的监听 + +### 协作抢占式监控线程 sysmon M +- M 的特殊之处在于它不需要绑定 P 就可以运行(以 g0 这个 G 的形式) +- sysmon 每 20us~10ms 启动一次 +- 释放闲置超过 5 分钟的 span 内存 +- 如果超过 2 分钟没有垃圾回收,强制执行 +- 将长时间未处理的 netpoll 结果添加到任务队列 +- 向长时间运行的 G 任务发出抢占调度 +- 收回因 syscall 长时间阻塞的 P + +### sysmon M 抢占调度goroutine +- 如果一个 G 任务运行 10ms,sysmon 就会认为它的运行时间太久而发出抢占式调度的请求 +- 一旦 G 的抢占标志位被设为 true,等到这个 G 下一次调用函数或方法时,运行时就可以将 G 抢占并移出运行状态,放入队列中,等待下一次被调度 + +### channel 阻塞或网络 I/O 情况下的调度 +- G 被阻塞在某个 channel 操作或网络 I/O 操作上时,G 会被放置到某个等待(wait)队列中,而 M 会尝试运行 P 的下一个可运行的 G +- 如果这个时候 P 没有可运行的 G 供 M 运行,那么 M 将解绑 P,并进入挂起状态 +- 当 I/O 操作完成或 channel 操作完成,在等待队列中的 G 会被唤醒,标记为可运行(runnable),并被放入到某 P 的队列中,绑定一个 M 后继续执行 + +### 系统调用阻塞情况下的调度 +- G 被阻塞在某个系统调用(system call)上,不光 G 会阻塞,执行这个 G 的 M 也会解绑 P,与 G 一起进入挂起状态 +- 如果此时有空闲的 M,那么 P 就会和它绑定,并继续执行其他 G +- 如果没有空闲的 M,但仍然有其他 G 要去执行,Go 运行时就会创建一个新 M(线程) +- 当进行一些慢系统调用的时候,比如常规文件io,执行系统调用的m就要和g一起挂起,这是os的要求,不是go runtime的要求。毕竟真正执行代码的还是m diff --git a/note/Go/GMP细节.md b/note/Go/GMP细节.md new file mode 100644 index 0000000..b80a0e2 --- /dev/null +++ b/note/Go/GMP细节.md @@ -0,0 +1,30 @@ +[Go:g0,特殊的 Goroutine](https://zhuanlan.zhihu.com/p/213745994) +### 创建P +- P 的初始化:首先会创建逻辑 CPU 核数个 P ,存储在 sched 的 空闲链表(pidle)。 +![image](https://user-images.githubusercontent.com/39154923/127602696-7a68c508-2e07-43e8-b688-59346dc5b049.png) + +### 创建os thread +- 准备运行的新 goroutine 将唤醒 P 以更好地分发工作。这个 P 将创建一个与之关联的 M 绑定到一个OS thread +- go func() 中 触发 Wakeup 唤醒机制,有空闲的 Processor 而没有在 spinning 状态的 Machine 时候, 需要去唤醒一个空闲(睡眠)的 M 或者新建一个。spinning就是自选状态,没有任务处理,自旋一段时间后进入睡眠状态等待下次被唤醒 + +### 创建M0 main +- 程序启动后,Go 已经将主线程和 M 绑定(rt0_go) +- 当 goroutine 创建完后,放在当前 P 的 local queue,如果本地队列满了,它会将本地队列的前半部分和 newg 迁移到全局队列中 + +### Work-stealing goroutine偷取 +- M 绑定的 P 没有可执行的 goroutine 时,它会去按照优先级去抢占任务 +- 有1/61的概率去选择全局goroutine队列获取任务,防止全局goroutine饥饿 +- 如果没有的话,去自己本地的队列获取任务 +- 如果没有的话,去偷取其他P的队列的任务 +- 如果没有的话,检查其他阻塞的goroutine有没有就绪的 +- 如果没有进入自旋状态 +- 找到任何一个任务,切换调用栈执行任务。再循环不断的获取任务,直到进入休眠 +> 为了保证公平性,从随机位置上的 P 开始,而且遍历的顺序也随机化了(选择一个小于 GOMAXPROCS,且和它互为质数的步长),保证遍历的顺序也随机化了 + +### spinning thread 线程自旋 +- 线程自旋是相对于线程阻塞而言的,表象就是循环执行一个指定逻辑(就是上面提到的调度逻辑,目的是不停地寻找 G)。 +- 会产生问题,如果 G 迟迟不来,CPU 会白白浪费在这无意义的计算上。但好处也很明显,降低了 M 的上下文切换成本,提高了性能 +- 带P的M不停的找G +- 不带P的M找P挂载 +- G 创建又没 spining M 唤醒一个 M + diff --git a/note/Go/GoModule.md b/note/Go/GoModule.md new file mode 100644 index 0000000..1fb6cbd --- /dev/null +++ b/note/Go/GoModule.md @@ -0,0 +1,26 @@ +### 语义导入版本 +- Semantic Import Versioning +- go.mod 的 require 段中依赖的版本号,都符合 vX.Y.Z 的格式 +- 一个符合 Go Module 要求的版本号,由前缀 v 和一个满足语义版本规范的版本号组成 +- 借助于语义版本规范,Go 命令可以确定同一 module 的两个版本发布的先后次序,而且可以确定它们是否兼容 +- Go Module 规定:如果同一个包的新旧版本是兼容的,那么它们的包导入路径应该是相同的 + + +#### 语义版本号组成 +- 主版本 +- 次版本 +- 补丁版本 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/yuyibanben.png) + +### 最小版本选择 +- Minimal Version Selection +- 项目之间出现依赖同一个包但是不同版本的情况 +- go mod 选择依赖所有版本的最小的那个版本 +- go mod 不会选择最新的1.7.0版本,而是选择1.3.0 版本 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/zuixiaoyilai.png) + +### GO111MODULE配置值 +- on 开启 +- off 关闭 +- auto 编译器判断 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/go111module.jpeg) \ No newline at end of file diff --git a/note/Go/GoModule操作.md b/note/Go/GoModule操作.md new file mode 100644 index 0000000..1ddb078 --- /dev/null +++ b/note/Go/GoModule操作.md @@ -0,0 +1,31 @@ +### 为当前项目添加一个依赖 +- 在代码中import包地址 +- go get `新的依赖的包地址` +- go mod tidy 自动处理包的依赖导入 + +### 升级/降级依赖的版本 +- 项目modules目录,执行go get 指定的版本 +- go mod edit 更新依赖版本,然后执行go mod tidy +- 可以修改go mod 依赖版本号为要使用的分支,然后执行go mod tidy + +### 添加一个主版本号大于 1 的依赖 +- 在导包的路径上加上版本号 +- github.com/go-redis/redis/v7 +- 然后执行go get 或者 go mod tidy + +### 升级依赖版本到一个不兼容版本 +- 通过空应用更新版本 +- `import _ "github.com/go-redis/redis/v8"` +- 然后执行go get 或者 go mod tidy + +### 移除一个不用的依赖 +- 删除代码中的引用 +- 执行 go mod tidy,更新go.mod和go.sum + +### 特殊情况,可以使用vendor +- vendor做为go mod的补充 +- 不方便访问外网进行包下载 +- 比较关注构建过程中的性能 +- 通过 `go mod vendor`自动常见vendor目录 +- 基于vender文件夹进行构建 `go build -mod=vendor` +- Go 1.14 及以后版本中,如果 Go 项目的顶层目录下存在 vendor 目录,那么 go build 默认也会优先基于 vendor 构建,除非go build 传入 -mod=mod 的参数 \ No newline at end of file diff --git a/note/Go/Goroutine泄漏.md b/note/Go/Goroutine泄漏.md new file mode 100644 index 0000000..3b89cdc --- /dev/null +++ b/note/Go/Goroutine泄漏.md @@ -0,0 +1,88 @@ +### 泄漏的大多数原因 +- Goroutine 内正在进行 channel/mutex 等读写操作,但由于逻辑问题,某些情况下会被一直阻塞。 +- Goroutine 内的业务逻辑进入死循环,资源一直无法释放。 +- Goroutine 内的业务逻辑进入长时间等待,有不断新增的 Goroutine 进入等待 + +#### channel发送不接收 +- 开启多个goroutine,写channel +- 只读了部分的channel,导致goroutine阻塞不会释放 +```go +package main + +func main() { + for i := 0; i < 4; i++ { + queryAll() + fmt.Printf("goroutines: %d\n", runtime.NumGoroutine()) + } +} + +func queryAll() int { + ch := make(chan int) + for i := 0; i < 3; i++ { + go func() { ch <- query() }() + } + // 开启多个channel,只接收了一个 + return <-ch +} + +func query() int { + n := rand.Intn(100) + time.Sleep(time.Duration(n) * time.Millisecond) + return n +} +``` + +#### channel接收不发送 +- 只开启了接收,但是没有goroutine去发送数据到channel +```go +func main() { + defer func() { + fmt.Println("goroutines: ", runtime.NumGoroutine()) + }() + + var ch chan struct{} + go func() { + ch <- struct{}{} + }() + + time.Sleep(time.Second) +} +``` + +#### nil channel 读写都会阻塞goroutine +```go +ch := make(chan int) +go func() { + <-ch +}() +ch <- 0 +time.Sleep(time.Second) +``` + +#### 请求三方接口没有设置超时等待 +```go +func main() { + for { + go func() { + _, err := http.Get("https://www.xxx.com/") + if err != nil { + fmt.Printf("http.Get err: %v\n", err) + } + // do something... + }() + + time.Sleep(time.Second * 1) + fmt.Println("goroutines: ", runtime.NumGoroutine()) + } +} +``` + +#### 互斥锁忘记解锁 +- 互斥锁上锁后,忘记解锁 +- 造成其他goroutine锁等待,进而产生资源泄漏 +- `defer lock.Unlock()` + +#### 同步锁使用不当 +- sync.WaitGroup +- `Add`的数量和`Done`的数量不一致 +- `Wait`方法一直阻塞 \ No newline at end of file diff --git a/note/Go/Goroutine状态.md b/note/Go/Goroutine状态.md new file mode 100644 index 0000000..40ef3fa --- /dev/null +++ b/note/Go/Goroutine状态.md @@ -0,0 +1,12 @@ +状态 | 含义 +-- | -- +_Gidle | 刚刚被分配,还没有进行初始化。 +_Grunnable | 已经在运行队列中,还没有执行用户代码。 +_Grunning | 不在运行队列里中,已经可以执行用户代码,此时已经分配了 M 和 P。 +_Gsyscall | 正在执行系统调用,此时分配了 M。 +_Gwaiting | 在运行时被阻止,没有执行用户代码,也不在运行队列中,此时它正在某处阻塞等待中。 +_Gmoribund_unused | 尚未使用,但是在 gdb 中进行了硬编码。 +_Gdead | 尚未使用,这个状态可能是刚退出或是刚被初始化,此时它并没有执行用户代码,有可能有也有可能没有分配堆栈。 +_Genqueue_unused | 尚未使用。 +_Gcopystack | 正在复制堆栈,并没有执行用户代码,也不在运行队列中。 + diff --git a/note/Go/Go简介.md b/note/Go/Go简介.md new file mode 100644 index 0000000..44e2461 --- /dev/null +++ b/note/Go/Go简介.md @@ -0,0 +1,7 @@ +### Go 语言的设计哲学 +- 简单、显式、组合、并发和面向工程 +- 简单是指 Go 语言特性始终保持在少且足够的水平,不走语言特性融合的道路,但又不乏生产力。简单是 Go 生产力的源泉,也是 Go 对开发者的最大吸引力 +- 显式是指任何代码行为都需开发者明确知晓,不存在因“暗箱操作”而导致可维护性降低和不安全的结果:类型显示转换 +- 组合是构建 Go 程序骨架的主要方式,它可以大幅降低程序元素间的耦合,提高程序的可扩展性和灵活性,接口之间组合实现面向对象与继承 +- 并发是 Go 敏锐地把握了 CPU 向多核方向发展这一趋势的结果,可以让开发人员在多核时代更容易写出充分利用系统资源、支持性能随 CPU 核数增加而自然提升的应用程序:goroutine,select和channel的结合 +- 面向工程是 Go 语言在语言设计上的一个重大创新,它将语言要解决的问题域扩展到那些原本并不是由编程语言去解决的领域,从而覆盖了更多开发者在开发过程遇到的“痛点”,为开发者提供了更好的使用体验:没有用到的包和变量构建时报错,禁止包循环依赖 \ No newline at end of file diff --git a/note/Go/MAP哈希.md b/note/Go/MAP哈希.md new file mode 100644 index 0000000..c48e58c --- /dev/null +++ b/note/Go/MAP哈希.md @@ -0,0 +1,553 @@ +### Map 常用操作 +#### 初始化 +- var m1 map[string]int // m1 == nil 结果为true,此时写入会产生panic +- var m2 = map[string]int{} +- var m3 = make(map[string]int) +- 函数类型、map 类型自身,以及切片类型是不能作为 map 的 key 类型的 +```go + +s1 := make([]int, 1) +s2 := make([]int, 2) +f1 := func() {} +f2 := func() {} +m1 := make(map[int]string) +m2 := make(map[int]string) +println(s1 == s2) // 错误:invalid operation: s1 == s2 (slice can only be compared to nil) +println(f1 == f2) // 错误:invalid operation: f1 == f2 (func can only be compared to nil) +println(m1 == m2) // 错误:invalid operation: m1 == m2 (map can only be compared to nil) +``` +- makemap_small 源码 +```go +// makemap_small implements Go map creation for make(map[k]v) and +// make(map[k]v, hint) when hint is known to be at most bucketCnt +// at compile time and the map needs to be allocated on the heap. +// 创建map不指定容量,或者容量小于bucketCnt(这个容量为8) +func makemap_small() *hmap { + h := new(hmap) + h.hash0 = fastrand() + return h +} + +``` +- makemap 源码 +```go +// makemap implements Go map creation for make(map[k]v, hint). +// If the compiler has determined that the map or the first bucket +// can be created on the stack, h and/or bucket may be non-nil. +// If h != nil, the map can be created directly in h. +// If h.buckets != nil, bucket pointed to can be used as the first bucket. +func makemap(t *maptype, hint int, h *hmap) *hmap { + mem, overflow := math.MulUintptr(uintptr(hint), t.bucket.size) + // 数据范围溢出,设置为0 + if overflow || mem > maxAlloc { + hint = 0 + } + + // initialize Hmap + if h == nil { + h = new(hmap) + } + // 随机种子 + h.hash0 = fastrand() + + // Find the size parameter B which will hold the requested # of elements. + // For hint < 0 overLoadFactor returns false since hint < bucketCnt. + B := uint8(0) + for overLoadFactor(hint, B) { + B++ + } + h.B = B + + // allocate initial hash table + // if B == 0, the buckets field is allocated lazily later (in mapassign) + // If hint is large zeroing this memory could take a while. + if h.B != 0 { + var nextOverflow *bmap + h.buckets, nextOverflow = makeBucketArray(t, h.B, nil) + if nextOverflow != nil { + h.extra = new(mapextra) + h.extra.nextOverflow = nextOverflow + } + } + + return h +} + + +``` + +#### 写map +- key, value 写入 +- mapassign源码 +```go +// Like mapaccess, but allocates a slot for the key if it is not present in the map. +func mapassign(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { + if h == nil { + panic(plainError("assignment to entry in nil map")) + } + if raceenabled { + callerpc := getcallerpc() + pc := funcPC(mapassign) + racewritepc(unsafe.Pointer(h), callerpc, pc) + raceReadObjectPC(t.key, key, callerpc, pc) + } + if msanenabled { + msanread(key, t.key.size) + } + // hashWriting = 4 固定值 二进制 0000 0100 + if h.flags&hashWriting != 0 { + throw("concurrent map writes") + } + hash := t.hasher(key, uintptr(h.hash0)) + + + // Set hashWriting after calling t.hasher, since t.hasher may panic, + // in which case we have not actually done a write. + // map真正写入前设置标记位,其他goroutine写入会马上 throw("concurrent map writes") + // 异或操作,相同为0,不同为1,修改第三位为1,保留其他位为原值,再次进行与操作时,等于1,然后就会崩溃 + h.flags ^= hashWriting + + if h.buckets == nil { + h.buckets = newobject(t.bucket) // newarray(t.bucket, 1) + } + +// 省略部分代码 +``` + +#### 读map +- value := hash[key] +- value, ok := hash[key] +- 如果key不存在,返回value类型的零值 +- mapaccess1 返回val 源码 +- mapaccess2 返回val和bool +```go +// mapaccess1 returns a pointer to h[key]. Never returns nil, instead +// it will return a reference to the zero object for the elem type if +// the key is not in the map. +// NOTE: The returned pointer may keep the whole map live, so don't +// hold onto it for very long. + +// key不存在返回类型的零值 +// 不要持有返回的指针太长时间,容易造成GC无法回收map,导致内存泄漏 +func mapaccess1(t *maptype, h *hmap, key unsafe.Pointer) unsafe.Pointer { + if raceenabled && h != nil { + callerpc := getcallerpc() + pc := funcPC(mapaccess1) + racereadpc(unsafe.Pointer(h), callerpc, pc) + raceReadObjectPC(t.key, key, callerpc, pc) + } + if msanenabled && h != nil { + msanread(key, t.key.size) + } + // map 为空 + if h == nil || h.count == 0 { + if t.hashMightPanic() { + t.hasher(key, 0) // see issue 23734 + } + return unsafe.Pointer(&zeroVal[0]) + } + // 有正在写的goroutine,崩溃fatal error + if h.flags&hashWriting != 0 { + throw("concurrent map read and map write") + } + hash := t.hasher(key, uintptr(h.hash0)) // 根据key计算的hash值 + m := bucketMask(h.B) // 桶的个数 + + // 指针计算,找到key应该在的bmap + b := (*bmap)(add(h.buckets, (hash&m)*uintptr(t.bucketsize))) + + // 桶正在扩容 + if c := h.oldbuckets; c != nil { + if !h.sameSizeGrow() { + // There used to be half as many buckets; mask down one more power of two. + m >>= 1 + } + oldb := (*bmap)(add(c, (hash&m)*uintptr(t.bucketsize))) + if !evacuated(oldb) { + b = oldb + } + } + + top := tophash(hash) + // 遍历bucket +bucketloop: + for ; b != nil; b = b.overflow(t) { + for i := uintptr(0); i < bucketCnt; i++ { + if b.tophash[i] != top { + if b.tophash[i] == emptyRest { + break bucketloop + } + continue + } + k := add(unsafe.Pointer(b), dataOffset+i*uintptr(t.keysize)) + if t.indirectkey() { + k = *((*unsafe.Pointer)(k)) + } + if t.key.equal(key, k) { + e := add(unsafe.Pointer(b), dataOffset+bucketCnt*uintptr(t.keysize)+i*uintptr(t.elemsize)) + if t.indirectelem() { + e = *((*unsafe.Pointer)(e)) + } + return e + } + } + } + // 没有的话 返回零值 + return unsafe.Pointer(&zeroVal[0]) +} + +``` + +#### 删除map中的元素 +- delete(map, key) +- `mapdelete` 方法 + +#### range map +- 调用 `mapiterinit` 方法进行初始化 +- 不断调用 `mapiternext` 方法进行循环 +### 特性 +- map是个指针,底层指向hmap,所以是个引用类型 +- golang slice、map、channel都是引用类型,当引用类型作为函数参数时,可能会修改原内容数据 +- golang 中没有引用传递,只有值和指针传递。map 作为函数实参传递时本质上也是值传递,因为 map 底层数据结构是通过指针指向实际的元素存储空间,在被调函数中修改 map,对调用者同样可见,所以 map 作为函数实参传递时表现出了引用传递的效果 +- map 底层数据结构是通过指针指向实际的元素存储空间,对其中一个map的更改,会影响到其他map +- 遍历无序 +- map 可以自动扩容,map 中数据元素的 value 位置可能在这一过程中发生变化,所以 Go 不允许获取 map 中 value 的地址,这个约束是在编译期间就生效的 + +### Map 实现原理 +- Go中的map是一个指针,占用8个字节,指向hmap结构体; 源码src/runtime/map.go中可以看到map的底层结构 +- 每个map的底层结构是hmap,hmap包含若干个结构为bmap的bucket数组。每个bucket底层都采用链表结构 +- 每个 bucket 中存储的是 Hash 值低 bit 位数值相同的元素,默认的元素个数为 BUCKETSIZE(值为 8,Go 1.17 版本中在 $GOROOT/src/cmd/compile/internal/reflectdata/reflect.go 中定义,与runtime/map.go 中常量 bucketCnt 保持一致) +- 当某个 bucket(比如 buckets[0]) 的 8 个空槽 slot)都填满了,且 map 尚未达到扩容的条件的情况下,运行时会建立 overflow bucket,并将这个 overflow bucket 挂在上面 bucket(如 buckets[0])末尾的 overflow 指针上,这样两个 buckets 形成了一个链表结构,直到下一次 map 扩容之前,这个结构都会一直存在 +- map 结构,key和value单独排列在一起可以减少结构体对齐填充,减少内存浪费 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/map_struct.jpg) +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/hmap.jpg) +```golang + +// A header for a Go map. +type hmap struct { + count int + // 代表哈希表中的元素个数,调用len(map)时,返回的就是该字段值。 + flags uint8 // 标记 扩容状态,读写状态 + B uint8 + // buckets(桶)的对数log_2 + // 如果B=5,则buckets数组的长度 = 2^5=32,意味着有32个桶 + noverflow uint16 + // 溢出桶的大概数量 + hash0 uint32 + // 哈希种子 + + buckets unsafe.Pointer + // 指向buckets数组的指针,数组大小为2^B,如果元素个数为0,它为nil。 + oldbuckets unsafe.Pointer + // 如果发生扩容,oldbuckets是指向老的buckets数组的指针, + // 老的buckets数组大小是新的buckets的1/2;非扩容状态下,它为nil。 + nevacuate uintptr + // 表示扩容进度,小于此地址的buckets代表已搬迁完成。 + + extra *mapextra + // 这个字段是为了优化GC扫描而设计的。当key和value均不包含指针 + // 并且都可以inline时使用。extra是指向mapextra类型的指针。 + } +``` +- bmap结构 +```go +bucketCntBits = 3 +bucketCnt = 1 << bucketCntBits + +// A bucket for a Go map. +type bmap struct { + // tophash generally contains the top byte of the hash value + // for each key in this bucket. If tophash[0] < minTopHash, + // tophash[0] is a bucket evacuation state instead. + tophash [bucketCnt]uint8 + // Followed by bucketCnt keys and then bucketCnt elems. + // NOTE: packing all the keys together and then all the elems together makes the + // code a bit more complicated than alternating key/elem/key/elem/... but it allows + // us to eliminate padding which would be needed for, e.g., map[int64]int8. + // Followed by an overflow pointer. + + // len为8的数组 + // 用来快速定位key是否在这个bmap中 + // 桶的槽位数组,一个桶最多8个槽位,如果key所在的槽位在tophash中,则代表该key在这个桶中 + // key 单独放在一起,value单独放在一起,相同的类型放在一起,减少空间浪费, +} + + +``` +- mapextra结构 +```go +// mapextra holds fields that are not present on all maps. +// 字面理解附加字段 +type mapextra struct { + // If both key and elem do not contain pointers and are inline, then we mark bucket + // type as containing no pointers. This avoids scanning such maps. + // However, bmap.overflow is a pointer. In order to keep overflow buckets + // alive, we store pointers to all overflow buckets in hmap.extra.overflow and hmap.extra.oldoverflow. + // overflow and oldoverflow are only used if key and elem do not contain pointers. + // overflow contains overflow buckets for hmap.buckets. + // oldoverflow contains overflow buckets for hmap.oldbuckets. + // The indirection allows to store a pointer to the slice in hiter. + + // 如果 key 和 value 都不包含指针,并且可以被 inline(<=128 字节) + // 就使用 hmap的extra字段 来存储 overflow buckets,这样可以避免 GC 扫描整个 map + // 然而 bmap.overflow 也是个指针。这时候我们只能把这些 overflow 的指针 + // 都放在 hmap.extra.overflow 和 hmap.extra.oldoverflow 中了 + // overflow 包含的是 hmap.buckets 的 overflow 的 buckets + // oldoverflow 包含扩容时的 hmap.oldbuckets 的 overflow 的 bucket + + overflow *[]*bmap + oldoverflow *[]*bmap + + // nextOverflow holds a pointer to a free overflow bucket. + nextOverflow *bmap +} + +``` + +#### tophash区域 +- 向 map 插入一条数据,或者是从 map 按 key 查询数据的时候,运行时都会使用哈希函数对 key 做哈希运算,并获得一个哈希值(hashcode) +- 运行时会把 hashcode“一分为二”来看待,其中低位区的值用于选定 bucket,高位区的值用于在某个 bucket 中确定 key 的位置 +- 每个 bucket 的 tophash 区域其实是用来快速定位 key 位置的,避免了逐个 key 进行比较这种代价较大的操作 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/top_hash.jpg) + +### 为什么遍历map无序? +- range map,初始化时调用`fastrand()`随机一个数字,决定本次range的起始点 +```go +// mapiterinit initializes the hiter struct used for ranging over maps. +// The hiter struct pointed to by 'it' is allocated on the stack +// by the compilers order pass or on the heap by reflect_mapiterinit. +// Both need to have zeroed hiter since the struct contains pointers. +func mapiterinit(t *maptype, h *hmap, it *hiter) { + // 省略一部分 + + // decide where to start + // 开始迭代时会有一个随机数,决定起始位置 + r := uintptr(fastrand()) + if h.B > 31-bucketCntBits { + r += uintptr(fastrand()) << 31 + } + it.startBucket = r & bucketMask(h.B) + it.offset = uint8(r >> h.B & (bucketCnt - 1)) + + // iterator state + it.bucket = it.startBucket + + // Remember we have an iterator. + // Can run concurrently with another mapiterinit(). + if old := h.flags; old&(iterator|oldIterator) != iterator|oldIterator { + atomic.Or8(&h.flags, iterator|oldIterator) + } + + mapiternext(it) +} + + +``` + +### 怎么有序遍历map +- 先取出map的key +- 对key进行排序 +- 循环排序后的key,实现有序遍历map + +### 为什么map非线程安全 +- 并发访问需要控制锁相关,防止出现资源竞争 +- 大部分不需要从多个goroutine同时读写map,加锁反而造成性能降低 + +```go +func mapiternext(it *hiter) { + h := it.h + if raceenabled { + callerpc := getcallerpc() + racereadpc(unsafe.Pointer(h), callerpc, funcPC(mapiternext)) + } + if h.flags&hashWriting != 0 { + // 直接抛出异常,fatal error + throw("concurrent map iteration and map write") + } + t := it.t + bucket := it.bucket + b := it.bptr + i := it.i + checkBucket := it.checkBucket + // 省略部分代码 +} +``` + +### 线程安全的map怎么实现 +- 使用读写锁 `map` + `sync.RWMutex` +- [sync.Map](../../mkdocs_wiki/go/sync_map.md) + +### map扩容策略 + +- 装载因子超过阈值,源码里定义的阈值是 6.5 +- overflow 的 bucket 数量过多:当 B 小于 15,也即 bucket 总数小于 2^15 时,overflow 的 bucket 数量超过 2^B;当 B >= 15,也即 bucket 总数大于等于 2^15时,overflow 的 bucket 数量超过 2^15。 +- 命中装载因子增量扩容 +- 命中溢出桶太多,等量扩容 +- 扩容时,只是把原来的桶挂载到新的桶上,然后采用增量复制去迁移桶内的数据 + + +```go +// Maximum average load of a bucket that triggers growth is 6.5. +// Represent as loadFactorNum/loadFactorDen, to allow integer math. +loadFactorNum = 13 +loadFactorDen = 2 + + +// growing reports whether h is growing. The growth may be to the same size or bigger. +func (h *hmap) growing() bool { + return h.oldbuckets != nil +} + +// overLoadFactor reports whether count items placed in 1< bucketCnt && uintptr(count) > loadFactorNum*(bucketShift(B)/loadFactorDen) +} + +// tooManyOverflowBuckets reports whether noverflow buckets is too many for a map with 1< 15 { + B = 15 + } + // 15 & 15 = 15 + // 判断符右边最大的结果就是1 << 15 + // 这个操作可能是见的太少,为什么要用15呢? + // The compiler doesn't see here that B < 16; mask B to generate shorter shift code. + return noverflow >= uint16(1)<<(B&15) +} + +// Did not find mapping for key. Allocate new cell & add entry. + +// If we hit the max load factor or we have too many overflow buckets, +// and we're not already in the middle of growing, start growing. +// 最大装载因子或者溢出桶太多,然后还没有在扩容状态,开始扩容 +if !h.growing() && (overLoadFactor(h.count+1, h.B) || tooManyOverflowBuckets(h.noverflow, h.B)) { + hashGrow(t, h) + goto again +} + +func hashGrow(t *maptype, h *hmap) { + // 命中装载因子,增量扩容 + // 溢出桶太多,等量扩容 + // If we've hit the load factor, get bigger. + // Otherwise, there are too many overflow buckets, + // so keep the same number of buckets and "grow" laterally. + bigger := uint8(1) + if !overLoadFactor(h.count+1, h.B) { + bigger = 0 + h.flags |= sameSizeGrow + } + oldbuckets := h.buckets + newbuckets, nextOverflow := makeBucketArray(t, h.B+bigger, nil) + + flags := h.flags &^ (iterator | oldIterator) + if h.flags&iterator != 0 { + flags |= oldIterator + } + // commit the grow (atomic wrt gc) + h.B += bigger // 如果bigger是0就是等量扩容,是1就是2倍,翻倍扩容 + h.flags = flags + h.oldbuckets = oldbuckets + h.buckets = newbuckets + h.nevacuate = 0 + h.noverflow = 0 + + if h.extra != nil && h.extra.overflow != nil { + // Promote current overflow buckets to the old generation. + if h.extra.oldoverflow != nil { + throw("oldoverflow is not nil") + } + h.extra.oldoverflow = h.extra.overflow + h.extra.overflow = nil + } + if nextOverflow != nil { + if h.extra == nil { + h.extra = new(mapextra) + } + h.extra.nextOverflow = nextOverflow + } + + // 哈希表数据的实际复制是增量完成的 + // 通过growWork() 和evacuate()。 + // the actual copying of the hash table data is done incrementally + // by growWork() and evacuate(). +} + +// 写或者删map中的元素才会调用growWork +// mapassign +// mapdelete +func growWork(t *maptype, h *hmap, bucket uintptr) { + // make sure we evacuate the oldbucket corresponding + // to the bucket we're about to use + + evacuate(t, h, bucket&h.oldbucketmask()) + + // evacuate one more oldbucket to make progress on growing + if h.growing() { + evacuate(t, h, h.nevacuate) + } +} + +// 迁移桶内数据 +func evacuate(t *maptype, h *hmap, oldbucket uintptr) { + b := (*bmap)(add(h.oldbuckets, oldbucket*uintptr(t.bucketsize))) + newbit := h.noldbuckets() + if !evacuated(b) { + // TODO: reuse overflow buckets instead of using new ones, if there + // is no iterator using the old buckets. (If !oldIterator.) + + // 先搞长度2个的数组 + // xy contains the x and y (low and high) evacuation destinations. + var xy [2]evacDst + x := &xy[0] // 用一个 + x.b = (*bmap)(add(h.buckets, oldbucket*uintptr(t.bucketsize))) + x.k = add(unsafe.Pointer(x.b), dataOffset) + x.e = add(x.k, bucketCnt*uintptr(t.keysize)) + + if !h.sameSizeGrow() { // 不是等量扩容,再用另一个 + // Only calculate y pointers if we're growing bigger. + // Otherwise GC can see bad pointers. + y := &xy[1] + y.b = (*bmap)(add(h.buckets, (oldbucket+newbit)*uintptr(t.bucketsize))) + y.k = add(unsafe.Pointer(y.b), dataOffset) + y.e = add(y.k, bucketCnt*uintptr(t.keysize)) + } + +``` + +### map哈希冲突解决方法 + +- 开放寻址法:**依次探测和比较数组中的元素以判断目标键值对是否存在于哈希表中**,使用开放寻址法来实现哈希表,那么实现哈希表底层的数据结构就是数组 + + - 首次索引写入位置 `index := hash("author") % array.len` + + - 如果发生冲突,就会将键值对写入到下一个索引不为空的位置 + + ![open-addressing-and-set](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202404111150038.png) + + - 读取数据时,会从index位置开始读取并判断key是否相等,不相等的话读下一个索引位置,直到读到或者key为空时返回数据 + + ![open-addressing-and-get](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202404111152720.png) + + - 装载因子:数组中元素与数组长度的比值,随着装载因子的增加,线性探测的平均用时就会逐渐增加,会影响哈希表的读写性能。当装载率超过 70% 之后,哈希表的性能就会急剧下降,而一旦装载率达到 100%,整个哈希表就会完全失效,这时查找和插入任意元素的时间复杂度都是 𝑂(𝑛) 的,这时需要遍历数组中的全部元素 + +- 拉链法 + + - 一般会使用数组加上链表,一些编程语言会在拉链法的哈希中引入红黑树以优化性能,拉链法会使用链表数组作为哈希底层的数据结构,可以将它看成可以扩展的二维数组: + ![separate-chaing-and-set](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202404111159936.png) + + - 数组索引位置计算 `index := hash("Key6") % array.len`,根据索引位置,就可以遍历当前桶中的链表,在遍历链表的过程中会遇到以下两种情况: + + 1. 找到键相同的键值对 — 更新键对应的值; + 2. 没有找到键相同的键值对 — 在链表的末尾追加新的键值对 + + ![separate-chaing-and-get](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202404111203734.png) + + - 装载因子:=元素数量÷桶数量 + + diff --git a/note/Go/MAP深拷贝.md b/note/Go/MAP深拷贝.md new file mode 100644 index 0000000..585d724 --- /dev/null +++ b/note/Go/MAP深拷贝.md @@ -0,0 +1,52 @@ +### 深拷贝map + +### 方法一 encoding/gob包 + +```go +import "encoding/gob" +// 需要什么类型的map注册类型就可以 +func DeepCopy(dst, src interface{}) error { + var buf bytes.Buffer + gob.Register(map[string]interface{}{}) + gob.Register([]interface{}(nil)) + gob.Register([]map[string]interface{}{}) + if err := gob.NewEncoder(&buf).Encode(src); err != nil { + return err + } + return gob.NewDecoder(bytes.NewBuffer(buf.Bytes())).Decode(dst) +} +``` + +### 方法二 json 序列化后反序列化 + +```go +func MapDeepCopy(src, dst interface{}) error { + byteSlice, err := json.Marshal(src) + if err != nil { + return err + } + return json.Unmarshal(byteSlice, dst) +} +``` + +### 方法三 循环遍历 + +```go +func DeepCopy(value interface{}) interface{} { + if valueMap, ok := value.(map[string]interface{}); ok { + newMap := make(map[string]interface{}) + for k, v := range valueMap { + newMap[k] = DeepCopy(v) + } + return newMap + } else if valueSlice, ok := value.([]interface{}); ok { + newSlice := make([]interface{}, len(valueSlice)) + for k, v := range valueSlice { + newSlice[k] = DeepCopy(v) + } + return newSlice + } + return value +} +``` + diff --git a/note/Go/fmt包.md b/note/Go/fmt包.md new file mode 100644 index 0000000..7a42803 --- /dev/null +++ b/note/Go/fmt包.md @@ -0,0 +1,6 @@ +### fmt 各种v +```go +%v 按默认格式输出 +%+v 在%v的基础上额外输出字段名 +%#v 在%+v的基础上额外输出类型名 +``` \ No newline at end of file diff --git a/note/Go/for循环中尽量不要使用defer.md b/note/Go/for循环中尽量不要使用defer.md new file mode 100644 index 0000000..c969f06 --- /dev/null +++ b/note/Go/for循环中尽量不要使用defer.md @@ -0,0 +1,51 @@ +### for 循环中禁止使用defer +- 直接在 for 循环中使用 defer 很可能使得 defer 不能执行,导致内存泄露或者其他资源问题,所以应该将 defer 放到外层。 +- 若确实需要使用 defer,可以将逻辑封装为一个独立函数或者使用闭包 +- 错误 +```go +func readFiles(files []string) { + for i:=0;i 客户端关闭 Socket 后,如果服务端 Socket 尚未关闭,这个时候服务端向 Socket 的写入操作依然可能会成功,因为数据会成功写入己方的内核 socket 缓冲区中,即便最终发不到对方 socket 缓冲区 diff --git a/note/Go/sync_map.md b/note/Go/sync_map.md new file mode 100644 index 0000000..06db9a3 --- /dev/null +++ b/note/Go/sync_map.md @@ -0,0 +1,427 @@ +### sync Map 总结 +- sync.map 是线程安全的,读取,插入,删除也都保持着常数级的时间复杂度 +- 通过读写分离,降低锁时间来提高效率,适用于读多写少的场景 +- Range 操作需要提供一个函数,参数是 k,v,返回值是一个布尔值:f func(key, value interface{}) bool +- 调用 Load 或 LoadOrStore 函数时,如果在 read 中没有找到 key,则会将 misses 值原子地增加 1,当 misses 增加到和 dirty 的长度相等时,会将 dirty 提升为 read,来减少读miss +- 新写入的 key 会保存到 dirty 中,如果这时 dirty 为 nil,就会先新创建一个 dirty,并将 read 中未被删除的元素拷贝到 dirty。 +- 当 dirty 为 nil 的时候,read 就代表 map 所有的数据;当 dirty 不为 nil 的时候,dirty 才代表 map 所有的数据 + +### sync.Map结构 +```go +type Map struct { + mu Mutex + + // read contains the portion of the map's contents that are safe for + // concurrent access (with or without mu held). + // read里边存的是并发访问安全的(持不持有锁都是可以的) + // + // The read field itself is always safe to load, but must only be stored with + // mu held. + // load read里边内容总是安全的,但是当你想store进去的时候就必须加mutex 锁 + + // Entries stored in read may be updated concurrently without mu, but updating + // a previously-expunged entry requires that the entry be copied to the dirty + // map and unexpunged with mu held. + // 大致意思是更新已经删除的key需要加锁,然后把key放到dirty里边 + read atomic.Value // readOnly + + // dirty contains the portion of the map's contents that require mu to be + // held. To ensure that the dirty map can be promoted to the read map quickly, + // it also includes all of the non-expunged entries in the read map. + // 为了快速提升dirty为read,dirty中存储了read中未删除的key + // + // Expunged entries are not stored in the dirty map. An expunged entry in the + // clean map must be unexpunged and added to the dirty map before a new value + // can be stored to it. + // + // If the dirty map is nil, the next write to the map will initialize it by + // making a shallow copy of the clean map, omitting stale entries. + dirty map[interface{}]*entry + + // misses counts the number of loads since the read map was last updated that + // needed to lock mu to determine whether the key was present. + // + // 从read中读取不到key,miss就会加一,加到一定阈值,dirty将被提升为read + // Once enough misses have occurred to cover the cost of copying the dirty + // map, the dirty map will be promoted to the read map (in the unamended + // state) and the next store to the map will make a new dirty copy. + misses int +} + +// readOnly is an immutable struct stored atomically in the Map.read field. +// 不可改变,原子性的存在map的read字段里 +type readOnly struct { + m map[interface{}]*entry + amended bool // true if the dirty map contains some key not in m. +} + +// expunged is an arbitrary pointer that marks entries which have been deleted +// from the dirty map. +// 专用来标记 entry已经从dirty中删除 +var expunged = unsafe.Pointer(new(interface{})) + +// An entry is a slot in the map corresponding to a particular key. +// entry存放的就是一个指针,指向value的地址 +type entry struct { + // p points to the interface{} value stored for the entry. + // + // If p == nil, the entry has been deleted, and either m.dirty == nil or + // m.dirty[key] is e. + // 地址为nil,表明key已经被删除,要么map的dirty为空,要么dirty[key]是这个entry + // + // If p == expunged, the entry has been deleted, m.dirty != nil, and the entry + // is missing from m.dirty. + // 地址是expunged,就表示这个entry已经被删了,并且dirty也已经 不存这个值了 + // + // Otherwise, the entry is valid and recorded in m.read.m[key] and, if m.dirty + // != nil, in m.dirty[key]. + // 其他情况下,就是没有被删除,read[key]为这个p,然后如果dirty不为nil,则ditry[key]也为p + // + // An entry can be deleted by atomic replacement with nil: when m.dirty is + // next created, it will atomically replace nil with expunged and leave + // m.dirty[key] unset. + // 当删除 key 时,并不实际删除。一个 entry 可以通过原子地(CAS 操作)设置 p 为 nil 被删除。 + // 如果之后创建 m.dirty,nil 又会被原子地设置为 expunged,且不会拷贝到 dirty 中。 + // + // An entry's associated value can be updated by atomic replacement, provided + // p != expunged. If p == expunged, an entry's associated value can be updated + // only after first setting m.dirty[key] = e so that lookups using the dirty + // map find the entry. + // 如果 p 不为 expunged,和 entry 相关联的这个 value 可以被原子地更新; + //如果 p == expunged,那么仅当它初次被设置到 m.dirty 之后,才可以被更新 + p unsafe.Pointer // *interface{} +} +``` +> 引用知乎回答的一张图 https://zhuanlan.zhihu.com/p/344834329 + +![sync_map结构](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/sync_map.jpg) + + +### Load +- 读不到返回nil,和false +- 读到返回值和ok +- read map中不存在key,但是ditry map中有这个key,加锁防止dirty升级为map +- 加锁从 dirty中读取key,然后load函数会判断读取到的值是不是expunged(也就是被删除的情况) +- 标记miss,以便后续dirty升级为read +- miss的数量大于等于dirty的map的数量时,dirty升级为map + +```go +func (m *Map) Load(key interface{}) (value interface{}, ok bool) { + read, _ := m.read.Load().(readOnly) + e, ok := read.m[key] + // read map中不存在key,但是ditry map中有这个key,加锁防止dirty升级为map + // 加锁从 dirty中读取key,然后load函数会判断读取到的值是不是expunged(也就是被删除的情况) + // 标记miss,以便后续dirty升级为read + // miss的数量大于等于dirty的map的数量时,dirty升级为map + if !ok && read.amended { + m.mu.Lock() + // Avoid reporting a spurious miss if m.dirty got promoted while we were + // blocked on m.mu. (If further loads of the same key will not miss, it's + // not worth copying the dirty map for this key.) + read, _ = m.read.Load().(readOnly) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if !ok { + return nil, false + } + return e.load() +} + +func (e *entry) load() (value interface{}, ok bool) { + p := atomic.LoadPointer(&e.p) + // key 被删除 + if p == nil || p == expunged { + return nil, false + } + return *(*interface{})(p), true +} + +func (m *Map) missLocked() { + m.misses++ + if m.misses < len(m.dirty) { + return + } + m.read.Store(readOnly{m: m.dirty}) + m.dirty = nil + m.misses = 0 +} +``` + +### Store +- 存储一个key到sync Map +- key存在更新 +- 没读到已经存在read中的key要加锁进行存储 + +#### store 流程 +- 如果在 read 里能够找到待存储的 key,并且对应的 entry 的 p 值不为 expunged,也就是没被删除时,直接更新对应的 entry +- 第一步没有成功:要么 read 中没有这个 key,要么 key 被标记为删除。则先加锁,再进行后续的操作。 +- 再次在 read 中查找是否存在这个 key,也就是 double check 一下 +- 如果 read 中存在该 key,但 p == expunged,说明 m.dirty != nil 并且 m.dirty 中不存在该 key 值 此时: a. 将 p 的状态由 expunged 更改为 nil;b. dirty map 插入 key。然后,直接更新对应的 value。 +- 如果 read 中没有此 key,那就查看 dirty 中是否有此 key,如果有,则直接更新对应的 value,这时 read 中还是没有此 key +- 最后一步,如果 read 和 dirty 中都不存在该 key,则:a. 如果 dirty 为空,则需要创建 dirty,并从 read 中拷贝未被删除的元素;b. 更新 amended 字段,标识 dirty map 中存在 read map 中没有的 key;c. 将 k-v 写入 dirty map 中,read.m 不变。最后,更新此 key 对应的 value + +```go +// Store sets the value for a key. +func (m *Map) Store(key, value interface{}) { + // read 中可以读到这个key + read, _ := m.read.Load().(readOnly) + if e, ok := read.m[key]; ok && e.tryStore(&value) { + return + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnly) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + // 过去被删除了,就将这个key存到dirty + // The entry was previously expunged, which implies that there is a + // non-nil dirty map and this entry is not in it. + m.dirty[key] = e + } + // 原子存指针的值 + e.storeLocked(&value) //dirty和read都可以读到新存进去的值 + } else if e, ok := m.dirty[key]; ok { + e.storeLocked(&value) // dirty 中存在,就直接存储值 + } else { + // 两边都没读到这个key + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() // 如果dirty为nil,就新建一个dirty,然后把read中没删除的key存到dirty + m.read.Store(readOnly{m: read.m, amended: true}) // 标记dirtymap中有read中不存在的key + } + m.dirty[key] = newEntry(value) // 值存储到dirty,下次load可以取到 + } + m.mu.Unlock() +} + +// tryStore stores a value if the entry has not been expunged. +// +// If the entry is expunged, tryStore returns false and leaves the entry +// unchanged. +// 如果这个key已经被删除了,就返回了 +// key 没被删除原子交换entry中p的值 +func (e *entry) tryStore(i *interface{}) bool { + for { + p := atomic.LoadPointer(&e.p) + if p == expunged { + return false + } + if atomic.CompareAndSwapPointer(&e.p, p, unsafe.Pointer(i)) { + return true + } + } +} + +// 如果没有dirtymap的话新建一个,然后把read中没有删除的存到dirty +func (m *Map) dirtyLocked() { + if m.dirty != nil { + return + } + + read, _ := m.read.Load().(readOnly) + m.dirty = make(map[interface{}]*entry, len(read.m)) + for k, e := range read.m { + if !e.tryExpungeLocked() { + m.dirty[k] = e + } + } +} + +// 不是nil,也不是expunged的,也就是正常值才会被放到dirty中 +func (e *entry) tryExpungeLocked() (isExpunged bool) { + p := atomic.LoadPointer(&e.p) + for p == nil { + if atomic.CompareAndSwapPointer(&e.p, nil, expunged) { + return true + } + p = atomic.LoadPointer(&e.p) + } + return p == expunged +} + +``` + + + + + + +### LoadAndStore +```go +// LoadOrStore returns the existing value for the key if present. +// Otherwise, it stores and returns the given value. +// The loaded result is true if the value was loaded, false if stored. +func (m *Map) LoadOrStore(key, value interface{}) (actual interface{}, loaded bool) { + // Avoid locking if it's a clean hit. + // 正产查询 + read, _ := m.read.Load().(readOnly) + if e, ok := read.m[key]; ok { + actual, loaded, ok := e.tryLoadOrStore(value) + if ok { + return actual, loaded + } + } + + m.mu.Lock() + read, _ = m.read.Load().(readOnly) + if e, ok := read.m[key]; ok { + if e.unexpungeLocked() { + // e 为 nil,tryLoadOrStore 可以继续store,而不是直接return + m.dirty[key] = e + } + actual, loaded, _ = e.tryLoadOrStore(value) + } else if e, ok := m.dirty[key]; ok { + actual, loaded, _ = e.tryLoadOrStore(value) + m.missLocked() + } else { + if !read.amended { + // We're adding the first new key to the dirty map. + // Make sure it is allocated and mark the read-only map as incomplete. + m.dirtyLocked() + m.read.Store(readOnly{m: read.m, amended: true}) + } + m.dirty[key] = newEntry(value) + actual, loaded = value, false + } + m.mu.Unlock() + + return actual, loaded +} + +// tryLoadOrStore atomically loads or stores a value if the entry is not +// expunged. +// +// If the entry is expunged, tryLoadOrStore leaves the entry unchanged and +// returns with ok==false. +func (e *entry) tryLoadOrStore(i interface{}) (actual interface{}, loaded, ok bool) { + p := atomic.LoadPointer(&e.p) + if p == expunged { + return nil, false, false + } + if p != nil { + return *(*interface{})(p), true, true + } + + // Copy the interface after the first load to make this method more amenable + // to escape analysis: if we hit the "load" path or the entry is expunged, we + // shouldn't bother heap-allocating. + ic := i + for { + if atomic.CompareAndSwapPointer(&e.p, nil, unsafe.Pointer(&ic)) { + return i, false, true + } + p = atomic.LoadPointer(&e.p) + if p == expunged { + return nil, false, false + } + if p != nil { + return *(*interface{})(p), true, true + } + } +} +``` +### Delete && LoadAndDelete +- 查询数据的逻辑同Load +- 主要调用LoadAndDelete 方法 +- 返回val,和一个bool + +```go +// LoadAndDelete deletes the value for a key, returning the previous value if any. +// The loaded result reports whether the key was present. +func (m *Map) LoadAndDelete(key interface{}) (value interface{}, loaded bool) { + read, _ := m.read.Load().(readOnly) + e, ok := read.m[key] + if !ok && read.amended { + m.mu.Lock() + read, _ = m.read.Load().(readOnly) + e, ok = read.m[key] + if !ok && read.amended { + e, ok = m.dirty[key] + delete(m.dirty, key) + // Regardless of whether the entry was present, record a miss: this key + // will take the slow path until the dirty map is promoted to the read + // map. + m.missLocked() + } + m.mu.Unlock() + } + if ok { + return e.delete() + } + return nil, false +} + +// 直接标记成nil +func (e *entry) delete() (value interface{}, ok bool) { + for { + p := atomic.LoadPointer(&e.p) + if p == nil || p == expunged { + return nil, false + } + if atomic.CompareAndSwapPointer(&e.p, p, nil) { + return *(*interface{})(p), true + } + } +} +``` + +### Range +- 函数传参一个`func(key, value interface{}) bool` +- 函数返回false,结束循环 +- 如果dirty中存在read中没有的key,加锁将dirty升级为read +- 然后循环遍历read + +```go +/ Range calls f sequentially for each key and value present in the map. +// If f returns false, range stops the iteration. +// +// Range does not necessarily correspond to any consistent snapshot of the Map's +// contents: no key will be visited more than once, but if the value for any key +// is stored or deleted concurrently, Range may reflect any mapping for that key +// from any point during the Range call. +// +// Range may be O(N) with the number of elements in the map even if f returns +// false after a constant number of calls. +func (m *Map) Range(f func(key, value interface{}) bool) { + // We need to be able to iterate over all of the keys that were already + // present at the start of the call to Range. + // If read.amended is false, then read.m satisfies that property without + // requiring us to hold m.mu for a long time. + read, _ := m.read.Load().(readOnly) + if read.amended { + // m.dirty contains keys not in read.m. Fortunately, Range is already O(N) + // (assuming the caller does not break out early), so a call to Range + // amortizes an entire copy of the map: we can promote the dirty copy + // immediately! + m.mu.Lock() + read, _ = m.read.Load().(readOnly) + if read.amended { + read = readOnly{m: m.dirty} + m.read.Store(read) + m.dirty = nil + m.misses = 0 + } + m.mu.Unlock() + } + + for k, e := range read.m { + v, ok := e.load() + if !ok { + continue + } + if !f(k, v) { + break + } + } +} + +``` \ No newline at end of file diff --git a/note/Go/sync包.md b/note/Go/sync包.md new file mode 100644 index 0000000..2fb3b23 --- /dev/null +++ b/note/Go/sync包.md @@ -0,0 +1,77 @@ +### sync 包低级同步原语使用场景 +- 高性能的临界区(critical section)同步机制场景 +- 不想转移结构体对象所有权,但又要保证结构体内部状态数据的同步访问的场景 + +### sync包原语使用注意事项 +- 不要将原语复制后使用 +- 用闭包或者传递原语变量的地址(指针) + +### mutex 互斥锁 +- 零值可用,不用初始化 +- Lock,Unlock +- lock状态下任何goroutine加锁都会阻塞 + +### RWMutex 读写锁 +- 零值可用,不用初始化 +- RLock,RUnlock 加读锁,解读锁 +- Lock,Unlock 加写锁,解写锁 +- 加读锁状态下,不会阻塞加读锁,会阻塞加写锁 +- 加写锁状态下,会阻塞加读锁与写锁的goroutine + +### sync.Cond 条件变量 +- sync.Cond是传统的条件变量原语概念在 Go 语言中的实现 +- 可以把一个条件变量理解为一个容器,这个容器中存放着一个或一组等待着某个条件成立的 Goroutine +- 当条件成立后,处于等待状态的 Goroutine 将得到通知,并被唤醒继续进行后续的工作 +```go +type signal struct{} + +var ready bool + +func worker(i int) { + fmt.Printf("worker %d: is working...\n", i) + time.Sleep(1 * time.Second) + fmt.Printf("worker %d: works done\n", i) +} + +func spawnGroup(f func(i int), num int, groupSignal *sync.Cond) <-chan signal { + c := make(chan signal) + var wg sync.WaitGroup + + for i := 0; i < num; i++ { + wg.Add(1) + go func(i int) { + groupSignal.L.Lock() + for !ready { + groupSignal.Wait() + } + groupSignal.L.Unlock() + fmt.Printf("worker %d: start to work...\n", i) + f(i) + wg.Done() + }(i + 1) + } + + go func() { + wg.Wait() + c <- signal(struct{}{}) + }() + return c +} + +func main() { + fmt.Println("start a group of workers...") + groupSignal := sync.NewCond(&sync.Mutex{}) + c := spawnGroup(worker, 5, groupSignal) + + time.Sleep(5 * time.Second) // 模拟ready前的准备工作 + fmt.Println("the group of workers start to work...") + + groupSignal.L.Lock() + ready = true + groupSignal.Broadcast() + groupSignal.L.Unlock() + + <-c + fmt.Println("the group of workers work done!") +} +``` \ No newline at end of file diff --git a/note/Go/system_command.md b/note/Go/system_command.md new file mode 100644 index 0000000..3ad2070 --- /dev/null +++ b/note/Go/system_command.md @@ -0,0 +1,23 @@ +### 运行系统命令,获取结果,错误信息 + +```go +func RunCommand(path, name string, arg ...string) (string, string, error) { + var err error + var msg string + cmd := exec.Command(name, arg...) + var out bytes.Buffer + var stderr bytes.Buffer + cmd.Stdout = &out + cmd.Stderr = &stderr + cmd.Dir = path + err = cmd.Run() + log.Println(cmd.Args) + if err != nil { + msg = fmt.Sprint(err) + ": " + stderr.String() + err = errors.New(msg) + log.Println("err", err.Error(), "cmd", cmd.Args) + } + log.Println(out.String()) + return msg, out.String(), nil +} +``` \ No newline at end of file diff --git a/note/Go/uintptr_int_unsafe_pointer.md b/note/Go/uintptr_int_unsafe_pointer.md new file mode 100644 index 0000000..10b00ec --- /dev/null +++ b/note/Go/uintptr_int_unsafe_pointer.md @@ -0,0 +1,5 @@ +### uintptr + +### int + +### unsafe.Pointer \ No newline at end of file diff --git a/note/Go/zaxiang.md b/note/Go/zaxiang.md new file mode 100644 index 0000000..9c9169d --- /dev/null +++ b/note/Go/zaxiang.md @@ -0,0 +1,51 @@ +### 微信文章待整理 +- Go 依赖注入 https://mp.weixin.qq.com/s/Do-kTTbyKT4rsAGD3ujKwQ +- 结构体多字段原子操作 https://mp.weixin.qq.com/s/Wa1l4M5P89rQ2pyB_KnMxg +- 函数调用相关 https://mp.weixin.qq.com/s/Ekx9JpclqLaa4baB6V5rLw https://mp.weixin.qq.com/s/QGp1H6-__pus1Kbb7U8CHw +- 泛型 https://mp.weixin.qq.com/s/s9SITQB2xQb4tqmoLaJUpw +### 各种nil判断 +- 切片定义但不初始化,则为nil +```go +func main() { + var s []int + fmt.Println(s == nil) +} +``` + +- map定义但是不进行初始化,则为nil +```go +func main() { + var m map[string]int + fmt.Println(m == nil) +} +``` + +- 接口变量定义但是不赋初值 +```go +type MyInterface interface { + M1(string) +} + +func main() { + var m MyInterface + fmt.Println(m == nil) +} +``` + +- channel定义但是不初始化 +```go +func main() { + var ch chan int + fmt.Println(ch == nil) +} +``` + +- 指针类型变量没有被显式赋予初值 +```go +type Book struct{} + +func main() { + var b *Book + fmt.Println(b == nil) +} +``` \ No newline at end of file diff --git a/note/Go/代码块.md b/note/Go/代码块.md new file mode 100644 index 0000000..caa72e7 --- /dev/null +++ b/note/Go/代码块.md @@ -0,0 +1,30 @@ +### 代码块 +- 包裹在一对大括号内部的声明和语句序列 +- 如果大括号内没有声明和语句序列,则称为`空代码块` +- 代码块支持嵌套,可以在一个代码块中嵌入多个层次的代码块 + +```go + +func foo() { //代码块1 + { // 代码块2 + { // 代码块3 + { // 代码块4 + + } + } + } +} +``` + +#### 显示代码块 +- 肉眼可见的大括号包裹 + +#### 隐式代码块 +- 无法通过大括号来识别 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/daimakuai.jpg) + +### 作用域 +- 针对标识符的,不局限于变量 +- 一个标识符的作用域就是指这个标识符在被声明后可以被有效使用的源码区域 +- 作用域是一个编译期的概念,编译器在编译过程中会对每个标识符的作用域进行检查,对于在标识符作用域外使用该标识符的行为会给出编译错误的报错 \ No newline at end of file diff --git a/note/Go/内存分配.md b/note/Go/内存分配.md new file mode 100644 index 0000000..bb69238 --- /dev/null +++ b/note/Go/内存分配.md @@ -0,0 +1,125 @@ +## 设计原理 + +### 内存分配方式 + +- 线性分配 +- 空闲链表分配 + +#### 线性分配方式 + +- 线性分配(Bump Allocator)是一种高效的内存分配方法。只需要在内存中维护一个指向内存特定位置的指针,如果用户程序向分配器申请内存,分配器只需要检查剩余的空闲内存、返回分配的内存区域并修改指针在内存中的位置,即移动下图中的指针: + +![bump-allocator](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281025325.png) + +- 线性分配器实现带来较快的执行速度以及较低的实现复杂度,但是线性分配器无法在内存被释放时重用内存。如下图所示,如果已经分配的内存被回收,线性分配器无法重新利用红色的内存: + +![bump-allocator-reclaim-memory](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281026235.png) + +- 需要与垃圾分配算法配合使用,例如:标记压缩(Mark-Compact)、复制回收(Copying GC)和分代回收(Generational GC)等算法,它们可以通过拷贝的方式整理存活对象的碎片,将空闲内存定期合并,就能利用线性分配器的效率提升内存分配器的性能 + + + +#### 空闲链表分配方式 + +- 空闲链表分配器(Free-List Allocator)可以重用已经被释放的内存,它在内部会维护一个类似链表的数据结构。当用户程序申请内存时,空闲链表分配器会依次遍历空闲的内存块,找到足够大的内存,然后申请新的资源并修改链表: + +![free-list-allocator](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281033047.png) + +- 不同的内存块通过指针构成了链表,使用这种方式的分配器可以重新利用回收的资源,分配内存时需要遍历链表,所以它的时间复杂度是 𝑂(𝑛)。空闲链表分配器可以选择不同的策略在链表中的内存块中进行选择,最常见的是以下四种: + + - 首次适应(First-Fit)— 从链表头开始遍历,选择第一个大小大于申请内存的内存块; + + - 循环首次适应(Next-Fit)— 从上次遍历的结束位置开始遍历,选择第一个大小大于申请内存的内存块; + + - 最优适应(Best-Fit)— 从链表头遍历整个链表,选择最合适的内存块; + + - 隔离适应(Segregated-Fit)— 将内存分割成多个链表,每个链表中的内存块大小相同,申请内存时先找到满足条件的链表,再从链表中选择合适的内存块;隔离适应策略如下 + +![segregated-list](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281100525.png) + +- 隔离适应策略会将内存分割成由 4、8、16、32 字节的内存块组成的链表,当我们向内存分配器申请 8 字节的内存时,它会在上图中找到满足条件的空闲内存块并返回。隔离适应的分配策略减少了需要遍历的内存块数量,提高了内存分配的效率。 + +### 分级分配 + +- Go 语言的内存分配器就借鉴了 TCMalloc 的设计实现高速的内存分配,它的核心理念是使用多级缓存将对象根据大小分类,并按照类别实施不同的分配策略 + +##### 对象大小 + +- Go 语言的内存分配器会根据申请分配的内存大小选择不同的处理逻辑,运行时根据对象的大小将对象分成微对象、小对象和大对象三种: + +| 类别 | 大小 | +| :----: | :-----------: | +| 微对象 | `(0, 16B)` | +| 小对象 | `[16B, 32KB]` | +| 大对象 | `(32KB, +∞)` | + +##### 多级缓存 + +- 内存分配器不仅会区别对待大小不同的对象,还会将内存分成不同的级别分别管理,TCMalloc 和 Go 运行时分配器都会引入线程缓存(Thread Cache)、中心缓存(Central Cache)和页堆(Page Heap)三个组件分级管理内存: + +![image-20240628111504024](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281115082.png) + + + +#### 虚拟内存布局 + +##### Go 1.10及以前连续分配 + +- 启动时会初始化整片虚拟内存区域,如下所示的三个区域 `spans`、`bitmap` 和 `arena` 分别预留了 512MB、16GB 以及 512GB 的内存空间,这些内存并不是真正存在的物理内存,而是虚拟内存: + +![heap-before-go-1-10](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281117161.png) + +- `spans` 区域存储了指向内存管理单元 [`runtime.mspan`](https://draveness.me/golang/tree/runtime.mspan) 的指针,每个内存单元会管理几页的内存空间,每页大小为 8KB; +- `bitmap` 用于标识 `arena` 区域中的那些地址保存了对象,位图中的每个字节都会表示堆区中的 32 字节是否空闲; +- `arena` 区域是真正的堆区,运行时会将 8KB 看做一页,这些内存页中存储了所有在堆上初始化的对象; + +- 对于任意一个地址,都可以根据 `arena` 的基地址计算该地址所在的页数并通过 `spans` 数组获得管理该片内存的管理单元 [`runtime.mspan`](https://draveness.me/golang/tree/runtime.mspan),`spans` 数组中多个连续的位置可能对应同一个 [`runtime.mspan`](https://draveness.me/golang/tree/runtime.mspan) 结构。 + +##### Go 1.11以后稀疏分配 + +- 稀疏的内存布局能移除堆大小的上限 + +![heap-after-go-1-11](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281123301.png) + +### 地址空间 + +- Go 语言的运行时构建了操作系统的内存管理抽象层,该抽象层将运行时管理的地址空间分成以下四种状态[8](https://draveness.me/golang/docs/part3-runtime/ch07-memory/golang-memory-allocator/#fn:8): + + | 状态 | 解释 | + | :--------: | :----------------------------------------------------------: | + | `None` | 内存没有被保留或者映射,是地址空间的默认状态 | + | `Reserved` | 运行时持有该地址空间,但是访问该内存会导致错误 | + | `Prepared` | 内存被保留,一般没有对应的物理内存访问,该片内存的行为是未定义的可以快速转换到 `Ready` 状态 | + | `Ready` | 可以被安全访问 | + +![memory-regions-states-and-transitions](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281149871.png) + +运行时中包含多个操作系统实现的状态转换方法,所有的实现都包含在以 `mem_` 开头的文件中,本节将介绍 Linux 操作系统对上图中方法的实现: + +- [`runtime.sysAlloc`](https://draveness.me/golang/tree/runtime.sysAlloc) 会从操作系统中获取一大块可用的内存空间,可能为几百 KB 或者几 MB; +- [`runtime.sysFree`](https://draveness.me/golang/tree/runtime.sysFree) 会在程序发生内存不足(Out-of Memory,OOM)时调用并无条件地返回内存; +- [`runtime.sysReserve`](https://draveness.me/golang/tree/runtime.sysReserve) 会保留操作系统中的一片内存区域,访问这片内存会触发异常; +- [`runtime.sysMap`](https://draveness.me/golang/tree/runtime.sysMap) 保证内存区域可以快速转换至就绪状态; +- [`runtime.sysUsed`](https://draveness.me/golang/tree/runtime.sysUsed) 通知操作系统应用程序需要使用该内存区域,保证内存区域可以安全访问; +- [`runtime.sysUnused`](https://draveness.me/golang/tree/runtime.sysUnused) 通知操作系统虚拟内存对应的物理内存已经不再需要,可以重用物理内存; +- [`runtime.sysFault`](https://draveness.me/golang/tree/runtime.sysFault) 将内存区域转换成保留状态,主要用于运行时的调试; + +运行时使用 Linux 提供的 `mmap`、`munmap` 和 `madvise` 等系统调用实现了操作系统的内存管理抽象层,抹平了不同操作系统的差异,为运行时提供了更加方便的接口,除了 Linux 之外,运行时还实现了 BSD、Darwin、Plan9 以及 Windows 等平台上抽象层 + + + +## 内存管理组件 + +- 内存管理单元 runtime.mspan +- 线程缓存 runtime.mcache +- 中心缓存 runtime.mcentral +- 页堆 runtime.mheap + +### Go内存布局 + +![go-memory-layout](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202406281152718.png) + +- 每一个处理器都会分配一个线程缓存 [`runtime.mcache`](https://draveness.me/golang/tree/runtime.mcache) 用于处理微对象和小对象的分配,它们会持有内存管理单元 [`runtime.mspan`](https://draveness.me/golang/tree/runtime.mspan) + +- 每个类型的内存管理单元都会管理特定大小的对象,当内存管理单元中不存在空闲对象时,它们会从 [`runtime.mheap`](https://draveness.me/golang/tree/runtime.mheap) 持有的 134 个中心缓存 [`runtime.mcentral`](https://draveness.me/golang/tree/runtime.mcentral) 中获取新的内存单元,中心缓存属于全局的堆结构体 [`runtime.mheap`](https://draveness.me/golang/tree/runtime.mheap),它会从操作系统中申请内存 +- 在 amd64 的 Linux 操作系统上,[`runtime.mheap`](https://draveness.me/golang/tree/runtime.mheap) 会持有 4,194,304 [`runtime.heapArena`](https://draveness.me/golang/tree/runtime.heapArena),每个 [`runtime.heapArena`](https://draveness.me/golang/tree/runtime.heapArena) 都会管理 64MB 的内存,单个 Go 语言程序的内存上限也就是 256TB。 \ No newline at end of file diff --git a/note/Go/函数.md b/note/Go/函数.md new file mode 100644 index 0000000..d9958a1 --- /dev/null +++ b/note/Go/函数.md @@ -0,0 +1,57 @@ +### 函数参数 +- 实际参数:函数实际调用时传入的参数 +- 形式参数:把参数列表中的参数 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/function.jpg) + +### 传参 +- 函数参数传递采用是值传递的方式 +- 所谓“值传递”,就是将实际参数在内存中的表示逐位拷贝(Bitwise Copy)到形式参数中 +- 对于像整型、数组、结构体这类类型,它们的内存表示就是它们自身的数据内容,因此当这些类型作为实参类型时,值传递拷贝的就是它们自身,传递的开销也与它们自身的大小成正比 +- 像 string、切片、map 这些类型它们的内存表示对应的是它们数据内容的“描述符”。当这些类型作为实参类型时,值传递拷贝的也是它们数据内容的“描述符”,不包括数据内容本身,所以这些类型传递的开销是固定的,与数据内容大小无关 + +### 函数传参例外 +- 对于类型为接口类型的形参,Go 编译器会把传递的实参赋值给对应的接口类型形参 +- 对于为变长参数的形参,Go 编译器会将零个或多个实参按一定形式转换为对应的变长形参 +- 变长参数实际上是通过切片来实现 + +### Go函数特征 +- 多个可以命名的返回值 +- 函数可以存储在变量中 +- 支持在函数内创建并通过返回值返回 +- 可以作为参数传入函数 +- 拥有自己的类型`type HandlerFunc func(ResponseWriter, *Request)` + +### 函数健壮性原则 +- 不要相信任何外部输入的参数 +- 不要忽略任何一个错误 +- 不要假定异常不会发生 + +### defer 获取调用函数方法名 +- runtime.Caller,参数为0时,返回调用者本身的信息(Trace的信息),为1是返回调用调用者的信息(调用Trace的信息) +- Caller 函数有四个返回值: + - 第一个返回值代表的是程序计数(pc) + - 第二个和第三个参数代表对应函数所在的源文件名以及所在行数 + - 最后一个参数代表是否能成功获取这些信息 +- runtime.FuncForPC 函数和程序计数器(PC)得到被跟踪函数的函数信息 +```go + +// trace1/trace.go + +func Trace() func() { + pc, _, _, ok := runtime.Caller(1) + if !ok { + panic("not found caller") + } + + fn := runtime.FuncForPC(pc) + name := fn.Name() + + println("enter:", name) + return func() { println("exit:", name) } +} + +func main() { + defer Trace()() + foo() +} +``` \ No newline at end of file diff --git a/note/Go/协程与线程对比.md b/note/Go/协程与线程对比.md new file mode 100644 index 0000000..30c0c2c --- /dev/null +++ b/note/Go/协程与线程对比.md @@ -0,0 +1,15 @@ +### 内存占用 +- 创建一个 goroutine 的栈内存消耗为 2 KB(Linux AMD64 Go v1.4后),运行过程中,如果栈空间不够用,会自动进行扩容。 +- 创建一个 thread 为了尽量避免极端情况下操作系统线程栈的溢出,默认会为其分配一个较大的栈内存( 1 - 8 MB 栈内存,POSIX Thread),而且还需要一个被称为 “guard page” 的区域用于和其他 thread 的栈空间进行隔离。而栈内存空间一旦创建和初始化完成之后其大小就不能再有变化,这决定了在某些特殊场景下系统线程栈还是有溢出的风险。 + +### 创建和销毁 +- 线程创建和销毀都会有巨大的消耗,是内核级的交互(trap)。进入内核所消耗的性能代价比较高,开销较大。 +- goroutine 是用户态线程,是由 go runtime 管理,创建和销毁的消耗非常小。 + +### 调度切换 +- 抛开陷入内核,线程切换会消耗 1000-1500 纳秒(上下文保存成本高,较多寄存器,公平性,复杂时间计算统计),一个纳秒平均可以执行 12-18 条指令。所以由于线程切换,执行指令的条数会减少 12000-18000。 +- goroutine 的切换约为 200 ns(用户态、3个寄存器),相当于 2400-3600 条指令。因此,goroutines 切换成本比 threads 要小得多 + +### 复杂性 +- 线程的创建和退出复杂,多个thread间通讯复杂(share memory) +- 不能大量创建线程,成本高,使用网络多路复用,存在大量callback。对于应用服务线程门槛高,例如需要做第三方库隔离,需要考虑引入线程池等 \ No newline at end of file diff --git a/note/Go/命令行小程序.md b/note/Go/命令行小程序.md new file mode 100644 index 0000000..56abe7f --- /dev/null +++ b/note/Go/命令行小程序.md @@ -0,0 +1,20 @@ +### 命令行小程序合计 +- gzip 压缩 +- gzip 解压 + +#### gzip 压缩 +- go build -o gzip +- gzip < main.go > main.go.gz +```go +package main + +import ( + "compress/gzip" + "io" + "os" +) +func main() { + w := gzip.NewWriter(os.Stdout) + io.Copy(w, os.Stdin) +} +``` \ No newline at end of file diff --git a/note/Go/垃圾回收.md b/note/Go/垃圾回收.md new file mode 100644 index 0000000..06a006c --- /dev/null +++ b/note/Go/垃圾回收.md @@ -0,0 +1,78 @@ +### 内存管理的三个参与者 +- mutator 指的是我们的应用,也就是 application,我们将堆上的对象看作一个图,跳出应用来看的话,应用的代码就是在不停地修改这张堆对象图里的指向关系 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/mutator.png) + + +- allocator 就很好理解了,指的是内存分配器,应用需要内存的时候都要向 allocator 申请。allocator 要维护好内存分配的数据结构,在多线程场景下工作的内存分配器还需要考虑高并发场景下锁的影响,并针对性地进行设计以降低锁冲突 +- collector 是垃圾回收器。死掉的堆对象、不用的堆内存都要由 collector 回收,最终归还给操作系统。当 GC 扫描流程开始执行时,collector 需要扫描内存中存活的堆对象,扫描完成后,未被扫描到的对象就是无法访问的堆上垃圾,需要将其占用内存回收掉 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/neicunguanli.png) + +### tcmalloc +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/tcmalloc.png) + +### Go内存分配过程分类 +- tiny :size < 16 bytes && has no pointer(noscan) +- small :has pointer(scan) || (size >= 16 bytes && size <= 32 KB) +- large :size > 32 KB + +### 内存分配数据结构 +- arenas 是 Go 向操作系统申请内存时的最小单位,每个 arena 为 64MB 大小,在内存中可以部分连续,但整体是个稀疏结构 +- 单个 arena 会被切分成以 8KB 为单位的 page,由 page allocator 管理 +- 一个或多个 page 可以组成一个 mspan +- 每个 mspan 可以按照 sizeclass 再划分成多个 element +- 同样大小的 mspan 又分为 scan 和 noscan 两种,分别对应内部有指针的 object 和内部没有指针的 object +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/neicunshuju.png) + +### mspan +- 每一个 mspan 都有一个 allocBits 结构 +- 从 mspan 里分配 element 时,只要将 mspan 中对应该 element 位置的 bit 位置一就可以,其实就是将 mspan 对应 allocBits 中的对应 bit 位置一 +- 每一个 mspan 都会对应一个 allocBits 结构 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/mspanbitmap.png) + + +### GC 流程 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/gc.png) + +### 三色标记 +- 黑表示已经扫描完毕,子节点扫描完毕(gcmarkbits = 1,且在队列外) +- 灰表示已经扫描完毕,子节点未扫描完毕(gcmarkbits = 1, 在队列内) +- 白表示未扫描,collector 不知道任何相关信息。 + +### 解决错标漏标,引入三色不变性 +- 强三色不变性 strong tricolor invariant:黑色对象不能直接引用白色对象 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/qiangsanse.png) +- 弱三色不变性 weak tricolor invariant:黑色对象可以引用白色对象,但白色对象必须有能从灰色对象可达的路径 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/ruosanse.png) + +### Go实现三色不变性,引入写屏障 +- snippet of code insert before pointer modify +- 在应用进入 GC 标记阶段前的 stw 阶段,会将全局变量 runtime.writeBarrier.enabled 修改为 true +- 然后所有的堆上指针修改操作在修改之前便会额外调用 runtime.gcWriteBarrier +- 栈上的对象市不会开启写屏障的 +- 混合写屏障,指针断开的老对象和新对象都标灰的实现 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/hunhexiepingzhang.png) +### 常见的写屏障 +- Dijistra Insertion Barrier,指针修改时,指向的新对象要标灰 +- Yuasa Deletion Barrier,指针修改时,修改前指向的对象要标灰 + +### 回收的两个goroutine +- 一个叫 sweep.g,主要负责清扫死对象,合并相关的空闲页 +- 一个叫 scvg.g,主要负责向操作系统归还内存 + +### sweep.g +- GC 的标记流程结束之后,sweep goroutine 就会被唤醒,进行清扫工作 +- 循环执行 sweepone -> sweep。针对每个 mspan,sweep.g 的工作是将标记期间生成的 bitmap 替换掉分配时使用的 bitmap +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/sweepg.png) +- 根据 mspan 中的槽位情况决定该 mspan 的去向 + - 如果 mspan 中存活对象数 = 0,也就是所有 element 都变成了内存垃圾,执行 freeSpan -> 归还组成该 mspan 所使用的页,并更新全局的页分配器摘要信息 + - 如果 mspan 中没有空槽,说明所有对象都是存活的,将其放入 fullSwept 队列中 + - 如果 mspan 中有空槽,说明这个 mspan 还可以拿来做内存分配,将其放入 partialSweep 队列中 + + ### scvg.g 待补充 + - bgscavenge + - pageAlloc.scavenge + - pageAlloc.scavengeOne + - pageAlloc.scavengeRangeLocked + - sysUnused + - madvise + ![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/madvise.png) \ No newline at end of file diff --git a/note/Go/字符串类型.md b/note/Go/字符串类型.md new file mode 100644 index 0000000..894f833 --- /dev/null +++ b/note/Go/字符串类型.md @@ -0,0 +1,278 @@ +### 原生字符串 string类型 +- 数据不可变,提高了字符串的并发安全性和存储利用率 +- 没有"\0"结尾,获取长度的时间复杂度是常数时间 +- 原生字符串所见即所得,构造多行字符串更加简单,多行字符串用反引号包裹 +- 采用unicode字符集,对非ASCII字符提供原生日支持,消除不同环境下乱码问题 + +### Go字符串组成 +- 字节角度 + - Go 语言中的字符串值也是一个可空的字节序列,字节序列中的字节个数称为该字符串的长度 + - len方法返回字符串字节长度 +- 字符视角 + - 字符串是由一个可空的字符序列构成 + - range 循环字符 + - utf8.RuneCountInString(s)统计字符个数 + +### rune类型与字面值 +- Go 使用 rune 这个类型来表示一个 Unicode 码点。rune 本质上是 int32 类型的别名类型,它与 int32 类型是完全等价的 +> Unicode 码点,就是指将 Unicode 字符集中的所有字符“排成一队”,字符在这个“队伍”中的位次,就是它在 Unicode 字符集中的码点。也就说,一个码点唯一对应一个字符 + +- 一个 rune 实例就是一个 Unicode 字符,一个 Go 字符串也可以被视为 rune 实例的集合 +- 可以通过字符字面值来初始化一个 rune 变量 + +#### 字面值 +- 通过单引号括起的字符字面值 +```go +'a' // ASCII字符 +'中' // Unicode字符集中的中文字符 +'\n' // 换行字符 +'\'' // 单引号字符 +``` +- Unicode 专用的转义字符\u 或\U 作为前缀,来表示一个 Unicode 字符 + - \u 后面接两个十六进制数。如果是用两个十六进制数无法表示的 Unicode 字符,可以使用\U + - \U 后面可以接四个十六进制数来表示一个 Unicode 字符 +```go + +'\u4e2d' // 字符:中 +'\U00004e2d' // 字符:中 +'\u0027' // 单引号字符 +``` +- 直接用整型值来给rune赋值 +```go +'\x27' // 使用十六进制表示的单引号字符 +'\047' // 使用八进制表示的单引号字符 +``` + +### 字符串字面值 +- 使用双引号给字符串赋值 +- 如果需要表示原始的值可以使用转义字符`"\\xe4\\xb8\\xad\xe5\x9b\xbd\xe4\xba\xba"` +```go + +"abc\n" +"中国人" +"\u4e2d\u56fd\u4eba" // 中国人 +"\U00004e2d\U000056fd\U00004eba" // 中国人 +"中\u56fd\u4eba" // 中国人,不同字符字面值形式混合在一起 +"\xe4\xb8\xad\xe5\x9b\xbd\xe4\xba\xba" // 十六进制表示的字符串字面值:中国人 +``` + +### Go字符串内部表示 +- 本身并不真正存储字符串数据 +- 由一个指向底层存储的指针和字符串的长度字段组成的 +- 获取字符串长度,时间复杂度为O(1) +- 直接将 string 类型通过函数 / 方法参数传入也不会带来太多的开销 +```go +// $GOROOT/src/reflect/value.go + +// StringHeader是一个string的运行时表示 +type StringHeader struct { + Data uintptr + Len int +} +``` + +```go +package main + +import ( + "fmt" + "reflect" + "unsafe" +) + +func dumpBytesArray(arr []byte) { + fmt.Printf("[") + for _, b := range arr { + fmt.Printf("%c ", b) + } + fmt.Printf("]\n") +} + +func main() { + var s = "hello" + hdr := (*reflect.StringHeader)(unsafe.Pointer(&s)) // 将string类型变量地址显式转型为reflect.StringHeader + fmt.Printf("0x%x\n", hdr.Data) // 0x495db9 + p := (*[5]byte)(unsafe.Pointer(hdr.Data)) // 获取Data字段所指向的数组的指针 + dumpBytesArray((*p)[:]) // [h e l l o ] // 输出底层数组的内容 +} + +``` + +### 字符串转换 +- `string`、`[]byte`和`[]rune`互相转换 +```go + +var s string = "中国人" + +// string -> []rune +rs := []rune(s) +fmt.Printf("%x\n", rs) // [4e2d 56fd 4eba] + +// string -> []byte +bs := []byte(s) +fmt.Printf("%x\n", bs) // e4b8ade59bbde4baba + +// []rune -> string +s1 := string(rs) +fmt.Println(s1) // 中国人 + +// []byte -> string +s2 := string(bs) +fmt.Println(s2) // 中国人 +``` + +### 字符串拼接 +- `+` 操作符 +- fmt.Sprintf +- strings.Join +- strings.Builder +- bytes.Buffer + +```go + +var sl []string = []string{ + "Rob Pike ", + "Robert Griesemer ", + "Ken Thompson ", +} + +func concatStringByOperator(sl []string) string { + var s string + for _, v := range sl { + s += v + } + return s +} + +func concatStringBySprintf(sl []string) string { + var s string + for _, v := range sl { + s = fmt.Sprintf("%s%s", s, v) + } + return s +} + +func concatStringByJoin(sl []string) string { + return strings.Join(sl, "") +} + +func concatStringByStringsBuilder(sl []string) string { + var b strings.Builder + for _, v := range sl { + b.WriteString(v) + } + return b.String() +} + +func concatStringByStringsBuilderWithInitSize(sl []string) string { + var b strings.Builder + b.Grow(64) + for _, v := range sl { + b.WriteString(v) + } + return b.String() +} + +func concatStringByBytesBuffer(sl []string) string { + var b bytes.Buffer + for _, v := range sl { + b.WriteString(v) + } + return b.String() +} + +func concatStringByBytesBufferWithInitSize(sl []string) string { + buf := make([]byte, 0, 64) + b := bytes.NewBuffer(buf) + for _, v := range sl { + b.WriteString(v) + } + return b.String() +} + +func BenchmarkConcatStringByOperator(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByOperator(sl) + } +} + +func BenchmarkConcatStringBySprintf(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringBySprintf(sl) + } +} + +func BenchmarkConcatStringByJoin(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByJoin(sl) + } +} + +func BenchmarkConcatStringByStringsBuilder(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByStringsBuilder(sl) + } +} + +func BenchmarkConcatStringByStringsBuilderWithInitSize(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByStringsBuilderWithInitSize(sl) + } +} + +func BenchmarkConcatStringByBytesBuffer(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByBytesBuffer(sl) + } +} + +func BenchmarkConcatStringByBytesBufferWithInitSize(b *testing.B) { + for n := 0; n < b.N; n++ { + concatStringByBytesBufferWithInitSize(sl) + } +} +``` + +### []byte和string互相转换不拷贝方法 +- go 1.20之前 +```go +// toBytes performs unholy acts to avoid allocations +func toBytes(s string) []byte { + return *(*[]byte)(unsafe.Pointer(&s)) +} + +// toString performs unholy acts to avoid allocations +func toString(b []byte) string { + return *(*string)(unsafe.Pointer(&b)) +} + +func ByteArray2String(arr []byte) (str string) { + sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arr)) + stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) + + stringHeader.Data = sliceHeader.Data + stringHeader.Len = sliceHeader.Len + return +} + +func String2ByteArray(str string) (arr []byte) { + stringHeader := (*reflect.StringHeader)(unsafe.Pointer(&str)) + sliceHeader := (*reflect.SliceHeader)(unsafe.Pointer(&arr)) + + sliceHeader.Data = stringHeader.Data + sliceHeader.Len = stringHeader.Len + sliceHeader.Cap = stringHeader.Len + return +} +``` +- go 1.20之后 +> 常用的 reflect.SliceHeader 和 reflect.StringHeader 将会被标注为被废弃 +```go +func StringToBytes(s string) []byte { + return unsafe.Slice(unsafe.StringData(s), len(s)) +} + +func BytesToString(b []byte) string { + return unsafe.String(&b[0], len(b)) +} +``` \ No newline at end of file diff --git a/note/Go/安装多版本GO.md b/note/Go/安装多版本GO.md new file mode 100644 index 0000000..f6d7234 --- /dev/null +++ b/note/Go/安装多版本GO.md @@ -0,0 +1,15 @@ +### 命令 +- 正常会下载到$HOME/sdk +```shell +go install golang.org/dl/go1.13@latest +``` + +### 补全环境 +```shell +go1.13 download +``` + +### 查看版本 +```shell +go1.13 version +``` \ No newline at end of file diff --git a/note/Go/崩溃恢复.md b/note/Go/崩溃恢复.md new file mode 100644 index 0000000..1230d3f --- /dev/null +++ b/note/Go/崩溃恢复.md @@ -0,0 +1,65 @@ +https://mp.weixin.qq.com/s/ZmfwNlq5_A2RgpUSkJQXrQ + +### defer +- Go 语言提供的一种延迟调用机制,defer 的运作离不开函数 +- 只有在函数(和方法)内部才能使用 defer +- defer 关键字后面只能接函数(或方法),这些函数被称为 deferred 函数 +- defer 将它们注册到其所在 Goroutine 中,用于存放 deferred 函数的栈数据结构中,这些 deferred 函数将在执行 defer 的函数退出前,按后进先出(LIFO)的顺序被程序调度执行 +- 无论是执行到函数体尾部返回,还是在某个错误处理分支显式 return,又或是出现 panic,已经存储到 deferred 函数栈中的函数,都会被调度执行 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/defer.jpg) +![](blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/defer.jpg) + +blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/2023-12-11-xoomgtcr.png + +### defer 注意事项 +- 明确哪些函数可以作为 deferred 函数,有返回值的deferred函数的返回值会被自动丢弃 + - 内置函数 append、cap、len、make、new和imag不能被注册为deferred函数,可以包装一层func,进行调用 + - close、copy、delete、print、recover 可以被注册为deferred函数 +```go + +Functions: 内置函数列表 + append cap close complex copy delete imag len + make new panic print println real recover +``` +- 注意 defer 关键字后面表达式的求值时机,defer 关键字后面的表达式,是在将 deferred 函数注册到 deferred 函数栈的时候进行求值的 +```go +package main + +import ( + "fmt" +) + +func foo1() { + for i := 0; i <= 3; i++ { + defer fmt.Println(i) + } +} + +func foo2() { + for i := 0; i <= 3; i++ { + defer func(n int) { + fmt.Println(n) + }(i) + } +} + +func foo3() { + for i := 0; i <= 3; i++ { + defer func() { + fmt.Println(i) + }() + } +} + +func main() { + fmt.Println("foo1 result:") + foo1() + fmt.Println("\nfoo2 result:") + foo2() + fmt.Println("\nfoo3 result:") + foo3() +} + +``` + +- 知晓 defer 带来的性能损耗 \ No newline at end of file diff --git a/note/Go/常量.md b/note/Go/常量.md new file mode 100644 index 0000000..c086830 --- /dev/null +++ b/note/Go/常量.md @@ -0,0 +1,118 @@ +### Go 常量创新 +- 支持无类型常量 +- 支持隐式自动转型 +- 可用于实现枚举 + +### 无类型常量 +- 常量声明时没有被显示设置类型 +- const n 根据字面值推断类型为int +```go +package main + +import ( + "fmt" +) + +type myInt int + +const n = 13 +const m int64 = n + 5 // OK + +func main() { + var a int = 5 + fmt.Println(a + n) // 输出:18 +} + +``` + +### 隐式转型 +- 对于无类型常量参与的表达式求值,Go 编译器会根据上下文中的类型信息,把无类型常量自动转换为相应的类型后,再参与求值计算 +- 转型的对象是一个常量,并不会引发类型安全问题,Go 编译器会保证这一转型的安全性 +- 如果 Go 编译器在做隐式转型时,发现无法将常量转换为目标类型,Go 编译器也会报错 +```go + +const m = 1333333333 + +var k int8 = 1 +j := k + m // 编译器报错:constant 1333333333 overflows int8 +``` + +### 实现枚举 +- Go 的 const 语法提供了“隐式重复前一个非空表达式”的机制 +```go +package main + +import ( + "fmt" +) + +const ( + Apple = 11 + Banana = 12 + Strawberry + Pear +) + +func main() { + fmt.Println(Apple) + fmt.Println(Pear) +} +``` + +- `iota` const 声明块(包括单行声明)中,每个常量所处位置在块中的偏移值(从零开始) +- 每一行中的 iota 自身也是一个无类型常量,可以像无类型常量那样,自动参与到不同类型的求值过程中来,不需要再对它进行显式转型操作 +- 同一行的 iota 即便出现多次,多个 iota 的值也是一样的 +- 其值一直自增1直到遇到下一个const关键字,其值才被重新置为0 +```go + +// $GOROOT/src/sync/mutex.go +const ( + mutexLocked = 1 << iota // 刚开始iota为1,后续自增1, 1 << 0 + mutexWoken // 定义时没有显示赋值,重复上一行的值,1 << iota,相当于1 << 1 + mutexStarving // 同上 + mutexWaiterShift = iota // 重新设置为3 + starvationThresholdNs = 1e6 +) + +const ( + Apple, Banana = iota, iota + 10 // 0, 10 (iota = 0) + Strawberry, Grape // 1, 11 (iota = 1) + Pear, Watermelon // 2, 12 (iota = 2) +) +``` + +- 略过iota中的0或者某个值 +```go + +// $GOROOT/src/syscall/net_js.go +const ( + _ = iota + IPV6_V6ONLY // 1 + SOMAXCONN // 2 + SO_ERROR // 3 +) + +const ( + _ = iota // 0 + Pin1 + Pin2 + Pin3 + _ + Pin5 // 5 +) +``` + +- 每个const块都有自己的iota +```go +const ( + a = iota + 1 // 1, iota = 0 + b // 2, iota = 1 + c // 3, iota = 2 +) + +const ( + i = iota << 1 // 0, iota = 0 + j // 2, iota = 1 + k // 4, iota = 2 +) +``` \ No newline at end of file diff --git a/note/Go/接口.md b/note/Go/接口.md new file mode 100644 index 0000000..423d4f4 --- /dev/null +++ b/note/Go/接口.md @@ -0,0 +1,125 @@ +### 接口定义注意事项 +- Go语言要求接口类型声明中的方法必须是具名的,并且方法名字在这个接口类型的方法集合中是唯一的 +- 即使嵌套接口出现交集,也要求交集的方法名,入参和返回值一样,否则编译器会报错 +- 在 Go 接口类型的方法集合中放入首字母小写的非导出方法也是合法的 + +### 实现接口 +- 如果一个类型 T 的方法集合是某接口类型 I 的方法集合的等价集合或超集,类型 T 实现了接口类型 I,那么类型 T 的变量就可以作为合法的右值赋值给接口类型 I 的变量 +- 任何类型都实现了空接口 + +### 接口的静态特性与动态特性 +- 静态特性:接口类型变量具有静态类型,比如var err error中变量 err 的静态类型为 error +- 静态类型:接口类型变量在运行时还存储了右值的真实类型信息,这个右值的真实类型被称为接口类型变量的动态类型 +```go +var err error +err = errors.New("error1") +fmt.Printf("%T\n", err) // *errors.errorString +``` + +### 动静特性优势 +- 接口类型变量在程序运行时可以被赋值为不同的动态类型变量,每次赋值后,接口类型变量中存储的动态类型信息都会发生变化 +- 鸭子类型 Duck Typing,某一类型表现出的行为不是有基因决定的,而是由类型所表现出的行为决定的 +- Go 接口还可以保证“动态特性”使用时的安全性,编译阶段就可以发现接口类型赋值不正确的错误 +```go +type QuackableAnimal interface { + Quack() +} + +type Duck struct{} + +func (Duck) Quack() { + println("duck quack!") +} + +type Dog struct{} + +func (Dog) Quack() { + println("dog quack!") +} + +type Bird struct{} + +func (Bird) Quack() { + println("bird quack!") +} + +func AnimalQuackInForest(a QuackableAnimal) { + a.Quack() +} + +func main() { + animals := []QuackableAnimal{new(Duck), new(Dog), new(Bird)} + for _, animal := range animals { + AnimalQuackInForest(animal) + } +} +``` + +### 接口变量的内部表示 +```go +// $GOROOT/src/runtime/runtime2.go +type iface struct { + tab *itab + data unsafe.Pointer +} + +type eface struct { + _type *_type + data unsafe.Pointer +} +``` + +- eface 用于表示没有方法的空接口(empty interface)类型变量,也就是 interface{}类型的变量 +- iface 用于表示其余拥有方法的接口 interface 类型变量 +- 第二个指针字段的功能相同,都是指向当前赋值给该接口类型变量的动态类型变量的值 +- _type 指向赋值给这个接口的变量的动态类型信息 +```go + +// $GOROOT/src/runtime/type.go + +type _type struct { + size uintptr + ptrdata uintptr // size of memory prefix holding all pointers + hash uint32 + tflag tflag + align uint8 + fieldAlign uint8 + kind uint8 + // function for comparing objects of this type + // (ptr to object A, ptr to object B) -> ==? + equal func(unsafe.Pointer, unsafe.Pointer) bool + // gcdata stores the GC type data for the garbage collector. + // If the KindGCProg bit is set in kind, gcdata is a GC program. + // Otherwise it is a ptrmask bitmap. See mbitmap.go for details. + gcdata *byte + str nameOff + ptrToThis typeOff +} +``` +- iface 除了要存储动态类型信息之外,还要存储接口本身的信息(接口的类型信息、方法列表信息等)以及动态类型所实现的方法的信息,因此 iface 的第一个字段指向一个itab类型结构 +```go +// $GOROOT/src/runtime/runtime2.go +type itab struct { + inter *interfacetype // 接口类型自身的信息 + _type *_type + hash uint32 // copy of _type.hash. Used for type switches. + _ [4]byte + fun [1]uintptr // variable sized. fun[0]==0 means _type does not implement inter. 字段fun则是动态类型已实现的接口方法的调用地址数组。 +} + + +// $GOROOT/src/runtime/type.go +type interfacetype struct { + typ _type + pkgpath name + mhdr []imethod +} +``` + +### 接口相等判断 +- 接口存储的动态数据类型一样 + - 空接口使用_type + - 非空接口使用tab._type +- 接口存储的data指向的数据一样 +- 对于空接口类型变量,只有 _type 和 data 所指数据内容一致的情况下,两个空接口类型变量之间才能划等号 +- Go 在创建 eface 时一般会为 data 重新分配新内存空间,将动态类型变量的值复制到这块内存空间,并将 data 指针指向这块内存空间,多数情况下看到的 data 指针值都是不同的 \ No newline at end of file diff --git a/note/Go/数据类型.md b/note/Go/数据类型.md new file mode 100644 index 0000000..343a022 --- /dev/null +++ b/note/Go/数据类型.md @@ -0,0 +1,191 @@ +### 整型 +- 平台无关整型 +- 平台相关整型 +- 区别在于整数类型在不同 CPU 架构或操作系统下面,它们的长度是否是一致 + +#### 平台无关整型 +- 任何CPU架构下,长度都是一致的 +- 有符号整数 int8-int64 +- 无符号整数 uint8-unint64 +- 本质差别在于最高二进制位(bit 位)是否被解释为符号位 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/zhengxing1.jpg) + +#### 平台相关整型 +- int +- uint +- uintptr +- 可以调用unsafe.Sizeof(变量) 方法来获取对应平台占用的大小 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/zhengxing2.jpg) + +#### 整型溢出 +- 整型运算结果超出表示的范围 +- 溢出的结果依然在整型表示范围 +- 结果与预期结果不符 +```go + +var s int8 = 127 +s += 1 // 预期128,实际结果-128 + +var u uint8 = 1 +u -= 2 // 预期-1,实际结果255 +``` + +#### 整型字面值与格式化输出 +- 二进制 +- 八进制 +- 十进制 +- 十六进制 +```go +d1 := 0b10000001 // 二进制,以"0b"为前缀,在go1.13之后的版本才生效 +d2 := 0B10000001 // 二进制,以"0B"为前缀,在go1.13之后的版本才生效 + +b := 0700 // 八进制,以"0"为前缀 +e1 := 0o700 // 八进制,以"0o"为前缀 +e2 := 0O700 // 八进制,以"0O"为前缀 + +a := 53 // 十进制 + +c1 := 0xaabbcc // 十六进制,以"0x"为前缀 +c2 := 0Xddeeff // 十六进制,以"0X"为前缀 + +``` + +#### 支持在字面值中增加数字分隔符"-" +- 在go1.13之后的版本才生效 +```go + +a := 5_3_7 // 十进制: 537 +b := 0b_1000_0111 // 二进制位表示为10000111 +c1 := 0_700 // 八进制: 0700 +c2 := 0o_700 // 八进制: 0700 +d1 := 0x_5c_6d // 十六进制:0x5c6d +``` + +### 浮点数 +- float32 32位,单精度浮点数 +- float64 64位,双精度浮点数 +- 默认值0.0 +- Go提供的浮点数都是平台无关的 +#### 浮点数二进制表示 +- IEEE 754算术标准 +- 单精度 32位 +- 双精度 64位 +- 扩展单精度 43位以上 +- 扩展双精度 79位以上 +- 二进制表示方式 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/ieee754.jpg) + +#### 浮点数阶码和尾数 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/jiema.jpg) + +#### 十进制小数转换成二进制计算方式 +- 乘2取整的竖式计算 +- 小数部分每次乘2,结果大于1记1,结果小于1记0 +- 剩下的小数部分再乘2,记录方式同上 +- 直到结果等于小数部分为0 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/shushijisuan.jpg) + +#### 十进制形式的浮点值 139.8125转换为IEEE754单精度二进制 +##### 二进制转换 +- 整数部分转换为二进制 `10001011b` +- 小数部分转换为二进制 `1101b` +- 原来的`139.8125`变为`10001011.1101` + +##### 移动小数点,直到整数部分仅有1个1 +- `10001011.1101b` => `1.00010111101b` +- 小数点移动7位 +- 尾数为`00010111101b` + +##### 计算阶码 +- 阶码 = 指数 + 偏移值,指数就是小数点移动的位数,偏移值为2^(e-1)-1,e就是阶码部分的bit位数,32位精度e为8,64精度e为11 +- 阶码 = 7 + 127,为134d,`10000110b` + +##### 符号位,阶码和尾数填充,尾数位不够的话补0 +- 符号位 0 +- 阶码 10000110 +- 尾数 00010111101(000000000000),补充12个0 +- 二进制139.8125的表示方式就是`0b_0_10000110_00010111101_000000000000` +```go + +func main() { + var f float32 = 139.8125 + bits := math.Float32bits(f) + fmt.Printf("%b\n", bits)// 高危补0表示符号位 + // 1000011000010111101000000000000 +} +``` + + +#### 字面值与格式化输出 +##### 字面值 +- 十进制表示浮点值的形式 +```go +3.1415 +.15 // 整数部分如果为0,整数部分可以省略不写 +81.80 +82. // 小数部分如果为0,小数点后的0可以省略不写 +``` +- 科学计数法 +```go +v +6674.28e-2 // 6674.28 * 10^(-2) = 66.742800 +.12345E+5 // 0.12345 * 10^5 = 12345.000000 +``` +- 16进制科学计数法,整数和小数部分用的都是16进制,但是指数部分依然是10进制 +```go +0x2.p10 // 2.0 * 2^10 = 2048.000000 +0x1.Fp+0 // 1.9375 * 2^0 = 1.937500 +``` + +##### 格式化 +- %f格式化 +```go +var f float64 = 123.45678 +fmt.Printf("%f\n", f) // 123.456780 +``` + +- 输出科学计数法形式 +```go +var f float64 = 123.45678 +fmt.Printf("%e\n", f) // 1.234568e+02 十进制科学计数法 +fmt.Printf("%x\n", f) // 0x1.edd3be22e5de1p+06 16进制科学计数法 +``` + +### 复数 +- complex64,实部与虚部都是float32 +- complex128,实部与虚部都是float64 +- 如果一个复数没有显示赋予类型,则默认类型为complex128 + +#### 字面值与初始化 +- 通过字面值直接初始化 +```go + +var c = 5 + 6i +var d = 0o123 + .12345E+5i // 83+12345i +``` + +- `complex` 函数,方便创建一个 complex128 类型值 +```go + +var c = complex(5, 6) // 5 + 6i +var d = complex(0o123, .12345E+5) // 83+12345i +``` + +- 通过预定义函数 `real`和`imag`来获取一个复数的实部和虚部 +```go + +var c = complex(5, 6) // 5 + 6i +r := real(c) // 5.000000 +i := imag(c) // 6.000000 +``` + +### 创建自定义的数值类型 +- type MyInt int32 +- MyInt底层仍然是int32,数值性质也与int32相同 +- 但是Go的类型安全规则,限定MyInt和int32仍是完全不同的两种类型,无法进行相互复制和计算 +- 赋值和计算需显示类型转换 + +### 通过类型别名来定义数值类型 +- type MyInt = int32 +- 通过类型别名语法定义的新类型与原类型别无二致,可以完全相互替代 \ No newline at end of file diff --git a/note/Go/数组切片.md b/note/Go/数组切片.md new file mode 100644 index 0000000..236fffb --- /dev/null +++ b/note/Go/数组切片.md @@ -0,0 +1,258 @@ +### 一、数组 + +#### 1. 定义 + +- 一个长度固定的、由同构类型(相同类型)元素组成的连续序列 +- `var arr [N]T` +- 数组变量 arr,类型为`[N]T`,长度为N,元素类型为T +- 如果两个数组类型的元素类型 T 与数组长度 N 都是一样的,那么这两个数组类型是等价的 +- 如果有一个属性不同,它们就是两个不同的数组类型 +- 通过`len`方法获得数组长度 +- 通过`unsafe.Pointer()`可以获得数组总的占用空间 +- 数组做为函数参数,值拷贝,性能代价高 + +#### 2. 初始化 + +- 定义但不初始化 +- 定义且初始化并指定长度 +- 定义且初始化当不指定长度 +- 下标赋值的方式对它进行初始化 +```go + +var arr1 [6]int // 没有显示初始化,数据默认为零值,[0 0 0 0 0 0] + +var arr2 = [6]int { + 11, 12, 13, 14, 15, 16, +} // [11 12 13 14 15 16] + +var arr3 = [...]int { + 21, 22, 23, +} // 编译器自动推断数组的长度,[21 22 23] +fmt.Printf("%T\n", arr3) // [3]int + +// 下标赋值的方式对它进行初始化 +var arr4 = [...]int{ + 99: 39, // 将第100个元素(下标值为99)的值赋值为39,其余元素值均为0 +} +fmt.Printf("%T\n", arr4) // [100]int +``` + +#### 3. 底层方法 + +- 初始化 + +```go +// NewArray returns a new fixed-length array Type. +func NewArray(elem *Type, bound int64) *Type { + if bound < 0 { + Fatalf("NewArray: invalid bound %v", bound) + } + t := New(TARRAY) + t.Extra = &Array{Elem: elem, Bound: bound} + t.SetNotInHeap(elem.NotInHeap()) + return t +} +``` + +- 长度推导 + - 定义时指定长度,变量类型和长度会在编译中的类型检查阶段取出来,直接调用NewArray方法创建一个Array + - 未指定长度会在编译中的类型检查阶段调用typecheckcomplit阶段推导出长度 + +```go +// The result of typecheckcomplit MUST be assigned back to n, e.g. +// n.Left = typecheckcomplit(n.Left) +func typecheckcomplit(n *Node) (res *Node) { + // Need to handle [...]T arrays specially. + if n.Right.Op == OTARRAY && n.Right.Left != nil && n.Right.Left.Op == ODDD { + n.Right.Right = typecheck(n.Right.Right, ctxType) + if n.Right.Right.Type == nil { + n.Type = nil + return n + } + elemType := n.Right.Right.Type // 元素类型 + + length := typecheckarraylit(elemType, -1, n.List.Slice(), "array literal") // 数组长度 + + n.Op = OARRAYLIT + n.Type = types.NewArray(elemType, length) + n.Right = nil + return n + } + switch t.Etype { + default: + yyerror("invalid composite literal type %v", t) + n.Type = nil + + case TARRAY: + typecheckarraylit(t.Elem(), t.NumElem(), n.List.Slice(), "array literal") // 对数组进行元素和索引的检查 + n.Op = OARRAYLIT + n.Right = nil + // ... 省略代码 +} +``` + +- 数组越界检查 + - 使用常量或整数数值在静态编译期间可以验出数组越界风险 + +```go +func typecheck1(n *Node, top int) (res *Node) { + if enableTrace && trace { + defer tracePrint("typecheck1", n)(&res) + } + + switch n.Op { + case OINDEX: + ok |= ctxExpr + n.Left = typecheck(n.Left, ctxExpr) + n.Left = defaultlit(n.Left, nil) + n.Left = implicitstar(n.Left) + l := n.Left // 数组 + n.Right = typecheck(n.Right, ctxExpr) // 索引 + r := n.Right + t := l.Type + case TSTRING, TARRAY, TSLICE: // 判断是数组 + n.Right = indexlit(n.Right) + if n.Right.Type != nil && !n.Right.Type.IsInteger() { + yyerror("non-integer %s index %v", why, n.Right) + break + } + + if !n.Bounded() && Isconst(n.Right, CTINT) { + x := n.Right.Int64() + if x < 0 { + yyerror("invalid %s index %v (index must be non-negative)", why, n.Right) + } else if t.IsArray() && x >= t.NumElem() { // 数组越界 + yyerror("invalid array index %v (out of bounds for %d-element array)", n.Right, t.NumElem()) + } + } +``` + +- 运行时数组越界 + - runtime.panicindex + +```go +TEXT runtime·panicIndex(SB),NOSPLIT,$0-16 + MOVQ AX, x+0(FP) + MOVQ CX, y+8(FP) + JMP runtime·goPanicIndex(SB) +``` + + + +- runtime.goPanicIndex + +```go +func goPanicIndex(x int, y int) { + panicCheck1(getcallerpc(), "index out of range") + panic(boundsError{x: int64(x), signed: true, y: y, code: boundsIndex}) +} +``` + + + +### 切片 + +- 定义切片变量时,不像数组一样定义长度 +- 长度容量不固定 +- len 返回切片长度 +- append 向切片中追加元素 +- 切片传参数,相当于传递底层数组的描述符 + +#### 切片结构 +- 底层运行时结构 +- array 底层数组的指针 +- len 切片长度 +- cap 切片容量 +```go + +type slice struct { + array unsafe.Pointer + len int + cap int +} +``` + +#### 切片定义与初始化 +- 通过make创建切片,可以指定长度与容量 + - `var arr = make([]int, 3, 6)` 类型,长度,容量 + - 不指定容量,cap=len +- 采用 array[low : high : max]语法基于一个已存在的数组创建切片。这种方式被称为数组的切片化 + - 从原数组起始位置low开始 + - 新数组长度 high - low + - 数组容量是 max - low + - 对新切片中元素的修改将直接影响原数组 + - 新数组的容量和长度不能大于原数组 +```go +arr := [10]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} +sl := arr[3:7:9] +``` +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/arr_low_high_max.jpg) + +- 基于切片创建切片 + +#### 切片动态扩容 +- 翻倍扩容 +- 基于一个已有数组建立的切片,一旦追加的数据操作触碰到切片的容量上限(实质上也是数组容量的上界),切片就会和原数组解除“绑定”,后续对切片的任何修改都不会反映到原数组中 + +### 数组与切片互相转换(go1.17后官方支持) +- 数组转切片 +```go +func main() { + arr := [3]int{1, 2, 3} + sli := arr[:] + fmt.Println(sli) +} +``` + +- 切片转数组 `unsafe` 方法 +```go +func main() { + b := []int{1, 2, 3} + p := (*[3]int)(unsafe.Pointer(&b[0])) + (*p)[1]+= 10 + fmt.Println(*p) +} +``` + +- 切片转数组官方方法,转换后的数组长度不能大于原切片的长度,nil 切片或 cap 为 0 的 empty 切片都可以被转换为一个长度为 0 的数组指针 +```go +func main() { + b := []int{1, 2, 3} + p := (*[3]int)(b) + (*p)[1] += 10 + fmt.Println(*p) +} +``` + +### 函数传参性能 +- 传参都是值拷贝 +- slice拷贝的是引用结构 +- array拷贝的是数组的值(包含元素) +```go +a1 := [16]int{} + d1 := func(arr [16]int) int { + return len(arr) + } + + f1 := func() int { + return d1(a1) + } + + a2 := [65535]int{} + d2 := func(arr [65535]int) int { + return len(arr) + } + f2 := func() int { + return d2(a2) + } + + for _, f := range []func() int{f1, f2} { + start := time.Now() + for i := 0; i < 10000; i++ { + f() + } + fmt.Printf("time.Since(start).Milliseconds(): %v\n", time.Since(start).Microseconds()) + } + // 88 + // 398362 +``` \ No newline at end of file diff --git a/note/Go/方法.md b/note/Go/方法.md new file mode 100644 index 0000000..d1f633d --- /dev/null +++ b/note/Go/方法.md @@ -0,0 +1,153 @@ +### 方法 +- Go 语言中的方法的本质就是,一个以方法的 receiver 参数作为第一个参数的普通函数 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/methods.jpg) +- 方法支持赋值给变量,相当于第一个参数是结构体自身或者自身的指针类型 +```go +package main + +import "fmt" + +type T struct{} + +func (t *T) M(n int) { + fmt.Println(n) +} + +func (t T)N(n int) { + fmt.Println(n) +} + +func main() { + m1 := (*T).M + m1(&T{}, 2) + + m2 := T.N + m2(T{}, 3) +} +``` + +### 方法接收receiver +```go +func (t *T或T) MethodName(参数列表) (返回值列表) { + // 方法体 +} +``` +- T为基类型 +- receiver 参数的基类型本身不能为指针类型或接口类型 +- 每个方法只能有一个 receiver 参数 +- Go 不支持在方法的 receiver 部分放置包含多个 receiver 参数的参数列表,或者变长 receiver 参数 +- 方法接收器(receiver)参数、函数 / 方法参数,以及返回值变量对应的作用域范围,都是函数 / 方法体对应的显式代码块 +- receiver 部分的参数名不能与方法参数列表中的形参名,以及具名返回值中的变量名存在冲突,必须在这个方法的作用域中具有唯一性 +- 如果在方法体中,我们没有用到 receiver 参数,我们也可以省略 receiver 的参数名 + +### 方法声明约束 +- 方法声明要与 receiver 参数的基类型声明放在同一个包内 +- 不能为原生类型(诸如 int、float64、map 等)添加方法 +- 不能跨越 Go 包为其他包的类型声明新方法 + +### 选择receiver 原则 +- 如果需要修改receiver内部属性,使用指针类型 +- 不需要修改内部属性,使用结构体类型,传参值拷贝,但是结构体过大时,选用指针类型 +- 如果 T 类型需要实现某个接口,那就要使用 T 作为 receiver 参数的类型,来满足接口类型方法集合中的所有方法 + +### 方法集合 +```go +- 用来判断一个类型是否实现了某接口类型的唯一手段 +- T类型实现了M1方法 +- *T类型实现了M1和M2方法 +type Interface interface { + M1() + M2() +} + +type T struct{} + +func (t T) M1() {} +func (t *T) M2() {} + +func main() { + var t T + var pt *T + var i Interface + + i = pt + i = t // cannot use t (type T) as type Interface in assignment: T does not implement Interface (M2 method has pointer receiver) +} +``` + +### type定义的新类型和命名的类型的方法集合 +- 自定义非接口类型的 defined 类型的方法集合为空 +- 自定义接口类型的 defined 类型的方法集合为接口的方法 +```go +package main + +type T struct{} + +func (T) M1() {} +func (*T) M2() {} + +type T1 T + +func main() { + var t T + var pt *T + var t1 T1 + var pt1 *T1 + + dumpMethodSet(t) + dumpMethodSet(t1) + + dumpMethodSet(pt) + dumpMethodSet(pt1) +} + + +main.T's method set: +- M1 + +main.T1's method set is empty! + +*main.T's method set: +- M1 +- M2 + +*main.T1's method set is empty! +``` +- 无论原类型是接口类型还是非接口类型,类型别名都与原类型拥有完全相同的方法集合 +```go + +type T struct{} + +func (T) M1() {} +func (*T) M2() {} + +type T1 = T + +func main() { + var t T + var pt *T + var t1 T1 + var pt1 *T1 + + dumpMethodSet(t) + dumpMethodSet(t1) + + dumpMethodSet(pt) + dumpMethodSet(pt1) +} + + +main.T's method set: +- M1 + +main.T's method set: +- M1 + +*main.T's method set: +- M1 +- M2 + +*main.T's method set: +- M1 +- M2 +``` \ No newline at end of file diff --git a/note/Go/流程控制.md b/note/Go/流程控制.md new file mode 100644 index 0000000..4ef9f0e --- /dev/null +++ b/note/Go/流程控制.md @@ -0,0 +1,122 @@ +### 带label的continue语句 +- 多用于嵌套循环 +- 终止内层循环用,继续执行外层循环 +- 如果使用`goto`语句不管是内层循环还是外层循环都会被终结,代码将会从 outerloop 这个 label 处,开始重新执行嵌套循环语句,这与带 label 的 continue 的跳转语义是完全不同的 +```go +func main() { + var sl = [][]int{ + {1, 34, 26, 35, 78}, + {3, 45, 13, 24, 99}, + {101, 13, 38, 7, 127}, + {54, 27, 40, 83, 81}, + } + +outerloop: + for i := 0; i < len(sl); i++ { + for j := 0; j < len(sl[i]); j++ { + if sl[i][j] == 13 { + fmt.Printf("found 13 at [%d, %d]\n", i, j) + continue outerloop + } + } + } +} +``` + +### 带label的break语句 +- 用于嵌套循环,结束外层循环 +```go +var gold = 38 + +func main() { + var sl = [][]int{ + {1, 34, 26, 35, 78}, + {3, 45, 13, 24, 99}, + {101, 13, 38, 7, 127}, + {54, 27, 40, 83, 81}, + } + +outerloop: + for i := 0; i < len(sl); i++ { + for j := 0; j < len(sl[i]); j++ { + if sl[i][j] == gold { + fmt.Printf("found gold at [%d, %d]\n", i, j) + break outerloop + } + } + } +} +``` + + +### for 循环的坑 +- 循环变量重用,i和v变量只会定义一次,后续每次都是重用。为闭包函数增加参数,传参iv +```go +func main() { + var m = []int{1, 2, 3, 4, 5} + + for i, v := range m { + go func() { + time.Sleep(time.Second * 3) + fmt.Println(i, v) + }() + } + + time.Sleep(time.Second * 10) +} +``` + +- 参与 for range 循环的是 range 表达式的副本,参与循环的a是a的副本,但是修改的是原始的a,数组类型循环可以用切片代替 +```go +func main() { + var a = [5]int{1, 2, 3, 4, 5} + var r [5]int + + fmt.Println("original a =", a) + + for i, v := range a { + if i == 0 { + a[1] = 12 + a[2] = 13 + } + r[i] = v + } + + fmt.Println("after for range loop, r =", r) + fmt.Println("after for range loop, a =", a) +} + + +// original a = [1 2 3 4 5] +// after for range loop, r = [1 2 3 4 5] +// after for range loop, a = [1 12 13 4 5] +``` + +- 遍历 map 中元素的随机性 + +### switch case type +- x 必须是一个interface{} 接口类型 +- switch不用短变量接收,只会有x的动态类型 +- 用短变量接受还可以获得x的value,v 存储的是变量 x 的动态类型对应的值信息 +- Go 中所有类型都实现了 interface{}类型,所以 case 后面可以是任意类型信息 +- 如果在 switch 后面使用了某个特定的接口类型 I,那么 case 后面就只能使用实现了接口类型 I 的类型了,否则 Go 编译器会报错 +```go + +func main() { + var x interface{} = Book{Name: "11"} + switch v := x.(type) { + case nil: + println("v is nil") + case int: + println("the type of v is int, v =", v) + case string: + println("the type of v is string, v =", v) + case bool: + println("the type of v is bool, v =", v) + case Book: + fmt.Println("the type of v is book, v =", v) + default: + println("don't support the type") + } +} +``` \ No newline at end of file diff --git a/note/Go/简易HTTP_SERVER.md b/note/Go/简易HTTP_SERVER.md new file mode 100644 index 0000000..6fbae67 --- /dev/null +++ b/note/Go/简易HTTP_SERVER.md @@ -0,0 +1,34 @@ +### 原理 +- 结构体实现ServeHTTP(http.ResponseWriter, *http.Request)方法 + +### 代码 +```go +package main + +import ( + "fmt" + "io/ioutil" + "net/http" +) + +type Handler struct{} + +func (h *Handler) ServeHTTP(w http.ResponseWriter, req *http.Request) { + requestBody, err := ioutil.ReadAll(req.Body) + if err != nil { + w.Write([]byte(err.Error())) + return + } + fmt.Println(req.RequestURI) + fmt.Println(string(requestBody)) +} + +func main() { + server := http.Server{Addr: ":8081", Handler: &Handler{}} + err := server.ListenAndServe() + if err != nil { + fmt.Println(err.Error()) + } +} + +``` \ No newline at end of file diff --git a/note/Go/结构体.md b/note/Go/结构体.md new file mode 100644 index 0000000..85ea9f3 --- /dev/null +++ b/note/Go/结构体.md @@ -0,0 +1,201 @@ +### 自定义新类型 +- 用`type`定义新类型 + - 基于已有类型定义新类型 `type NewType int` + - 字面值来定义新类型,多用于自定义一个新的复合类型 + - `type M map[int]string` + - `type S []string` + - 支持使用`type`块进行批量定义 + ```go + type ( + T1 int + T2 T1 + T3 string + ) + ``` + +- 类型别名,通常用在项目的渐进式重构,还有对已有包的二次封装方面 + - `type MyInt = int` + - 新类型与原类型完全等价 + ```go + + type T = string + + var s string = "hello" + var t T = s // ok + fmt.Printf("%T\n", t) // string + ``` + +### 结构体类型定义 +- 结构体的类型字面值由若干个字段(field)聚合而成,每个字段有自己的名字与类型 +- 每个结构体的field name 都是唯一的 +- 如果结构体类型只在它定义的包内使用,可以将类型名的首字母小写 +- 如果不想将结构体类型中的某个字段暴露给其他包,可以把这个字段名字的首字母小写 +- 可以用空标识符“_”作为结构体类型定义中的字段名称,不能被外部包引用,也无法被结构体所在的包使用 +```go +type T struct { + Field1 T1 + Field2 T2 + ... ... + FieldN Tn +} +``` + +### 空结构体 +- `type Empty struct{}` +- Empty是一个不包含任何字段的空结构体类型 +- 空结构体类型变量的内存占用为 0 +- 基于空结构体类型内存零开销这样的特性, Go 开发中会经常使用空结构体类型元素,作为一种“事件”信息进行 Goroutine 之间的通信 +```go + +var c = make(chan Empty) // 声明一个元素类型为Empty的channel +c<-Empty{} // 向channel写入一个“事件” +``` + +### 结构体嵌套 +- 其他结构体作为自定义结构体中字段的类型 +- 可以只引入结构体类型,而不命名 +- 不支持在结构体类型定义中,递归的放入自身类型字段的定义方式 +- 不可在自身中出现自身的字段,但是可以拥有自身的指针类型、以自身类型为元素的切片类型和以自身类型作为value的map类型 +- 一个类型,它所占用的大小是固定的,因此一个结构体定义好的时候,其大小是固定的。但是,如果结构体里面套结构体,那么在计算该结构体占用大小的时候,就会成死循环 +- 但如果是指针、切片、map等类型,其本质都是一个int大小(指针,4字节或者8字节,与操作系统有关),因此该结构体的大小是固定的,类型就能决定占用内存大小 +```go + +type Person struct { + Name string + Phone string + Addr string +} + +type Book struct { + Title string + Author Person + ... ... +} + +type BookV2 struct { + Title string + Person + ... ... +} + + +type T struct { + t *T // ok + st []T // ok + m map[string]T // ok +} +``` + +### 初始化 +- 零值初始化 + +```go + +type Book struct { + ... +} + +var book Book +var book = Book{} // 标准变量声明 +book := Book{} // 短变量声明 +``` +- 复合字面值 + - 按顺序一次给每个结构体字段进行赋值,结构体字段较少,且没有非导出字段 + ```go + + type Book struct { + Title string // 书名 + Pages int // 书的页数 + Indexes map[string]int // 书的索引 + } + + var book = Book{"The Go Programming Language", 700, make(map[string]int)} + ``` + - field:value”形式的复合字面值 + +- 使用构造函数初始化结构体 +```go + +// $GOROOT/src/time/sleep.go +func NewTimer(d Duration) *Timer { + c := make(chan Time, 1) + t := &Timer{ + C: c, + r: runtimeTimer{ + when: when(d), + f: sendTime, + arg: c, + }, + } + startTimer(&t.r) + return t +} +``` + + +### 结构体类型的内存布局 +- 将结构体字段平铺的形式,存放在一个连续内存块中,理想情况 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/struct_ram.jpg) +- 现实实际存储,需要在字段之间添加padding,为了内存对齐 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/struct_padding.jpg) +- 使用`unsafe.Sizeof(t)` 获取结构体占用空间大小 +- 使用`unsafe.Offsetof(t.Fn)` 获取字段Fn在内存中相对于t起始地址的偏移量 + +#### 内存对齐 +- 出于对处理器存取数据效率的考虑 +- 对于各种基本数据类型来说,它的变量的内存地址值必须是其类型本身大小的整数倍,比如,一个 int64 类型的变量的内存地址,应该能被 int64 类型自身的大小,也就是 8 整除;一个 uint16 类型的变量的内存地址,应该能被 uint16 类型自身的大小,也就是 2 整除 +- 对于结构体而言,它的变量的内存地址,只要是它最长字段长度与系统对齐系数两者之间较小的那个的整数倍就可以。但对于结构体类型来说,还要让它每个字段的内存地址都严格满足内存对齐要求 +- 可以主动填充结构体,内存对齐 + +#### 举例 +- 64bit平台系统对齐系数是8 +```go +type T struct { + b byte // 1字节,对齐需要填充7字节 + + i int64 // 8字节,对齐不需要填充 + u uint16 // 2字节,对齐需要填充6字节 +} +``` +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/neicunduiqi.jpg) + +### 内存对齐 +- CPU 访问内存时,并不是逐个字节访问,而是以字长(word size)为单位访问 +- 32位的CPU,字长为4字节CPU访问内存的单位是4字节 +- 64为的CPU,字长为8字节CPU访问内存的单位是8字节 +- 为了减少 CPU 访问内存的次数,加大 CPU 访问内存的吞吐量。同样读取 8 个字节的数据,一次读取 4 个字节只需要读取 2 次 +- CPU 始终以字长访问内存,如果不进行内存对齐,很可能增加 CPU 访问内存的次数 +- 内存对齐对实现变量的原子性操作也有好处,每次内存访问是原子的,如果变量的大小不超过字长,那么内存对齐后,对该变量的访问就是原子的 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/neicunduiqijuli.png) + +#### 内存对齐倍数 +- unsafe.Alignof(变量) +- 表示内存占用的量必须是结果的倍数 + +#### Go内存对齐倍数 +- 对于任意类型的变量 x ,`unsafe.Alignof(x)` 至少为 1。 +- 对于 struct 结构体类型的变量 x,计算 x 每一个字段 f 的 `unsafe.Alignof(x.f)`,`unsafe.Alignof(x)` 等于其中的最大值。 +- 对于 array 数组类型的变量 x,`unsafe.Alignof(x)` 等于构成数组的元素类型的对齐倍数 + +#### 空struct内存对齐 +- 空 struct{} 大小为 0,作为其他 struct 的字段时,一般不需要内存对齐 +- 有一种情况除外:即当 struct{} 作为结构体最后一个字段时,需要内存对齐。因为如果有指针指向该字段, 返回的地址将在结构体之外 +- 如果此指针一直存活不释放对应的内存,就会有内存泄露的问题(该内存不因结构体释放而释放) +- 当 struct{} 作为其他 struct 最后一个字段时,需要填充额外的内存保证安全 +```go +type demo3 struct { + c int32 + a struct{} +} + +type demo4 struct { + a struct{} + c int32 +} + +func main() { + fmt.Println(unsafe.Sizeof(demo3{})) // 8 + fmt.Println(unsafe.Sizeof(demo4{})) // 4 +} +``` + diff --git a/note/Go/结构体类型嵌入.md b/note/Go/结构体类型嵌入.md new file mode 100644 index 0000000..60af35d --- /dev/null +++ b/note/Go/结构体类型嵌入.md @@ -0,0 +1,140 @@ +### 多个嵌入方式 +- 接口嵌入 +- 结构体嵌入 + +### 嵌入总结 +- 结构体类型的方法集合包含嵌入的接口类型的方法集合 +- 当结构体类型 T 包含嵌入字段 E 时,*T 的方法集合不仅包含类型 E 的方法集合,还要包含类型 *E 的方法集合 +- 基于非接口类型的 defined 类型创建的新 defined 类型不会继承原类型的方法集合 +- 通过类型别名定义的新类型则和原类型拥有相同的方法集合 + +### 接口嵌入 +- 接口就是一个方法集合 +- 新接口类型(如接口类型 I)将嵌入的接口类型(如接口类型 E)的方法集合,并入到自己的方法集合中 +- 在 Go 1.14 版本之前是有约束的:如果新接口类型嵌入了多个接口类型,这些嵌入的接口类型的方法集合不能有交集,同时嵌入的接口类型的方法集合中的方法名字,也不能与新接口中的其他方法同名 +```go +type I interface { + M1() + M2() + M3() +} + + +type I interface { + E + M3() +} +``` +- 以某个类型名、类型的指针类型名或接口类型名,直接作为结构体字段的方式就叫做结构体的类型嵌入,这些字段也被叫做嵌入字段(Embedded Field) +- 结构体可以嵌入结构体和接口类型 +- 嵌入字段类型的底层类型不能为指针类型 +- 嵌入字段的名字在结构体定义也必须是唯一的,如果两个类型的名字相同,它们无法同时作为嵌入字段放到同一个结构体定义中 +### 结构体嵌入 +```go +type T1 int +type t2 struct{ + n int + m int +} + +type I interface { + M1() +} + +type S1 struct { + T1 + *t2 + I + a int + b string +} +``` + +### 嵌入后方法集合 +- 结构体类型的方法集合,包含嵌入的接口类型的方法集合 +```go + +type I interface { + M1() + M2() +} + +type T struct { + I +} + +func (T) M3() {} + +func main() { + var t T + var p *T + dumpMethodSet(t) + dumpMethodSet(p) +} + +main.T's method set: +- M1 +- M2 +- M3 + +*main.T's method set: +- M1 +- M2 +- M3 +``` + +- 嵌入了其他类型的结构体类型本身是一个代理 +- 调用其实例所代理的方法时,Go 会首先查看结构体自身是否实现了该方法 +- 如果实现方法了,Go 就会优先使用结构体自己实现的方法 +- 如果没有实现,那么 Go 就会查找结构体中的嵌入字段的方法集合中,是否包含了这个方法 +- 如果多个嵌入字段的方法集合中都包含这个方法,那么方法集合存在交集。Go 编译器就会因无法确定究竟使用哪个方法而报错 +- 去掉因嵌入造成的交集的方法或者结构体自身实现交集的方法 +```go + type E1 interface { + M1() + M2() + M3() + } + + type E2 interface { + M1() + M2() + M4() + } + + type T struct { + E1 + E2 + } + + func main() { + t := T{} + t.M1() + t.M2() + } + ``` + + ### 打印一个interface的方法集合 + ```go + func dumpMethodSet(i interface{}) { + dynTyp := reflect.TypeOf(i) + + if dynTyp == nil { + fmt.Printf("there is no dynamic type\n") + return + } + + n := dynTyp.NumMethod() + if n == 0 { + fmt.Printf("%s's method set is empty!\n", dynTyp) + return + } + + fmt.Printf("%s's method set:\n", dynTyp) + for j := 0; j < n; j++ { + fmt.Println("-", dynTyp.Method(j).Name) + } + fmt.Printf("\n") +} + +``` diff --git a/note/Go/通信并发.md b/note/Go/通信并发.md new file mode 100644 index 0000000..1907bd6 --- /dev/null +++ b/note/Go/通信并发.md @@ -0,0 +1,34 @@ +### 并发&&并行 +- 并发不是并行 +- 并发是应用结构设计相关的概念,而并行只是程序执行期的概念 +- 并行的必要条件是具有多个处理器或多核处理器,否则无论是否是并发的设计,程序执行时都有且仅有一个任务可以被调度到处理器上执行 + +### Go并发方案 +- Go 的并发方案:goroutine +- Go 并没有使用操作系统线程作为承载分解后的代码片段(模块)的基本执行单元 +- 实现了goroutine这一由 Go 运行时(runtime)负责调度的、轻量的用户级线程,为并发程序设计提供原生支持 +- 无论是 Go 自身运行时代码还是用户层 Go 代码,都无一例外地运行在 goroutine 中 + +### goroutine优势 +- 资源占用小,每个 goroutine 的初始栈大小仅为 2k +- 由 Go 运行时而不是操作系统调度,goroutine 上下文切换在用户层完成,开销更小 +- 在语言层面而不是通过标准库提供。goroutine 由go关键字创建,一退出就会被回收或销毁,开发体验更佳 +- 语言内置 channel 作为 goroutine 间通信原语,为并发设计提供了强大支撑 + +### goroutine使用 +#### 创建goroutine +- Go 语言通过go关键字+函数/方法的方式创建一个 goroutine +- Go 也可以基于匿名函数 / 闭包创建 goroutine +- 创建 goroutine 后,go 关键字不会返回 goroutine id 之类的唯一标识 goroutine 的 id +- 一个应用内部启动的所有 goroutine 共享进程空间的资源,如果多个 goroutine 访问同一块内存数据,将会存在竞争,需要进行 goroutine 间的同步 +- 创建后,新 goroutine 将拥有独立的代码执行流,并与创建它的 goroutine 一起被 Go 运行时调度 + +#### 退出goroutine +- goroutine 的执行函数的返回,就意味着 goroutine 退出,goroutine 执行的函数或方法即便有返回值,Go 也会忽略这些返回值,如果需要返回值,通过 goroutine 间的通信来实现 +- main goroutine 退出,那么也意味着整个应用程序的退出,其他的goroutine也就都退出 + +#### goroutine间的通信 +> 操作系统线程间的通信方式:共享内存、信号(signal)、管道(pipe)、消息队列、套接字(socket),基于对内存的共享的 + +- channel 符合`CSP(Communicationing Sequential Processes,通信顺序进程)`模型的通信方式,通过使用 channel 将goroutine组合在一起 +- Go 也支持传统的、基于共享内存的并发模型,并提供了基本的低级别同步原语(主要是 sync 包中的互斥锁、条件变量、读写锁、原子操作等) \ No newline at end of file diff --git a/note/Go/通道channel.md b/note/Go/通道channel.md new file mode 100644 index 0000000..00435f9 --- /dev/null +++ b/note/Go/通道channel.md @@ -0,0 +1,736 @@ +### Channel +- 使用`make(chan eleType, cap)`创建 +- 底层是`runtime.hchan`类型的指针 + +### 有缓冲channel和无缓冲channel +- 无缓冲 + - 发送数据的时候,如果没有对应的接收者ready,那么发送者就进入到等待发送队列中,等待有对应的接收者唤醒它 + - 接收数据的时候,如果没有对应的发送者ready,那么接收者就进入到等待接收队列中,等待有对应的发送者唤醒它 +- 有缓冲 + - 对于发送者来说:只要缓冲区未满,发送者就可以继续发送数据存放在缓冲区。一旦缓冲区满了,发送者就只能进入到等待发送队列中,等待有对应的接收者唤醒它,然后它再把数据放入到刚刚被取走数据的位置 + + - 对于接收者来说:只要缓冲区不为空,接收者就可以继续接收数据。一旦缓冲区空了,那么接收者就只能进入到等待接收队列中,等待有对应的发送者唤醒它 + +### 读取写入channel几种情况 +#### 定义channel,但是不进行初始化 +- `var ch chan string`定义的channel为 `nil channel` 导致 `fatal error` +- 阻塞模式读取或写入到chan,才会崩溃 +- `select`中向`nil channel`写入或读取不会崩溃,因为select是非阻塞模式 + ```go + // select 发送 + func selectnbsend(c *hchan, elem unsafe.Pointer) (selected bool) { + return chansend(c, elem, false, getcallerpc()) + } + + // select 接收 + func selectnbrecv(elem unsafe.Pointer, c *hchan) (selected, received bool) { + return chanrecv(c, elem, false) + } + + func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { + // nilchannel 非阻塞写入不会崩溃 + if c == nil { + if !block { + return false + } + gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) + throw("unreachable") + } + + if debugChan { + print("chansend: chan=", c, "\n") + } + // ...省略代码 + } + + func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { + // raceenabled: don't need to check ep, as it is always on the stack + // or is new memory allocated by reflect. + + if debugChan { + print("chanrecv: chan=", c, "\n") + } + + if c == nil { + if !block { + return + } + gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) + throw("unreachable") + } + // ...省略代码 + } + ``` +```go +func main() { + wg := sync.WaitGroup{} + var ch chan string + + write := func() { + fmt.Println("writing") + s := "t" + ch <- s + fmt.Println("write:", s) + wg.Done() + } + write() +} +// true +// writing +// fatal error: all goroutines are asleep - deadlock! +``` + +#### 定义channel,make初始化 +- 正常读取,不会崩溃 +- 读取如果处理不当会阻塞 + +```go +func main() { + wg := sync.WaitGroup{} + var ch chan string = make(chan string) + + read := func() { + fmt.Println("reading") + s := <-ch + fmt.Println("read:", s) + wg.Done() + } + + write := func() { + fmt.Println("writing") + s := "t" + ch <- s + fmt.Println("write:", s) + wg.Done() + } + + wg.Add(2) + go read() + go write() + + fmt.Println("waiting") + wg.Wait() + +} +// waiting +// reading +// writing +// write: t +// read: t +``` + +#### 读取已经关闭的channel +- channel 关闭前正常读取 +- channel 关闭后读取到chan元素类型的零值,并且第二个comma返回值返回false +```go +func main() { + wg := sync.WaitGroup{} + var ch = make(chan string, 5) + + read := func() { + for { + fmt.Println("reading") + s, ok := <-ch + fmt.Println("read:", len(s), s, ok) + time.Sleep(time.Second) + } + wg.Done() + } + + write := func() { + for i := 0; i < 5; i++ { + fmt.Println("writing") + s := "t" + ch <- s + fmt.Println("write:", s) + } + wg.Done() + close(ch) + fmt.Println("closed") + } + + wg.Add(2) + write() + go read() + + fmt.Println("waiting") + wg.Wait() + fmt.Println("finish") +} + +//reading +//read: 1 t true +//reading +//read: 0 false +//reading +//read: 0 false +``` + +#### 写入已经关闭的channel +- panic: send on closed channel +```go +func main() { + ch := make(chan string) + close(ch) + ch <- "1" +} +//panic: send on closed channel +``` +### Channel 结构 hchan + +```go +type hchan struct { + // channel中当前元素个数 + qcount uint // total data in the queue + // 队列容量 + dataqsiz uint // size of the circular queue + // 队列元素缓存区,数组指针 + buf unsafe.Pointer // points to an array of dataqsiz elements + // 每个元素大小 + elemsize uint16 + // channel是否关闭 + closed uint32 + // 元素类型 + elemtype *_type // element type + // 发送位置索引 + sendx uint // send index + // 接收位置索引 + recvx uint // receive index + // 等待接收阻塞的goroutine + recvq waitq // list of recv waiters + // 等待发送阻塞的goroutine + sendq waitq // list of send waiters + + // lock protects all fields in hchan, as well as several + // fields in sudogs blocked on this channel. + // + // Do not change another G's status while holding this lock + // (in particular, do not ready a G), as this can deadlock + // with stack shrinking. + lock mutex +} +``` + +### runtime.makechan +- 必要的边界检查,内存溢出,容量为负数 +- 如果创建一个无缓冲channel ,只需要为runtime.hchan本身分配一段内存空间 +- 如果创建的缓冲channel存储的类型不是指针类型,会为当前channel和存储类型元素的缓冲区,分配一块连续的内存空间 +- 默认情况下(缓冲channel存储类型包含指针),会单独为runtime.hchan和缓冲区分配内存 + +```go +func makechan(t *chantype, size int) *hchan { + elem := t.elem + + // compiler checks this but be safe. + if elem.size >= 1<<16 { + throw("makechan: invalid channel element type") + } + if hchanSize%maxAlign != 0 || elem.align > maxAlign { + throw("makechan: bad alignment") + } + + mem, overflow := math.MulUintptr(elem.size, uintptr(size)) + if overflow || mem > maxAlloc-hchanSize || size < 0 { + panic(plainError("makechan: size out of range")) + } + + + // Hchan does not contain pointers interesting for GC when elements stored in buf do not contain pointers. + // buf points into the same allocation, elemtype is persistent. + // SudoG's are referenced from their owning thread so they can't be collected. + // TODO(dvyukov,rlh): Rethink when collector can move allocated objects. + var c *hchan + switch { + case mem == 0: + // Queue or element size is zero. + // 无缓冲队列或者元素占用大小为0 + c = (*hchan)(mallocgc(hchanSize, nil, true)) + // Race detector uses this location for synchronization. + c.buf = c.raceaddr() + case elem.ptrdata == 0: + // Elements do not contain pointers. + // Allocate hchan and buf in one call. + // 存储的元素不包含指针,分配hchan和buf在一块内存区域 + c = (*hchan)(mallocgc(hchanSize+mem, nil, true)) + + // maxAlign = 8 + // hchanSize = unsafe.Sizeof(hchan{}) + uintptr(-int(unsafe.Sizeof(hchan{}))&(maxAlign-1)) + c.buf = add(unsafe.Pointer(c), hchanSize) + default: + // Elements contain pointers. + // 独立分配hchan和buff内存 + c = new(hchan) + c.buf = mallocgc(mem, elem, true) + } + + c.elemsize = uint16(elem.size) + c.elemtype = elem + c.dataqsiz = uint(size) + lockInit(&c.lock, lockRankHchan) + + if debugChan { + print("makechan: chan=", c, "; elemsize=", elem.size, "; dataqsiz=", size, "\n") + } + return c +} +``` + + + +### waitq && sudog +```go + +type waitq struct { + first *sudog + last *sudog +} + +// sudog represents a g in a wait list, such as for sending/receiving +// on a channel. +// +// sudog is necessary because the g ↔ synchronization object relation +// is many-to-many. A g can be on many wait lists, so there may be +// many sudogs for one g; and many gs may be waiting on the same +// synchronization object, so there may be many sudogs for one object. +// +// sudogs are allocated from a special pool. Use acquireSudog and +// releaseSudog to allocate and free them. +type sudog struct { + // The following fields are protected by the hchan.lock of the + // channel this sudog is blocking on. shrinkstack depends on + // this for sudogs involved in channel ops. + + g *g + + next *sudog + prev *sudog + elem unsafe.Pointer // data element (may point to stack) + + // The following fields are never accessed concurrently. + // For channels, waitlink is only accessed by g. + // For semaphores, all fields (including the ones above) + // are only accessed when holding a semaRoot lock. + + acquiretime int64 + releasetime int64 + ticket uint32 + + // isSelect indicates g is participating in a select, so + // g.selectDone must be CAS'd to win the wake-up race. + isSelect bool + + // success indicates whether communication over channel c + // succeeded. It is true if the goroutine was awoken because a + // value was delivered over channel c, and false if awoken + // because c was closed. + success bool + + parent *sudog // semaRoot binary tree + waitlink *sudog // g.waiting list or semaRoot + waittail *sudog // semaRoot + c *hchan // channel +} + +``` +### 向channel发送数据 runtime.chansend +- c channel对象的指针 +- ep 具体要发送的元素 +- block true阻塞模式,false 非阻塞模式 +- callerpc 调用者的调用者的程序计数器 (PC) + +```go +func full(c *hchan) bool { + // c.dataqsiz is immutable (never written after the channel is created) + // so it is safe to read at any time during channel operation. + // 创建之后就不会边,0是一个无缓冲channel + // 没有接收的,返回true + if c.dataqsiz == 0 { + // Assumes that a pointer read is relaxed-atomic. + return c.recvq.first == nil + } + // Assumes that a uint read is relaxed-atomic. + // 有缓冲channel + return c.qcount == c.dataqsiz +} + +func chansend(c *hchan, ep unsafe.Pointer, block bool, callerpc uintptr) bool { + // nil channel 判断 + // 阻塞模式,阻塞goroutine,然后崩溃 + // 非阻塞模式 比如select case情况下,返回false + if c == nil { + if !block { + return false + } + gopark(nil, nil, waitReasonChanSendNilChan, traceEvGoStop, 2) + throw("unreachable") + } + + // 调试日志 + if debugChan { + print("chansend: chan=", c, "\n") + } + + // go build -race 才会用到 + if raceenabled { + racereadpc(c.raceaddr(), callerpc, funcPC(chansend)) + } + + // 非阻塞模式,然后channel没有关闭,并且channel满的情况下返回false + if !block && c.closed == 0 && full(c) { + return false + } + + var t0 int64 + if blockprofilerate > 0 { + t0 = cputicks() + } + + // 上锁 + lock(&c.lock) + + // 关闭的channel 报错 + if c.closed != 0 { + unlock(&c.lock) + panic(plainError("send on closed channel")) + } + + // 取出第一个接收者, + if sg := c.recvq.dequeue(); sg != nil { + // Found a waiting receiver. We pass the value we want to send + // directly to the receiver, bypassing the channel buffer (if any). + send(c, sg, ep, func() { unlock(&c.lock) }, 3) + return true + } + + // 有缓冲通道 + if c.qcount < c.dataqsiz { + // Space is available in the channel buffer. Enqueue the element to send. + qp := chanbuf(c, c.sendx) + if raceenabled { + racenotify(c, c.sendx, nil) + } + typedmemmove(c.elemtype, qp, ep) + c.sendx++ + // 循环写入 + if c.sendx == c.dataqsiz { + c.sendx = 0 + } + c.qcount++ + unlock(&c.lock) + return true + } + + // 无缓冲channel select 也不会阻塞 + if !block { + unlock(&c.lock) + return false + } + // 当前发送的g,封装成sudog + // Block on the channel. Some receiver will complete our operation for us. + gp := getg() + mysg := acquireSudog() + mysg.releasetime = 0 + if t0 != 0 { + mysg.releasetime = -1 + } + // No stack splits between assigning elem and enqueuing mysg + // on gp.waiting where copystack can find it. + mysg.elem = ep + mysg.waitlink = nil + mysg.g = gp + mysg.isSelect = false + mysg.c = c + gp.waiting = mysg + gp.param = nil + + // sudog入队 + c.sendq.enqueue(mysg) + // Signal to anyone trying to shrink our stack that we're about + // to park on a channel. The window between when this G's status + // changes and when we set gp.activeStackChans is not safe for + // stack shrinking. + + // 原子标记goroutine 因为chansend 或者chanrecv阻塞 + atomic.Store8(&gp.parkingOnChan, 1) + + // 挂起当前G + gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanSend, traceEvGoBlockSend, 2) + // Ensure the value being sent is kept alive until the + // receiver copies it out. The sudog has a pointer to the + // stack object, but sudogs aren't considered as roots of the + // stack tracer. + + // 保持当前ep 不会被GC回收,也就是发送的元素不会被回收 + KeepAlive(ep) + + // someone woke us up. + // 被唤醒后才会使用 + if mysg != gp.waiting { + throw("G waiting list is corrupted") + } + gp.waiting = nil + gp.activeStackChans = false + closed := !mysg.success + gp.param = nil + if mysg.releasetime > 0 { + blockevent(mysg.releasetime-t0, 2) + } + mysg.c = nil + releaseSudog(mysg) + if closed { + if c.closed == 0 { + throw("chansend: spurious wakeup") + } + // 关闭的channel 写入 panic + panic(plainError("send on closed channel")) + } + return true +} +``` + + + + +### 从channel接收数据 runtime.chanrecv +- c 读取的channel +- ep 要读取出的内容存的地址 +- block 阻塞模式 +- nil channel 非阻塞模式不会报错,会读不到return,阻塞模式读nil channel 崩溃 +- 非阻塞模式读取关闭的channel +```go + +// empty reports whether a read from c would block (that is, the channel is +// empty). It uses a single atomic read of mutable state. +func empty(c *hchan) bool { + // c.dataqsiz is immutable. + if c.dataqsiz == 0 { + return atomic.Loadp(unsafe.Pointer(&c.sendq.first)) == nil + } + return atomic.Loaduint(&c.qcount) == 0 +} + +// chanrecv receives on channel c and writes the received data to ep. +// ep may be nil, in which case received data is ignored. +// If block == false and no elements are available, returns (false, false). +// Otherwise, if c is closed, zeros *ep and returns (true, false). +// Otherwise, fills in *ep with an element and returns (true, true). +// A non-nil ep must point to the heap or the caller's stack. +func chanrecv(c *hchan, ep unsafe.Pointer, block bool) (selected, received bool) { + // raceenabled: don't need to check ep, as it is always on the stack + // or is new memory allocated by reflect. + + if debugChan { + print("chanrecv: chan=", c, "\n") + } + // nil channel 判断 + if c == nil { + if !block { + return + } + gopark(nil, nil, waitReasonChanReceiveNilChan, traceEvGoStop, 2) + throw("unreachable") + } + + // Fast path: check for failed non-blocking operation without acquiring the lock. + if !block && empty(c) { + // After observing that the channel is not ready for receiving, we observe whether the + // channel is closed. + // + // Reordering of these checks could lead to incorrect behavior when racing with a close. + // For example, if the channel was open and not empty, was closed, and then drained, + // reordered reads could incorrectly indicate "open and empty". To prevent reordering, + // we use atomic loads for both checks, and rely on emptying and closing to happen in + // separate critical sections under the same lock. This assumption fails when closing + // an unbuffered channel with a blocked send, but that is an error condition anyway. + if atomic.Load(&c.closed) == 0 { + // Because a channel cannot be reopened, the later observation of the channel + // being not closed implies that it was also not closed at the moment of the + // first observation. We behave as if we observed the channel at that moment + // and report that the receive cannot proceed. + return + } + // The channel is irreversibly closed. Re-check whether the channel has any pending data + // to receive, which could have arrived between the empty and closed checks above. + // Sequential consistency is also required here, when racing with such a send. + if empty(c) { + // The channel is irreversibly closed and empty. + if raceenabled { + raceacquire(c.raceaddr()) + } + // value := <- ch 读取方式 + if ep != nil { + typedmemclr(c.elemtype, ep) + } + return true, false + } + } + + var t0 int64 + if blockprofilerate > 0 { + t0 = cputicks() + } + + lock(&c.lock) + + // value := <- ch 读取方式 + // channel 已经关闭,没有未读出的元素 + if c.closed != 0 && c.qcount == 0 { + if raceenabled { + raceacquire(c.raceaddr()) + } + unlock(&c.lock) + if ep != nil { + typedmemclr(c.elemtype, ep) + } + return true, false + } + + // 有阻塞的send goroutine + if sg := c.sendq.dequeue(); sg != nil { + // Found a waiting sender. If buffer is size 0, receive value + // directly from sender. Otherwise, receive from head of queue + // and add sender's value to the tail of the queue (both map to + // the same buffer slot because the queue is full). + recv(c, sg, ep, func() { unlock(&c.lock) }, 3) + return true, true + } + + // 有缓冲 + if c.qcount > 0 { + // Receive directly from queue + qp := chanbuf(c, c.recvx) + if raceenabled { + racenotify(c, c.recvx, nil) + } + if ep != nil { + typedmemmove(c.elemtype, ep, qp) + } + typedmemclr(c.elemtype, qp) + c.recvx++ + if c.recvx == c.dataqsiz { + c.recvx = 0 + } + c.qcount-- + unlock(&c.lock) + return true, true + } + + if !block { + unlock(&c.lock) + return false, false + } + + // 阻塞情况下封装G,存到recvq + // no sender available: block on this channel. + gp := getg() + mysg := acquireSudog() + mysg.releasetime = 0 + if t0 != 0 { + mysg.releasetime = -1 + } + // No stack splits between assigning elem and enqueuing mysg + // on gp.waiting where copystack can find it. + mysg.elem = ep + mysg.waitlink = nil + gp.waiting = mysg + mysg.g = gp + mysg.isSelect = false + mysg.c = c + gp.param = nil + c.recvq.enqueue(mysg) + // Signal to anyone trying to shrink our stack that we're about + // to park on a channel. The window between when this G's status + // changes and when we set gp.activeStackChans is not safe for + // stack shrinking. + atomic.Store8(&gp.parkingOnChan, 1) + + //挂起goroutine + gopark(chanparkcommit, unsafe.Pointer(&c.lock), waitReasonChanReceive, traceEvGoBlockRecv, 2) + + // someone woke us up + if mysg != gp.waiting { + throw("G waiting list is corrupted") + } + gp.waiting = nil + gp.activeStackChans = false + if mysg.releasetime > 0 { + blockevent(mysg.releasetime-t0, 2) + } + success := mysg.success + gp.param = nil + mysg.c = nil + releaseSudog(mysg) + return true, success +} +``` + +### 有缓存channel用做计数器,用作计数信号量(counting semaphore),可以用来控制活动goroutine数量 +```go + +var active = make(chan struct{}, 3) +var jobs = make(chan int, 10) + +func main() { + go func() { + for i := 0; i < 8; i++ { + jobs <- (i + 1) + } + close(jobs) + }() + + var wg sync.WaitGroup + + for j := range jobs { + wg.Add(1) + go func(j int) { + active <- struct{}{} + log.Printf("handle job: %d\n", j) + time.Sleep(2 * time.Second) + <-active + wg.Done() + }(j) + } + wg.Wait() +} +``` + +### nil channel配合 select使用 才不会崩溃 +- channel 关闭后读取会读取到零值 +- 用select可以将关闭后的channel变成nil channel,而不是每次都读到零值 +```go +func main() { + ch1, ch2 := make(chan int), make(chan int) + go func() { + time.Sleep(time.Second * 5) + ch1 <- 5 + close(ch1) + }() + + go func() { + time.Sleep(time.Second * 7) + ch2 <- 7 + close(ch2) + }() + + for { + select { + case x, ok := <-ch1: + if !ok { + ch1 = nil + } else { + fmt.Println(x) + } + case x, ok := <-ch2: + if !ok { + ch2 = nil + } else { + fmt.Println(x) + } + } + if ch1 == nil && ch2 == nil { + break + } + } + fmt.Println("program end") +} +``` \ No newline at end of file diff --git a/note/Go/限流.md b/note/Go/限流.md new file mode 100644 index 0000000..b2ec7bc --- /dev/null +++ b/note/Go/限流.md @@ -0,0 +1,472 @@ +> 笔记来自微信公众号 + +> 常用限流算法的应用场景和实现原理 + +> 原创 KevinYan11 网管叨bi叨 2020-12-20 10:28 + +> [常用限流算法的应用场景和实现原理](https://mp.weixin.qq.com/s?__biz=MzUzNTY5MzU2MA==&mid=2247486937&idx=1&sn=d4ea6ebb38c52e8004e73f235bde9848&scene=21#wechat_redirect) + +### 场景 +- 在高并发业务场景下,保护系统时,常用的"三板斧"有:"熔断、降级和限流"。 +- 业务代码中的逻辑限流 + +### 常见限流算法 +- 计数器 +- 滑动敞口 +- 漏桶 +- 令牌桶 + +### 计数器 +- 计数器是一种比较简单粗暴的限流算法,思想是在固定时间窗口内对请求进行计数,与阀值进行比较判断是否需要限流 +- 一旦到了时间临界点,将计数器清零 + +#### 缺陷 +- 可以在清零的前一秒和后一秒,两秒内发送阈值乘2的请求 +- 一旦请求数量瞬间变多,还是会有崩溃的风险 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/count.png) + +#### 简单代码实现 +```go +type LimitRate struct { + rate int //阀值 + begin time.Time //计数开始时间 + cycle time.Duration //计数周期 + count int //收到的请求数 + lock sync.Mutex //锁 +} + +func (limit *LimitRate) Allow() bool { + limit.lock.Lock() + defer limit.lock.Unlock() + + // 判断收到请求数是否达到阀值 + if limit.count == limit.rate-1 { + now := time.Now() + // 达到阀值后,判断是否是请求周期内 + if now.Sub(limit.begin) >= limit.cycle { + limit.Reset(now) + return true + } + return false + } else { + limit.count++ + return true + } +} + +func (limit *LimitRate) Set(rate int, cycle time.Duration) { + limit.rate = rate + limit.begin = time.Now() + limit.cycle = cycle + limit.count = 0 +} + +func (limit *LimitRate) Reset(begin time.Time) { + limit.begin = begin + limit.count = 0 +} +``` + +### 滑动窗口 +- 滑动窗口算法将一个大的时间窗口分成多个小窗口,每次大窗口向后滑动一个小窗口,并保证大的窗口内流量不会超出最大值 +- 比固定窗口的流量曲线更加平滑 +- 滑动时间窗口,我们可以把1s的时间窗口划分成10个小窗口,或者想象窗口有10个时间插槽time slot, 每个time slot统计某个100ms的请求数量。每经过100ms,有一个新的time slot加入窗口,早于当前时间1s的time slot出窗口 +- 窗口内最多维护10个time slot + +#### 缺陷 +- 滑动窗口算法是固定窗口的一种改进,但从根本上并没有真正解决固定窗口算法的临界突发流量问题 + +#### 代码实现 +```go +package main + +import ( + "fmt" + "sync" + "time" +) + +type timeSlot struct { + timestamp time.Time // 这个timeSlot的时间起点 + count int // 落在这个timeSlot内的请求数 +} + +// 统计整个时间窗口中已经发生的请求次数 +func countReq(win []*timeSlot) int { + var count int + for _, ts := range win { + count += ts.count + } + return count +} + +type SlidingWindowLimiter struct { + mu sync.Mutex // 互斥锁保护其他字段 + SlotDuration time.Duration // time slot的长度 + WinDuration time.Duration // sliding window的长度 + numSlots int // window内最多有多少个slot + windows []*timeSlot + maxReq int // 大窗口时间内允许的最大请求数 +} + +func NewSliding(slotDuration time.Duration, winDuration time.Duration, maxReq int) *SlidingWindowLimiter { + return &SlidingWindowLimiter{ + SlotDuration: slotDuration, + WinDuration: winDuration, + numSlots: int(winDuration / slotDuration), + maxReq: maxReq, + } +} + +func (l *SlidingWindowLimiter) validate() bool { + l.mu.Lock() + defer l.mu.Unlock() + + now := time.Now() + // 已经过期的time slot移出时间窗 + timeoutOffset := -1 + for i, ts := range l.windows { + if ts.timestamp.Add(l.WinDuration).After(now) { + break + } + timeoutOffset = i + } + if timeoutOffset > -1 { + l.windows = l.windows[timeoutOffset+1:] + } + + // 判断请求是否超限 + var result bool + if countReq(l.windows) < l.maxReq { + result = true + } + + // 记录这次的请求数 + var lastSlot *timeSlot + if len(l.windows) > 0 { + lastSlot = l.windows[len(l.windows)-1] + if lastSlot.timestamp.Add(l.SlotDuration).Before(now) { + // 如果当前时间已经超过这个时间插槽的跨度,那么新建一个时间插槽 + lastSlot = &timeSlot{timestamp: now, count: 1} + l.windows = append(l.windows, lastSlot) + } else { + lastSlot.count++ + } + } else { + lastSlot = &timeSlot{timestamp: now, count: 1} + l.windows = append(l.windows, lastSlot) + } + + return result +} + +func (l *SlidingWindowLimiter) LimitTest() string { + if l.validate() { + return "Accepted" + } else { + return "Ignored" + } +} + +func main() { + limiter := NewSliding(100*time.Millisecond, time.Second, 10) + for i := 0; i < 5; i++ { + fmt.Println(limiter.LimitTest()) + } + time.Sleep(100 * time.Millisecond) + for i := 0; i < 5; i++ { + fmt.Println(limiter.LimitTest()) + } + fmt.Println(limiter.LimitTest()) + for _, v := range limiter.windows { + fmt.Println(v.timestamp, v.count) + } + + fmt.Println("moments later...") + time.Sleep(time.Second) + for i := 0; i < 7; i++ { + fmt.Println(limiter.LimitTest()) + } + for _, v := range limiter.windows { + fmt.Println(v.timestamp, v.count) + } +} + +``` + +### 漏桶 +#### 算法思想 +- 漏桶算法是首先想象有一个木桶,桶的容量是固定的。当有请求到来时先放到木桶中,处理请求的worker以固定的速度从木桶中取出请求进行相应 +- 如果木桶已经满了,直接返回请求频率超限的错误码或者页面 + +#### 使用场景 +- 漏桶算法是流量最均匀的限流实现方式,一般用于流量“整形” +- 例如保护数据库的限流,先把对数据库的访问加入到木桶中,worker再以db能够承受的qps从木桶中取出请求,去访问数据库 + +#### 缺陷 +- 木桶流入请求的速率是不固定的,但是流出的速率是恒定的。能保护系统资源不被打满 +- 面对突发流量时会有大量请求失败,不适合电商抢购和微博出现热点事件等场景的限流 + +#### 简单代码实现 +```go +// 一个固定大小的桶,请求按照固定的速率流出 +// 请求数大于桶的容量,则抛弃多余请求 + +type LeakyBucket struct { + rate float64 // 每秒固定流出速率 + capacity float64 // 桶的容量 + water float64 // 当前桶中请求量 + lastLeakMs int64 // 桶上次漏水微秒数 + lock sync.Mutex // 锁 +} + +func (leaky *LeakyBucket) Allow() bool { + leaky.lock.Lock() + defer leaky.lock.Unlock() + + now := time.Now().UnixNano() / 1e6 + // 计算剩余水量,两次执行时间中需要漏掉的水 + leakyWater := leaky.water - (float64(now-leaky.lastLeakMs) * leaky.rate / 1000) + leaky.water = math.Max(0, leakyWater) + leaky.lastLeakMs = now + if leaky.water+1 <= leaky.capacity { + leaky.water++ + return true + } else { + return false + } +} + +func (leaky *LeakyBucket) Set(rate, capacity float64) { + leaky.rate = rate + leaky.capacity = capacity + leaky.water = 0 + leaky.lastLeakMs = time.Now().UnixNano() / 1e6 +} +``` + +### 令牌桶 +#### 算法思想 +- 倒着的漏桶 +- 以恒定的速率向桶中添加令牌 +- 木桶满了则不再加入令牌。服务收到请求时尝试从木桶中取出一个令牌 +- 令牌桶空闲时,可以攒着最高的限额数的令牌 +- 由于木桶内只要有令牌,请求就可以被处理,所以令牌桶算法可以支持突发流量 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/go/lingpaitong.png) + +#### 参数设置 +- 木桶的容量 - 考虑业务逻辑的资源消耗和机器能承载并发处理多少业务逻辑。 +- 生成令牌的速度 - 太慢的话起不到“攒”令牌应对突发流量的效果 + +#### 简单代码实现 +```go +type TokenBucket struct { + rate int64 //固定的token放入速率, r/s + capacity int64 //桶的容量 + tokens int64 //桶中当前token数量 + lastTokenSec int64 //上次向桶中放令牌的时间的时间戳,单位为秒 + + lock sync.Mutex +} + +func (bucket *TokenBucket) Take() bool { + bucket.lock.Lock() + defer bucket.lock.Unlock() + + now := time.Now().Unix() + bucket.tokens = bucket.tokens + (now-bucket.lastTokenSec)*bucket.rate // 先添加令牌 + if bucket.tokens > bucket.capacity { + bucket.tokens = bucket.capacity + } + bucket.lastTokenSec = now + if bucket.tokens > 0 { + // 还有令牌,领取令牌 + bucket.tokens-- + return true + } else { + // 没有令牌,则拒绝 + return false + } +} + +func (bucket *TokenBucket) Init(rate, cap int64) { + bucket.rate = rate + bucket.capacity = cap + bucket.tokens = 0 + bucket.lastTokenSec = time.Now().Unix() +} +``` + +### 官方限流器 +- `golang.org/x/time/rate` +- 基于令牌桶实现 + +```go +type Limiter struct { + mu sync.Mutex + limit Limit + burst int + tokens float64 + // last is the last time the limiter's tokens field was updated + last time.Time + // lastEvent is the latest time of a rate-limited event (past or future) + lastEvent time.Time +} +``` + +#### 初始化 +- `limiter := rate.NewLimiter(10, 100);` +- 两个参数 + + 第一个参数每秒向桶中放令牌的个数 + + 令牌桶的容量,令牌最多的个数 +- 还可以用`every`方法指定向桶中放置token的间隔 +```go +limit := rate.Every(100 * time.Millisecond); +limiter := rate.NewLimiter(limit, 100); +``` + +#### 动态调整 +- `SetLimit(Limit)` 改变放入 Token 的速率 +- `SetBurst(int)` 改变 Token 桶大小 + +#### 使用 +- 三类方法供程序消费 token +- 可以同步等待token生成,也可以没有token时返回token获取失败 +- `Wait/WaitN` +- `Allow/AllowN` +- `Reserve/ReserveN` + +#### Wait/WaitN +- Wait 相当于 WaitN(ctx, 1) +- 如果此时桶内 Token 数组不足 (小于 N), Wait 方法将会阻塞一段时间,直至 Token 满足条件 +- 如果充足则直接返回 +- 可以设置 context 的 Deadline 或者 Timeout,来决定此次 Wait 的最长时间 + +#### Allow/AllowN +- Allow 实际上就是对 AllowN(time.Now(),1) 进行简化的函数 +- AllowN 方法表示,截止到某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token。反之不消费桶中的Token,返回false +- 对应线上的使用场景是,如果请求速率超过限制,就直接丢弃超频后的请求 + +#### Reserve/ReserveN +- Reserve 相当于 ReserveN(time.Now(), 1)。 +- ReserveN 的用法就相对来说复杂一些,当调用完成后,无论 Token 是否充足,都会返回一个 *Reservation 对象 +- 可以调用该对象的Delay()方法,该方法返回的参数类型为time.Duration,反映了需要等待的时间,必须等到等待时间之后,才能进行接下来的工作 +- 如果不想等待,可以调用Cancel()方法,该方法会将 Token 归还 + +#### 主要逻辑代码 +```go +// reserveN is a helper method for AllowN, ReserveN, and WaitN. +// maxFutureReserve specifies the maximum reservation wait duration allowed. +// reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. +func (lim *Limiter) reserveN(now time.Time, n int, maxFutureReserve time.Duration) Reservation { + lim.mu.Lock() + defer lim.mu.Unlock() + + // Inf 一个特别大的值,产生令牌的速率最大,代表一直有令牌 + if lim.limit == Inf { + return Reservation{ + ok: true, + lim: lim, + tokens: n, + timeToAct: now, + } + // 不产生令牌,桶内的用光就没有了 + } else if lim.limit == 0 { + var ok bool + if lim.burst >= n { + ok = true + lim.burst -= n + } + return Reservation{ + ok: ok, + lim: lim, + tokens: lim.burst, + timeToAct: now, + } + } + + // 运行检查看是不是需要生成令牌,和limit生成令牌的时间 + // now 就是传进去的时间 + // last 如果为now,本轮没有生成令牌,否则生成新令牌了 + // tokens 本轮令牌总数 + now, last, tokens := lim.advance(now) + + // Calculate the remaining number of tokens resulting from the request. + // 扣除需要使用的令牌 + tokens -= float64(n) + + // Calculate the wait duration + // 根据令牌数机选需要等待时间 + var waitDuration time.Duration + if tokens < 0 { + waitDuration = lim.limit.durationFromTokens(-tokens) + } + + // Decide result + // 本轮是不是能拿到令牌 + ok := n <= lim.burst && waitDuration <= maxFutureReserve + + // Prepare reservation + r := Reservation{ + ok: ok, + lim: lim, + limit: lim.limit, + } + if ok { + r.tokens = n + r.timeToAct = now.Add(waitDuration) + } + + // Update state + if ok { + lim.last = now + lim.tokens = tokens + lim.lastEvent = r.timeToAct + } else { + lim.last = last + } + + return r +} + +// advance calculates and returns an updated state for lim resulting from the passage of time. +// lim is not changed. +// advance requires that lim.mu is held. +func (lim *Limiter) advance(now time.Time) (newNow time.Time, newLast time.Time, newTokens float64) { + last := lim.last + if now.Before(last) { + last = now + } + + // Calculate the new number of tokens, due to time that passed. + elapsed := now.Sub(last) + delta := lim.limit.tokensFromDuration(elapsed) + tokens := lim.tokens + delta + if burst := float64(lim.burst); tokens > burst { + tokens = burst + } + return now, last, tokens +} + +// durationFromTokens is a unit conversion function from the number of tokens to the duration +// of time it takes to accumulate them at a rate of limit tokens per second. +func (limit Limit) durationFromTokens(tokens float64) time.Duration { + if limit <= 0 { + return InfDuration + } + seconds := tokens / float64(limit) + return time.Duration(float64(time.Second) * seconds) +} + +// tokensFromDuration is a unit conversion function from a time duration to the number of tokens +// which could be accumulated during that duration at a rate of limit tokens per second. +func (limit Limit) tokensFromDuration(d time.Duration) float64 { + if limit <= 0 { + return 0 + } + return d.Seconds() * float64(limit) +} +``` + diff --git a/note/Joplin导出归档/_resources/0372eee0edece0cf1d6dced94329db89.png b/note/Joplin导出归档/_resources/0372eee0edece0cf1d6dced94329db89.png new file mode 100644 index 0000000..635f79a Binary files /dev/null and b/note/Joplin导出归档/_resources/0372eee0edece0cf1d6dced94329db89.png differ diff --git a/note/Joplin导出归档/_resources/0746d379ccf516f12052987cdb69d62e.png b/note/Joplin导出归档/_resources/0746d379ccf516f12052987cdb69d62e.png new file mode 100644 index 0000000..05ab45d Binary files /dev/null and b/note/Joplin导出归档/_resources/0746d379ccf516f12052987cdb69d62e.png differ diff --git a/note/Joplin导出归档/_resources/0d845598e834d2c9d8526aafdbfd3aaa.png b/note/Joplin导出归档/_resources/0d845598e834d2c9d8526aafdbfd3aaa.png new file mode 100644 index 0000000..9a15b6e Binary files /dev/null and b/note/Joplin导出归档/_resources/0d845598e834d2c9d8526aafdbfd3aaa.png differ diff --git a/note/Joplin导出归档/_resources/0ebd3ca9f7ddbf9e5dc52446e22995e4.png b/note/Joplin导出归档/_resources/0ebd3ca9f7ddbf9e5dc52446e22995e4.png new file mode 100644 index 0000000..9af5923 Binary files /dev/null and b/note/Joplin导出归档/_resources/0ebd3ca9f7ddbf9e5dc52446e22995e4.png differ diff --git a/note/Joplin导出归档/_resources/105d782b5f5fb4eb79e7fe7583585a65.png b/note/Joplin导出归档/_resources/105d782b5f5fb4eb79e7fe7583585a65.png new file mode 100644 index 0000000..f541a92 Binary files /dev/null and b/note/Joplin导出归档/_resources/105d782b5f5fb4eb79e7fe7583585a65.png differ diff --git a/note/Joplin导出归档/_resources/108a61ca70a4bcca4a791ab64600503a.png b/note/Joplin导出归档/_resources/108a61ca70a4bcca4a791ab64600503a.png new file mode 100644 index 0000000..485ad43 Binary files /dev/null and b/note/Joplin导出归档/_resources/108a61ca70a4bcca4a791ab64600503a.png differ diff --git a/note/Joplin导出归档/_resources/130836e5300e6e1e037d37ee040bb7b1.png b/note/Joplin导出归档/_resources/130836e5300e6e1e037d37ee040bb7b1.png new file mode 100644 index 0000000..346a9ce Binary files /dev/null and b/note/Joplin导出归档/_resources/130836e5300e6e1e037d37ee040bb7b1.png differ diff --git a/note/Joplin导出归档/_resources/1929245ee849038cfe1a9d5da7fc3443.png b/note/Joplin导出归档/_resources/1929245ee849038cfe1a9d5da7fc3443.png new file mode 100644 index 0000000..a754d5c Binary files /dev/null and b/note/Joplin导出归档/_resources/1929245ee849038cfe1a9d5da7fc3443.png differ diff --git a/note/Joplin导出归档/_resources/199d047fd3786c3065bb4442a7268a82.png b/note/Joplin导出归档/_resources/199d047fd3786c3065bb4442a7268a82.png new file mode 100644 index 0000000..bd076c4 Binary files /dev/null and b/note/Joplin导出归档/_resources/199d047fd3786c3065bb4442a7268a82.png differ diff --git a/note/Joplin导出归档/_resources/21f9477ffd1ea12c46e439205e44a117.png b/note/Joplin导出归档/_resources/21f9477ffd1ea12c46e439205e44a117.png new file mode 100644 index 0000000..aa74c06 Binary files /dev/null and b/note/Joplin导出归档/_resources/21f9477ffd1ea12c46e439205e44a117.png differ diff --git a/note/Joplin导出归档/_resources/25e2449631afbce54c6b4f757aae82db.png b/note/Joplin导出归档/_resources/25e2449631afbce54c6b4f757aae82db.png new file mode 100644 index 0000000..bea2a79 Binary files /dev/null and b/note/Joplin导出归档/_resources/25e2449631afbce54c6b4f757aae82db.png differ diff --git a/note/Joplin导出归档/_resources/294a6fb86ea2c755baf11d4517227967.png b/note/Joplin导出归档/_resources/294a6fb86ea2c755baf11d4517227967.png new file mode 100644 index 0000000..ff6720f Binary files /dev/null and b/note/Joplin导出归档/_resources/294a6fb86ea2c755baf11d4517227967.png differ diff --git a/note/Joplin导出归档/_resources/29f889742bb51955068fb21f8993fdff.png b/note/Joplin导出归档/_resources/29f889742bb51955068fb21f8993fdff.png new file mode 100644 index 0000000..dfef2c1 Binary files /dev/null and b/note/Joplin导出归档/_resources/29f889742bb51955068fb21f8993fdff.png differ diff --git a/note/Joplin导出归档/_resources/2d661ab455541517d45e666109787aab.png b/note/Joplin导出归档/_resources/2d661ab455541517d45e666109787aab.png new file mode 100644 index 0000000..b9509be Binary files /dev/null and b/note/Joplin导出归档/_resources/2d661ab455541517d45e666109787aab.png differ diff --git a/note/Joplin导出归档/_resources/2f4d02d4c9263d854c2f7d8adf177139.png b/note/Joplin导出归档/_resources/2f4d02d4c9263d854c2f7d8adf177139.png new file mode 100644 index 0000000..b35c70f Binary files /dev/null and b/note/Joplin导出归档/_resources/2f4d02d4c9263d854c2f7d8adf177139.png differ diff --git a/note/Joplin导出归档/_resources/35d9622d638eaac96466c765d9bf1092.png b/note/Joplin导出归档/_resources/35d9622d638eaac96466c765d9bf1092.png new file mode 100644 index 0000000..a42e2f4 Binary files /dev/null and b/note/Joplin导出归档/_resources/35d9622d638eaac96466c765d9bf1092.png differ diff --git a/note/Joplin导出归档/_resources/3af55d15a2738c93e1728f5ca9edb2e0.png b/note/Joplin导出归档/_resources/3af55d15a2738c93e1728f5ca9edb2e0.png new file mode 100644 index 0000000..7a7c28e Binary files /dev/null and b/note/Joplin导出归档/_resources/3af55d15a2738c93e1728f5ca9edb2e0.png differ diff --git a/note/Joplin导出归档/_resources/59e2e6e78b7e5d8f0b2bf74c16f74ff0.png b/note/Joplin导出归档/_resources/59e2e6e78b7e5d8f0b2bf74c16f74ff0.png new file mode 100644 index 0000000..99e2620 Binary files /dev/null and b/note/Joplin导出归档/_resources/59e2e6e78b7e5d8f0b2bf74c16f74ff0.png differ diff --git a/note/Joplin导出归档/_resources/67afdff1310314bd312eebfdafa233a6.png b/note/Joplin导出归档/_resources/67afdff1310314bd312eebfdafa233a6.png new file mode 100644 index 0000000..2e6fe0e Binary files /dev/null and b/note/Joplin导出归档/_resources/67afdff1310314bd312eebfdafa233a6.png differ diff --git a/note/Joplin导出归档/_resources/6ba5ae15c9216b3d6197865ca445df74.png b/note/Joplin导出归档/_resources/6ba5ae15c9216b3d6197865ca445df74.png new file mode 100644 index 0000000..81e1112 Binary files /dev/null and b/note/Joplin导出归档/_resources/6ba5ae15c9216b3d6197865ca445df74.png differ diff --git a/note/Joplin导出归档/_resources/7f95339bd98c5f0bee5868e52d28ee80.png b/note/Joplin导出归档/_resources/7f95339bd98c5f0bee5868e52d28ee80.png new file mode 100644 index 0000000..b0c2f81 Binary files /dev/null and b/note/Joplin导出归档/_resources/7f95339bd98c5f0bee5868e52d28ee80.png differ diff --git a/note/Joplin导出归档/_resources/81661e9a437155916091b778a65f9e22.png b/note/Joplin导出归档/_resources/81661e9a437155916091b778a65f9e22.png new file mode 100644 index 0000000..ffb5473 Binary files /dev/null and b/note/Joplin导出归档/_resources/81661e9a437155916091b778a65f9e22.png differ diff --git a/note/Joplin导出归档/_resources/859a677e28513694723c0a197bed2786.png b/note/Joplin导出归档/_resources/859a677e28513694723c0a197bed2786.png new file mode 100644 index 0000000..53b8655 Binary files /dev/null and b/note/Joplin导出归档/_resources/859a677e28513694723c0a197bed2786.png differ diff --git a/note/Joplin导出归档/_resources/8801a0e5453be03569f1ee0cf0adfda0.png b/note/Joplin导出归档/_resources/8801a0e5453be03569f1ee0cf0adfda0.png new file mode 100644 index 0000000..7a264b8 Binary files /dev/null and b/note/Joplin导出归档/_resources/8801a0e5453be03569f1ee0cf0adfda0.png differ diff --git a/note/Joplin导出归档/_resources/8b4818c7593fa7bc16f12bef265c9ccc.png b/note/Joplin导出归档/_resources/8b4818c7593fa7bc16f12bef265c9ccc.png new file mode 100644 index 0000000..e6b88f7 Binary files /dev/null and b/note/Joplin导出归档/_resources/8b4818c7593fa7bc16f12bef265c9ccc.png differ diff --git a/note/Joplin导出归档/_resources/99275e98a56fc125fcba3c17489f58a4.png b/note/Joplin导出归档/_resources/99275e98a56fc125fcba3c17489f58a4.png new file mode 100644 index 0000000..1755b31 Binary files /dev/null and b/note/Joplin导出归档/_resources/99275e98a56fc125fcba3c17489f58a4.png differ diff --git a/note/Joplin导出归档/_resources/99e455bd412ce7823ce72bda88e93c7d.png b/note/Joplin导出归档/_resources/99e455bd412ce7823ce72bda88e93c7d.png new file mode 100644 index 0000000..9e6d384 Binary files /dev/null and b/note/Joplin导出归档/_resources/99e455bd412ce7823ce72bda88e93c7d.png differ diff --git a/note/Joplin导出归档/_resources/9c89f9daed2d117cc3247a5e5a72cf1c.png b/note/Joplin导出归档/_resources/9c89f9daed2d117cc3247a5e5a72cf1c.png new file mode 100644 index 0000000..5d1ddfb Binary files /dev/null and b/note/Joplin导出归档/_resources/9c89f9daed2d117cc3247a5e5a72cf1c.png differ diff --git a/note/Joplin导出归档/_resources/b0338a4f367376224e8339ba29bec865.png b/note/Joplin导出归档/_resources/b0338a4f367376224e8339ba29bec865.png new file mode 100644 index 0000000..08f7c6c Binary files /dev/null and b/note/Joplin导出归档/_resources/b0338a4f367376224e8339ba29bec865.png differ diff --git a/note/Joplin导出归档/_resources/c01a0eb6107168c67d46719fee3904d9.png b/note/Joplin导出归档/_resources/c01a0eb6107168c67d46719fee3904d9.png new file mode 100644 index 0000000..5baf29a Binary files /dev/null and b/note/Joplin导出归档/_resources/c01a0eb6107168c67d46719fee3904d9.png differ diff --git a/note/Joplin导出归档/_resources/ce08963815fd84594116f3c89e7a8b03.png b/note/Joplin导出归档/_resources/ce08963815fd84594116f3c89e7a8b03.png new file mode 100644 index 0000000..8a3e63c Binary files /dev/null and b/note/Joplin导出归档/_resources/ce08963815fd84594116f3c89e7a8b03.png differ diff --git a/note/Joplin导出归档/_resources/d9e84b7b4e975ae227430434f3768c15.png b/note/Joplin导出归档/_resources/d9e84b7b4e975ae227430434f3768c15.png new file mode 100644 index 0000000..b82f5cd Binary files /dev/null and b/note/Joplin导出归档/_resources/d9e84b7b4e975ae227430434f3768c15.png differ diff --git a/note/Joplin导出归档/_resources/e46d8cc96a6a687642c396363f226422.png b/note/Joplin导出归档/_resources/e46d8cc96a6a687642c396363f226422.png new file mode 100644 index 0000000..985a4ae Binary files /dev/null and b/note/Joplin导出归档/_resources/e46d8cc96a6a687642c396363f226422.png differ diff --git a/note/Joplin导出归档/_resources/e71126bba215cba932a20e2823001160.png b/note/Joplin导出归档/_resources/e71126bba215cba932a20e2823001160.png new file mode 100644 index 0000000..00c7877 Binary files /dev/null and b/note/Joplin导出归档/_resources/e71126bba215cba932a20e2823001160.png differ diff --git a/note/Joplin导出归档/_resources/f9e436b5b8d1d281b5d270b28b5a22a5.png b/note/Joplin导出归档/_resources/f9e436b5b8d1d281b5d270b28b5a22a5.png new file mode 100644 index 0000000..f5f7427 Binary files /dev/null and b/note/Joplin导出归档/_resources/f9e436b5b8d1d281b5d270b28b5a22a5.png differ diff --git a/note/Joplin导出归档/_resources/fa951c7b58b47ebd846fc00df0ecc70d.png b/note/Joplin导出归档/_resources/fa951c7b58b47ebd846fc00df0ecc70d.png new file mode 100644 index 0000000..83875b8 Binary files /dev/null and b/note/Joplin导出归档/_resources/fa951c7b58b47ebd846fc00df0ecc70d.png differ diff --git a/note/Joplin导出归档/_resources/spacer_23c0cdccfe53417ab9137b55990fbf8b.gif b/note/Joplin导出归档/_resources/spacer_23c0cdccfe53417ab9137b55990fbf8b.gif new file mode 100644 index 0000000..e10ec66 --- /dev/null +++ b/note/Joplin导出归档/_resources/spacer_23c0cdccfe53417ab9137b55990fbf8b.gif @@ -0,0 +1,7 @@ + +403 Forbidden + +

403 Forbidden

+
openresty
+ + diff --git a/note/Joplin导出归档/devops/k8s/k3s/k3s 部署.md b/note/Joplin导出归档/devops/k8s/k3s/k3s 部署.md new file mode 100644 index 0000000..94d3254 --- /dev/null +++ b/note/Joplin导出归档/devops/k8s/k3s/k3s 部署.md @@ -0,0 +1,12 @@ +部署 master +curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn sh -s - server \ + --token=Sunqi0220. \ + --write-kubeconfig-mode "0644" + +增加 worker + +curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | INSTALL_K3S_MIRROR=cn K3S_URL=https://192.168.110.2:6443 K3S_TOKEN=Sunqi0220. sh - + + + + \ No newline at end of file diff --git a/note/Joplin导出归档/devops/k8s/k3s/默认 kubectl yaml 路径.md b/note/Joplin导出归档/devops/k8s/k3s/默认 kubectl yaml 路径.md new file mode 100644 index 0000000..daf9934 --- /dev/null +++ b/note/Joplin导出归档/devops/k8s/k3s/默认 kubectl yaml 路径.md @@ -0,0 +1 @@ +/etc/rancher/k3s/k3s.yaml \ No newline at end of file diff --git a/note/Joplin导出归档/devops/k8s/sealos/创建单节点.md b/note/Joplin导出归档/devops/k8s/sealos/创建单节点.md new file mode 100644 index 0000000..256e0f7 --- /dev/null +++ b/note/Joplin导出归档/devops/k8s/sealos/创建单节点.md @@ -0,0 +1 @@ +sealos run labring/kubernetes:v1.22.13 labring/helm:v3.8.2 labring/calico:v3.24.1 --single \ No newline at end of file diff --git a/note/Joplin导出归档/devops/k8s/sealos/创建多节点.md b/note/Joplin导出归档/devops/k8s/sealos/创建多节点.md new file mode 100644 index 0000000..e69de29 diff --git a/note/Joplin导出归档/devops/k8s/sealos/安装 sealos.md b/note/Joplin导出归档/devops/k8s/sealos/安装 sealos.md new file mode 100644 index 0000000..e9aaf0f --- /dev/null +++ b/note/Joplin导出归档/devops/k8s/sealos/安装 sealos.md @@ -0,0 +1,2 @@ +wget https://github.com/labring/sealos/releases/download/v4.1.7/sealos_4.1.7_linux_amd64.tar.gz +tar -zxvf sealos_4.1.7_linux_amd64.tar.gz sealos && chmod +x sealos && mv sealos /usr/bin \ No newline at end of file diff --git a/note/Joplin导出归档/golang/golang 代码生成.md b/note/Joplin导出归档/golang/golang 代码生成.md new file mode 100644 index 0000000..e69de29 diff --git a/note/Joplin导出归档/golang/golang 调试.md b/note/Joplin导出归档/golang/golang 调试.md new file mode 100644 index 0000000..e69de29 diff --git a/note/Joplin导出归档/golang/单测 mock 函数与方法.md b/note/Joplin导出归档/golang/单测 mock 函数与方法.md new file mode 100644 index 0000000..54337ed --- /dev/null +++ b/note/Joplin导出归档/golang/单测 mock 函数与方法.md @@ -0,0 +1,77 @@ +### 方法 mock +```go +var cMock *PassService +passSer := gomonkey.ApplyMethod(reflect.TypeOf(cMock), "PassUidToUnionId", +func(_ *PassService, userId uint64) (string, error) { + return "test", nil +}) +defer passSer.Reset() +``` + +```go +func (t *PassService) PassUidToUnionId(userId uint64)(string, error){ + // 函数逻辑 + return ret.Encoding.UnionID, nil +} +``` + +### 函数 mock +```go +func function_name([parameter list]) [return_types] { + 函数体 +} + +func A(ctx context.Context, str string) error { + if len(str) == 0 { + return errors.New("str is empty") + } + return test_package_name.B(ctx, str) +} + +func TestA(t *testing.T) { + type args struct { + ctx context.Context + str string + } + tests := []struct { + name string + args args + Setup func(t *testing.T) + wantErr error + }{ + { + name: "len(str) == 0", + wantErr: errors.New("str is empty") + }, + { + name: "正常响应", + Setup: func(t *testing.T) { + patches := gomonkey.ApplyFunc(test_package_name.B, func(_ context.Context, _ string) error { + return nil + }) + t.Cleanup(func() { + patches.Reset() + }) + }, + args: args{ + ctx: context.Background(), + str: "test", + }, + wantErr: nil, + }, + } + + // 执行测试用例 + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.Setup != nil { + tt.Setup(t) + } + err := A(tt.args.ctx, tt.args.str) + if err != nil { + assert.EqualError(t, err, tt.wantErr.Error(), "error 不符合预期") + } + }) + } +} +``` \ No newline at end of file diff --git a/note/Joplin导出归档/php/数据类型.md b/note/Joplin导出归档/php/数据类型.md new file mode 100644 index 0000000..56b8a7a --- /dev/null +++ b/note/Joplin导出归档/php/数据类型.md @@ -0,0 +1,16 @@ +### 数据类型 +- String(字符串) +- Integer(整型) +- Float(浮点型) +- Boolean(布尔型) +- Array(数组) +- Object(对象) +- NULL(空值) +- Resource(资源类型) + +### 整数 +- 整数必须至少有一个数字 (0-9) +- 整数不能包含逗号或空格 +- 整数是没有小数点的 +- 整数可以是正数或负数 +- 整型可以用三种格式来指定:十进制, 十六进制( 以 0x 为前缀)或八进制(前缀为 0)。 diff --git a/note/Joplin导出归档/计算机网络/http/TCP_IP.md b/note/Joplin导出归档/计算机网络/http/TCP_IP.md new file mode 100644 index 0000000..9372fd5 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/TCP_IP.md @@ -0,0 +1,2 @@ +### 协议族 +![f9e436b5b8d1d281b5d270b28b5a22a5.png](../../_resources/f9e436b5b8d1d281b5d270b28b5a22a5.png) \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/https.md b/note/Joplin导出归档/计算机网络/http/https.md new file mode 100644 index 0000000..604d1d2 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/https.md @@ -0,0 +1,59 @@ +### HTTPS +- 超文本传输安全协议 +- HyperText Transfer Protocol Secure +- HTTP over TLS, HTTP over SSL +- 默认占用端口443 + +### TLS、SSL +- TLS Transport Layer Security传输层安全协议 +- SSL Secure Socket Layer 安全套接层 +- 工作在传输层和应用层之间 + +### TLS 流程 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/tls.png) +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/https.jpg) + +#### 客户端发送 `client hello` +- TLS 版本号 +- 支持的加密组件列表 +- 使用的加密算法以及秘钥长度 +- 一个随机数字 client random(用途:防止连接重放) + +#### 服务发送 `server hello` +- TLS版本号 +- 从客户端发送的加密组件列表中选择的加密方式 +- 一个随机数 server random + +#### 服务端发送 `Certificate` +- 服务器被CA签名过的公钥证书 + +#### 服务端发送 `Server Key Exchange` +- 实现`ECHDE`算法的其中一个参数(Server Params) +- Server Params 经过了服务端私钥签名 + +#### 服务端 `Server Hello Done` +- 告知客户端,服务端发送完毕,协商过程结束 +- 明文共享 + - client random + - server random + - server params + - 服务端公钥证书 + + +#### 客户端发送 `Client Key Exchange` +- 实现`ECHDE`算法中的另一个参数(Client Params) +- 客户端和服务端此时都有了 Client Params 和 Server Params +- 用 ECHDE 算法和两个Params 生成随机秘钥串 pre-master +- 用client random,server random和pre-master生成主秘钥 +- 主密钥衍生出,客户端和服务端发送用的会话秘钥 + +#### 客户端发送 `Change Ciper Spec` +- 告知服务端之后的通信会采用计算出来的秘钥进行加密通信 + +#### 客户端发送 `Finished` +- 连接至今全部报文的整体校验值(摘要),加密之后发送给服务端 + +#### 服务端发送 `Change Ciper Spec`和`Finished` +- 服务端解密检查报文没有问题,然后TLS完毕 +- 后续所有数据采用加密传输 + diff --git a/note/Joplin导出归档/计算机网络/http/http升级改造.md b/note/Joplin导出归档/计算机网络/http/http升级改造.md new file mode 100644 index 0000000..b63203f --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/http升级改造.md @@ -0,0 +1,49 @@ +### HTTP1.1 协议不足 +- 同一时间,一个连接只能对应一个请求 +- 只允许客户端主动发起连接 +- 同一个会话的多次请求中,头信息会被重复传输 + +### SPDY +- 基于TCP的应用层协议,强制使用TLS/SSL +- 修改了HTTP请求与响应的传输方式 +- SPDY是HTTP2的前身 + +### HTTP2 +- 底层传输做了优化和改进,在语意上完全兼容HTTP1.1 +- HTTP2采用了二进制传输,而HTTP1.1采用了文本传输 + +### HTTP2常用概念 +#### 数据流 +- 已建立的连接的双向字节流,可以承载一条或多条消息 +- 所有通信都在一个TCP连接上完成,此连接可以承载任意数量的双向数据流 + +#### 消息 +- 与逻辑HTTP请求或响应消息对应,由一系列帧组成 + +#### 帧 +- HTTP2通信的最小单位,每个帧都包含帧头,标识出当前帧所属的数据流 +- 来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装 + +#### 多路复用 Multiplexing +- 客户端和服务端可以将消息分解成互不依赖的帧,交错发送,然后收到后重新组装起来 +- 并行交错发送多个请求,请求之间互不影响 +- 并行交错发送多个响应,响应之间互不影响 +- 使用一个连接发送请求和响应 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/http1_2.png) + +#### 优先级 +- HTTP2允许每个数据流有一个权重和优先级 +- 客户端可以设置每个优先级 +- 服务端根据优先级进行响应,高优先级的优先传输给客户端 + +#### 头部压缩 +- 使用HPACK压缩请求头和响应头 +- 减少开销,提高性能 + +#### 服务器推送 Server Push +- 服务器除了响应客户端的需求外,还可以主动推送内容给客户端 + +### HTTP2问题 +- 队头阻塞 +- 握手延迟 \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/传输层.md b/note/Joplin导出归档/计算机网络/http/传输层.md new file mode 100644 index 0000000..b62e1f5 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/传输层.md @@ -0,0 +1,164 @@ +### 传输层有两个协议 +- TCP Transmission Control Protocol 传输控制协议 +- UDP User Datagram Protocol 用户数据报协议 +image + +### UDP +- 无连接,减少了建立和释放链接的开销 +- 尽最大能力交付,不保证可靠传输 +- 首部较小,UDP首部只有8个字节 +![image](https://user-images.githubusercontent.com/39154923/137895070-81ec320b-4196-4766-9438-2f8166e7712c.png) +- UDP长度:首部的长度 + 数据部分的长度 +- UDP检验和:计算的是伪首部(12字节) + 首部(8字节) + 数据 +- 伪首部:仅在计算检验和时起作用,并不会传递给网络层 +![image](https://user-images.githubusercontent.com/39154923/137895425-44be7955-9425-4853-aefa-390eebe621e1.png) + +- UDP:客户端源端口时临时开启的随机端口 + +### 伪首部 +- 源IP 4字节 +- 目标IP 4字节 +- 零填充 + 协议 + 长度(首部 + 数据部分) 4字节 +- TCP与UDP伪首部是一样的 +- 仅在计算检验和时起作用 + +### TCP +![image](https://user-images.githubusercontent.com/39154923/138079676-0dfb6af5-a696-465e-9602-5be81e29e9a1.png) + +- TCP首部的空间至少是20字节,最大60字节 +- 和UDP一样 也有12个字节的伪首部,不会传递到网络层 +- 数据偏移占4位,取值范围 0x0101~0x1111,乘4代表首部长度,最小值是5,最大值是15 + +### TCP要点 +- 可靠传输 +- 流量控制 +- 拥塞控制 +- 连接管理 + +### 数据长度 +- UDP首部用16位记录整个UDP数据加首部的长度 +- TCP用4位标记了TCP报文首部长度,并没有记录数据长度 +- 传输层的数据长度 = 网络层的数据长度 - 网络层的首部长度 - 传输层的首部长度 + +### TCP flag含义 + +- URG:Urgent,紧急字段,为1时,表示当前报文段中有紧急数据,需要优先传输,尽快传送 +- ACK:Acknowledgment 确认字段,当ack为1时,首部中的确认号字段才有效 +- PSH:Push 交互式网络 +- RST:Reset,为1时,表明链接中出现严重差错,必须释放连接,然后重新创建连接 +- SYN:Synchronization + - SYN = 1,ACK = 0时,表明这是一个建立连接的请求 + - SYN = 1,ACK = 1时,表明对方同意建立连接 +- FIN:Finish,为1时,表明数据已经发送完毕,要求释放连接 + +### 序号 +- 4个字节 +- 传输过程中,每一个字节都会有一个编号 +- 建立连接后,序号代表:这一次传给对方的TCP数据部分的第一个字节的编号 + +### 确认号 +- 4个字节 +- 建立连接后,确认号代表:期望对方下一次传过来的TCP数据部分的第一个字节的编号 + +### TCP 可靠传输 +- 三次握手 +- 四次挥手 +- ARQ协议(Automatic Repeat Request) 自动重传协议,超时重传 + + 每个包重试次数过多会发送reset报文,重新创建连接 + +- 滑动窗口协议:批量发送,批量确认,从确认的报文的下一个开始重新发送 + - SACK(selective acknowledgment),选择确认,放在TCP首部的选项部分 +- 拥塞控制 + +#### SACK 选择确认 +- 放在TCP首部的选项部分 +- kind 1字节,值为5代表是sack +- length 1字节,代表sack部分的长度占用多少字节 +- left range 左边界 4字节 +- right range 右边界 4字节 +- 每个选项部分最多携带4组边界值 + - 选项部分最长40字节 + - 4 * 8 + 2 = 34 + +### TCP 流量控制 +- 不进行流量控制接收方已经无法接受了,发送方还在发送,造成浪费 +- `点对点`的控制发送方的发送速率 +- 通过读取接收方的窗口大小,来控制发送方发送数据的多少 +- 发送方的窗口 `<=` 接收方的窗口大小 +- 接收方窗口为0,发送方停止发送,如果还有报文要发送,就定时询问接收方的窗口大小 + +### TCP 拥塞控制 +- 防止过多的数据注入到网络中 +- 防止链路过载或网络中传输的硬件过载 + +#### 方法 +- 慢开始:cwnd 指数级增长 +- 拥塞避免:到达慢开始阈值后,cwdn线性增长 +- 快速重传:连续收到同一个报文的3次确认后,立即重新发送下一个报文 +- 快速恢复:遇到网络拥塞,然后cwnd的起始值变为慢开始阈值的一半,线性增长 + +> - MSS: Maximum Segment Size 每个数据段的数据最大大小 +> - cwnd congestion window 拥塞窗口 +> - rwnd receive window 接收窗口 +> - swnd send window 发送窗口 +> - swnd = min(cwnd, rwnd) +> - slow start threshold 慢开始阈值,到达阈值后,cwdn 线性增长 + +### TCP 连接管理 +- 连接时三次握手 +- 断开连接时四次挥手 + +#### TCP 建立连接 三次握手 + +- closed 客户端处于关闭状态 +- listen server处于监听状态,等待客户端连接 +- syn-rcvd server端收到了客户端发送的建立链接报文后的状态 +- syn-sent client端发送了建立连接报文后的状态 +- established 连接建立状态 + - client收到了server返回的确认建立连接报文的状态 + - server端收到了client返回的对建立连接报文进行确认的报文后的状态 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/tcp_handshake.png) + +- 前两次建立连接的特点 + - 数据部分长度都为0 + - SYN都设置为1 + - 首部长度为32字节(20字节首部 + 12字节选线部分) + - 会交换MSS,是否支持SACK,和窗口大小 + + +##### 为什么不采用2次握手 +- 两次握手,服务端收到客户端的请求就建立连接,容易造成浪费资源 +- 客户端发送的链接建立请求因为网络波动,导致超时送达,此时链接已经释放 +- 如果采用两次握手,服务端会对这个延迟的报文进行确认建立连接,但是客户端并无链接意愿,就会造成服务端连接浪费 + +##### 第3次握手失败了,怎么处理 +- 因为网络问题,导致server并没有收到client端的3次握手 +- 服务端超时时间内,重新发送SYN + ACK确认包 +- 如果发送多次确认包都没有收到回复,则会发送RST包,强制关闭连接 + +#### TCP 断开连接 四次挥手 +- `fin_wait1` 主动关闭链接,并且向对方发送了关闭链接报文 +- `fin_wait2` 收到对方发送的ACK确认报文,然后等待对方发送关闭连接报文 +- `close_wait` 收到对方发送的关闭链接报文,并向对方回复了ACK确认报文 + - 判断对方自己是否还有数据要发给对方,有就继续发送数据 + - 没有就发送FIN报文表示自己也想关闭连接 + +- `closing` 双方同时收到了对方发送的FIN报文 +- `last_ack` 被动关闭的一方,在发送完FIN报文后,等待另一方确认 +- `time_wait` 收到了对方的FIN报文,并回复了ACK,等2MSL时间后就自动进入`closed`状态 +- 如果在`fin_wait1`状态据收到了对方的FIN和ACK一起的报文,就可以直接进入到`time_wait`状态 +- `closed` 关闭状态 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/tcp_wave.jpg) + +#### TCP 断开连接细节 +- 连接双方任意一方都可以主动关闭连接 +- 全双工通信,双向关闭连接 +- 主动关闭一方发送对另一方的FIN报文确认的ACK后,需要等待2MSL + - MSL(Maximum Segment Lifetime)TCP报文在internet中最大生存时间 + +#### 一方发送ACK后,断开连接 +- 如果网络波动,造成另一方没有收到ACK,就会重复发送FIN +- 忽略FIN,造成另一方资源浪费 +- 接收FIN,造成端口相同的新连接在收到报文后响应关闭连接 \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/传输方式.md b/note/Joplin导出归档/计算机网络/http/传输方式.md new file mode 100644 index 0000000..9f3b61b --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/传输方式.md @@ -0,0 +1,16 @@ +### 连接角度 +- 面相连接 +- 面相无连接 +![29f889742bb51955068fb21f8993fdff.png width](../../_resources/29f889742bb51955068fb21f8993fdff.png) + +### 链路角度 +- 电路交换 +- 分组交换 +![6ba5ae15c9216b3d6197865ca445df74.png](../../_resources/6ba5ae15c9216b3d6197865ca445df74.png) + +### 接收方数量角度 +- 单播 unicast +- 广播 broadcast +- 多播 multicat +- 任播 anycast dns域名解析服务器有用到 +![ce08963815fd84594116f3c89e7a8b03.png](../../_resources/ce08963815fd84594116f3c89e7a8b03.png) \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/协议&&OSI七层模型.md b/note/Joplin导出归档/计算机网络/http/协议&&OSI七层模型.md new file mode 100644 index 0000000..5493776 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/协议&&OSI七层模型.md @@ -0,0 +1,35 @@ +### 概念 +- 计算机与计算机之间通过网络实现通信时事先达成的一种“约定” +- 使那些由不同厂商的设备,不同的 CPU 以及不同的操作系统组成的计算机之间,只要遵循相同的协议就能够实现通信,反之,如果协议不同,就无法通信 +- 协议可以分为很多种,每一种协议都明确的界定了它的行为规范 +- 两台计算机之间必须能够支持相同的协议,并遵循相同协议进行处理,才能实现互相通信 +![0ebd3ca9f7ddbf9e5dc52446e22995e4.png](../../_resources/0ebd3ca9f7ddbf9e5dc52446e22995e4.png) + +### 分组交换协议 +- 将大数据分割为一个个叫做包的较小单位进行传输的方法 +- 每一个分组中附加上源主机地址和目标主机地址送给通信线路,发送端地址,接收端地址以及分组序号写的部分叫做“报文首部” + + +### OSI七层模型 +- 应用层 +- 表示层 +- 会话层 +- 网络层 +- 数据链路层 +- 物理层 +- 上下层之间交互遵循的约定叫做`接口` +- 同一层交互遵循的约定叫做`协议` +![d9e84b7b4e975ae227430434f3768c15.png](../../_resources/d9e84b7b4e975ae227430434f3768c15.png) + + +### OSI优点与劣势 +- 优点1:每个分层可以独立使用,即使系统中的某些分层发生变化,也不会波及整个系统,因此可以构造一个扩展性和灵活性都很强的系统 +- 优点2:分层能够系统通信功能,更易于单独实现每个分层的协议,并界定各个分层的具体责任和义务 +- 劣势:过分模块化,式处理变得更加沉重以及每个模块都不得不实现相似的处理逻辑 + +### OSI各个分层的作用 +- 作用具体表格 +![35d9622d638eaac96466c765d9bf1092.png](../../_resources/35d9622d638eaac96466c765d9bf1092.png) + +- 示意图 +![81661e9a437155916091b778a65f9e22.png](../../_resources/81661e9a437155916091b778a65f9e22.png) \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/数据链路层.md b/note/Joplin导出归档/计算机网络/http/数据链路层.md new file mode 100644 index 0000000..b74191f --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/数据链路层.md @@ -0,0 +1,55 @@ +### 数据链路层 Data Link +- 链路: 从一个节点到相邻节点到一段物理线路(有线或无线),中间没有其他交换节点(网桥和集线器除外) +- 数据链路层的数据叫 `帧` +- 不同类型的数据链路,所用的通信协议可能是不同的 +- 广播信道: CSMA/CD协议,比如同轴电缆,集线器等组成的电路 +- 点对点信道: PPP协议,比如两个路由器之间的信道 + +### 数据链路层的3个基本问题 +- 封装成帧 +- 透明传输 +- 差错检验 + +#### 封装成帧 +![image](https://user-images.githubusercontent.com/39154923/135198947-d37752b6-ae36-441e-ab79-6e78b1e94272.png) + +- 帧的数据部分就是网络层传递下来的数据包 +- 最大传输单元MTU,每一种数据链路层协议都规定了所能传送的帧的数据长度上限 +- 以太网的MTU为1500字节 + +#### 透明传输 + +![image](https://user-images.githubusercontent.com/39154923/135201189-b0dc94dc-2f1d-4bc0-8f85-9cf6ea3f795f.png) + +- SOH帧开始符 +- EOT帧结束符 +- 数据部分一旦出现帧开始或者帧结束符就进行转义 +![image](https://user-images.githubusercontent.com/39154923/135202743-71b37be3-7dc6-43a7-9f65-77e16dec6267.png) + +#### 差错检验 +![image](https://user-images.githubusercontent.com/39154923/135203157-2bca9d97-7e0d-40f6-9bc0-b22d64bb9cf8.png) + +- FCS是根据数据部分 + 首部计算得出来的 +### CSMA/CD协议 +- Carrier Sense Multiple Access wich Collision Detectio 载波监听多路访问/冲突检测 +- 使用这个协议的网络可以称为是以太网,它传输的是以太网帧,格式有Ethernet V2标准,IEEE的802.3标准,使用最多的是前者 +- 为了能够正常检测正在发送的帧是否产生了冲突,以太网的帧至少要64字节 +- 用交换机组建的网络,支持全双工通信,不需要使用CSMA/CD协议,但是传输的帧依然是以太网帧,组建的网络依然可以叫以太网 +- 数据的长度至少是46字节,数据长度不够时数据链路层会进行填充,接收端会去掉填充的字节 +- 以太网帧的数据长度时46-1500,整个以太网帧的长度时64-1518 + ![图片](https://user-images.githubusercontent.com/39154923/136492904-f1d5fd45-7ab7-489f-bf68-38617daa9bf1.png) + +### 点对点PPP协议(Point to Point Protocol) +![image](https://user-images.githubusercontent.com/39154923/136524602-5f2fbe65-20de-4a37-a2dc-6e59903d01a2.png) + +- Address字段, 0xFF,点到点通信不需要源mac地址和目标mac地址 +- Control字段,0x03 目前没有用到 +- Protocal PPP协议使用到的内部协议 +- 帧开始符和帧结束符 0x7E +- PPP帧封装的网络层数据和以太网帧是一样 + +### 物理设备 +- 网卡:工作在物理层,负责fcs差错检验,通过后会删除fcs内容 +- 交换机:二层交换机,数据链路层,知道双方的mac地址 +- 集线器:工作在物理层 +- 路由器:三层,网络层,知道双方的ip地址 \ No newline at end of file diff --git a/note/Joplin导出归档/计算机网络/http/网络发展概述.md b/note/Joplin导出归档/计算机网络/http/网络发展概述.md new file mode 100644 index 0000000..b144662 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/网络发展概述.md @@ -0,0 +1,11 @@ +### 七个阶段 +- 批处理:用户程序和数据装入磁带或卡带,交由计算机按照一定的顺序读取,使用户所要执行的这些程序和数据能够一并批量得到处理的方式 +- 分时系统:多个终端与容易个计算机连接,允许多个用户同时使用一台计算机的系统 + - 独占性:用户感觉好像完全是自己在使用一台计算机一样 + - 交互性 + - 及时性 +- 计算机与计算机之间的连接:私人或公司内计算机传输处理数据需求,推动了计算机与计算机之间的连接 +- 计算机网络的诞生 +- 互联网的普及 +- 已互联网技术为中心的时代 +- 无论何时何地地一切皆TCP/IP 的网络时代 diff --git a/note/Joplin导出归档/计算机网络/http/网络安全.md b/note/Joplin导出归档/计算机网络/http/网络安全.md new file mode 100644 index 0000000..7902485 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/网络安全.md @@ -0,0 +1,113 @@ +### 网络安全威胁 +- 窃听通信内容 +- 中断网络通信 +- 篡改通信内容 +- 伪造通信内容 + +### 网络层 ARP欺骗 +- ARP spoofing 又称为ARP毒化 +- 可以让攻击者获取局域网内的数据包 +- 可以让网络中特定的电脑之间无法通信 +- 让送至某一个ip的流量送到攻击者的电脑上 + +#### ARP解决 +- 静态ARP +- DHCP Snooping +- 软件监听ARP不正常变动 + +### DoS DDoS +#### DoS 拒绝服务攻击,Denial of Service Attack +- 使目标电脑的网络资源或系统资源耗尽,无法提供服务 + +#### DDoS Distributed Denial of Service Attack +- 使用多个僵尸电脑对统一目标进行DoS攻击 + +#### 攻击分类 +- 带宽消耗型 UDP洪水攻击,ICMP洪水攻击 +- 资源消耗型 SYN洪水攻击,LAND 攻击 + +#### 防御 +- 入侵检测 +- 流量过滤 +- 多重验证 +- 防火墙 +- 交换机 +- 路由器 +- 黑洞引导 攻击流量引导到空地址或不存在的计算机 +- 流量清洗 通过DDoS流量清洗软件,区分攻击流量和正常流量 + +### 传输层 SYN 洪水攻击 +- 攻击者发送一系列的SYN请求到目标主机,但是不然目标主机收到ACK确认(第三次握手) +- 造成目标主机等待,资源浪费 +- 伪造ACK确认的主机ip地址,让目标主机发送SYN=1,ACK=1到一个不存在或者不能回复的ip地址 + +### 传输层 LAND攻击 +- LAND攻击(局域网拒绝服务攻击) +- Local Area Network Denial attack +- 通过持续发送相同源地址和目标地址的欺骗数据包,使目标视图与自己建立连接,消耗系统资源直至崩溃 + +#### 防护 +- 添加防火墙 +- 操作系统打补丁 +- 路由器配置策略,屏蔽源地址与目标地址相同的数据包 + + +### 应用层 DNS劫持 +- DNS劫持,又称域名劫持 +- 攻击者篡改了某个域名的解析结果,使得域名指向了另一个ip +- 被攻击者访问了不可达或假冒的网站 + +### HTTP 劫持 +- 对HTTP数据包进行拦截处理,比如插入JS代码 + +### 常见加密方式 +- 不可逆加密 单向散列函数加密 MD5,SHA +- 可逆加密 + - 对称加密 DES,3DES,AES + - 非对称加密 RSA +- 其他 + - 混合密码系统 + - 数字签名 + - 证书 + + +### 单向散列函数 +- 单向散列函数也被称为`消息摘要函数`或者`哈希函数` +- 输出的值也被称为`消息摘要`或`指纹` +- 根据任意长度的消息,计算出`固定长度`的散列值 +- 计算速度快,能快速计算出散列值 +- 消息不同,散列值也不同 +- 具备`单向性` + +#### 常见单向散列函数 +- MD4,MD5 产生128bit的散列值 +- SHA-1 产生160bit的散列值 +- SHA-2 SHA-256 SHA-384 SHA-512 散列值长度分别是256bit,384bit和512bit +- SHA-3 + +### AES 对称加密 +- Advanced Enryption Standard +- 密钥长度有128bit,192bit,256bit + +### 非对称加密 +- Asymmetric Cryptography +- 密钥分为加密密钥和解密密钥,两者不一样 +- 加密密钥一般是公开的也称为公钥 +- 解密密钥不公开,也被称为私钥 +- 公钥与私钥是一一对应的,不能单独生成 +- 公钥负责加密,私钥负责解密 + +#### RSA加密算法 + - 由罗纳德·李维斯特(Ron Rivest)、阿迪·萨莫尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)一起提出的。当时他们三人都在麻省理工学院工作。RSA就是他们三人姓氏开头字母拼在一起组成的 + + +### 数字签名 +- 用发消息的人的私钥进行签名 +- 数字签名不保证消息的机密性,只保证消息内容没有被篡改 +- 防止消息发送人否认发送消息 +- 公钥负责验签,私钥负责签名 + +### CA证书 +- 数字签名与非对称加密签名 +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/wiki/http/ca_1.png) + diff --git a/note/Joplin导出归档/计算机网络/http/网络层.md b/note/Joplin导出归档/计算机网络/http/网络层.md new file mode 100644 index 0000000..0675eb8 --- /dev/null +++ b/note/Joplin导出归档/计算机网络/http/网络层.md @@ -0,0 +1,29 @@ +### 网络层数据包(IP数据包,packet)由两部分组成,首部加数据 +### 数据:大部分是由传输层传下来的数据段(Segment) +image + +### 字段含义 +- 版本:4位,ipv4,ipv6 +- 首部长度:4位,乘4表示首部长度,最大60,固定20 + 可变部分 40 +- 总长度:16位,最大65535字节,首部加数据部分长度总和 +- 区分服务:设置标记,提高优先级,用于提高网络质量 +- 标识:数据包的ID,占16位,当数据包过大超过数据链路层MTU进行分片时,同一个数据包的所有片的标识都是一样的,每发出一个数据包,ID就+1 +- 标志:占3位,第一位保留,第二位1代表不允许分片,0代表允许分片,第三位1代表不是最后一片,0代表是最后一片 +- 片偏移:占13位,乘8代表字节偏移,可以是总共字节数量相比13位二进制扩大8倍 +- TTL:占8位,每个路由器在转发之前会将TTL减一,一旦发现TTL为0,路由器就会返回错误报告,控制数据包在网络中的生存时间,可以防止路由死循环 +image + +- 协议:占8位,表明封装的数据使用了什么协议 +image + +- 首部检验和:检查首部是否有错误 + +### 协议以及所在的层 +image + + +### ping 命令 +- ping /? 查看`ping`的用法 +- ping ip -l 数据包大小 +- ping ip -f 不允许网络层分片 +- ping ip -i TTL 设置存活时间 \ No newline at end of file diff --git a/note/K8S/学习笔记/kubectl.md b/note/K8S/学习笔记/kubectl.md new file mode 100644 index 0000000..1d4bf0c --- /dev/null +++ b/note/K8S/学习笔记/kubectl.md @@ -0,0 +1,22 @@ +### 常用命令 + +#### 1. 标签相关 + +##### 1.1 查看节点标签 + +```bash +kubectl get node --show-labels +``` + +##### 1.2 给节点打标签 + +```bash + kubectl label node vm-worker-02 ingress=true +``` + +##### 1.3 删除节点标签 + +```bash +kubectl label node vm-worker-02 ingress- +``` + diff --git a/note/K8S/实战笔记/1.27之后创建sa没有创建token.md b/note/K8S/实战笔记/1.27之后创建sa没有创建token.md new file mode 100644 index 0000000..2278e44 --- /dev/null +++ b/note/K8S/实战笔记/1.27之后创建sa没有创建token.md @@ -0,0 +1,21 @@ +### 1.创建sa + +- 不会自动创建secret + +```bash +kubectl create sa sa-demo +``` + +### 2.创建secret + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: secret-sa-demo + namespace: default + annotations: + kubernetes.io/service-account.name: sa-demo +type: kubernetes.io/service-account-token +``` + diff --git a/note/K8S/实战笔记/cron.md b/note/K8S/实战笔记/cron.md new file mode 100644 index 0000000..4315c95 --- /dev/null +++ b/note/K8S/实战笔记/cron.md @@ -0,0 +1,33 @@ +### 定时任务 + +```yaml +apiVersion: batch/v1 +kind: CronJob +metadata: + name: sync-guess-content-to-db + namespace: cron +spec: + schedule: "30 9 * * *" + jobTemplate: + spec: + template: + spec: + restartPolicy: Never + containers: + - name: sync-guess-content-to-db + image: busybox:1.36 + imagePullPolicy: IfNotPresent + command: ["/bin/sh"] + args: + - -c + - "/data/nd-feedback-offline-go -i /data/config.yaml -c SyncGuessContent2DB" + volumeMounts: + - name: data + mountPath: /data + volumes: + - name: data + hostPath: + path: /root/cron + type: Directory +``` + diff --git a/note/K8S/实战笔记/docker部署监控.md b/note/K8S/实战笔记/docker部署监控.md new file mode 100644 index 0000000..10a6a97 --- /dev/null +++ b/note/K8S/实战笔记/docker部署监控.md @@ -0,0 +1,169 @@ +### prometheus 部署 + +- 部署网络和持久化存储卷 + +```bash +docker network create monitoring +docker volume create prometheus-data +``` + +- 初始配置文件,prometheus.yml + +``` yaml +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'prometheus' + static_configs: + - targets: ['localhost:9090'] + +``` + +- 启动prometheus + +```bash +docker run -itd \ + --name prometheus \ + --network internal \ + -v /mnt/sdb3/Configs/prometheus/data:/prometheus \ + -v /mnt/sdb3/Configs/prometheus/prometheus.yml:/etc/prometheus/prometheus.yml \ + prom/prometheus:v2.45.1 \ + --web.enable-lifecycle \ + --config.file=/etc/prometheus/prometheus.yml \ + --web.external-url=/prometheus/ +``` + + + +### grafana 部署 + +- 持久化存储 + +```bash +docker volume create grafana-data +``` + +- 启动容器 + +```bash +docker run -d \ + --name grafana \ + --network internal \ + -v /mnt/sdb3/Configs/grafana/data:/var/lib/grafana \ + -v /mnt/sdb3/Configs/grafana/grafana.ini:/etc/grafana/grafana.ini \ + grafana/grafana +``` + +### Nginx 子路径 Grafana + +- 配置grafana.ini + +```bash +root_url = https://home.heysq.com:8443/grafana/ + +# Serve Grafana from subpath specified in `root_url` setting. By default it is set to `false` for compatibility reasons. +serve_from_sub_path = true +``` + +- 添加nginx配置 + +```nginx +location /grafana/ { + proxy_set_header Host $http_host; + proxy_pass http://grafana; +} + +# Proxy Grafana Live WebSocket connections. +location /grafana/api/live/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $http_host; + proxy_pass http://grafana; +} +``` + + +### Nginx 监控 + +- nginx 开启stub_status + +```nginx +server { + listen 8444; + keepalive_timeout 0; + + access_log off; + + allow 0.0.0.0/0; + deny all; + + location /stub_status { + stub_status on; + } +} +``` + +- docker 部署nginx_exporter + +```bash +docker run -itd -p 9113:9113 \ +--name=nginx_exporter \ +--network=monitoring \ +nginx/nginx-prometheus-exporter:0.11 \ +-nginx.scrape-uri=http://192.168.0.3:8444/stub_satus +``` + +### IKuai监控 + +```bash +docker run -itd \ +-p 8001:8001 \ +--name ikuai-aio \ +--restart always \ +-e HTTP_INSECURE_SKIP_VERIFY=false \ +-e HTTP_TIMEOUT=30s \ +-e TZ=Asia/Shanghai \ +-e IKUAI_ADDR=http://192.168.0.1 \ +-e IKUAI_USERNAME=admin \ +-e IKUAI_PASSWORD=Sunqi0220. \ +-e IKUAI_CRON_SKIP_START=false \ +-e IKUAI_EXPORTER_LISTEN_ADDR=0.0.0.0:8001 \ +ghcr.io/nervebing/ikuai-aio:latest +``` + +- grafana 面板 https://grafana.com/grafana/dashboards/19247-ikuai/ + +### openwrt 配置 nginx + +```bash +docker run -itd \ +--name nginx \ +--network=internal \ +--restart=always \ +-p 8443:8443 \ +-v /mnt/sdb3/Configs/nginx/conf.d:/etc/nginx/conf.d \ +-v /mnt/sdb3/Configs/nginx/ssl/:/etc/nginx/ssl \ +-v /mnt/sdb3/Configs/nginx/logs:/var/log/nginx/ \ +-v /mnt/sdb3/Configs/nginx/nginx.conf:/etc/nginx/nginx.conf \ +-v /mnt/sdb3/Configs/nginx/htpasswd_file:/etc/nginx/htpasswd_file \ +nginx:1.22 +``` + +### cAdvisor 部署 + +```bash +docker run \ + --volume=/:/rootfs:ro \ + --volume=/var/run:/var/run:ro \ + --volume=/sys:/sys:ro \ + --volume=/mnt/sdb3/:/var/lib/docker:ro \ + --network=internal \ + --detach=true \ + --name=cadvisor \ + --privileged \ + --device=/dev/kmsg \ + google/cadvisor:latest +``` + diff --git a/note/K8S/实战笔记/ingress-nginx.md b/note/K8S/实战笔记/ingress-nginx.md new file mode 100644 index 0000000..01ce310 --- /dev/null +++ b/note/K8S/实战笔记/ingress-nginx.md @@ -0,0 +1,15 @@ +### helm 方式安装 + +#### 1. 添加包管理 + +```bash +helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx +``` + +#### 2. 安装 ingress-nginx + +```bash +kubectl create ns ingress-nginx +helm install ingress-nginx +``` + diff --git a/note/K8S/实战笔记/k8s删除一直terninating.md b/note/K8S/实战笔记/k8s删除一直terninating.md new file mode 100644 index 0000000..eb9da70 --- /dev/null +++ b/note/K8S/实战笔记/k8s删除一直terninating.md @@ -0,0 +1,6 @@ +### 1. 删除 + +```sh +kubectl delete pod web-1 --grace-period=0 --force +``` + diff --git a/note/K8S/实战笔记/kubeadm-centos 部署.md b/note/K8S/实战笔记/kubeadm-centos 部署.md new file mode 100644 index 0000000..73a922b --- /dev/null +++ b/note/K8S/实战笔记/kubeadm-centos 部署.md @@ -0,0 +1,166 @@ +### 1. 关闭防火墙 + +```sh +systemctl stop firewalld +systemctl disable firewalld +``` + +### 2. 关闭selinux + +```sh +sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久 +setenforce 0 # 临时 +``` + +### 3. 关闭swap + +```sh +swapoff -a # 临时 +sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久 +``` + +### 4. 关闭完swap后,一定要重启一下虚拟机 + +### 5. 根据规划设置主机名 + +```sh +hostnamectl set-hostname +``` + +### 6. 在master添加hosts + +```sh +cat >> /etc/hosts << EOF +192.168.0.30 c-m-01 +192.168.0.35 c-w-01 +192.168.0.36 c-w-02 +EOF +``` + +### 7. 将桥接的IPv4流量传递到iptables的链 + +```sh +cat > /etc/sysctl.d/k8s.conf << EOF +net.bridge.bridge-nf-call-ip6tables = 1 +net.bridge.bridge-nf-call-iptables = 1 +EOF +sysctl --system # 生效 +``` + +### 8. 时间同步 + +- centos7 + +```sh +yum install ntpdate -y +ntpdate time.windows.com +``` + +- centos8 + +```sh +yum install -y chrony +systemctl enable chronyd --now +mv /etc/chrony.conf /etc/chrony.conf.bak +cat >> /etc/chrony.conf << EOF +server ntp.aliyun.com iburst +server cn.ntp.org.cn iburst +EOF +systemctl restart chronyd.service +chronyc sources -v +``` + +### 9. 添加软件源 + +```sh +cat > /etc/yum.repos.d/kubernetes.repo << EOF +[kubernetes] +name=Kubernetes +baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 +enabled=1 +gpgcheck=0 +repo_gpgcheck=0 +gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg +EOF +``` + +### 10. 安装启动kubelet + +```sh +yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6 +systemctl enable kubelet +``` + +### 11.配置关闭 Docker 的 cgroups,修改 /etc/docker/daemon.json + +```sh +cat > /etc/docker/daemon.json << EOF +{"exec-opts": ["native.cgroupdriver=systemd"]} +EOF +``` + +### 12. 重启 docker + +```sh +systemctl daemon-reload +systemctl restart docker +systemctl restart kubelet +``` + +### 13. 在 Master 节点下执行 + +```sh +kubeadm init \ +--apiserver-advertise-address=192.168.0.30 \ +--image-repository registry.aliyuncs.com/google_containers \ +--kubernetes-version v1.23.6 \ +--service-cidr=10.96.0.0/12 \ +--pod-network-cidr=10.244.0.0/16 +``` + +### 14. 安装成功后,复制如下配置并执行 + +```sh +mkdir -p $HOME/.kube +sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config +sudo chown $(id -u):$(id -g) $HOME/.kube/config +kubectl get nodes + + +kubeadm join 192.168.0.20:6443 --token 1rqvtc.llsvwdbyynmsccnu \ + --discovery-token-ca-cert-hash sha256:b9c12195b80ef6b8997b8275fc650ed252f64053a6e8273cbf59ec703112f6cf +``` + +### 15. 部署CNI网络插件,在 master 节点上执行 + +#### 15.1 下载 calico 配置文件,可能会网络超时 + +```sh +curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.1/manifests/calico.yaml -O +``` + +#### 15.2 修改 calico.yaml 文件中的 CALICO_IPV4POOL_CIDR 配置,修改为与初始化的 cidr 相同 + +#### 15.3 修改 IP_AUTODETECTION_METHOD 下的网卡名称(新版没有) + +#### 15.4 删除镜像 docker.io/ 前缀,避免下载过慢导致失败 + +```sh +sed -i 's#docker.io/##g' calico.yaml +``` + +### 16. 任意节点都可使用kubectl + +#### 16.1 将 master 节点中 /etc/kubernetes/admin.conf 拷贝到需要运行的服务器的 /etc/kubernetes 目录中 + +```sh +scp /etc/kubernetes/admin.conf root@vm-worker-01:/etc/kubernetes +``` + +#### 16.2 在对应的服务器上配置环境变量 + +```sh +echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile +source ~/.bash_profile +``` + diff --git a/note/K8S/实战笔记/kubepi连接k8s集群.md b/note/K8S/实战笔记/kubepi连接k8s集群.md new file mode 100644 index 0000000..cee5663 --- /dev/null +++ b/note/K8S/实战笔记/kubepi连接k8s集群.md @@ -0,0 +1,42 @@ +### 1. docker部署 kubepi + +```bash +docker run --privileged -d --name kubepi --network=internal -v /root/data/kubepi:/var/lib/kubepi -p 8180:80 --restart=unless-stopped 1panel/kubepi +``` + +### 2. 创建用户,绑定集群管理员角色 + +```bash +kubectl create sa kubepi-user -n kube-system +kubectl create clusterrolebinding kubepi-user --clusterrole=cluster-admin --serviceaccount=kube-system:kubepi-user +``` + +### 3. 获得token + +- k8s小于1.27版本自动创建secret + +```bash +kubectl -n kube-system describe secret $(kubectl -n kube-system get secret | grep kubepi-user | awk '{print $1}') | grep token: | awk '{print $2}' +``` + +- k8s大于等于1.27版本取消自动创建token,需要手动创建 + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: secret-kubepi-user + namespace: kube-system + annotations: + kubernetes.io/service-account.name: kubepi-user +type: kubernetes.io/service-account-token +``` + +- 创建secret后获取token + +```bash +kubectl describe secret secret-kubepi-user -n kube-system | grep token: | awk '{ print $2}' +``` + + + diff --git a/note/K8S/实战笔记/kuboard部署.md b/note/K8S/实战笔记/kuboard部署.md new file mode 100644 index 0000000..30d3e6a --- /dev/null +++ b/note/K8S/实战笔记/kuboard部署.md @@ -0,0 +1,29 @@ +### 1. docker部署kuboard + +```bash +sudo docker run -d \ +--restart=unless-stopped \ +--name=kuboard \ +-p 8080:80/tcp \ +-p 10081:10081/tcp \ +-e KUBOARD_ENDPOINT="http://192.168.0.3:8080" \ +-e KUBOARD_AGENT_SERVER_TCP_PORT="10081" \ +-v /root/kuboard-data:/data \ +swr.cn-east-2.myhuaweicloud.com/kuboard/kuboard:v3 +``` + +### 2. 访问集群 host:8080 + +- 用户名 admin +- 密码 Kuboard123 + +### 3. 添加集群 + +- agent方式 + + ```bash + curl -k 'http://192.168.110.3:8080/kuboard-api/cluster/pve-k8s/kind/KubernetesCluster/pve-k8s/resource/installAgentToKubernetes?token=rN9FegdHLOQ0mvfhpYLXc9Lx5E7qECM3' > kuboard-agent.yaml + kubectl apply -f ./kuboard-agent.yaml + ``` + + \ No newline at end of file diff --git a/note/K8S/实战笔记/master宕机apiserver无法启动.md b/note/K8S/实战笔记/master宕机apiserver无法启动.md new file mode 100644 index 0000000..93ef066 --- /dev/null +++ b/note/K8S/实战笔记/master宕机apiserver无法启动.md @@ -0,0 +1,6 @@ +### 1. 排查案例 + +- https://www.cnblogs.com/framework-bk/articles/16563066.html + +### 2. 实践 + diff --git a/note/K8S/实战笔记/metrics-server 部署x509 error.md b/note/K8S/实战笔记/metrics-server 部署x509 error.md new file mode 100644 index 0000000..86450cb --- /dev/null +++ b/note/K8S/实战笔记/metrics-server 部署x509 error.md @@ -0,0 +1,11 @@ +### 1. 部署高可用 metrics-server + +```sh +wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/high-availability-1.21+.yaml +``` + +### 2. 报错及修复 + +#### 2.1 x509: cannot validate certificate + +- metrics-server deployment 添加启动参数 `- --kubelet-insecure-tls` \ No newline at end of file diff --git a/note/K8S/实战笔记/minikube使用.md b/note/K8S/实战笔记/minikube使用.md new file mode 100644 index 0000000..e144946 --- /dev/null +++ b/note/K8S/实战笔记/minikube使用.md @@ -0,0 +1,14 @@ +### 使用 containerd 作为运行时 + +```bash +minikube start --cni=calico \ + --container-runtime='containerd' \ + --cpus=4 \ + --delete-on-failure=true \ + --disk-size=40g \ + --kubernetes-version='1.23.9' \ + --memory='4g' \ + --nodes=3 \ + --service-cluster-ip-range='10.96.0.0/12' +``` + diff --git a/note/K8S/实战笔记/mysql客户端.md b/note/K8S/实战笔记/mysql客户端.md new file mode 100644 index 0000000..75c8796 --- /dev/null +++ b/note/K8S/实战笔记/mysql客户端.md @@ -0,0 +1,128 @@ +### mysql-cli + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: mysql-cli + namespace: cli +spec: + selector: + matchLabels: + app: mysql-cli + replicas: 1 + serviceName: mysql-cli-svc + template: + metadata: + labels: + app: mysql-cli + spec: + containers: + - image: mysql:5.7.41 + name: mysql-cli-pod + env: + - name: MYSQL_ROOT_PASSWORD + value: Sunqi0220. + - name: LANG + value: C.UTF-8 +``` + +### redis-cli + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis-cli + namespace: cli +spec: + selector: + matchLabels: + app: redis-cli + replicas: 1 + serviceName: redis-cli-svc + template: + metadata: + labels: + app: redis-cli + spec: + containers: + - image: redis:5.0 + name: redis-cli-pod + command: ["/bin/sh"] + args: ["-c","while true;do echo ${date};sleep 3600;done"] + env: + - name: LANG + value: C.UTF-8# +``` + +### rails-cli + +```yaml +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: rails-cli + namespace: cli +spec: + selector: + matchLabels: + app: rails-cli + replicas: 1 + serviceName: rails-cli + template: + metadata: + labels: + app: rails-cli + spec: + containers: + - image: ruby:3.1.4-buster + name: rails-cli-pod + command: [ "/bin/sh", "-c"] + args: ["gem sources -r https://rubygems.org/;gem sources -a https://gems.ruby-china.com;gem sources -l;while true; do sleep 30000; done;"] + volumeMounts: + - mountPath: /data + name: rails-volume + volumes: + - name: rails-volume + hostPath: + # directory location on host + path: /root/code-server/k8s/rails_data + # this field is optional + type: Directory +``` + +### svc.yaml + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: mysql-cli-svc + namespace: cli + labels: + app: mysql-cli-svc +spec: + ports: + - port: 3306 + name: mysql + clusterIP: None + selector: + app: mysql-cli +----- +appiVersion: v1 +kind: Service +metadata: + name: redis-cli-svc + namespace: cli + labels: + app: mysql-cli-svc +spec: + ports: + - port: 6379 + name: redis + clusterIP: None + selector: + app: redis-cli +``` + diff --git a/note/K8S/实战笔记/openelb.md b/note/K8S/实战笔记/openelb.md new file mode 100644 index 0000000..e69de29 diff --git a/note/K8S/实战笔记/prometheus监控.md b/note/K8S/实战笔记/prometheus监控.md new file mode 100644 index 0000000..feabc00 --- /dev/null +++ b/note/K8S/实战笔记/prometheus监控.md @@ -0,0 +1,28 @@ +### 通过kube-prometheus安装 + +#### 1. 克隆github 仓库 + +- https://github.com/prometheus-operator/kube-prometheus + +```bash +git clone https://github.com/prometheus-operator/kube-prometheus +``` + +#### 2. 切换版本 + +- 不同版本兼容的k8s版本不一样 + +```bash +git checkout release-0.11 +``` + +#### 3. 创建资源 + +```bash +kubectl create -f manifests/setup/ +kubectl wait --for condition=Established --all CustomResourceDefinition --namespace=monitoring +kubectl apply -f manifests/ +``` + + + diff --git a/note/K8S/实战笔记/pve的lxc容器部署k3s.md b/note/K8S/实战笔记/pve的lxc容器部署k3s.md new file mode 100644 index 0000000..93a2b6d --- /dev/null +++ b/note/K8S/实战笔记/pve的lxc容器部署k3s.md @@ -0,0 +1,113 @@ +## 1. 准备工作 + +在 pve 使用 lxc安装k3s需要修改一些宿主机配置 + +### 1.1 节点内核参数开启 `bridge-nf-call-iptables` + +```bash +sysctl -w net.bridge.bridge-nf-call-iptables=1 +``` + + 这个主要是为了解决Service 同节点通信问题(启用 `bridge-nf-call-iptables` 这个内核参数 (置为 1),表示 bridge 设备在二层转发时也去调用 iptables 配置的三层规则)) + +### 1.2 关闭swap + +```bash +sysctl vm.swappiness=0 +swapoff -a +``` + +### 1.3 启用 IP 转发 + +```bash +echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf +sysctl --system +``` + + + +## 2 创建lxc容器 + +- k3s-master-01 +- k3s-slave-01 +- k3s-slave-02 +- 不要勾选无特权容器 + +### 2.1 修改pve的lxc容器配置 + +```bash +vim /etc/pve/lxc/300.conf + +添加如下内容 +cat >> /etc/pve/lxc/302.conf << EOF +lxc.apparmor.profile: unconfined +lxc.cgroup.devices.allow: a +lxc.cap.drop: +lxc.mount.auto: "proc:rw sys:rw" +EOF +``` + +### 2.2 lxc容器修改 + +- touch /etc/rc.local +- 填入以下内容 + +```bash + +#!/bin/sh -e + +# Kubeadm 1.15 needs /dev/kmsg to be there, but it's not in lxc, but we can just use /dev/console instead +# see: https://github.com/kubernetes-sigs/kind/issues/662 +if [ ! -e /dev/kmsg ]; then + ln -s /dev/console /dev/kmsg +fi + +# https://medium.com/@kvaps/run-kubernetes-in-lxc-container-f04aa94b6c9c +mount --make-rshared / +``` + +- chmod +x /etc/rc.local + +```bash +cat >> /etc/rc.local << EOF +#!/bin/sh -e + +# Kubeadm 1.15 needs /dev/kmsg to be there, but it's not in lxc, but we can just use /dev/console instead +# see: https://github.com/kubernetes-sigs/kind/issues/662 +if [ ! -e /dev/kmsg ]; then + ln -s /dev/console /dev/kmsg +fi + +# https://medium.com/@kvaps/run-kubernetes-in-lxc-container-f04aa94b6c9c +mount --make-rshared / +EOF +``` + +### 2.3 lxc换软件源安装curl(可选) + +```bash +sed -i 's@//.*archive.ubuntu.com@//mirrors.ustc.edu.cn@g' /etc/apt/sources.list +apt update -y +apt install curl -y +``` + +## 3安装k3s + +### 3.1主节点执行 + +```bash +curl -sfL https://get.k3s.io | INSTALL_K3S_VERSION=v1.27.13+k3s1 sh - +``` + +### 3.2 查看集群令牌 + +```bash +cat /var/lib/rancher/k3s/server/node-token +``` + +### 3.3 worker节点执行 + +```bash +curl -fsL https://get.k3s.io | INSTALL_K3S_VERSION=v1.28.12+k3s1 K3S_URL=https://192.168.0.20:6443 K3S_TOKEN="xxxxx" sh -s - --node-name k3s-slave-01 +``` + diff --git a/note/K8S/实战笔记/sealos部署k8s.md b/note/K8S/实战笔记/sealos部署k8s.md new file mode 100644 index 0000000..6e8eb3c --- /dev/null +++ b/note/K8S/实战笔记/sealos部署k8s.md @@ -0,0 +1,48 @@ +### 1. 安装sealos + +- [官网](https://www.sealyun.com/zh-Hans/docs/lifecycle-management/quick-start/installation) + +```bash +wget https://github.com/labring/sealos/releases/download/v4.2.0/sealos_4.2.0_linux_amd64.tar.gz && tar zxvf sealos_4.2.0_linux_amd64.tar.gz sealos && chmod +x sealos && mv sealos /usr/bin +``` + +### 2. 部署单节点k8s + +- [官网](https://www.sealyun.com/zh-Hans/docs/lifecycle-management/quick-start/) + +```bash +# sealos version must >= v4.1.0 +$ sealos run labring/kubernetes:v1.27.13 labring/helm:v3.8.2 labring/calico:v3.24.1 --single +``` + +### 3. 部署多节点k8s + +```bash +sealos run labring/kubernetes:v1.27.4 labring/helm:v3.8.2 labring/calico:v3.24.1 \ + --masters 192.168.110.20 \ + --nodes 192.168.110.25,192.168.110.26,192.168.110.27 -p Sunqi0220. +``` + +```bash +# 阿里云镜像 +sealos run registry.cn-hangzhou.aliyuncs.com/idevops/kubernetes:v1.27.4 \ +registry.cn-hangzhou.aliyuncs.com/idevops/helm:v3.8.2 \ +registry.cn-hangzhou.aliyuncs.com/idevops/calico:v3.24.1 \ +--masters 192.168.0.20 \ +--nodes 192.168.0.25,192.168.0.26 -p Sunqi0220. +``` + + + +### 4. 添加master节点 + +```bash +sealos add --masters 192.168.110.21,192.168.110.22 +``` + +### 5. 添加worker节点 + +```bash +sealos add --nodes 192.168.110.28,192.168.110.29 +``` + diff --git a/note/K8S/实战笔记/删除一直terminating的pod.md b/note/K8S/实战笔记/删除一直terminating的pod.md new file mode 100644 index 0000000..5ffd515 --- /dev/null +++ b/note/K8S/实战笔记/删除一直terminating的pod.md @@ -0,0 +1,5 @@ +### 1. 删除 + +```bash +kubectl delete pod web-1 --grace-period=0 --force +``` \ No newline at end of file diff --git a/note/K8S/实战笔记/动态制备pv-nfs子文件夹.md b/note/K8S/实战笔记/动态制备pv-nfs子文件夹.md new file mode 100644 index 0000000..a3d2ba7 --- /dev/null +++ b/note/K8S/实战笔记/动态制备pv-nfs子文件夹.md @@ -0,0 +1,105 @@ +### 一、Github仓库手动安装 + +- github 仓库 https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner + +#### 1. clone 仓库 + +```bash +git clone https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner +cd nfs-subdir-external-provisioner/deploy +``` + +#### 2. 修改deployment和rbac + +- vim deployment.yaml + +![image-20231028145130015](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20231028145130015.png) + +- vim rbac.yaml 替换namespace为你想部署pod的空间 + +![image-20231028145243771](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20231028145243771.png) + +- 执行以下命令 + +```bash +kubectl apply -f rbac.yaml +kubectl apply -f deployment.yaml +``` + +#### 3. 创建存储类 + +```bash +kubectl apply -f class.yaml +``` + +![image-20231028145331651](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20231028145331651.png) + +#### 4. 部署pvc和pod + +```bash +kubectl apply -f test-claim.yaml +kubectl apply -f test-pod.yaml +``` + +#### 5. pod的存储卷生命和pvc声明组合 + +```yaml +--- +apiVersion: v1 +kind: Service +metadata: + name: nginx + labels: + app: nginx +spec: + ports: + - port: 80 + name: web + clusterIP: None + selector: + app: nginx +--- +apiVersion: apps/v1 +kind: StatefulSet # StatefulSet 类型的资源 +metadata: + name: web # StatefulSet 对象的名字 +spec: + serviceName: "nginx" # 使用哪个 service 来管理 dns + replicas: 2 + selector: + matchLabels: + app: nginx + template: + metadata: + labels: + app: nginx + spec: + containers: + - name: nginx + image: nginx:1.14 + ports: # 容器内部要暴露的端口 + - containerPort: 80 # 具体暴露的端口号 + name: web # 该端口配置的名字 + volumeMounts: + - mountPath: /usr/share/nginx/html + name: nginx-test-pvc + + volumeClaimTemplates: + - metadata: + name: nginx-test-pvc + spec: + storageClassName: nfs-client + accessModes: + - ReadWriteMany + resources: + requests: + storage: 1Gi +``` + +### 二、helm 安装 + +```bash +helm repo add nfs-subdir-external-provisioner https://kubernetes-sigs.github.io/nfs-subdir-external-provisioner/ +helm install nfs-subdir-external-provisioner nfs-subdir-external-provisioner/nfs-subdir-external-provisioner --set nfs.server=192.168.0.12 --set nfs.path=/volume1/share +``` + diff --git a/note/K8S/实战笔记/单独部署grafana.md b/note/K8S/实战笔记/单独部署grafana.md new file mode 100755 index 0000000..e69de29 diff --git a/note/K8S/实战笔记/开源loadblancer.md b/note/K8S/实战笔记/开源loadblancer.md new file mode 100644 index 0000000..1fc8763 --- /dev/null +++ b/note/K8S/实战笔记/开源loadblancer.md @@ -0,0 +1,29 @@ +### 1. OpenELB + + + +### 2. MetalLB + +#### 2.1 部署 + +```bash +wget https://raw.githubusercontent.com/metallb/metallb/v0.13.12/config/manifests/metallb-native.yaml +kubectl apply -f metallb-native.yaml +``` + +#### 2.2 创建ip池 + +- metallb-ip.yaml +- kubectl apply -f metallb-ip.yaml + +```yaml +apiVersion: metallb.io/v1beta1 +kind: IPAddressPool +metadata: + name: first-pool + namespace: metallb-system +spec: + addresses: + - 192.168.0.40-192.168.0.50 +``` + diff --git a/note/K8S/实战笔记/镜像仓库+web.md b/note/K8S/实战笔记/镜像仓库+web.md new file mode 100644 index 0000000..79dfd5a --- /dev/null +++ b/note/K8S/实战笔记/镜像仓库+web.md @@ -0,0 +1,79 @@ +### 1. 私有镜像仓库部署 + +#### 1.1 拉取镜像 + +```bash +docker pull registry +``` + +#### 1.2 设置https + +##### 1.2.1 创建证书 + +```bash +openssl req -newkey rsa:4096 -nodes -sha256 -keyout /opt/registry/certs/registry.key -x509 -days 365 -out /opt/registry/certs/registry.crt +``` + +##### 1.2.2 创建密码文件 + +```bash +htpasswd -Bbn Securitit 123456 > /opt/registry/auth/htpasswd +``` + +##### 1.2.3 创建registry config 文件 + +```yaml +version: 0.1 +log: + fields: + service: registry +storage: + cache: + blobdescriptor: inmemory + filesystem: + rootdirectory: /var/lib/registry +http: + addr: :5000 + headers: + X-Content-Type-Options: [nosniff] + Access-Control-Allow-Origin: ['*'] + Access-Control-Allow-Methods: ['*'] + Access-Control-Max-Age: [1728000] +health: + storagedriver: + enabled: true + interval: 10s + threshold: 3 +``` + +##### 1.2.4 使用ssl 启动容器 + +```bash +docker run -d \ +--name registry \ +-p 5000:5000 \ +--restart=always \ +--privileged=true \ +-e "REGISTRY_AUTH=htpasswd" \ +-e "REGISTRY_AUTH_HTPASSWD_REALM=Registry Realm" \ +-v /opt/registry/auth/:/opt/registry/auth \ +-e "REGISTRY_AUTH_HTPASSWD_PATH=/opt/registry/auth/htpasswd" \ +-v /opt/registry/certs/:/opt/registry/certs \ +-v /opt/registry/config/config.yml:/etc/docker/registry/config.yml \ +-e REGISTRY_HTTP_TLS_CERTIFICATE=/opt/registry/certs/registry.crt \ +-e REGISTRY_HTTP_TLS_KEY=/opt/registry/certs/registry.key \ +registry +``` + +### 2. 镜像仓库web管理 + +```bash +docker run -d -p 8080:80 \ +--name registry-ui \ +--restart=always \ +--privileged=true \ +-e "REGISTRY_TITLE=Sunqi Docker" \ +-e "REGISTRY_URL=https://192.168.0.3:5000" \ +joxit/docker-registry-ui:latest +``` + diff --git a/note/Python/内置函数.md b/note/Python/内置函数.md new file mode 100644 index 0000000..b994fec --- /dev/null +++ b/note/Python/内置函数.md @@ -0,0 +1,144 @@ +### 和序列相关的内置函数 + +- list +- tuple +- set +- dict +- string + +| 函数 | 功能 | +| :---------- | :----------------------------------------------------------- | +| len() | 计算序列的长度,即返回序列中包含多少个元素。 | +| max() | 找出序列中的最大元素。注意,对序列使用 sum() 函数时,做加和操作的必须都是数字,不能是字符或字符串,否则该函数将抛出异常,因为解释器无法判定是要做连接操作(+ 运算符可以连接两个序列),还是做加和操作。 | +| min() | 找出序列中的最小元素。 | +| list() | 将序列转换为列表。 | +| str() | 将序列转换为字符串。 | +| sum() | 计算元素和。 | +| sorted() | 对元素进行排序。 | +| reversed() | 反向序列中的元素。 | +| enumerate() | 将序列组合为一个索引序列,多用在 for 循环中。 | + +```python +citys = ["jinan", "qingdao", "yantai", "zibo"] +for city in enumerate(citys): + print(city) +# 输出: +(0, 'jinan') +(1, 'qingdao') +(2, 'yantai') +(3, 'zibo') +``` + +```python +citys = ["jinan", "qingdao", "yantai", "zibo"] +for index,city in enumerate(citys): + print(index,city) +# 输出: +0 jinan +1 qingdao +2 yantai +3 zibo +``` + + + +### 字符串相关内置函数 + +| 序号 | 方法及描述 | +| :--- | :----------------------------------------------------------- | +| 1 | [capitalize()](https://www.w3cschool.cn/python3/python3-string-capitalize.html) 将字符串的第一个字符转换为大写 | +| 2 | [center(width, fillchar)](https://www.w3cschool.cn/python3/python3-string-center.html)返回一个指定的宽度 width 居中的字符串,fillchar 为填充的字符,默认为空格。 | +| 3 | [count(str, beg= 0,end=len(string))](https://www.w3cschool.cn/python3/python3-string-count.html)返回 str 在 string 里面出现的次数,如果指定 beg 或者 end,则返回指定范围内 str 出现的次数 | +| 4 | [bytes.decode(encoding="utf-8", errors="strict")](https://www.w3cschool.cn/python3/python3-string-decode.html)Python3 中没有 decode 方法,但我们可以使用 bytes 对象的 decode() 方法来解码给定的 bytes 对象,这个 bytes 对象可以由 str.encode() 来编码返回。 | +| 5 | [encode(encoding='UTF-8',errors='strict')](https://www.w3cschool.cn/python3/python3-string-encode.html)以 encoding 指定的编码格式编码字符串,如果出错默认报`ValueError`异常,除非 errors 指定的是'ignore'或者'replace' | +| 6 | [endswith(suffix, beg=0, end=len(string))](https://www.w3cschool.cn/python3/python3-string-endswith.html) 检查字符串是否以指定的字符串结束,如果指定了beg 或 end 则检查指定的范围内是否以指定的字符串结束,如果是,返回 True,否则返回 False. | +| 7 | [expandtabs(tabsize=8)](https://www.w3cschool.cn/python3/python3-string-expandtabs.html)把字符串 string 中的 tab 符号转为空格,tab 符号默认的空格数是 8 。 | +| 8 | [find(str, beg=0, end=len(string))](https://www.w3cschool.cn/python3/python3-string-find.html)检测 str 是否包含在字符串中,如果指定范围 beg 和 end ,则检查是否包含在指定范围内,如果包含,返回开始的索引值,否则返回-1 | +| 9 | [index(str, beg=0, end=len(string))](https://www.w3cschool.cn/python3/python3-string-index.html)跟find()方法一样,只不过如果str不在字符串中会报一个异常。 | +| 10 | [isalnum()](https://www.w3cschool.cn/python3/python3-string-isalnum.html)如果字符串至少有一个字符并且所有字符都是字母或数字则返回 True,否则返回 False | +| 11 | [isalpha()](https://www.w3cschool.cn/python3/python3-string-isalpha.html)如果字符串至少有一个字符并且所有字符都是字母或中文字则返回 True, 否则返回 False | +| 12 | [isdigit()](https://www.w3cschool.cn/python3/python3-string-isdigit.html)如果字符串只包含数字则返回 True 否则返回 False | +| 13 | [islower()](https://www.w3cschool.cn/python3/python3-string-islower.html)如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是小写,则返回 True,否则返回 False | +| 14 | [isnumeric()](https://www.w3cschool.cn/python3/python3-string-isnumeric.html)如果字符串中只包含数字字符,则返回 True,否则返回 False | +| 15 | [isspace()](https://www.w3cschool.cn/python3/python3-string-isspace.html)如果字符串中只包含空白,则返回 True,否则返回 False. | +| 16 | [istitle()](https://www.w3cschool.cn/python3/python3-string-istitle.html)如果字符串中所有的单词拼写首字母是否为大写,且其他字母为小写则返回 True,否则返回 False | +| 17 | [isupper()](https://www.w3cschool.cn/python3/python3-string-isupper.html)如果字符串中包含至少一个区分大小写的字符,并且所有这些(区分大小写的)字符都是大写,则返回 True,否则返回 False | +| 18 | [join(seq)](https://www.w3cschool.cn/python3/python3-string-join.html)以指定字符串作为分隔符,将 seq 中所有的元素(的字符串表示)合并为一个新的字符串 | +| 19 | [len(string)](https://www.w3cschool.cn/python3/python3-string-len.html)返回字符串长度 | +| 20 | [ljust(width, fillchar)](https://www.w3cschool.cn/python3/python3-string-ljust.html)返回一个原字符串左对齐,并使用 fillchar 填充至长度 width 的新字符串,fillchar 默认为空格。 | +| 21 | [lower()](https://www.w3cschool.cn/python3/python3-string-lower.html)转换字符串中所有大写字符为小写. | +| 22 | [lstrip()](https://www.w3cschool.cn/python3/python3-string-lstrip.html)截掉字符串左边的空格或指定字符。 | +| 23 | [maketrans()](https://www.w3cschool.cn/python3/python3-string-maketrans.html)创建字符映射的转换表,对于接受两个参数的最简单的调用方式,第一个参数是字符串,表示需要转换的字符,第二个参数也是字符串表示转换的目标。 | +| 24 | [max(str)](https://www.w3cschool.cn/python3/python3-string-max.html)返回字符串 str 中最大的字母。 | +| 25 | [min(str)](https://www.w3cschool.cn/python3/python3-string-min.html)返回字符串 str 中最小的字母。 | +| 26 | [replace(old, new , max)](https://www.w3cschool.cn/python3/python3-string-replace.html)把 将字符串中的 old 替换成 new,如果 max 指定,则替换不超过 max 次。 | +| 27 | [rfind(str, beg=0,end=len(string))](https://www.w3cschool.cn/python3/python3-string-rfind.html)类似于 find()函数,不过是从右边开始查找. | +| 28 | [rindex( str, beg=0, end=len(string))](https://www.w3cschool.cn/python3/python3-string-rindex.html)类似于 index(),不过是从右边开始. | +| 29 | [rjust(width,[, fillchar\])](https://www.w3cschool.cn/python3/python3-string-rjust.html)返回一个原字符串右对齐,并使用fillchar(默认空格)填充至长度 width 的新字符串 | +| 30 | [rstrip()](https://www.w3cschool.cn/python3/python3-string-rstrip.html)删除字符串末尾的空格或指定字符。 | +| 31 | [split(str="", num=string.count(str))](https://www.w3cschool.cn/python3/python3-string-split.html)以 str 为分隔符截取字符串,如果 num 有指定值,则仅截取 num+1 个子字符串 | +| 32 | [splitlines([keepends\])](https://www.w3cschool.cn/python3/python3-string-splitlines.html)按照行('\r', '\r\n', \n')分隔,返回一个包含各行作为元素的列表,如果参数 keepends 为 False,不包含换行符,如果为 True,则保留换行符。 | +| 33 | [startswith(substr, beg=0,end=len(string))](https://www.w3cschool.cn/python3/python3-string-startswith.html)检查字符串是否是以指定子字符串 substr 开头,是则返回 True,否则返回 False。如果beg 和 end 指定值,则在指定范围内检查。 | +| 34 | [strip([chars\])](https://www.w3cschool.cn/python3/python3-string-strip.html)在字符串上执行 lstrip()和 rstrip() | +| 35 | [swapcase()](https://www.w3cschool.cn/python3/python3-string-swapcase.html)将字符串中大写转换为小写,小写转换为大写 | +| 36 | [title()](https://www.w3cschool.cn/python3/python3-string-title.html)返回"标题化"的字符串,就是说所有单词都是以大写开始,其余字母均为小写(见 istitle()) | +| 37 | [translate(table, deletechars="")](https://www.w3cschool.cn/python3/python3-string-translate.html)根据 str 给出的表(包含 256 个字符)转换 string 的字符, 要过滤掉的字符放到 deletechars 参数中 | +| 38 | [upper()](https://www.w3cschool.cn/python3/python3-string-upper.html)转换字符串中的小写字母为大写 | +| 39 | [zfill (width)](https://www.w3cschool.cn/python3/python3-string-zfill.html)返回长度为 width 的字符串,原字符串右对齐,前面填充0 | +| 40 | [isdecimal()](https://www.w3cschool.cn/python3/python3-string-isdecimal.html)检查字符串是否只包含十进制字符,如果是返回 true,否则返回 false。 | + + + +### 列表list相关内置函数 + +| 方法 | 描述 | +| :----------------------------------------------------------- | :--------------------------------------------------- | +| [append()](https://www.w3school.com.cn/python/ref_list_append.asp) | 在列表的末尾添加一个元素 | +| [clear()](https://www.w3school.com.cn/python/ref_list_clear.asp) | 删除列表中的所有元素 | +| [copy()](https://www.w3school.com.cn/python/ref_list_copy.asp) | 返回列表的副本 | +| [count()](https://www.w3school.com.cn/python/ref_list_count.asp) | 返回具有指定值的元素数量。 | +| [extend()](https://www.w3school.com.cn/python/ref_list_extend.asp) | 将列表元素(或任何可迭代的元素)添加到当前列表的末尾 | +| [index()](https://www.w3school.com.cn/python/ref_list_index.asp) | 返回具有指定值的第一个元素的索引 | +| [insert()](https://www.w3school.com.cn/python/ref_list_insert.asp) | 在指定位置添加元素 | +| [pop()](https://www.w3school.com.cn/python/ref_list_pop.asp) | 删除指定位置的元素 | +| [remove()](https://www.w3school.com.cn/python/ref_list_remove.asp) | 删除具有指定值的项目 | +| [reverse()](https://www.w3school.com.cn/python/ref_list_reverse.asp) | 颠倒列表的顺序 | +| [sort()](https://www.w3school.com.cn/python/ref_list_sort.asp) | 对列表进行排序 | + +### 集合set相关内置函数 + +| 方法 | 描述 | +| :----------------------------------------------------------- | :------------------------------------------- | +| [add()](https://www.w3school.com.cn/python/ref_set_add.asp) | 向集合添加元素。 | +| [clear()](https://www.w3school.com.cn/python/ref_set_clear.asp) | 删除集合中的所有元素。 | +| [copy()](https://www.w3school.com.cn/python/ref_set_copy.asp) | 返回集合的副本。 | +| [difference()](https://www.w3school.com.cn/python/ref_set_difference.asp) | 返回包含两个或更多集合之间差异的集合。 | +| [difference_update()](https://www.w3school.com.cn/python/ref_set_difference_update.asp) | 删除此集合中也包含在另一个指定集合中的项目。 | +| [discard()](https://www.w3school.com.cn/python/ref_set_discard.asp) | 删除指定项目。 | +| [intersection()](https://www.w3school.com.cn/python/ref_set_intersection.asp) | 返回为两个其他集合的交集的集合。 | +| [intersection_update()](https://www.w3school.com.cn/python/ref_set_intersection_update.asp) | 删除此集合中不存在于其他指定集合中的项目。 | +| [isdisjoint()](https://www.w3school.com.cn/python/ref_set_isdisjoint.asp) | 返回两个集合是否有交集。 | +| [issubset()](https://www.w3school.com.cn/python/ref_set_issubset.asp) | 返回另一个集合是否包含此集合。 | +| [issuperset()](https://www.w3school.com.cn/python/ref_set_issuperset.asp) | 返回此集合是否包含另一个集合。 | +| [pop()](https://www.w3school.com.cn/python/ref_set_pop.asp) | 从集合中删除一个元素。 | +| [remove()](https://www.w3school.com.cn/python/ref_set_remove.asp) | 删除指定元素。 | +| [symmetric_difference()](https://www.w3school.com.cn/python/ref_set_symmetric_difference.asp) | 返回具有两组集合的对称差集的集合。 | +| [symmetric_difference_update()](https://www.w3school.com.cn/python/ref_set_symmetric_difference_update.asp) | 插入此集合和另一个集合的对称差集。 | +| [union()](https://www.w3school.com.cn/python/ref_set_union.asp) | 返回包含集合并集的集合。 | +| [update()](https://www.w3school.com.cn/python/ref_set_update.asp) | 用此集合和其他集合的并集来更新集合。 | + +### 字典dict相关内置函数 + +| 方法 | 描述 | +| :----------------------------------------------------------- | :----------------------------------------------------- | +| [clear()](https://www.w3school.com.cn/python/ref_dictionary_clear.asp) | 删除字典中的所有元素 | +| [copy()](https://www.w3school.com.cn/python/ref_dictionary_copy.asp) | 返回字典的副本 | +| [fromkeys()](https://www.w3school.com.cn/python/ref_dictionary_fromkeys.asp) | 返回拥有指定键和值的字典 | +| [get()](https://www.w3school.com.cn/python/ref_dictionary_get.asp) | 返回指定键的值 | +| [items()](https://www.w3school.com.cn/python/ref_dictionary_items.asp) | 返回包含每个键值对的元组的列表 | +| [keys()](https://www.w3school.com.cn/python/ref_dictionary_keys.asp) | 返回包含字典键的列表 | +| [pop()](https://www.w3school.com.cn/python/ref_dictionary_pop.asp) | 删除拥有指定键的元素 | +| [popitem()](https://www.w3school.com.cn/python/ref_dictionary_popitem.asp) | 删除最后插入的键值对 | +| [setdefault()](https://www.w3school.com.cn/python/ref_dictionary_setdefault.asp) | 返回指定键的值。如果该键不存在,则插入具有指定值的键。 | +| [update()](https://www.w3school.com.cn/python/ref_dictionary_update.asp) | 使用指定的键值对字典进行更新 | +| [values()](https://www.w3school.com.cn/python/ref_dictionary_values.asp) | 返回字典中所有值的列表 | diff --git a/note/Python/函数.md b/note/Python/函数.md new file mode 100644 index 0000000..0068222 --- /dev/null +++ b/note/Python/函数.md @@ -0,0 +1,94 @@ +### 函数定义与参数 + +- def定义 + +```python +def my_function(fname): + print(fname + " Gates") + +my_function("Bill") +my_function("Steve") +my_function("Elon") +``` + +- 默认参数 + +```python +def my_function(country = "China"): + print("I am from " + country) + +my_function() +my_function("Brazil") +``` + +- 关键字参数:使用key=value形式传递参数,顺序不严格要求 + +```python +def my_function(child3, child2, child1): + print("The youngest child is " + child3) + +my_function(child1 = "Phoebe", child2 = "Jennifer", child3 = "Rory") +``` + +- 任意参数:相当于多个参数元组 + +```python +def my_function(*kids): + print("The youngest child is " + kids[2]) + +my_function("Phoebe", "Jennifer", "Rory") +``` + +- pass语句 + +```python +def myfunction: + pass +``` + +### 函数返回值 return + +- 返回两个参数,分别接收 + +```python +>>> def myfunc(): +... return 1,2 +... +>>> a, b = myfunc() +>>> a +1 +>>> b +2 +>>> +``` + +- 返回两个结果,一个变量接收 + +```python +>>> def myfunc(): +... return 1,2 +... +>>> a= myfunc() +>>> a +(1, 2) +>>> +``` + +- 返回值元组形式,多个变量接收,接收变量数超出返回值数量会报错 + +```python +>>> def myfunc(): +... return (1,2) +... +>>> a, b = myfunc() +>>> a +1 +>>> b +2 +>>> a, b, c = myfunc() +Traceback (most recent call last): + File "", line 1, in +ValueError: not enough values to unpack (expected 3, got 2) +>>> +``` + diff --git a/note/Python/字符串格式化.md b/note/Python/字符串格式化.md new file mode 100644 index 0000000..f32931d --- /dev/null +++ b/note/Python/字符串格式化.md @@ -0,0 +1,140 @@ +### 占位符 + +| 符 号 | 描述 | +| :----: | :----------------------------------: | +| `%c` | 格式化字符及其ASCII码 | +| ` %s` | 格式化字符串 | +| `%d` | 格式化整型 | +| `%u` | 格式化无符号整型 | +| ` %o` | 格式化无符号八进制数 | +| `%x` | 格式化无符号十六进制数 | +| `%X` | 格式化无符号十六进制数(大写) | +| ` %f` | 格式化浮点数字,可指定小数点后的精度 | +| ` %e` | 用科学计数法格式化浮点数 | +| `%E` | 作用同`%e`,用科学计数法格式化浮点数 | +| `%g` | `%f`和`%e`的简写 | +| `%G` | `%f `和` %E` 的简写 | +| `%p` | 用十六进制数格式化变量的地址 | + +### 格式化辅助指令 + +| 符号 | 功能 | +| :---: | :----------------------------------------------------------: | +| * | 定义宽度或者小数点精度 | +| - | 用做左对齐 | +| + | 在正数前面显示加号( + ) | +| | 在正数前面显示空格 | +| # | 在八进制数前面显示零('0'),在十六进制前面显示'0x'或者'0X'(取决于用的是'x'还是'X') | +| 0 | 显示的数字前面填充'0'而不是默认的空格 | +| % | '%%'输出一个单一的'%' | +| (var) | 映射变量(字典参数) | +| m.n. | m 是显示的最小总宽度,n 是小数点后的位数(如果可用的话) | + + + +### str.format + +- 通过 {} 和 : 来代替以前的 % +- 用大括号来转义大括号 + +```python +>>> print ("{} 对应的位置是 {{0}}".format("school")) +school 对应的位置是 {0} +>>> +``` + +- format 函数可以接受不限个参数,位置可以不按顺序 + +```python +>>>"{} {}".format("hello", "world") # 不设置指定位置,按默认顺序 +'hello world' +>>> "{0} {1}".format("hello", "world") # 设置指定位置 +'hello world' +>>> "{1} {0} {1}".format("hello", "world") # 设置指定位置 +'world hello world' +``` + +- 为format设置参数 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- +print("网站名:{name}, 地址 {url}".format(name="w3cschool教程", url="www.w3cschool.cn")) +# 通过字典设置参数 +site = {"name": "w3cschool教程", "url": "www.w3cschool.cn"} +print("网站名:{name}, 地址 {url}".format(**site)) +site = {"name": "w3cschool教程", "url": "www.w3cschool.cn"} +print("网站名:{0[name]}, 地址 {0[url]}".format(site)) +# 通过列表索引设置参数 +my_list = ['w3cschool教程', 'www.w3cschool.cn'] +print("网站名:{0[0]}, 地址 {0[1]}".format(my_list)) # "0" 是必须的 +``` + +- 向str.format传递对象,利用对象的属性进行格式化 + +```python +#!/usr/bin/python +# -*- coding: UTF-8 -*- +class AssignValue(object): + def __init__(self, value): + self.value = value + +my_value = AssignValue(6) +print('value 为: {0.value}'.format(my_value)) # "0" 是可选的 +print('value 为: {.value}'.format(my_value)) +``` + + + +### f-string格式化 + +- f-string 格式化字符串以 f 开头,后面跟着字符串,字符串中的表达式用大括号 {} 包起来,它会将变量或表达式计算后的值替换进去 + +```python +>>> name = 'W3Cschool' +>>> print(f'Hello {name}') # 替换变量 +Hello W3Cschool +>>> +>>> print(f'{1+2}') # 使用表达式 +3 +>>> +>>> w = {'name': 'W3Cschool', 'url': 'www.w3cschool.cn'} +>>> print(f'{w["name"]}: {w["url"]}') +W3Cschool: www.w3cschool.cn +>>> +``` + +- 在 Python 3.8 的版本中可以使用 = 符号来拼接运算表达式与结果 + +```python +>>> x = 1 +>>> print(f'{x+1=}') # Python 3.8 +x+1=2 +>>> +``` + + + +### 数字格式化 + +| 数字 | 格式 | 输出 | 描述 | +| :--------- | :----------------------------------------------------------- | :--------------------------------------- | :--------------------------- | +| 3.1415926 | {:.2f} | 3.14 | 保留小数点后两位 | +| 3.1415926 | {:+.2f} | +3.14 | 带符号保留小数点后两位 | +| -1 | {:+.2f} | -1.00 | 带符号保留小数点后两位 | +| 2.71828 | {:.0f} | 3 | 不带小数 | +| 5 | {:0>2d} | 05 | 数字补零 (填充左边, 宽度为2) | +| 5 | {:x<4d} | 5xxx | 数字补x (填充右边, 宽度为4) | +| 10 | {:x<4d} | 10xx | 数字补x (填充右边, 宽度为4) | +| 1000000 | {:,} | 1,000,000 | 以逗号分隔的数字格式 | +| 0.25 | {:.2%} | 25.00% | 百分比格式 | +| 1000000000 | {:.2e} | 1.00e+09 | 指数记法 | +| 13 | {:10d} | 13 | 右对齐 (默认, 宽度为10) | +| 13 | {:<10d} | 13 | 左对齐 (宽度为10) | +| 13 | {:^10d} | 13 | 中间对齐 (宽度为10) | +| 11 | '{:b}'.format(11)
'{:d}'.format(11)
'{:o}'.format(11)
'{:x}'.format(11)
'{:#x}'.format(11)
'{:#X}'.format(11) | 1011
11
13
b
0xb
0XB | 二进制 | + +- ^, <, > 分别是居中、左对齐、右对齐,后面带宽度 +- : 号后面带填充的字符,只能是一个字符,不指定则默认是用空格填充 +- \+ 表示在正数前显示 +,负数前显示 -; (空格)表示在正数前加空格 +- b、d、o、x 分别是二进制、十进制、八进制、十六进制 \ No newline at end of file diff --git a/note/Python/控制语句.md b/note/Python/控制语句.md new file mode 100644 index 0000000..0055e87 --- /dev/null +++ b/note/Python/控制语句.md @@ -0,0 +1,146 @@ +### 条件语句 + +- 正常写法 + +```python +a = 20 +b = 10 +if a==b: + print("a == b") +elif a < b: + print("a < b") +else: + pring("a > b") +``` + +- 简写一行if + +```python +a = 20 +b = 10 +if a > b: print("a > b") +``` + +- 简写一行if else + +```python +a = 20 +b = 10 +print("A") if a > b else print("B") +``` + +- pass 语句,空if处理 + +```python +a = 66 +b = 200 + +if b > a: + pass +``` + + + +### and or + +```python +a = 200 +b = 66 +c = 500 +if a > b and c > a: + print("Both conditions are True") + +a = 200 +b = 66 +c = 500 +if a > b or a > c: + print("At least one of the conditions is True") +``` + + + +### while 循环 + +- break 终止循环 +- continue 开始下一次循环 + +```python +i = 1 +while i < 7: + print(i) + if i == 3: + break + i += 1 + +``` + +```python +i = 0 +while i < 7: + i += 1 + if i == 3: + continue + print(i) +``` + +- else语句,条件不成立,执行else语句 + +```python +i = 1 +while i < 6: + print(i) + i += 1 +else: + print("i is no longer less than 6") +``` + + + +### for 循环 + +- break +- continue + +```python +fruits = ["apple", "banana", "cherry"] +for x in fruits: + if x == "banana": + continue + print(x) + +fruits = ["apple", "banana", "cherry"] +for x in fruits: + print(x) + if x == "banana": + break +``` + +- range() 函数:返回一个数字序列,默认情况下从 0 开始,并递增 1(默认地),并以指定的数字结束 + +```python +for x in range(10): + print(x) + +for x in range(3, 10): + print(x) + +for x in range(3, 50, 6): + print(x) +``` + +- else for循环不满足条件时执行else语句 + +```python +for x in range(10): + print(x) +else: + print("Finally finished!") +``` + +- pass语句 + +```python +for x in [0, 1, 2]: + pass +``` + diff --git a/note/Python/数学函数.md b/note/Python/数学函数.md new file mode 100644 index 0000000..eebd440 --- /dev/null +++ b/note/Python/数学函数.md @@ -0,0 +1,48 @@ +### 数学运算函数 + +| 函数 | 返回值 ( 描述 ) | +| :----------------------------------------------------------- | :----------------------------------------------------------- | +| [abs(x)](https://www.w3cschool.cn/python3/python3-func-number-abs.html) | 返回数字的绝对值,如abs(-10) 返回 10 | +| [ceil(x)](https://www.w3cschool.cn/python3/python3-func-number-ceil.html) | 返回数字的上入整数,如math.ceil(4.1) 返回 5 | +| cmp(x, y) | 如果 x < y 返回 -1, 如果 x == y 返回 0, 如果 x > y 返回 1。 **Python 3 已废弃,使用 (x>y)-(x 可变数据和不可变数据是相对于引用地址来说的。 +> +> 不可变数据类型不允许变量的值发生变化,如果改变了变量的值,相当于新建了一个对象,而对于具有相同的值的对象,内部会有一个引用计数来记录有多少个变量引用了这个对象。 +> +> 可变数据类型允许变量的值发生变化。对变量进行修改操作只会改变变量的值,不会新建对象,变量引用的地址也不会发生变化,不过对于相同的值的不同对象,在内存中则会存在不同的对象,即每个对象都有自己的地址,相当于内存中对于同值的对象保存了多份,不存在引用计数,是实实在在的对象。 +> +> 简单地讲,可变数据和不可变数据的“变”是相对于引用地址来说的,不是不能改变其数据,而是改变数据的时候会不会改变变量的引用地址。 + +### 确定数据类型 + +- 使用 `type(变量名)` + +### 字符串 + +- 自然字符串, 通过在字符串前加 r 或 R。 如 r"this is a line with \n" 则\n会显示,并不是换行 + +- Python 允许处理 unicode 字符串,加前缀 u 或 U, 如 u"this is an unicode string" + +- 级联字符串,如"this " "is " "string"会被自动转换为this is string + + image-20231201114240737 + +- 字符串的截取的语法格式如下:变量 **[头下标: 尾下标: 步长]** \ No newline at end of file diff --git a/note/Python/文件操作.md b/note/Python/文件操作.md new file mode 100644 index 0000000..5ca4a35 --- /dev/null +++ b/note/Python/文件操作.md @@ -0,0 +1,86 @@ +### 读文件 + +- 读取全部内容 + +```python +f = open("xxx.py") +f.read() +``` + +- 读取指定多少个字符内容 + +```python +f = open("xxx.py") +f.read(2) +``` + +- 按行读取:如果返回空就代表文件读完了 + +```python +>>> f = open("xxx.py") +>>> f.readline() +'This is the first line of the file.\n' +>>> f.readline() +'Second line of the file\n' +>>> f.readline() +'' +``` + +- 读取所有行到list + +```python +>>> f = open("module_support.py") +>>> f.readlines() +['#!/usr/bin/python3\n', '\n', 'def add(a, b):\n', ' return a + b\n', '\n', 'if __name__ == "__main__":\n', ' print("__main__")\n', 'else:\n', ' print(__name__)\n'] +>>> +``` + +- for循环读取每一行 + +```python +>>> f = open("module_support.py") +>>> for line in f: +... print(line, end = "") +... +#!/usr/bin/python3 + +def add(a, b): + return a + b + +if __name__ == "__main__": + print("__main__") +else: + print(__name__) +>>> +``` + + + +### 写文件 + +```python +f = open("module_support.py", "a") # 追加 +f = open("module_support.py", "w") # 文件不存在创建,存在就清空 +f.wtite("one line string") +f.close() +``` + +### 文件操作cursor + +- f.tell() 显示当前cursor位置 +- f.seek(offset, from_what) 重新设置cursor位置 + - from_what 的值, 如果是 0 表示开头, 如果是 1 表示当前位置, 2 表示文件的结尾,例如: + - seek(x, 0) : 从起始位置即文件首行首字符开始移动 x 个字符 + - seek(x, 1) : 表示从当前位置往后移动x个字符 + - seek(-x, 2):表示从文件的结尾往前移动x个字符 + +```python +>>> f = open("module_support.py", "a") +>>> f.tell() +128 +>>> f = open("module_support.py", "a") +>>> f.seek(10, 0) +10 +>>> +``` + diff --git a/note/Python/杂七杂八.md b/note/Python/杂七杂八.md new file mode 100644 index 0000000..003640a --- /dev/null +++ b/note/Python/杂七杂八.md @@ -0,0 +1,94 @@ +### type 和 isinstance 区别 + +- type()不会认为子类是一种父类类型。 +- isinstance()会认为子类是一种父类类型。 + +```python +>>> class A: +... pass +... +>>> class B(A): +... pass +... +>>> isinstance(A(), A) +True +>>> type(A()) == A +True +>>> isinstance(B(), A) +True +>>> type(B()) == A +False +``` + +### 删除变量定义 + +- del +- 通过使用 del 语句删除单个或多个对象 + +```python +>>> a = 1 +>>> a +1 +>>> del a +>>> a +Traceback (most recent call last): + File "", line 1, in +NameError: name 'a' is not defined +>>> +``` + +### 模块 `__name__` + +- 区分是自身运行还是被作为模块导入 + +```python +#!/usr/bin/python3 +# Filename: using_name.py + +if __name__ == '__main__': + print('程序自身在运行') +else: + print('我来自另一模块') + +``` + + + +### dir 函数 + +- 内置的函数 dir() 可以找到模块内定义的所有名称。以一个字符串列表的形式返回 + +```python +>>> import module_support +>>> dir(module_support) +['__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__', 'add'] +>>> +>>> import sys +>>> dir(sys) +['__displayhook__', '__doc__', '__excepthook__', '__loader__', '__name__', + '__package__', '__stderr__', '__stdin__', '__stdout__', + '_clear_type_cache', '_current_frames', '_debugmallocstats', '_getframe', + '_home', '_mercurial', '_xoptions', 'abiflags', 'api_version', 'argv', + 'base_exec_prefix', 'base_prefix', 'builtin_module_names', 'byteorder', + 'call_tracing', 'callstats', 'copyright', 'displayhook', + 'dont_write_bytecode', 'exc_info', 'excepthook', 'exec_prefix', + 'executable', 'exit', 'flags', 'float_info', 'float_repr_style', + 'getcheckinterval', 'getdefaultencoding', 'getdlopenflags', + 'getfilesystemencoding', 'getobjects', 'getprofile', 'getrecursionlimit', + 'getrefcount', 'getsizeof', 'getswitchinterval', 'gettotalrefcount', + 'gettrace', 'hash_info', 'hexversion', 'implementation', 'int_info', + 'intern', 'maxsize', 'maxunicode', 'meta_path', 'modules', 'path', + 'path_hooks', 'path_importer_cache', 'platform', 'prefix', 'ps1', + 'setcheckinterval', 'setdlopenflags', 'setprofile', 'setrecursionlimit', + 'setswitchinterval', 'settrace', 'stderr', 'stdin', 'stdout', + 'thread_info', 'version', 'version_info', 'warnoptions'] +``` + +- 如果没有给定参数,那么 dir() 函数会罗列出当前定义的所有名称 + +```python +>>> dir() +['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__', 'a', 'answers', 'b', 'c', 'i', 'keyword', 'module_support', 'name', 'questions', 'random', 'str', 'student', 'tup', 'w', 'x', 'y'] +>>> +``` + diff --git a/note/Python/转义字符.md b/note/Python/转义字符.md new file mode 100644 index 0000000..871efc6 --- /dev/null +++ b/note/Python/转义字符.md @@ -0,0 +1,17 @@ +| 转义字符 | 描述 | 实例 | +| :---------- | :----------------------------------------------------------- | :----------------------------------------------------------- | +| \(在行尾时) | 续行符 | >>> print("line1 \
... line2 \
... line3")
line1 line2 line3
>>> | +| | 反斜杠符号 | >>> print("\\")
\ | +| \' | 单引号 | >>> print('\'')
' | +| \" | 双引号 | >>> print("\"")
" | +| \a | 响铃 | >>> print("\a") | +| \b | 退格(Backspace) | >>> print("Hello \b World!")
Hello World! | +| \000 | 空 | >>> print("\000")

>>> | +| \n | 换行 | >>> print("\n")


>>> | +| \v | 纵向制表符 | >>> print("Hello \v World!")
Hello
World!
>>> | +| \t | 横向制表符 | >>> print("Hello \t World!")
Hello World!
>>> | +| \r | 回车,将 \r 后面的内容移到字符串开头,并逐一替换开头部分的字符,直至将 \r 后面的内容完全替换完成。 | >>> print("Hello\rWor!")
Wor!o
>>> print('google w3cschool taobao\r123456')
123456 w3cschool taobao | +| \f | 换页 | >>> print("Hello \f World!")
Hello
World!
>>> | +| \yyy | 八进制数,y 代表 0~7 的字符,例如:\012 代表换行。 | >>> print("\110\145\154\154\157\40\127\157\162\154\144\41")
Hello World! | +| \xyy | 十六进制数,以 \x 开头,y 代表的字符,例如:\x0a 代表换行 | >>> print("\x48\x65\x6c\x6c\x6f\x20\x57\x6f\x72\x6c\x64\x21")
Hello World! | +| \other | 其它的字符以普通格式输出 | | \ No newline at end of file diff --git a/note/Python/运算符.md b/note/Python/运算符.md new file mode 100644 index 0000000..58b3bb7 --- /dev/null +++ b/note/Python/运算符.md @@ -0,0 +1,126 @@ +### 算术运算符 + +| 运算符 | 描述 | 实例 | +| :----- | :---------------------------------------------- | :-------------------------------------- | +| + | 加 - 两个对象相加 | a + b 输出结果 31 | +| - | 减 - 得到负数或是一个数减去另一个数 | a - b 输出结果 11 | +| * | 乘 - 两个数相乘或是返回一个被重复若干次的字符串 | a * b 输出结果 210 | +| / | 除 - x 除以 y | a / b 输出结果 2.1 | +| % | 取模 - 返回除法的余数 | a % b 输出结果 1 | +| ** | 幂 - 返回 x 的 y 次幂 | a ** b 为 21 的 10 次方 | +| // | 取整除 - 返回商的整数部分 | 9//2 输出结果 4 , 9.0//2.0 输出结果 4.0 | + + + +### 比较运算符 + +| 运算符 | 描述 | 实例 | +| :----- | :---------------------------------- | :-------------------- | +| == | 等于 -- 比较对象是否相等 | (a == b) 返回 False。 | +| != | 不等于 -- 比较两个对象是否不相等 | (a != b) 返回 True. | +| > | 大于 -- 返回 x 是否大于 y | (a > b) 返回 False。 | +| < | 小于 -- 返回 x 是否小于 y。 | (a < b) 返回 True。 | +| >= | 大于等于 -- 返回 x 是否大于等于 y。 | (a >= b) 返回 False。 | +| <= | 小于等于 -- 返回 x 是否小于等于 y。 | (a <= b) 返回 True。 | + + + +### 赋值运算符 + +| 运算符 | 描述 | 实例 | +| :----- | :----------------------------------------------- | :----------------------------------------------------------- | +| = | 简单的赋值运算符 | c = a + b 将 a + b 的运算结果赋值为 c | +| += | 加法赋值运算符 | c += a 等效于 c = c + a | +| -= | 减法赋值运算符 | c -= a 等效于 c = c - a | +| *= | 乘法赋值运算符 | c *= a 等效于 c = c * a | +| /= | 除法赋值运算符 | c /= a 等效于 c = c / a | +| %= | 取模赋值运算符 | c %= a 等效于 c = c % a | +| **= | 幂赋值运算符 | `c **= a` 等效于 `c = c ** a` | +| //= | 取整除赋值运算符 | c //= a 等效于 c = c // a | +| := | 海象运算符,计算结果赋值给一个变量,防止重复运算 | my_list = [1, 2, 3, 4, 5]
if (length := len(my_list)) > 0:
print(f"列表中有{length}个元素!") | + +### 位运算符 + +| 运算符 | 描述 | 实例 | +| :----: | :----------------------------------------------------------- | :-------------------------------------------- | +| & | 按位与运算符:参与运算的两个值,如果两个相应位都为 1,则该位的结果为 1,否则为 0 | (a & b) 输出结果 12 ,二进制解释: 0000 1100 | +| \| | 按位或运算符:只要对应的二个二进位有一个为 1 时,结果位就为 1。 | (a \| b) 输出结果 61 ,二进制解释: 0011 1101 | +| ^ | 按位异或运算符:当两对应的二进位相异(不同)时,结果为 1 | (a ^ b) 输出结果 49 ,二进制解释: 0011 0001 | +| ~ | 按位取反运算符:对数据的每个二进制位取反,即把 1 变为 0,把 0 变为 1 | (~a ) 输出结果 -61 ,二进制解释: 1100 0011 | +| << | 左移动运算符:运算数的各二进位全部左移若干位,由"<<"右边的数指定移动的位数,高位丢弃,低位补 0。 | a << 2 输出结果 240 ,二进制解释: 1111 0000 | +| >> | 右移动运算符:把">>"左边的运算数的各二进位全部右移若干位,">>"右边的数指定移动的位数 | a >> 2 输出结果 15 ,二进制解释: 0000 1111 | + +- 与运算:都是1才是1 +- 或运算:有1就是1 +- 异或运算:相同为0,不同位1 +- 左移一位 = 乘2 +- 右移一位 = 除2取整 + +| | 按位与运算(a&b) | 按位或运算(a\|b) | 按位异或(a^b) | +| :-----------------: | :---------------: | :--------------: | :-------------: | +| a(60)的二进制表示 | 0011 1100 | 0011 1100 | 0011 1100 | +| b(13)的二进制表示 | 0000 1101 | 0000 1101 | 0000 1101 | +| 运算结果 | 0000 1100 | 0011 1101 | 0011 0001 | +| 结果的十进制表示 | 12 | 61 | 49 | + +| | 按位取反(~a) | 左移(a<<2) | 右移(a>>2) | +| :------------------: | :------------: | :----------: | :--------: | +| a(60)的二进制表示 | 0011 1100 | 0011 1100 | 0011 1100 | +| 运算结果 | 1100 0011 | 1111 0000 | 0000 1111 | +| 运算结果的十进制表示 | -61 | 240 | 15 | + + + +### 逻辑运算符 + +- a = 10 +- b = 20 + +| 运算符 | 逻辑表达式 | 描述 | 实例 | +| :----- | :--------- | :----------------------------------------------------------- | :---------------------- | +| and | x and y | 布尔"与" - 如果 x 为 False,x and y 返回 False,否则它返回 y 的计算值。 | (a and b) 返回 20。 | +| or | x or y | 布尔"或" - 如果 x 是 True,它返回 x的值,否则它返回 y 的计算值。 | (a or b) 返回 10。 | +| not | not x | 布尔"非" - 如果 x 为 True,返回 False 。如果 x 为 False,它返回 True。 | not(a and b) 返回 False | + + + +### 成员运算符 + +| 运算符 | 描述 | 实例 | +| :----- | :------------------------------------------------------ | :------------------------------------------------ | +| in | 如果在指定的序列中找到值返回 True,否则返回 False。 | x 在 y 序列中 , 如果 x 在 y 序列中返回 True。 | +| not in | 如果在指定的序列中没有找到值返回 True,否则返回 False。 | x 不在 y 序列中 , 如果 x 不在 y 序列中返回 True。 | + + + +### 身份运算符 + +| 运算符 | 描述 | 实例 | +| :----- | :------------------------------------------ | :----------------------------------------------------------- | +| is | is 是判断两个标识符是不是引用自一个对象 | x is y, 如果 id(x) 等于 id(y) , **is** 返回结果 True | +| is not | is not 是判断两个标识符是不是引用自不同对象 | x is not y, 如果 id(x) 不等于 id(y). **is not** 返回结果 True | + + + +### 运算符优先级 + +| 运算符 | 描述 | +| :----------------------------------------------------------- | :--------------------------------- | +| `(expressions...)`,`[expressions...]`, `{key: value...}`, `{expressions...}` | 圆括号的表达式 | +| `x[index]`, `x[index:index]`, `x(arguments...)`, `x.attribute` | 读取,切片,调用,属性引用 | +| await x | await 表达式 | +| `**` | 乘方(指数) | +| `+x`, `-x`, `~x` | 正,负,按位非 NOT | +| `*`, `@`, `/`, `//`, `%` | 乘,矩阵乘,除,整除,取余 | +| `+`, `-` | 加和减 | +| `<<`, `>>` | 移位 | +| `&` | 按位与 AND | +| `^` | 按位异或 XOR | +| `|` | 按位或 OR | +| `in,not in, is,is not, <, <=, >, >=, !=, ==` | 比较运算,包括成员检测和标识号检测 | +| `not x` | 逻辑非 NOT | +| `and` | 逻辑与 AND | +| `or` | 逻辑或 OR | +| `if -- else` | 条件表达式 | +| `lambda` | lambda 表达式 | +| `:=` | 赋值表达式 | \ No newline at end of file diff --git a/note/Python/遍历技巧.md b/note/Python/遍历技巧.md new file mode 100644 index 0000000..070a956 --- /dev/null +++ b/note/Python/遍历技巧.md @@ -0,0 +1,69 @@ +### dict 遍历 + +- 关键字和对应的值可以使用 items() 方法同时解读出来 + +```python +>>> knights = {'gallahad': 'the pure', 'robin': 'the brave'} +>>> for k, v in knights.items(): +... print(k, v) +... +gallahad the pure +robin the brave + +``` + +### 序列遍历 + +- 索引位置和对应值可以使用 enumerate() 函数同时得到 + +```python +>>> for i, v in enumerate(['tic', 'tac', 'toe']): +... print(i, v) +... +0 tic +1 tac +2 toe + +``` + +- 同时遍历两个或更多的序列,可以使用 zip() 组合,迭代器性质,遍历一遍后清空,无法再次遍历 + +```python +>>> questions = ['name', 'quest', 'favorite color'] +>>> answers = ['lancelot', 'the holy grail', 'blue'] +>>> for q, a in zip(questions, answers): +... print('What is your {0}? It is {1}.'.format(q, a)) +... +What is your name? It is lancelot. +What is your quest? It is the holy grail. +What is your favorite color? It is blue. +``` + +- 反向遍历一个序列,首先指定这个序列,然后调用 reversed() + +```python +>>> for i in reversed(range(1, 10, 2)): +... print(i) +... +9 +7 +5 +3 +1 + +``` + +- 按顺序遍历一个序列,使用 sorted() 函数返回一个已排序的序列,并不修改原值 + +```python +>>> basket = ['apple', 'orange', 'apple', 'pear', 'orange', 'banana'] +>>> for f in sorted(set(basket)): +... print(f) +... +apple +banana +orange +pear + +``` + diff --git a/note/README.md b/note/README.md new file mode 100644 index 0000000..16b9721 --- /dev/null +++ b/note/README.md @@ -0,0 +1,92 @@ +# note + + + +## Getting started + +To make it easy for you to get started with GitLab, here's a list of recommended next steps. + +Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)! + +## Add your files + +- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files +- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command: + +``` +cd existing_repo +git remote add origin https://home.heysq.com:19001/sunqi/note.git +git branch -M main +git push -uf origin main +``` + +## Integrate with your tools + +- [ ] [Set up project integrations](https://home.heysq.com:19001/sunqi/note/-/settings/integrations) + +## Collaborate with your team + +- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/) +- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html) +- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically) +- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/) +- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html) + +## Test and Deploy + +Use the built-in continuous integration in GitLab. + +- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html) +- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/) +- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html) +- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/) +- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html) + +*** + +# Editing this README + +When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template. + +## Suggestions for a good README +Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information. + +## Name +Choose a self-explaining name for your project. + +## Description +Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors. + +## Badges +On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge. + +## Visuals +Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method. + +## Installation +Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection. + +## Usage +Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README. + +## Support +Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc. + +## Roadmap +If you have ideas for releases in the future, it is a good idea to list them in the README. + +## Contributing +State if you are open to contributions and what your requirements are for accepting them. + +For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self. + +You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser. + +## Authors and acknowledgment +Show your appreciation to those who have contributed to the project. + +## License +For open source projects, say how it is licensed. + +## Project status +If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers. diff --git a/note/Shell/_ coding_utf-8.md b/note/Shell/_ coding_utf-8.md new file mode 100644 index 0000000..e69de29 diff --git a/note/Shell/awk 条件判断.md b/note/Shell/awk 条件判断.md new file mode 100644 index 0000000..4368ebf --- /dev/null +++ b/note/Shell/awk 条件判断.md @@ -0,0 +1,47 @@ +- if +``` +[sunqi@manjaro-xfce ~]$ awk -F: '{ if($3>=500 && $3<=60000) {print $1,$3}}' /etc/passwd +systemd-coredump 981 +systemd-network 980 +systemd-oom 979 +systemd-journal-remote 978 +systemd-resolve 977 +systemd-timesync 976 +tss 975 +dhcpcd 974 +dnsmasq 973 +avahi 971 +colord 970 +flatpak 969 +geoclue 968 +git 967 +lightdm 966 +nm-openconnect 965 +nm-openvpn 964 +openvpn 963 +sunqi 1000 +vncuser 1001 +[sunqi@manjaro-xfce ~]$ +``` + +- if else +``` +[sunqi@manjaro-xfce ~]$ awk -F: '{ if($3==0) {print $1" is admin"} else {print $1" not admin"} }' /etc/passwd +root is admin +nobody not admin +dbus not admin +bin not admin +daemon not admin +mail not admin +ftp not admin +http not admin +``` + +- if else begin 结合shell 命令 +``` +[sunqi@manjaro-xfce ~]$ awk 'BEGIN{ if($(id -u) -ge 500 && $(id -u) -ne 65534) {print "是普通用户"} else {print "不是普通用户"}}' +是普通用户 +[sunqi@manjaro-xfce ~]$ +``` + +- if else if else \ No newline at end of file diff --git a/note/Shell/awk.md b/note/Shell/awk.md new file mode 100644 index 0000000..c00bfc2 --- /dev/null +++ b/note/Shell/awk.md @@ -0,0 +1,195 @@ +### 概念 +- awk 是一种编程语言,对文件和数据进行处理 +- 数据可以来自标准输入,一个或多个文件,或其他命令的输出 +- 朱行扫描文件爱你,默认从第一行到最后一行,寻找匹配的特定模式的行 + +### 语法结构 +- awk 选项 '命令部分' 文件名 +- 引用shell变量需要使用双引号引起 + +### 常用选项介绍 +- -F 定义字段分割符号,默认的分隔符是空格 +- -v 定义变量并赋值 + +### 命名部分说明 +![59e2e6e78b7e5d8f0b2bf74c16f74ff0.png](../../_resources/59e2e6e78b7e5d8f0b2bf74c16f74ff0.png) + +### 内置变量 +- $n 当前记录的第n个字段,字段间由FS分隔 +- $0 完整的输入记录 +- ARGC 命令行参数的数目 +- ARGIND 命令行中当前文件的位置(从0开始算) +- ARGV 包含命令行参数的数组 +- CONVFMT 数字转换格式(默认值为%.6g)ENVIRON环境变量关联数组 +- ERRNO 最后一个系统错误的描述 +- FIELDWIDTHS 字段宽度列表(用空格键分隔) +- FILENAME 当前文件名 +- FNR 各文件分别计数的行号 +- FS 字段分隔符(默认是任何空格) +- IGNORECASE 如果为真,则进行忽略大小写的匹配 +- NF 一条记录的字段的数目 +- NR 已经读出的记录数,就是行号,从1开始 +- OFMT 数字的输出格式(默认值是%.6g) +- OFS 输出字段分隔符,默认值与输入字段分隔符一致。 +- ORS 输出记录分隔符(默认值是一个换行符) +- RLENGTH 由match函数所匹配的字符串的长度 +- RS 记录分隔符(默认是一个换行符) +- RSTART 由match函数所匹配的字符串的第一个位置 +- SUBSEP 数组下标分隔符(默认值是/034) + +### 实践 +- 打印文件的每一行 +``` +[sunqi@manjaro-vm shell_demo]$ awk 'NR=1,NR=5{print $0}' passwd +root:x:0:0::/root:/bin/bash +nobody:x:65534:65534:Kernel Overflow User:/:/usr/bin/nologin +dbus:x:81:81:System Message Bus:/:/usr/bin/nologin +bin:x:1:1::/:/usr/bin/nologin +daemon:x:2:2::/:/usr/bin/nologin +mail:x:8:12::/var/spool/mail:/usr/bin/nologin +ftp:x:14:11::/srv/ftp:/usr/bin/nologin +http:x:33:33::/srv/http:/usr/bin/nologin +systemd-coredump:x:981:981:systemd Core Dumper:/:/usr/bin/nologin +systemd-network:x:980:980:systemd Network Management:/:/usr/bin/nologin +``` + +- 打印第一行到第五行 +``` +[sunqi@manjaro-vm shell_demo]$ awk 'NR==1,NR==5{print $0}' passwd +root:x:0:0::/root:/bin/bash +nobody:x:65534:65534:Kernel Overflow User:/:/usr/bin/nologin +dbus:x:81:81:System Message Bus:/:/usr/bin/nologin +bin:x:1:1::/:/usr/bin/nologin +daemon:x:2:2::/:/usr/bin/nologin +[sunqi@manjaro-vm shell_demo]$ + +me@me-EQ59:~/shell_demo$ awk 'NR>=3 && NR<=5 {print $0}' passwd +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +me@me-EQ59:~/shell_demo$ +``` + + +- 打印第一行或第五行 +``` +me@me-EQ59:~/shell_demo$ awk 'NR==1 || NR==5 {print $0}' passwd +root:x:0:0:root:/root:/bin/bash +sync:x:4:65534:sync:/bin:/bin/sync +me@me-EQ59:~/shell_demo$ +``` + +- 打印 冒号 分割第一行,最后一行和倒数第二行 +``` +me@me-EQ59:~/shell_demo$ awk -F: '{print $1, $NF, $(NF-1)}' passwd +root /bin/bash /root +daemon /usr/sbin/nologin /usr/sbin +bin /usr/sbin/nologin /bin +sys /usr/sbin/nologin /dev +sync /bin/sync /bin +games /usr/sbin/nologin /usr/games +man /usr/sbin/nologin /var/cache/man +lp /usr/sbin/nologin /var/spool/lpd +mail /usr/sbin/nologin /var/mail +news /usr/sbin/nologin /var/spool/news +``` + +- 以 冒号 分割,包含root关键字的行的第一列和最后一列 +``` +me@me-EQ59:~/shell_demo$ awk -F: '/root/{print $1,$NF}' passwd +root /bin/bash +nm-openvpn /usr/sbin/nologin +me@me-EQ59:~/shell_demo$ +``` + +- 自定义结果输出分隔符 +``` +me@me-EQ59:~/shell_demo$ awk -F: 'BEGIN{OFS="#"}/root/{print $1,$NF}' passwd +root#/bin/bash +nm-openvpn#/usr/sbin/nologin +me@me-EQ59:~/shell_demo$ awk -F: '/root/{print $1"###"$NF}' passwd +root###/bin/bash +nm-openvpn###/usr/sbin/nologin +me@me-EQ59:~/shell_demo$ +``` + +### 格式化输出 +- print 类似echo +- printf 类似 echo -n +- %s 字符类型 +- %d 数值类型 +- %-15s 占15字符,- 表示左对齐,默认是右对齐 +- printf 默认不会在行尾自动换行,加`\n` +``` +me@me-EQ59:~/shell_demo$ awk -F: '{printf "%-15s %-10s %-15s\n", $1,$2,$3}' passwd +root x 0 +daemon x 1 +bin x 2 +sys x 3 +sync x 4 +games x 5 +man x 6 +lp x 7 +mail x 8 +news x 9 +``` + +### 变量定义 +- -v 变量名=变量值 +- 调用变量不需要使用 $ 符号 +``` +me@me-EQ59:~/shell_demo$ head -n 5 /etc/passwd > passwd +me@me-EQ59:~/shell_demo$ cat passwd +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +me@me-EQ59:~/shell_demo$ awk -v NUM=4 -F: '{ print $NUM }' passwd +0 +1 +2 +3 +65534 +me@me-EQ59:~/shell_demo$ awk -v NUM=4 -F: '{ print NUM }' passwd +4 +4 +4 +4 +4 +me@me-EQ59:~/shell_demo$ +``` + +### begin end +- begin 表示在程序开始前执行 +- end 表示在所有文件处理完后执行 +- 'BEGIN{开始处理之前};{处理中};END{处理结束后}' +``` +me@me-EQ59:~/shell_demo$ cat passwd +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +sync:x:4:65534:sync:/bin:/bin/sync +me@me-EQ59:~/shell_demo$ awk -v NUM=4 -F: 'BEGIN{print "user\tshell_name"};{ print $1"\t"$NF };END{print "user\tshell_name"}' passwd +user shell_name +root /bin/bash +daemon /usr/sbin/nologin +bin /usr/sbin/nologin +sys /usr/sbin/nologin +sync /bin/sync +user shell_name +me@me-EQ59:~/shell_demo$ +``` + +### 实践 +- 正则匹配从某一行到某一行 +![99275e98a56fc125fcba3c17489f58a4.png](../../_resources/99275e98a56fc125fcba3c17489f58a4.png) + +- 正则筛选行然后打印行 +![0746d379ccf516f12052987cdb69d62e.png](../../_resources/0746d379ccf516f12052987cdb69d62e.png) + +- 支持逻辑运算符 `&&`、 `||`和`!` +![130836e5300e6e1e037d37ee040bb7b1.png](../../_resources/130836e5300e6e1e037d37ee040bb7b1.png) +![1929245ee849038cfe1a9d5da7fc3443.png](../../_resources/1929245ee849038cfe1a9d5da7fc3443.png) +![7f95339bd98c5f0bee5868e52d28ee80.png](../../_resources/7f95339bd98c5f0bee5868e52d28ee80.png) \ No newline at end of file diff --git a/note/Shell/bash 执行脚本参数.md b/note/Shell/bash 执行脚本参数.md new file mode 100644 index 0000000..f1fa913 --- /dev/null +++ b/note/Shell/bash 执行脚本参数.md @@ -0,0 +1,18 @@ +- bash -x 脚本执行过程 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ bash -x ./first_shell.sh ++ echo 'hello world' +hello world ++ echo 'hello world' +hello world ++ echo 'hello world' +hello world +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` +- bash -n 脚本语法syntax 检测 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ bash -n first_shell.sh +first_shell.sh: 行 6: 寻找匹配的 `"' 时遇到了未预期的 EOF +first_shell.sh: 行 7: 语法错误:未预期的文件结束符 +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` \ No newline at end of file diff --git a/note/Shell/case 语法.md b/note/Shell/case 语法.md new file mode 100644 index 0000000..03b5223 --- /dev/null +++ b/note/Shell/case 语法.md @@ -0,0 +1,53 @@ +### 语法结构 +- 多重匹配语句 +- 如果匹配成功,执行相匹配的命令 +``` +pattern 表示需要匹配的模式 + +case var in +pattern 1) # 可以用 | 分割多个模式,相当于 or + command 1 # 需要执行的语句 + ;; # 两个分号代表命令结束 +pattern 2) + command 2 + ;; + *) # default 不满足以上模式,默认执行下边的语句 + command 3 + ;; +esac +``` + +``` +#! /bin/env bash + +case $1 in + start|S) + echo "service starting" + ;; + stop|T) + echo "service stopping" + ;; + reload|R) + echo "service restarting" + ;; + *) + echo "invalid params: $1" + ;; +esac +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh +invalid params: +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh 2 +invalid params: 2 +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh start +service starting +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh S +service starting +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh reload +service restarting +me@me-EQ59:~/shell_demo/scripts/shell03$ ./case_1.sh T +service stopping +me@me-EQ59:~/shell_demo/scripts/shell03$ +``` \ No newline at end of file diff --git a/note/Shell/expect 自动应答 tcl语言.md b/note/Shell/expect 自动应答 tcl语言.md new file mode 100644 index 0000000..50cb06e --- /dev/null +++ b/note/Shell/expect 自动应答 tcl语言.md @@ -0,0 +1,27 @@ +- 与shell结合 +``` +#! /bin/bash +while read ip pass +do + /usr/bin/expect <<-END &>/dev/null + spawn ssh root@$ip + expect { + "yes/no" { send "yes\r";exp_continue } + "passwprd:" { send "$pass\r" } + } + expect "#" { send "useradd yy1;rm -rf /tmp/*;exit\r" } + expect eof + END + echo "$ip 用户创建完毕" +done < ip.txt +``` + +- 并发执行 +``` +tr ":" " " < /ip.txt | while read ip pass +do +{ + command +}& +done +``` \ No newline at end of file diff --git a/note/Shell/for 循环语法.md b/note/Shell/for 循环语法.md new file mode 100644 index 0000000..cebb42d --- /dev/null +++ b/note/Shell/for 循环语法.md @@ -0,0 +1,164 @@ +### 概览 +- 列表循环:用于将一组命令执行已知的次数 +- 不带列表循环,执行时由用户指定参数和参数的个数 +- 类C风格循环 +- break continue exit +- +### 列表循环 +- 方式一 +```shell +# 无步长 +for variable in {list} +do + command1 + command2 + ... +done + +# 有步长 +for variable in {list..步长} +do + command1 + command2 + ... +done + +``` + +- 方式二 +``` +for variable in a b c +do + command1 + command2 + ... +done +``` +- 示例 +``` +#! /bin/env bash + +# 步长为2 +for i in {1..5..2} +do + echo $i +done + +#! /bin/env bash +for i in 1 2 3 4 5 +do + echo $i +done +``` + +### 不带列表循环 +- 方式一 +``` +for variable +do + command + command + ... +done +``` + +- 示例 +``` +#! /bin/env bash + +for i +do + echo hello +done +``` +- 自动用$@ 用户输入的所有参数进行循环 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ bash -x for_not_list.sh a b c ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ bash -x for_not_list.sh {1..5} ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ bash -x for_not_list.sh `seq 5` ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello ++ for i in "$@" ++ echo hello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +### 类C风格循环 +- 方式一 +``` +# expr1 定义变量并赋初值 +# expr 决定是否进行循环 +# expr3 决定循环变量如何改变,决定循环什么时候退出 +for (( expr1;expr2;expr4 )) +do + command + command + ... +done +``` + +- 示例 +``` +for (( i=1;i<=5;i++ )) +do + echo $i +done +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ for ((i=0;i<=5;i++));do echo $i;done +0 +1 +2 +3 +4 +5 +me@me-EQ59:~/shell_demo/scripts/shell01$ for ((i=0;i<=5;i++));do [ $((i%2)) -eq 0 ] && echo $i;done +0 +2 +4 +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` \ No newline at end of file diff --git a/note/Shell/sed 搜索替换.md b/note/Shell/sed 搜索替换.md new file mode 100644 index 0000000..74962c4 --- /dev/null +++ b/note/Shell/sed 搜索替换.md @@ -0,0 +1,78 @@ +### 语法 +- sed 选项 's/搜索的内容/替换的内容/动作' 需要处理的文件 +- 动作 + - p 打印 + - g 全局替换 +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed -n 's/fla/FLA/p' file1 +lFLA +FLA +FLAvnanv +me@me-EQ59:~/shell_demo$ sed -n '1s/fla/FLA/p' file1 +lFLA +me@me-EQ59:~/shell_demo$ sed -n '1s/fla/FLA/g' file1 +me@me-EQ59:~/shell_demo$ sed -n '1s/fla/FLA/gp' file1 +lFLA +me@me-EQ59:~/shell_demo$ +``` + +- 替换 /user/local 为 lalala +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +/usr/local +/user/local/bin +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed -n 's@/user/local@lalala@gp' file1 +lalala/bin +me@me-EQ59:~/shell_demo$ sed -n 's#/user/local#lalala#gp' file1 +lalala/bin +me@me-EQ59:~/shell_demo$ +``` + + +- 一到五行替换成#号 +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +/usr/local +/user/local/bin +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed -n '1,5s/^/#/gp' file1 +#lfla +#fla +#flavnanv +#rpprp +#/usr/local +me@me-EQ59:~/shell_demo$ +``` + +- 10.1.1.1 替换成 10.1.1.254 +``` +me@me-EQ59:~/shell_demo$ cat ip.txt +10.1.1.1 +10.2.2.2 +me@me-EQ59:~/shell_demo$ sed -n 's/10.1.1.1/10.1.1.254/gp' ip.txt +10.1.1.254 +me@me-EQ59:~/shell_demo$ sed -n 's/\(10.1.1.\)1/\1254/gp' ip.txt +10.1.1.254 # 小括号括起来,保留为第一个正则表达式,后续使用 \1 来使用刚才保存的值 +me@me-EQ59:~/shell_demo$ sed -n 's#\(10.1.1.\)1#\1254#gp' ip.txt +10.1.1.254 +me@me-EQ59:~/shell_demo$ +``` + + +- sed 结合正则 +![3af55d15a2738c93e1728f5ca9edb2e0.png](../../_resources/3af55d15a2738c93e1728f5ca9edb2e0.png) \ No newline at end of file diff --git a/note/Shell/sed.md b/note/Shell/sed.md new file mode 100644 index 0000000..c0024e3 --- /dev/null +++ b/note/Shell/sed.md @@ -0,0 +1,212 @@ +### 概念 +- stream editor的缩写 +- 流编辑器,用来处理文件 +- 一行一行读取文件,按照要求进行处理,并把结果输出到屏幕 +- 对缓冲区中副本进行编辑,不会修改源文件 + +### 运行模式 +- 命令行模式 +- 脚本模式 + +### 命令行格式 +- sed options ‘处理动作’ 文件名 + ![294a6fb86ea2c755baf11d4517227967.png](../../_resources/294a6fb86ea2c755baf11d4517227967.png) + + - 常见处理动作 + ![21f9477ffd1ea12c46e439205e44a117.png](../../_resources/21f9477ffd1ea12c46e439205e44a117.png) + + +### 举例 +- 打印文件 `sed '' passwd` +- 打印文件第2行 +``` +me@me-EQ59:~/shell_demo$ sed -n '2p' passwd +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +me@me-EQ59:~/shell_demo$ +``` + +- 打印文件 1-4 行 +``` +me@me-EQ59:~/shell_demo$ sed -n '1,4p' passwd +root:x:0:0:root:/root:/bin/bash +daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin +bin:x:2:2:bin:/bin:/usr/sbin/nologin +sys:x:3:3:sys:/dev:/usr/sbin/nologin +me@me-EQ59:~/shell_demo$ +``` + +- 打印文件文件最后一行 +``` +me@me-EQ59:~/shell_demo$ sed -n '$p' passwd +gitlab-runner:x:1001:1001:GitLab Runner:/home/gitlab-runner:/bin/bash +me@me-EQ59:~/shell_demo$ +``` + +- 文件第二行前插入内容 +``` +me@me-EQ59:~/shell_demo$ sed -n '2ihello world' passwd +hello world +``` + +- 文件第三行后插入内容 +``` +sed -n '3ahello world' paswd +``` + +- 支持换行插入内容 +``` +me@me-EQ59:~/shell_demo$ sed '3ihello\ +world\ +999\ +flal' file1 # 命令到这结束后 enter +lfla +fla +hello +world +999 +flal +flavnanv +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ +``` + +- 多行之间插入,在第二行和第三行和第四行前边插入内容 +``` +me@me-EQ59:~/shell_demo $ sed '2,4ihello' file1 +lfla +hello +fla +hello +flavnanv +hello +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ +``` + + +- 替换整行内容,替换第五行内容 +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed '5cheloworld' file1 +lfla +fla +flavnanv +rpprp +heloworld +me@me-EQ59:~/shell_demo$ +``` + +- 替换文件所有内容 sed 'hello world' file1 +- 替换多行内容,替换 1-5 行的内容为 hello world +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed '2,4cheloworld' file1 +lfla +heloworld +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ +``` + +- fla 开头的行替换成 haha +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed '/^fla/chaha' file1 +lfla +haha +haha +rpprp +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ +``` + +- 删除第四行 `sed '4d' file1` +- 删除 1 到 4 行 `sed '1,4d' file1` + + +### 其他命令 +![2d661ab455541517d45e666109787aab.png](../../_resources/2d661ab455541517d45e666109787aab.png) + +- 读取其他文件的内容到指定位置,读取/etc/hosts 文件的内容,放到 第一行下边 +``` +me@me-EQ59:~/shell_demo$ sed '1r /etc/hosts' file1 +lfla +127.0.0.1 localhost +127.0.1.1 me-EQ59 + +# The following lines are desirable for IPv6 capable hosts +::1 ip6-localhost ip6-loopback +fe00::0 ip6-localnet +ff00::0 ip6-mcastprefix +ff02::1 ip6-allnodes +ff02::2 ip6-allrouters +fla +flavnanv +rpprp +/usr/local +/user/local/bin +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ +``` + +- 保存当前文件的多行到当前目下某一个文件内 +``` +me@me-EQ59:~/shell_demo$ cat file1 +lfla +fla +flavnanv +rpprp +/usr/local +/user/local/bin +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed -n '2,4w another.txt' file1 +me@me-EQ59:~/shell_demo$ ls +another.txt EOF file1 hello_grep.txt ip.txt passwd passwd.bak scripts test1 +me@me-EQ59:~/shell_demo$ cat another.txt +fla +flavnanv +rpprp +me@me-EQ59:~/shell_demo$ +``` + +- 保留匹配串,fla开头的行前边加注释 +``` +me@me-EQ59:~/shell_demo$ cat file +lfla +fla +flavnanv +rpprp +/usr/local +/user/local/bin +dbdfblbjaljl;ka +me@me-EQ59:~/shell_demo$ sed -n 's/^fla/#&/gp' file +#fla +#flavnanv +me@me-EQ59:~/shell_demo$ +``` + +- 多次编辑,打印包含 root 的行,以及行号 +``` +me@me-EQ59:~/shell_demo$ sed -ne '/root/p;/root/=' passwd +root:x:0:0:root:/root:/bin/bash +1 +nm-openvpn:x:121:127:NetworkManager OpenVPN,,,:/var/lib/openvpn/chroot:/usr/sbin/nologin +40 +me@me-EQ59:~/shell_demo$ +``` \ No newline at end of file diff --git a/note/Shell/shebang _!.md b/note/Shell/shebang _!.md new file mode 100644 index 0000000..8f24adc --- /dev/null +++ b/note/Shell/shebang _!.md @@ -0,0 +1,10 @@ +`shebang`指的是出现在文本文件的第一行的前两个字符 `#!` + +- 以 `#! /bin/sh` 开头的文件,程序执行时会调用 `/bin/sh`,也就是 bash 解释器 +- 以 `#! /usr/bin/python` 开头的文件,代表指定python 解释器去执行 +- 以 `#! /usr/bin/env 解释器名称` 开头,是一种在不同平台上都能找到解释器的写法 + +注意: +- 未指定 shebang ,脚本执行时会用当前 shell 去运行脚本,`$SHELL` +- shebang 之后的解释程序,需要写绝对路径,不会自动到 $PATH 中自动寻找解释器 +- 脚本作为参数传递给解释器,会忽略脚本中的 shebang \ No newline at end of file diff --git a/note/Shell/shell 脚本概念.md b/note/Shell/shell 脚本概念.md new file mode 100644 index 0000000..59776a8 --- /dev/null +++ b/note/Shell/shell 脚本概念.md @@ -0,0 +1,7 @@ +- 弱类型的脚本语言,无需声明变量类型,直接定义使用 +- 定义的变量的数据类型默认都是`字符串`类型 + +``` +debian@vm-m01:~$ echo $HISTSIZE +1000 +``` \ No newline at end of file diff --git a/note/Shell/until 循环语法.md b/note/Shell/until 循环语法.md new file mode 100644 index 0000000..95be851 --- /dev/null +++ b/note/Shell/until 循环语法.md @@ -0,0 +1,8 @@ +- 条件为假就进入循环,条件为真就退出循环 +- 语法结构 +``` +until condition +do + command +done +``` \ No newline at end of file diff --git a/note/Shell/while 循环语法.md b/note/Shell/while 循环语法.md new file mode 100644 index 0000000..73c64bb --- /dev/null +++ b/note/Shell/while 循环语法.md @@ -0,0 +1,32 @@ +- 条件为真就进入循环,条件为假就退出循环 +- 语法结构 +```shell +while 表达式 +do + command +done +``` + +- 与read结合读取文件 +``` +me@me-EQ59:~/shell_demo/scripts/shell02$ ./while_read.sh +10.0.0.1 +123 +10.0.0.2 +124 +10.0.0.3 +125 +me@me-EQ59:~/shell_demo/scripts/shell02$ cat while_read.sh +#! /bin/env bash + +while read ip pass +do + echo $ip + echo $pass +done < ip.txt +me@me-EQ59:~/shell_demo/scripts/shell02$ cat ip.txt +10.0.0.1 123 +10.0.0.2 124 +10.0.0.3 125 +me@me-EQ59:~/shell_demo/scripts/shell02$ +``` \ No newline at end of file diff --git a/note/Shell/函数.md b/note/Shell/函数.md new file mode 100644 index 0000000..29ff8f3 --- /dev/null +++ b/note/Shell/函数.md @@ -0,0 +1,43 @@ +### 概念 +- 一组命令集合或语句行程一段可用代码,这些代码块成为 shell 函数 +- 给这段代码起个名字,称为函数名,后续可以直接调用该段代码的功能 + +### 定义 +- 方法一 +``` +函数名() +{ + command # 一堆命令的集合 + command +} +``` + +- 方法二 +``` +function 函数明() +{ + command + command +} +``` + +### return +- return 可以结束一个函数 +- return 默认返回函数中最后一个命令状态值,也可以给定参数值,0-256 之间 +- 如果没有 return 命令,函数将返回最后一个指令的状态退出值 + +### 调用 +- 命令行调用 + +### 函数传参 +``` +#! /bin/env bash + +hello() +( + echo "hello" + echo $1 +) + +hello name +``` \ No newline at end of file diff --git a/note/Shell/变量概念.md b/note/Shell/变量概念.md new file mode 100644 index 0000000..4b7f85b --- /dev/null +++ b/note/Shell/变量概念.md @@ -0,0 +1,117 @@ +### 部分概念 +- 变量和值之间不能有空格 +- 默认所有变量类型都是字符串 +- 变量名区分大小写 +- 变量名不能含有特殊符号 & ? * # +- 变量名不能以数字开头 +- 可以使用下划线开头 +- 名字尽量做到见名知意 + +### 切片 +- ${变量名:索引开始位置:切片长度} +- 切片长度可以超过变量实际长度 +``` +me@me-EQ59:~$ A=123456 +me@me-EQ59:~$ echo $A +123456 +me@me-EQ59:~$ echo ${A} +123456 +me@me-EQ59:~$ echo ${A:2:4} +3456 +me@me-EQ59:~$ +``` + +### 命令的结果赋值给变量 +- 反引号 包裹 +``` +me@me-EQ59:~$ A=`date +'%F %T'` +me@me-EQ59:~$ echo $A +2023-06-22 22:48:00 +me@me-EQ59:~$ +``` +- $() 包裹 +``` +me@me-EQ59:~$ A=$(date +'%F %T') +me@me-EQ59:~$ echo $A +2023-06-22 22:49:25 +me@me-EQ59:~$ +``` + +### 交互式给变量赋值 +- read 参数 变量名 +``` +me@me-EQ59:~$ read name +name +me@me-EQ59:~$ echo $name +name +me@me-EQ59:~$ +``` + +- 输入提示 +``` +me@me-EQ59:~$ read -p "Input your name:" name +Input your name:tom +me@me-EQ59:~$ echo $name +tom +me@me-EQ59:~$ +``` + +- 输入提示,隐藏用户输入内容 +``` +me@me-EQ59:~$ read -sp "Input your password:" pass +Input your password:me@me-EQ59:~$ +me@me-EQ59:~$ echo $pass +123456 +me@me-EQ59:~$ +``` + +- 限制输入长度,超过长度后自动结束输入 +``` +me@me-EQ59:~$ read -n 5 -p "Input your name: " name +Input your name: zhangme@me-EQ59:~$ san +找不到命令 “san”,但有 16 个相似命令。 +me@me-EQ59:~$ echo $name +zhang +me@me-EQ59:~$ +``` + +- 限制用户输入超时时间,超过限制时间自动关闭输入 +``` +me@me-EQ59:~$ read -t 3 -p "Input your name:" name +Input your name:me@me-EQ59:~$ +me@me-EQ59:~$ echo $name + +me@me-EQ59:~$ +``` + +### declare 定义限制变量 +- https://www.runoob.com/linux/linux-comm-declare.html +- -i 整型,限定类型后不符合类型的值,会被设置为默认值 +``` +me@me-EQ59:~$ declare -i A=1234 +me@me-EQ59:~$ echo $A +1234 +me@me-EQ59:~$ A=hello +me@me-EQ59:~$ echo $A +0 +me@me-EQ59:~$ +``` +- -r 只读变量,不能进行 unset 操作 +- -x 设置环境变量 + +### 变量分类 +- 本地变量,只在当前进程中有效 +``` +me@me-EQ59:~$ echo $A +0 +me@me-EQ59:~$ bash +me@me-EQ59:~$ echo $A + +me@me-EQ59:~$ +``` +- 环境变量,当前进程有效,并且能够被子进程调用 + - env 查询当前用户的环境变量 + - set 查询当前有的所有变量,包括环境变量和临时变量 +- 全局变量:全局所有的用户和程序都能调用,且继承,新建的用户也能默认调用 +- 系统变量:shell 本身已经固定好的名字和作用 +![8801a0e5453be03569f1ee0cf0adfda0.png](../../_resources/8801a0e5453be03569f1ee0cf0adfda0.png) diff --git a/note/Shell/变量附加.md b/note/Shell/变量附加.md new file mode 100644 index 0000000..31681f4 --- /dev/null +++ b/note/Shell/变量附加.md @@ -0,0 +1,32 @@ +### 获取变量字符串长度 +``` +➜ ~ url=www.taobao.com +➜ ~ echo ${#url} +14 +➜ ~ url=20 +➜ ~ echo ${#url} +2 +``` + + +### 变量截取 +- 一个 `%` 代表从右往左去掉一个/key/ +- 两个 `%%` 代表从右往左去掉最大/key/ +``` +➜ ~ url=www.taobao.com +➜ ~ echo ${url%.*} +www.taobao +➜ ~ echo ${url%%.*} +www +➜ ~ +``` + +- `#` 代表相反方向,从左往右 +``` +➜ ~ url=www.taobao.com +➜ ~ echo ${url#*.} +taobao.com +➜ ~ echo ${url##*.} +com +➜ ~ +``` diff --git a/note/Shell/四则运算 $(()) let 计算.md b/note/Shell/四则运算 $(()) let 计算.md new file mode 100644 index 0000000..a4aaed8 --- /dev/null +++ b/note/Shell/四则运算 $(()) let 计算.md @@ -0,0 +1,34 @@ +- shell 只支持整数运算 +``` +me@me-EQ59:~$ echo $((1+1)) +2 +me@me-EQ59:~$ echo $[1+1] +2 +me@me-EQ59:~$ +``` + +- expr +``` +me@me-EQ59:~$ expr 1 + 1 +2 +me@me-EQ59:~$ expr 1 * 1 +expr: 语法错误:未预期的参数 "公共的" +me@me-EQ59:~$ expr 1 \* 1 +1 +me@me-EQ59:~$ expr 1 / 1 +1 +me@me-EQ59:~$ +``` + +- let +``` +me@me-EQ59:~$ n=1;let n=n+1;echo $n +2 +me@me-EQ59:~$ let n+=2 +me@me-EQ59:~$ echo $n +4 +me@me-EQ59:~$ let n*=2 +me@me-EQ59:~$ echo $n +8 +me@me-EQ59:~$ +``` \ No newline at end of file diff --git a/note/Shell/嵌套循环.md b/note/Shell/嵌套循环.md new file mode 100644 index 0000000..27b9497 --- /dev/null +++ b/note/Shell/嵌套循环.md @@ -0,0 +1,43 @@ +``` +#! /bin/env bash + +read -p "input a number:" num +for ((i=1;i<=$num;i++)) +do + for ((j=1;j<=i;j++)) + do + echo -n $j + done + echo +done + + + +for ((i=1;i<=$num;i++)) +do + str="" + for ((j=1;j<=i;j++)) + do + str=$str$j + done + echo $str +done +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell02$ ./print-1-5.sh +input a number:6 +1 +12 +123 +1234 +12345 +123456 +1 +12 +123 +1234 +12345 +123456 +me@me-EQ59:~/shell_demo/scripts/shell02$ +``` \ No newline at end of file diff --git a/note/Shell/影响shell程序的内置命令.md b/note/Shell/影响shell程序的内置命令.md new file mode 100644 index 0000000..81969e7 --- /dev/null +++ b/note/Shell/影响shell程序的内置命令.md @@ -0,0 +1,38 @@ +- exit 退出程序 +- continue 进行下一次循环 +- break 退出循环 +- shift 输入参数位移 +``` +#! /bin/env bash + +sum=0 +for i +do + let sum+=$i +done +echo $sum +``` + +``` +#! /bin/env bash + +sum=0 +while [ $# -ne 0 ] +do + let sum+=$1 + shift +done +echo $sum +``` + +``` +#! /bin/env bash + +sum=0 +while [ $# -ne 0 ] +do + let sum+=$1 + shift 2 +done +echo $sum +``` \ No newline at end of file diff --git a/note/Shell/数组.md b/note/Shell/数组.md new file mode 100644 index 0000000..e961393 --- /dev/null +++ b/note/Shell/数组.md @@ -0,0 +1,55 @@ +### 数组分类 +- 普通数组:只能使用整数作为数组索引 +- 关联数组:可以使用字符串作为数组索引 + +### 普通数组定义 + +- 一次赋予一个值 +``` +数组名[索引下标]=值 +array[0]=v1 +array[1]=v2 +array[2]=v3 +``` + +- 一次赋予多个值,元素之间用逗号隔开 +``` +数组名=(值1 值2 值3 值4) +array=(1 2 3 4) +array2=(`cat /etc/passwd`) # 文件中的每一行赋值给array,有空格按照空格切分,没有空格按照换行符切分 +array3=(`ls /root`) +array4=(harry may jack "Miss You") +array5=(1 2 3 "hello world" [10]=linux) +``` + +### 数组值的读取 + +- ${数组名[元素下标]} +``` +echo ${array[1]} 获取数组里指定元素 +echo ${array[*]} 获取数组里所有元素 +echo ${#array[*]} 获取数组元素个数,也就是数组长度 +echo ${!array[@]} 获取数组的索引下标 +echo ${!array[*]} 获取数组的索引下标 +echo ${array[@]:1:2} 获取指定下表的元素,1 代表从索引为 1 的元素开始,2 代表获取后面的几个元素 +echo ${array[*]:1:2} 同上 +``` + + +### 关联数组定义 +- 先声明定义一个关联数组 +``` +declare -A asso_array1 +``` + +- 数组单个赋值 +``` +asso_array1[linux]=one +asso_array1[java]=two +asso_array1[php]=three +``` + +- 数组一次多个赋值 +``` +asso_array1=([name1]=linux [name2]=jack [name3]=amy) +``` \ No newline at end of file diff --git a/note/Shell/条件判断语法.md b/note/Shell/条件判断语法.md new file mode 100644 index 0000000..452dc31 --- /dev/null +++ b/note/Shell/条件判断语法.md @@ -0,0 +1,225 @@ +### 使用方式分类 +- 0 真 1 假 +- test 条件表达式 +- [ 条件表达式 ],括号两边都有空格 +- [[ 条件表达式 ]] 支持正则,括号两边都有空格 + +### test 条件表达式 +``` +me@me-EQ59:~/shell_demo$ ls +passwd scripts +me@me-EQ59:~/shell_demo$ test -e passwd +me@me-EQ59:~/shell_demo$ echo $? +0 +me@me-EQ59:~/shell_demo$ test -e passwds +me@me-EQ59:~/shell_demo$ echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +### [ 条件表达式 ] +``` +me@me-EQ59:~/shell_demo$ ls +passwd scripts +me@me-EQ59:~/shell_demo$ [ -d scripts ]; echo $? +0 +me@me-EQ59:~/shell_demo$ [ -d script_dir ]; echo $? +1 +me@me-EQ59:~/shell_demo$ +me@me-EQ59:~/shell_demo$ ls -l +总计 8 +-rwxrwxrwx 1 root root 3311 6月 22 14:30 passwd +drwxrwxr-x 3 me me 4096 6月 22 16:20 scripts +lrwxrwxrwx 1 me me 6 6月 23 14:24 test1 -> passwd +me@me-EQ59:~/shell_demo$ [ -L test1 ]; echo $? +0 +me@me-EQ59:~/shell_demo$ mv passwd passwd.bak +me@me-EQ59:~/shell_demo$ ls -l +总计 8 +-rwxrwxrwx 1 root root 3311 6月 22 14:30 passwd.bak +drwxrwxr-x 3 me me 4096 6月 22 16:20 scripts +lrwxrwxrwx 1 me me 6 6月 23 14:24 test1 -> passwd +me@me-EQ59:~/shell_demo$ [ -L test1 ]; echo $? +0 +me@me-EQ59:~/shell_demo$ +``` + +### [[ 条件表达式 ]] +``` +me@me-EQ59:~/shell_demo$ ls +passwd.bak scripts test1 +me@me-EQ59:~/shell_demo$ [[ -f passwd.bak ]]; echo $? +0 +me@me-EQ59:~/shell_demo$ [[ -f test ]]; echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +### 取反判断 +- 表达式前添加 `! ` +``` +# 判断不存在 test 文件时为真 +me@me-EQ59:~/shell_demo$ [[ ! -f test ]]; echo $? +0 +me@me-EQ59:~/shell_demo$ +``` + +### 文件判断参数 +![fa951c7b58b47ebd846fc00df0ecc70d.png](../../_resources/fa951c7b58b47ebd846fc00df0ecc70d.png) + +### 判断文件权限 +![e46d8cc96a6a687642c396363f226422.png](../../_resources/e46d8cc96a6a687642c396363f226422.png) +``` +me@me-EQ59:~/shell_demo$ test -r /etc/passwd; echo $? +0 +me@me-EQ59:~/shell_demo$ test -w /etc/passwd; echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +### 判断文件新旧 && 相等 +![105d782b5f5fb4eb79e7fe7583585a65.png](../../_resources/105d782b5f5fb4eb79e7fe7583585a65.png) +- 文件新旧判断 +``` +me@me-EQ59:~/shell_demo$ ls -l +总计 12 +-rwxrwxr-x 1 me me 3311 6月 23 14:39 passwd +-rwxrwxrwx 1 root root 3311 6月 22 14:30 passwd.bak +drwxrwxr-x 3 me me 4096 6月 22 16:20 scripts +lrwxrwxrwx 1 me me 6 6月 23 14:24 test1 -> passwd +me@me-EQ59:~/shell_demo$ test passwd -nt passwd.bak; echo $? +0 +me@me-EQ59:~/shell_demo$ test passwd -ot passwd.bak; echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +- 文件是否为同一个文件 +``` +me@me-EQ59:~/shell_demo$ touch file1 +me@me-EQ59:~/shell_demo$ touch file2 +me@me-EQ59:~/shell_demo$ test file1 -ef file2; echo $? +1 +me@me-EQ59:~/shell_demo$ [ file1 -ef file2 ]; echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +### 判断数字 +![c01a0eb6107168c67d46719fee3904d9.png](../../_resources/c01a0eb6107168c67d46719fee3904d9.png) + +### 判断字符串 +![b0338a4f367376224e8339ba29bec865.png](../../_resources/b0338a4f367376224e8339ba29bec865.png) + +### 多重条件判断 +- -a 或 && 逻辑与 +- -o 或 || 逻辑或 +``` +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 -a 2 -lt 3 ]; echo $? +0 +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 -a 2 -gt 3 ]; echo $? +1 +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 ] && [ 2 -lt 3 ]; echo $? +0 +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 ] || [ 2 -gt 3 ]; echo $? +0 +me@me-EQ59:~/shell_demo$ +``` + +- && 前边为真,后边才会执行 +``` +me@me-EQ59:~/shell_demo$ id -u +1000 +me@me-EQ59:~/shell_demo$ [ $(id -u) -eq 0 ] && echo "admin" +me@me-EQ59:~/shell_demo$ [ ! $(id -u) -eq 0 ] && echo "not admin" +not admin +me@me-EQ59:~/shell_demo$ +``` + +- || 前边为假,后边才会执行 +``` +me@me-EQ59:~/shell_demo$ id -u +1000 +me@me-EQ59:~/shell_demo$ [ $(id -u) -eq 0 ] || echo "not admin" +not admin +me@me-EQ59:~/shell_demo$ +``` + +- 逻辑运算符没有优先级 +``` +me@me-EQ59:~/shell_demo$ [ $(id -u) -eq 0 ] && echo "admin" || echo "not admin" +not admin +me@me-EQ59:~/shell_demo$ + +me@me-EQ59:~/shell_demo$ [ 1 -eq 2 ] && ehco AAA || echo BBB && echo CCC +BBB +CCC +me@me-EQ59:~/shell_demo$ +``` + +### 数值比较类 C 风格 +- = 表示赋值 +- == 表示判断相等 +``` +me@me-EQ59:~/shell_demo$ ((1==2)); echo $? +1 +me@me-EQ59:~/shell_demo$ ((1<=2)); echo $? +0 +me@me-EQ59:~/shell_demo$ ((1!=2)); echo $? +0 +me@me-EQ59:~/shell_demo$ ((`id -u` == 0)); echo $? +1 +me@me-EQ59:~/shell_demo$ ((`id -u`==0)); echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +### 字符串判断 +- = 和 == 都表示判断 +``` +me@me-EQ59:~/shell_demo$ str1="hello world";str2="world" +me@me-EQ59:~/shell_demo$ [ str1 = str2 ];echo $? +1 +me@me-EQ59:~/shell_demo$ [ $str1 = $str2 ];echo $? +-bash: [: 参数太多 +2 +me@me-EQ59:~/shell_demo$ [ "$str1" = "$str2" ];echo $? +1 +me@me-EQ59:~/shell_demo$ [ "$str1" == "$str2" ];echo $? +1 +me@me-EQ59:~/shell_demo$ [ "$str1" != "$str2" ];echo $? +0 +me@me-EQ59:~/shell_demo$ +``` + +### [] 与 [[]]区别 +- 判断是空字符串时`[]` 报错 `[[]]`不报错 +``` +me@me-EQ59:~/shell_demo$ A= +me@me-EQ59:~/shell_demo$ echo $A + +me@me-EQ59:~/shell_demo$ [ $A = "hello" ] +-bash: [: =: 需要一元运算符 +me@me-EQ59:~/shell_demo$ [[ $A = "hello" ]] +me@me-EQ59:~/shell_demo$ [[ $A = "hello" ]]; echo $? +1 +me@me-EQ59:~/shell_demo$ +``` + +- 逻辑运算符 && || 不拆开写的时候需要 +``` +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 -a 1 -gt 2 ]; echo $? +1 +me@me-EQ59:~/shell_demo$ [ 1 -eq 1 && 1 -gt 2 ]; echo $? +-bash: [: 缺少 "]" +2 +me@me-EQ59:~/shell_demo$ [[ 1 -eq 1 && 1 -gt 2 ]]; echo $? +1 +me@me-EQ59:~/shell_demo$ [[ 1 -lt 1 || 1 -gt 2 ]]; echo $? +1 +me@me-EQ59:~/shell_demo$ [ 1 -lt 1 || 1 -gt 2 ]; echo $? +-bash: [: 缺少 "]" +1:未找到命令 +127 +me@me-EQ59:~/shell_demo$ +``` \ No newline at end of file diff --git a/note/Shell/正则表达式.md b/note/Shell/正则表达式.md new file mode 100644 index 0000000..67323bf --- /dev/null +++ b/note/Shell/正则表达式.md @@ -0,0 +1,42 @@ +### 部分概念 +- 元字符:在正则表达式中具有特殊意义的字符,如星号(*)、问号(?)和点(.) +- 前导字符:位于元字符前边的第一个字符 + +### 其他常用元字符 +![9c89f9daed2d117cc3247a5e5a72cf1c.png](../../_resources/9c89f9daed2d117cc3247a5e5a72cf1c.png) +``` +me@me-EQ59:~/shell_demo$ cat hello_grep.txt +hello world + +hello hello +me@me-EQ59:~/shell_demo$ grep '\' hello_grep.txt +hello world +hello hello +me@me-EQ59:~/shell_demo$ +``` + +``` +me@me-EQ59:~/shell_demo$ grep '\d' hello_grep.txt +hello world +me@me-EQ59:~/shell_demo$ grep -P '\d' hello_grep.txt +10.1.1.1 +me@me-EQ59:~/shell_demo$ cat hello_grep.txt +hello world + +hello hello +10.1.1.1 +me@me-EQ59:~/shell_demo$ +``` + + +### 扩展正则 +- grep 使用需要加参数 `-E` 或者使用 egrep +- sed 使用需要加参数 `-r` +![25e2449631afbce54c6b4f757aae82db.png](../../_resources/25e2449631afbce54c6b4f757aae82db.png) + + +### 第二类正则 +![2f4d02d4c9263d854c2f7d8adf177139.png](../../_resources/2f4d02d4c9263d854c2f7d8adf177139.png) \ No newline at end of file diff --git a/note/Shell/流程控制.md b/note/Shell/流程控制.md new file mode 100644 index 0000000..dd1b895 --- /dev/null +++ b/note/Shell/流程控制.md @@ -0,0 +1,123 @@ +### 概览 +- if 结构 +- if else 结构 +- if elif else 结构 +- 层层嵌套 + +### if 结构 +``` +if [ condition ];then + command + command +fi + +if test condition;then + command +fi + +if [[ condition ]];then + command +fi + +# 等价于 +[ condition ] && command +``` + +### if else 结构 +``` +if [ condition ];then + command1 + else + command2 +fi + +# 等价于 +[ condition ] && command1 || command2 +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ cat if1.sh +#! /bin/env bash + +# S1 输入参数的第一个 +if [ "$1" = "hello" ];then + echo $1 +else + echo "world" +fi +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if1.sh +world +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if1.sh hello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_read.sh +请输入hello:hello +world +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_read.sh +请输入hello:world +再次运行后,请输入hello +me@me-EQ59:~/shell_demo/scripts/shell01$ cat if_read.sh +#! /bin/env bash + +read -p "请输入hello:" str +if [ "$str" = "hello" ];then + echo "world" +else + echo "再次运行后,请输入hello" +fi +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_read_oneline.sh +please input hello:hello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_read_oneline.sh +please input hello:world +please input hello! +me@me-EQ59:~/shell_demo/scripts/shell01$ cat if_read_oneline.sh +#! /bin/env bash +read -p "please input hello:" str; [ "$str" = "hello" ] && echo "hello" || echo "please input hello!" +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` + +### if elif else结构 + +``` +if [ condition1 ];then + command1 + elif [ condition2 ];then + command2 + elif [ condition3 ];then + else + command3 +fi +``` + +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ cat if_elif.sh +#! /bin/env bash + +read -p "请输入hello" str +if [ "$str" = "hello" ];then + echo "hello" +elif [ "$str" = "hello_elif" ];then + echo "hello_elif" +else + echo "world" +fi + +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_elif.sh +请输入hellohello +hello +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_elif.sh +请输入hellohello_elif +hello_elif +me@me-EQ59:~/shell_demo/scripts/shell01$ ./if_elif.sh +请输入hellonothing +world +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` \ No newline at end of file diff --git a/note/Shell/随机数.md b/note/Shell/随机数.md new file mode 100644 index 0000000..da20196 --- /dev/null +++ b/note/Shell/随机数.md @@ -0,0 +1,21 @@ +- 生成随机数 系统变量 echo $RANDOM +``` +me@me-EQ59:~$ echo $RANDOM +32656 +me@me-EQ59:~$ echo $RANDOM +16858 +me@me-EQ59:~$ echo $RANDOM +14105 +me@me-EQ59:~$ echo $RANDOM +5455 +me@me-EQ59:~$ +``` + +- 1-50的随机数 +``` +me@me-EQ59:~$ echo $[$RANDOM%50 + 1] +14 +me@me-EQ59:~$ echo $[$RANDOM%50 + 1] +49 +me@me-EQ59:~$ +``` \ No newline at end of file diff --git a/note/devops/docker/Namespace实战.md b/note/devops/docker/Namespace实战.md new file mode 100644 index 0000000..e69de29 diff --git a/note/devops/docker/底层原理1——容器技术.md b/note/devops/docker/底层原理1——容器技术.md new file mode 100644 index 0000000..eefc0f4 --- /dev/null +++ b/note/devops/docker/底层原理1——容器技术.md @@ -0,0 +1,136 @@ +### 一、背景:虚拟化 + +#### 虚拟化出现的原因 + +- 大多数企业使用的都是物理服务器或者由单家供应商提供的应用,每台服务器又只能运行一个供应商特定的任务 +- 不同供应商之间硬件也并不兼容,如果这时候再各自为他们配备不同的硬件设备必然也会面临物理硬件利用率不足的问题 +- 此时虚拟化技术才得以大展身手,它主要解决了 可对服务器分区 、 可在同一个主机上运行不同环境的应用 两个主要的问题。 + +#### 虚拟化原理 + +- 虚拟化实现技术依托于`Hypervisor` (虚拟机监控程序) 。它处于计算机物理层与虚拟机之间,能够有效地管理计算机的物理资源并将这些资源分配给不同虚拟环境。 +- 作为服务器,它也可以直接安装在硬件上,直接接管物理资源,可以对资源进行分配,分配给多个虚拟机使用,用户在虚拟机中的指令可以直接通过Hypervisor转发到硬件,直接运行在物理硬件中的 `Hypervisor` 即为 `Type1` ,也称为裸机管理程序(`Metal Hypervisor`),目前市面上常用的 `VMware ESXi`、`MiscroSoft Hyper-V` 和 `KVM(Kernel-based Virtual Machine)`都基于这类 `Hypervisor` + +image-20240102145540489 + +- 作为软件,`Hypervisor` 可以直接运行在操作系统之上,即为 `Type2`,使 `Hypervisor` 不直接与物理层基础,因此也称作托管程序(`Hosted Hypervisor`),它主要用于面向个体用户,我们经常在windows中安装的 `Virtual Box`、`VMware WorkStation` 就属于这种类型。此时,相较于 `Type1` ,`Type2` 显然多了一些延迟 + +image-20240102145829993 + +- 每个虚拟机在 `Hypervisor` 之上相互独立,运行不同的操作系统,操作不同的物理资源,这也带来了我们期望了灵活性和可移植性,我们可以将一个虚拟机从一个 `Hypervisor` 中直接迁移到一个新的`Hypervisor` 中,此时就达到了一种 环境复用 的效果 + +Virtualization management + +#### 虚拟化缺点 + +- 如果希望将自己开发的一个 NodeJs 程序放入虚拟机中,就需要在宿主系统中基于 Hypervisor 安装一个 Linux 虚拟机(单独安装 Linux OS),并在 Linux 中为该服务配置一个完整的 JS 应用运行环境和必要的库,如图所示 + +image-20240102150451101 + +- NodeJS 运行时需要的资源可能非常少(10M),而虚拟机本身却占有相当多的资源(>400M),如果继续在该服务器中放入更多的服务,资源消耗程度可见一般,服务器迅速过载 + +- 不同的开发环境可能会造成应用程序环境不一致,本地环境一切正常,投入生产环境后却病态百出,不利于 DevOps、持续集成和交付。 + + + +### 二、容器化概念与特点 + +#### 容器化概念 + +- 容器化技术是一种轻量级的虚拟化技术,它让应用程序和其依赖项可以一起打包成一个独立的、可运行的单元,这个单元可以在任何环境中运行,无论这个环境是物理机、虚拟机还是云环境。 + +- 容器化技术的核心是利用操作系统级别的特性来实现隔离和限制。在 Linux 系统中,最主要的就是 `namespaces` 和 `cgroups` 这两个特性。 + +#### 容器化特点 + +- 轻量:容器化去掉虚拟化中间操作系统层。传统虚拟化技术需要在每个虚拟机中运行完整的操作系统,包括操作系统的内核和所有必要的库文件,容器化通过共享宿主操作系统的内核,无需重复加载多个内核和lib库。**容器可以在相同的硬件资源下运行更多的容器实例,从而更加提高硬件资源的利用率,降低运行成本** +- 易于迁移:将应用程序代码和运行所需的相关配置文件,库和依赖项捆绑起来形成一个镜像,这个**镜像可以被快速地复制和传输到其他环境中。运行这个镜像,即可重现相同的容器实例**,而无需重新配置和安装应用程序 +- 快速启动: 启动传统虚拟机需要启动整个操作系统,包括操作系统的内核、服务和应用程序,因此启动时间相对较长,**容器启动只需加载应用程序及其依赖项,无需启动完整的操作系统,因此启动时间非常快速**。 + +container and vm + +### 三、容器化核心 + +1. Namespaces +2. Cgroups + +#### Namespaces 命名空间 + +Namespaces 能够在同一台机器上创建多个独立的运行空间,每个空间内的进程只能看到本空间内的资源,看不到其他空间的资源。Linux 系统提供了多种类型的 namespaces,包括 PID(进程)、NET(网络)、IPC(进程间通信)、MNT(文件系统挂载点)、UTS(主机名和域名)和 User(用户) + +image-20240102175343179 + +##### Mount Namespace 挂载命名空间 + +- Mount Namespace:用来隔离文件系统的挂载点,不同`Mount Namespace`的进程拥有不同的挂载点,同时也拥有了不同的文件系统视图,不通的Mount Namespace中进程看到的文件系统层次结构是不同的 +- 一个进程新建或进入一个 Mount Namespace 后,它的挂载点视图将与其他所有 Namespace 隔离。该进程内部进行的任何挂载或卸载操作都只会影响当前 Namespace,其他 Namespace 的挂载点视图不会受到影响 +- 其他 Namespace 的修改也不会影响当前 Namespace。这种特性使得每个容器可以拥有自己独立的文件系统视图,从而在文件系统层面上隔离容器 + +##### PID Namespace 进程命名空间 + +- PID namespace 是 Linux namespace 的一种类型,用于隔离进程 ID 的空间。也就是说,在 PID namespace 中,每个 namespace 都有自己独立的 PID 空间,即在不同的 PID namespace 中,相同的 PID 可能代表着不同的进程 +- 允许每个容器有自己独立的进程空间,而且每个容器都可以有自己的 init 进程(PID 为1的进程)。这就使得容器在行为上更加像一个独立的系统 +- 在一个新的 PID namespace 中启动一个进程时,这个进程和它的所有子进程都会存在于这个新的 PID namespace 中。这个进程在新的 PID namespace 中的 PID 会是 1,而在原来的 PID namespace 中,它的 PID 会是一个新分配的、不同的值 + +##### Net Namespace 网络命名空间 + +- 用于隔离网络设备、网络协议栈、端口等网络资源。每个NET Namespace都有自己的网络设备、IP地址、路由表、/proc/net目录、端口号等等 +- 可以使容器拥有自己独立的网络环境,每个容器默认都会在自己的NET Namespace中启动,并拥有一个虚拟的以太网接口和一个独立的网络栈 +- 除了以上的隔离,NET Namespace还提供了更灵活的网络配置,例如在每个Namespace中设置不同的防火墙规则,或者在每个Namespace中使用不同的网络服务 + +##### IPC Namespace 进程间通信命名空间 + +- 用于隔离进程间通信资源的一种Linux命名空间。每个IPC Namespace都有自己独立的System V IPC对象和POSIX消息队列 + - System V IPC(Inter-Process Communication)是一种旧的进程间通信机制,包括信号量(semaphores)、消息队列(message queues)和共享内存(shared memory) + - POSIX消息队列是一种更现代的、基于标准POSIX接口的进程间通信机制 +- IPC Namespace能够隔离容器中的进程间通信,使得每个容器都有自己独立的IPC资源,而不会与宿主机或其他容器共享。这种隔离性能够增强容器的安全性,并使得容器更像一个独立的系统 +- IPC Namespace并**不会隔离其他类型的进程间通信,例如UNIX域套接字、管道和网络套接字等**。这些类型的进程间通信仍然可以跨越IPC Namespace的边界 + +##### UTS Namespace Unix分时系统命名空间 + +- Unix Timesharing System Namespace +- 用于隔离两个系统标识符:主机名(hostname)和网络域名(NIS domain name) +- 进程在新的 UTS Namespace 中,它可以拥有自己的主机名和网络域名,而这些变化不会影响到其他 UTS Namespace。这样,每个容器可以有自己的主机名,就好像每个容器都是一个独立的网络节点 + +##### User Namespace 用户命名空间 + +- 用于隔离用户和用户组 ID。每个 User Namespace 都有自己的用户和用户组 ID 空间。在 User Namespace 中,一个用户的 UID 和 GID 可以被映射到外部 Namespace 的不同 UID 和 GID +- 最主要的用途是将容器的 root 用户映射到宿主机的非 root 用户,从而增强容器的安全性 +- 一个进程可以在 User Namespace 中拥有 root 权限,进行各种需要超级用户权限的操作,但在外部 Namespace 中,这个进程只是一个普通用户,无法影响到宿主系统 +- User Namespace 也可以让每个容器有自己的用户和用户组配置,这对于运行需要特定用户配置的应用程序非常有用 +- 虽然 User Namespace 提供了用户 ID 的隔离,但并不能防止进程直接访问和修改系统资源,如直接操作系统调用、设备文件等。这些操作的安全性需要通过其他机制(如 seccomp、capabilities、cgroups 等)来保证。 + +#### Cgroups 控制组 + +Cgroups(Control Groups)是 Linux 内核的一种特性,用于限制、记录、隔离进程组(Process Groups)使用的物理资源,如 CPU、内存、磁盘 I/O、网络等。 + +cgroups + +> 每个进程都会竞争系统资源,这样就会导致一些次要的进程占用了系统的大部分资源,从而影响系统效率,也可能导致资源耗尽的时候引起查杀到主进程,因此linux引入了linux cgroups来控制进程资源,让进程更可控 + +- 资源限制:你可以设置某个进程组能使用的最大资源,比如 CPU 使用率、内存使用量等。 + +- 优先级调整:你可以改变某个进程组的资源调度优先级。比如,你可以设置让某个进程组优先获取 CPU 时间。 + +- 资源统计:Cgroups 可以记录某个进程组的资源使用情况,如 CPU 时间、内存使用量等。 + +- 进程隔离:Cgroups 可以将某个进程组的资源使用与系统中的其他进程隔离开来。 + +- 控制:Cgroups 可以冻结、恢复进程组中的进程,用于创建检查点和恢复。 + +##### Cgroups子系统 + +- cpu:用于限制和管理CPU资源的使用,如CPU时间片、CPU核心数量等 +- cpuacct 子系统,可以统计 cgroups 中的进程的 cpu 使用报告 +- memory:用于限制和管理内存资源的使用,如进程的内存使用量、内存压缩等 +- blkio:用于限制和管理块设备I/O资源的使用,如磁盘读写速度、I/O请求队列长度等 +- cpuset:用于将进程绑定到特定的CPU和内存节点 +- devices:用于限制和管理设备访问的权限,如阻止进程访问某些设备 +- freezer:用于暂停和恢复进程的执行 +- net_cls:用于将网络流量分类和标记,以便对不同类型的流量进行限制和管理 +- net_prio:用于设置网络流量的优先级 +- pids:用于限制和管理进程数量 +- hugetlb:用于管理和分配大页面(Huge Page) +- rdma:用于管理和分配RDMA(Remote Direct Memory Access)资源 +- perf_event:用于对进程的性能事件进行采样和监控 +- ns 子系统,可以使不同 cgroups 下面的进程使用不同的 namespace \ No newline at end of file diff --git a/note/devops/docker/底层原理2——镜像.md b/note/devops/docker/底层原理2——镜像.md new file mode 100644 index 0000000..c3c9052 --- /dev/null +++ b/note/devops/docker/底层原理2——镜像.md @@ -0,0 +1,208 @@ +### 一、镜像概述 + +Docker 镜像包含运行应用程序所需的所有内容:代码、运行时、库、环境变量和配置文件。镜像是构建 Docker 容器的蓝图。它们是分层的,每一层都代表一个镜像构建的指令。当你创建一个新的镜像时,你会在一个基础镜像之上添加一个新的层,这个基础镜像可能是一个操作系统(如 Ubuntu 或 CentOS),也可能是另一个已经包含了额外组件的镜像。每当你在镜像上执行一个操作(如安装软件包或修改文件),Docker 都会在镜像的顶部添加一个新的层 + + + +### 二、镜像核心技术——联合文件系统 Union File System + +联合文件系统允许多个不同的文件系统被叠加在一起,形成单个文件系统。每一层都可以添加变更,而不影响其他层。广泛应用于 Linux 发行版中的 Live CD、Docker 镜像等场景。常见的联合文件系统有 AUFS、OverlayFS、Btrfs 等 + +联合文件系统的原理是将多个目录(称为分支)叠加在一起,这样就可以合并它们的内容。用户在访问文件或目录时,实际上是在访问一个由这些分支叠加而成的统一视图。下面是一些关键特点和工作原理: + +- **分层结构:**在 UnionFS 中,每个分支都可以看作是一个层,层与层之间可以是只读或者可写的。这些层从下往上叠加,形成一个统一的文件系统。最下面的层通常是只读的,而最上面的层是可写的 +- **只读和可写层:**只读层包含不变的数据,而可写层通常用于存储变更。这种结构使得基础数据(只读层)在多个应用中被重复利用,而不需要复制,从而节省空间 +- **Copy-On-Write(COW):**这是 UnionFS 的一种核心策略。当需要对文件进行修改时,如果该文件处于只读层,则首先会将该文件复制到最上面的可写层,然后再进行修改。这个过程称为“写时复制”,意味着只有在需要修改时,文件的副本才会被创建 +- **文件和目录的合并视图:**从用户的角度来看,叠加在一起的所有层就像是一个普通的文件系统。如果多个层中包含了相同的文件或目录,UnionFS 按照一定的优先级规则(通常是最上面的层优先)来决定哪个版本对用户可见 +- **删除和修改:**当删除或修改一个文件时,UnionFS 会在可写层创建一个特殊的标记(白板或删除标记),表示该文件在这个层被删除或修改了。即使底层的只读分支中仍然存在这个文件的原始版本,用户也看不到 +- **透明性:**对于最终用户和应用程序来说,UnionFS 提供了一个透明的文件系统接口,使得它们无需关心底层的分层和叠加细节 + +### 三、镜像核心技术——写时复制 Copy On Write + +一种优化策略,用于在容器中管理文件系统的写入操作 + +#### 基本原理 + +1. **初始状态:** 当容器启动时,文件系统的各个层次被堆叠在一起,形成一个联合文件系统。这些层次中的每一个层都是只读的 +2. **写操作触发:** 当容器需要对文件系统进行写操作时,例如创建、修改或删除文件,"Copy-On-Write"被触发 +3. **创建副本:** 联合文件系统不直接在底层文件系统上执行写操作。相反,它在顶层的可写层创建一个被写入的文件或目录的副本 +4. **仅在需要时复制:** 副本的创建是按需进行的,只有在写入操作发生时才会复制相关的文件或目录。直到写入操作发生,原始文件系统层仍然是只读的 + +#### 优势与好处 + +1. **避免污染底层层次:** "Copy-On-Write"策略确保了底层文件系统层不会被写入,因此原始镜像或者其他容器共享的底层层次不会被修改。这有助于维护原始镜像的不变性 +2. **节省存储空间:** 由于只有在写入操作发生时才会创建副本,"Copy-On-Write"减少了对存储空间的需求。多个容器可以共享相同的底层层次,只需保存它们各自的修改,而不必复制整个文件系统 +3. **快速创建副本:** 由于只有在需要时才复制文件,"Copy-On-Write"使得容器可以快速创建修改,而不需要复制整个文件系统,提高了性能和效率 +4. **隔离写入操作:** "Copy-On-Write"确保了容器之间的文件写入操作是相互隔离的。每个容器都有自己的写入层,使得它们可以独立地对文件系统进行修改,而不会相互影响 + +### 四、镜像制作 + +#### 技术 + +1. **Dockerfile:** Docker 使用 Dockerfile 来定义和描述镜像的构建步骤。Dockerfile 是一个文本文件,包含了从基础镜像开始,逐步添加、配置和操作的指令,最终构建出一个新的镜像。 +2. **基础镜像:** 基础镜像是构建其他镜像的起点,它通常包含了操作系统和基本的运行时环境。选择适当的基础镜像对于镜像的性能和大小都有影响。 +3. **Docker CLI:** Docker 命令行工具是与 Docker 引擎进行交互的主要方式。通过 Docker CLI,可以执行构建、运行、推送等操作,对 Docker 镜像进行管理。 +4. **Docker Hub:** Docker Hub 是一个中央仓库,用于存储和分享 Docker 镜像。用户可以将自己构建的镜像推送到 Docker Hub,也可以从 Docker Hub 拉取公共镜像 + +#### 流程 + +1. **创建 Dockerfile:** 首先,通过创建一个 Dockerfile 来定义镜像的构建步骤。Dockerfile 包含了一系列指令,例如基础镜像的选择、软件的安装、配置文件的添加等 + +```dockerfile +FROM base_image:tag +RUN apt-get update && apt-get install -y software +COPY local-files /app +``` + +2. **构建镜像:** 使用 `docker build` 命令执行 Dockerfile 中定义的构建步骤。这将会逐步执行每个指令,生成一个新的 Docker 镜像 + +```bash +docker build -t my_custom_image:tag . +``` + +3. **运行容器:** 可以使用 `docker run` 命令基于刚刚构建的镜像启动容器。在容器中,你可以测试和验证构建的应用程序或服务 + +```bash +docker run -it my_custom_image:tag +``` + +4. **推送到仓库:** 如果你想分享或备份你的镜像,可以使用 `docker push` 命令将镜像推送到 Docker Hub 或其他容器仓库 + +```bash +docker push my_custom_image:tag +``` + +5. 持续集成(可选):** 对于大型项目或团队,可以考虑将 Docker 镜像的构建过程整合到持续集成(CI)流程中,确保代码变更触发自动的构建和测试 + +#### Dockerfile编写关键指令 + +1. **FROM:** 指定基础镜像,所有其他指令都基于该镜像。 + +```dockerfile +FROM ubuntu:20.04 +``` + +2. **LABEL:** 为镜像添加元数据,通常用于提供版本、描述等信息。 + +```dockerfile +LABEL maintainer="your-name" +``` + +3. **RUN:** 在镜像中执行命令,用于安装软件包、配置环境等。 + +```dockerfile +RUN apt-get update && apt-get install -y software +``` + +4. **COPY:** 将文件从主机复制到镜像中。 + +```dockerfile +COPY local-files /app +``` + +5. **ADD:** 类似于 COPY,但可以处理 URL 和解压缩 tar 文件。 + +```dockerfile +ADD https://example.com/file.tar.gz /app +``` + +6. **WORKDIR:** 设置工作目录,后续的相对路径都相对于该目录。 + +```dockerfile +WORKDIR /app +``` + +7. **ENV:** 设置环境变量。 + +```dockerfile +ENV MY_VARIABLE=my-value +``` + +8. **EXPOSE:** 声明容器运行时监听的网络端口。 + +```dockerfile +EXPOSE 80 +``` + +9. **CMD:** 指定容器启动时要运行的命令。可以有多个 CMD,但只有最后一个生效。 + +```dockerfile +CMD ["executable", "param1", "param2"] +``` + +10. **ENTRYPOINT:** 指定容器启动时要运行的命令,与 CMD 结合使用。CMD 提供默认参数。 + +```dockerfile +ENTRYPOINT ["executable", "param1", "param2"] +``` + +11. **VOLUME:** 创建挂载点,用于持久化存储或与其他容器共享数据。 + +```dockerfile +VOLUME /data +``` + +12. **USER:** 设置运行时的用户名或 UID。 + +```dockerfile +USER username +``` + +13. **ARG:** 定义构建时的参数,可通过 `docker build` 命令传递。 + +```dockerfile +ARG version=latest +``` + + + +### 五、镜像存储与分发 + +#### 存储方式: + +1. **本地文件系统:** + - Docker 镜像可以直接存储在本地文件系统上。这适用于开发和测试阶段,以及在单机环境中使用。 +2. **Docker Hub 和其他 Registry:** + - Docker Hub 是 Docker 官方提供的中央镜像仓库,允许用户将镜像上传至其中,并从中下载。私有仓库(如 Harbor、Amazon ECR、Azure Container Registry 等)也提供了类似的功能,适用于企业内部或有特殊需求的场景。 +3. **Docker Volume:** + - Docker Volume 用于持久化存储容器数据,但也可以用来存储镜像。这种方式适用于需要在容器之间共享数据的场景,但并不是首选的镜像存储方式。 +4. **分布式存储系统:** + - 在某些场景中,可以使用分布式存储系统(如 Ceph、GlusterFS、NFS 等)来存储 Docker 镜像。这种方式允许多个 Docker 主机共享相同的存储,提供高可用性和扩展性。 + +#### 分发方式: + +1. **Docker Push 和 Pull:** + +使用 `docker push` 命令将本地构建的镜像上传到 Docker Hub 或其他 Registry,并使用 `docker pull` 命令在其他机器上下载镜像。这是最常见的分发方式。 + +```bash +# 将镜像推送至 Docker Hub +docker push username/repository:tag + +# 在其他机器上拉取镜像 +docker pull username/repository:tag +``` + +**Docker Save 和 Load:** + +使用 `docker save` 命令将镜像保存为 tar 文件,然后通过 `docker load` 命令在其他机器上加载。这种方式适用于离线环境或需要手动迁移镜像的情况。 + +```bash +# 将镜像保存为 tar 文件 +docker save -o image.tar username/repository:tag + +# 在其他机器上加载镜像 +docker load -i image.tar +``` + +**导出和导入容器快照:** + +使用 `docker export` 命令导出容器快照,然后使用 `docker import` 命令导入为镜像。这种方式适用于将容器快照分享给其他人。 + +```bash +# 导出容器快照 +docker export container_id > container.tar + +# 导入为镜像 +cat container.tar | docker import - username/repository:tag +``` diff --git a/note/devops/docker/底层原理3——网络.md b/note/devops/docker/底层原理3——网络.md new file mode 100644 index 0000000..78878c9 --- /dev/null +++ b/note/devops/docker/底层原理3——网络.md @@ -0,0 +1,212 @@ +### 一、网络概述 + +Docker 的网络底层原理利用 Linux 操作系统提供的网络命名空间、虚拟网络设备、iptables 规则等特性,通过 Bridge 桥接网络或 Overlay 网络模式实现容器之间的通信。这种网络隔离和虚拟化的设计使得容器可以更灵活地运行在不同的网络环境中 + +### 二、Docker 网络模式 + +- Bridge 桥接模式(默认模式) +- Host 主机模式 +- None 无网络模式 +- Overlay 覆盖模式 +- Macvlan MACVLAN 模式 +- Custom 自定义网络模式 + +### 三、具体网络模式详解 + +#### 1. Bridge模式 + +Docker 默认的网络模式,容器连接到一个本地的 Docker 桥接网络,由宿主机上的 docker0 虚拟网桥负责管理。在这个模式下,容器可以相互通信,也可以通过宿主机与外部网络通信。这种模式提供了网络隔离,但容器之间可以通过容器名称进行访问 + +```bash +docker run --network=bridge my_container +``` + + + +#### 2. Host模式 + +容器与宿主机共享网络栈,即容器使用宿主机的网络命名空间和网络设备。这样可以提高容器的网络性能,但会导致容器失去网络隔离,与宿主机共享相同的网络地址和端口空间 + +```bash +docker run --network=host my_container +``` + + + +#### 3. None 无网络模式 + +容器没有自己的网络命名空间,也不连接到任何网络。这意味着容器无法进行网络通信,适用于一些特殊的场景,例如只需要本地访问的容器 + +```bash +docker run --network=none my_container +``` + + + +#### 4. Overlay 覆盖模式 + +Overlay 模式用于实现多个 Docker 守护进程之间的容器通信,特别适用于跨主机的容器编排工具(如 Docker Swarm)。Overlay 网络通过在容器之间创建逻辑网络层,使得它们可以在不同主机上进行通信 + +```bash +# 在 Docker Swarm 中使用 overlay 网络 +docker network create --driver overlay myoverlaynetwork +``` + + + +#### 5. Macvlan macvlan模式 + +Macvlan 模式允许容器拥有自己的 MAC 地址,从而使其在网络中表现得像一个物理设备一样。这种模式通常用于需要容器与物理网络设备直接通信的场景 + +```bash +docker network create -d macvlan \ + --subnet=192.168.1.0/24 \ + --gateway=192.168.1.1 \ + -o parent=eth0 \ + mymacvlan +docker run --network=mymacvlan my_container +``` + + + +#### 6. Custom 自定义网络模式 + +用户可以创建自定义的 Docker 网络,灵活配置网络属性,以满足特定应用的需求。自定义网络模式提供了更高级的网络配置选项,用户可以定义子网、网关等参数 + +```bash +docker network create mynetwork +docker run --network=mynetwork my_container +``` + +### 四、同一个网络间容器通信原理 + +1. **共享网络命名空间:** 同一网络中的容器共享相同的网络命名空间。网络命名空间是 Linux 内核提供的一种隔离机制,它使得容器能够在网络层面互相感知和通信。容器内的网络配置、接口和路由表等信息都是在这个共享的网络命名空间中 +2. **Docker Bridge:** 在默认的 Bridge 模式下,Docker 创建一个本地的桥接网络,通常称为 `docker0` 网桥。这个桥接网络作为同一网络中所有容器的连接点,使得它们可以相互通信。每个容器会被分配一个独立的 IP 地址,这样它们可以通过这个 IP 地址进行通信 +3. **DNS 解析:** Docker 提供了容器间的 DNS 解析服务。通过容器的名称,可以在同一网络中的容器之间进行域名解析。这意味着你可以使用容器的名称而不是 IP 地址来访问其他容器 +4. **iptables 规则:** Docker 利用 iptables 来实现网络地址转换(NAT)和端口映射等功能。这些规则允许容器之间的通信,以及容器与宿主机之间的通信。Docker 通过这些规则确保网络包从一个容器正确地流向另一个容器或宿主机 +5. **虚拟网卡:**使用docker network创建自定义网络会在主机上创建一个虚拟网卡,容器需要与主机通信时需要通过该虚拟网卡 + +image-20240109173303317 + + + +### 五、network 常用命令 + +#### 1. 列出所有网络 + +```bash +bash: docker network ls +NETWORK ID NAME DRIVER SCOPE +d343c2f00fd6 1panel-network bridge local +6e0b3a12badf bridge bridge local +7f5baf2ce0a8 host host local +c612b4fb2640 internal bridge local +52194e1004e7 none null local +``` + +#### 2. 创建网络 + +- 命令行参数 + +```bash +Usage: docker network create [OPTIONS] NETWORK + +Create a network + +Options: + --attachable Enable manual container attachment + --aux-address map Auxiliary IPv4 or IPv6 addresses used by Network driver (default map[]) + --config-from string The network from which to copy the configuration + --config-only Create a configuration only network + -d, --driver string Driver to manage the Network (default "bridge") + --gateway strings IPv4 or IPv6 Gateway for the master subnet + --ingress Create swarm routing-mesh network + --internal Restrict external access to the network + --ip-range strings Allocate container ip from a sub-range + --ipam-driver string IP Address Management Driver (default "default") + --ipam-opt map Set IPAM driver specific options (default map[]) + --ipv6 Enable IPv6 networking + --label list Set metadata on a network + -o, --opt map Set driver specific options (default map[]) + --scope string Control the network's scope + --subnet strings Subnet in CIDR format that represents a network segment +``` + +- 创建自定义网络 + - driver_name:bridge,macvlan,host,overlay, none + +```bash +docker network create --subnet= --gateway= -d drive_name + +``` + + + +#### 3. 使用某个网络 + +- 创建容器时设置网络 + +```bash +docker run -itd --name nginx-test --network=mynetwork nginx:1.18 +``` + +- 运行中的容器连接到网络,此时容器中会多一个网卡 + +```bash +docker network connect mynetwork1 nginx-test +``` + +image-20240109171909268 + +- 将某个网络与容器断开连接 + +```bash +docker network disconnect mynetwork1 nginx-test +``` + + + +#### 4. 查看网络信息 + +```bash +docker network inspect mynetwork +[ + { + "Name": "mynetwork", + "Id": "bf66c98d13a8159c5539e873a0236974f501ac33c0b0622e66389fb7195d784b", + "Created": "2024-01-09T04:25:50.892326739-05:00", + "Scope": "local", + "Driver": "bridge", + "EnableIPv6": false, + "IPAM": { + "Driver": "default", + "Options": {}, + "Config": [ + { + "Subnet": "172.21.0.0/16", + "Gateway": "172.21.0.1" + } + ] + }, + "Internal": false, + "Attachable": false, + "Ingress": false, + "ConfigFrom": { + "Network": "" + }, + "ConfigOnly": false, + "Containers": { + "ef4755825054960b2042f217e10666926a1f4226d56ac1a179c9dce2cc825b53": { + "Name": "nginx-test", + "EndpointID": "3b234715756c75194ce7947930f733424acef4f6855a53882d6986bc50ab2cfb", + "MacAddress": "02:42:ac:15:00:02", + "IPv4Address": "172.21.0.2/16", + "IPv6Address": "" + } + }, + "Options": {}, + "Labels": {} + } +] +``` + diff --git a/note/devops/docker/底层原理4——存储.md b/note/devops/docker/底层原理4——存储.md new file mode 100644 index 0000000..c0c4c19 --- /dev/null +++ b/note/devops/docker/底层原理4——存储.md @@ -0,0 +1,136 @@ +### 一、存储概述 + +Docker 支持多种存储类型用于容器和镜像的存储。以下是一些常见的 Docker 存储类型: + +- Overlay 存储驱动 +- Aufs 存储驱动 +- Device Mapper 存储驱动 +- VFS(Virtual File System) +- Btrfs 存储驱动 +- ZFS 存储驱动 + +[Docker官方存储驱动文档](https://docs.docker.com/storage/storagedriver/) + + + +### 二、Overlay 存储驱动 + +#### 1. 基本原理 + +- 层次结构: Docker 镜像是由多个文件系统层(layers)组成的。每个层都包含文件和目录,并且每个层都是只读的。这些层通过联合文件系统(Union File System)的概念组合在一起,形成一个统一的文件系统 + +- Overlay 文件系统:** Overlay 存储驱动使用 OverlayFS 文件系统,它是一个联合文件系统,通过将多个文件系统层叠加在一起,为用户提供一个单一的文件系统视图。这使得容器能够访问所有层的文件和目录,而实际上这些层是分开存储的 + +- Upper、Lower 和 Work 目录: OverlayFS 包括三个关键的目录: + + - Upper 目录: 包含了容器运行时的可写层,任何对文件系统的写操作都将在这个目录中进行。这使得容器可以在运行时修改文件 + + - Lower 目录: 包含了只读的基础镜像层,即 Docker 镜像的各个层次 + + - Work 目录: 用于存储 OverlayFS 内部的工作文件 + +#### 2. Upper,Lower和worker目录 + +- **Upper 目录:** + + - **位置:** `upper` 目录包含 OverlayFS 的可读写层。这是容器内部所做更改的存储位置 + + - **功能:** 容器内部对文件系统的任何修改或添加都会存储在 `upper` 目录中。这个目录允许在其他层之上创建一个可写层 + +- **Lower 目录:** + + - **位置:** `lower` 目录包含 OverlayFS 的只读层。这些层代表了基础镜像和任何额外的只读层 + + - **功能:** `lower` 目录中的内容是 Docker 镜像中的初始文件和目录集。这些层叠加在一起,而上层提供了一个可写表面用于进行更改 + +- **Work 目录:** + + - **位置:** `work` 目录在 OverlayFS 的内部使用,用于进行覆盖过程中的操作 + + - **功能:** 在覆盖过程中,例如合并和复制操作,`work` 目录充当某些 OverlayFS 操作的工作空间。操作完成后,更改将应用到上层 + +![iT 邦幫忙::一起幫忙解決難題,拯救IT 人的一天](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202401221032111.png) + +#### 3. Overlay2相比Overlay优化点 + +- **并发写操作:** Overlay2 引入了对并发写操作的更好支持。这意味着在多个容器同时对文件系统进行写操作时,Overlay2 能够更有效地管理并发性,减少冲突和性能下降。 + +- **硬链接:** Overlay2 在硬链接的处理上进行了优化。硬链接是一种在文件系统中创建文件的方法,Overlay2 利用硬链接的方式减少重复的文件存储,进一步降低存储开销。 + +- **SELinux 支持:** Overlay2 更好地支持 SELinux(Security-Enhanced Linux),提供了更强大的安全性选项。这对于需要强调容器安全性的生产环境非常重要。 + +- **复制操作的优化:** 在 Overlay2 中,复制操作经过优化,减少了复制数据的需求,提高了效率。这有助于加速容器的构建和启动过程。 + +- **可读性和可维护性:** Overlay2 的存储结构更加清晰和模块化,使得整个系统更易于理解和维护。每个容器有自己的独立文件夹,包含了与该容器相关的所有数据。 + + + +#### 4. overlay copy-up + +- 试图修改一个只读层的文件时,会发生一个称为 “copy-up” 的操作。因为每个镜像层都是只读的,而容器运行时对文件的任何修改都必须发生在容器的顶层,也就是可写层 +- 简化的 copy-up 操作流程: + - **查找文件**:当一个进程请求修改一个文件时,OverlayFS 首先会在堆栈的各个层中从上到下查找该文件 + - **复制文件**:如果该文件位于一个只读层,OverlayFS 会将该文件(包括其元数据)复制到最上层的可写层中。这个过程称为 “copy-up” + - **修改文件**:一旦文件被复制到可写层,进程就可以在这个副本上执行修改操作 +- Copy-up 操作使得容器可以在不更改下层镜像的情况下修改文件,保持了镜像的不可变性,并确保了容器间的文件隔离。容器可以基于同一个镜像启动,但它们的文件系统状态可以是独立且不同的 + +### 三、Aufs 存储驱动简介 + +- Advanced Multi-Layered Unification Filesystem +- 允许将多个目录层(layers)叠加(overlay)在一起,形成一个单一的、统一的文件系统视图 +- AUFS 最初是为了提高容器技术(如 Docker)的效率而设计的,它通过允许文件系统层的重用和共享,来减少磁盘占用和提高文件操作的效率 +- 随着 OverlayFS 的出现和成熟(特别是 `overlay2` 驱动),以及 AUFS 的一些限制(如对 Linux 内核的补丁需求和一些性能问题),Docker 社区和 Docker 官方推荐的存储驱动已逐渐从 AUFS 过渡到了 `overlay2` +- OverlayFS 提供了类似的功能,但通常与 Linux 内核更兼容,且性能更优。AUFS 在某些 Linux 发行版中可能不是默认安装的,它可能需要额外的内核支持或第三方模块 + +### 四、**Device Mapper** + +- 一个 Linux 内核的存储驱动,它使用了设备映射框架(Device Mapper)来管理层(layer)和快照。在 Docker 中,`devicemapper` 曾经是一个用于管理容器和镜像存储的流行选择 +- `devicemapper` 的工作原理是通过创建虚拟的磁盘设备(称为逻辑卷),为容器和镜像提供存储空间。这些逻辑卷由 Device Mapper 来管理,它可以动态地创建、删除和调整大小 +- 运行模式: + - **Loop-lvm**:这是 `devicemapper` 的默认模式,它使用已存在的文件(loopback 文件)作为逻辑卷。这种模式不需要额外的分区或物理磁盘,但性能较差,因为它没有直接访问底层存储设备 + - **Direct-lvm**:这种模式提供了更好的性能,因为它使用物理磁盘的分区(通常是 LVM 卷组)作为存储的后端。在 Direct-lvm 模式中,每个容器和镜像都有自己的逻辑卷,这些逻辑卷可以进行快照操作以支持 Docker 镜像层 +- `devicemapper` 还支持许多高级功能,例如: + - **存储配额**:可以为每个容器设置磁盘空间配额,防止容器使用过多的磁盘空间 + - **快照和克隆**:支持创建逻辑卷的快照,这可以用于备份或在容器之间共享数据 + - **薄置备**:薄置备(Thin Provisioning)允许在物理存储资源不足的情况下创建逻辑卷,这可以提高存储的灵活性和效率 +- 缺点: + - 配置的复杂性和相对较高的维护要求 + - 需要仔细的存储管理和监控,以防止存储空间耗尽 + +### 五、VFS + +- 一种简单的存储机制,它不会尝试去重用或共享层之间的数据 +- 在 VFS 中,每个容器都有自己的完整文件系统副本,即使是相同的文件或目录也会在每个容器中分别存储多份副本 +- 每次启动新容器或构建新镜像层时,VFS 都会创建一个完整的文件系统副本,导致磁盘空间的快速消耗和性能下降 +- 特点: + - **简单性**:VFS 驱动非常简单,没有复杂的配置和依赖关系,因此它通常用于测试和调试 + - **不依赖特定文件系统**:VFS 不依赖任何底层的文件系统特性,因此它可以在任何文件系统上运行 + - **不进行层共享**:由于没有层共享,每个容器的文件系统都是独立的,这意味着每个容器的存储是完全隔离的 + +### 六、btrfs + +- 一种Linux 文件系统 +- 支持以下高级功能: + - **Copy-on-Write(COW)**:`btrfs` 使用 COW 技术,这意味着当文件被修改时,只有更改的部分会被写入新的位置,原有数据保持不变 + - **快照**:`btrfs` 支持快速创建文件系统的快照,这对于 Docker 镜像和容器的版本控制非常有用 + - **子卷**:`btrfs` 支持创建子卷,这些子卷可以被用来为 Docker 容器和镜像提供独立的文件系统环境 + - **透明压缩**:`btrfs` 支持在文件系统级别进行数据压缩,这有助于减少磁盘空间的使用 + - **去重**:`btrfs` 支持数据去重,这可以进一步提升存储效率,尤其是在存储大量重复数据时 + - **内置 RAID 支持**:`btrfs` 支持多种 RAID 级别,包括 RAID 0、1、10、5 和 6,这些都是通过软件实现的 + - **动态卷管理和扩展**:`btrfs` 支持在线动态调整卷的大小,这使得在现有文件系统上添加更多存储空间变得非常简单 + +- 在 Docker 中使用 `btrfs` 存储驱动时,每个容器和镜像层都可以映射到 `btrfs` 的子卷和快照。这允许 Docker 高效地创建和管理容器数据 + + + +### 七、ZFS + +- ZFS(Zettabyte File System)是一个高级的文件系统和逻辑卷管理器,设计用来处理大量数据并提供极高的数据完整性。它集成了文件系统和卷管理器的功能,意味着你可以直接在 ZFS 池上创建文件系统,而不需要单独的卷管理器 +- 先进特性: + - **Copy-on-Write (COW)**:ZFS 是一个 COW 文件系统,这意味着当数据被修改时,原始数据不会被覆盖,而是会将改变的部分写入新位置 + - **快照和克隆**:ZFS 支持无性能损失的快照功能,快照是文件系统的只读版本。基于快照,你可以创建克隆,即快照的可写副本 + - **数据完整性**:ZFS 使用校验和来确保数据的完整性,它可以自动检测和修复数据损坏,即使在单磁盘系统上也是如此 + - **内建 RAID 支持**:ZFS 支持多种级别的 RAID 配置,包括 RAID-Z(类似于 RAID-5 或 RAID-6),无需额外的硬件或软件 + - **动态条带化**:在 ZFS 中,数据自动分布在所有可用磁盘上,提高了性能和空间利用率 + - **透明压缩**:ZFS 支持在文件系统级别的数据压缩,这有助于节省空间 + - **存储池**:ZFS 使用存储池的概念来管理物理存储,你可以轻松地向存储池添加新的存储设备,以扩展容量 \ No newline at end of file diff --git a/note/devops/docker/核心原理.md b/note/devops/docker/核心原理.md new file mode 100644 index 0000000..f281629 --- /dev/null +++ b/note/devops/docker/核心原理.md @@ -0,0 +1,9 @@ +Docker 是一个开源的应用容器引擎,它的核心原理主要基于以下几个方面: + +1. **容器化技术**:Docker 使用了 Linux 内核的一些特性(如 cgroups 和 namespaces)来实现容器化。容器内的应用程序和宿主机共享同一个内核,但是在文件系统、网络、进程树等方面,容器内的应用程序和宿主机是隔离的。这使得容器非常轻量级,启动速度非常快。 +2. **镜像**:Docker 使用镜像(Image)来打包和分发应用程序。Docker 镜像是只读的,包含了运行应用程序所需的所有内容,包括代码、运行时、库、环境变量和配置文件。 +3. **层次存储**:Docker 镜像是分层的,每一层都对应 Dockerfile 中的一条指令。这种分层的结构使得 Docker 镜像的复用、共享和修改变得非常容易。当构建镜像或启动容器时,Docker 会使用 Union File System(联合文件系统)来将这些层组合到一起,形成一个统一的视图。 +4. **Docker Daemon**:Docker Daemon 是一个后台服务进程,负责构建、运行和管理 Docker 容器。用户通过 Docker Client(命令行或 API)和 Docker Daemon 进行交互。 +5. **网络**:Docker 提供了多种网络模式,如 bridge 网络、host 网络和 overlay 网络,以满足不同的网络需求。Docker 也支持用户自定义网络。 +6. **存储**:Docker 提供了多种存储选项,如 bind mount、volume 和 tmpfs,以满足不同的存储需求。Docker 也支持用户自定义存储驱动。 + diff --git a/note/devops/docker/远程管理docker.md b/note/devops/docker/远程管理docker.md new file mode 100644 index 0000000..1e9c31f --- /dev/null +++ b/note/devops/docker/远程管理docker.md @@ -0,0 +1,33 @@ +### /etc/docker/daemon.json + +- 在原有基础上增加hosts部分 + +```json +{ + "hosts": ["unix:///var/run/docker.sock", "tcp://0.0.0.0:2375"] +} +``` + + + +### docker启动脚本,systemctl service方式 + +- 停止docker,systemctl stop docker + +- 修改/usr/lib/systemd/system/docker.service,保留 dockerd就可以 + + ![image-20240914172847794](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/typora/image-20240914172847794.png) + +- systemctl daemon-reload +- systemctl start docker +- netstat -nlp | grep 2375 +- lsof -i:2375 + + + +### 远程连接 + +```bash +docker -H192.168.0.3 ps +``` + diff --git a/note/devops/etcd/etcd.md b/note/devops/etcd/etcd.md new file mode 100644 index 0000000..f6398c3 --- /dev/null +++ b/note/devops/etcd/etcd.md @@ -0,0 +1,159 @@ +> 参考Grafana官方部署文档 +> +> https://grafana.com/docs/grafana/latest/setup-grafana/installation/kubernetes/ + +### 创建命名空间 + +```bash +kubectl create ns monitor +``` + +### 创建PVC + +```yaml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: grafana-pvc + namespace: monitor +spec: + storageClassName: nfs-client + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi + +``` + +### 创建ConfigMap + +> 参考 Grafana官方文档 +> +> https://grafana.com/docs/grafana/latest/setup-grafana/installation/kubernetes/#increasing-log-levels-to-debug-mode + +- 准备grafana.ini 配置文件 +- 可以先不挂载grafana配置文件部署,然后去pod里边查看需要的配置内容在新建ini文件 + +```bash +kubectl create configmap grafana-config --from-file=grafana.ini --namespace=monitor +``` + + + +### 创建Deployment + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + labels: + app: grafana + name: grafana + namespace: monitor +spec: + selector: + matchLabels: + app: grafana + template: + metadata: + labels: + app: grafana + spec: + securityContext: + fsGroup: 472 + supplementalGroups: + - 0 + containers: + - name: grafana + image: grafana/grafana:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3000 + name: http-grafana + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /robots.txt + port: 3000 + scheme: HTTP + initialDelaySeconds: 10 + periodSeconds: 30 + successThreshold: 1 + timeoutSeconds: 2 + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 30 + periodSeconds: 10 + successThreshold: 1 + tcpSocket: + port: 3000 + timeoutSeconds: 1 + resources: + requests: + cpu: 250m + memory: 750Mi + volumeMounts: + - mountPath: /var/lib/grafana + name: grafana-pv + - mountPath: /etc/grafana # 没有配置ini文件时可以注释此部分 + name: grafana-config + volumes: + - name: grafana-pv + persistentVolumeClaim: + claimName: grafana-pvc + - name: grafana-config # 没有配置ini文件时可以注释此部分 + configMap: + name: grafana-config + +``` + +### 创建Service + +- ClusterIP 模式,也可以NodePort或LoadBalancer模式,我用ingress,所以使用的ClusterIP模式 + +```yaml +apiVersion: v1 +kind: Service +metadata: + name: grafana + namespace: monitor +spec: + ports: + - port: 3000 + protocol: TCP + targetPort: http-grafana + selector: + app: grafana + sessionAffinity: None + type: ClusterIP + +``` + +### 创建Ingress基于trafik + +```yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: kubepi-traefik-ingress + namespace: monitor + annotations: + traefik.ingress.kubernetes.io/router.entrypoints: web +spec: + ingressClassName: "traefik" + rules: + - host: home.heysq.com + http: + paths: + - backend: + service: + name: grafana + port: + number: 3000 + path: /grafana + pathType: Prefix + +``` + diff --git a/note/devops/gitlab/docker nginx 容器转发 gitlab.md b/note/devops/gitlab/docker nginx 容器转发 gitlab.md new file mode 100644 index 0000000..54fbd26 --- /dev/null +++ b/note/devops/gitlab/docker nginx 容器转发 gitlab.md @@ -0,0 +1,25 @@ +``` +server { + listen *:9090; + server_name gitlab.heysq.com; + server_tokens off; + root /opt/gitlab/embedded/service/gitlab-rails/public; + client_max_body_size 250m; + access_log /var/log/gitlab/gitlab_access.log; + error_log /var/log/gitlab/gitlab_error.log; + + location / { + proxy_read_timeout 300; # Some requests take more than 30 seconds. + proxy_connect_timeout 300; # Some requests take more than 30 seconds. + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Frame-Options SAMEORIGIN; + + proxy_pass http://10.44.147.76:9090; + } +} +``` \ No newline at end of file diff --git a/note/devops/gitlab/gitlab 使用外部 nginx.md b/note/devops/gitlab/gitlab 使用外部 nginx.md new file mode 100644 index 0000000..1d2e6f3 --- /dev/null +++ b/note/devops/gitlab/gitlab 使用外部 nginx.md @@ -0,0 +1,56 @@ +### /etc/gitlab/gitlab.rb +``` +gitlab_rails['trusted_proxies'] = ['192.168.110.0/24'] +web_server['external_users'] = ['www-data'] +nginx['enable'] = false +``` + +### gitlab.conf +``` +upstream gitlab { + server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket; +} + +server { + listen *:8443; + listen [::]:8443; + server_name igit.heysq.com; + server_tokens off; + root /opt/gitlab/embedded/service/gitlab-rails/public; + client_max_body_size 250m; + access_log /var/log/gitlab/gitlab_access.log; + error_log /var/log/gitlab/gitlab_error.log; + + location / { + # serve static files from defined root folder;. + # @gitlab is a named location for the upstream fallback, see below + try_files $uri $uri/index.html $uri.html @gitlab; + } + + location @gitlab { + # If you use https make sure you disable gzip compression + # to be safe against BREACH attack + + proxy_read_timeout 300; # Some requests take more than 30 seconds. + proxy_connect_timeout 300; # Some requests take more than 30 seconds. + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Frame-Options SAMEORIGIN; + + proxy_pass http://gitlab; + } + + location ~ ^/(assets)/ { + root /opt/gitlab/embedded/service/gitlab-rails/public; + # gzip_static on; # to serve pre-gzipped version + expires max; + add_header Cache-Control public; + } + + error_page 502 /502.html; +} +``` \ No newline at end of file diff --git a/note/devops/gitlab/gitlab 14 轻量配置.md b/note/devops/gitlab/gitlab 14 轻量配置.md new file mode 100644 index 0000000..dcdf7cb --- /dev/null +++ b/note/devops/gitlab/gitlab 14 轻量配置.md @@ -0,0 +1,78 @@ +```ruby +external_url 'https://igit.heysq.com:9090' +gitlab_rails['time_zone'] = 'Asia/Shanghai' + +#关闭电子邮件相关功能 + +gitlab_rails['smtp_enable'] = false +gitlab_rails['gitlab_email_enabled'] = false +gitlab_rails['incoming_email_enabled'] = false + +#Terraform + +gitlab_rails['terraform_state_enabled'] = false + +#Usage Statistics + +gitlab_rails['usage_ping_enabled'] = false +gitlab_rails['sentry_enabled'] = false +grafana['reporting_enabled'] = false + +#关闭容器仓库功能 + +gitlab_rails['gitlab_default_projects_features_container_registry'] = false +gitlab_rails['registry_enabled'] = false +registry['enable'] = false +registry_nginx['enable'] = false + +#包仓库 + +gitlab_rails['packages_enabled'] = false +gitlab_rails['dependency_proxy_enabled'] = false + +#GitLab KAS + +gitlab_kas['enable'] = false +gitlab_rails['gitlab_kas_enabled'] = false + +#Mattermost + +mattermost['enable'] = false +mattermost_nginx['enable'] = false + +#Kerberos + +gitlab_rails['kerberos_enabled'] = false +sentinel['enable'] = false + +#GitLab Pages + +gitlab_pages['enable'] = false +pages_nginx['enable'] = false + +#禁用 PUMA 集群模式 + +puma['worker_processes'] = 0 +puma['min_threads'] = 1 +puma['max_threads'] = 2 + +#降低后台守护进程并发数 + +sidekiq['max_concurrency'] = 5 + +gitlab_ci['gitlab_ci_all_broken_builds'] = false +gitlab_ci['gitlab_ci_add_pusher'] = false + +#关闭监控 + +prometheus_monitoring['enable'] = false +alertmanager['enable'] = false +node_exporter['enable'] = false +redis_exporter['enable'] = false +postgres_exporter['enable'] = false +pgbouncer_exporter['enable'] = false +gitlab_exporter['enable'] = false +grafana['enable'] = false +sidekiq['metrics_enabled'] = false +``` + diff --git a/note/devops/gitlab/gitlab 下载安装.md b/note/devops/gitlab/gitlab 下载安装.md new file mode 100644 index 0000000..ed63360 --- /dev/null +++ b/note/devops/gitlab/gitlab 下载安装.md @@ -0,0 +1,3 @@ +wget --content-disposition https://packages.gitlab.com/gitlab/gitlab-ce/packages/ubuntu/xenial/gitlab-ce_8.17.8-ce.0_amd64.deb/download.deb + +https://packages.gitlab.com/app/gitlab/gitlab-ce/search?dist=ubuntu&filter=debs&page=86&q= \ No newline at end of file diff --git a/note/devops/gitlab/gitlab 低版本登录报错.md b/note/devops/gitlab/gitlab 低版本登录报错.md new file mode 100644 index 0000000..f3ac2d3 --- /dev/null +++ b/note/devops/gitlab/gitlab 低版本登录报错.md @@ -0,0 +1,11 @@ +报错内容 如下: +gitlab BCrypt::Errors::InvalidHash (invalid hash): app/controllers/sessions_controller.rb:24:in new + + +### 解决方案一 +https://juejin.cn/post/6943220883888865293 +修改文件 +find / -iname engine.rb +vim /opt/gitlab/embedded/lib/ruby/gems/2.3.0/gems/bcrypt-3.1.11/lib/bcrypt/engine.rb +这行代码后加 .gsub(/(\n|\x00).*/, '') 即可 +__bc_crypt(secret.to_s, salt).gsub(/(\n|\x00).*/, '') diff --git a/note/devops/gitlab/gitlab 架构.md b/note/devops/gitlab/gitlab 架构.md new file mode 100644 index 0000000..fea7178 --- /dev/null +++ b/note/devops/gitlab/gitlab 架构.md @@ -0,0 +1,417 @@ +# [原文](https://chegva.com/3229.html) +# [CHEGVA](https://chegva.com/ "CHEGVA") + +## 让我们面对现实 让我们忠于理想 + +- [😺 关于我](https://chegva.com/an/) +- [👧 我的崽崽](https://chegva.com/yaya/) +- [✪ 切·格瓦拉与自由精神](https://chegva.com/chegva/) +- [🌈 每日诗文](https://chegva.com/poem_article/) +- [📣 说说动态](https://chegva.com/anzhihe/) +- [☕ 博客时光](https://chegva.com/archive/) +- [🤖 站点地图](https://chegva.com/sitemap-html/) +- [📸 照片墙](https://chegva.com/album/) +- [🎶 音乐墙](https://chegva.com/music/) +- [🎮 轻松一下](https://chegva.com/gameoflife/) +- [💌 给我留言](https://chegva.com/contact/) + +### ⛳ 公众号 + +### ✈ 找一找 + +### ☰ 分类目录 + +- [linux相关](https://chegva.com/linux/) (22) +- [操作系统](https://chegva.com/system/) (24) +- [常用工具](https://chegva.com/tools/) (55) +- [存储技术](https://chegva.com/storage/) (23) +- [网络技术](https://chegva.com/network/) (49) +- [DNS与IPv6实战](https://chegva.com/dns_ipv6/) (11) +- [数据库](https://chegva.com/database/) (23) +- [编程学习](https://chegva.com/program/) (20) +- [Shell编程](https://chegva.com/shell/) (48) +- [Python开发](https://chegva.com/python/) (136) +- [Go语言学习](https://chegva.com/golang/) (15) +- [Java学习](https://chegva.com/java/) (14) +- [Web前端](https://chegva.com/frontend/) (35) +- [HTTP与WEB服务](https://chegva.com/http_webservice/) (28) +- [SaltStack实战](https://chegva.com/saltstack/) (8) +- [虚拟化与云计算](https://chegva.com/calculate/) (11) +- [OpenStack实战](https://chegva.com/openstack/) (19) +- [Docker容器技术](https://chegva.com/docker/) (21) +- [Kubernetes & 云原生](https://chegva.com/k8s_cloudnative/) (18) +- [持续集成交付(CI/CD)](https://chegva.com/continuous-integration/) (32) +- [日志服务实战](https://chegva.com/logservice/) (12) +- [Zabbix监控体系](https://chegva.com/zabbix-monitor/) (19) +- [软件测试](https://chegva.com/test/) (7) +- [安全防护](https://chegva.com/security/) (16) +- [性能优化及排错](https://chegva.com/optimize/) (36) +- [高效工作](https://chegva.com/work/) (53) +- [个人随笔](https://chegva.com/essay/) (43) +- [人生感悟](https://chegva.com/life/) (25) +- [我的崽崽](https://chegva.com/mybaby/) (27) +- [志合的诗](https://chegva.com/zhihe-poem/) (40) +- [儒释道禅](https://chegva.com/philosophy/) (43) +- [大哉周易](https://chegva.com/yijing/) (33) +- [天命无算](https://chegva.com/destiny/) (11) +- [历史科学趣闻](https://chegva.com/history_science/) (29) +- [读书笔记](https://chegva.com/read/) (33) +- [电影动漫](https://chegva.com/movie/) (56) +- [诗歌分享](https://chegva.com/poem/) (40) +- [美文分享](https://chegva.com/share/) (40) +- [资源分享](https://chegva.com/resource/) (24) +- [游戏分享](https://chegva.com/game/) (28) +- [公益讲座](https://chegva.com/lecture/) (9) + +### ✎ 近期文章 + +- [游戏里的无聊人生(4)——抢榜](https://chegva.com/5731.html) +- [运维工作核心关注点与运维军规](https://chegva.com/5727.html) +- [ubuntun 网络设置](https://chegva.com/5720.html) +- [游戏里的无聊人生(3)——合区](https://chegva.com/5717.html) +- [Docker格式化命令输出](https://chegva.com/5716.html) +- [游戏里的无聊人生(2)——魔神](https://chegva.com/5715.html) +- [游戏里的无聊人生(1)——开篇](https://chegva.com/5714.html) +- [王小波经典语录名句精选](https://chegva.com/5711.html) +- [Python虚拟环境(4)—pipenv使用](https://chegva.com/5700.html) +- [ChatGPT资料汇总学习](https://chegva.com/5694.html) + +### ♚ 大家正在看 + +- [Vue.js 在线挑战平台](https://chegva.com/5375.html "Vue.js 在线挑战平台") \- 1,406 views +- [达摩禅师全集(悟性论、血脉论、破相论、四行观)](https://chegva.com/2605.html "达摩禅师全集(悟性论、血脉论、破相论、四行观)") \- 623 views +- [跟我一起写Makefile (PDF重制版)](https://chegva.com/3761.html "跟我一起写Makefile (PDF重制版)") \- 614 views +- [暗黑破坏神系列剧情全集](https://chegva.com/5408.html "暗黑破坏神系列剧情全集") \- 519 views +- [中国古代著名关隘大全](https://chegva.com/4028.html "中国古代著名关隘大全") \- 366 views +- [劝学网](https://chegva.com/1092.html "劝学网") \- 354 views +- [《星际争霸》快捷键及操作指南技巧](https://chegva.com/2471.html "《星际争霸》快捷键及操作指南技巧") \- 353 views +- [Gitlab更改项目的owner](https://chegva.com/3375.html "Gitlab更改项目的owner") \- 294 views +- [星际争霸官方小说全集下载](https://chegva.com/3221.html "星际争霸官方小说全集下载") \- 273 views +- [关闭浏览器自带的 WebRTC 功能,防止暴露真实ip](https://chegva.com/3338.html "关闭浏览器自带的 WebRTC 功能,防止暴露真实ip") \- 271 views +- [IPv6实战(二)—— IPv6 Header详解](https://chegva.com/3298.html "IPv6实战(二)—— IPv6 Header详解") \- 222 views +- [GitLab实战三——Gitlab架构组件详解](https://chegva.com/3229.html "GitLab实战三——Gitlab架构组件详解") \- 210 views + +### ⛅ 标签云 + +### ☘ 随机文章 + +### ☯ 传统文化 + +### ⌚ 博客统计 + +### ☺ 近期评论 + +### ♡ 友情链接 + +# GitLab实战三——Gitlab架构组件详解 + +[2019年1月14日](https://chegva.com/3229.html) by [anzhihe](https://chegva.com/author/anzhihe/)·[0评论](https://chegva.com/3229.html#respond) · 13,720 人阅读 · 隐藏边栏 + +## 1.[Gitlab](https://chegva.com/tag/gitlab/ "View all posts in Gitlab")架构 + +> GitLab有两个软件发行版:开源社区版(CE)和企业版(EE)。新版本的GitLab在稳定的分支机构中发布,主分支用于前沿开发。新版本通常与GitLab CE版本大致同时发布,除非有重要的安全更新。Gitlab生态现在主要由以下三大模块组成: + +[![GitLab实战三——Gitlab架构组件详解](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034474333344.png "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034474333344.png "1545034474333344.png") + +#### **官方架构图:** + +[![GitLab实战三——Gitlab架构组件详解](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034504273781.png "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034504273781.png "1545034504273781.png")](https://cdn.chegva.com/wp-content/plugins/Ueditor/ueditor/themes/default/images/spacer.gif) + +Gitlab通常安装在`GNU/Linux`上。使用`Nignx`或`Apache` 作为Web前端将请求代理到`Unicorn Web` 服务器,默认情况下,Unicorn和前端通过Unix套接字来通信,也支持使用TCP来转发请求。Web前端访问`/home/git/gitlab/public` 绕过Unicorn服务器以提供静态页面,上传(例如头像图片或附件)和预编译资源。 GitLab使用Unicorn Web服务器提供网页和 [GitLab API](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/api)。使用`Sidekiq`作为作业队列,而后者又使用redis作为作业信息,元数据和传入作业的非持久数据库后端。 + +Gitlab 应用程序使用 `MySQL` 或 `PostgreSQL` 作为持久数据库,用来保存用户,权限,issue,其他元数据等 git repository 默认存储在 `/home/git/repositories` 中,保留默认分支和 hooks 信息。 + +当通过 HTTP/HTTPS 提供 repository 时,GitLab 使用 GitLab API 来解析授权和访问以及提供 git 对象。 + +`gitlab-shell` 通过 SSH 提供 repository。它管理 `/home/git/.ssh/authorized_keys` 内的 SSH 密钥,不应手动编辑。gitlab-shell 通过 Gitaly 访问 bare repository 以提供 git 对象并与 redis 进行通信以向 Sidekiq 提交作业以供 GitLab 处理。gitlab-shell 查询 GitLab API 以确定授权和访问。 + +`Gitaly` 从 gitlab-shell 和 GitLab web 应用中获取操作命令,执行 git 操作,并为 GitLab web 应用程序提供 API 以从 git 获取属性(例如 title,branches,tags,其他元数据)和 blob(例如 diffs,commits ,files)。 + +Gitlab的生产架构可以[参考这里](https://about.gitlab.com/handbook/engineering/infrastructure/production-architecture/)。 + +#### **Gitlab组件:** + +- `repository`:代码库,可以是硬盘或 分布式文件系统 + +- `Nginx`:Web 入口 + +- `gitlab-workhorse`:轻量级反向代理服务器,可以处理一些大的HTTP请求(磁盘上的 CSS、JS 文件、文件上传下载等),处理 Git Push/Pull 请求,处理到Rails 的连接会反向代理给后端的unicorn(修改由 Rails 发送的响应或发送给 Rails 的请求,管理 Rails 的长期 WebSocket 连接等)。 + +- `gitlab-shell`:用于 SSH 交互,而不是 HTTP。gitlab-shell 通过 Redis 与 Sidekiq 进行通信,并直接或通过 TCP 间接访问 Unicorn。用于处理Git命令和修改authorized keys列表 + +- `Unicorn`:Gitlab 自身的 Web 服务器(Ruby Web Server),包含了 Gitlab 主进程,负责处理快速/一般任务,与 Redis 一起工作,配置参考:`CPU核心数 + 1 = unicorn workers数量`。工作内容包括: + + +- 通过检查存储在 Redis 中的用户会话来检查权限 + +- 为 Sidekiq 制作任务 + +- 从仓库(warehouse)取东西或在那里移动东西 + + +- `Redis`:缓存每个客户端的sessions和后台队列,负责分发任务。Redis需求的存储空间很小,大约每个用户25KB + +- `Gitaly`:后台服务,专门负责访问磁盘以高效处理 gitlab-shell 和 gitlab-workhorse 的git 操作,并缓存耗时操作。所有的 git 操作都通过 Gitaly 处理,并向 GitLab web 应用程序提供一个 API,以从 git(例如 title, branches, tags, other meta data)获取属性,并获取 blob(例如 diffs,commits,files) + +- `Sidekiq`:后台核心服务,可以从redis队列中提取作业并对其进行处理。后台作业允许GitLab通过将工作移至后台来提供更快的请求/响应周期。Sidekiq任务需要来自Redis + +- `数据库(PostgreSQL/MySQL)`:包含以下信息: + + +- repository 中的数据(元数据,issue,合并请求 merge request 等) + +- 可以登录 Web 的用户(权限) + + +- `mail_room`:处理邮件请求。回复 GitLab 发出的邮件时,GitLab 会调用此服务处理Sidekiq、Unicorn 和 GitLab-shell 的任务 + +- `logrotate`:日志文件管理,切割 + + +#### **Gitlab-shell组件:** + +GitLab Shell 的作用:`处理 Git 命令`、`修改 authorized keys 列表`。GitLab Shell 不是 Unix shell,也不是 Bash 或 Zsh 的替代品。早期的 gitlab-shell 的实现方式为 gitolite。 + +当通过 `SSH` 访问 GitLab Server 时,GitLab Shell 会: + +- 限制执行预定义好的Git命令(git push, git pull) + +- 调用 GitLab Rails API,检查用户权限 + +- 执行 pre-receive 钩子(在 GitLab 企业版中叫做 Git 钩子) + +- 执行你请求的动作 + +- 处理 GitLab 的 post-receive 动作 + +- 处理自定义的 post-receive 动作 + + +git pull over ssh  - > gitlab-shell  - > API调用gitlab-rails(授权) - >接受或拒绝 - >建立Gitaly会话 + +git push over ssh  - > gitlab-shell(git命令尚未执行) - >建立Gitaly会话 - >(在Gitaly中)gitlab-shell pre-receive hook  - > API调用gitlab-rails(授权) - >接受或拒绝push + +当通过 `http(s)` 访问 GitLab Server 时,工作流程取决于你是从 Git 仓库拉取(pull)代码,还是向 git 仓库推送(push)代码。 + +- 如果你是从 Git 仓库 pull 代码,GitLab Rails 应用会全权负责处理用户鉴权和执行 Git 命令的工作 + +- 如果你是向 Git 仓库 push 代码,GitLab Rails 应用既不会进行用户鉴权也不会执行 Git 命令,它会把以下工作交由 GitLab Shell 进行处理: + +- 调用GitLab Rails API 检查权限 + +- 执行pre-receive 钩子 + +- 执行你请求的动作 + +- 处理 GitLab 的 post-receive 动作 + +- 处理自定义的 post-receive 动作 + + +也许你会奇怪在通过 http(s) push 代码的情况下,GitLab Rails应用为什么不在 GitLab Shell之前进行鉴权。这是因为 GitLab Rails 应用没有解析 git push 命令的逻辑。好的方法是将这些解析代码放在一个地方,这个地方就是 GitLab Shell,这样我们就可以在通过 SSH 进行访问时重用这段代码。实际上,GitLab Shell 在执行 git push 命令时根本不会进行权限检查,它是依赖于 pre-receive 钩子进行权限检查的。而当你执行 git pull 命令时,权限检查是在命令执行之前的。对 git pull 命令的权限检查要简单得多,因为你只需要检查一个用户是否可以访问这个仓库就可以了(不需要检查分支权限)。 + +#### **Gitlab-workhorse组件:** + +- Workhorse 可以处理一些请求,而不涉及 Rails:例如,Javascript 文件和 CSS 文件直接从磁盘载入 + +- Workhorse 可以修改 Rails 发送的响应:例如,如果你在 Rails 中使用 send_file,那么 gitlab-workhorse 将打开磁盘上的文件,并将其内容作为响应体发送给客户端 + +- Workhorse 可以在从 Rails 请求权限后接管请求。 例如:处理 git clone 动作 + +- Workhorse 可以在将请求传递给 Rails 之前对其进行修改。 例如:处理 Git LFS 上传 Workhorse 首先向 Rails 请求权限,然后将请求主体存储在临时文件中,然后它将包含临时文件路径的修改后的请求发送到 Rails + +- Workhorse 可以管理 Rails 的长期 WebSocket 连接。 例如:处理环境的终端 websocket + +- Workhorse 不连接 Redis 或 Postgresql,只连接到 Rails + +- 我们假设所有到达 Workhorse 的请求首先通过一个上游代理,如 NGINX 或 Apache + +- Workhorse 不接受 HTTPS 连接 + +- Workhorse 不清除空闲客户端连接 + +- 我们假设所有对 Rails 的请求都通过 Workhorse + + +#### **Gitaly组件:** + +Gitaly是一个Git RPC服务,使用gRPC协议与客户端通信,用于处理GitLab发出的所有git调用。[详细介绍](https://gitlab.com/gitlab-org/gitaly-proto) + +[![GitLab实战三——Gitlab架构组件详解](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034531481913.png "1545034531481913.png")[![GitLab实战三——Gitlab架构组件详解](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/wp-content/plugins/Ueditor/ueditor/themes/default/images/spacer.gif) + +#### **Gitlab老版架构图(< 9.0):** + +[![GitLab实战三——Gitlab架构组件详解](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/wp-content/plugins/Ueditor/ueditor/themes/default/images/spacer.gif)[![GitLab实战三——Gitlab架构组件详解](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034544666644.png "1545034544666644.png") + +## 2.Gitlab结构及配置 + +`~/git` 表示 git 用户的主目录,通常是 `/home/git`。 + +GitLab 在安装时,会创建 git 用户,并安装在 `/home/git` 目录中。在主目录中是 gitlabhq 服务器软件以及 repository 所在的位置,repository 的位置可以单独配置。 + +repository 位于 `/home/git/repositories` 中。GitLab 是一款 Ruby on Rails 应用程序,因此可以通过研究 Ruby on Rails 应用程序的工作方式来了解内部工作的细节。 + +要通过 SSH 提供 repository,可以使用 gitlab-shell 这个附加应用程序,该应用程序安装在 `/home/git/gitlab-shell` 中。 + +#### **Gitlab目录结构:** + +[![GitLab实战三——Gitlab架构组件详解](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/ueditor/php/upload/image/20181217/1545034668930982.png "1545034668930982.png") + +`/home/git/.ssh` \- 包含 openssh 设置。指定由 gitlab-shell 管理的 authorized_keys 文件。 `/home/git/gitlab` \- GitLab 核心软件。 `/home/git/gitlab-shell` \- GitLab 核心的附加组件。包括 SSH 复制和其他功能。 `/home/git/data/repositories` \- 由命名空间组织的所有项目的裸仓库(bare repository)。这是为所有项目维护推/拉的 git repository 的地方。这是项目的关键数据,最好备份。注意:repository 的默认位置可以在 GitLab 的 `config/gitlab.yml` 和 gitlab-shell 的 `config.yml` 中配置。 + +#### **Gitlab配置文件:** + +Gitlab 的配置文件在 `/home/git/gitlab/config/*`目录下, 常用的有: + +- `gitlab.yml` \- GitLab 配置. + +- `unicorn.rb` \- Unicorn web server 配置. + +- `database.yml` \- 数据库连接配置. + +- `gitaly.yml` \- Gitaly 配置. + + +gitlab-shell 的配置文件在 `/home/git/gitlab-shell/config.yml`. + +#### **Gitlab备份与恢复:** + +\# 执行备份命令 +bundle exec rake gitlab:backup:create RAILS_ENV=production   +\# 备份成功,你会找到一个类似 /home/git/data/backups/1531046569\_2018\_07\_14\_10.6.4\_gitlab\_backup.tar 的文件 + +\# 执行恢复命令 +bundle exec rake gitlab:backup:restore RAILS_ENV=production + +详情请看官网。 + +#### **Gitlab进程:** + +作为系统用户,它需要持久数据库(MySQL/PostreSQL)和 redis 数据库。它还使用 Nginx 来代理 Unicorn。作为 git 用户,它启动 Sidekiq 和 Unicorn(默认情况下运行在端口 8080 上的基于 ruby 的 HTTP 服务器)。在 GitLab 用户下,通常有 4 个进程:`gitlab-workhorse` (1进程) , `unicorn_rails master`(1进程),`unicorn_rails worker`(2进程),`sidekiq`(1进程),`gitaly`(1进程),具体启动进程数以各组件配置文件为准。 + +#### **Repository访问:** + +可以通过 HTTP 或 SSH 访问代码库。HTTP 中的 cloning/push/pull 使用 GitLab API,SSH 克隆由 gitlab-shell 处理。 + +#### **Gitlab环境检查:** + +GitLab 还提供了 rake 任务,可以用来查看版本信息或检查配置是否正确。详情可以参考 [maintenance rake tasks](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/administration/raketasks/maintenance.md)。简而言之: + +sudo -i -u git +cd gitlab +bundle exec rake gitlab:env:info RAILS_ENV=production +bundle exec rake gitlab:check RAILS_ENV=production + +注意:建议使用 `sudo -i -u git` 或 `sudo su - git` 登录 git 用户。虽然 gitlabhq 提供的 sudo 命令在 Ubuntu 中工作,但它们并不总是在 RHEL 中工作。 + +#### **Gitlab服务日志:** + +gitlabhq (includes Unicorn and Sidekiq logs) + +- `/home/git/gitlab/log/` contains `application.log`, `production.log`, `sidekiq.log`, `unicorn.stdout.log`, `githost.log` and `unicorn.stderr.log` normally. + + +gitlab-shell + +- `/home/git/gitlab-shell/gitlab-shell.log` + + +ssh + +- `/var/log/auth.log` auth log (on Ubuntu). + +- `/var/log/secure` auth log (on RHEL). + + +nginx + +- `/var/log/nginx/` contains error and access logs. + + +Apache httpd + +- [Explanation of Apache logs](https://httpd.apache.org/docs/2.2/logs.html). + +- `/var/log/apache2/` contains error and output logs (on Ubuntu). + +- `/var/log/httpd/` contains error and output logs (on RHEL). + + +redis + +- `/var/log/redis/redis.log` there are also log-rotated logs there. + + +PostgreSQL + +- `/var/log/postgresql/*` + + +MySQL + +- `/var/log/mysql/*` + +- `/var/log/mysql.*` + + +#### 参考: + +- https://github.com/gitlabhq/gitlabhq/blob/master/doc/development/architecture.md + +- https://blog.mallux.me/2017/02/27/gitlab/ + +- [https://blog.csdn.net/kikajack/article/details/8035477](https://blog.csdn.net/kikajack/article/details/80354774) + + +[![anzhihe](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%2060%2060%22%3E%3C/svg%3E "GitLab实战三——Gitlab架构组件详解")](https://cdn.chegva.com/wp-content/uploads/2016/06/179f8716-60x60.png) + + 安志合个人博客,版权所有 丨 如未注明,均为原创 丨 转载请注明转自:[https://chegva.com/3229.html](https://chegva.com/3229.html "GitLab实战三——Gitlab架构组件详解") | *☆★★每天进步一点点,加油!★★☆* |  + + **![](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E) 标签:** [持续集成](https://chegva.com/tag/%e6%8c%81%e7%bb%ad%e9%9b%86%e6%88%90/) [Gitlab](https://chegva.com/tag/gitlab/) + +### ![](data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20viewBox=%220%200%20210%20140%22%3E%3C/svg%3E) 您可能还感兴趣的文章! + +* * * + +- [Gitlab调用CAS新用户登陆报422错误无法获取邮箱问题](https://chegva.com/3715.html "Gitlab调用CAS新用户登陆报422错误无法获取邮箱问题") +- [Gitlab sidekiq队列频繁崩溃问题解决](https://chegva.com/3712.html "Gitlab sidekiq队列频繁崩溃问题解决") +- [GitLab实战十五——Gitlab 11.0.6 升级至 11.4.14](https://chegva.com/3591.html "GitLab实战十五——Gitlab 11.0.6 升级至 11.4.14") +- [GitLab实战十四——Gitlab 10.8.7 升级至 11.0.6](https://chegva.com/3590.html "GitLab实战十四——Gitlab 10.8.7 升级至 11.0.6") +- [GitLab实战十三——Gitlab 10.0.6 升级至 10.8.7](https://chegva.com/3589.html "GitLab实战十三——Gitlab 10.0.6 升级至 10.8.7") +- [GitLab实战十二——Gitlab 9.3.11 升级至 10.0.6](https://chegva.com/3588.html "GitLab实战十二——Gitlab 9.3.11 升级至 10.0.6") +- [GitLab实战十一——Gitlab 8.11.3 升级至 9.3.11](https://chegva.com/3587.html "GitLab实战十一——Gitlab 8.11.3 升级至 9.3.11") +- [GitLab实战十——Gitlab 8.11 手动编译升级至 11.4](https://chegva.com/3586.html "GitLab实战十——Gitlab 8.11 手动编译升级至 11.4") +- [GitLab实战九——Gitlab CI yaml文件示例](https://chegva.com/3585.html "GitLab实战九——Gitlab CI yaml文件示例") +- [GitLab实战八——Gitlab CI yaml使用](https://chegva.com/3584.html "GitLab实战八——Gitlab CI yaml使用") +- [GitLab实战七——Gitlab CI使用](https://chegva.com/3521.html "GitLab实战七——Gitlab CI使用") +- [shell + python脚本监控gitlab ci停滞任务数](https://chegva.com/3497.html "shell + python脚本监控gitlab ci停滞任务数") +- [shell脚本监控进程挂掉自动重启](https://chegva.com/3471.html "shell脚本监控进程挂掉自动重启") +- [Gitlab使用system hooks新建项目时自动添加webhooks](https://chegva.com/3457.html "Gitlab使用system hooks新建项目时自动添加webhooks") +- [Gitlab Disable /explore and /help](https://chegva.com/3413.html "Gitlab Disable /explore and /help") +- [Gitlab更改项目的owner](https://chegva.com/3375.html "Gitlab更改项目的owner") +- [GitLab实战六——Gitlab集成Openldap & Cas3认证](https://chegva.com/3315.html "GitLab实战六——Gitlab集成Openldap & Cas3认证") +- [Shell脚本更改Gitlab所有用户创建项目限制数](https://chegva.com/3366.html "Shell脚本更改Gitlab所有用户创建项目限制数") +- [GitLab实战五——Gitlab认证授权机制及权限管理](https://chegva.com/3314.html "GitLab实战五——Gitlab认证授权机制及权限管理") +- [GitLab实战四——Gitlab扩展与高可用](https://chegva.com/3234.html "GitLab实战四——Gitlab扩展与高可用") + +## 文章导航 + +[上一 上篇文章: 看一部机器人爱情片——《机器管家 Bicentennial Man》](https://chegva.com/3302.html) + +[继续 下篇文章: 《出师表》](https://chegva.com/3211.html) + +### 发表评论 + +电子邮件地址不会被公开。 必填项已用*标注 + +姓名 * + +电子邮件 * + +站点 + +在此浏览器中保存我的显示名称、邮箱地址和网站地址,以便下次评论时使用。 + +© 2016-2023 + +**安志合** 版权所有 | [*订阅*](https://chegva.com/feed/)  [| 站点地图](https://chegva.com/sitemap.html) [| 站点统计 |](http://tongji.baidu.com/web/welcome/login) 京 ICP17028760 | [由WordPress自豪地提供](http://cn.wordpress.org/ "优雅的个人发布平台"). Theme: Flat by [Themeisle](https://themeisle.com/themes/flat/ "Flat WordPress Theme"). \ No newline at end of file diff --git a/note/devops/gitlab/igit nginx conf.md b/note/devops/gitlab/igit nginx conf.md new file mode 100644 index 0000000..8a927c9 --- /dev/null +++ b/note/devops/gitlab/igit nginx conf.md @@ -0,0 +1,58 @@ +```nginx +upstream gitlab { + server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket; +} + +server { + listen *:8443 ssl; + listen [::]:8443 ipv6only=on ssl; + server_name igit.heysq.com; + server_tokens off; + root /opt/gitlab/embedded/service/gitlab-rails/public; + client_max_body_size 250m; + + access_log /var/log/gitlab/gitlab_access.log; + error_log /var/log/gitlab/gitlab_error.log; + + ssl_certificate /etc/gitlab/ssl/cert.pem; + ssl_certificate_key /etc/gitlab/ssl/key.pem; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + error_page 497 https://$http_host$request_uri; + + location / { + # serve static files from defined root folder;. + # @gitlab is a named location for the upstream fallback, see below + try_files $uri $uri/index.html $uri.html @gitlab; + } + + location @gitlab { + # If you use https make sure you disable gzip compression + # to be safe against BREACH attack + + proxy_read_timeout 300; # Some requests take more than 30 seconds. + proxy_connect_timeout 300; # Some requests take more than 30 seconds. + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Frame-Options SAMEORIGIN; + proxy_pass http://gitlab; + } + + location ~ ^/(assets)/ { + root /opt/gitlab/embedded/service/gitlab-rails/public; + # gzip_static on; # to serve pre-gzipped version + expires max; + add_header Cache-Control public; + } + + error_page 502 /502.html; +} +``` + diff --git a/note/devops/gitlab/发送邮件配置.md b/note/devops/gitlab/发送邮件配置.md new file mode 100644 index 0000000..19bd650 --- /dev/null +++ b/note/devops/gitlab/发送邮件配置.md @@ -0,0 +1,25 @@ +### gitlab.rb添加以下配置 + +``` yaml +gitlab_rails['smtp_enable'] = true +gitlab_rails['smtp_address'] = "smtp.163.com" +gitlab_rails['smtp_port'] = 465 +gitlab_rails['smtp_user_name'] = "15735657423@163.com" +gitlab_rails['smtp_password'] = "HJYXCQIGREDDVHQI" #163授权的密码,非登陆密码 +gitlab_rails['smtp_domain'] = "smtp.163.com" +gitlab_rails['smtp_authentication'] = "login" +gitlab_rails['gitlab_email_from'] = "15735657423@163.com" +gitlab_rails['gitlab_email_reply_to'] = "noreply@gitlab.heysq.com" +gitlab_rails['smtp_tls'] = true +# +``` + +### 测试邮件 + +``` shell +gitlab-rails c -e production +Notify.test_email('1904705132@qq.com', 'Message Subject', 'Message Body').deliver_now +``` + + + diff --git a/note/devops/gitlab/更改 root 用户密码.md b/note/devops/gitlab/更改 root 用户密码.md new file mode 100644 index 0000000..8cd866b --- /dev/null +++ b/note/devops/gitlab/更改 root 用户密码.md @@ -0,0 +1,12 @@ +### sudo 权限用户 +``` +sudo gitlab-rails c -e production +``` + +### console 输入 +``` +user = User.find_by_name("root") +user.password = "xxxxxxxx" +user.password_confirmation = "xxxxxxxx" +user.save! +``` \ No newline at end of file diff --git a/note/devops/kubernetes/前言.md b/note/devops/kubernetes/前言.md new file mode 100644 index 0000000..b7a805c --- /dev/null +++ b/note/devops/kubernetes/前言.md @@ -0,0 +1,20 @@ +### 为什么使用容器? + +为什么是容器? + +传统的应用部署方式是通过插件或脚本来安装应用。这样做的缺点是应用的运行、配置、管理、所有生存周期将与当前操作系统绑定,这样做并不利于应用的升级更新/回滚等操作,当然也可以通过创建虚机的方式来实现某些功能,但是虚拟机非常重,并不利于可移植性。 + +新的方式是通过部署容器方式实现,每个容器之间互相隔离,每个容器有自己的文件系统 ,容器之间进程不会相互影响,能区分计算资源。相对于虚拟机,容器能快速部署,由于容器与底层设施、机器文件系统解耦的,所以它能在不同云、不同版本操作系统间进行迁移。 + +容器占用资源少、部署快,每个应用可以被打包成一个容器镜像,每个应用与容器间成一对一关系也使容器有更大优势,使用容器可以在build或release 的阶段,为应用创建容器镜像,因为每个应用不需要与其余的应用堆栈组合,也不依赖于生产环境基础结构,这使得从研发到测试、生产能提供一致环境。类似地,容器比虚机轻量、更“透明”,这更便于监控和管理。最后, + +容器优势总结: + +- **快速创建/部署应用:**与VM虚拟机相比,容器镜像的创建更加容易。 +- **持续开发、集成和部署:**提供可靠且频繁的容器镜像构建/部署,并使用快速和简单的回滚(由于镜像不可变性)。 +- **开发和运行相分离:**在build或者release阶段创建容器镜像,使得应用和基础设施解耦。 +- **开发,测试和生产环境一致性:**在本地或外网(生产环境)运行的一致性。 +- **云平台或其他操作系统:**可以在 Ubuntu、RHEL、 CoreOS、on-prem、Google Container Engine或其它任何环境中运行。 +- **Loosely coupled,分布式,弹性,微服务化:**应用程序分为更小的、独立的部件,可以动态部署和管理。 +- **资源隔离** +- **资源利用:**更高效 \ No newline at end of file diff --git a/note/devops/kubernetes/命名空间.md b/note/devops/kubernetes/命名空间.md new file mode 100644 index 0000000..9e1ea2d --- /dev/null +++ b/note/devops/kubernetes/命名空间.md @@ -0,0 +1,87 @@ +### 一、命名空间 + +Kubernetes 的命名空间是一种将集群内的资源进行逻辑分割的机制。它们是用来实现多租户环境的一种方式,允许不同的团队或项目在同一个集群中运行,而不会互相干扰。命名空间可以用来组织资源,提供一种隔离机制,并可以用于访问控制、资源配额以及策略的应用 + +### 二、命名空间的主要用途 + +1. **资源分隔**:命名空间可以让资源(如Pods、Services、Deployments等)彼此隔离。不同命名空间中同名的资源不会冲突 +2. **多租户环境**:在同一个集群中,不同的用户(或团队、部门、项目)可以使用各自的命名空间,从而保持工作环境的独立性 +3. **权限控制**:通过Role-Based Access Control(RBAC),可以为不同的命名空间设置不同的访问权限,实现细粒度的权限管理 +4. **配额管理**:可以为每个命名空间设置资源配额(ResourceQuotas),限制该命名空间可以使用的资源数量,例如CPU、内存、存储等 +5. **策略应用**:可以为命名空间应用策略,如网络策略(NetworkPolicies)来控制命名空间内部或跨命名空间的网络流量 + +### 三、默认命名空间 + +- `default`:如果不指定命名空间,资源默认被创建在这个命名空间中 +- `kube-system`:由Kubernetes系统创建的资源所在的命名空间,如系统组件和服务 +- `kube-public`:这是一个自动创建的命名空间,对所有用户都是可读的。它通常用于集群级别的资源,如集群信息(如集群CIDR范围) +- `kube-node-lease`:用于持有每个节点的租约对象,这些租约用于节点寿命的确定 + +### 四、命名空间操作 + +1. 使用kubectl 工具操作 + + - **创建命名空间:** + + ``` + kubectl create namespace + ``` + + - **获取命名空间列表:** + + ``` + kubectl get namespaces + # 或者使用缩写 + kubectl get ns + ``` + + - **获取特定命名空间的详细信息:** + + ``` + kubectl describe namespace + ``` + + - **删除命名空间:** + + ``` + kubectl delete namespace + ``` + + - **设置默认命名空间:** + + 为了避免每次都在`kubectl`命令中指定`--namespace`参数,你可以更改你的Kubernetes配置来设置默认的命名空间: + + ``` + kubectl config set-context $(kubectl config current-context) --namespace= + ``` + + - **查看当前上下文的默认命名空间:** + + ``` + kubectl config view | grep namespace: + ``` + +2. 使用yaml文件 + + - **创建命名空间的YAML文件(namespace.yaml):** + + ``` + apiVersion: v1 + kind: Namespace + metadata: + name: + ``` + + - **应用YAML文件来创建命名空间:** + + ``` + kubectl apply -f namespace.yaml + ``` + + - **删除命名空间:** + + 使用YAML文件删除命名空间,首先你需要确保知道要删除的命名空间的准确名称: + + ``` + kubectl delete -f namespace.yaml + ``` \ No newline at end of file diff --git a/note/devops/kubernetes/对象.md b/note/devops/kubernetes/对象.md new file mode 100644 index 0000000..1dac826 --- /dev/null +++ b/note/devops/kubernetes/对象.md @@ -0,0 +1,21 @@ +### 一、对象概述 + +Kubernetes 中,有很多不同类型的对象,它们是 Kubernetes 系统中持久化的实体。Kubernetes 对象用于表示系统的状态,包括描述哪些容器化应用程序运行在哪些节点上、可以使用哪些资源、应用程序的行为方式(如重启策略、升级和容错)等。一旦创建某个对象, Kubernetes 系统将不断工作以确保该对象存在。通过创建对象,你本质上是在告知 Kubernetes 系统,你想要的集群工作负载状态看起来应是什么样子的。 + +对象通常通过 YAML 或 JSON 文件定义,并且使用 Kubernetes 提供的命令行工具 `kubectl` 创建和管理。每个 Kubernetes 对象都包括两个嵌套的对象字段:`spec` 字段和 `status` 字段。`spec` 字段必须提供,它描述了对象的期望状态,而 `status` 字段描述了对象的当前状态,通常由 Kubernetes 系统自动填充和更新。 + +理解和使用这些对象是在 Kubernetes 中成功部署和管理应用程序的关键。 + +### 二、核心对象: + +1. **Pod**:Pod 是 Kubernetes 中最基本的部署单元,它是一个或多个容器的集合,这些容器共享存储、网络和运行配置。每个 Pod 都有一个独特的 IP 地址,允许容器与其他 Pod 进行交互。 +2. **Service**:Service 是定义一组 Pod 的访问策略的抽象概念。它允许内部或外部的通信到达 Pod 集合,即使这些 Pod 可能随着时间的推移而改变。 +3. **Deployment**:Deployment 为 Pod 和 ReplicaSets 提供声明性的更新。它允许你描述应用的预期状态,Deployment 控制器则负责将当前状态更改为期望状态,比如滚动更新应用程序的版本。 +4. **ReplicaSet**:ReplicaSet 的目的是维护一组可复制的 Pod 副本的稳定运行。它保证指定数量的 Pod 副本始终处于运行状态。 +5. **StatefulSet**:StatefulSet 是用于管理有状态应用程序的对象。与 Deployment 相比,StatefulSet 为每个 Pod 维护一个持久的标识符和存储。 +6. **DaemonSet**:DaemonSet 确保所有(或某些)节点上都运行一个 Pod 的副本。当有节点加入集群时,一个新的 Pod 会自动添加到这些节点上。 +7. **Job**:Job 对象会创建一个或者多个 Pod,并确保指定数量的 Pod 成功终止。当任务需要批处理执行而非持续运行时,Job 对象非常有用。 +8. **CronJob**:CronJob 管理基于时间的 Job,例如在给定时间点只运行一次或者周期性地运行任务。 +9. **Namespace**:Namespace 是多个虚拟集群的抽象,它可以帮助你在同一个物理集群中隔离不同的用户、团队或项目的资源。 +10. **ConfigMap** 和 **Secret**:这些对象允许你将配置项和敏感信息如密码、OAuth 令牌和 ssh 密钥存储和管理,这样你就可以将这些信息从 Pod 容器的定义中解耦。 +11. **Volume**:Volume 是存储相关的对象,它允许数据在容器重启后持久化,并且可以在 Pod 的容器之间共享。 \ No newline at end of file diff --git a/note/devops/kubernetes/标签.md b/note/devops/kubernetes/标签.md new file mode 100644 index 0000000..3f733d2 --- /dev/null +++ b/note/devops/kubernetes/标签.md @@ -0,0 +1,204 @@ +### 一、标签 + +在 Kubernetes(k8s)中,标签(Labels)和标签选择器(Label Selectors)是用于组织和选择资源子集的关键概念。它们是元数据的一部分,可以附加到几乎所有的 Kubernetes 对象上,例如 Pods、Services、Deployments 等 + +标签是键值对,附加到 Kubernetes 对象上,用于指定这些对象的属性,这样可以通过这些属性对它们进行识别和组织。一个标签由一个键(key)和一个值(value)组成,例如: + +```yaml +labels: + app: myapp + env: production +``` + +### 二、标签选择器 + +标签选择器用于从一组对象中选择匹配特定标签的对象。选择器可以是等式式的(equality-based)或者集合式的(set-based)。 + +- 等式式选择器(Equality-based Selectors): + - `=` 或 `==`:选择标签与指定值相等的对象 + - `!=`:选择标签与指定值不相等的对象 +- 集合式选择器(Set-based Selectors): + - `in`:选择标签值在指定集合中的对象 + - `notin`:选择标签值不在指定集合中的对象 + - `exists`(仅通过键指定):选择具有指定键的对象,不论其值为何 + - `does not exist`(通过键前的 `!` 指定):选择不具有指定键的对象 + +选择器可以在 kubectl 命令中使用,也可以在配置文件中指定,用于定义服务或者复制控制器等应该处理哪些 Pods + +- 使用等式式选择器来获取所有 “env” 标签值为 “production” 的 Pods: + +```bash +kubectl get pods -l env=production +``` + +- 使用集合式选择器来选择 “app” 标签值为 “myapp” 或 “yourapp” 的 Pods: + +```bash +kubectl get pods -l 'app in (myapp, yourapp)' +``` + +- yaml中指定标签选择器 + +```yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: myapp-deployment +spec: + replicas: 2 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + env: production + spec: + containers: + - name: myapp-container + image: myapp:1.0 +``` + + + +### 三、标签操作 + +#### 1. 添加标签 + +- 创建 Kubernetes 资源(如 Pods、Deployments、Services 等)时,您可以直接在资源的定义文件中添加标签。例如,为一个 Pod 添加标签: + +```yaml +apiVersion: v1 +kind: Pod +metadata: + name: example-pod + labels: + app: myapp +spec: + containers: + - name: myapp-container + image: myapp:latest +``` + +- kubectl 添加标签 + +```bash +kubectl label pods example-pod app=myapp +``` + +#### 2. 更新标签 + +- 命令行更新标签 + +```bash +kubectl label pods example-pod app=newapp --overwrite +``` + +- 通过修改资源定义yaml文件,更新标签,`pod-definition.yaml` 是包含了对标签的更改的资源定义文件 + +```bash +kubectl apply -f pod-definition.yaml +``` + +#### 3. 删除标签 + +- 命令行删除标签 + +```bash +kubectl label pods example-pod app- +``` + +#### 4. 按照标签查询资源 + +- 命令行查询标签,列出所有带有 `app=myapp` 标签的 Pods + +```bash +kubectl get pods -l app=myapp +``` + +#### 5. 查看资源上的所有标签 + +- 命令行指令操作,列出example-pod的所有标签 + +```bash +kubectl describe pods example-pod +``` + + + +### 四、标签选择器操作 + +#### 1. 等式选择器(Equality-based Selectors) + +- 包括以下使用方式: + + - `=` 或 `==`:选择具有特定键和值的资源 + + - `!=`:选择不具有特定键和值的资源 + +- 举例: + +```bash +kubectl get pods -l env=production # 生产环境的pod +kubectl get pods -l app=frontend # 前端 pod +kubectl get pods -l 'app!=frontend' # 非前端的pod +``` + +#### 2. 集合选择器(**Set-based Selectors**) + +- 包括以下使用方式: + - `in`:选择标签值属于指定集合的资源 + - `notin`:选择标签值不属于指定集合的资源 + - `exists`(只指定键):选择存在特定键的资源(不关心值) + - `does not exist`(通过在键前加 `!` 表示):选择不存在特定键的资源 +- 命令行使用举例: + +```bash +kubectl get pods -l 'env in (production, qa)' # 环境为生产环境或QA环境的pod +kubectl get pods -l 'app notin (frontend, backend)' # 不是前端也不是后端的app的pod +kubectl get pods -l 'tier' # 标签包含tier的pod +kubectl get pods -l '!tier' # 不含有 “tier” 标签的 pod +``` + +- 资源定义清单中使用举例: + + - Deployment 或 Service 中使用选择器来简单确定哪些pod应该被包含在内: + + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: myapp-deployment + spec: + replicas: 3 + selector: + matchLabels: + app: myapp + template: + metadata: + labels: + app: myapp + ``` + + - 实用复杂选择器选择pod: + + ```yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: myapp-deployment + spec: + replicas: 3 + selector: + matchExpressions: + - {key: app, operator: In, values: [myapp]} + - {key: tier, operator: NotIn, values: [frontend]} + template: + metadata: + labels: + app: myapp + tier: backend + ``` + + \ No newline at end of file diff --git a/note/devops/kubernetes/概述.md b/note/devops/kubernetes/概述.md new file mode 100644 index 0000000..9393d7c --- /dev/null +++ b/note/devops/kubernetes/概述.md @@ -0,0 +1,68 @@ +### 一、概述 + +Kubernetes(通常缩写为K8s)是一个开源的容器编排平台,旨在自动化部署、扩展和管理容器化应用程序。最初由Google设计并捐赠给Cloud Native Computing Foundation(CNCF),它现在是云原生生态系统中最受欢迎的容器编排工具 + +### 二、Kubernetes核心功能: + +- **自动化容器部署和回滚**:Kubernetes 允许用户声明式地定义容器应用的状态,自动化地部署和更新应用程序,并在更新不如预期时自动回滚到之前的状态 +- **服务发现和负载均衡**:Kubernetes 可以自动分配IP地址和DNS名称给容器,并在容器组之间实现负载均衡。 +- **存储编排**:自动挂载所选的存储系统,无论是本地存储、公有云提供商的存储服务,还是网络文件系统。 +- **自动化容器排版**:根据资源需求和约束条件,自动选择合适的主机以运行容器 +- **自我修复**:自动替换或重启失败的容器,杀死不响应用户定义的健康检查的容器,并且不向它们分发流量直到它们准备就绪 +- **横向扩展**:基于CPU使用率或其他选定的指标自动扩展或缩减应用副本数量 +- **安全和访问控制**:提供细粒度的访问控制机制,包括用户认证、授权和秘密管理 + +### 三、Kubernetes架构组件: + +- **控制平面(Control Plane)**:控制平面的组件负责集群决策,比如调度和响应集群事件(如当节点失效时启动新的副本) +- **节点(Nodes)**:节点可能是一个虚拟机或物理机,它们是运行应用程序容器的工作机器 + +### 四、Kubernetes 基础组件 + +#### (一)主节点(Master Node)组件: + +1. **API服务器(kube-apiserver):** + +- 提供Kubernetes API的终端,是所有控制面组件的入口 +- 处理API请求,执行操作,并更新etcd中的数据 + +2. **控制器管理器(kube-controller-manager):** + +- 监控集群中的控制器,确保实际状态与期望状态一致 +- 包括Node Controller、Replication Controller、Endpoint Controller等 + +3. **调度器(kube-scheduler):** + +- 负责将Pod调度到可用的工作节点上 +- 考虑资源需求、亲和性、反亲和性等因素进行智能调度 + +4. **etcd:** + +- 是一个分布式键值存储系统,用于保存整个集群的配置数据和状态 +- 存储了所有的集群数据,包括节点信息、Pod状态、服务信息等 + +#### (二)工作节点(Node)组件: + +1. **Kubelet:** + +- 运行在每个工作节点上,负责与主节点通信 + +- 管理节点上的容器,确保它们按照Pod的规格运行 + +1. **Kube-Proxy:** + +- 负责维护节点上的网络规则,处理Pod之间的网络通信 + +- 提供服务的负载均衡,使得服务可以通过统一的IP地址和端口进行访问 + +1. **容器运行时(Container Runtime):** + +- 负责在节点上运行容器,与Kubelet进行通信 + +- 常见的容器运行时包括Docker、containerd、cri-o等 + +### Kubernetes生态系统: + +Kubernetes周围有一个庞大且不断增长的生态系统,包括各种支持工具和服务,这些工具和服务可以帮助用户更容易地部署、运行和管理Kubernetes集群。这包括网络插件、存储解决方案、CI/CD工具、监控和日志服务等 + +Kubernetes的设计目标之一是跨不同的环境中保持应用的便携性,包括公有云、私有云和混合云。这使得它成为企业和开发人员构建、部署和管理现代应用程序的理想选择 \ No newline at end of file diff --git a/note/devops/kubernetes/组件.md b/note/devops/kubernetes/组件.md new file mode 100644 index 0000000..4ae9aab --- /dev/null +++ b/note/devops/kubernetes/组件.md @@ -0,0 +1,203 @@ +在Kubernetes中,集群分为Master节点(也称为控制平面节点)和Worker节点(也称为Node节点)。每个节点类型上运行着不同的组件,这些组件共同工作以管理Kubernetes集群。 + +### 一、Master节点(控制平面)组件: + +1. **kube-apiserver**:作为Kubernetes API的服务端,它是集群的管理中心,其他组件都通过API与之通信。 +2. **etcd**:一个分布式键值存储,用于保存所有集群数据,是集群状态的“真理源” +3. **kube-scheduler**:负责决定将新创建的Pods分配到哪个节点上,根据资源需求、服务质量要求、亲和性和反亲和性规范等因素进行调度 +4. **kube-controller-manager**:它运行集群级别的控制循环,例如节点控制器、副本控制器、端点控制器、服务帐户和令牌控制器等 +5. **cloud-controller-manager**(可选):当Kubernetes运行在云服务提供商的环境中时,该组件允许将集群操作与云服务提供商的APIs相链接 + +### 二、Node节点组件: + +1. **kubelet**:在每个Node节点上运行的代理,确保容器在Pods中正确运行。它管理的操作包括Pod的创建、启动、监控等 +2. **kube-proxy**:在Node节点上运行的网络代理,用于维护节点上的网络规则,实现Service资源的网络连接 +3. **Container Runtime**:容器运行环境,负责运行容器。常见的容器运行时包括Docker、containerd和CRI-O + +### 三、其他重要组件: + +- **CoreDNS**(或者Kube-DNS):提供集群内部的DNS服务,使得Pods和Services可以通过名称互相查找 +- **Metrics Server**(可选):用于收集资源使用信息,这对于资源自动扩展(如Horizontal Pod Autoscaler)是必要的 +- **Dashboard**(可选):提供了一个基本的Web UI,可以用来查看和管理集群 +- **Ingress Controller**(可选):用于处理外部访问集群服务的规则和路由,常见的有nginx-ingress、traefik等 + +请注意,Kubernetes项目经常更新,所以特定版本的Kubernetes可能引入或弃用某些组件。此外,Kubernetes生态系统中还有许多其他的插件和扩展,可以根据需要添加到集群中 + +### 四、Kubernetes 组件架构图 + +![Kubernetes总架构图- kubernetes-notes](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202401241043760.jpg) + +### 五、具体组件 + +#### 1. etcd + +> 详细解读,后续加入 ../etcd/etcd.md + +##### 1.1 概述 + +etcd是一个分布式键值数据库,它使用Raft协议实现了强一致性和高可用性。etcd专为分布式系统设计,可以忍受网络分区并保持可用性,同时保证了读写操作的一致性。 + +- **Raft协议**:Raft是一个一致性算法,Raft通过选举一个领导者来管理日志复制。在etcd集群中,所有的变更(如写操作)都通过领导者进行协调,这些变更被分发到其他节点(称为Follower)以保持状态一致 + +- **存储模型**:etcd使用键值对存储数据。它支持事务、监视键的变化、时间点快照(snapshot)和自动清理(compaction) + +- **gRPC和HTTP/2**:etcd使用gRPC作为其客户端和服务器之间的通信机制,它基于HTTP/2协议提供了双向流、头部压缩、多路复用请求等特性 + +##### 1.2 功能 + +etcd在Kubernetes中是保存整个集群状态的后端数据库。以下是它在Kubernetes集群中承担的一些关键功能: + +- **保存集群数据**:etcd存储了整个Kubernetes的状态信息,包括节点信息、Pods状态、配置数据、Secrets、Deployments、DaemonSets等资源的状态和元数据。 + +- **服务发现**:etcd可以用作服务发现机制,因为它保存了所有Pods和服务的信息以及它们的位置。 + +- **分布式系统的同步**:etcd为Kubernetes提供了一个可靠的服务,以同步分布在集群中的服务的状态。 + +- **持久化存储**:即使Kubernetes控制平面的组件出现故障,etcd确保集群的配置数据不会丢失。 + +- **实现锁和领导者选举**:etcd的强一致性模型可以用于实现分布式锁或选举集群中的领导者 + +- **观察变更**:客户端可以监视特定键的变化,当这些键被修改时,etcd会通知客户端。这对于实现自动更新配置非常有用 + +##### 1.3 组件交互 + +###### 1.3.1. kube-apiserver与etcd的交互 + +kube-apiserver是与etcd交互最频繁的组件。它作为Kubernetes API的前端服务,处理用户、内部组件和外部组件的所有请求。当这些请求涉及到集群状态的变更或查询时,kube-apiserver会与etcd通信: + +- **读取操作**:当需要获取集群状态时(例如,获取Pod列表),kube-apiserver会从etcd读取数据 +- **写入操作**:当创建、更新或删除Kubernetes资源(如Deployments、Services等)时,kube-apiserver会将这些变更写入etcd +- **监视操作**:kube-apiserver还可以监视etcd中的资源变更。这使得控制器可以响应资源状态的变化,执行相应的业务逻辑(如调度Pod或更新Endpoints) + +###### 1.3.2控制器与etcd的交互 + +虽然控制器通常不直接与etcd交互,但它们依赖于kube-apiserver提供的信息,这些信息最终来源于etcd: + +- **控制循环**:控制器通过kube-apiserver观察集群状态的变化(例如,新的Pod需要被调度),并在状态变化时作出响应,执行必要的操作以推动集群状态向期望状态转变 +- **状态更新**:当控制器作出决策并执行操作后(如创建新的Pods),它们会通过kube-apiserver将新状态写回etcd + +###### 1.3.3. kube-scheduler与etcd的交互 + +kube-scheduler负责决定将Pods分配给哪个节点。它使用的信息同样来自etcd,但通过kube-apiserver提供: + +- **调度决策**:kube-scheduler监视新创建且未分配到节点的Pods,选择最适合的节点进行调度,并通过kube-apiserver更新Pods的信息,这些信息最终保存在etcd中 + +###### 1.3.4. kubelet与etcd的交互 + +kubelet负责管理分配给其节点的Pods,并保证Pods的状态与etcd中保存的期望状态一致。不过,kubelet与etcd的交互也是通过kube-apiserver进行的: + +- **状态报告**:kubelet定期向kube-apiserver报告Pod状态,这些状态信息随后被存储在etcd中 +- **配置获取**:当kubelet启动或运行时,它会从kube-apiserver获取必要的配置信息,这些信息最终来自etcd + +#### 2. kube-apiserver + +##### 2.1 概述 + +kube-apiserver 是 Kubernetes 控制平面的核心组件,它提供了 Kubernetes API 的服务端实现。kube-apiserver 设计成了一个无状态的 HTTP 服务器,它处理集群内外的 REST API 请求,并提供 JSON 格式的资源状态数据。可以通过增加 kube-apiserver 实例的数量来横向扩展服务,以支持更高的负载 + +##### 2.2 功能 + +- **API 提供**:作为 Kubernetes API 的入口点,提供 RESTful API 供用户、外部系统和集群内部组件调用 + +- **资源操作**:处理对各种 Kubernetes 资源(如 Pods、Services、Deployments 等)的操作请求 + +- **认证和授权**:确保所有进入集群的请求都经过适当的安全检查 + +- **数据校验**:在将数据持久化到 etcd 之前,对资源定义进行校验,保证数据的完整性和准确性 + +- **集群状态管理**:维护和管理集群的状态信息,包括资源的创建、更新、删除和查询 + +- **API 聚合**:通过 API 聚合层,kube-apiserver 能够扩展新的 API 服务器,支持自定义资源和扩展 Kubernetes API + +- **扩展机制**:支持通过 Webhooks 或其他机制扩展其功能,如自定义调度器、准入控制器等 + +- **监控和日志**:提供监控 API 端点,允许集群监控和日志系统访问集群操作数据 + +- **服务发现**:通过 API 提供服务发现机制,允许 Pods 通过服务名来互相发现和通信 + +How to Monitor Kubernetes API Server – Sysdig + + + +#### 3. kube-scheduler + +##### 3.1 概述 + +负责集群中的Pod调度。其基本工作原理是:在Kubernetes集群中,当我们创建一个Pod并提交给kube-apiserver后,kube-scheduler会根据当前集群的状态,通过一系列复杂的调度算法,决定将Pod放置到哪个Node上运行 + +##### 3.2 功能 + +- **负载均衡:**通过合理的调度策略,kube-scheduler可以将工作负载分散到各个工作节点,避免资源的浪费和瓶颈 + +- **高可用:**在节点失效时,kube-scheduler可以将其上的Pod重新调度到其他节点,保证应用的可用性 + +- **亲和性和反亲和性调度:**kube-scheduler可以根据用户定义的亲和性和反亲和性规则,将相关联的Pod调度到同一或不同的节点上 + +- **资源预留和限制:**kube-scheduler可以根据Pod的资源请求和限制进行调度,确保每个Pod在运行时都能获取到足够的资源 + +- **多租户支持:**kube-scheduler支持通过Namespace和ResourceQuota进行多租户调度,保证每个租户获得公平的资源分配 + +##### 3.3 工作过程概述 + +- kube-scheduler从etcd中获取集群所有节点的信息 + +- kube-scheduler监听kube-apiserver的Pod事件,当有新的Pod创建时,kube-scheduler会接收到这个事件。 + +- 对于接收到的每个未调度的Pod,kube-scheduler都会开始进行调度 + +- 调度过程首先会进行预选(Predicates)阶段,过滤掉不符合调度要求(例如资源不足、标签不匹配等)的节点 + +- 然后进入优选(Priorities)阶段,对剩余的节点进行打分,分数高的节点将被选为候选节点 + +- 最后,kube-scheduler会将Pod的nodeName字段设置为选定的节点,然后kubelet会在该节点上启动Pod + +#### 4. kube-controller-manager + +##### 4.1 概述 + +kube-controller-manager是Kubernetes集群中的一个重要组件,它负责管理和运行控制器。控制器是Kubernetes中实现其各种功能的后台线程,例如,Node控制器负责处理节点故障,Replica控制器负责维护Pod副本的正确数量,Service控制器负责设置负载均衡器等 + +##### 4.2 功能 + +- **节点管理:**Node控制器负责处理节点故障,如果一个节点在一定时间内没有响应,节点控制器就会把该节点上的Pod移动到其他节点上。 +- **Pod副本管理:**Replica控制器负责维护每个ReplicaSet或Deployment中Pod的正确数量。 +- **服务负载均衡:**Service控制器负责设置云提供商的负载均衡器,以实现Service的LoadBalancer类型。 +- **卷管理:**PersientVolume控制器负责管理持久卷和持久卷声明,例如创建、删除和挂载卷。 +- **名称空间管理:**Namespace控制器负责删除在命名空间中的所有资源,当该命名空间被删除时。 +- **服务账户和令牌管理:**ServiceAccount控制器创建默认服务账户和API访问令牌。 +- **Job和CronJob管理:**Job控制器和CronJob控制器负责管理Job和CronJob,例如创建和删除Job,以及触发CronJob。 + +#### 5. kubelet + +##### 5.1 概述 + +kubelet是Kubernetes集群中每个节点上运行的代理,它负责管理该节点上的容器运行时,以及Pod的创建、启动、停止等生命周期操作。kubelet主要和Kubernetes API服务器交互,获取需要运行的Pod信息,然后确保这些Pod和其中的容器正确运行 + +##### 5.2 功能 + +- Pod生命周期管理:kubelet负责创建、启动和停止Pod,以及Pod中的容器 +- 节点状态管理:kubelet会周期性地收集节点和容器的状态信息,并将这些信息报告给Kubernetes API服务器 +- 容器运行时管理:kubelet负责和容器运行时(例如Docker或rkt)交互,创建和管理容器 +- 资源监控:kubelet可以收集和报告节点和容器的资源使用信息,例如CPU、内存和存储的使用情况 +- 日志管理:kubelet可以帮助用户收集和存储容器的日志,方便用户查看和调试 +- 安全和隔离:kubelet可以设置Pod的网络和安全上下文,例如网络策略、安全上下文和SELinux标签,以确保容器的隔离和安全 + +#### 6. kube-proxy + +##### 6.1 概述 + +kube-proxy是Kubernetes集群中每个节点上运行的网络代理,负责实现Kubernetes Service的概念。它的主要职责是管理节点上的网络规则,并进行连接转发,以实现服务发现和负载均衡 + +kube-proxy的实现原理主要包括以下几个步骤: + +1. kube-proxy首先会从Kubernetes API服务器获取集群中Service和Endpoint的信息 +2. 然后,kube-proxy会在节点上设置网络规则,以捕获到达Service Cluster IP和Port的流量,并将这些流量转发到正确的Pod +3. kube-proxy会周期性地检查从API服务器获取的Service和Endpoint信息,如果有任何变化,kube-proxy会相应地更新网络规则 + +##### 6.2 功能 + +- 服务发现:kube-proxy负责实现Kubernetes Service的概念,使得应用可以通过Service名称来发现服务,而不需要知道具体的Pod IP +- 负载均衡:kube-proxy可以将到达Service的连接转发到后端的多个Pod,从而实现负载均衡。转发策略可以是轮询(Round Robin)、随机(Random)或者基于源IP的hash +- 网络规则管理:kube-proxy负责在节点上设置和管理网络规则,以实现服务发现和负载均衡 +- 健康检查:kube-proxy会周期性地检查后端Pod的健康状态,如果某个Pod不健康,kube-proxy会将其从服务的后端列表中移除 +- 流量控制:kube-proxy支持基于Session Affinity的流量控制,可以将来自同一客户端的所有请求都转发到同一个Pod \ No newline at end of file diff --git a/note/devops/nginx/acme lets encrypt ssl证书.md b/note/devops/nginx/acme lets encrypt ssl证书.md new file mode 100644 index 0000000..0a16c3a --- /dev/null +++ b/note/devops/nginx/acme lets encrypt ssl证书.md @@ -0,0 +1,24 @@ +### 安装acme.sh + +```shell +curl https://get.acme.sh | sh -s email=xxxxxxxxxxxx@163.com +``` + +### 访问dns pod 拿到id和token + +```shell +https://console.dnspod.cn/account/token/token +``` + +### 执行命令 + +```shell +export DP_Id="xxxxx" +export DP_Key="xxxxxxxxxxxxxxxxxxxxxxx" +/root/.acme.sh/acme.sh --issue --dns dns_dp -d heysq.com -d igit.heysq.com --server letsencrypt +/root/.acme.sh/acme.sh --install-cert -d example.com \ +--key-file /path/to/keyfile/in/nginx/key.pem \ +--fullchain-file /path/to/fullchain/nginx/cert.pem \ +--reloadcmd "service nginx force-reload" +``` + diff --git a/note/devops/nginx/docker-nginx.md b/note/devops/nginx/docker-nginx.md new file mode 100644 index 0000000..97ff14f --- /dev/null +++ b/note/devops/nginx/docker-nginx.md @@ -0,0 +1,14 @@ +### host模式,挂载conf目录和日志路径 + +```bash +docker run -itd --name nginx \ +--restart=always \ +--network=mynet \ +-p 8443:8443 \ +-v /root/data/nginx/log:/var/log/nginx \ +-v /root/data/nginx/conf.d:/etc/nginx/conf.d \ +-v /root/data/nginx/ssl:/etc/nginx/ssl \ +-v /root/data/nginx/nginx.conf:/etc/nginx/nginx.conf \ +nginx:1.20 +``` + diff --git a/note/devops/nginx/gitea.md b/note/devops/nginx/gitea.md new file mode 100644 index 0000000..273cc61 --- /dev/null +++ b/note/devops/nginx/gitea.md @@ -0,0 +1,25 @@ +### nginx conf + +```ngin +server { + listen [::]:8443 ssl ipv6only=on; + server_name u20.heysq.com; + ssl_certificate u20_ssl/u20.crt; + ssl_certificate_key u20_ssl/u20.key; + ssl_session_timeout 5m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; + ssl_prefer_server_ciphers on; + + error_page 497 https://$http_host$request_uri; + client_max_body_size 500m; + + location /gitea/ { + proxy_pass http://localhost:3000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } +} +``` \ No newline at end of file diff --git a/note/devops/nginx/gitlab外部nginx+https.md b/note/devops/nginx/gitlab外部nginx+https.md new file mode 100644 index 0000000..38a5ffc --- /dev/null +++ b/note/devops/nginx/gitlab外部nginx+https.md @@ -0,0 +1,99 @@ +### 一、内部自带nginx 启动 + +```shell +server { + listen 9090 ssl; + server_name igit.heysq.com; + server_tokens off; + root /opt/gitlab/embedded/service/gitlab-rails/public; + + client_max_body_size 250m; + access_log /var/log/gitlab/gitlab_access.log; + error_log /var/log/gitlab/gitlab_error.log; + + ssl_certificate /root/nginx_container/conf/ssl_file/heysq_com/cert.pem; + ssl_certificate_key /root/nginx_container/conf/ssl_file/heysq_com/key.pem; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + error_page 497 https://$http_host$request_uri; + location / { + proxy_read_timeout 300; # Some requests take more than 30 seconds. + proxy_connect_timeout 300; # Some requests take more than 30 seconds. + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Frame-Options SAMEORIGIN; + proxy_pass https://10.44.147.76:9090; + } +} +``` + +### 二、不使用内部nginx + +- 外部nginx代理socket +- 使用ssl +- 非标准端口 + +``` +upstream gitlab { + server unix:/var/opt/gitlab/gitlab-workhorse/sockets/socket; +} + +server { + listen *:8443 ssl; + server_name igit.heysq.com; + server_tokens off; + root /opt/gitlab/embedded/service/gitlab-rails/public; + client_max_body_size 250m; + + access_log /var/log/gitlab/gitlab_access.log; + error_log /var/log/gitlab/gitlab_error.log; + + ssl_certificate /root/nginx/conf/ssl_file/heysq_com/cert.pem; + ssl_certificate_key /root/nginx/conf/ssl_file/heysq_com/key.pem; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + error_page 497 https://$http_host$request_uri; + + location / { + # serve static files from defined root folder;. + # @gitlab is a named location for the upstream fallback, see below + try_files $uri $uri/index.html $uri.html @gitlab; + } + + location @gitlab { + # If you use https make sure you disable gzip compression + # to be safe against BREACH attack + + proxy_read_timeout 300; # Some requests take more than 30 seconds. + proxy_connect_timeout 300; # Some requests take more than 30 seconds. + proxy_redirect off; + + proxy_set_header X-Forwarded-Proto $scheme; + proxy_set_header Host $http_host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Frame-Options SAMEORIGIN; + + proxy_pass http://gitlab; + } + + location ~ ^/(assets)/ { + root /opt/gitlab/embedded/service/gitlab-rails/public; + # gzip_static on; # to serve pre-gzipped version + expires max; + add_header Cache-Control public; + } + + error_page 502 /502.html; +} +``` + diff --git a/note/devops/nginx/home.heysq,com.md b/note/devops/nginx/home.heysq,com.md new file mode 100644 index 0000000..650a705 --- /dev/null +++ b/note/devops/nginx/home.heysq,com.md @@ -0,0 +1,58 @@ +```nginx +server { + listen 8443 ssl; + server_name home.heysq.com; + ssl_certificate /etc/nginx/ssl/home_heysq_nginx/home.crt; + ssl_certificate_key /etc/nginx/ssl/home_heysq_nginx/home.key; + ssl_session_timeout 5m; + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; + ssl_prefer_server_ciphers on; + + error_page 497 https://$http_host$request_uri; + client_max_body_size 500m; + + location /gitea/ { + proxy_pass http://gitea:3000/; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /grafana/ { + proxy_set_header Host $http_host; + proxy_pass http://grafana:3000; + } + + location /grafana/api/live/ { + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection $connection_upgrade; + proxy_set_header Host $http_host; + proxy_pass http://grafana:3000; + } + + location /prometheus/ { + auth_basic "Prometheus"; + auth_basic_user_file /etc/nginx/htpasswd_file; + proxy_pass http://prometheus:9090; + } + + location /kubepi { + proxy_pass http://kubepi; + } + + location /code/ { + proxy_pass http://code-server:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host:8443/code/; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Accept-Encoding gzip; + proxy_connect_timeout 600; + proxy_set_header Origin ""; + } +} +``` + diff --git a/note/devops/nginx/nginx http转https.md b/note/devops/nginx/nginx http转https.md new file mode 100644 index 0000000..9c65153 --- /dev/null +++ b/note/devops/nginx/nginx http转https.md @@ -0,0 +1,12 @@ +### 1. error page + +- server 端的配置中加入以下配置 + + ```ngin + error_page 497 https://$http_host$request_uri; + ``` + + + + + diff --git a/note/devops/nginx/nginx-netdata.md b/note/devops/nginx/nginx-netdata.md new file mode 100644 index 0000000..6288e86 --- /dev/null +++ b/note/devops/nginx/nginx-netdata.md @@ -0,0 +1,27 @@ +### 1、配置文件 + +```bash +upstream netdata { + server localhost:19999; +} + +server { + listen *:8443 ssl; + server_name istat.heysq.com; + server_tokens off; + + ssl_certificate /root/nginx/conf/ssl_file/heysq_com/cert.pem; + ssl_certificate_key /root/nginx/conf/ssl_file/heysq_com/key.pem; + ssl_session_timeout 5m; + ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; + ssl_protocols TLSv1 TLSv1.1 TLSv1.2; + ssl_prefer_server_ciphers on; + + error_page 497 https://$http_host$request_uri; + + location / { + proxy_pass http://netdata; + } +} +``` + diff --git a/note/devops/nginx/nginx_stub_status.md b/note/devops/nginx/nginx_stub_status.md new file mode 100644 index 0000000..388cd0c --- /dev/null +++ b/note/devops/nginx/nginx_stub_status.md @@ -0,0 +1,19 @@ +### 1、增加conf文件 + +```bash +server { + listen localhost; + server_name status.localhost; + keepalive_timeout 0; + + access_log off; + + allow 127.0.0.1; + deny all; + + location /stub_status { + stub_status on; + } +} +``` + diff --git a/note/devops/监控/docker-alertmanager.md b/note/devops/监控/docker-alertmanager.md new file mode 100644 index 0000000..73769ed --- /dev/null +++ b/note/devops/监控/docker-alertmanager.md @@ -0,0 +1,12 @@ +### docker 部署 + +```bash +docker -H 192.168.0.3 \ +run -itd \ +--name alertmanager \ +-v /mnt/nfs/lxc_data/docker_machine/alertmanager/conf:/opt/bitnami/alertmanager/conf \ +-v /mnt/nfs/lxc_data/docker_machine/alertmanager/data:/opt/bitnami/alertmanager/data \ +-p 9093:9093 \ +bitnami/alertmanager:0.27.0 +``` + diff --git a/note/devops/监控/docker-cadvisor.md b/note/devops/监控/docker-cadvisor.md new file mode 100644 index 0000000..b10d851 --- /dev/null +++ b/note/devops/监控/docker-cadvisor.md @@ -0,0 +1,17 @@ +### docker 部署 + +```bash +docker -H 192.168.0.1 run \ + --volume=/:/rootfs:ro \ + --volume=/var/run:/var/run:ro \ + --volume=/sys:/sys:ro \ + --volume=/var/lib/docker/:/var/lib/docker:ro \ + --volume=/cgroup:/cgroup:ro \ + --publish=8080:8080 \ + --detach=true \ + --device=/dev/kmsg \ + --restart unless-stopped \ + --name=cadvisor \ + gcr.io/cadvisor/cadvisor:latest +``` + diff --git a/note/devops/监控/docker-grafana.md b/note/devops/监控/docker-grafana.md new file mode 100644 index 0000000..a641ebe --- /dev/null +++ b/note/devops/监控/docker-grafana.md @@ -0,0 +1,10 @@ +### docker部署 + +```bash +docker run -d -p 3000:3000 --name=grafana \ + --user "$(id -u)" \ + --volume "/mnt/nfs/lxc_data/docker_machine/grafana/conf/grafana.ini:/etc/grafana/grafana.ini" \ + --volume "$PWD/data:/var/lib/grafana" \ + grafana/grafana +``` + diff --git a/note/devops/监控/docker-mysqld-exporter.md b/note/devops/监控/docker-mysqld-exporter.md new file mode 100644 index 0000000..dbc39b6 --- /dev/null +++ b/note/devops/监控/docker-mysqld-exporter.md @@ -0,0 +1,11 @@ +### docker 部署 + +```bash +docker run -d \ +--name mysqld_exporter \ +-p 9104:9104 \ +-v /mnt/nfs/lxc_data/docker_machine/mysqld_exporter/my.cnf:/.my.cnf \ +--network=host \ +prom/mysqld-exporter +``` + diff --git a/note/devops/监控/docker-nodeexporter.md b/note/devops/监控/docker-nodeexporter.md new file mode 100644 index 0000000..0c47b7e --- /dev/null +++ b/note/devops/监控/docker-nodeexporter.md @@ -0,0 +1,12 @@ +### docker 部署 + +```bash +docker run -d \ +--name=nodexeporter_on_openwrt \ +--net="host" \ +--pid="host" \ +-v "/:/host:ro,rslave,shared" \ +quay.io/prometheus/node-exporter:latest \ +--path.rootfs=/host +``` + diff --git a/note/devops/监控/docker-prometheus.md b/note/devops/监控/docker-prometheus.md new file mode 100644 index 0000000..c743aea --- /dev/null +++ b/note/devops/监控/docker-prometheus.md @@ -0,0 +1,10 @@ +### docker 部署 + +```bash +docker -H 192.168.0.3 run -itd --name prometheus \ +-p 9090:9090 \ +-v /mnt/nfs/lxc_data/docker_machine/prometheus/conf:/etc/prometheus \ +-v /mnt/nfs/lxc_data/docker_machine/prometheus/data:/opt/bitnami/prometheus/data \ +bitnami/prometheus:2.54.1 +``` + diff --git a/note/devops/监控/prometheus采集外部k3s集群.md b/note/devops/监控/prometheus采集外部k3s集群.md new file mode 100644 index 0000000..cf707b7 --- /dev/null +++ b/note/devops/监控/prometheus采集外部k3s集群.md @@ -0,0 +1,111 @@ +### rbac.yaml + +```yaml +apiVersion: v1 +kind: ServiceAccount +metadata: + name: prometheus + namespace: devops + +--- +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: monitoring-token + namespace: devops + annotations: + kubernetes.io/service-account.name: "prometheus" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: prometheus +rules: +- apiGroups: + - "" + resources: + - nodes + - services + - endpoints + - pods + - nodes/proxy + verbs: + - get + - list + - watch +- apiGroups: + - "extensions" + resources: + - ingresses + verbs: + - get + - list + - watch +- apiGroups: + - "" + resources: + - configmaps + - nodes/metrics + verbs: + - get +- nonResourceURLs: + - /metrics + verbs: + - get +--- +#apiVersion: rbac.authorization.k8s.io/v1beta1 +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: prometheus +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: prometheus +subjects: +- kind: ServiceAccount + name: prometheus + namespace: monitoring +``` + + + +### token.yaml + +```yaml +apiVersion: v1 +kind: Secret +type: kubernetes.io/service-account-token +metadata: + name: prometheus + namespace: devops + annotations: + kubernetes.io/service-account.name: "prometheus" + +``` + + + +### 查看token + +```bash +kubectl describe secrets prometheus -n devops +``` + +```bash +Name: prometheus +Namespace: devops +Labels: +Annotations: kubernetes.io/service-account.name: prometheus + kubernetes.io/service-account.uid: fcb541a1-c929-4f77-a759-dd2dda845798 + +Type: kubernetes.io/service-account-token + +Data +==== +token: eyJhbGciOiJSUzI1NiIsImtpZCI6InE2V1A1Y2V1QUxTV3BQRkVreDFCMlgwYjFDRXNDc2lpTFlPdk12TFdBYmMifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJkZXZvcHMiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlY3JldC5uYW1lIjoicHJvbWV0aGV1cyIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VydmljZS1hY2NvdW50Lm5hbWUiOiJwcm9tZXRoZXVzIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiZmNiNTQxYTEtYzkyOS00Zjc3LWE3NTktZGQyZGRhODQ1Nzk4Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50OmRldm9wczpwcm9tZXRoZXVzIn0.GNxB8hX7Z_2L9wj4gdlggImkR2yzn2KtUj83u_8po34qirji5R2lmk7U8Re2GIJJ4OWZr8J-LUmyPPamwPtcsVgF9despM-bdz-1xDbaEmvxrhdXUtZXvnCcPRfYivGRWocWloJeinVaRmeu12wRokzHrBIpzEu8GZWpQZFrMH9CSwIBl8rPoeErf5a5FHIg2FTFF_VZCdgB9yTOVO_5iLKyK5CG6yZ-4K4M6xaiv1zdcnNG4eON2_2YJzE5FnAq3KoGMFU00IdUCFgQ29GowWlVj8ldZVP6NYl5SJ1Nf2L1xwgswnteFVN41conKlz6A3DeDSlhIEajoueohalXng +ca.crt: 570 bytes +namespace: 6 bytes +``` + diff --git a/note/linux/centos/centos7.9升级内核.md b/note/linux/centos/centos7.9升级内核.md new file mode 100644 index 0000000..df4aee8 --- /dev/null +++ b/note/linux/centos/centos7.9升级内核.md @@ -0,0 +1,68 @@ +### 1. 更新系统 + +```bash +yum update -y +``` + +### 2.添加elrepo软件源 + +```bash +yum install vim -y +vi /etc/yum.repos.d/elrepo.repo +# 添加以下内容 +cat > /etc/yum.repos.d/elrepo.repo << EOF +[elrepo] +name=elrepo +baseurl=https://mirrors.aliyun.com/elrepo/archive/kernel/el7/x86_64 +gpgcheck=0 +enabled=1 +EOF + +``` + +### 3. 刷新源数据缓存 + +```bash +yum clean all && yum makecache +``` + +### 4. 安装5.4内核 + +#### 4.1 列出可用内核 + +```bash +[root@centos7 ~]# yum list kernel-lt +已加载插件:fastestmirror +Loading mirror speeds from cached hostfile + * base: mirrors.aliyun.com + * extras: mirrors.aliyun.com + * updates: mirrors.aliyun.com +可安装的软件包 +kernel-lt.x86_64 5.4.265-1.el7.elrepo elrepo +``` + +#### 安装指定内核 + +```bash +yum install -y kernel-lt-5.4.265 +yum install -y kernel-lt-devel-5.4.265 +``` + +### 5. 查看安装内核版本 + +```bash +awk -F\' '$1=="menuentry " {print i++ " : " $2}' /etc/grub2.cfg +``` + +### 6. 设置默认内核 + +```bash +grub2-set-default 0 +``` + +### 7. 重启 + +```bash +reboot +``` + diff --git a/note/linux/centos/固定ip.md b/note/linux/centos/固定ip.md new file mode 100644 index 0000000..301dcc4 --- /dev/null +++ b/note/linux/centos/固定ip.md @@ -0,0 +1,19 @@ +### 1. 备份网卡配置文件 + +```bash +cp /etc/sysconfig/network-scripts/ifcfg-eth0 /temp/ifcfg-eth0.bak +``` + +### 2. 修改网卡配置文件 + +```shell +BOOTPROTO="static" +ONBOOT="yes" +IPADDR=172.29.208.1 +NETMASK=255.255.240.0 +GATEWAY=172.29.208.0 +DNS1=172.29.208.0 +``` + + + diff --git a/note/linux/centos/挂载nfs.md b/note/linux/centos/挂载nfs.md new file mode 100644 index 0000000..b5ce2cb --- /dev/null +++ b/note/linux/centos/挂载nfs.md @@ -0,0 +1,22 @@ +### nfs 客户端 + +#### 1. 安装 nfs 客户端 + +```bash +yun install nfs-utils -y +``` + +#### 2. 查看nfs 服务端目录 + +```bash +showmount -e 192.168.0.12 +``` + +#### 3. 挂载nfs + +```bash +mount -t nfs 192.168.0.12:/volume1/share /root/nfs +``` + + + diff --git a/note/linux/centos/防火墙firewalld.md b/note/linux/centos/防火墙firewalld.md new file mode 100644 index 0000000..8c79262 --- /dev/null +++ b/note/linux/centos/防火墙firewalld.md @@ -0,0 +1,32 @@ +### 1. 查看防火墙状态 + +```bash + systemctl status firewalld.service +``` + +### 2. 关闭防火墙 + +```bash + systemctl stop firewalld.service +``` + +### 3. 禁用防火墙 + +```bash + systemctl disable firewalld.service +``` + +### 4. 启用防火墙 + +```bash + systemctl enable firewalld.service +``` + +### 5. 开启防火墙 + +```bash + systemctl start firewalld.service +``` + + + diff --git a/note/linux/command/basename.md b/note/linux/command/basename.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/bash 匹配特性.md b/note/linux/command/bash 匹配特性.md new file mode 100644 index 0000000..bcbd07a --- /dev/null +++ b/note/linux/command/bash 匹配特性.md @@ -0,0 +1,5 @@ +- `ls -alh file[0-9,10,11]` 匹配中括号里的单个字符 +- `ls -alh file{1,2,3,44,11}` 匹配花括号逗号分割的每一个 +- `ls -alh file{1..12}` 匹配1到12 +- ![e71126bba215cba932a20e2823001160.png](../../../_resources/e71126bba215cba932a20e2823001160.png) +- \ No newline at end of file diff --git a/note/linux/command/bash中的引号.md b/note/linux/command/bash中的引号.md new file mode 100644 index 0000000..2795a34 --- /dev/null +++ b/note/linux/command/bash中的引号.md @@ -0,0 +1,3 @@ +![4818c7593fa7bc16f12bef265c9ccc.png](../../../_resources/8b4818c7593fa7bc16f12bef265c9ccc.png) + +![859a677e28513694723c0a197bed2786.png](../../../_resources/859a677e28513694723c0a197bed2786.png) \ No newline at end of file diff --git a/note/linux/command/cat.md b/note/linux/command/cat.md new file mode 100644 index 0000000..1bde7f9 --- /dev/null +++ b/note/linux/command/cat.md @@ -0,0 +1 @@ +-n 打印行号 \ No newline at end of file diff --git a/note/linux/command/cut 列过滤文件.md b/note/linux/command/cut 列过滤文件.md new file mode 100644 index 0000000..80f83c7 --- /dev/null +++ b/note/linux/command/cut 列过滤文件.md @@ -0,0 +1,8 @@ +- -d 指定分隔符 +- -f 选择列范围 +- cut -d: -f1,3 /etc/passwd 选择第一列和第三列 +- cut -d: -f1-3 /etc/passwd 选择第一列到第三列 +- cut -d: -f3- /etc/passwd 选择第三列到最后 +- cut -c1-5 /etc/passwd 选择第一列到第五列字符 +- cut -c1,5 /etc/passwd 选择第一列和第五列字符 +- cut -c10- /etc/passwd 选择第十列到最后的字符 \ No newline at end of file diff --git a/note/linux/command/diff 文件不同.md b/note/linux/command/diff 文件不同.md new file mode 100644 index 0000000..4ccce87 --- /dev/null +++ b/note/linux/command/diff 文件不同.md @@ -0,0 +1,5 @@ +- 逐行比较文件的不同 +- 告诉我们怎么改变第一个文件才能与第二个文件一样 +![0d845598e834d2c9d8526aafdbfd3aaa.png](../../../_resources/0d845598e834d2c9d8526aafdbfd3aaa.png) +- diff -u file1 file2 > file.patch 保存不一致文件的补丁 +- patch file file.patch 按照补丁直接修改file1文件 \ No newline at end of file diff --git a/note/linux/command/dirname.md b/note/linux/command/dirname.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/grep 行过滤文件.md b/note/linux/command/grep 行过滤文件.md new file mode 100644 index 0000000..4415b57 --- /dev/null +++ b/note/linux/command/grep 行过滤文件.md @@ -0,0 +1,33 @@ +- --color=auto 添加色彩 alias grep='grep --color=auto' +- -n 打印包含关键字的行号 `grep -n root /etc/passwd` +- -i 忽略大小写匹配 +- grep '^root' /etc/passwd 以 root 开头 +- grep 'bash$' /etc/passwd 以 bash 结尾 +- -v 取反,grep -v '^root' /etc/passwd 不以 root 开头的文件 +- -A 搜索值的后几行连同展示 grep -niA "^root" /etc/passwd ![199d047fd3786c3065bb4442a7268a82.png](../../../_resources/199d047fd3786c3065bb4442a7268a82.png) +- -B 搜索行的前几行连同展示 ![99e455bd412ce7823ce72bda88e93c7d.png](../../../_resources/99e455bd412ce7823ce72bda88e93c7d.png) +- -w 按照单词搜索,包含单词的行 grep -w "hello" /etc/passwd ![108a61ca70a4bcca4a791ab64600503a.png](../../../_resources/108a61ca70a4bcca4a791ab64600503a.png) +- -o 只打印关键字本身 +- 过滤空行 grep -v '^$' 1.txt +- -E 使用扩展正则 `grep -E 'A[0-9]{3}' 1.txt` +``` +me@me-EQ59:~/shell_demo$ ifconfig enp2s0 # 取出 ip 子网掩码 和 网关地址 +enp2s0: flags=4163 mtu 1500 + inet 192.168.110.2 netmask 255.255.255.0 broadcast 192.168.110.255 + inet6 fe80::eedd:38e2:edd:f678 prefixlen 64 scopeid 0x20 + ether 7c:83:34:b4:b0:a3 txqueuelen 1000 (以太网) + RX packets 178803597 bytes 90913766807 (90.9 GB) + RX errors 0 dropped 9947 overruns 0 frame 0 + TX packets 177733548 bytes 81380631943 (81.3 GB) + TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 + +me@me-EQ59:~/shell_demo$ ifconfig enp2s0 | grep -oE "([0-9]{1,3}\.){3}[0-9]{1,3}" +192.168.110.2 +255.255.255.0 +192.168.110.255 +me@me-EQ59:~/shell_demo$ ifconfig enp2s0 | grep -oP "(\d{1,3}\.){3}\d{1,3}" +192.168.110.2 +255.255.255.0 +192.168.110.255 +me@me-EQ59:~/shell_demo$ +``` \ No newline at end of file diff --git a/note/linux/command/history 命令历史.md b/note/linux/command/history 命令历史.md new file mode 100644 index 0000000..2e1a6ce --- /dev/null +++ b/note/linux/command/history 命令历史.md @@ -0,0 +1,9 @@ +### 命令参数 +- `history -c` 清空历史命令,不会清空`.bash_history` +- `history -r ~/.bash_history` 使用文件恢复历史命令 + +### 执行历史命令 +- 上下左右寻找 +- 复制粘贴 +- `!id`,历史命令 id号![0372eee0edece0cf1d6dced94329db89.png](../../../_resources/0372eee0edece0cf1d6dced94329db89.png) +- ``!!`` 执行上一次命令 \ No newline at end of file diff --git a/note/linux/command/ntpdate 时间同步.md b/note/linux/command/ntpdate 时间同步.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/paste 合并文件.md b/note/linux/command/paste 合并文件.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/pgrep 过滤出进程id.md b/note/linux/command/pgrep 过滤出进程id.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/ping 网络通断测试.md b/note/linux/command/ping 网络通断测试.md new file mode 100644 index 0000000..e69de29 diff --git a/note/linux/command/seq 构造序列.md b/note/linux/command/seq 构造序列.md new file mode 100644 index 0000000..911d224 --- /dev/null +++ b/note/linux/command/seq 构造序列.md @@ -0,0 +1,37 @@ +![67afdff1310314bd312eebfdafa233a6.png](../../../_resources/67afdff1310314bd312eebfdafa233a6.png) +- seq 5 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ seq 5 +1 +2 +3 +4 +5 +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` +- seq 5 10 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ seq 5 10 +5 +6 +7 +8 +9 +10 +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` +- seq 10 -1 1 +``` +me@me-EQ59:~/shell_demo/scripts/shell01$ seq 10 -1 1 +10 +9 +8 +7 +6 +5 +4 +3 +2 +1 +me@me-EQ59:~/shell_demo/scripts/shell01$ +``` \ No newline at end of file diff --git a/note/linux/command/sort 排序.md b/note/linux/command/sort 排序.md new file mode 100644 index 0000000..b01be93 --- /dev/null +++ b/note/linux/command/sort 排序.md @@ -0,0 +1,2 @@ +- 排序 +- 支持去重,去重后每份数据,只保留一份 \ No newline at end of file diff --git a/note/linux/command/tee 输入到文件.md b/note/linux/command/tee 输入到文件.md new file mode 100644 index 0000000..bcdf693 --- /dev/null +++ b/note/linux/command/tee 输入到文件.md @@ -0,0 +1,4 @@ +- 打印到屏幕,输出到文件 +- 默认是覆盖 +- -a 追加模式 +- echo "hello world" | tee -a 1.txt \ No newline at end of file diff --git a/note/linux/command/tr 字符串转换替换删除.md b/note/linux/command/tr 字符串转换替换删除.md new file mode 100644 index 0000000..c2edaa3 --- /dev/null +++ b/note/linux/command/tr 字符串转换替换删除.md @@ -0,0 +1,7 @@ +- 逐个替换,不是整体替换 +- command | tr string1 string2 替换命令执行结果中的string1为string2 +- tr string1 string2 < filename 替换文件中的string1为string2 +- tr options string1 < filename 替换文件中的匹配string 为string1 +- tr "a-z" "A-Z" < passwd 小写字母替换成大写字母 +- tr -d 'a-z' < passwd 只删除小写字母 +- https://www.runoob.com/linux/linux-comm-tr.html \ No newline at end of file diff --git a/note/linux/command/trap.md b/note/linux/command/trap.md new file mode 100644 index 0000000..5d34567 --- /dev/null +++ b/note/linux/command/trap.md @@ -0,0 +1,6 @@ + - 忽略屏蔽 某些信号 + - 可以用来忽略 ctrl c z +``` +#!/bin/env bash +trap : 1 2 3 9 20 +``` \ No newline at end of file diff --git a/note/linux/command/uniq 去重.md b/note/linux/command/uniq 去重.md new file mode 100644 index 0000000..2154ee9 --- /dev/null +++ b/note/linux/command/uniq 去重.md @@ -0,0 +1 @@ +- 连续的行去重 \ No newline at end of file diff --git a/note/linux/command/快速清空文件.md b/note/linux/command/快速清空文件.md new file mode 100644 index 0000000..09e17ee --- /dev/null +++ b/note/linux/command/快速清空文件.md @@ -0,0 +1 @@ +- `> filename` \ No newline at end of file diff --git a/note/linux/command/日志计数排序.md b/note/linux/command/日志计数排序.md new file mode 100644 index 0000000..4987017 --- /dev/null +++ b/note/linux/command/日志计数排序.md @@ -0,0 +1,11 @@ +### 总体命令 + +```bash +grep status=5 2023111317225316148_2023111317225316148 | grep -v "nd-membership-proxy.log.isis" | awk -F" " '{ print $17}' | sort| uniq -c | sort -k 1 -nr +``` + +#### 解读 + +- `uniq -c` : 表示合并相邻的重复记录,并统计重复数。因为uniq -c 只会合并相邻的记录,所以在使用该命令之前需要先排序 +- sort -k 1 -nr : 经过uniq -c 处理之后的数据格式形如"2 data",第一个字段是数字,表示重复的记录数;第二个字段为记录的内容。我们将对此内容进行排序。sort -k 1表示对于每行的第一个字段进行排序,这里即指代表重复记录数的那个字段。因为sort命令的默认排序是按照ASCII,这就会导致按从大到小进行排序时,数值2会排在数值11的前面,所以需要使用-n 参数指定sort命令按照数值大小进行排序。-r 表示逆序,即按照从大到小的顺序进行排序 +- `head -20` : 排完序,取前20行 \ No newline at end of file diff --git a/note/linux/command/检测端口是否开放.md b/note/linux/command/检测端口是否开放.md new file mode 100644 index 0000000..b5713aa --- /dev/null +++ b/note/linux/command/检测端口是否开放.md @@ -0,0 +1,5 @@ +```shell +#!/bin/bash +nc -z -w2 www.baidu.com 80 | grep -oP "succeeded" > /dev/null && [ $? == 0 ] && echo "bignat_ok:1" || echo "bignat_fail:1" +``` + diff --git a/note/linux/debian/debian 固定 ip.md b/note/linux/debian/debian 固定 ip.md new file mode 100644 index 0000000..04d1caa --- /dev/null +++ b/note/linux/debian/debian 固定 ip.md @@ -0,0 +1,17 @@ +# This file describes the network interfaces available on your system +# and how to activate them. For more information, see interfaces(5). + +source /etc/network/interfaces.d/* + +# The loopback network interface +auto lo +iface lo inet loopback + +# The primary network interface +allow-hotplug ens160 +auto ens160 +iface ens160 inet static +address 192.168.39.100 +netmask 255.255.255.0 +gateway 192.168.39.2 +dns-nameservers 192.168.39.2 \ No newline at end of file diff --git a/note/linux/debian/debian 换源.md b/note/linux/debian/debian 换源.md new file mode 100644 index 0000000..73acfc8 --- /dev/null +++ b/note/linux/debian/debian 换源.md @@ -0,0 +1,6 @@ +``` +sudo sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list + + +sudo sed -i 's|security.debian.org/debian-security|mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list +``` \ No newline at end of file diff --git a/note/linux/rshnc同步文件到群晖nas.md b/note/linux/rshnc同步文件到群晖nas.md new file mode 100644 index 0000000..fda4ee8 --- /dev/null +++ b/note/linux/rshnc同步文件到群晖nas.md @@ -0,0 +1,17 @@ +### ubuntu & debian系统安装 rsync + +```bash +apt install rsync +``` + +### ubuntu & debian 系统安装 sshpass + +```bash +apt install sshpass +``` + +### rsync通过ssh 传输文件到nas + +```bash +sshpass -p 'Sunqi0220.' rsync -av -e 'ssh' --progress /root/data/sql rsyncuser@rsynchost::share/backup/sql +``` \ No newline at end of file diff --git a/note/linux/编译安装git.md b/note/linux/编译安装git.md new file mode 100644 index 0000000..185cfc8 --- /dev/null +++ b/note/linux/编译安装git.md @@ -0,0 +1,34 @@ +### 1. 下载git + +wget --no-check-certificate https://mirrors.edge.kernel.org/pub/software/scm/git/git-2.30.1.tar.gz +### 2. 解压 + +tar -zxvf git-2.30.1.tar.gz +### 3. 进入文件夹 + +cd git-2.30.1 +### 4. 安装所需依赖 + +yum install curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker + +> 没有gcc的话需要安装gcc +> +> yum install gcc curl-devel expat-devel gettext-devel openssl-devel zlib-devel perl-ExtUtils-MakeMaker + +### 5.编译 + +make prefix=/usr/local/git all +### 6.安装 + +make prefix=/usr/local/git install +### 7.修改环境变量 + +vi /etc/profile +最下面加上: +export PATH=$PATH:/usr/local/git/bin +刷新环境变量 +source /etc/profile + + ### 8. 检查 + +git --version diff --git a/note/前端/vue3/nvm.md b/note/前端/vue3/nvm.md new file mode 100644 index 0000000..e38903d --- /dev/null +++ b/note/前端/vue3/nvm.md @@ -0,0 +1,27 @@ +### NVM安装 + +- [github](https://github.com/nvm-sh/nvm#installing-and-updating) + +```bash +curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash +# or +wget -qO- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash +``` + + + +### NVM安装后命令 + +```bash +export NVM_DIR="$HOME/.nvm" +[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm +[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion +``` + +### NVM 安装node + +```bash +nvm install 16.13.0 +nvm use 16.13.0 +``` + diff --git a/note/前端/vue3/vue3-vite.md b/note/前端/vue3/vue3-vite.md new file mode 100644 index 0000000..76cf1f5 --- /dev/null +++ b/note/前端/vue3/vue3-vite.md @@ -0,0 +1,35 @@ +### 项目初始化 + +```bash +npm init vue@3.5.0 +``` + + + +### 配置端口和监听地址 + +- vite.config.js + +```js +import { fileURLToPath, URL } from 'node:url' + +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [vue()], + server: { + port: 8998, // 自定义端口,默认为5173 + open: false, // 服务启动后,自动在浏览器中打开,默认是不打开的 + hmr: true, // 为开发服务启用热更新,默认是不启用热更新的 + host: '0.0.0.0', + }, + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + } +}) + +``` diff --git a/note/前端/vue3/vue语法.md b/note/前端/vue3/vue语法.md new file mode 100644 index 0000000..120d20e --- /dev/null +++ b/note/前端/vue3/vue语法.md @@ -0,0 +1,471 @@ +### 动态绑定 + +```html + + + + + +``` + + + +### 条件渲染 + +- v-if:只有条件值为v-if的时候才会渲染 +- v-else +- v-else-if +- v-show:每次都会渲染,v-show只会更改css的display属性 + +```html + + + +``` + +### 列表渲染 + +- 简单数据 + +```html + + + +``` + +- 复杂数据 + +```html + + + +``` + +- 遍历一个对象的所有属性 + +```html + + + +``` + +### 事件处理 + +- v-on +- 简写`:on` 或`@` +- 内联事件处理器 + +```html + + + +``` + +- 方法事件处理器 + +```html + + + +``` + +### 事件修饰符 + +```html + + + +``` + + + +### 数组动态监听 + +- 数组调用以下方法是,会动态的更新循环列表 + +- push +- pop +- shift +- unshift +- splice +- sort +- reverse + +```html + + + +``` + + + +### 计算属性 + +- 计算属性会基于其响应式依赖被缓存,一个计算属性只会在其响应式依赖更新时重新计算 + +```html + + +``` + + +### 侦听器 + +```html + + + +``` + +### 表单输入绑定 + +```html + + +``` + +- 修饰符 + - `lazy`:取消每次 `input` 事件后更新数据,改为在每次 `change` 事件后更新数据 + - `trim`:去除空格 + - `number`: 转为数字 + + + +### 模板引用 + +- 获取DOM + +```html + + +``` + + + +### 组件传递数据 + +- props +- 只能父组件向子组件传递数据,不能反向传递 +- 子组件 + +```html + + +``` + +- 父组件 + +```html + + + +``` + diff --git a/note/工具集/MacOS.md b/note/工具集/MacOS.md new file mode 100644 index 0000000..c55e974 --- /dev/null +++ b/note/工具集/MacOS.md @@ -0,0 +1,17 @@ +### 打开软件提示已损坏,移到废纸篓 + +- 方法一 + - 前往 设置——隐私与安全性——安全性(在设置最下面)——“强制打开已拦截的xxxx.app” + +- 方法二 + +```shell +sudo spctl --master-disable +``` + +### 安装PicGo无法打开 + +```shell +sudo xattr -d com.apple.quarantine "/Applications/PicGo.app" +``` + diff --git a/note/工具集/acme免费SSL证书.md b/note/工具集/acme免费SSL证书.md new file mode 100644 index 0000000..b1e6a3e --- /dev/null +++ b/note/工具集/acme免费SSL证书.md @@ -0,0 +1,38 @@ +### 1、安装blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog buypass证书 + +```shell +/root/.acme.sh/acme.sh --install-cert -d heysq.com --key-file /root/nginx/conf/ssl_file/image_heysq_com/key.pem --fullchain-file /root/nginx/conf/ssl_file/image_heysq_com/cert.pem +``` + +### 2、安装heysq.com 泛域名 lets encrypt 证书 + +```shell +/root/.acme.sh/acme.sh --install-cert -d heysq.com \ +--key-file /root/nginx/conf/ssl_file/heysq_com/key.pem \ +--fullchain-file /root/nginx/conf/ssl_file/heysq_com/cert.pem \ +--reloadcmd "service nginx force-reload" +``` + +### 3、注册buypass账号 + +```shell +/root/.acme.sh/acme.sh --server https://api.buypass.com/acme/directory --register-account --accountemail xxx@163.com +``` + +### 4、申请buypass证书-腾讯dns pod 解析 + +```shell +export DP_Id=111111 +export DP_Key=xxxxxxxxxxxxxxxxx +/root/.acme.sh/acme.sh --issue --dns dns_dp -d heysq.com -d blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog --server https://api.buypass.com/acme/directory +``` + +### 5. 签发安装buypass证书 + +```bash +# 签发证书 +acme.sh --issue -d igit.heysq.com --days 175 --dns dns_dp --server https://api.buypass.com/acme/directory --force +# 安装证书 +acme.sh --install-cert -d igit.heysq.com --key-file /etc/gitlab/ssl/key.pem --fullchain-file /etc/gitlab/ssl/cert.pem +``` + diff --git a/note/工具集/centos同步时间.md b/note/工具集/centos同步时间.md new file mode 100644 index 0000000..f6a7f61 --- /dev/null +++ b/note/工具集/centos同步时间.md @@ -0,0 +1,36 @@ +### centos7 时间同步 + +#### 1. 安装 ntpdate + +```bash +yum install ntp -y +``` + +#### 2. 同步阿里云时间 + +```bash +ntpdate ntp3.aliyun.com +``` + +#### 3. 定时同步 + +- 10分钟同步一次 + +```bash +0-59/10 * * * * /usr/sbin/ntpdate ntp3.aliyun.com +``` + +### centos8 时间同步 + +```bash +yum install -y chrony +systemctl enable chronyd --now +mv /etc/chrony.conf /etc/chrony.conf.bak +cat >> /etc/chrony.conf << EOF +server ntp.aliyun.com iburst +server cn.ntp.org.cn iburst +EOF +systemctl restart chronyd.service +chronyc sources -v +``` + diff --git a/note/工具集/code-server+nginx.md b/note/工具集/code-server+nginx.md new file mode 100644 index 0000000..207c054 --- /dev/null +++ b/note/工具集/code-server+nginx.md @@ -0,0 +1,29 @@ +### 部署code-server + +``` bash +mkdir -p ~/.config +docker run -itd --name code-server \ + --network=internal \ + -v "/mnt/sdb3/Configs/code-server/.local/:/home/code/.local/" \ + -v "/mnt/sdb3/Configs/code-server/.config/:/root/.config/" \ + -v "/mnt/sdb3/Configs/code-server/project:/home/coder/project" \ + -u "$(id -u):$(id -g)" \ + -e "DOCKER_USER=$USER" \ + codercom/code-server:latest +``` + +### nginx 反向代理 + +```go +location /code/ { + proxy_pass http://code-server:8080/; + proxy_http_version 1.1; + proxy_set_header Host $host:8443/code/; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection upgrade; + proxy_set_header Accept-Encoding gzip; + proxy_connect_timeout 600; + proxy_set_header Origin ""; +} +``` + diff --git a/note/工具集/docker空容器常驻运行.md b/note/工具集/docker空容器常驻运行.md new file mode 100644 index 0000000..ea06a90 --- /dev/null +++ b/note/工具集/docker空容器常驻运行.md @@ -0,0 +1,6 @@ +### 睡眠 + +```bash +docker run -d --name container-name image:tags sleep inf +``` + diff --git a/note/工具集/docker部署ddns-go.md b/note/工具集/docker部署ddns-go.md new file mode 100644 index 0000000..8e068a4 --- /dev/null +++ b/note/工具集/docker部署ddns-go.md @@ -0,0 +1,10 @@ +### 1. Github仓库 + +- https://github.com/jeessy2/ddns-go + +### 2. docker部署 + +```bash +docker run -d --name ddns-go --restart=always --net=host -v /opt/ddns-go:/root jeessy/ddns-go +``` + diff --git a/note/工具集/hyper-v/嵌套虚拟化.md b/note/工具集/hyper-v/嵌套虚拟化.md new file mode 100644 index 0000000..f3e8f9c --- /dev/null +++ b/note/工具集/hyper-v/嵌套虚拟化.md @@ -0,0 +1,9 @@ +### 查询是否支持嵌套虚拟化 + +- ` Get-VMProcessor -VMName KVM主机 | fl ` + +![image-20240422174723851](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202404221747000.png) + +### 开启嵌套虚拟化 + +- `Set-VMProcessor -ExposeVirtualizationExtensions $true -VMName KVM主机` \ No newline at end of file diff --git a/note/工具集/linux+zsh.md b/note/工具集/linux+zsh.md new file mode 100644 index 0000000..40b6d4e --- /dev/null +++ b/note/工具集/linux+zsh.md @@ -0,0 +1,35 @@ +### 1.原码安装ZSH新版 + +> ubuntu 18.04 安装的zsh版本较低(5.4)oh-my-zsh需要使用5.8以上 + +1. 源码网址 https://zsh.sourceforge.io/Arc/source.html + +2. `wget https://jaist.dl.sourceforge.net/project/zsh/zsh/5.8/zsh-5.8.tar.xz --no-check-certificate` + +3. `tar xvf zsh-5.8.tar.xz` + +4. centos 需要 + + ```shell + yum install gcc perl-ExtUtils-MakeMaker + yum install ncurses-devel + ``` + +5. `cd zsh-5.8` + +6. `./configure` + +7. `make && make install` + +8. `vim /etc/shells` 添加 `/usr/local/bin/zsh` + +### 2.安装ohmyzsh + +1. `sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"` + +### 3.安装插件 + +1. `git clone https://github.com/zsh-users/zsh-autosuggestions ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-autosuggestions` +2. `git clone https://github.com/zsh-users/zsh-syntax-highlighting.git ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/zsh-syntax-highlighting` +3. `zsh-syntax-highlighting zsh-autosuggestions` + diff --git a/note/工具集/multipass ubuntu 虚拟机.md b/note/工具集/multipass ubuntu 虚拟机.md new file mode 100644 index 0000000..00aef49 --- /dev/null +++ b/note/工具集/multipass ubuntu 虚拟机.md @@ -0,0 +1,4 @@ +### 创建虚拟机 +```shell +multipass launch -n test_vm -c 2 -m 4G -d 20G 22.04 +``` \ No newline at end of file diff --git a/note/工具集/pve/lxc安装openwrt.md b/note/工具集/pve/lxc安装openwrt.md new file mode 100644 index 0000000..eb49383 --- /dev/null +++ b/note/工具集/pve/lxc安装openwrt.md @@ -0,0 +1,178 @@ +### 1. 下载openwrt镜像 + +- https://openwrt.org/downloads + +- 选用中国区地址 + +image-20240525165203213 + +- 选设备类型 + + ![image-20240525165319112](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251653170.png) + +- 选用rootfs.tar.gz,可以直接用于pve的lxc模板 + + ![image-20240525165400390](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251654443.png) + +### 2. 上传lxc模板到pve + +#### 2.1 如果直接下载的是rootfs.tar.gz文件直接上传即可 + +![image-20240525165516671](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251655726.png) + +#### 2.2 如果下载的为带rootfs的img.gz文件,则需要解压img文件 + +- 通过 ssh 工具将文件上传到pve服务器上 + +- 安装img文件解压工具 + + ```bash + apt install squashfs-tools + ``` + +- 解压 img文件,需要在img文件存储路径操作 + + ![image-20240525173410855](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251734920.png) + + ```bash + unsquashfs openwrt-23.05.2-x86-legacy-generic-squashfs-rootfs.img + ``` + +- 解压成功后会在当前目录生成`squashfs-root`文件夹 + + ![image-20240525173526262](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251735315.png) + +- 进入生成的文件夹内,打包解压出的内容到pve的lxc模板存储路径内 + + ```bash + tar zcf /var/lib/vz/template/cache/OpenWRT-x86-64-generic-rootfs.tar.gz ./* + ``` + + 也可以将`/var/lib/vz/template/cache/`替换为其他的pve存储目录对应的路径,一般在`/mnt/pve`下边 + +#### 2.3 如果下载的文件为img.gz文件,则需要挂载打包处理 + +- 开启 nbd模块 + + ```bash + modprobe nbd + ``` + +- 挂载img文件到设备 + + ```bash + qemu-nbd -c /dev/nbd0 -f raw OpenWRT-x86-64-generic-squashfs-combined.img + ``` + +- 查看挂载的分区 + + ```bash + lsblk -f /dev/nbd0 + ``` + + ![image-20240525182033136](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251820206.png) + +- 挂载带有squashfs的文件 + + ```bash + mkdir /mnt/openwrt + mount /dev/nbd0p2 /mnt/openwrt + ``` + +- 进入挂载的openwrt目录,打包lxc模板 + + ```bash + cd /mnt/openwrt + tar -czvf /var/lib/vz/template/cache/istoreos.tar.gz * + ``` + +- 模板打包完成后卸载挂载的文件与nbd设备 + + ```bash + cd .. # 移动到openwrt文件夹的上一层 + umount /mnt/openwrt + qemu-nbd -d /dev/nbd0 + ``` + + + + + +### 3. 创建pve lxc容器 + +```bash +pct create 102 ssd1t:vztmpl/OpenWRT-x86-64-generic-rootfs.tar.gz --rootfs ssd1t:32 --ostype unmanaged --hostname openwrt --arch amd64 --cores 4 --memory 1024 --swap 0 -net0 bridge=vmbr0,name=eth0 +``` + +- `pct create` 创建命令 + +- `102` pve容器序列号,不要重复 + +- `ssd1t:vztmpl/OpenWRT-x86-64-generic-rootfs.tar.gz`:模板路径,前边ssd1t为pve存储的ID,一定要选中数据中心,然后再点右侧的存储 + + ![image-20240525165842480](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251658517.png) + +- `--rootfs`:指定模板未rootfs文件 + +- `ssd1t:32`:存储路径,ssd1t依然为存储的ID,代表将openwrt的虚拟磁盘存储在ssd1t的硬盘下,32为虚拟磁盘容量大小 + +- `--ostype unmanaged`:指定操作系统类型,暂时只能写`unmanaged` + +- `--hostname openwrt`:主机名,随便取一个,我用openwrt + +- `--arch amd64`:虚拟机架构,我是x86的主机所以选择`amd64`,如果不确定可以在网页端创建一个lxc容器然后查看架构 + + ![image-20240525170403423](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251704498.png) + +- `--cores 4`:cpu核心数,随意分配 + +- `--memory 1024`:内存大小,单位`M` + +- `--swap 0`:swap分区大小,如果不需要设置为0 + +- `-net0 bridge=vmbr0,name=eth0`:网络参数,桥接pve的网卡,并将网卡名字设置为`eth0`,在openwrt里可以直接用 + +### 5. 查看创建的openwrt容器 + +- 概要 + + ![image-20240525170740114](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251707164.png) + +- 资源分配,主要为内存和硬盘和CPU + + ![image-20240525170804348](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251708411.png) + +- 网络,因为用命令行创建时没有为容器分配ip地址,所以可以用web管理页面,给容器分配ip,我已经分配好了 + + ![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/image-20240525170827336.png) + +- 容器选项,基本是一些附加功能 + + ![image-20240525170948198](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251709253.png) + +### 6. 启动openwrt + +- 控制台输出,按回车可以进入openwrt的终端窗口 + + ![image-20240525171109336](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251711403.png) + +- 可以在终端修改网卡配置![image-20240525171246977](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251712024.png) + +- 修改网卡配置 + + - `vi /etc/config/network` + - lan接口的ip地址修改为刚刚在pve管理页设置的ip + + ![image-20240525171346917](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251713972.png) + +### 7. 登录openwrt + +- 浏览器访问openwrt的lan接口的ip,首次进入root密码不需要数据,登录后会提示设置密码 + +![image-20240525171504464](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251715519.png) + +- openwrt状态信息 + + ![image-20240525171628857](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251716907.png) + +- ![image-20240525171703528](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/halo2/202405251717626.png) \ No newline at end of file diff --git a/note/工具集/pve/pve开启samba服务.md b/note/工具集/pve/pve开启samba服务.md new file mode 100644 index 0000000..b3e81cc --- /dev/null +++ b/note/工具集/pve/pve开启samba服务.md @@ -0,0 +1,83 @@ +### 背景 + +- 不想安装虚拟机或lxc容器 +- 可以方便的在pve宿主机直接查询共享的文件 + +### 安装samba + +- 更换[中科大软件源](http://mirrors.ustc.edu.cn/help/proxmox.html)(可选) + +```bash +sed -i 's|^deb http://ftp.debian.org|deb https://mirrors.ustc.edu.cn|g' /etc/apt/sources.list +sed -i 's|^deb http://security.debian.org|deb https://mirrors.ustc.edu.cn/debian-security|g' /etc/apt/sources.list +``` + +- pve 8 执行 + +```bash +echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/pve bookworm pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list +``` + +- pve 7 执行 + +```bash +echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/pve bullseye pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list +``` + +- pve 6 执行 + +```bash +echo "deb https://mirrors.ustc.edu.cn/proxmox/debian/pve buster pve-no-subscription" > /etc/apt/sources.list.d/pve-no-subscription.list +``` + +- 更新软件包缓存,安装samba + +```bash +apt update +apt install samba samba-common -y +``` + +### 配置samba用户以及共享目录 + +- 添加用户 + +```bash +useradd smbuser +``` + +- 设置samba用户密码 + +```bash +smbpasswd -a smbuser +``` + +- 创建共享目录 + +```bash +mkdir /mnt/pve/ssd1t/share +``` + +- 共享目录设置授权 + +```bash +chown -R smbuser /mnt/pve/ssd1t/share +``` + +### 配置samba服务 + +- 修改samba配置文件 `/etc/samba/samba.conf`文件末尾追加配置,其余默认即可 + +```bash +[share] +comment = smbuser files +path = /mnt/pve/ssd1t/share +guest ok = no +browseable = no +write list = smbuser +``` + +### 重启samba服务 + +```bash +systemctl restart smbd +``` \ No newline at end of file diff --git a/note/工具集/pve/解锁虚拟机.md b/note/工具集/pve/解锁虚拟机.md new file mode 100644 index 0000000..53881b0 --- /dev/null +++ b/note/工具集/pve/解锁虚拟机.md @@ -0,0 +1,10 @@ +### 1. 解锁虚拟机 + +- 进入pve shell 命令行 + +```bash +qm unlock 100 +``` + + + diff --git a/note/工具集/ubuntu关闭自动睡眠.md b/note/工具集/ubuntu关闭自动睡眠.md new file mode 100644 index 0000000..19dd7ef --- /dev/null +++ b/note/工具集/ubuntu关闭自动睡眠.md @@ -0,0 +1,17 @@ +### 一、查看自动睡眠状态 + +````shell +sudo systemctl status sleep.target +```` + +- Loaded:表示自动睡眠开启 + +![image-20230709082424627](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709082424627.png) + +### 二、关闭自动睡眠和挂起 + +```shell +sudo systemctl mask sleep.target suspend.target hibernate.target hybrid-sleep.target +``` + +![image-20230709082545897](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709082545897.png) \ No newline at end of file diff --git a/note/工具集/ubuntu安装kubectl.md b/note/工具集/ubuntu安装kubectl.md new file mode 100644 index 0000000..dd36717 --- /dev/null +++ b/note/工具集/ubuntu安装kubectl.md @@ -0,0 +1,51 @@ +> [官网安装](https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/) +> +> 推荐使用snap方式安装 + +### 1. 二进制包方式安装 + +#### 1.1 下载二进制包 + +```bash +curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" +``` + +#### 1.2 安装二进制包 + +```bash +sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl +``` + + + +### 2.1 包管理器安装 + +#### 2.1 必要组件安装 + +```bash +sudo apt-get update +sudo apt-get install -y ca-certificates curl +``` + +#### 2.2 添加软件源 + +```bash +curl -fsSL https://packages.cloud.google.com/apt/doc/apt-key.gpg | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-archive-keyring.gpg + +echo "deb [signed-by=/etc/apt/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list + +``` + +#### 2.3 更新软件源&&安装kubectl + +```bash +sudo apt-get update +sudo apt-get install -y kubectl +``` + +### 3 snap包安装 + +```bash +sudo snap install kubectl --classic +``` + diff --git a/note/工具集/ubuntu防火墙ufw.md b/note/工具集/ubuntu防火墙ufw.md new file mode 100644 index 0000000..f0acc1c --- /dev/null +++ b/note/工具集/ubuntu防火墙ufw.md @@ -0,0 +1,38 @@ +### 1. 查看防火墙状态 + +```bash +sudo ufw status +``` + +### 2. 开启防火墙 + +```bash +sudo ufw enable +``` + +### 3. 关闭防火墙 + +```bash +sudo ufw disable +``` + +### 4. 放开端口 + +```bash +sudo ufw allow 8000/tcp +sudo ufw allow 443 +``` + +### 5. 网卡+IP端 + +```bash +sudo ufw allow in on ens18 from 192.168.0.0/24 +sudo ufw allow out on ens18 from 192.168.0.0/24 +``` + +### 6. 更多用法 + +```bash +man ufw +``` + diff --git a/note/工具集/配置.md b/note/工具集/配置.md new file mode 100644 index 0000000..e0ab7d6 --- /dev/null +++ b/note/工具集/配置.md @@ -0,0 +1,89 @@ +### 加密内容 + +- 解密网站 http://m.ab173.com/gongju/enc/aesdes.php + +``` +U2FsdGVkX1+6VCHcz70lEgp/RYU+RTGsJ4QP/bkpQVjqCJ6U9VC+lcFEI/n4kcmW +I2Y18Nrnbl/CIiRfRmZIFM22cNwL95fImcaPRlp3oGY1CQ4Ad9nbEok4Cp5IwVqn +y4qrsIti7wjTwiJRiYMfqHW0bJboF/oB6NjW2Pm1r92XyvhTehvG7UYt58o3xWkF +ERleA1vEfTE2M9XnP4RfYP15KyMciSAXuAp7gs54uaL7sxuC98FZFC1bJoTuTHJB +nCKBjBY/JcJ7h3Yb6q5fXCz5z4QCMI9WOZOogzYVqpLDLbcRvKpK2AARdb76g32d +RYPMHsU9X8PWTRAY8lK8yWnv9wFj1AwEUdaA7pfKM53z5447dSyz3cgGPkMMb8w+ ++wuvti54Cdq4yAlilciZO5o2hTKH1ysQC0cg08CuyWETMynNZfLYiDTsYWYu9v5b +gA60d5sR8JyBi2wekfHrWW7GoC5ZsGbX8FbsL2plLFJrzUq07GuPgjeekx/Q4d6S +jN6PuLHTT9p/gYKORhpcEC3so0IBrTXbxaeOzDGzXehInbsgzcschd/AZDrjHgTI +twx5qWD5YP6wESe1FWW3MmIDbghpyrQKWy2tif2lrBirF4pvE05PQw7SFItBzsgS +QZ+/jjRrEVJJ3PQYz2xEY2xAve2fo554NXqQr7V2+5ESIGwW8GkmW4EZlwsGp5eS +o9ypo8mozudCn4Ihn6cd0p1QWIsj3lO/TS8hCvqVVRuXonUI8JSJPJannS/9g8h+ +J7GXh0TgTB2gmmZaOnublg3m/qh8wi61zgm8Jt++TG0uOp2iAstviFX8TWfUfagA +ogjGgrqH9OgR5kcBpw6Q7i9kwXq0fsWM4qv/JjgZsheH7z9dwGMV7PPfIsmrnHhu +mC8e+Ta5MTMFyCvSpcMeCkPOdTDghWXdF+p1nDTQh5AwJU/RwhFRizoSl/JWrMB5 +CVNOiPdpIXp4/d7+hG2Fx3kH94/Cg88V9u1sF9GuUmA4JsCWbjRWnboK85r+T9hc +upMKyF2XXM0Czu9dcwfjGJ0i7QWKoY3EtEa4ayUzLJY= +``` + +### dns pod + +``` +U2FsdGVkX1/N32b9dCvC4UlZ/nrCsPr1eFMcctHJf8LA5fQsjx0Iv5/+mWiKS3CT/+AoSkXxppMaRikKfvo8cCqJTGbUjNbbNph1lJMl/uHfQnAyiriKA/kst8Yo3kto35Bjz/NwyARPM9hVeZ0Grg== +``` + +### 七牛云 + +``` +U2FsdGVkX19/u8omBKTZA3j7eQk63LCMcm5AMZryBhS/ycV9UdrDoSGVamdRtzHSwqJc5iLvMuzlBg1h8aRKgjkkaiQAOLK+B7SGZpe3GKSuUmHZ6Ya0t6NTR0WlqqpQTQbftmXzKmVXVSc7YXit6g== +``` + +### 七牛云 OSS +``` +id 1jiNCYA49ReH13UNwd0fISHf8MhEY2-5BLaBVWsd +key ZjHFBUePY2DsSkgdZfh_qBpYnjWmCtHOB6y0YrKw +``` + +### 网易邮箱 + +``` +U2FsdGVkX19dlR6qDlCik9eSM6uAoEi7f/WroAayqPS6o1CMsJ7a3HXlb+NV7Unk +``` + +### 密码 +``` +U2FsdGVkX1/kQ7T1xHgZIyr2ZG2Vk6i/O9LegjlxIV4= +``` + +### ChatGPT +``` +U2FsdGVkX196467Apj7pcz6QodyvOkM6Ka6JrFmY0veCUJ70nhu79GdBZbcmBD4rUwSmY+5H8/YrPxmv9Y1wENo9BMtAiDzkP1/AI2TyopP+XWrWyn8eUukWid36Idaj53CT9t9uTs6pcDxiHF5ZmzU1p5W6pV0eHGQGrHX2y/67lyPp7xlFVXmFB+MWaqy9jKgwLQ28Vai4No4X7E3XvAYcSnbqQ97IgcoOUtNr+nqwt4DmUdxHgIkDwWIbCEHx +``` + +#### 2929 邮箱 +``` +U2FsdGVkX19fOub+t6qRDYRA4/GmHJ4RQUcFlj5lkEx1ttZDj6GX1I/m9oXnSo1Q +``` + +#### github token +``` +U2FsdGVkX18IO/4qgTaX3zEtFlV9PM3AQzM3S52VkHl3RJvWODsTOeVycsvlRV6eI1sXlLTKf6+CLTixOG8bZw== +``` + +### 腾讯云秘钥 +``` +U2FsdGVkX1870/3slLBiO0tMRvUvuLk7ZpJqGdI+PE4yjIDM7C3TvZJOJoDHtqspJKI0WO9CFchGyOBpvTenJLsZG9ceT6baAcVK7OFgu9YQSRk1oRJ6KPHtghTbNK4A1mwcIk5tsEMxXm8ztO4MFQ== +``` + +### RackNerd VPS Control Panel + +``` +U2FsdGVkX18wLp6UcMj/99x1NYUXWKA8YeeYUsEsKIXJZ2BFdQVs8NIxDJZU9jMeWeOFM1jLkv+uJiF09BRu4jg3AGfDyczn5iNunHcP6cn+iYNQaqMpIO0/yedN33UUJDkmlVQIDh2nqg8d9ChhggG/dDsAolx+Z6mq2yxIv/2FWKtB3UJwQkS8xP3OoPem +``` + + + +### RackNerd VPS SSH User + +``` +U2FsdGVkX187NKcNGk7D4hz35oALJw5h0TJkEBMfnvDCQnX5bgSewJaL2kW6Kv9ZCzyL/8SR0jbgzfmEtyOK0zdHRpVTeZ+L3SEUzg45FvkZgh2FVy6UAn2w5dgezc/EJRuq85rG7TO25Vj01hm6lA== +``` + + + diff --git a/note/工具集/阿里云docker加速.md b/note/工具集/阿里云docker加速.md new file mode 100644 index 0000000..46dfa87 --- /dev/null +++ b/note/工具集/阿里云docker加速.md @@ -0,0 +1,26 @@ +### 1. Ubuntu + +```shell +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": ["https://ywylpup9.mirror.aliyuncs.com"] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + +### 2.CentOS + +```shell +sudo mkdir -p /etc/docker +sudo tee /etc/docker/daemon.json <<-'EOF' +{ + "registry-mirrors": ["https://ywylpup9.mirror.aliyuncs.com"] +} +EOF +sudo systemctl daemon-reload +sudo systemctl restart docker +``` + diff --git a/note/操作系统/内存虚拟化.md b/note/操作系统/内存虚拟化.md new file mode 100644 index 0000000..bfb806c --- /dev/null +++ b/note/操作系统/内存虚拟化.md @@ -0,0 +1,220 @@ +### 1. 虚拟内存 + +- 透明:对应用程序透明,程序觉得自己获得的都是私有的物理内存 +- 效率: + - 时间上,分配虚拟内存要快,不要拖慢应用程序的执行速度 + - 空间上,不要占用太多内存来支持虚拟化 +- 保护:操作系统应该要对进程的内存进行保护,免受其他进程影响,操作系统也不应该受其他进程影响 + - 进程和进程之间隔离 + +### 2. 栈内存和堆内存 + +- 栈内存:申请和释放操作由编译器来管理,常用于函数内部定义的变量 +- 堆内存:长期存在,函数结束后不会被释放掉,所有的申请和释放操作都显示完成 + +### 3. 地址转换 + +- 基于硬件的地址转换 +- hardware-based address translation +- 将应用程序中的内存引用重定向到实际物理内存地址 +- 操作系统提供内存的管理操作,记录被占用和空闲的位置 + +### 4. 基于硬件的重定位 + +- 每个CPU含有两个寄存器 + - 基址寄存器:存储物理内存的开始地址 + - 限界寄存器:存储物理内存最大分配的范围,保护内存访问不会越界 +- 进程访问超过限界寄存器的地址或负数的地址,会被终止 +- 真实物理地址 = 基址寄存器地址 + 虚拟内存地址 +- 缺点: + - 内存空间碎片 + - 需要空闲链表来表示哪个区块是空闲的 + +![进程内存重定位](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230724150902703.png) + +> CPU负责内存地址转换的部分也称为MMU,内存管理单元,Memory Management Unit + +### 5. 分段 + +> 出现原因: +> +> - 基于硬件的地址转换太过于浪费内存,在堆和栈之间的内存,并没有被完全使用,但是依然占用了物理内存 +> - 剩下的内存如果连续范围不够大,就会造成进程无法运行 + +- 原理:给内存中的每个段都分配一对基址+限界寄存器 + - 代码段 + - 栈段 + - 堆段 +- 分段的机制使操作系统可以将不同的段放在物理内存不同位置 + +image-20230725204859460 + +#### 5.1 怎么确定当前地址访问哪个段 + +- 地址二进制前两位,用来表示代码段,栈段和堆段 + - 用两位表示3个短,空间浪费 +- 还可以用地址的产生方式来表示 + - 如果地址由程序计数器产生(即它是指令获取),那么地址在代码段 + - 如果基于栈或基址指针,它一定在栈段。其他地址则在堆段 + +#### 5.2 段共享 + +- 硬件和操作系统在地址中加标识,可以用来共享段 +- 代码段共享,每个进程都可以访问执行代码段中指令 +- 物理内存中的一个段可以映射到多个虚拟地址空间 + +#### 5.3 分段机制缺陷 + +- 空间割裂,造成内存浪费 + - 每个段之间的空闲空间发利用 +- 空间大小不一致,内存无法分配 + - 进程需要一个24KB的空间,但是分段后没有一个足够大的内存空间来支撑分配 + +image-20230726113528993 + +> 上面展示了该问题的一个例子。在这个例子中,全部可用空闲空间是 20 字节,但被切成 +> 两个 10 字节大小的碎片,导致一个 15 字节的分配请求失败 + +#### 5.4 空闲空间管理 + +> 空闲空间列表:将所有空闲的空间统计到一起,方便统计和分配空间 +> +> image-20230726114634791 + +- 分割与合并 + + - 分割:此时如果需要分配大小为1个的空间,将会从前后两个free空间中分配1个,假设分割20后的空闲空间,则空闲列表的第二个空闲空间将会变成21 + + image-20230726114850318 + + - 合并:此时如果中间使用的10个空间释放了,则空闲列表会进行合并为一个节点,而不是变成三个节点 + + - 合并前,三个连续的10个大小的空闲空间,只能分配小于等于10空间给其他进程 + - 合并后,可以分配更大的空间给其他进程 + + image-20230726115025892 + +#### 5.5. 多种空闲空间分配方式 + +- 最优匹配:先遍历整个空闲列表,找到和请求大小一样或更大的空闲块,然后返回这组候选者中最小的一块 + + - 优点:减少空间浪费 + - 缺点:遍历列表有性能损失较多 + +- 最差匹配:与最优匹配相反,尝试找最大的空闲块,分割并满足用户需求后,将剩余的块(很大)加入空闲列表。最差匹配尝试在空闲列表中保留较大的块,而不是向最优匹配那样可能剩下很多难以利用的小块 + + - 缺点:遍历列表性能损耗,剩很多小碎块无法分配 + +- 首次匹配:遍历列表,将遇到的第一个满足需求的空闲块返回给用户,分割后剩下的空间加入空闲列表 + + - 优点:性能损耗较少,通常不需要遍历整个列表 + - 缺点:列表开头会有很多小碎空间 + +- 下次查找:用指针记录上一次分配的位置,本次分配继续沿着上一次的指针向后遍历 + + - 优点:避免了遍历查找,碎空间分布在列表各个位置 + +- 分离空闲列表:如果某个应用程序经常申请一种(或几种)大小的内存空间,就用一个独立的列表,只 + + 管理这样大小的对象。其他大小的请求交给更通用的内存分配程序 + + - 优点:解决碎片问题 + +- 伙伴系统:空闲空间首先从概念上被看成大小为 2*N* 的大空间。当有一个内存分配请求时,空闲空间被递归地一分为二,直到刚好可以满足请求的大小(再一分为二就无法满足) + + - 优点:合并速度快,当有一个8kb的块释放后,会检查伙伴8kb,是否释放,如果释放就进行合并操作,然后递归向上合并 + +- 平衡二叉树、伸展树和偏序树 + +### 6. 分页 + +> 分段固有缺陷:内存碎片化 + +- 虚拟内存中将空间分割成固定长度的分片,分割成固定大小的一个单元,每个单元称为一个页 +- 把物理内存看成是定长槽块的阵列,叫作页帧 + +![image-20230726180817486](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230726180817486.png) + +> - 虚拟内存页4个,每个大小16字节 +> - 物理内存页帧8个,每个大小也是16字节 +> - 如果操作系统希望将 64 字节的小地址空间放到 8 页的物理地址空间中,它只要找到 4 个空闲页。操作系统保存了一个所有空闲页的空闲列表(free list),只需要从这个列表中拿出 4 个空闲页。在这个例子 里,操作系统将地址空间的虚拟页 0 放在物理页帧 3,虚拟页 1 放在物理页帧 7,虚拟页 2放在物理页帧 5,虚拟页 3 放在物理页帧 2。页帧 1、4、6 目前是空闲的。 + +#### 6.1 页表 + +- 操作系统为每个进程保存一个数据结构,记录地址空间的每个虚拟页放在物理内存中的位置,称为**页表(page table)** +- 主要作用是为地址空间的每个虚拟页面保存地址转换(address translation),让我们知道每个页在物理内存中的位置 +- 对应的例子有四个条目(虚拟页 0→物理帧 3)、(VP 1→PF 7)、 (VP 2→PF 5)和(VP 3→PF 2) + +#### 6.2 虚拟地址到物理地址转换 + +> 首先将虚拟地址分成两个组件:虚拟页面号(virtual page number,VPN)和页内的偏移量(offset)。 + +- 进程的虚拟地址空间是64字节,2^6,需要6位来表示 +- 虚拟页的数量为4个,2^2,需要2位来表示 +- 每个页的大小为16字节,2^4,需要4位来表示 +- 最高位Va5,最低位Va0 +- 于是例子中的虚拟地址表示如下,前两位为页号,后4位为每个页面内的偏移量 + +image-20230726182834419 + +> - 虚拟地址`21`,二进制`为010101`,则页号位第一页,偏移量为5,也就是第一页第5个字节处 +> - 根据页表,找出属于第7个物理页,因此可以通过替换虚拟页号为物理页号,进而找到物理地址 + +image-20230726183239264 + +#### 6.3 页表存放内容 + +- 假设页表是一个数组,通过虚拟页号(VPN)检索到页表项(PTN),最终定位到物理页号(PFN) +- 有效位(valid bit)通常用于指示特定地址转换是否有效。例如,当一个程序开始运行时,它的代码和堆在其地址空间的一端,栈在另一端。所有未使用的中间空间都将被标记为无效(invalid),如果进程尝试访问这种内存,就会陷入操作系统,可能会导致该进程终止 +- 保护位(protection bit),表明页是否可以读取、写入或执行。以不允许的方式访问页,会陷入操作系统 +- 存在位(present bit)表示该页是在物理存储器还是在磁盘上(即它已被换出,swapped out) +- 脏位(dirty bit)也很常见,表明页面被带入内存后是否被修改过 + +![image-20230726184526113](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230726184526113.png) + +> x86 架构的示例页表项[I09]。它包含一个存在位(P),确定是否允许写入该页面的读/写位(R/W) 确定用户模式进程是否可以访问该页面的用户/超级用户位(U/S),有几位(PWT、PCD、PAT 和 G)确定硬件缓存如何为这些页面工作,一个访问位(A)和一个脏位(D),最后是页帧号(PFN)本身 + + + +#### 6.4 芯片内的快速地址转换(地址转换旁路缓冲存储器 (translation-lookaside buffer TLB) + +> 背景:页表的信息一般需要放在物理内存中,进行地址转换,需要在内存中查一次页表,进行一次内存读取,影响性能 + +- 地址转换缓存(address-translation cache),对每次内存访问,硬件先检查 TLB,看看其中是否有期望的转换映射,如果有,就完成转换(很快),不用访问页表(其中有全部的转换映射) +- 首先从虚拟地址中提取出页号(VPN),然后检查TLB中是否有对应页号的转换映射,如果有则命中了TLB,从映射中取出物理页号(页帧号PFN)原来虚拟地址中的偏移量组合形成期望的物理地址(PA),并访问内存 +- 如果没有命中TLB,硬件就会访问页表,找到对应的映射,并将映射更新到TLB中,然后重新执行指令 +- 连续访问同一个虚拟页中的数据,只会在第一次TLB位命中,后续都会命中,如果TLB足够大,缓存时间足够长,则后续再次访问该虚拟页就都会命中TLB + +#### 6.5 更新TLB中内容 + +- CISC 复杂指令集 x86 架构,由硬件进行更新 +- RISC 精简指令集,由操作系统进行更新 + +#### 6.6 上下文切换TLB的变化 + +- TLB中存储的映射关系,只对当前进程生效,对其他进程是没有意义的 +- 在上下文切换时,操作系统通过某个特权寄存器存储当前进程的地址空间标识符asid(address space identifier) +- 在TLB中增加asid字段,标识每个映射对应的进程,实现跨上下文切换的 TLB 共享 + +![image-20230727111238640](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230727111238640.png) + +#### 6.7 优化较大的页表 + +> 32位的地址空间,4KB的页,和4字节的页表项,一个地址空间中大约有一百万个虚拟页面(2^32/2^12)。乘以页表项的大小,发现页表大小为 4MB,通常每个进程都会持有一个页表。如果有上百个进程,页表占用的内存空间就会打到数百兆 + +##### 6.7.1 简单的解决方案:更大的页 + +- 以 32 位地址空间为例,这次假设用 16KB(2^14) 的页。因此,会有 18 位的 VPN 加上 14 位的偏移量。假设每个页表项(4字节)的大小相同,现在线性页表中有 2^18 个项,因此每个页表的总大小为 1MB,页表缩到四分之一 + +##### 6.7.2 混合方法:分段和分页 + +- 分段依然是代码段,栈段和堆段,依然是用地址空间的前两位表示访问的是哪个段 +- 每个段都有一对寄存器 + - 基址寄存器:保存该段的页表的物理地址 + - 限界寄存器:页表的结尾(即它有多少有效页) +- TLB未命中时,用地址空间前两位确定访问哪个段,然后利用基址寄存器存储的物理地址和虚拟页号结合形成页表项 + +##### 6.7.3 多级页表 + +- TODO 重新理解 + diff --git a/note/操作系统/操作系统控制进程切换.md b/note/操作系统/操作系统控制进程切换.md new file mode 100644 index 0000000..3f9ba21 --- /dev/null +++ b/note/操作系统/操作系统控制进程切换.md @@ -0,0 +1,27 @@ +### 1. 协作方式:等待系统调用或非法操作 + +- 操作系统相信进程会合理运行 +- 运行时间长的进程被假定为会定期释放CPU,以便操作系统可以运行其他进程 +- 大多数进程通过系统调用,将CPU还给操作系统,例如打开文件,创建一个新的进程 +- 如果进程尝试非法操作,也会将CPU还给操作系统,比如除0或者尝试访问无法访问的内存 + + + +### 2. 非协作方式:时钟中断 + +> 时钟中断 +> +> TODO + +- 在操作系统启动时,时钟也会跟随启动 +- 可以理解为每隔一段时间产生一次时钟中断 +- 运行的进程在接收到中断信号时会停止 +- 操作系统预先配置好的中断程序会运行 + + + +### 3. 保存和恢复上下文 + +- 保存当前进程的通用寄存器,程序计数器,以及进程的内核栈指针 +- 恢复或准备即将执行的进程的寄存器 + diff --git a/note/操作系统/系统调用与过程调用.md b/note/操作系统/系统调用与过程调用.md new file mode 100644 index 0000000..e69de29 diff --git a/note/操作系统/超越物理内存.md b/note/操作系统/超越物理内存.md new file mode 100644 index 0000000..2d427c4 --- /dev/null +++ b/note/操作系统/超越物理内存.md @@ -0,0 +1,47 @@ +### 1. 交换空间 + +- 在硬盘上开辟一部分空间用于物理页的移入和移出 +- 操作系统需要记住给定页的硬盘地址 + +### 2. 操作系统替换内存策略 + +> 内存相当于系统虚拟内存页的缓存 +> +> 目标: +> +> 1. 减少缓存未命中情况,使得从磁盘获取页的次数最少 +> 2. 让缓存命中最多,即在内存中找到虚拟页的次数更多 + +- 最优替换策略:替换最远将来才会使用到的内存页,可以打到缓存未命中率最低 +- 简单策略 FIFO +- 随机策略 random +- 利用历史数据 LRU LFU + +> 程序局部性 +> +> - 空间局部性:如果页 P 被访问,可能围绕着他的页 P-1 或 P+1也会被访问 +> +> - 时间局部性:近期被访问过的页可能在不就的将来还会被访问 + +#### 2.1 FIFO + +- 先入先出替换策略 +- 页在进入系统时,简单地放入一个队列。当发生替换时,队列尾部的页(“先入”页)被踢出 +- 优势:实现相当简单 +- 缺点:命中率低 + +#### 2.2 随机策略 + +- 内存满的时候它随机选择一个页进行替换 + +#### 2.3 LRU & LFU + +- Last Recently Used 替换最近最少使用的页面 +- Least Frequently Used 会替换最不经常使用的页 +- 某个程序在过去访问过某个页,则很有可能在不久的将来会再次访问该页 + +#### 2.4 近似 LRU + +- 不会精确的扫描页表中,最近最少的页面 +- 通过硬件存储标志位,利用时钟周期来进行查找 +- 最差的情况是遍历一次内存 \ No newline at end of file diff --git a/note/操作系统/进程调度策略.md b/note/操作系统/进程调度策略.md new file mode 100644 index 0000000..de38362 --- /dev/null +++ b/note/操作系统/进程调度策略.md @@ -0,0 +1,104 @@ +## 一、调度基本思想 + +> 前提假设:操作系统可以看到程序执行的未来 +> +> 1. 知道进程执行时间 +> 2. 了解进程执行过程 + +### 1. 先进先出 + +- First In First Out +- 先到先服务调度方式 First Come First Served +- 缺点:耗时较少的进程被排在耗时较长的进程的后边,造成进程执行效率不高 + +### 2. 最短作业时间优先 + +- Shortest Jon First +- 执行时间短的任务先执行 +- 需要知道任务执行时间且短的任务先到达 + +### 3. 最短完成时间优先 + +- Short Time to Completion FIrst +- 抢占式最短作业时间优先 Preemptive Shortest Job First +- 有新工作加入时,会先确定剩余工作和新工作中谁的时间短 +- 调度执行时间短的那个工作 + +### 4. 轮转 + +- Round Robin +- 在一个时间片内运行一个工作,时间片结束后运行任务队列中的下一个工作,知道任务队列全部结束 +- 时间片长度是时钟周期的整数倍 + +### 5. 小结 + +- 最短作业和最短完成时间可以做到最快调度(进程的平均调度时间短),但是进程的响应时间会变长 +- 轮转可以提高进程的响应时间,但是平均每个进程的调度时间会变长 + + + +## 二、单处理器调度方法 + +### 1. 多级反馈队列 + +- Multi-level Feedback Queue +- 从历史经验预测未来 +- 在进程执行过程中学习其行为,进而预测未来的行为 + +#### 1.1 基本规则 + +- MLFQ有许多队列,每个队列的优先级范围不同 +- 总是先执行优先级较高的队列中的任务 +- 同一个队列中的任务如果优先级相同,采用轮转的方式调度执行 +- MLFQ会动态的调整每个任务的优先级 + - 如果进程使用IO操作,可以理解为是交互行为,需要提高响应时间,从而调高该任务的优先级 + - 如果进程一直都是CPU操作,长时间的占用CPU,则会降低优先级 + +image-20230720153314626 + +> C和D的优先级最低,如果A和B一直调度执行,则C和D一直不会被执行 + +#### 1.2 如何改变优先级 + +- 进程开始执行时放入最高优先级 +- 程序使用完一个优先级队列中的一个时间分片配额(多个时间分片)时,就会降低一个优先级 + - 防止进程在使用99%时间分片时调用IO操作,保留优先级,从而造成进程一直在高优先级执行 + - 其他低优先级得不到有效调度 +- 进程在一个时间分片内主动释放CPU,则优先级保持不变 +- 每隔一定时间将所有任务都加到最高优先级队列 + - 确保CPU交互密集且长时间运行的任务不会被饿死 + - 确保CPU密集变成IO交互的可以快速执行 +- 不同优先级队列的时间片配额不相同 + - 高优先级队列,时间片配额短,确保交互行为快速响应 + - 低优先级队列,时间片分配额长,CPU密集进程也能长时间执行 + + + +### 2 比例份额 + +- 有时也称为公平份额 +- 确保每个工作获得一定比例的CPU时间 +- 采用随机抽选的方式 +- 工作执行时间越长,获得CPU时间片比例越公平 + +## 三、多队列调度 + +### 1.单队列多处理器调度 + +- Single Queue Multiprocessors Scheduling SQMS +- 一个队列存储任务 +- 多个处理器加锁从队列中获取执行任务 +- 主要问题: + - 为了确保多处理器取任务不会冲虚,需要加锁,加锁性能损耗,争抢锁的成本,CPU越多损耗越高 + - 缓存亲和性较差,多个工作轮流在不同的处理器上执行,缓存数据每次都要重新获取 + +### 2.多队列多处理器调度 + +- Multi-Queue Multiprocessors Scheduling MQMS +- 包含多个队列,每个队列可以使用不同的调度算法 +- 解决了加锁同步的问题 +- 主要问题: + - 负载不均衡,造成一个CPU空闲,其他的可能满载 +- 解决负载不均衡 + - 迁移任务到其他CPU + - work stealing 工作窃取机制,空闲的CPU主动监控其他CPU的队列,比自己多的就窃取一部分过来,迁移一部分任务 \ No newline at end of file diff --git a/note/操作系统/锁.md b/note/操作系统/锁.md new file mode 100644 index 0000000..a5522c6 --- /dev/null +++ b/note/操作系统/锁.md @@ -0,0 +1,45 @@ +### 1. 设计要点 + +- 提供互斥,能否阻止多个线程进入临界区 +- 公平性,·锁释放时需要确保每个线程都有同等概率获得锁,不会有线程出现饿死的情况 +- 性能消耗 + +### 2. 锁早期设计方法 + +- 在进临界区前关闭中断 +- 在出临界区后打开中断 +- 优点:设计简单,中断控制线程切换,中断关闭,一个线程会持续运行到结束 +- 缺点: + - 要求每个运行的线程都可以对中断进行开关,风险较高,容易被恶意调用,造成死机 + - 不适用于多处理器,中断只能关闭一个,线程可以在其他处理器上继续运行 + - 中断丢失,丢失部分设备的中断信号 + - 性能低,现代处理器,对中断的开关代码执行较慢 + +### 3. 迭代-自旋锁 + +- 通过 LoadAndStore命令实现 +- 返回旧值,设置新值,原子操作 +- 线程会不断自旋,可能多个线程一直都在自旋 +- 自旋期间,单处理器系统,浪费大量时间片 + +image-20230821200141278 + +### 4. 迭代-比较并交换 + +- CompareAndSwap + + ![image-20230905112738473](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230905112738473.png) + +### 5. 自旋,立刻让出CPU,休眠+队列减轻线程在等待锁时的消耗 + +- 自旋,一直循环判断锁,浪费CPU时间中断 +- 立刻让出CPU,线程没有获取到锁时,主动让出锁,可能会持续获取不到锁造成饥饿现象 +- 休眠和队列组合使用,可以唤醒指定线程 + +### 6. 两阶段锁 + +- 先自旋 +- 后休眠 + + + diff --git a/note/计算机网络/ARP.md b/note/计算机网络/ARP.md new file mode 100644 index 0000000..cc4a64b --- /dev/null +++ b/note/计算机网络/ARP.md @@ -0,0 +1,37 @@ +### 1、ARP概要 + +- Address Resolution Protocol +- 根据IP地址寻找MAC地址 +- 底层数据链路层通信需要知道对方的MAC地址 +- 以目标IP地址为线索,用来定位下一个应该接受数据分包的网络设备的MAC地址 +- 如果目标主机不在同一个链路上,可以通过ARP查找下一跳路由器的MAC地址 +- ARP协议只适用于IPv4,IPv6网络需要使用ICMPv6协议 + +### 2、一个链路内ARP工作机制 + +- 主机A 172.20.1.1,主机B 172.20.1.2 +- 两个主机互相不知道MAC地址 +- 主机A向主机B发送数据 +- 主机A先发送一个ARP广播,包含目的主机的IP地址,广播包可以被同一个链路上的所有主机接受 +- 主机B查看目的地址和自己的IP一致,返回包含自己MAC地址的ARP响应给主机A +- 主机A和主机B分别缓存对方的MAC地址 + +![image-20230709162452866](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709162452866.png) + +### 3、不通链路ARP + +- IP层的源IP和目的IP不会变 +- 数据链路层的源MAC地址和目的MAC地址经过一跳后就会变化 + +![image-20230709163129978](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709163129978.png) + +### 3、ARP包格式 + +![image-20230709162900405](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709162900405.png) + +### 3、RARP + +- Reverse Address Resolution Protocol +- ARP反向解析,从MAC地址定位IP地址 + +![image-20230709163531478](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709163531478.png) \ No newline at end of file diff --git a/note/计算机网络/DHCP.md b/note/计算机网络/DHCP.md new file mode 100644 index 0000000..a44e6bf --- /dev/null +++ b/note/计算机网络/DHCP.md @@ -0,0 +1,40 @@ +### 1、DHCP概要 + +- Dynamic Host Configuration Protocol +- 设备接入到网络,就可以自动获取用于TCP/IP通信所必须得设置 +- DHCP可以用在IPv4和IPv6网络中 + +### 2、工作机制与流程 + +- 架设DHCP服务器 + - 配置 IP地址 + - 配置 子网掩码 + - 配置 路由控制信息 + - 配置 DNS服务器地址 +- 设备接入 + - 发送DHCP发现包:目的地址为广播地址 255.255.255.255,源地址为 0.0.0.0 + - 接受DHCP提供包 + - 发送DHCP请求包 + - 接受DHCP提供包 + +![image-20230709175004443](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709175004443.png) + +### 3、DHCP 分配方式 + +- IP地址池中自动选出一个进行分配 +- 根据MAC地址分配一个固定地址 +- 两种方法可以并用 + +### 4、同一链路内多台DHCP服务器,冗余设计 + +- 容易造成IP地址冲突 +- DHCP服务器在分配前,需要发送ICMP回送请求包,确认没有返回应答 +- 对从DHCP服务器获取的IP地址发送ARP请求包,确认没有返回应答 + +### 5、DHCP中继代理 + +- 减少多个网段的DHCP维护和管理成本,不需要在每个网段都架设一个DHCP服务器 +- 对不同网段的IP地址分配,由一个DHCP服务器进行管理和运维 +- 需要再每个网段设置一个DHCP中继代理 + +![image-20230709175620808](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709175620808.png) \ No newline at end of file diff --git a/note/计算机网络/DNS.md b/note/计算机网络/DNS.md new file mode 100644 index 0000000..f161a4f --- /dev/null +++ b/note/计算机网络/DNS.md @@ -0,0 +1,18 @@ +### 1、DNS协议 + +- Domain Name System +- 域名系统 +- 将域名转换为IP地址 +- 适用于IPv4和IPv6 + +### 2、域名服务器 + +- 存储自己管理的域名的下一层域名服务器的IP地址 +- 所有域名服务器都必须注册根域名服务器的IP地址,DNS检索时,需要从根域名服务器开始按顺序进行 + +### 3、域名查询流程 + +- 递归查询 +- 迭代查询 + +![img](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MjE0NjEzNg==,size_16,color_FFFFFF,t_70.png) \ No newline at end of file diff --git a/note/计算机网络/ICMP.md b/note/计算机网络/ICMP.md new file mode 100644 index 0000000..55a2305 --- /dev/null +++ b/note/计算机网络/ICMP.md @@ -0,0 +1,71 @@ +### 1、ICMP主要功能 + +- 错误通知 +- 诊断查询 + +### 2、ICMP报文格式 + +![image-20230709165939976](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709165939976.png) + +### 3、ICMP通知主机不可达流程 + +- 包通过IP协议原路返回到发送方 + +![image-20230709165145074](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709165145074.png) + +### 4、ICMP报文类型 + +![image-20230709165228961](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709165228961.png) + +### 5、目标不可达消息 + +- 类型 3,携带不可达具体原因 +- 不可达原因与具体错误号 +- 4代表MTU路径发现,分片大小超限 + +![image-20230709165756565](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709165756565.png) + +### 6、重定向消息 + +- 类型 5 +- 路由器发现发送端主机使用了次优的路径发送数据,会返回一个重定向消息 +- 消息中包含最合适的路由信息和源数据 + +![image-20230709170456622](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709170456622.png) + +### 7、超时消息 + +- 类型 11 +- IP包中TTL字段值每经过一次路由就会减一,直到减到0时包就会被丢弃,此时IP路由器将会发送一个ICMP的超时消息给发送端主机 + +![image-20230709170710061](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709170710061.png) + +### 8、回送消息 + +- 类型 0或8 +- 用于进行通信的主机或路由器之间,判断所发送的数据包是否已经成功到达对端 +- 可以相对端主机发送会送请求的消息(ICMP Echo Request Message 类型 8),也可以接受对端主机发回来的回送应答消息(ICMP Echo Reply Mesage 类型 0) + +- ping 命令就是利用ICMP实现的 TODO + +![image-20230709171124972](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709171124972.png) + +### 9、ICMPv6 + +- 没有ICMPv6,IPv6网络就无法通信 +- 整合了ARP和ICMP重定向以及ICMP路由器选择消息等功能于一体,甚至还提供自动设置IP地址的功能 +- 一类是错误消息,另一类是信息消息 + - 错误消息 0-127 + - 信息消息 128-255 + +![image-20230709171625663](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709171625663.png) + +![image-20230709171636369](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709171636369.png) + +### 10、ICMPv6 探索消息 + +- 类型133-127 +- 用于查询IPv6的地址与MAC地址对应关系,并由邻居宣告消息得知MAC地址 +- 利用IPv6多播地址实现传输 + +![image-20230709172007397](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709172007397.png) \ No newline at end of file diff --git a/note/计算机网络/IPv4.md b/note/计算机网络/IPv4.md new file mode 100644 index 0000000..c78720d --- /dev/null +++ b/note/计算机网络/IPv4.md @@ -0,0 +1,113 @@ +### 1、IPv4数据报格式 + +![image-20230709145705180](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709145705180.png) + +### 2、version + +- 4个比特构成 +- 表示IP首部的版本号 + +![image-20230709145940327](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709145940327.png) + +### 3、首部长度 IHL + +- Internet Header Length +- 4比特构成 +- 表示IP首部的大小 +- 单位为4字节(32比特) +- 没有可选项,没有数据的IP数据报,首部长度字段为5,也就是首部真实数据长度为20字节 + +### 4、区分服务 TOS + +- Type Of Service +- 8比特构成 +- 表明服务质量 +- 通常有应用指定 +- 有人提出将TOS字段划分为 DSCP和ECN两个字段 + - DSCP Differential Services Codepoint 差分服务代码点 + - Explicit Congestion Notification 显式拥塞通告 + +![image-20230709150354408](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709150354408.png) + +### 5、总长度 Total Length + +- 首部+数据部分总长度 +- 长为16比特 +- IP包的最大长度为65535字节,2^16 + +### 6、标识 ID:Identification + +- 16比特构成 +- 用于分片重组 +- 同意分片的标识值相同 +- 每发送一个IP包,数值自增 +- ID相同,如果目标地址源地址或协议不同的话,也是不同的分片 + +### 7、标志 Flags + +- 3比特构成 +- 表示包被分片的相关信息 + +![image-20230709151237049](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709151237049.png) + +### 8、片便宜 FO Frament Offset + +- 13比特构成 +- 用来标识被分片的每个分段相对于原始数据的位置 +- 第一个分片该字段为0 +- 对多可以表示8192个相对位置 +- 单位为8字节 +- 最大可表示原始数据 8 * 8192 = 65536字节的位置 + +### 9、生存时间 TTL Time To Life + +- 8比特构成 +- 中转多少个路由器的意思 +- 每经过一个路由器,TTL自动减少1 +- 对多中转次数不会超过256次,可以避免IP包在网络内无限传递 +- 变成0时,该包被丢弃 + +### 10、协议编号 Protocol + +- 8比特构成 +- 表示包内数据使用的是哪一个协议 + +![image-20230709151906055](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709151906055.png) + +![image-20230709151917758](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709151917758.png) + +### 11、首部校验和 Header Checksum + +- 16比特构成 +- 只用于校验首部,不校验数据部分 +- 首部校验和计算方式 TODO + +### 12、可选项 Options + +- 长度可变 +- 包含以下信息 + - 安全级别 + - 源路径 + - 路径记录 + - 时间戳 + +### 13、填充 Padding + +- 在有可选项的情况下,首部长度可能不是32比特的整数倍 +- 向字段填充0,调整为32比特的整数倍 + + + +### 14、网络字节序 + +- 大端序:4个字节的32bit用下面的次序传输 + - 首先是0-7bit + - 其次8-15bit + - 然后16-23bit + - 最后24-31bit +- TCP/IP首部中所有二进制整数在网络中传输都要求大端序,因此又称作网络字节序 +- 以其他形式存储二进制整数的机器,必须在传输数据之前把首部转换成网络字节序 + +### 15、特殊IP地址 + +![image-20230716172604785](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230716172604785.png) \ No newline at end of file diff --git a/note/计算机网络/IPv6.md b/note/计算机网络/IPv6.md new file mode 100644 index 0000000..8cbaba1 --- /dev/null +++ b/note/计算机网络/IPv6.md @@ -0,0 +1,9 @@ +### 1、IPv6数据报格式 + +- 省略了首部校验和字段,路由器不在计算校验和,提高了转发效率 + +![image-20230709154838006](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709154838006.png) + +### 2、特殊IPv6地址 + +![image-20230716172648406](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230716172648406.png) \ No newline at end of file diff --git a/note/计算机网络/IP协议相关.md b/note/计算机网络/IP协议相关.md new file mode 100644 index 0000000..e01882c --- /dev/null +++ b/note/计算机网络/IP协议相关.md @@ -0,0 +1,64 @@ +### 1、概念 + +- Internet Protocol 网际协议 +- 主要负责将数据包发送给最终的目标计算机 +- 实现终端节点之间的通信,也叫`点对点通信` + +### 2、跳 Hop + +- 网络中的一个区间 +- 利用数据链路层以下分层的功能传输数据帧的一个区间 +- 以太网等数据链路中使用MAC地址进行传输数据帧,此时的一跳是指从源MAC地址到目的MAC地址之间传输帧的区间,也就是说它是主机或路由器网卡不经其他路由器而能直接到达的相邻主机或路由器网卡之间的一个区间 +- 在一跳的区间内,电缆可以通过网桥或交换集线器项链,不会通过路由器或网关相连 +- 路由器或主机在转发IP数据包时只指定下一个路由器或主机,不会将到最终目标地址为止的所有通路全部指定出来 +- 每个区间在转发IP数据包时会分别指定下一跳的操作,直至包达到最终的目的地址 + +![image-20230708153314081](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230708153314081.png) + +### 3、IP协议面向无连接 + +- 原因 + - 简化 + - 提速 +- 提供尽力服务,尽最大努力传递包,不做最终对方收到与否的验证 + +### 4、IP地址分类 + +- A:第一位是0,0.0.0.0~127.0.0.0,24位主机号,可容纳地址上限为16777214个 +- B:前两位是10,128.0.0.1~191.255.0.0,16位主机号,可容纳地址上限为65534个 +- C:前三位是110,192.168.0.0~239.255.255.0,8位主机号,可容纳地址上限为254个 +- D:前四位是1110,224.0.0.0~239.255.255.255,没有主机标识,常被用于多播 + +### 5、广播 + +- 主机号部分全部设置为1,就是广播地址,172.16.0.0/16的广播地址172.16.255.255 +- 本地广播:向同网段内的广播地址发送数据 +- 直接广播:向不通网段内的广播地址发送广播数据 + +![image-20230708180333693](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230708180333693.png) + +### 6、IP多播 + +- 将包发送给特定组内的所有主机 + +- 使用D类地址,从首位开始到第4位是1110,剩下的28位可以成为多播的组播号 + +- 相比广播之下,多播可以穿透路由器 + +- 地址范围 224.0.0.0~239.255.255.255 + +![image-20230708181537396](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230708181537396.png) + +![image-20230708195723407](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230708195723407.png) + +### 7、显式拥塞通知 + +- Explicit Congestion Notification +- IP层新增的一种显式拥塞通知的机制 +- 将IP首部的TOS字段置换为ENC字段,并在TCP首部的保留位中追加CRW标志和ECE标志 + - CWR:Congestion Window Reduced + - ECE:ECN-Echo +- 在发送包的IP首部中记录路由器是否遇到了拥塞,并在返回包的TCP首部中通知是否发生过拥塞 +- 拥塞的检测发生在网络层,拥塞的通知在传输层 + +![image-20230709202410792](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709202410792.png) diff --git a/note/计算机网络/IP隧道.md b/note/计算机网络/IP隧道.md new file mode 100644 index 0000000..304dd7b --- /dev/null +++ b/note/计算机网络/IP隧道.md @@ -0,0 +1,15 @@ +### 1、IP隧道概要 + +- 网络层首部后面继续追加网络层首部的通信方法 + +### 2、IPv4隧道连接两个IPv6网络 + +- 两个IPv6之间是IPv4网络,导致两个IPv6之间无法直接利用IPv6通讯,需要IPv4网络将IPv6网络发过来的数据封装成一个数据,然后追加IPv4首部后转发给IPv6网络 + +![image-20230709200804298](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709200804298.png) + +![image-20230709200915653](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709200915653.png) + +### 3、IP隧道传递多播信息 + +![image-20230709201229920](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709201229920.png) \ No newline at end of file diff --git a/note/计算机网络/NAT.md b/note/计算机网络/NAT.md new file mode 100644 index 0000000..479b79a --- /dev/null +++ b/note/计算机网络/NAT.md @@ -0,0 +1,35 @@ +### 1、NAT概要 + +- Network Address Translator +- 为了解决IPv4地址枯竭的问题 +- 用于在本地网络中使用私有地址,在连接互联网时转而使用全局IP地址的技术 +- 通常提到的NAT都是NAPT(Network Address Ports Translator)转换TCP和UDP的端口号 +- 可以实现一个全局地址与多个主机通信 +- 也叫IP伪装或Multi NAT + +### 2、工作机制 + +- NAT(NAPT)路由器内部,有一张自动生成的用来转换地址的表 + +- 发送请求时:私有地址转换为全局的IP地址,并将端口加入到转换表 + +- 接收请求是:根据端口选择转发的私有地址,根据全局IP地址转换为私有地址 + + > 在使用TCP或UDP通信时,需要目标地址,源地址,目标端口,源端口和协议类型一致,才会被认为是同一个连接,此时使用的是NAPT + +![image-20230709180623296](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709180623296.png) + +### 3、NAT-PT(NAPT-PT) + +- 适用于IPv6网络的NAT技术 +- 将IPv6首部转换为IPv4首部 +- 只有IPv6地址的主机能够与IPv4地址的主机进行通信 + +![image-20230709181223860](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709181223860.png) + +### 4、NAT潜在问题 + +- 无法从NAT的外部向内部服务器建立连接 +- 转换标的生成与转换操作都需要一定开销 +- NAT异常重启,所有TCP连接都将被重置 +- NAT灾备,TCP连接还是会断开 \ No newline at end of file diff --git a/note/计算机网络/PPP协议数据帧格式.md b/note/计算机网络/PPP协议数据帧格式.md new file mode 100644 index 0000000..dc88e83 --- /dev/null +++ b/note/计算机网络/PPP协议数据帧格式.md @@ -0,0 +1,26 @@ +### 一、PPP协议 + +- point to point 点对点协议 + +### 二、协议内容 + +- LCP协议:Link Control Protocol,不依赖上层协议,负责连接的建立与断开、设置最大的接收单元MRU,设置验证协议以及设置是否进行通信质量的监控 +- NCP协议:Network Control Protocol,依赖上层协议,如果上层协议为IP协议,则此时的NCP也叫IPCP,负责IP地址的设置,以及是否进行TCP/IP首部压缩的协商 + +### 三、PPP用户验证 + +- PAP协议:Password Authentication Protocol,建立连接时通过两次握手进行用户名和密码的验证,密码明文传输 +- CHAP协议:Chanllege HandShake Authentication Protocol,使用一次性密码,建立连接后定期交换密码,有效防止窃听 + + + +### 四、数据帧格式 + +![image-20230707174300044](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707174300044.png) + +### 五、PPPoE + +- PPP over Ethernet +- 在以太网的基础上提供PPP功能 + +![image-20230707175353594](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707175353594.png) \ No newline at end of file diff --git a/note/计算机网络/互联网分层.md b/note/计算机网络/互联网分层.md new file mode 100644 index 0000000..3db168f --- /dev/null +++ b/note/计算机网络/互联网分层.md @@ -0,0 +1,19 @@ +### 1、 OSI七层协议 + + + + + +### 2、TCP/IP协议族四个层次 + +- 应用层:处理特定的应用程序细节 + +- 运输层:为两台主机上的应用程序提供端到端的通信 + +- 网络层:处理分组在网络中的互动,例如分组的选路 + +- 链路层:也叫数据链路层,包括操作系统中的设备驱动程序和计算机中对应的网卡 + + > 局域网内,两台主机运行FTP程序,涉及协议 + +![image-20230711220351828](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230711220351828.png) \ No newline at end of file diff --git a/note/计算机网络/以太网数据帧格式.md b/note/计算机网络/以太网数据帧格式.md new file mode 100644 index 0000000..b3664c9 --- /dev/null +++ b/note/计算机网络/以太网数据帧格式.md @@ -0,0 +1,31 @@ +### 一、前导码 + +- 以太网帧前端的一部分 +- 有0和1数字交替组成,表示一个以太网帧的开始 +- 前导码最后两个比特位为`11`,也叫SFD(Start Frame Delimiter),在这之后的数据就是以太网帧的本体 +- 前导码与SFD加起来占8个字节(64比特) + +![image-20230707170933511](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707170933511.png) + +### 二、以太网帧本体 + +- 以太网的首部 +- 占14个字节,分别是6个字节的源mac地址和6个字节的目的mac地址以及两个字节的上层协议类型 + +- 以太网帧最大携带数据范围是**46-1500**字节 +- 帧尾部有一个占用4个字节的`FCS(Frame Check Sequence)` +- IEEE802.3帧格式中协议类型被放在`SNAP`字段中 + +![image-20230707171542895](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707171542895.png) + +### 三、以太网帧类型 + +- 和数据一起传送,占用2个字节 +- 包含用来标识以太网上一层使用协议的编号 + +![image-20230707171715988](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707171715988.png) + +### 四、带有VLAN的以太网数据帧 + +![image-20230707172526881](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230707172526881.png) + diff --git a/note/计算机网络/组播.md b/note/计算机网络/组播.md new file mode 100644 index 0000000..800d6c1 --- /dev/null +++ b/note/计算机网络/组播.md @@ -0,0 +1,5 @@ +### IPv4组播地址 + +![image-20230716173659648](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230716173659648.png) + +![image-20230716173716614](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230716173716614.png) \ No newline at end of file diff --git a/note/计算机网络/路径MTU发现.md b/note/计算机网络/路径MTU发现.md new file mode 100644 index 0000000..1534ffd --- /dev/null +++ b/note/计算机网络/路径MTU发现.md @@ -0,0 +1,42 @@ +### 1、MTU + +![image-20230709093828838](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709093828838.png) + +### 2、IP分片与组装 + +- 超过最大MTU的数据,路由器负责切分,最终目的主机负责组装 +- 路由器不负责组装原因 + - 每个IP数据包,不一定始终经过同一个路由器进行转发 + - 中途数据包丢失,组装数据无意义 + - 组装增加路由器负担,降低转发效率 + +![](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709093951581.png) + +### 3、路径MTU发现 + +- Path MTU Discovery +- 从发送端主机到接收端主机之间不需要分片时最大MTU的大小,即路径中存在的所有数据链路中最小的MTU +- 从发送主机按照路径MTU的大小将数据报分片后进行发送 +- 可以避免在传输中途的路由器上进行分片处理,也可以在TCP中发送更大的包 + +### 4、MTU发现过程 + +(1)发送端主机在发送IP数据报时将其首部的分片禁止标志设置为1,根据这个标志位,途中的路由器即使遇到需要分片才能处理的大包,也不会去分片,而是丢包 + +(2)丢包的路由器返回一个ICMP不可达消息报文将数据链路上的MTU值发给主机 + +(3)发送主机根据ICMP中的MTU值进行分片处理,如此反复知道数据报备发送到目标主机为止没有在收到任何ICMP,就认为最后一个ICMP中的MTU是合适的 + +(4)MTU值较多时,可以缓存10分钟。过了10分钟后就要重新进行MTU发现 + +### 5、UDP MTU发现 + +- 无重发机制,分片数据IP层处理 + +![image-20230709094530096](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709094530096-20230709145336162.png) + +### 6、TCP MTU发现 + +- 有重发机制,分片数据在TCP协议处理 + +![image-20230709145507885](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709145507885.png) diff --git a/note/计算机网络/路由控制.md b/note/计算机网络/路由控制.md new file mode 100644 index 0000000..672db5c --- /dev/null +++ b/note/计算机网络/路由控制.md @@ -0,0 +1,29 @@ +### 1、分类 + +- 静态路由:手动设置,修改路由表 +- 动态路由:路由器之间交互,自动存储并刷新 + +![image-20230711195341776](http://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230711195341776.png) + +### 2、路由表 + +- 记录着网络地址与下一步应该发送至路由器的地址 +- 发送IP包时,首先要确定IP包首部中的目标地址,再从路由表中找到与该地址具有相同网络地址的记录,进而转发至下一个路由器 +- 如果路由表中存在多条相同的网络记录,就选择一条最吻合的记录进行转发 + - 相同位数最多的 + - 172.20.100.52与127.20/16和172.20.100/24,都匹配,按照位数最多应该选择172.20.100/24进行转发 + +### 3、路由分类 + +- 默认路由:任何地址都能与之匹配的记录,一般是0.0.0.0/0 或default +- 主机路由:IP地址/32, 子网掩码32位,整个IP地址的所有位都参与路由 +- 环回地址:127.0.0.1 同一台计算机上的程序之间进行网络通信时所使用的一个默认地址 + +### 4、路由聚合 + +- 减少路由表条目,增加查找转发效率 +- 即使有多个子网掩码,对外呈现出的也是同一个网络地址 +- 将已知的聚合路由信息,告知周围其他的路由器,控制路由信息 +- 192.168.2.0/24与192.168.3.0/24可以聚合为192.168.2.0/23 + +![image-20230709092407809](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230709092407809.png) \ No newline at end of file diff --git a/note/计算机网络/路由相关.md b/note/计算机网络/路由相关.md new file mode 100644 index 0000000..5a2a488 --- /dev/null +++ b/note/计算机网络/路由相关.md @@ -0,0 +1,70 @@ +### 1、路由概念 + + + +### 2、路由算法 + +- 距离向量算法:根据距离代价和方向决定目标网络或目标主机的一种方法 +- 链路状态算法:路由器在了解网络整体状态的基础上生成路由表,每个路由器必须保持同样的信息才能进行正确的路由选择 + +### 3、主要路由协议 + +![image-20230712222025107](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230712222025107.png) + +### 4、 RIP协议 + +- Routing Information Protocol +- 距离向量型的一种路由协议 +- 每隔30秒向全网广播自己的路由信息 +- 已知的路由信息经过一跳之后继续广播 +- 如果没有收到路由控制信息,连接就会被断开 +- RIP规定等待5次,如果等了6次仍未收到路由信息,就会关闭连接 + +![image-20230712222502063](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230712222502063.png) + +![image-20230712222615123](https://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230712222615123.png) + +#### 4.1、RIP路由器无限计数 + +- 路由器A链接网络A和路由器B +- 路由器A发送广播消息告知路由器B到通过路由器A可以到A网络 +- A网络发生故障,路由器A断开与A网络的连接 +- 路由器B广播自己可以通过路由器A到A网络 +- 路由器A接收到广播通知,认为自己可以通过路由器B到A网络 +- 然后此时路由器A或B到网络A都是是不可达的 + +#### 4.2、解决无限计数 + +- 从传播时间进行控制,限制传播最长距离不超过16 +- 水平分割,规定路由器不在把收到的路由消息原路返回给发送端 +- 毒性逆转,网络当中发生链路被断开时,将无法通信的消息传播出去,即发送一个距离为16的消息 +- 触发更新,当路由信息发生变化时,不等30s而是立刻发送出去 + +#### 4.3、RIP2 + + + +### 5、OSPF TODO + +- Open Short Path First +- 采用链路状态进行路由的协议 +- 可以给每条链路赋予一个权重,并始终选择一个权重小的路径作为最终路由 +- 网络复杂时,不需要每个路由器之间都互相交换路由信息,而是选择一个中心路由器进行路由信息交换 + +![image-20230713104925887](http://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230713104925887.png) + +#### 5.1、路由信息包的类型 + +![image-20230713105429003](http://blog-heysq-1255479807.cos.ap-beijing.myqcloud.com/blog/note/image-20230713105429003.png) + +- 通过发送问候(hello)包确定是否连接 +- 每个路由器为了同步路由控制信息,利用数据库描述(Database Description)包相互发送路由摘要信息和版本信息 +- 如果版本较老,则首先发出一个链路状态请求(Link State Request)包请求路由控制信息,然后由链路状态更新(Link State Update)包接收路由状态信息 +- 最后再通过链路状态确认(Link State ACK Packet)包通知大家本地已经接收到路由控制信息 + +### 6、BGP协议 + +- Border Gateway Protocol +- 连接不同的自治系统,连接不同的组织机构 +- 主要用于ISP之间互相连接 +- 路径向量协议