使用wire重構商品微服務

一.wire簡介

Wire 是一個輕巧的Golang依賴注入工具。它由Go Cloud團隊開發,通過自動生成代碼的方式在編譯期完成依賴注入。

依賴注入是保持軟件 “低耦合、易維護” 的重要設計準則之一。

此準則被廣泛應用在各種開發平臺之中,有很多與之相關的優秀工具。

其中最著名的當屬 Spring,Spring IOC 作為框架的核心功能對Spring的發展到今天統治地位起了決定性作用。

依賴注入很重要,所以Golang社區中早已有人開發了相關工具, 比如來自Uber 的 dig 、來自Facebook 的 inject 。他們都通過反射機制實現了運行時依賴注入。

二.快速使用

2.1安裝

安裝很簡單,運行 go get github.com/google/wire/cmd/wire 之后, wire 命令行工具 將被安裝到 $GOPATH/bin 。只要確保 $GOPATH/bin 在 $PATH 中, wire 命令就可以在任何目錄調用了。安裝成功后運行如下命令
在這里插入圖片描述

2.2快速入門

設計一個程序,其中 Event依賴Greeter,Greeter依賴Message

package mainimport ("fmt""github.com/pkg/errors""time"
)type Message stringfunc NewMessage(phrase string) Message {return Message(phrase)
}type Greeter struct {Message Message
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}func (g Greeter) Greet() Message {return g.Message
}type Event struct {Greeter Greeter // <- adding a Greeter field
}func NewEvent(g Greeter) (Event, error) {if time.Now().Unix()%2 == 0 {return Event{}, errors.New("could not create event: event greeter is grumpy")}return Event{Greeter: g}, nil
}func (e Event) Start() {msg := e.Greeter.Greet()fmt.Println(msg)
}

如果運行Event需要逐個構建依賴,代碼如下

func main() {message := NewMessage("lisus2000")greeter := NewGreeter(message)event := NewEvent(greeter)event.Start()
}

在此之前先介紹兩個概念

Provider
Provider 你可以把它理解成工廠函數,這個函數的入參是依賴的屬性,返回值為新一個新的類型實例

如下所示都是 provider 函數,在實際使用的時候,往往是一些簡單的工廠函數,這個函數不會太復雜。

func NewMessage() Message {return Message("Hi there!")
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}

Injector

我們常常在 wire.go 文件中定義 injector ,injector也是一個普通函數,它用來聲明組件之間的依賴關系

如下代碼,我們把Event、Greeter、Message 的工廠函數(provider)一股腦塞入wire.Build()中,代表著構建 Event依賴Greeter、Message。我們不必關心Greeter、Message之間的依賴關系,wire會幫我們處理

func InitializeEvent() Event {wire.Build(NewEvent, NewGreeter, NewMessage)return Event{}
}

編寫wire.go文件

//go:build wireinject
// +build wireinjectpackage mainimport "github.com/google/wire"var wireSet = wire.NewSet(wire.Struct(new(Greeter), "Message"), NewMessage)func InitializeEvent(phrase string) (Event, error) {//我們常常在 wire.go 文件中定義 injector ,injector也是一個普通函數,它用來聲明組件之間的依賴關系//如下代碼,我們把Event、Greeter、Message 的工廠函數(provider)一股腦塞入wire.Build()中,//代表著構建 Event依賴Greeter、Message。我們不必關心Greeter、Message之間的依賴關系,wire會幫我們處理panic(wire.Build(NewEvent, wireSet))//return Event{}, nil
}

注:使用wire生成代碼時,要在代碼上加以下

可以在wire.go第一行加入 //+build wireinject (與//go:build wireinject等效)注釋,確保了這個文件在我們正常編譯的時候不會被引用

在帶有wire.go目錄下運行wire命令,就會生成wire_gen.go文件,如下圖所示

在這里插入圖片描述

在這里插入圖片描述

完整的main.go文件如下

