跳到主要内容

PotPlayer 快捷键

· 阅读需 2 分钟
素明诚
Full stack development

常用快捷键

功能快捷键
播放/暂停空格键
停止X
下一帧D
上一帧E
快进 10 秒
快退 10 秒
快进 30 秒Ctrl + →
快退 30 秒Ctrl + ←
快进 1 分钟Alt + →
快退 1 分钟Alt + ←
增加播放速度C
减少播放速度X
恢复正常速度Z
增加音量
减少音量
静音M
显示/隐藏字幕Ctrl + Alt + H
切换字幕Alt + L
下一字幕Ctrl + L
上一字幕Ctrl + Shift + L
打开文件Ctrl + O
打开网址Ctrl + U
打开文件夹Ctrl + F
截图Ctrl + E
全屏Enter 或 Alt + Enter
退出全屏Esc
画面尺寸 - 原始1
画面尺寸 - 50%2
画面尺寸 - 100%3
画面尺寸 - 150%4
画面尺寸 - 200%5
比例 - 16:9F5
比例 - 4:3F6
比例 - 1.85:1F7
比例 - 2.35:1F8
旋转 - 顺时针 90 度Ctrl + Shift + 3
旋转 - 逆时针 90 度Ctrl + Shift + 1
播放列表 - 打开F6
播放列表 - 清空Ctrl + Shift + L
播放列表 - 保存Ctrl + S
跳转 - 到上一个播放点Alt + P
跳转 - 到下一个播放点Alt + N
循环播放R
随机播放S

要详细查看所有快捷键并进行自定义,可以在 PotPlayer 中通过以下路径进行查看和修改

  1. 打开 PotPlayer。
  2. 点击右上角的菜单按钮(或按 F5 打开设置)。
  3. 在设置菜单中,选择 快捷键 选项。

下载地址

https://potplayer.daum.net/

如何编写 Go 语言的单元测试

· 阅读需 2 分钟
素明诚
Full stack development

testing 包

testing 包是 Go 语言的标准库组成部分,专门用于支持自动化测试。该包提供了丰富的功能,允许开发者定义测试用例、记录测试结果,并利用一系列工具函数控制测试逻辑和报告测试状态。这包括处理失败、日志记录和条件性测试中断等功能。

示例代码

以一个基本的加法函数测试为例。首先,假设你有一个加法函数定义在 calculator.go 文件中

package calculator

// Add 返回两个整数的和
func Add(a, b int) int {
return a + b
}

对应的测试文件 calculator_test.go 包含如下代码

package calculator

import "testing"

// TestAdd 测试 Add 函数
func TestAdd(t *testing.T) {
result := Add(1, 2)
expected := 3
if result != expected {
t.Errorf("Add(1, 2) = %d; expected %d", result, expected)
}
}

编写测试的规则

测试文件名应以 _test.go 结尾,存放在与被测试源文件相同的包中。每个测试函数必须以 Test 开头,并接受一个 *testing.T 类型的参数。这些函数可以使用 testing 包提供的方法进行错误报告和日志记录

  • t.Errorf()t.Error(): 当测试需要报告错误时使用,Errorf 允许按格式输出。
  • t.Fatalf()t.Fatal(): 遇到无法继续的错误时调用,会立即终止测试。
  • t.Log()t.Logf(): 用于记录测试信息,这些信息在使用 -v 参数运行测试时显示。

运行测试

打开命令行或终端,在包含测试文件的目录下运行

go test

使用 -v 参数可以查看每个测试函数的详细输出信息。若只想运行特定的测试,可以使用 -run 参数,如

go test -run TestAdd

如何编写 Go 语言的性能测试

· 阅读需 1 分钟
素明诚
Full stack development

testing 包

testing 包提供了一个专门用于性能测试的工具,称为基准测试(benchmarks)。基准测试函数以 Benchmark 开头,并接受一个 *testing.B 类型的参数。这个参数提供了控制测试运行的方法和属性,例如 b.N,它代表测试循环的次数。

示例代码

package main

import (
"encoding/json"
"github.com/bytedance/sonic"
)

type Student struct {
Name string
Age int
Gender string
}

type Class struct {
Id string
Students []Student
}

