当前位置: 首页 > news >正文

使用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文件,如下显示,正常启动

在这里插入图片描述

相关文章:

使用wire重构商品微服务

一.wire简介 Wire 是一个轻巧的Golang依赖注入工具。它由Go Cloud团队开发&#xff0c;通过自动生成代码的方式在编译期完成依赖注入。 依赖注入是保持软件 “低耦合、易维护” 的重要设计准则之一。 此准则被广泛应用在各种开发平台之中&#xff0c;有很多与之相关的优秀工…...

大三上实训内容

项目一&#xff1a;爬取天气预报数据 【内容】 在中国天气网(http://www.weather.com.cn)中输入城市的名称&#xff0c;例如输入信阳&#xff0c;进入http://www.weather.com.cn/weather1d/101180601.shtml#input 的网页显示信阳的天气预报&#xff0c;其中101180601是信阳的…...

IOT安全学习路标

1. 物联网基础知识 首先&#xff0c;你需要建立坚实的物联网基础知识&#xff0c;包括IoT的架构和组件&#xff0c;传感器和设备的连接和通信技术&#xff0c;云端和边缘计算等。 2. 通信和网络安全 学习关于物联网通信和网络安全的基础知识&#xff0c;包括加密和认证技术、…...

java中线程的状态是如何转换的?

在 Java 中&#xff0c;线程有几种状态&#xff0c;主要包括 NEW&#xff08;新建&#xff09;、RUNNABLE&#xff08;可运行&#xff09;、BLOCKED&#xff08;阻塞&#xff09;、WAITING&#xff08;等待&#xff09;、TIMED_WAITING&#xff08;计时等待&#xff09;、和 TE…...

处理合并目录下的Excel文件数据并指定列去重

处理合并目录下的Excel文件数据并指定列去重 需求&#xff1a;读取指定目录下的Excel文件并给数据做合并与去重处理 Python代码实现 import os import pandas as pd import warnings import time from tqdm import tqdm #进度条展示def read_excel(path):dfs []for file in…...

Numpy数组的去重 np.unique()(第15讲)

Numpy数组的去重 np.unique()(第15讲)         🍹博主 侯小啾 感谢您的支持与信赖。☀️ 🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ🌹꧔ꦿ�…...

ROS-log功能区别

ROS使用rosout包来记录各个节点的log信息&#xff0c;通常这些log信息是一些可以读懂的字符串信息&#xff0c;这些信息一般用来记录节点的运行状态。 ROS有五种不同类型的log信息&#xff0c;分别为&#xff1a;logdebug、loginfo、logwarn、logerr、logfatal。 等级由低到高&…...

学习git后,真正在项目中如何使用?

文章目录 前言下载和安装Git克隆远程仓库PyCharm链接本地Git创建分支修改项目工程并提交到本地仓库推送到远程仓库小结 前言 网上学习git的教程&#xff0c;甚至还有很多可视化很好的git教程&#xff0c;入门git也不是什么难事。但我发现&#xff0c;当我真的要从网上克隆一个…...

Qt国际化翻译Linguist使用

QT的国际化是非常方便的&#xff0c;简单的说就是QT有自带的翻译工具把我们源代码中的字符串翻译成任何语言文件&#xff0c;再把这个语言文件加载到项目中就可以显示不同的语言。下面直接上手&#xff1a; 步骤一&#xff1a;打开pro文件&#xff0c;添加&#xff1a;TRANSLA…...

ShardingSphere数据分片之分表操作

1、概述 Apache ShardingSphere 是一款分布式的数据库生态系统&#xff0c; 可以将任意数据库转换为分布式数据库&#xff0c;并通过数据分片、弹性伸缩、加密等能力对原有数据库进行增强。 Apache ShardingSphere 设计哲学为 Database Plus&#xff0c;旨在构建异构数据库上…...

基于ssm鲸落文化线上体验馆论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本鲸落文化线上体验馆就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短时间内处理完毕庞大的数据信…...

LeetCode Hot100 131.分割回文串

题目&#xff1a; 给你一个字符串 s&#xff0c;请你将 s 分割成一些子串&#xff0c;使每个子串都是 回文串 。返回 s 所有可能的分割方案。 回文串 是正着读和反着读都一样的字符串。 方法&#xff1a;灵神-子集型回溯 假设每对相邻字符之间有个逗号&#xff0c;那么就看…...

SAP UI5 walkthrough step9 Component Configuration

在之前的章节中&#xff0c;我们已经介绍完了MVC的架构和实现&#xff0c;现在我们来讲一下&#xff0c;SAPUI5的结构 这一步&#xff0c;我们将所有的UI资产从index.html里面独立封装在一个组件里面 这样组件就变得独立&#xff0c;可复用了。这样&#xff0c;无所什么时候我…...

【数据结构和算法】--- 栈