package mainimport ("fmt""github.com/pkg/errors""time"
)type Message stringfunc NewMessage(phrase string) Message {return Message(phrase)
}type Greeter struct {Message Message
}func NewGreeter(m Message) Greeter {return Greeter{Message: m}
}func (g Greeter) Greet() Message {return g.Message
}type Event struct {Greeter Greeter // <- adding a Greeter field
}func NewEvent(g Greeter) (Event, error) {if time.Now().Unix()%2 == 0 {return Event{}, errors.New("could not create event: event greeter is grumpy")}return Event{Greeter: g}, nil
}func (e Event) Start() {msg := e.Greeter.Greet()fmt.Println(msg)
}func main() {event, _ := InitializeEvent("hello")event.Start()
}

wire的進階,詳見https://blog.csdn.net/weixin_50071922/article/details/133278161

三.基于wire構建商品微服務

3.1編寫商品微服務提供者
func NewGoodsAppWire(logOpts *log.Options, registrar registry.Registrar, serverOpts *options.ServerOptions,rpcServer *rpcserver.Server) (*gapp.App, error) {//初始化loglog.Init(logOpts)defer log.Flush()return gapp.New(gapp.WithName(serverOpts.Name),gapp.WithRPCServer(rpcServer),gapp.WithRegistrar(registrar),), nil
}
func NewRegistrar(registry *options.RegistryOptions) registry.Registrar {c := api.DefaultConfig()c.Address = registry.Addressc.Scheme = registry.Schemeclient, err := api.NewClient(c)if err != nil {panic(err)}return consul.New(client, consul.WithHealthCheck(true))
}
func NewGoodsRPCServerWire(telemetry *options.TelemetryOptions,serverOpts *options.ServerOptions, gserver gpb.GoodsServer) (*rpcserver.Server, error) {// 初始化 open-telemetry 的 exportertrace.InitAgent(trace.Options{Name:     telemetry.Name,Endpoint: telemetry.Endpoint,Sampler:  telemetry.Sampler,Batcher:  telemetry.Batcher,})// 這里會根據 endpoint 為單元注冊 trace 服務的實例rpcAddr := fmt.Sprintf("%s:%d", serverOpts.Host, serverOpts.Port)var opts []rpcserver.ServerOptionopts = append(opts, rpcserver.WithAddress(rpcAddr))if serverOpts.EnableLimit {opts = append(opts, rpcserver.WithUnaryInterceptor(grpc.NewUnaryServerInterceptor()))}grpcServer := rpcserver.NewServer(opts...)gpb.RegisterGoodsServer(grpcServer.Server, gserver)return grpcServer, nil
}
func NewGoodsServerWire(srv v1.ServiceFactory) proto.GoodsServer {return &goodsServer{srv: srv,}
}
func NewGoodsServiceWire(data v1.DataFactory, dataSearch v12.SearchFactory) ServiceFactory {return &service{data:       data,dataSearch: dataSearch,}
}
func GetDBFactoryOr(mysqlOpts *options.MySQLOptions) (v1.DataFactory, error) {if mysqlOpts == nil && dbFactory == nil {return nil, errors.WithCode(code.ErrConnectDB, "failed to get mysql store factory")}var err erroronce.Do(func() {dsn := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=True&loc=Local",mysqlOpts.Username,mysqlOpts.Password,mysqlOpts.Host,mysqlOpts.Port,mysqlOpts.Database)//希望大家自己可以去封裝loggernewLogger := logger.New(log.New(os.Stdout, "\r\n", log.LstdFlags), // io writer(日志輸出的目標,前綴和日志包含的內容——譯者注)logger.Config{SlowThreshold:             time.Second,                         // 慢 SQL 閾值LogLevel:                  logger.LogLevel(mysqlOpts.LogLevel), // 日志級別IgnoreRecordNotFoundError: true,                                // 忽略ErrRecordNotFound(記錄未找到)錯誤Colorful:                  false,                               // 禁用彩色打印},)db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{Logger: newLogger,})if err != nil {return}sqlDB, _ := db.DB()dbFactory = &mysqlFactory{db: db,}//允許連接多少個mysqlsqlDB.SetMaxOpenConns(mysqlOpts.MaxOpenConnections)//允許最大的空閑的連接數sqlDB.SetMaxIdleConns(mysqlOpts.MaxIdleConnections)//重用連接的最大時長sqlDB.SetConnMaxLifetime(mysqlOpts.MaxConnectionLifetime)})if dbFactory == nil || err != nil {return nil, errors.WithCode(code.ErrConnectDB, "failed to get mysql store factory")}return dbFactory, nil
}
func GetSearchFactoryOr(opts *options.EsOptions) (v12.SearchFactory, error) {if opts == nil && searchFactory == nil {return nil, errors.New("failed to get es client")}once.Do(func() {esOpt := db.EsOptions{Host: opts.Host,Port: opts.Port,}esClient, err := db.NewEsClient(&esOpt)if err != nil {return}searchFactory = &dataSearch{esClient: esClient,}})if searchFactory == nil {return nil, errors.New("failed to get es client")}return searchFactory, nil
}
3.2編寫wire.go文件
//go:build wireinject
// +build wireinjectpackage srvimport ("github.com/google/wire"v1 "mxshop/app/goods/srv/internal/controller/v1""mxshop/app/goods/srv/internal/data/v1/data_search/v1/es""mxshop/app/goods/srv/internal/data/v1/db"v12 "mxshop/app/goods/srv/internal/service/v1""mxshop/app/pkg/options"gapp "mxshop/gmicro/app""mxshop/pkg/log"
)func initApp(*log.Options, *options.ServerOptions, *options.RegistryOptions,*options.TelemetryOptions, *options.MySQLOptions, *options.EsOptions) (*gapp.App, error) {wire.Build(NewGoodsAppWire,NewRegistrar,NewGoodsRPCServerWire,v1.NewGoodsServerWire,v12.NewGoodsServiceWire,db.GetDBFactoryOr,es.GetSearchFactoryOr,)return &gapp.App{}, nil
}

3.3生成wire_gen.go文件

在這里插入圖片描述

生成后的文件如下:

// Code generated by Wire. DO NOT EDIT.//go:generate go run github.com/google/wire/cmd/wire
//go:build !wireinject
// +build !wireinjectpackage srvimport (v1_2 "mxshop/app/goods/srv/internal/controller/v1""mxshop/app/goods/srv/internal/data/v1/data_search/v1/es""mxshop/app/goods/srv/internal/data/v1/db""mxshop/app/goods/srv/internal/service/v1""mxshop/app/pkg/options""mxshop/gmicro/app""mxshop/pkg/log"
)// Injectors from wire.go:func initApp(logOptions *log.Options, serverOptions *options.ServerOptions, registryOptions *options.RegistryOptions, telemetryOptions *options.TelemetryOptions, mySQLOptions *options.MySQLOptions, esOptions *options.EsOptions) (*app.App, error) {registrar := NewRegistrar(registryOptions)dataFactory, err := db.GetDBFactoryOr(mySQLOptions)if err != nil {return nil, err}searchFactory, err := es.GetSearchFactoryOr(esOptions)if err != nil {return nil, err}serviceFactory := v1.NewGoodsServiceWire(dataFactory, searchFactory)goodsServer := v1_2.NewGoodsServerWire(serviceFactory)server, err := NewGoodsRPCServerWire(telemetryOptions, serverOptions, goodsServer)if err != nil {return nil, err}appApp, err := NewGoodsAppWire(logOptions, registrar, serverOptions, server)if err != nil {return nil, err}return appApp, nil
}