var (
s = Student{"张三", 18, "女"}
c = Class{
Id: "1年2班",
Students: []Student{s, s, s},
}
)
// Benchmark 开头
func
Json(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes, _ := json.Marshal(c)
var c2 Class
json.Unmarshal(bytes, &c2)
}
}

func BenchmarkSonic(b *testing.B) {
for i := 0; i < b.N; i++ {
bytes, _ := sonic.Marshal(c)
var c2 Class
sonic.Unmarshal(bytes, &c2)
}
}

IDE 执行

44c617759eee959e35e5c76c15fb67df### 命令行执行

打开命令行或终端,在包含性能测试文件的目录下运行:

go test -bench=.

使用 -bench 参数可以指定要运行的基准测试。-bench=. 表示运行所有基准测试。

监控单机Docker容器使用cAdvisorPrometheus和Grafana

· 阅读需 1 分钟
素明诚
Full stack development

docker-compose.yml

services:
prometheus:
image: prom/prometheus:v2.53.0
container_name: prometheus
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
ports:
- "59090:9090"
networks:
- monitor-net
restart: unless-stopped
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.path=/prometheus'
- '--web.enable-lifecycle'
depends_on:
- cadvisor
logging:
driver: json-file
options:
max-size: "200m"
max-file: "10"
grafana:
image: grafana/grafana:10.4.4
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana.ini:/etc/grafana/grafana.ini # Grafana 配置文件
environment:
GF_SECURITY_ADMIN_PASSWORD: admin123456 # 管理员密码
GF_USERS_ALLOW_SIGN_UP: "false" # 禁止用户自行注册
ports:
- "53000:3000"
networks:
- monitor-net
restart: unless-stopped
depends_on:
- prometheus
logging:
driver: json-file
options:
max-size: "200m"
max-file: "10"
cadvisor:
image: gcr.io/cadvisor/cadvisor
container_name: cadvisor
volumes:
- /:/rootfs:ro
- /var/run:/var/run:rw
- /sys:/sys:ro
- /var/lib/docker/:/var/lib/docker:ro
ports:
- "58080:8080"
networks:
- monitor-net
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "100m"
max-file: "5"
volumes:
prometheus_data:
driver: local
grafana_data:
driver: local
networks:
monitor-net:
driver: bridge

prometheus.yml

scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090'] # 使用 Docker Compose 服务名和内部端口
- job_name: 'cadvisor'
scrape_interval: 5s
static_configs:
- targets: ['cadvisor:8080'] # 使用 Docker Compose 服务名和内部端口

grafana 配置

添加数据源:prometheus

设置 prometheus 的 Connection 为 http://prometheus:9090 保存

面板使用

a99b4cb38396d462a5825c43f5c55e8d

Linux 命令行实用的小技巧

· 阅读需 3 分钟
素明诚
Full stack development

重复上一个命令

  • 输入 !! 并按回车,快速执行上一条命令。

重用上一个命令的最后一个参数

  • 输入 !$ 可以在新命令中使用上一个命令的最后一个参数。

快速创建或删除多个文件夹

  • 使用 mkdir folder1 folder2 folder3 同时创建多个文件夹。
  • 使用 rm -r folder1 folder2 folder3 一次性删除多个文件夹及其内容。

搜索命令历史

  • ctrl+r 开始输入搜索历史命令,继续按 ctrl+r 可查看更多匹配的历史命令。

创建命令别名

  • 对于经常使用的长命令,可以使用 alias 创建一个简短的别名,例如 alias ll='ls -la'
  • 取消使用 unalias ll,重载配置 source ~/.bashrc

暂停和继续进程

  • 使用 ctrl+z 暂停当前进程,使用 fg 将其恢复到前台继续运行。

定时执行命令并显示结果

  • 使用 watch 命令定时执行其他命令,并实时显示结果,例如 watch -n 10 df -h 每 10 秒检查一次磁盘使用情况。
  • 60 秒后自动终止 watch 命令timeout 60 watch -n 10 df -h

快速切换回之前的目录

  • 使用 cd - 快速返回到之前的目录。

批量重命名或移动文件

  • 使用 rename 's/.txt/.bak/' *.txt 将所有 .txt 文件扩展名改为 .bak

倒序输出文件内容

  • 使用 tac 命令可以倒序显示文件内容,适用于查看日志文件。