目录 栈的概念及结构栈的实现初始化栈入栈出栈其他一些栈函数 小结栈相关的题目 栈的概念及结构 栈是一种特殊的线性表。相比于链表和顺序表&#xff0c;栈只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶&#xff0c;另一端称为栈底。栈中的…...

CentOS7.0 下rpm安装MySQL5.5.60

下载 下载路径: MySQL :: Download MySQL Community Server -->looking for the latest GA version-->5.5.60 此压缩包中有多个rpm包 有四个不是必须的,只需安装这三个 MySQL-server-5.5.60-1.el6.x86_64 MySQL-devel-5.5.60-1.el6.x86_64 MySQL-client-5.5.60-1.el6.x8…...

智慧能源:数字孪生压缩空气储能管控平台

压缩空气储能在解决可再生能源不稳定性和提供可靠能源供应方面具有重要的优势。压缩空气储能&#xff0c;是指在电网负荷低谷期将电能用于压缩空气&#xff0c;在电网负荷高峰期释放压缩空气推动汽轮机发电的储能方式。通过提高能量转换效率、增加储能密度、快速启动和调节能力…...

【链表OJ—反转链表】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 目录 前言 1、反转链表题目&#xff1a; 2、方法讲解&#xff1a; 解法一&#xff1a; 解法二&#xff1a; 总结 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升起的太…...

TCP一对一聊天

客户端 import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.BufferedReader; import java.io.IOException; import java.io…...

基于Java的招聘系统的设计与实现

末尾获取源码 开发语言&#xff1a;Java Java开发工具&#xff1a;JDK1.8 后端框架&#xff1a;SSM 前端&#xff1a;Vue 数据库&#xff1a;MySQL5.7和Navicat管理工具结合 服务器&#xff1a;Tomcat8.5 开发软件&#xff1a;IDEA / Eclipse 是否Maven项目&#xff1a;是 目录…...

spring boot整合mybatis进行部门管理管理的增删改查

部门列表查询&#xff1a; 功能实现&#xff1a; 需求&#xff1a;查询数据库表中的所有部门数据&#xff0c;展示在页面上。 准备工作&#xff1a; 准备数据库表dept&#xff08;部门表&#xff09;&#xff0c;实体类Dept。在项目中引入mybatis的起步依赖&#xff0c;mysql的…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【快手拥抱开源】通过快手团队开源的 KwaiCoder-AutoThink-preview 解锁大语言模型的潜力

引言&#xff1a; 在人工智能快速发展的浪潮中&#xff0c;快手Kwaipilot团队推出的 KwaiCoder-AutoThink-preview 具有里程碑意义——这是首个公开的AutoThink大语言模型&#xff08;LLM&#xff09;。该模型代表着该领域的重大突破&#xff0c;通过独特方式融合思考与非思考…...

微服务商城-商品微服务

数据表 CREATE TABLE product (id bigint(20) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 商品id,cateid smallint(6) UNSIGNED NOT NULL DEFAULT 0 COMMENT 类别Id,name varchar(100) NOT NULL DEFAULT COMMENT 商品名称,subtitle varchar(200) NOT NULL DEFAULT COMMENT 商…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

大学生职业发展与就业创业指导教学评价

这里是引用 作为软工2203/2204班的学生&#xff0c;我们非常感谢您在《大学生职业发展与就业创业指导》课程中的悉心教导。这门课程对我们即将面临实习和就业的工科学生来说至关重要&#xff0c;而您认真负责的教学态度&#xff0c;让课程的每一部分都充满了实用价值。 尤其让我…...

Qemu arm操作系统开发环境

使用qemu虚拟arm硬件比较合适。 步骤如下&#xff1a; 安装qemu apt install qemu-system安装aarch64-none-elf-gcc 需要手动下载&#xff0c;下载地址&#xff1a;https://developer.arm.com/-/media/Files/downloads/gnu/13.2.rel1/binrel/arm-gnu-toolchain-13.2.rel1-x…...

API网关Kong的鉴权与限流:高并发场景下的核心实践

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 引言 在微服务架构中&#xff0c;API网关承担着流量调度、安全防护和协议转换的核心职责。作为云原生时代的代表性网关&#xff0c;Kong凭借其插件化架构…...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关

在水泥厂的生产流程中&#xff0c;工业自动化网关起着至关重要的作用&#xff0c;尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关&#xff0c;为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多&#xff0c;其中不少设备采用Devicenet协议。Devicen…...

【堆垛策略】设计方法

堆垛策略的设计是积木堆叠系统的核心&#xff0c;直接影响堆叠的稳定性、效率和容错能力。以下是分层次的堆垛策略设计方法&#xff0c;涵盖基础规则、优化算法和容错机制&#xff1a; 1. 基础堆垛规则 (1) 物理稳定性优先 重心原则&#xff1a; 大尺寸/重量积木在下&#xf…...