wip: 保存 Go 练习和技术笔记
This commit is contained in:
@@ -0,0 +1,50 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"net/http"
|
||||
_ "net/http/pprof"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 来自文章 https://www.cnblogs.com/hwnd/p/20682814
|
||||
func simulateRequest() {
|
||||
timeout := 500 * time.Millisecond
|
||||
ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(timeout))
|
||||
defer cancel()
|
||||
resultCh := make(chan string) // 无缓冲!
|
||||
|
||||
// 启动 worker goroutine 处理"请求"
|
||||
go func() {
|
||||
time.Sleep(time.Duration(rand.Intn(500)+300) * time.Millisecond)
|
||||
select {
|
||||
case <-time.After(timeout):
|
||||
fmt.Println("worker 超时,放弃处理")
|
||||
case <-ctx.Done():
|
||||
case resultCh <- fmt.Sprintf("处理完成,耗时 %dms", rand.Intn(500)+300):
|
||||
|
||||
}
|
||||
}()
|
||||
|
||||
// TODO: 模拟请求取消或超时的情况(比如 500ms 后调用方不再关心结果)
|
||||
// 如果 worker 还没完成,它就 leak 了
|
||||
select {
|
||||
case resultCh := <-resultCh:
|
||||
fmt.Println(resultCh)
|
||||
case <-ctx.Done():
|
||||
fmt.Println("请求方取消了请求")
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 5; i++ {
|
||||
simulateRequest()
|
||||
fmt.Printf("第 %d 个请求处理完毕\n\n", i+1)
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
fmt.Println("所有请求处理完毕")
|
||||
// 运行后观察:goroutine 数量只增不减
|
||||
http.ListenAndServe(":6060", nil)
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
_ "net/http/pprof" // 注册 pprof 相关 handler
|
||||
"time"
|
||||
)
|
||||
|
||||
// memoryBlocks 保留一部分内存块,方便内存剖析观察。
|
||||
// 这个全局变量会一直存活,便于分析堆上的对象。
|
||||
var memoryBlocks [64][]byte
|
||||
|
||||
func main() {
|
||||
// 启动 pprof HTTP 服务
|
||||
go func() {
|
||||
log.Println("pprof server started at http://localhost:6060/debug/pprof/")
|
||||
if err := http.ListenAndServe("localhost:6060", nil); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
// 模拟 CPU 密集型业务
|
||||
go cpuIntensiveTask()
|
||||
|
||||
// 模拟内存分配型业务
|
||||
go memoryIntensiveTask()
|
||||
|
||||
// 阻塞主协程,让程序持续运行
|
||||
select {}
|
||||
}
|
||||
|
||||
// fib 递归计算斐波那契数列,非常耗费 CPU
|
||||
func fib(n int) int {
|
||||
if n <= 1 {
|
||||
return n
|
||||
}
|
||||
return fib(n-1) + fib(n-2)
|
||||
}
|
||||
|
||||
// cpuIntensiveTask 循环执行 CPU 密集型计算
|
||||
func cpuIntensiveTask() {
|
||||
for {
|
||||
_ = fib(30)
|
||||
}
|
||||
}
|
||||
|
||||
// memoryIntensiveTask 循环分配内存并保留在全局切片中
|
||||
func memoryIntensiveTask() {
|
||||
for i := 0; ; i++ {
|
||||
idx := i % len(memoryBlocks)
|
||||
// 每次分配 1MB,替换掉旧的内存块
|
||||
memoryBlocks[idx] = make([]byte, 1<<20) // 1 << 20 = 1MB
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-contrib/sessions"
|
||||
"github.com/gin-contrib/sessions/cookie"
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
var (
|
||||
mockUsers = map[string]string{
|
||||
"zhangsan": "123456",
|
||||
"lisi": "654321",
|
||||
}
|
||||
// secretKey = "your-secret-key"
|
||||
hmacSecretKey = "5KiinhGcvoXFmgOttvgMWd9ugadE0KVP"
|
||||
blockSecretKey = "OLZmfTWHGyjCfJoH1Y8SjbSY"
|
||||
)
|
||||
|
||||
const sessionTTL = 60 * 5
|
||||
|
||||
func sessionOptions(maxAge int) sessions.Options {
|
||||
return sessions.Options{
|
||||
Path: "/",
|
||||
Domain: "", // 设置为 "" 表示当前域名
|
||||
MaxAge: maxAge, // 单位为秒。0 表示不写Max-Age(浏览器会话cookie); -1 表示删除 cookie
|
||||
Secure: false, // 生产走 HTTPS 时改 true
|
||||
HttpOnly: true, // true 表示禁止 JS 读取 cookie
|
||||
SameSite: http.SameSiteStrictMode, // 严格模式,仅在同源请求中发送 cookie
|
||||
}
|
||||
}
|
||||
|
||||
// 代码来自文章 https://www.cnblogs.com/XY-Heruo/p/22864951
|
||||
func main() {
|
||||
r := gin.Default()
|
||||
|
||||
// Create cookie-based session store with a secret key
|
||||
// 传单个密钥时, 只做签名认证,不加密cookie内容. 认证密钥建议 32 或 64 字节
|
||||
// 传一对密钥时,第一个用于认证(HMAC),第二个用于加密(AES-128, AES-192, AES-256)。加密密钥必须是16、24或32字节长度,对应 AES-128/192/256。
|
||||
// 多对密钥用于密钥轮换,第一对用于当前加密和认证,后续的用于解密和认证旧的cookie。Example: cookie.NewStore([]byte("newAuth"), []byte("newEnc"), []byte("oldAuth"), []byte("oldEnc"))
|
||||
store := cookie.NewStore([]byte(hmacSecretKey), []byte(blockSecretKey))
|
||||
store.Options(sessionOptions(sessionTTL)) // 全局设置 cookie 过期时间为 5 分钟
|
||||
r.Use(sessions.Sessions("mysession", store))
|
||||
|
||||
r.POST("/login", login)
|
||||
|
||||
auth := r.Group("/", authRequired())
|
||||
|
||||
auth.GET("/profile", profile)
|
||||
auth.POST("/logout", logout)
|
||||
|
||||
r.Run(":8080")
|
||||
}
|
||||
|
||||
func login(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
username := c.PostForm("username")
|
||||
password := c.PostForm("password")
|
||||
|
||||
if username == "" || password == "" {
|
||||
c.JSON(http.StatusBadRequest, gin.H{"error": "username or password is empty"})
|
||||
return
|
||||
}
|
||||
|
||||
if mockUsers[username] != password {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "invalid username or password"})
|
||||
return
|
||||
}
|
||||
|
||||
session.Set("user", username)
|
||||
session.Set("expire_at", time.Now().Add(time.Second*sessionTTL).Unix()) // 设置过期时间
|
||||
if err := session.Save(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "save session failed"})
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(http.StatusOK, gin.H{"message": "logged in"})
|
||||
}
|
||||
|
||||
func logout(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
session.Clear()
|
||||
session.Options(sessionOptions(-1)) // 让浏览器删除 cookie
|
||||
if err := session.Save(); err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "save session failed"})
|
||||
return
|
||||
}
|
||||
c.JSON(http.StatusOK, gin.H{"message": "logged out"})
|
||||
}
|
||||
|
||||
func profile(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
user := session.Get("user").(string)
|
||||
c.JSON(http.StatusOK, gin.H{"user": user})
|
||||
}
|
||||
|
||||
func authRequired() gin.HandlerFunc {
|
||||
return func(c *gin.Context) {
|
||||
session := sessions.Default(c)
|
||||
if session.Get("user") == nil {
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "unauthorized"})
|
||||
return
|
||||
}
|
||||
|
||||
if expireAt, ok := session.Get("expire_at").(int64); !ok || time.Now().Unix() > expireAt {
|
||||
session.Clear()
|
||||
session.Options(sessionOptions(-1))
|
||||
session.Save()
|
||||
c.AbortWithStatusJSON(http.StatusUnauthorized, gin.H{"error": "expired session"})
|
||||
return
|
||||
}
|
||||
c.Next()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user