日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

優雅使用GoFrame共享變量Context示例詳解_Golang

作者:王中陽Go ? 更新時間: 2022-08-22 編程語言

前言摘要

昨天和同事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

欄目分類
最近更新