優(yōu)雅使用GoFrame共享變量Context示例詳解
前言摘要
昨天和同事merge代碼又遇到了很多沖突,發(fā)現(xiàn)之前有些方法寫的參數(shù)不規(guī)范,沒(méi)有傳入Context,不方便進(jìn)行鏈路追蹤。他在review項(xiàng)目代碼,基本把項(xiàng)目中的方法都加了Context參數(shù)。
今天就為大家介紹一下Context的使用,告訴大家Context是什么?怎么用?為什么要用Context以及使用中的小技巧和注意問(wèn)題。
Context是什么?
Context
指的是標(biāo)準(zhǔn)庫(kù)的context.Context
,是一個(gè)接口對(duì)象,常用于異步IO
控制以及上下文流程變量的傳遞。
本文將要介紹的是Context
如何優(yōu)雅的在業(yè)務(wù)流程中進(jìn)行變量的傳遞,以及為什么需要要進(jìn)行變量的傳遞。
為什么需要Context?
在Go
的執(zhí)行流程中,特別是HTTP/RPC
執(zhí)行流程中,不存在”全局變量”獲取請(qǐng)求參數(shù)的方式,只有通過(guò)上下文Context
變量傳遞到后續(xù)流程的方法中。
Context是如何實(shí)現(xiàn)共享變量的?
而Context
上下文變量即包含了所有需要傳遞的共享變量。
并且該Context
中的共享變量是事先約定的,并且往往存儲(chǔ)為對(duì)象指針形式。
如何使用?
通過(guò)Context
上下文共享變量非常簡(jiǎn)單,下面通過(guò)示例帶大家了解一下如何傳遞和使用通用的共享變量。
一、結(jié)構(gòu)定義
上下文對(duì)象中往往存儲(chǔ)一些需要共享的變量,這些變量通常使用結(jié)構(gòu)化的對(duì)象來(lái)存儲(chǔ),以方便維護(hù)。
例如,我們?cè)?code>model定義一個(gè)上下文中的共享變量:
const ( // 上下文變量存儲(chǔ)鍵名,前后端系統(tǒng)共享 ContextKey = "ContextKey" ) // 請(qǐng)求上下文結(jié)構(gòu) type Context struct { Session *ghttp.Session // 當(dāng)前Session管理對(duì)象 User *ContextUser // 上下文用戶信息 Data g.Map // 自定KV變量,業(yè)務(wù)模塊根據(jù)需要設(shè)置,不固定 } // 請(qǐng)求上下文中的用戶信息 type ContextUser struct { Id uint // 用戶ID Passport string // 用戶賬號(hào) Nickname string // 用戶名稱 Avatar string // 用戶頭像 }
介紹
model.ContextKey
常量表示存儲(chǔ)在context.Context
上下文變量中的鍵名,該鍵名用于從傳遞的context.Context
變量中存儲(chǔ)/獲取業(yè)務(wù)自定義的共享變量。
model.Context
結(jié)構(gòu)體中的Session
表示當(dāng)前請(qǐng)求的Session
對(duì)象,在GoFrame
框架中每個(gè)HTTP
請(qǐng)求對(duì)象中都會(huì)有一個(gè)空的Session
對(duì)象,該對(duì)象采用了懶初始化設(shè)計(jì),只有在真正執(zhí)行讀寫操作時(shí)才會(huì)初始化。
model.Context
結(jié)構(gòu)體中的User
表示當(dāng)前登錄的用戶基本信息,只有在用戶登錄后才有數(shù)據(jù),否則是nil
。
model.Context
結(jié)構(gòu)體中的Data
屬性用于存儲(chǔ)自定義的KV
變量,因此一般來(lái)說(shuō)開發(fā)者無(wú)需再往context.Context
上下文變量中增加自定義的鍵值對(duì),而是直接使用model.``Context
對(duì)象的這個(gè)Data
屬性即可。
二、邏輯封裝
由于該上下文對(duì)象也是和業(yè)務(wù)邏輯相關(guān)的,因此我們需要通過(guò)service
對(duì)象將上下文變量封裝起來(lái)以方便其他模塊使用。
// 上下文管理服務(wù) var Context = new(contextService) type contextService struct{} // 初始化上下文對(duì)象指針到上下文對(duì)象中,以便后續(xù)的請(qǐng)求流程中可以修改。 func (s *contextService) Init(r *ghttp.Request, customCtx *model.Context) { r.SetCtxVar(model.ContextKey, customCtx) } // 獲得上下文變量,如果沒(méi)有設(shè)置,那么返回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 } // 將上下文信息設(shè)置到上下文請(qǐng)求中,注意是完整覆蓋 func (s *contextService) SetUser(ctx context.Context, ctxUser *model.ContextUser) { s.Get(ctx).User = ctxUser }
Tips
在架構(gòu)設(shè)計(jì)中,在哪個(gè)場(chǎng)景下設(shè)置Context是非常關(guān)鍵的。
上下文的變量必須在請(qǐng)求一開始便注入到請(qǐng)求流程中,以便于其他方法調(diào)用,所以在中間件中來(lái)實(shí)現(xiàn)是非常優(yōu)雅的選擇。
我們來(lái)看下面的介紹:
三、上下文變量注入
在HTTP
請(qǐng)求中我們可以使用GoFrame
的中間件來(lái)實(shí)現(xiàn)。
在GRPC
請(qǐng)求中我們也可以使用攔截器來(lái)實(shí)現(xiàn)。
在service
層的middleware
管理對(duì)象中,我們可以這樣來(lái)定義:
// 自定義上下文對(duì)象 func (s *middlewareService) Ctx(r *ghttp.Request) { // 初始化,務(wù)必最開始執(zhí)行 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, } } // 將自定義的上下文對(duì)象傳遞到模板變量中使用 r.Assigns(g.Map{ "Context": customCtx, }) // 執(zhí)行下一步請(qǐng)求邏輯 r.Middleware.Next() }
這個(gè)中間件初始化了用戶執(zhí)行流程共享的對(duì)象,并且存儲(chǔ)到context.Context
變量中的對(duì)象是指針類型*model.Context
。
這樣做的好處是:任何一個(gè)地方獲取到這個(gè)指針,不僅可以獲取到里面的數(shù)據(jù),而且能夠直接修改里面的數(shù)據(jù)。
TIPS
如果Session
中存在用戶登錄后的存儲(chǔ)信息,那么也會(huì)將需要共享的用戶基本信息寫入到*model.Context
中。
四、上下文變量使用
方法定義
方法定義的第一個(gè)輸入?yún)?shù)往往預(yù)留給context.Context
類型參數(shù)使用,以便接受上下文變量,特別是service
層的方法。
例如:
// 執(zhí)行用戶登錄 func (s *userService) Login(ctx context.Context, loginReq *define.UserServiceLoginReq) error { ... } // 查詢內(nèi)容列表 func (s *contentService) GetList(ctx context.Context, r *define.ContentServiceGetListReq) (*define.ContentServiceGetListRes, error) { ... } // 創(chuàng)建回復(fù)內(nèi)容 func (s *replyService) Create(ctx context.Context, r *define.ReplyServiceCreateReq) error { ... }
TIPS
另外一個(gè)好習(xí)慣是:方法的最后一個(gè)返回參數(shù)往往是error
類型。如果確定方法內(nèi)部永不會(huì)產(chǎn)生error
,那么可以忽略。
Context對(duì)象獲取
通過(guò)service
中封裝的以下方法,將context.Context
上下文變量傳遞進(jìn)去即可。
context.Context
上下文變量在GoFrame
框架的HTTP
請(qǐng)求中可以通過(guò)r.Context()
方法獲取。
在GRPC
請(qǐng)求中,編譯生成的pb
文件中執(zhí)行方法的第一個(gè)參數(shù)即固定是context.Context
。
service.Context.Get(ctx)
自定義Key-Value
我們可以通過(guò)以下方式設(shè)置/獲取自定義的key-value
鍵值對(duì)。
// 設(shè)置自定義鍵值對(duì) service.Context.Get(ctx).Data[key] = value ...go // 獲取自定義鍵值對(duì) service.Context.Get(ctx).Data[key]
五、注意問(wèn)題
- 上下文變量只傳遞必須的鏈路參數(shù)數(shù)據(jù),不要什么參數(shù)都往里面塞。特別是一些方法參數(shù)、傳參的數(shù)據(jù),千萬(wàn)不能往上下文里面塞,而應(yīng)當(dāng)顯示的傳遞方法參數(shù)。
- 上下文變量?jī)H用作運(yùn)行時(shí)臨時(shí)使用,不可做持久化存儲(chǔ)長(zhǎng)期使用。
總結(jié)
這篇文章詳細(xì)的為大家介紹了GoFrame上下文對(duì)象Context的知識(shí)點(diǎn):
Context的作用:在業(yè)務(wù)流程中進(jìn)行變量的共享。
Context的結(jié)構(gòu)定義、邏輯封裝、如何在中間件中注入、如何通過(guò)Context設(shè)置值和取值、Context如何自定義key-value、以及在項(xiàng)目開發(fā)中使用的注意問(wèn)題,更多關(guān)于GoFrame共享變量Context的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- bug解決Failed_to_execute_goal_org.springframework
- Django Rest Framework框架構(gòu)建復(fù)雜API技能詳解
- Android FrameWork之Zygote啟動(dòng)示例詳解
- GoFrame實(shí)現(xiàn)順序性校驗(yàn)示例詳解
- GoFrame錯(cuò)誤處理常用方法及錯(cuò)誤碼使用示例
- GoFrame框架數(shù)據(jù)校驗(yàn)之校驗(yàn)結(jié)果Error接口對(duì)象
- GoFrame基于性能測(cè)試得知grpool使用場(chǎng)景
- goframe重寫FastAdmin后端實(shí)現(xiàn)實(shí)例詳解
相關(guān)文章
Go語(yǔ)言dolphinscheduler任務(wù)調(diào)度處理
這篇文章主要為大家介紹了Go語(yǔ)言dolphinscheduler任務(wù)調(diào)度處理,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-06-06Go語(yǔ)言實(shí)現(xiàn)百萬(wàn)級(jí)WebSocket連接架構(gòu)設(shè)計(jì)及服務(wù)優(yōu)化
本文將詳細(xì)介紹如何在Go中構(gòu)建一個(gè)能夠支持百萬(wàn)級(jí)WebSocket連接的服務(wù),包括系統(tǒng)架構(gòu)設(shè)計(jì)、性能優(yōu)化策略以及具體的實(shí)現(xiàn)步驟和代碼示例2024-01-01詳解golang consul-grpc 服務(wù)注冊(cè)與發(fā)現(xiàn)
這篇文章主要介紹了詳解golang consul-grpc 服務(wù)注冊(cè)與發(fā)現(xiàn),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-06-06go mock server的簡(jiǎn)易實(shí)現(xiàn)示例
這篇文章主要為大家介紹了go mock server的簡(jiǎn)易實(shí)現(xiàn)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-07-07詳解Go語(yǔ)言中new和make關(guān)鍵字的區(qū)別
本篇文章來(lái)介紹一道非常常見(jiàn)的面試題,到底有多常見(jiàn)呢?可能很多面試的開場(chǎng)白就是由此開始的。那就是 new 和 make 這兩個(gè)內(nèi)置函數(shù)的區(qū)別,希望對(duì)大家有所幫助2023-03-03Golang監(jiān)聽日志文件并發(fā)送到kafka中
這篇文章主要介紹了Golang監(jiān)聽日志文件并發(fā)送到kafka中,日志收集項(xiàng)目的準(zhǔn)備中,本文主要講的是利用golang的tail庫(kù),監(jiān)聽日志文件的變動(dòng),將日志信息發(fā)送到kafka中?,需要的朋友可以參考一下2022-04-04