当前位置: 代码网 > it编程>编程语言>其他编程 > 使用wire重构商品微服务

使用wire重构商品微服务

2024年08月01日 其他编程 我要评论
使用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 main

import (
	"fmt"
	"github.com/pkg/errors"
	"time"
)

type message string

func 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 wireinject

package main

import "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 main

import (
   "fmt"
   "github.com/pkg/errors"
   "time"
)

type message string

func 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()
}

,详见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) {
   //初始化log
   log.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.address
   c.scheme = registry.scheme
   client, 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 的 exporter
   trace.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.serveroption
   opts = 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 error
   once.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)
      //希望大家自己可以去封装logger
      newlogger := 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,
      }
      //允许连接多少个mysql
      sqldb.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 wireinject

package srv

import (
   "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 !wireinject

package srv

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

在这里插入图片描述

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com