在如下方法替換接口

在這里插入圖片描述

運行main.go文件,如下顯示,正常啟動

在這里插入圖片描述

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/208357.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/208357.shtml
英文地址,請注明出處:http://en.pswp.cn/news/208357.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用pyftpdlib組件實現FTP文件共享

目錄 一、引言 二、技術背景 三、實現邏輯 1、創建FTP服務器&#xff1a; 2、實現文件共享&#xff1a; 3、設置用戶權限&#xff1a; 4、處理異常&#xff1a; 5、優化與擴展&#xff1a; 四、代碼實現 五、測試與評估 測試用例&#xff1a; 評估方法&#xff1a;…

React/Vue/Svelte 前端項目中開始使用TailwindCSS

背景 TailwindCSS 近年來在前端圈非常流行&#xff0c;它擺脫了原有的CSS限制&#xff0c;以靈活實用為賣點&#xff0c;用戶通過各種class組合即可構建出漂亮的用戶界面。對于初學者而言&#xff0c;可能需要一些上手成本&#xff0c;一旦掌握實用技巧后&#xff0c;Tailwind…

Unity中Batching優化的GPU實例化整理總結

文章目錄 前言一、GPU Instancing的支持1、硬件支持2、Shader支持3、腳本支持 二、我們來順著理一下GPU實例化的使用步驟1、GPU實例化前的C#代碼準備2、在 appdata 和 v2f 中定義GPU實例化ID3、在頂點著色 和 片元著色器 設置GPU Instance ID&#xff0c;使實例化對象頂點位置正…