搜索内容并高亮显示

  • 使用 grep --color 'text' filename 可以高亮显示匹配的文本。

管理多个会话

  • 使用 screentmux 可以在一个窗口中开启多个会话,适合同时运行多个命令行程序。

在后台运行程序并忽略挂起信号

  • 使用 nohup command & 可以让程序在退出终端后继续运行。

快速删除大量文件

  • 使用 find . -type f -name 'pattern' -delete 可以更高效地删除大量文件。

设置环境变量的默认值

  • 使用 ${VAR:-default} 在变量未设置或为空时提供一个默认值。

比较两个文件的不同

  • 使用 diff 快速识别两个文件之间的差异。
  • diff -u version1.txt version2.txt。这将输出两个文件内容的差异,并使用 -u 选项格式显示上下文,帮助你理解每处差异的前后关系。

监控文件变化

  • 使用 tail -f filename 实时监控文件末尾部分的变化,非常适合实时查看正在写入的日志文件。

Go fmt 格式化输出

· 阅读需 2 分钟
素明诚
Full stack development

基本格式化占位符

占位符描述
%v默认格式输出变量的值。结构体时输出字段值。
%+v输出结构体时包括字段名和字段值。
%#v输出值的 Go 语法表示,即源代码片段。
%T输出值的类型。
%d, %b, %o, %x, %X分别表示整数的十进制、二进制、八进制、小写十六进制和大写十六进制格式。
%f, %e, %E, %g, %G分别表示浮点数的默认格式、小写和大写的科学计数法、最短表示法(自动选择 %e 或 %f)。
%s输出字符串。
%q输出双引号围绕的由 Go 语法安全转义的字符串。
%p输出指针的地址(十六进制)。
%c输出字符(Unicode 码点)。

格式控制选项

选项描述
%9d宽度为 9 的十进制数,右对齐。
%.2f浮点数,小数点后只保留两位。
%9.2f, %-9.2f宽度为 9,小数点后保留两位的浮点数,分别右对齐和左对齐。
%09d宽度为 9 的十进制数,空白处用零填充。
%+d输出整型数时,显示符号(正数显示 +,负数显示 -)。
% 6d宽度至少为 6,不足部分以空格填充。

注意事项

类型匹配:确保占位符与变量类型匹配,防止类型错误。

精度与宽度:合理设置精度和宽度,注意过小的宽度不会截断输出,而过小的精度可能导致数据丢失。

性能考虑:频繁使用字符串格式化会影响程序性能,尤其是在数据处理和循环中。

Go 语言中 defer 语句

· 阅读需 1 分钟
素明诚
Full stack development

Go 语言中,defer 语句遵循后进先出(LIFO)的原则执行。这样做主要是为了简化资源(如文件、锁)的管理,使得资源的获取和释放顺序保持一致,易于维护。

package main

import (
"fmt"
)

func main() {
defer fmt.Println("第一个 defer") // 最后执行
defer fmt.Println("第二个 defer") // 第二个执行
defer fmt.Println("第三个 defer") // 最先执行

fmt.Println("主体")
}

本质原因就是因为 defer 语句使用了栈来存储

defer 与 匿名函数

使用 defer 匿名函数时,核心问题在于变量捕获特性

package main

import "fmt"

func main() {
a := 1
defer func() {
fmt.Println(a) // 输出 2
}()
a = 2
fmt.Println(a) // 输出 2
}

defer 后面如果是 go 语句,这个 go 语句里面的变量,在注册的时候就已经计算好了

defer 后面如果是匿名函数,这个匿名函数设计到的变量,是在执行的时候才会计算。

Go 语言中切片的声明初始化和操作详解

· 阅读需 2 分钟
素明诚
Full stack development

声明切片变量

声明:当声明一个切片变量如 var s []int 时,s 初始化为 nil,表示它没有指向任何底层数组。其长度 (len) 和容量 (cap) 都为 0,无法访问或修改任何元素,因为实际上还没有底层数组与之关联。

使用 make 函数创建切片

内存分配:调用 make([]int, 3, 5) 时,底层会为切片分配一个长度(容量)为 5 的整型数组。这个数组是在堆上分配的,以便动态管理内存,允许切片在运行时调整大小。

