From 4278b208e75453dc5c10afb3bec4bc67b77e78f4 Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Mon, 25 Sep 2023 16:26:27 +0800 Subject: [PATCH 01/10] =?UTF-8?q?=E5=A4=9A=E5=AE=9E=E4=BE=8B=20Session=20?= =?UTF-8?q?=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 ++ go.sum | 5 +++++ webook/docker-compose.yaml | 10 +++++++++- webook/main.go | 14 ++++++++++++-- 4 files changed, 28 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index ba5df9d..04d6bbf 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( ) require ( + github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect @@ -25,6 +26,7 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect + github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect diff --git a/go.sum b/go.sum index bb325f5..cffd274 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,5 @@ +github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= +github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -38,6 +40,8 @@ github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGF github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= +github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -45,6 +49,7 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/webook/docker-compose.yaml b/webook/docker-compose.yaml index c220889..b195c12 100644 --- a/webook/docker-compose.yaml +++ b/webook/docker-compose.yaml @@ -2,7 +2,7 @@ version: "3" # 我这个 docker compose 由几个服务组成 services: mysql8: - image: mysql:8.0 + image: mysql:8.0.29 restart: always command: --default-authentication-plugin=mysql_native_password environment: @@ -14,3 +14,11 @@ services: # - 外部访问用 13316 - 13316:3306 + redis: + image: "bitnami/redis:latest" + restart: always + environment: + - ALLOW_EMPTY_PASSWORD=yes + ports: + - '6379:6379' + diff --git a/webook/main.go b/webook/main.go index 8fa1d2e..0eea102 100644 --- a/webook/main.go +++ b/webook/main.go @@ -8,7 +8,7 @@ import ( "gitee.com/geekbang/basic-go/webook/internal/web/middleware" "github.com/gin-contrib/cors" "github.com/gin-contrib/sessions" - "github.com/gin-contrib/sessions/cookie" + "github.com/gin-contrib/sessions/redis" "github.com/gin-gonic/gin" "gorm.io/driver/mysql" "gorm.io/gorm" @@ -71,7 +71,17 @@ func initWebServer() *gin.Engine { login := &middleware.LoginMiddlewareBuilder{} // 存储数据的,也就是你 userId 存哪里 // 直接存 cookie - store := cookie.NewStore([]byte("secret")) + //store := cookie.NewStore([]byte("secret")) + // 基于内存的实现 + //store := memstore.NewStore([]byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgK"), + // []byte("eF1`yQ9>yT1`tH1,sJ0.zD8;mZ9~nC6(")) + store, err := redis.NewStore(16, "tcp", + "localhost:6379", "", + []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgK"), + []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgA")) + if err != nil { + panic(err) + } server.Use(sessions.Sessions("ssid", store), login.CheckLogin()) return server } -- Gitee From 6e2d6d49a3d42767d26b771f529b26f64cdbf8a2 Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Mon, 25 Sep 2023 21:44:03 +0800 Subject: [PATCH 02/10] =?UTF-8?q?=E5=88=B7=E6=96=B0=E8=BF=87=E6=9C=9F?= =?UTF-8?q?=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webook/internal/web/middleware/login.go | 27 ++++++++++++++++++++++++- webook/internal/web/user.go | 7 ++++--- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/webook/internal/web/middleware/login.go b/webook/internal/web/middleware/login.go index fa05ae2..b3d3fa8 100644 --- a/webook/internal/web/middleware/login.go +++ b/webook/internal/web/middleware/login.go @@ -1,15 +1,20 @@ package middleware import ( + "encoding/gob" + "fmt" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" "net/http" + "time" ) type LoginMiddlewareBuilder struct { } func (m *LoginMiddlewareBuilder) CheckLogin() gin.HandlerFunc { + // 注册一下这个类型 + gob.Register(time.Now()) return func(ctx *gin.Context) { path := ctx.Request.URL.Path if path == "/users/signup" || path == "/users/login" { @@ -17,10 +22,30 @@ func (m *LoginMiddlewareBuilder) CheckLogin() gin.HandlerFunc { return } sess := sessions.Default(ctx) - if sess.Get("userId") == nil { + userId := sess.Get("userId") + if userId == nil { // 中断,不要往后执行,也就是不要执行后面的业务逻辑 ctx.AbortWithStatus(http.StatusUnauthorized) return } + + now := time.Now() + + // 我怎么知道,要刷新了呢? + // 假如说,我们的策略是每分钟刷一次,我怎么知道,已经过了一分钟? + const updateTimeKey = "update_time" + // 试着拿出上一次刷新时间 + val := sess.Get(updateTimeKey) + lastUpdateTime, ok := val.(time.Time) + if val == nil || !ok || now.Sub(lastUpdateTime) > time.Second*10 { + // 你这是第一次进来 + sess.Set(updateTimeKey, now) + sess.Set("userId", userId) + err := sess.Save() + if err != nil { + // 打日志 + fmt.Println(err) + } + } } } diff --git a/webook/internal/web/user.go b/webook/internal/web/user.go index 2278de0..5a8b2f4 100644 --- a/webook/internal/web/user.go +++ b/webook/internal/web/user.go @@ -111,8 +111,8 @@ func (h *UserHandler) Login(ctx *gin.Context) { sess := sessions.Default(ctx) sess.Set("userId", u.Id) sess.Options(sessions.Options{ - // 十五分钟 - MaxAge: 900, + // 十分钟 + MaxAge: 30, }) err = sess.Save() if err != nil { @@ -128,9 +128,10 @@ func (h *UserHandler) Login(ctx *gin.Context) { } func (h *UserHandler) Edit(ctx *gin.Context) { - + // 嵌入一段刷新过期时间的代码 } func (h *UserHandler) Profile(ctx *gin.Context) { ctx.String(http.StatusOK, "这是 profile") + // 嵌入一段刷新过期时间的代码 } -- Gitee From c9c79702587148e1326ae8fe930342293521880c Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Tue, 26 Sep 2023 16:27:10 +0800 Subject: [PATCH 03/10] JWT --- go.mod | 1 + go.sum | 2 + webook-fe/src/axios/axios.ts | 58 ++++++++--------- webook/internal/web/middleware/login_jwt.go | 71 +++++++++++++++++++++ webook/internal/web/user.go | 46 ++++++++++++- webook/main.go | 16 ++++- 6 files changed, 162 insertions(+), 32 deletions(-) create mode 100644 webook/internal/web/middleware/login_jwt.go diff --git a/go.mod b/go.mod index 04d6bbf..e9116cb 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/gin-contrib/sessions v0.0.5 github.com/gin-gonic/gin v1.9.1 github.com/go-sql-driver/mysql v1.7.0 + github.com/golang-jwt/jwt/v5 v5.0.0 github.com/stretchr/testify v1.8.3 golang.org/x/crypto v0.9.0 gorm.io/driver/mysql v1.5.1 diff --git a/go.sum b/go.sum index cffd274..3ec2c71 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9 github.com/goccy/go-json v0.9.7/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= diff --git a/webook-fe/src/axios/axios.ts b/webook-fe/src/axios/axios.ts index 7583d1b..23dfd9d 100644 --- a/webook-fe/src/axios/axios.ts +++ b/webook-fe/src/axios/axios.ts @@ -7,35 +7,35 @@ const instance = axios.create({ }) -// instance.interceptors.response.use(function (resp) { -// const newToken = resp.headers["x-jwt-token"] -// const newRefreshToken = resp.headers["x-refresh-token"] -// console.log("resp headers", resp.headers) -// if (newToken) { -// localStorage.setItem("token", newToken) -// } -// if (newRefreshToken) { -// localStorage.setItem("refresh_token", newRefreshToken) -// } -// if (resp.status == 401) { -// window.location.href="/users/login" -// } -// return resp -// }, (err) => { -// console.log(err) -// if (err.response.status == 401) { -// window.location.href="/users/login" -// } -// return err -// }) +instance.interceptors.response.use(function (resp) { + const newToken = resp.headers["x-jwt-token"] + const newRefreshToken = resp.headers["x-refresh-token"] + console.log("resp headers", resp.headers) + if (newToken) { + localStorage.setItem("token", newToken) + } + if (newRefreshToken) { + localStorage.setItem("refresh_token", newRefreshToken) + } + // if (resp.status == 401) { + // window.location.href="/users/login" + // } + return resp +}, (err) => { + console.log(err) + // if (err.response.status == 401) { + // window.location.href="/users/login" + // } + return err +}) // -// // 在这里让每一个请求都加上 authorization 的头部 -// instance.interceptors.request.use((req) => { -// const token = localStorage.getItem("token") -// req.headers.setAuthorization("Bearer " + token, true) -// return req -// }, (err) => { -// console.log(err) -// }) +// 在这里让每一个请求都加上 authorization 的头部 +instance.interceptors.request.use((req) => { + const token = localStorage.getItem("token") + req.headers.setAuthorization("Bearer " + token, true) + return req +}, (err) => { + console.log(err) +}) export default instance \ No newline at end of file diff --git a/webook/internal/web/middleware/login_jwt.go b/webook/internal/web/middleware/login_jwt.go new file mode 100644 index 0000000..24fe941 --- /dev/null +++ b/webook/internal/web/middleware/login_jwt.go @@ -0,0 +1,71 @@ +package middleware + +import ( + "gitee.com/geekbang/basic-go/webook/internal/web" + "github.com/gin-gonic/gin" + "github.com/golang-jwt/jwt/v5" + "log" + "net/http" + "strings" + "time" +) + +type LoginJWTMiddlewareBuilder struct { +} + +func (m *LoginJWTMiddlewareBuilder) CheckLogin() gin.HandlerFunc { + return func(ctx *gin.Context) { + path := ctx.Request.URL.Path + if path == "/users/signup" || path == "/users/login" { + // 不需要登录校验 + return + } + // 根据约定,token 在 Authorization 头部 + // Bearer XXXX + authCode := ctx.GetHeader("Authorization") + if authCode == "" { + // 没登录,没有 token, Authorization 这个头部都没有 + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + segs := strings.Split(authCode, " ") + if len(segs) != 2 { + // 没登录,Authorization 中的内容是乱传的 + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + tokenStr := segs[1] + var uc web.UserClaims + token, err := jwt.ParseWithClaims(tokenStr, &uc, func(token *jwt.Token) (interface{}, error) { + return web.JWTKey, nil + }) + if err != nil { + // token 不对,token 是伪造的 + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + if token == nil || !token.Valid { + // token 解析出来了,但是 token 可能是非法的,或者过期了的 + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + + expireTime := uc.ExpiresAt + // 不判定都可以 + //if expireTime.Before(time.Now()) { + // ctx.AbortWithStatus(http.StatusUnauthorized) + // return + //} + // 剩余过期时间 < 50s 就要刷新 + if expireTime.Sub(time.Now()) < time.Second*50 { + uc.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute)) + tokenStr, err = token.SignedString(web.JWTKey) + ctx.Header("x-jwt-token", tokenStr) + if err != nil { + // 这边不要中断,因为仅仅是过期时间没有刷新,但是用户是登录了的 + log.Println(err) + } + } + ctx.Set("user", uc) + } +} diff --git a/webook/internal/web/user.go b/webook/internal/web/user.go index 5a8b2f4..da5d6d8 100644 --- a/webook/internal/web/user.go +++ b/webook/internal/web/user.go @@ -6,7 +6,9 @@ import ( regexp "github.com/dlclark/regexp2" "github.com/gin-contrib/sessions" "github.com/gin-gonic/gin" + jwt "github.com/golang-jwt/jwt/v5" "net/http" + "time" ) const ( @@ -38,7 +40,8 @@ func (h *UserHandler) RegisterRoutes(server *gin.Engine) { // POST /users/signup ug.POST("/signup", h.SignUp) // POST /users/login - ug.POST("/login", h.Login) + //ug.POST("/login", h.Login) + ug.POST("/login", h.LoginJWT) // POST /users/edit ug.POST("/edit", h.Edit) // GET /users/profile @@ -96,6 +99,39 @@ func (h *UserHandler) SignUp(ctx *gin.Context) { } } +func (h *UserHandler) LoginJWT(ctx *gin.Context) { + type Req struct { + Email string `json:"email"` + Password string `json:"password"` + } + var req Req + if err := ctx.Bind(&req); err != nil { + return + } + u, err := h.svc.Login(ctx, req.Email, req.Password) + switch err { + case nil: + uc := UserClaims{ + Uid: u.Id, + RegisteredClaims: jwt.RegisteredClaims{ + // 1 分钟过期 + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + }, + } + token := jwt.NewWithClaims(jwt.SigningMethodHS512, uc) + tokenStr, err := token.SignedString(JWTKey) + if err != nil { + ctx.String(http.StatusOK, "系统错误") + } + ctx.Header("x-jwt-token", tokenStr) + ctx.String(http.StatusOK, "登录成功") + case service.ErrInvalidUserOrPassword: + ctx.String(http.StatusOK, "用户名或者密码不对") + default: + ctx.String(http.StatusOK, "系统错误") + } +} + func (h *UserHandler) Login(ctx *gin.Context) { type Req struct { Email string `json:"email"` @@ -132,6 +168,14 @@ func (h *UserHandler) Edit(ctx *gin.Context) { } func (h *UserHandler) Profile(ctx *gin.Context) { + //us := ctx.MustGet("user").(UserClaims) ctx.String(http.StatusOK, "这是 profile") // 嵌入一段刷新过期时间的代码 } + +var JWTKey = []byte("k6CswdUm77WKcbM68UQUuxVsHSpTCwgK") + +type UserClaims struct { + jwt.RegisteredClaims + Uid int64 +} diff --git a/webook/main.go b/webook/main.go index 0eea102..e30de60 100644 --- a/webook/main.go +++ b/webook/main.go @@ -53,7 +53,9 @@ func initWebServer() *gin.Engine { //AllowOrigins: []string{"http://localhost:3000"}, AllowCredentials: true, - AllowHeaders: []string{"Content-Type"}, + AllowHeaders: []string{"Content-Type", "Authorization"}, + // 这个是允许前端访问你的后端响应中带的头部 + ExposeHeaders: []string{"x-jwt-token"}, //AllowHeaders: []string{"content-type"}, //AllowMethods: []string{"POST"}, AllowOriginFunc: func(origin string) bool { @@ -68,6 +70,17 @@ func initWebServer() *gin.Engine { println("这是我的 Middleware") }) + useJWT(server) + //useSession(server) + return server +} + +func useJWT(server *gin.Engine) { + login := middleware.LoginJWTMiddlewareBuilder{} + server.Use(login.CheckLogin()) +} + +func useSession(server *gin.Engine) { login := &middleware.LoginMiddlewareBuilder{} // 存储数据的,也就是你 userId 存哪里 // 直接存 cookie @@ -83,5 +96,4 @@ func initWebServer() *gin.Engine { panic(err) } server.Use(sessions.Sessions("ssid", store), login.CheckLogin()) - return server } -- Gitee From 3d35a5a467d897d1625d4ba403e906a4ef63374d Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Tue, 26 Sep 2023 21:19:19 +0800 Subject: [PATCH 04/10] 9.26 --- qa/2023.09.26/main.go | 44 ++++++++++++++++++++++++++++++++ qa/2023.09.26/user.go | 58 +++++++++++++++++++++++++++++++++++++++++++ syntax/funcs/defer.go | 53 +++++++++++++++++++++++++++++++++++++++ syntax/funcs/func.go | 1 + 4 files changed, 156 insertions(+) create mode 100644 qa/2023.09.26/main.go create mode 100644 qa/2023.09.26/user.go diff --git a/qa/2023.09.26/main.go b/qa/2023.09.26/main.go new file mode 100644 index 0000000..6ad9fcb --- /dev/null +++ b/qa/2023.09.26/main.go @@ -0,0 +1,44 @@ +package main + +import "fmt" + +func main() { + //DeferClosureLoopV1() + //DeferClosureLoopV2() + //DeferClosureLoopV3() + OfNullable[User](User{}).Apply(func(t User) { + println(t.Name) + }) +} + +func DeferClosureLoopV1() { + for i := 0; i < 10; i++ { + fmt.Printf("循环 %p \n", &i) + defer func() { + fmt.Printf("%p \n", &i) + println(i) + }() + } + println("跳出循环") +} + +func DeferClosureLoopV2() { + for i := 0; i < 10; i++ { + defer func(val int) { + fmt.Printf("%p \n", &val) + println(val) + }(i) + } + println("跳出循环") +} + +func DeferClosureLoopV3() { + for i := 0; i < 10; i++ { + j := i + defer func() { + fmt.Printf("%p \n", &j) + println(j) + }() + } + println("跳出循环") +} diff --git a/qa/2023.09.26/user.go b/qa/2023.09.26/user.go new file mode 100644 index 0000000..f707a44 --- /dev/null +++ b/qa/2023.09.26/user.go @@ -0,0 +1,58 @@ +package main + +func UseUser() { + u, err := GetUser(12) + if err != nil { + println(err) + return + } + // u.Name + println(u.Name) +} + +type Optional[T any] struct { + Val T +} + +func (o Optional[T]) Apply(fn func(t T)) { + var val any = o.Val + if val == nil { + return + } + fn(o.Val) +} + +func OfNullable[T any](t T) Optional[T] { + return Optional[T]{ + Val: t, + } +} + +// GetUser 最佳实践,没有 error,*User 一定不为 nil +func GetUser(id int64) (*User, error) { + return &User{}, nil +} + +func Component() { + var u User // 这个时候。 + // Address就已经初始化了,零值(但不是 nil) + println(u.Friend.name) // panic,因为 Friend 是指针,所以初始化的是 nil + + var u1 User = User{ + Friend: &Friend{}, + } + println(u1.Friend.name) +} + +type User struct { + Name string + Address + *Friend +} + +type Address struct { +} + +type Friend struct { + name string +} diff --git a/syntax/funcs/defer.go b/syntax/funcs/defer.go index 7392e41..8e2d293 100644 --- a/syntax/funcs/defer.go +++ b/syntax/funcs/defer.go @@ -1,5 +1,10 @@ package main +import ( + "os" + "sync" +) + func Defer() { defer func() { println("第一个 defer") @@ -45,6 +50,15 @@ func DeferReturn() int { return a } +func DeferReturnV0() (b int) { + a := 0 + defer func() { + a = 1 + }() + b = a + return +} + func DeferReturnV1() (a int) { a = 0 defer func() { @@ -63,6 +77,45 @@ func DeferReturnV2() *MyStruct { return res } +func DeferReturnV3() MyStruct { + res := MyStruct{ + name: "Tom", + } + defer func() { + res.name = "Jerry" + }() + return res +} + type MyStruct struct { name string } + +type SafeResourceV1 struct { + lock *sync.Mutex + resource any +} + +func (s SafeResourceV1) UseResource() { + s.lock.Lock() + defer s.lock.Unlock() +} + +type SafeResource struct { + lock sync.Mutex + resource any +} + +func (s *SafeResource) UseResource() { + s.lock.Lock() + defer s.lock.Unlock() +} + +func ReadFile(file string) { + f, err := os.Open(file) + if err != nil { + println(err) + return + } + defer f.Close() +} diff --git a/syntax/funcs/func.go b/syntax/funcs/func.go index d5b7563..a7c014d 100644 --- a/syntax/funcs/func.go +++ b/syntax/funcs/func.go @@ -13,6 +13,7 @@ func main() { fmt.Println("DeferReturn", DeferReturn()) fmt.Println("DeferReturnV1", DeferReturnV1()) fmt.Println("DeferReturnV2", DeferReturnV2().name) + fmt.Println("DeferReturnV3", DeferReturnV3().name) } func Invoke() { -- Gitee From 9a7a7a90b98b48f4b5dc0c34071da51e3e60982d Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Fri, 29 Sep 2023 20:58:44 +0800 Subject: [PATCH 05/10] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E5=91=A8=E4=BF=9D?= =?UTF-8?q?=E6=8A=A4=E7=B3=BB=E7=BB=9F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 3 + go.sum | 8 +++ webook/main.go | 27 +++++--- .../pkg/ginx/middleware/ratelimit/builder.go | 64 +++++++++++++++++++ .../middleware/ratelimit/slide_window.lua | 26 ++++++++ 5 files changed, 119 insertions(+), 9 deletions(-) create mode 100644 webook/pkg/ginx/middleware/ratelimit/builder.go create mode 100644 webook/pkg/ginx/middleware/ratelimit/slide_window.lua diff --git a/go.mod b/go.mod index e9116cb..027ef9a 100644 --- a/go.mod +++ b/go.mod @@ -9,6 +9,7 @@ require ( github.com/gin-gonic/gin v1.9.1 github.com/go-sql-driver/mysql v1.7.0 github.com/golang-jwt/jwt/v5 v5.0.0 + github.com/redis/go-redis/v9 v9.2.1 github.com/stretchr/testify v1.8.3 golang.org/x/crypto v0.9.0 gorm.io/driver/mysql v1.5.1 @@ -19,8 +20,10 @@ require ( require ( github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/bytedance/sonic v1.9.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gin-contrib/sse v0.1.0 // indirect github.com/go-playground/locales v0.14.1 // indirect diff --git a/go.sum b/go.sum index 3ec2c71..0eed584 100644 --- a/go.sum +++ b/go.sum @@ -1,8 +1,12 @@ github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= @@ -10,6 +14,8 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0= github.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= @@ -90,6 +96,8 @@ github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNc github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.2.1 h1:WlYJg71ODF0dVspZZCpYmoF1+U1Jjk9Rwd7pq6QmlCg= +github.com/redis/go-redis/v9 v9.2.1/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= diff --git a/webook/main.go b/webook/main.go index e30de60..e359deb 100644 --- a/webook/main.go +++ b/webook/main.go @@ -6,10 +6,12 @@ import ( "gitee.com/geekbang/basic-go/webook/internal/service" "gitee.com/geekbang/basic-go/webook/internal/web" "gitee.com/geekbang/basic-go/webook/internal/web/middleware" + "gitee.com/geekbang/basic-go/webook/pkg/ginx/middleware/ratelimit" "github.com/gin-contrib/cors" "github.com/gin-contrib/sessions" - "github.com/gin-contrib/sessions/redis" + "github.com/gin-contrib/sessions/cookie" "github.com/gin-gonic/gin" + "github.com/redis/go-redis/v9" "gorm.io/driver/mysql" "gorm.io/gorm" "strings" @@ -70,6 +72,13 @@ func initWebServer() *gin.Engine { println("这是我的 Middleware") }) + redisClient := redis.NewClient(&redis.Options{ + Addr: "localhost:6379", + }) + + server.Use(ratelimit.NewBuilder(redisClient, + time.Second, 1).Build()) + useJWT(server) //useSession(server) return server @@ -84,16 +93,16 @@ func useSession(server *gin.Engine) { login := &middleware.LoginMiddlewareBuilder{} // 存储数据的,也就是你 userId 存哪里 // 直接存 cookie - //store := cookie.NewStore([]byte("secret")) + store := cookie.NewStore([]byte("secret")) // 基于内存的实现 //store := memstore.NewStore([]byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgK"), // []byte("eF1`yQ9>yT1`tH1,sJ0.zD8;mZ9~nC6(")) - store, err := redis.NewStore(16, "tcp", - "localhost:6379", "", - []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgK"), - []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgA")) - if err != nil { - panic(err) - } + //store, err := redis.NewStore(16, "tcp", + // "localhost:6379", "", + // []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgK"), + // []byte("k6CswdUm75WKcbM68UQUuxVsHSpTCwgA")) + //if err != nil { + // panic(err) + //} server.Use(sessions.Sessions("ssid", store), login.CheckLogin()) } diff --git a/webook/pkg/ginx/middleware/ratelimit/builder.go b/webook/pkg/ginx/middleware/ratelimit/builder.go new file mode 100644 index 0000000..2a576ec --- /dev/null +++ b/webook/pkg/ginx/middleware/ratelimit/builder.go @@ -0,0 +1,64 @@ +package ratelimit + +import ( + _ "embed" + "fmt" + "github.com/gin-gonic/gin" + "github.com/redis/go-redis/v9" + "log" + "net/http" + "time" +) + +type Builder struct { + prefix string + cmd redis.Cmdable + interval time.Duration + // 阈值 + rate int +} + +//go:embed slide_window.lua +var luaScript string + +func NewBuilder(cmd redis.Cmdable, interval time.Duration, rate int) *Builder { + return &Builder{ + cmd: cmd, + prefix: "ip-limiter", + interval: interval, + rate: rate, + } +} + +func (b *Builder) Prefix(prefix string) *Builder { + b.prefix = prefix + return b +} + +func (b *Builder) Build() gin.HandlerFunc { + return func(ctx *gin.Context) { + limited, err := b.limit(ctx) + if err != nil { + log.Println(err) + // 这一步很有意思,就是如果这边出错了 + // 要怎么办? + // 保守做法:因为借助于 Redis 来做限流,那么 Redis 崩溃了,为了防止系统崩溃,直接限流 + ctx.AbortWithStatus(http.StatusInternalServerError) + // 激进做法:虽然 Redis 崩溃了,但是这个时候还是要尽量服务正常的用户,所以不限流 + // ctx.Next() + return + } + if limited { + log.Println(err) + ctx.AbortWithStatus(http.StatusTooManyRequests) + return + } + ctx.Next() + } +} + +func (b *Builder) limit(ctx *gin.Context) (bool, error) { + key := fmt.Sprintf("%s:%s", b.prefix, ctx.ClientIP()) + return b.cmd.Eval(ctx, luaScript, []string{key}, + b.interval.Milliseconds(), b.rate, time.Now().UnixMilli()).Bool() +} diff --git a/webook/pkg/ginx/middleware/ratelimit/slide_window.lua b/webook/pkg/ginx/middleware/ratelimit/slide_window.lua new file mode 100644 index 0000000..ee058b0 --- /dev/null +++ b/webook/pkg/ginx/middleware/ratelimit/slide_window.lua @@ -0,0 +1,26 @@ +-- 1, 2, 3, 4, 5, 6, 7 这是你的元素 +-- ZREMRANGEBYSCORE key1 0 6 +-- 7 执行完之后 + +-- 限流对象 +local key = KEYS[1] +-- 窗口大小 +local window = tonumber(ARGV[1]) +-- 阈值 +local threshold = tonumber( ARGV[2]) +local now = tonumber(ARGV[3]) +-- 窗口的起始时间 +local min = now - window + +redis.call('ZREMRANGEBYSCORE', key, '-inf', min) +local cnt = redis.call('ZCOUNT', key, '-inf', '+inf') +-- local cnt = redis.call('ZCOUNT', key, min, '+inf') +if cnt >= threshold then + -- 执行限流 + return "true" +else + -- 把 score 和 member 都设置成 now + redis.call('ZADD', key, now, now) + redis.call('PEXPIRE', key, window) + return "false" +end \ No newline at end of file -- Gitee From 43ff505d837cd30b371a03d9bae2a81a7e976fcf Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Sat, 30 Sep 2023 22:25:22 +0800 Subject: [PATCH 06/10] =?UTF-8?q?=E7=AC=AC=E4=BA=8C=E5=91=A8=20=E5=A2=9E?= =?UTF-8?q?=E5=BC=BA=E7=99=BB=E5=BD=95=E5=AE=89=E5=85=A8=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webook/internal/web/middleware/login_jwt.go | 9 ++++++++- webook/internal/web/user.go | 8 +++++--- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/webook/internal/web/middleware/login_jwt.go b/webook/internal/web/middleware/login_jwt.go index 24fe941..2150699 100644 --- a/webook/internal/web/middleware/login_jwt.go +++ b/webook/internal/web/middleware/login_jwt.go @@ -50,6 +50,13 @@ func (m *LoginJWTMiddlewareBuilder) CheckLogin() gin.HandlerFunc { return } + if uc.UserAgent != ctx.GetHeader("User-Agent") { + // 后期我们讲到了监控告警的时候,这个地方要埋点 + // 能够进来这个分支的,大概率是攻击者 + ctx.AbortWithStatus(http.StatusUnauthorized) + return + } + expireTime := uc.ExpiresAt // 不判定都可以 //if expireTime.Before(time.Now()) { @@ -58,7 +65,7 @@ func (m *LoginJWTMiddlewareBuilder) CheckLogin() gin.HandlerFunc { //} // 剩余过期时间 < 50s 就要刷新 if expireTime.Sub(time.Now()) < time.Second*50 { - uc.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute)) + uc.ExpiresAt = jwt.NewNumericDate(time.Now().Add(time.Minute * 5)) tokenStr, err = token.SignedString(web.JWTKey) ctx.Header("x-jwt-token", tokenStr) if err != nil { diff --git a/webook/internal/web/user.go b/webook/internal/web/user.go index da5d6d8..bd946bf 100644 --- a/webook/internal/web/user.go +++ b/webook/internal/web/user.go @@ -112,10 +112,11 @@ func (h *UserHandler) LoginJWT(ctx *gin.Context) { switch err { case nil: uc := UserClaims{ - Uid: u.Id, + Uid: u.Id, + UserAgent: ctx.GetHeader("User-Agent"), RegisteredClaims: jwt.RegisteredClaims{ // 1 分钟过期 - ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute)), + ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Minute * 5)), }, } token := jwt.NewWithClaims(jwt.SigningMethodHS512, uc) @@ -177,5 +178,6 @@ var JWTKey = []byte("k6CswdUm77WKcbM68UQUuxVsHSpTCwgK") type UserClaims struct { jwt.RegisteredClaims - Uid int64 + Uid int64 + UserAgent string } -- Gitee From 29a66ae0ad355b44a14aec7671a9b3fee6221658 Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Sun, 1 Oct 2023 15:37:59 +0800 Subject: [PATCH 07/10] =?UTF-8?q?k8s=20=E5=AE=89=E8=A3=85=20web=20?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- go.mod | 2 -- go.sum | 5 ----- webook/Dockerfile | 4 ++++ webook/Makefile | 7 +++++++ webook/main.go | 11 ++++++++--- webook/webook-deployment.yaml | 23 +++++++++++++++++++++++ webook/webook-service.yaml | 13 +++++++++++++ 7 files changed, 55 insertions(+), 10 deletions(-) create mode 100644 webook/Dockerfile create mode 100644 webook/Makefile create mode 100644 webook/webook-deployment.yaml create mode 100644 webook/webook-service.yaml diff --git a/go.mod b/go.mod index 027ef9a..0772ecf 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,6 @@ require ( ) require ( - github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff // indirect github.com/bytedance/sonic v1.9.1 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect @@ -30,7 +29,6 @@ require ( github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/goccy/go-json v0.10.2 // indirect - github.com/gomodule/redigo v2.0.0+incompatible // indirect github.com/gorilla/context v1.1.1 // indirect github.com/gorilla/securecookie v1.1.1 // indirect github.com/gorilla/sessions v1.2.1 // indirect diff --git a/go.sum b/go.sum index 0eed584..c5a2f2a 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff h1:RmdPFa+slIr4SCBg4st/l/vZWVe9QJKMXGO60Bxbe04= -github.com/boj/redistore v0.0.0-20180917114910-cd5dcc76aeff/go.mod h1:+RTT1BOk5P97fT2CiHkbFQwkK3mjsFAP6zCYV2aXtjw= github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= @@ -48,8 +46,6 @@ github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MG github.com/golang-jwt/jwt/v5 v5.0.0 h1:1n1XNM9hk7O9mnQoNBGolZvzebBQ7p93ULHRc28XJUE= github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/gomodule/redigo v2.0.0+incompatible h1:K/R+8tc58AaqLkqG2Ol3Qk+DR/TlNuhuh457pBFPtt0= -github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -57,7 +53,6 @@ github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8 github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.1.1/go.mod h1:8KCfur6+4Mqcc6S0FEfKuN15Vl5MgXW92AE8ovaJD0w= github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= diff --git a/webook/Dockerfile b/webook/Dockerfile new file mode 100644 index 0000000..082ceab --- /dev/null +++ b/webook/Dockerfile @@ -0,0 +1,4 @@ +FROM ubuntu:20.04 +COPY webook /app/webook +WORKDIR /app +CMD ["/app/webook"] \ No newline at end of file diff --git a/webook/Makefile b/webook/Makefile new file mode 100644 index 0000000..e5edc67 --- /dev/null +++ b/webook/Makefile @@ -0,0 +1,7 @@ +.PHONY: docker +docker: + @rm webook || true + @go mod tidy + @GOOS=linux GOARCH=arm go build -o webook . + @docker rmi -f flycash/webook:v0.0.1 + @docker build -t flycash/webook:v0.0.1 . \ No newline at end of file diff --git a/webook/main.go b/webook/main.go index e359deb..2a57f18 100644 --- a/webook/main.go +++ b/webook/main.go @@ -14,15 +14,20 @@ import ( "github.com/redis/go-redis/v9" "gorm.io/driver/mysql" "gorm.io/gorm" + "net/http" "strings" "time" ) func main() { - db := initDB() + //db := initDB() - server := initWebServer() - initUserHdl(db, server) + //server := initWebServer() + //initUserHdl(db, server) + server := gin.Default() + server.GET("/hello", func(ctx *gin.Context) { + ctx.String(http.StatusOK, "hello,启动成功了!") + }) server.Run(":8080") } diff --git a/webook/webook-deployment.yaml b/webook/webook-deployment.yaml new file mode 100644 index 0000000..780f6d8 --- /dev/null +++ b/webook/webook-deployment.yaml @@ -0,0 +1,23 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webook-record-service +spec: +# 三个副本 + replicas: 3 + selector: + matchLabels: + app: webook-record + template: + metadata: + labels: +# 这个 webook-record 一定要和前面的 selector 的 matchLabels 匹配上 + app: webook-record +# 这个是 Deployment 管理的 Pod 的模板 + spec: +# Pod 里面运行的所有的 container + containers: + - name: webook-record + image: flycash/webook:v0.0.1 + ports: + - containerPort: 8080 diff --git a/webook/webook-service.yaml b/webook/webook-service.yaml new file mode 100644 index 0000000..6c14889 --- /dev/null +++ b/webook/webook-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: webook-record +spec: + selector: + app: webook-record + ports: + - protocol: TCP + port: 98 + targetPort: 8080 + type: LoadBalancer + \ No newline at end of file -- Gitee From b39bc5eb3f2e9377b6496583050410071820a3bc Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Sun, 1 Oct 2023 20:26:43 +0800 Subject: [PATCH 08/10] =?UTF-8?q?k8s=E9=83=A8=E7=BD=B2=20MySQL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/dataSources.xml | 7 +++++ webook/webook-record-mysql-deployment.yaml | 35 ++++++++++++++++++++++ webook/webook-record-mysql-pv.yaml | 12 ++++++++ webook/webook-record-mysql-pvc.yaml | 11 +++++++ webook/webook-record-mysql-service.yaml | 13 ++++++++ 5 files changed, 78 insertions(+) create mode 100644 webook/webook-record-mysql-deployment.yaml create mode 100644 webook/webook-record-mysql-pv.yaml create mode 100644 webook/webook-record-mysql-pvc.yaml create mode 100644 webook/webook-record-mysql-service.yaml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml index 6f1b6ba..8bf2780 100644 --- a/.idea/dataSources.xml +++ b/.idea/dataSources.xml @@ -29,5 +29,12 @@ jdbc:mysql://localhost:13316/webook $ProjectFileDir$ + + mysql.8 + true + com.mysql.cj.jdbc.Driver + jdbc:mysql://localhost:3308/mysql + $ProjectFileDir$ + \ No newline at end of file diff --git a/webook/webook-record-mysql-deployment.yaml b/webook/webook-record-mysql-deployment.yaml new file mode 100644 index 0000000..77e2123 --- /dev/null +++ b/webook/webook-record-mysql-deployment.yaml @@ -0,0 +1,35 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webook-record-mysql + labels: + app: webook-record-mysql +spec: + replicas: 1 + selector: + matchLabels: + app: webook-record-mysql + template: + metadata: + name: webook-record-mysql + labels: + app: webook-record-mysql + spec: + containers: + - name: webook-record-mysql + image: mysql:8.0 + env: + - name: MYSQL_ROOT_PASSWORD + value: root + imagePullPolicy: IfNotPresent + ports: + - containerPort: 3306 + volumeMounts: + - mountPath: /var/lib/mysql + name: mysql-storage + restartPolicy: Always + volumes: + - name: mysql-storage + persistentVolumeClaim: +# PVC persistent volume claim + claimName: webook-mysql-pvc diff --git a/webook/webook-record-mysql-pv.yaml b/webook/webook-record-mysql-pv.yaml new file mode 100644 index 0000000..548547b --- /dev/null +++ b/webook/webook-record-mysql-pv.yaml @@ -0,0 +1,12 @@ +apiVersion: v1 +kind: PersistentVolume +metadata: + name: webook-mysql-pvc +spec: + storageClassName: record + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + hostPath: + path: "/mnt/data" \ No newline at end of file diff --git a/webook/webook-record-mysql-pvc.yaml b/webook/webook-record-mysql-pvc.yaml new file mode 100644 index 0000000..868a729 --- /dev/null +++ b/webook/webook-record-mysql-pvc.yaml @@ -0,0 +1,11 @@ +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: webook-mysql-pvc +spec: + storageClassName: record + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi \ No newline at end of file diff --git a/webook/webook-record-mysql-service.yaml b/webook/webook-record-mysql-service.yaml new file mode 100644 index 0000000..7676723 --- /dev/null +++ b/webook/webook-record-mysql-service.yaml @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: Service +metadata: + name: webook-record-mysql +spec: + selector: + app: webook-record-mysql + ports: + - protocol: TCP + port: 3308 + targetPort: 3306 + type: LoadBalancer + \ No newline at end of file -- Gitee From 084f73914bbd78a43703fada4556c0eb73062999 Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Sun, 1 Oct 2023 21:18:55 +0800 Subject: [PATCH 09/10] =?UTF-8?q?=E9=83=A8=E7=BD=B2=20redis=20=E5=92=8C=20?= =?UTF-8?q?nginx?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webook/webook-record-ingress.yaml | 20 +++++++++++++++++ webook/webook-record-redis-deployment.yaml | 25 ++++++++++++++++++++++ webook/webook-record-redis-service.yaml | 17 +++++++++++++++ webook/webook-service.yaml | 2 +- 4 files changed, 63 insertions(+), 1 deletion(-) create mode 100644 webook/webook-record-ingress.yaml create mode 100644 webook/webook-record-redis-deployment.yaml create mode 100644 webook/webook-record-redis-service.yaml diff --git a/webook/webook-record-ingress.yaml b/webook/webook-record-ingress.yaml new file mode 100644 index 0000000..46063f8 --- /dev/null +++ b/webook/webook-record-ingress.yaml @@ -0,0 +1,20 @@ +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: webook-record-ingress + annotations: + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: localhost + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: webook-record + port: + number: 98 + \ No newline at end of file diff --git a/webook/webook-record-redis-deployment.yaml b/webook/webook-record-redis-deployment.yaml new file mode 100644 index 0000000..2b5bdbb --- /dev/null +++ b/webook/webook-record-redis-deployment.yaml @@ -0,0 +1,25 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: webook-record-redis + labels: + app: webook-record-redis +spec: + replicas: 1 + selector: + matchLabels: + app: webook-record-redis + template: + metadata: + name: webook-record-redis + labels: + app: webook-record-redis + spec: + containers: + - name: webook-record-redis + image: redis:latest + imagePullPolicy: IfNotPresent + ports: + - containerPort: 6379 + restartPolicy: Always + \ No newline at end of file diff --git a/webook/webook-record-redis-service.yaml b/webook/webook-record-redis-service.yaml new file mode 100644 index 0000000..8277040 --- /dev/null +++ b/webook/webook-record-redis-service.yaml @@ -0,0 +1,17 @@ +apiVersion: v1 +kind: Service +metadata: + name: webook-record-redis +spec: + selector: + app: webook-record-redis + ports: + - protocol: TCP +# k8s 内部访问接口 + port: 6379 +# 外部访问端口,必须在 30000-32767 + nodePort: 31379 +# pod 暴露的端口 + targetPort: 6379 + type: NodePort + \ No newline at end of file diff --git a/webook/webook-service.yaml b/webook/webook-service.yaml index 6c14889..3d564fe 100644 --- a/webook/webook-service.yaml +++ b/webook/webook-service.yaml @@ -9,5 +9,5 @@ spec: - protocol: TCP port: 98 targetPort: 8080 - type: LoadBalancer + type: ClusterIP \ No newline at end of file -- Gitee From b78989c2e1b8f3d1b901ae8547f39979864caa1d Mon Sep 17 00:00:00 2001 From: Deng Ming Date: Mon, 2 Oct 2023 09:32:04 +0800 Subject: [PATCH 10/10] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E5=91=A8=20=E9=9B=86?= =?UTF-8?q?=E6=88=90=E5=85=A8=E9=83=A8=E9=83=A8=E7=BD=B2=E5=B9=B6=E5=90=AF?= =?UTF-8?q?=E5=8A=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- webook/Makefile | 2 +- webook/config/dev.go | 12 ++++++++++++ webook/config/k8s.go | 12 ++++++++++++ webook/config/types.go | 14 ++++++++++++++ webook/main.go | 13 +++++++------ 5 files changed, 46 insertions(+), 7 deletions(-) create mode 100644 webook/config/dev.go create mode 100644 webook/config/k8s.go create mode 100644 webook/config/types.go diff --git a/webook/Makefile b/webook/Makefile index e5edc67..e0c687b 100644 --- a/webook/Makefile +++ b/webook/Makefile @@ -2,6 +2,6 @@ docker: @rm webook || true @go mod tidy - @GOOS=linux GOARCH=arm go build -o webook . + @GOOS=linux GOARCH=arm go build -tags=k8s -o webook . @docker rmi -f flycash/webook:v0.0.1 @docker build -t flycash/webook:v0.0.1 . \ No newline at end of file diff --git a/webook/config/dev.go b/webook/config/dev.go new file mode 100644 index 0000000..f3085e9 --- /dev/null +++ b/webook/config/dev.go @@ -0,0 +1,12 @@ +//go:build !k8s + +package config + +var Config = config{ + DB: DBConfig{ + DSN: "root:root@tcp(localhost:13316)/webook", + }, + Redis: RedisConfig{ + Addr: "localhost:6379", + }, +} diff --git a/webook/config/k8s.go b/webook/config/k8s.go new file mode 100644 index 0000000..b0bf5bc --- /dev/null +++ b/webook/config/k8s.go @@ -0,0 +1,12 @@ +//go:build k8s + +package config + +var Config = config{ + DB: DBConfig{ + DSN: "root:root@tcp(webook-record-mysql:3308)/webook", + }, + Redis: RedisConfig{ + Addr: "webook-record-redis:6379", + }, +} diff --git a/webook/config/types.go b/webook/config/types.go new file mode 100644 index 0000000..a2316f7 --- /dev/null +++ b/webook/config/types.go @@ -0,0 +1,14 @@ +package config + +type config struct { + DB DBConfig + Redis RedisConfig +} + +type DBConfig struct { + DSN string +} + +type RedisConfig struct { + Addr string +} diff --git a/webook/main.go b/webook/main.go index 2a57f18..d3b7302 100644 --- a/webook/main.go +++ b/webook/main.go @@ -1,6 +1,7 @@ package main import ( + "gitee.com/geekbang/basic-go/webook/config" "gitee.com/geekbang/basic-go/webook/internal/repository" "gitee.com/geekbang/basic-go/webook/internal/repository/dao" "gitee.com/geekbang/basic-go/webook/internal/service" @@ -20,11 +21,11 @@ import ( ) func main() { - //db := initDB() + db := initDB() - //server := initWebServer() - //initUserHdl(db, server) - server := gin.Default() + server := initWebServer() + initUserHdl(db, server) + //server := gin.Default() server.GET("/hello", func(ctx *gin.Context) { ctx.String(http.StatusOK, "hello,启动成功了!") }) @@ -40,7 +41,7 @@ func initUserHdl(db *gorm.DB, server *gin.Engine) { } func initDB() *gorm.DB { - db, err := gorm.Open(mysql.Open("root:root@tcp(localhost:13316)/webook")) + db, err := gorm.Open(mysql.Open(config.Config.DB.DSN)) if err != nil { panic(err) } @@ -78,7 +79,7 @@ func initWebServer() *gin.Engine { }) redisClient := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", + Addr: config.Config.Redis.Addr, }) server.Use(ratelimit.NewBuilder(redisClient, -- Gitee