網站首頁 編程語言 正文
前言摘要
昨天和同事merge代碼又遇到了很多沖突,發現之前有些方法寫的參數不規范,沒有傳入Context,不方便進行鏈路追蹤。他在review項目代碼,基本把項目中的方法都加了Context參數。
今天就為大家介紹一下Context的使用,告訴大家Context是什么?怎么用?為什么要用Context以及使用中的小技巧和注意問題。
Context是什么?
Context
指的是標準庫的context.Context
,是一個接口對象,常用于異步IO
控制以及上下文流程變量的傳遞。
本文將要介紹的是Context
如何優雅的在業務流程中進行變量的傳遞,以及為什么需要要進行變量的傳遞。
為什么需要Context?
在Go
的執行流程中,特別是HTTP/RPC
執行流程中,不存在”全局變量”獲取請求參數的方式,只有通過上下文Context
變量傳遞到后續流程的方法中。
Context是如何實現共享變量的?
而Context
上下文變量即包含了所有需要傳遞的共享變量。
并且該Context
中的共享變量是事先約定的,并且往往存儲為對象指針形式。
如何使用?
通過Context
上下文共享變量非常簡單,下面通過示例帶大家了解一下如何傳遞和使用通用的共享變量。
一、結構定義
上下文對象中往往存儲一些需要共享的變量,這些變量通常使用結構化的對象來存儲,以方便維護。
例如,我們在model
定義一個上下文中的共享變量:
const ( // 上下文變量存儲鍵名,前后端系統共享 ContextKey = "ContextKey" ) // 請求上下文結構 type Context struct { Session *ghttp.Session // 當前Session管理對象 User *ContextUser // 上下文用戶信息 Data g.Map // 自定KV變量,業務模塊根據需要設置,不固定 } // 請求上下文中的用戶信息 type ContextUser struct { Id uint // 用戶ID Passport string // 用戶賬號 Nickname string // 用戶名稱 Avatar string // 用戶頭像 }
介紹
model.ContextKey
常量表示存儲在context.Context
上下文變量中的鍵名,該鍵名用于從傳遞的context.Context
變量中存儲/獲取業務自定義的共享變量。
model.Context
結構體中的Session
表示當前請求的Session
對象,在GoFrame
框架中每個HTTP
請求對象中都會有一個空的Session
對象,該對象采用了懶初始化設計,只有在真正執行讀寫操作時才會初始化。
model.Context
結構體中的User
表示當前登錄的用戶基本信息,只有在用戶登錄后才有數據,否則是nil
。
model.Context
結構體中的Data
屬性用于存儲自定義的KV
變量,因此一般來說開發者無需再往context.Context
上下文變量中增加自定義的鍵值對,而是直接使用model.``Context
對象的這個Data
屬性即可。
二、邏輯封裝
由于該上下文對象也是和業務邏輯相關的,因此我們需要通過service
對象將上下文變量封裝起來以方便其他模塊使用。
// 上下文管理服務 var Context = new(contextService) type contextService struct{} // 初始化上下文對象指針到上下文對象中,以便后續的請求流程中可以修改。 func (s *contextService) Init(r *ghttp.Request, customCtx *model.Context) { r.SetCtxVar(model.ContextKey, customCtx) } // 獲得上下文變量,如果沒有設置,那么返回nil func (s *contextService) Get(ctx context.Context) *model.Context { value := ctx.Value(model.ContextKey) if value == nil { return nil } if localCtx, ok := value.(*model.Context); ok { return localCtx } return nil } // 將上下文信息設置到上下文請求中,注意是完整覆蓋 func (s *contextService) SetUser(ctx context.Context, ctxUser *model.ContextUser) { s.Get(ctx).User = ctxUser }
Tips
在架構設計中,在哪個場景下設置Context是非常關鍵的。
上下文的變量必須在請求一開始便注入到請求流程中,以便于其他方法調用,所以在中間件中來實現是非常優雅的選擇。
我們來看下面的介紹:
三、上下文變量注入
在HTTP
請求中我們可以使用GoFrame
的中間件來實現。
在GRPC
請求中我們也可以使用攔截器來實現。
在service
層的middleware
管理對象中,我們可以這樣來定義:
// 自定義上下文對象 func (s *middlewareService) Ctx(r *ghttp.Request) { // 初始化,務必最開始執行 customCtx := &model.Context{ Session: r.Session, Data: make(g.Map), } service.Context.Init(r, customCtx) if userEntity := Session.GetUser(r.Context()); userEntity != nil { customCtx.User = &model.ContextUser{ Id: userEntity.Id, Passport: userEntity.Passport, Nickname: userEntity.Nickname, Avatar: userEntity.Avatar, } } // 將自定義的上下文對象傳遞到模板變量中使用 r.Assigns(g.Map{ "Context": customCtx, }) // 執行下一步請求邏輯 r.Middleware.Next() }
這個中間件初始化了用戶執行流程共享的對象,并且存儲到context.Context
變量中的對象是指針類型*model.Context
。
這樣做的好處是:任何一個地方獲取到這個指針,不僅可以獲取到里面的數據,而且能夠直接修改里面的數據。
TIPS
如果Session
中存在用戶登錄后的存儲信息,那么也會將需要共享的用戶基本信息寫入到*model.Context
中。
四、上下文變量使用
方法定義
方法定義的第一個輸入參數往往預留給context.Context
類型參數使用,以便接受上下文變量,特別是service
層的方法。
例如:
// 執行用戶登錄 func (s *userService) Login(ctx context.Context, loginReq *define.UserServiceLoginReq) error { ... } // 查詢內容列表 func (s *contentService) GetList(ctx context.Context, r *define.ContentServiceGetListReq) (*define.ContentServiceGetListRes, error) { ... } // 創建回復內容 func (s *replyService) Create(ctx context.Context, r *define.ReplyServiceCreateReq) error { ... }
TIPS
另外一個好習慣是:方法的最后一個返回參數往往是error
類型。如果確定方法內部永不會產生error
,那么可以忽略。
Context對象獲取
通過service
中封裝的以下方法,將context.Context
上下文變量傳遞進去即可。
context.Context
上下文變量在GoFrame
框架的HTTP
請求中可以通過r.Context()
方法獲取。
在GRPC
請求中,編譯生成的pb
文件中執行方法的第一個參數即固定是context.Context
。
service.Context.Get(ctx)
自定義Key-Value
我們可以通過以下方式設置/獲取自定義的key-value
鍵值對。
// 設置自定義鍵值對 service.Context.Get(ctx).Data[key] = value ...go // 獲取自定義鍵值對 service.Context.Get(ctx).Data[key]
五、注意問題
- 上下文變量只傳遞必須的鏈路參數數據,不要什么參數都往里面塞。特別是一些方法參數、傳參的數據,千萬不能往上下文里面塞,而應當顯示的傳遞方法參數。
- 上下文變量僅用作運行時臨時使用,不可做持久化存儲長期使用。
總結
這篇文章詳細的為大家介紹了GoFrame上下文對象Context的知識點:
Context的作用:在業務流程中進行變量的共享。
原文鏈接:https://juejin.cn/post/7113118741776793636
相關推薦
- 2022-08-06 GIN的路由以及傳參問題_Golang
- 2022-05-02 Python實現連點器的示例代碼_python
- 2022-01-29 git 本地,遠程做了不同的修改,同步方法
- 2022-11-02 Kotlin協程的啟動方式介紹_Android
- 2022-04-02 Android?studio實現日期?、時間選擇器與進度條_Android
- 2022-02-27 VS2010引用全局變量報錯:無法解析的外部符號
- 2023-09-17 el-table嵌入輸入框下拉框按鈕等等
- 2023-01-19 Android?任務棧機制詳解_Android
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支