初始化切片结构:切片结构体的实例会在栈上创建,包含三个关键的字段

  • Pointer:指向已分配底层数组的第一个元素的指针。
  • Len:设置为指定的长度,此例中为 3,意味着切片当前访问到的元素数量。
  • Cap:设置为数组的总容量,此例中为 5,即最多可以扩展到的元素数量。

初始化数组元素:新分配的数组元素初始化为其类型的零值。对于 int 类型,每个元素都初始化为 0

使用字面量初始化切片

内存分配:使用字面量如 s := []int{1, 2, 3} 初始化切片时,底层同样会在堆上分配一个容纳 3 个整型元素的数组。

初始化切片结构:创建的切片结构体实例指向这个数组,长度和容量都设置为字面量中元素的数量,即 3。

填充数组元素:数组中的元素将直接使用提供的字面值(1, 2, 3)进行初始化,不使用零值。

切片操作

创建新切片:当执行 s2 := s[1:3] 的切片操作时,底层不会复制数组,而是创建一个新的切片结构体,指向原切片 s 的底层数组的第 2 个元素(索引 1)。

新切片属性

  • Len:新切片的长度为 2,因为它从索引 1 到索引 2,包含 2 个元素。
  • Cap:新切片的容量为 4,计算方式为原切片的容量 5 减去新切片的起始索引 1

Linux 运行 go 文件

· 阅读需 1 分钟
素明诚
Full stack development
package main

import "fmt"

func main(){
fmt.Println("hello")
}

直接运行

go run hello.go

编译后运行

go build hello.go
./hello

直接运行和编译后运行的区别

直接运行 (go run): 这个命令会在内部先编译程序成一个临时文件,然后立即执行这个编译后的文件。它适合于开发过程中快速测试和调试代码,因为它省去了手动编译的步骤。

编译后运行 (go build): 这个命令会将你的 Go 源代码编译成一个可执行文件。在编译过程中,Go 编译器会优化代码,从而使最终的可执行文件在运行时拥有更好的性能。编译完成后,你可以多次运行这个可执行文件而无需再次编译,这对于生产环境非常重要。

Docker 镜像优化策略

· 阅读需 2 分钟
素明诚
Full stack development

1. 选择小型基础镜像

选择 Alpine Linux 等小型基础镜像,因其体积小巧,有助于减少最终镜像的大小。

2. 最小化层的数量

通过合并多个 RUN 指令,减少镜像层数,例如将更新、安装和清理步骤合并到一个 RUN 中执行。

RUN apt-get update && \
apt-get install -y package1 package2 && \
rm -rf /var/lib/apt/lists/*

3. 清理不必要的文件

在安装软件包后,清理缓存和临时文件,移除不必要的包,以减少镜像的体积,配合多阶段构建。

4. 使用多阶段构建

通过多阶段构建,只在最终镜像中包含运行应用所必需的文件和依赖,从而减少镜像大小。

# Build stage
FROM golang:1.16 as builder
WORKDIR /app
COPY . .
RUN go build -o myapp .

# Final stage
FROM alpine:latest
COPY --from=builder /app/myapp /myapp
ENTRYPOINT ["./myapp"]

5. 使用.dockerignore 文件

利用 .dockerignore 文件排除不必要的文件和目录,防止它们被添加到镜像中。

6. 优化指令顺序

通过优化 Dockerfile 中指令的顺序,使得不经常变更的指令尽量放在前面,以利用 Docker 的构建缓存。

7. 精简健康检查指令

确保健康检查(HEALTHCHECK)指令尽可能轻量,不增加额外的负担。

8. 选择性拷贝文件

在使用 COPY 指令时,只拷贝必需的文件,避免引入不必要的数据。

9. 有效利用构建缓存

合理安排 Dockerfile 中的指令,以最大限度地复用构建缓存,减少不必要的重建过程。

10. 审查软件包安装

定期审查和更新 Dockerfile,移除不再需要的依赖,保持镜像的轻量级。

11. 压缩应用程序文件

对静态资源进行压缩处理,如使用 Gzip 压缩,以减小文件占用的空间。

12. 使用非 root 用户

指定非 root 用户运行容器,减少对安全特性的依赖,可能带来的额外文件或库。

通过实施以上策略,可以有效控制 Docker 镜像的大小,提升容器的启动速度和运行效率,降低资源消耗,优化容器的整体性能。