鸡汤语录
能够生存下来的物种,并不是那些最强壮的,也不是那些最聪明的,而是那些对变化作出快速反应的。—— 达尔文
投资股票的第一原则:不要赔钱;第二原则:永远不要忘记第一原则。—— 巴菲特
Github搜索Java、微服务等关键字,选择语言并按stars排序,会有很对优质资源,比如面试题、源码分析、优秀项目等。
格局管理和性能优化
推荐链接:
固定窗口计数器(Fixed Window Counter)是一种简单的限流算法。它将时间分成固定长度的窗口(如1秒或1分钟),在每个窗口中统计请求数。当某个窗口内的请求计数超过限流阀值时,该窗口剩余时间内的请求都被拒绝;窗口结束后计数清零,重新开始统计。该算法实现非常简单,仅需一个支持原子加减的计数器即可,但存在明显的窗口边界效应。例如,如果限制1秒内最多5个请求,1秒钟末尾来了5个请求,下一个窗口开始瞬间又来了5个请求,总共10个请求瞬间涌入就会突破限流阀值。
滑动窗口计数器(Sliding Window Counter)通过将固定窗口进一步细分来平滑限流。它将整个统计周期(如1秒)划分为多个更小的时间片(如每0.2秒一个),在每个小时间片中单独计数。每次请求到来时,累加当前时间所在小片的计数;然后将所有在整个滑动窗口范围内的小片计数相加,与阈值比较。随着时间流逝,窗口以微小步长向前滑动,自动丢弃过期片段中的请求。这样可以一定程度上解决固定窗口在边界时出现的“大脉冲”问题。
滑动日志算法(Sliding Log)是一种精确度更高的限流方法。它不使用固定窗口,而是为每个请求都记录时间戳,并将所有请求按时间排序保存。每次新请求到来时,只需清理日志中超出时间范围的旧记录,然后统计当前时间窗口内(例如过去1秒)的请求总数,与阈值比较。如果超过限制,则拒绝该请求。这种方法没有固定窗口边界的问题,限流非常精准和公平,不会出现窗口交界处被意外放行或拒绝的现象。但缺点是内存开销大:需要保存大量请求时间戳记录,随着请求量增长资源消耗显著。
漏桶算法(Leaky Bucket)将请求看作“水滴”注入一个固定容量的桶中,桶底有一个以恒定速率漏水的小孔。每个新请求到来时都加入桶中;如果桶满了(超出容量),多余的请求就被丢弃。而桶会持续以恒定的速率处理(漏出)请求,即使瞬间来了大量请求,出桶速度保持不变,保证系统稳定处理。该算法本质上把不规则的输入流平滑为固定输出流,对防止突发洪峰非常有效。
令牌桶算法(Token Bucket)维护一个“令牌桶”,桶有固定容量,并以固定速率向其中添加令牌。当请求到来时,如果桶中有令牌,则取出一个令牌放行该请求,否则拒绝。桶中的令牌最多累积到满;在处理空闲时刻,积累的令牌可用于应对突发流量。与漏桶不同,令牌桶允许一定量的突发流量,因为空闲时累计的令牌可以在瞬时释放,突发请求只要桶中有令牌就能继续处理。
分布式系统中,为了让多个实例共享限流状态,常采用Redis作为集中式存储,并借助原子操作保证多个实例间的一致性。通常做法是:每个实例对同一资源发送限流请求时,都访问同一个Redis键。例如可以使用固定窗口策略——为每个时间窗口创建一个Redis键(如apiKey:当前分钟
),调用 INCR
将计数+1,并在键初次创建时设置过期时间;当计数超过阈值时拒绝请求。Redis的单线程特性和Lua脚本支持使得这种操作可以原子执行,避免了并发冲突。例如,使用MULTI/EXEC
或Lua脚本将INCR
与EXPIRE
打包成单次操作,就可以保证即使同时多个实例发起请求,也不会出现数据竞争。此外,对于滑动窗口和滑动日志限流,也可以利用Redis的有序集合(ZSET)存储各个请求的时间戳,并在每次请求时原子地新增和删除过期成员,从而实现精确的滑动计数。
INCR
、HINCRBY
等更新操作变成原子操作,从而确保并发情况下计数正确。只要所有服务实例都指向同一个Redis key,就能做到全局限流。算法 | 是否支持突发流量 | 平滑程度 | 实现复杂度 | 资源(内存/计算) | 典型场景 |
---|---|---|---|---|---|
固定窗口计数器 | 否 | 较低(有边界效应) | 低 | 低 | 简单的QPS限制,对准确性要求不高 |
滑动窗口计数器 | 否 | 较高(可细化) | 中 | 中 | 需要比固定窗口更平滑的流量控制 |
滑动日志算法 | 是 | 很高(精确) | 高 | 高(存储时间戳) | 追求精确限流、请求量适中时 |
漏桶算法 | 否(限速稳定) | 很高(输出恒定) | 中 | 中 | 限制输出速率,防止请求尖峰 |
令牌桶算法 | 是 | 高(允许突发) | 中 | 低/中 | 需要平滑输出且允许突发场景 |
Redis分布式 | 视具体算法而定 | 视具体算法而定 | 较高 | 中/高 | 分布式环境下全局限流 |
固定窗口和滑动窗口算法简单、开销低,适用于快速实现限流策略;其中滑动窗口能稍微缓解窗口边界的突发效应,但对极端突发流量仍不够友好。
滑动日志算法在准确性和公平性上优势最大,但需要记录每次请求时间,资源消耗最高。
漏桶和令牌桶算法都能有效平滑输出流量,但侧重点不同:漏桶以恒定速率处理请求(不支持突发),适合输出受限的场景;令牌桶允许短时突发,但算法实现稍复杂,可根据空闲时段积累令牌平滑应对流量峰值。
分布式限流(如基于Redis的方案)则将上述任意算法扩展到多实例环境,关键在于通过Redis的原子操作来共享计数状态并保证一致性。
总体而言,选用哪种限流算法需综合考虑系统对突发流量的容忍度、实现成本和资源消耗:对极端突发容忍度高的场景,可优先考虑令牌桶;对精确性要求极高的,可采用滑动日志;而对于大部分场景,固定窗口或滑动窗口配合简单的分布式计数即可满足需求。
推荐链接:
所有版本:https://go.dev/dl/
发布历史:https://go.dev/doc/devel/release
搜 索 库:https://pkg.go.dev/
标 准 库:https://pkg.go.dev/std
Go 命 令:https://pkg.go.dev/cmd/go
用户手册:https://go.dev/doc/
Go 语言高效编程:https://go.dev/doc/effective_go
Go 编程语言规范:https://go.dev/ref/spec
Go by Example:https://gobyexample.com/ 、https://gobyexample-cn.github.io/
Go 菜鸟教程:https://www.runoob.com/go/go-tutorial.html
GoLand 下载:https://www.jetbrains.com/go/download/other.html
下载解压版:go1.24.2.windows-amd64.zip
GUI配置环境变量:
1 | GOROOT=D:\Program\go\ |
命令 | 作用 |
---|---|
go env |
查看所有当前 Go 环境变量 |
go env VAR |
查看单个变量(如 go env GOPATH ) |
go env -w VAR=value |
写入配置文件,永久设置变量(Go 1.13+ 支持)。 即将变量写入本地配置文件(通常位于 $HOME/.config/go/env 或 %USERPROFILE%\go\env )。 |
go env -u VAR |
取消变量设置,即从配置文件中删除(恢复默认行为) |
go env -json |
以 JSON 格式输出所有变量,适合脚本或工具 |
1 | # GOPATH 指定工作目录的根。1、存放依赖包缓存(比如在 $GOPATH/pkg/mod 下);2、存放 go install 安装后的二进制文件(在 $GOPATH/bin)。 |
1 | ├─01 |
这是 Go 模块的核心文件,描述项目的模块路径、依赖项及其版本。
主要作用:
require
)replace
替换依赖模块路径(本地调试等)示例内容:
1 | module example/hello |
记录每个依赖模块及其版本的哈希校验和,用于验证依赖包的一致性和完整性。
作用:
示例内容:
1 | rsc.io/quote v1.5.2 h1:w5fcysjrx7yqtD/aO+QwRjYZOKnaM9Uh2b40tElTs3Y= |
用于管理多个 Go 模块组成的工作区,支持跨模块开发,解决 monorepo 场景下的依赖问题。
使用场景:
replace
用于本地联调多个模块示例内容:
1 | go 1.24 |
官网学习中遇到的命令顺序
1 | go mod init example/hello |
命令 | 说明 |
---|---|
go mod init <模块名> |
初始化模块(生成 go.mod) |
go mod tidy |
自动添加/删除依赖 |
go mod download |
下载 go.mod 中声明的所有依赖到本地 |
go mod vendor |
将依赖复制到 vendor/ 目录中 |
go mod verify |
校验模块缓存 |
go list -m all |
查看所有依赖模块 |
命令 | 说明 |
---|---|
go run <file.go> |
编译并运行 Go 源码文件 |
go run . |
编译并运行当前目录的 main 包 |
go build |
编译当前包,生成可执行文件(二进制文件) |
go build -o myapp |
指定输出文件名 |
go install |
编译并安装当前模块(安装到 $GOBIN 或 $GOPATH/bin ) |
go clean |
清除当前模块下的构建产物,包括编译生成的二进制文件和中间文件 |
go clean -modcache |
清除全局的模块缓存(即下载的第三方依赖) |
命令 | 说明 |
---|---|
go test |
运行当前包的测试用例 |
go test -v |
显示详细的测试输出 |
go test ./... |
递归测试当前模块的所有包(包括子包) |
go test -cover |
查看测试覆盖率 |
go test -bench . |
执行基准测试(以 Benchmark 开头的函数) |
命令 | 说明 |
---|---|
go fmt ./... |
格式化所有 Go 文件 |
go vet |
静态代码分析(找潜在 bug)(推荐) |
go doc <包名/函数> |
查看文档,如:go doc fmt.Println |
go list |
列出当前模块信息 |
go list -m -u all |
列出当前模块依赖的所有模块,以及每个模块的最新版本: |
go list -m -u example.com/theirmodule |
显示特定模块的最新版本 |
go list -f '{{.Target}}' |
打印可编译包的目标文件路径 |
go list -json |
输出包的所有结构化信息(JSON 格式) |
命令 | 说明 |
---|---|
go version |
查看 Go 版本 |
go env |
查看 Go 环境变量 |
go env -w GOBIN=C:\path\to\your\bin |
指定 Go 程序安装目录 |
go help <命令> |
查看某个命令帮助,比如 go help mod |
命令 | 说明 |
---|---|
go get . |
添加模块中某个包的所有依赖项 |
go get <module> |
安装或升级某个包(Go 1.17 及以前) |
go install <module>@latest |
Go 1.18+ 推荐方式安装工具包 |
示例:安装一个工具(如静态分析器)
1 | // golangci-lint 是 Go 语言中最流行的 静态代码分析工具,它相当于一个多合一的“代码质量检查器” |
今天,我们的安全专栏迎来了尾声。
在学习本专栏之前,你可能认为黑客就和电影里面一样,对着命令行噼噼啪啪地敲键盘,就能够控制某个大型的系统,引起一场轰动的事件。但经过这段时间的学习,我相信你对黑客一定有了一个更理性的认知。
欢迎来到安全专栏的第5次加餐时间。
随着科技的快速发展,各种新的技术和概念不断出现,持续出现的新技术会不断推动安全的发展。虽然,每一个新技术都会衍生出新的安全威胁和隐患,但是,这些新的安全问题也正是安全行业保持活力的源泉。所以,对于安全人员来说,这些新技术的出现既是一种挑战,也是一种机遇。