Docker的資源控制

Docker的資源控制&#xff1a; 對容器使用宿主機的資源進行限制。 CPU 內存 磁盤I/O(讀寫性能) docker使用linux自帶的功能cgroup control groups是linux內核系統提供的一種可以限制&#xff0c;記錄&#xff0c;隔離進程組所使用的物理資源的一種機制。 docker借助這個機制…

go grpc高級用法

文章目錄 錯誤處理常規用法進階用法原理 多路復用元數據負載均衡壓縮數據 錯誤處理 gRPC 一般不在 message 中定義錯誤。畢竟每個 gRPC 服務本身就帶一個 error 的返回值&#xff0c;這是用來傳輸錯誤的專用通道。gRPC 中所有的錯誤返回都應該是 nil 或者 由 status.Status 產…

如何克服微服務測試的挑戰,并最大化收益

多年來&#xff0c;微服務一直是行業趨勢&#xff0c;但組織卻未能從該方法中獲益&#xff0c;并因發布失敗而苦苦掙扎。這些失敗通常歸結為測試服務之間的接口以獲得預期的質量、安全性和性能的困難。 最終&#xff0c;未能以足夠穩健的方式測試這些 API。一線希望是遺留 SOA…

cookie總結

cookie和session&#xff1a; 一、Cookie和Session二、使用Cookie保存用戶上次的訪問時間。三、Cookie常用方法總結亂碼問題解決&#xff1a; 一、Cookie和Session 會話&#xff1a;用戶從打開瀏覽器到關閉的整個過程就叫1次會話。 比如有的網站登錄過一次&#xff0c;下次再進…

Gitleaks - 一款高效的Github倉庫敏感信息泄露查詢工具

Gitleaks - 一款高效的Github倉庫敏感信息泄露查詢工具 1.工具概述2.安裝3.參數解析4.使用1.工具概述 Gitleaks 是一種 SAST 工具,用于檢測和防止 git 存儲庫中的硬編碼機密,如密碼、API 密鑰和令牌 Gitleaks 是一個開源工具,用于檢測和防止簽入 Git 存儲庫的機密(密碼/A…

使用 Kubernetes 為 CI/CD 流水線打造高效可靠的臨時環境

介紹 在不斷發展的科技世界中&#xff0c;快速構建高質量的軟件至關重要。在真實環境中測試應用程序是及早發現和修復錯誤的關鍵。但是&#xff0c;在真實環境中設置 CI/CD 流水線進行測試可能既棘手又昂貴。 Kubernetes 是一個流行的容器編排平臺&#xff0c;提供臨時環境解決…

【qt】Qt+OpenCv讀取帶有中文路徑的圖片

【opencv4.5.1版本】下載exe解壓即可。。。https://opencv.org/releases/page/2/ 【qt5.15.2】 pro文件 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In order to …

YOLOv8配置文件yolov8.yaml解讀

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 | 接輔導、項目定制 位置 該文件的位置位于 ./ultralytics/cfg/models/v8/yolov8.yaml 模型參數配置 # Parameters nc: 80 # number of classes scales: #…

碩士畢業論文格式修改要點_word

目錄 0、最開始要做的事情1、更改樣式&#xff08;先善器&#xff09;2、多級標題&#xff08;解決自動更新問題必要的基礎設置&#xff09;2、插入圖片&#xff08;1&#xff09;設置一個圖片樣式——“無間隔”&#xff08;2&#xff09;插入題注&#xff08;3&#xff09;修…

redis.conf詳解之replica-read-only

本文基于 redis_version:6.2.5 用法 控制從節點允許讀寫操作&#xff0c;還是讀操作。 replica-read-only yes 1 注意事項&#xff1a; 默認yes&#xff0c;只讀操作。 replica-read-only支持參數[yes,no]&#xff0c;no 支持讀寫操作。 這里說的從節點&#xff0c;是普通…

【flink番外篇】1、flink的23種常用算子介紹及詳細示例(完整版)

Flink 系列文章 一、Flink 專欄 Flink 專欄系統介紹某一知識點&#xff0c;并輔以具體的示例進行說明。 1、Flink 部署系列 本部分介紹Flink的部署、配置相關基礎內容。 2、Flink基礎系列 本部分介紹Flink 的基礎部分&#xff0c;比如術語、架構、編程模型、編程指南、基本的…

小白學java棧的經典算法問題——第四關白銀挑戰

內容1.括號匹配問題2.最小棧3.最大棧 1.括號匹配問題 棧的典型題目還是非常明顯的&#xff0c;括號匹配、表達式計算等等幾乎都少不了棧&#xff0c;本小節我們就看兩個最經典的問題 首先是LeetCode20,鏈接 本道題還是比較簡單的&#xff0c;其中比較麻煩的是如何判斷兩個符…

力扣面試題 08.12. 八皇后(java回溯解法)

Problem: 面試題 08.12. 八皇后 文章目錄 題目描述思路解題方法復雜度Code 題目描述 思路 八皇后問題的性質可以利用回溯來解決&#xff0c;將大問題具體分解成如下待解決問題&#xff1a; 1.以棋盤的每一行為回溯的決策階段&#xff0c;判斷當前棋盤位置能否放置棋子 2.如何判…

hbuilder + uniapp +vue3 開發微信云小程序

1、創建項目&#xff1a; 2、創建項目完成的默認目錄結構&#xff1a; 3、在根目錄新建一個文件夾cloudFns&#xff08;文件名字隨便&#xff09;&#xff0c;存放云函數源碼&#xff1a; 4、修改manifest.json文件&#xff1a;添加 小程序 appid和cloudfunctionRoot&#xff0…

python的websocket方法教程

WebSocket是一種網絡通信協議&#xff0c;它在單個TCP連接上提供全雙工的通信信道。在本篇文章中&#xff0c;我們將探討如何在Python中使用WebSocket實現實時通信。 websockets是Python中最常用的網絡庫之一&#xff0c;也是websocket協議的Python實現。它不僅作為基礎組件在…

pyside/qt03——人機協同的編程教學—直接面向chatGPT實戰開發(做中學,事上練)

先大概有個草圖框架&#xff0c;一點點豐富 我糾結好久&#xff0c;直接用Python寫UI代碼 還是用designer做UI 再轉Python呢&#xff0c; 因為不管怎么樣都要轉成Python代碼&#xff0c; 想了想還是學一下designer吧&#xff0c;有個中介&#xff0c;有直觀理解。 直接這樣也可…

智能優化算法應用:基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用&#xff1a;基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用&#xff1a;基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.食肉植物算法4.實驗參數設定5.算法結果6.參考…