Golang依赖注入提升开发效率!

2023-11-10

bb3c79fb555361fb6cdec32d8846cecc.png

导语 | 依赖注入并不是java独有的,也不是web框架独有的,本文用通俗易懂的语言讲解什么是依赖注入,为什么需要依赖注入,以及go语言如何使用依赖注入来提升开发效率。

一、什么依赖注入


依赖注入(Dependency Injection)也叫DI是软件工程的一种设计模式。

二、为什么需要依赖注入

比如我们使用go要开发一个http api服务,这个服务启动需要

  • 读取命令行

  • 读取配置

  • 连接数据库

  • 连接redis

  • 设置回调函数

  • 监听端口

这个服务关闭需要

  • 关闭端口

  • 关闭redis

  • 关闭mysql

你的代码可能看起来像是这样:

configFile := cmdFlags.get("--config")
configObject := readConfig(configFile)
mysqlConn ,err := mysql.Connect(configObject.mysql)
userModel := NewuserModel(mysqlConn)
redisConn ,err := redis.Connect(configObject.redis)
cache := NewCache(redisConn)
httpServer ,err := http.New(configObject.http)
httpServer.Router("/user/any",NewUserControl(userModel,cache))
httpServer.Start()
<-sig
httpServer.Stop()
redisConn.Close()
mysqlConn.Close()

看起来似乎没有问题,userModel使用mysql实现,cache接口使用redis实现,然后封装一个UserControl对象,这是一个简单的例子,假如再复杂一点。

user控制器需要读配置,你又要把配置对象传进去,或者直接读全局变量。

user控制器需要写日志,又要把日志对象传进去,或者读全局变量log.Info(xxx)。

user控制器要连其他HTTP API,又把NewHttpClient()传进去 或者直接干脆建一个全局包。

user控制器要调RPC,又需要一个RPC Client。

user控制器依赖很多其他对象,NewUserControl参数越来越多,于是你觉得没必要传进去,就用全局变量引用,项目慢慢变大。

代码里各种init函数,import全局变量,xx.attr=NewXXX。

不是说不可用,也没问题,就是代码依赖混乱,init加全局变量管理维护起来不是那么容易。

再来看看使用依赖注入伪代码的实现:

ioc := NewContainer()
ioc.Inject(configObject)
ioc.Inject(mysql.Connect,redis.Connect)
ioc.Inject(NewUserModel,NewCache,NewLogger)
ioc.Inject(NewUserControl,NewOtherControl)
ioc.Invoke(func(httpServer,UserControl){
  httpServer.Router("/user/any",UserControl)
})
ioc.Invoke(func(httpServer,OtherControl){
  httpServer.Router("/other/any",OtherControl)
})
ioc.Start()
<- sig
ioc.Stop()

这是一段伪代码大概能说明意思,ioc是一个容器Inject方法传递多个构造方法,告诉容器如何创建对象Invoke方法传递多个函数,函数参数可为容器内所有对象,告诉容器如何使用对象 这就是依赖注入,好处是:

  • 对象的创建和使用解耦(一般创建都交给了框架或者自己扩展的模块)。

  • 不用自己创建和组装,不用关心依赖创建顺序,根据使用顺序自动推导。

三、golang依赖注入开源库

facebook的inject基于反射,运行时注入。

google的wire基于AST,编译期注入。

uber的dig fx基于反射,运行时注入。

inject功能有点弱,也不维护了,wire有点抽象,没仔细研究。下面主要介绍一下fx的使用,以及如何使用fx封装一个开发框架。

四、fx framework

An application framework for Go that:

  • Makes dependency injection easy

  • Eliminates the need for global state and func init()

先来看fx的一个官方例子:

package main


import (
  "context"
  "log"
  "net/http"
  "os"
  "time"


  "go.uber.org/fx"
  "go.uber.org/fx/fxevent"
)


func NewLogger() *log.Logger {
  logger := log.New(os.Stdout, "" /* prefix */, 0 /* flags */)
  logger.Print("Executing NewLogger.")
  return logger
}


func NewHandler(logger *log.Logger) (http.Handler, error) {
  logger.Print("Executing NewHandler.")
  return http.HandlerFunc(func(http.ResponseWriter, *http.Request) {
    logger.Print("Got a request.")
  }), nil
}
func NewMux(lc fx.Lifecycle, logger *log.Logger) *http.ServeMux {


  mux := http.NewServeMux()
  server := &amp;http.Server{
    Addr: &nbsp; &nbsp;":8080",
    Handler: mux,
  }
  lc.Append(fx.Hook{
    OnStart: func(context.Context) error {
      go server.ListenAndServe()
      return nil
    },
    OnStop: func(ctx context.Context) error {
      logger.Print("Stopping HTTP server.")
      return server.Shutdown(ctx)
    },
  })
  return mux
}
func Register(mux *http.ServeMux, h http.Handler) {
  mux.Handle("/", h)
}


func main() {
  //app是一个容器
  //fx.Provider告诉容器如何创建对象,内置一个fx.Lifeycle对象可注册启动和关闭回调函数
  //fx.Invoke告诉容器如何使用对象
  app := fx.New(
    fx.Provide(
      NewLogger,
      NewHandler,
      NewMux,
    ),
    fx.Invoke(Register),
    fx.WithLogger(
      func() fxevent.Logger {
        return fxevent.NopLogger
      },
    ),
  )


  startCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  defer cancel()
  if err := app.Start(startCtx); err != nil {
    log.Fatal(err)
  }


  if _, err := http.Get("http://localhost:8080/"); err != nil {
    log.Fatal(err)
  }
  stopCtx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
  defer cancel()
  if err := app.Stop(stopCtx); err != nil {
    log.Fatal(err)
  }
}

五、fx的使用提效viego

fx详细的使用方式可查看我基于fx,zap,cobra,viper写的一个开发框架viego源码。

viego的核心就是负责根据配置文件创建对象或模块。用户只需要配置一下配置文件就可以使用viego创建的模块进行扩展业务模块,可开发http,grpc服务,或cli命令行。

简单谈一下框架设计理念和原理

程序是由一堆对象组成的,每个对象都有自己的生命周期(启动,停止),每个对象都有自己的依赖和被别人依赖。

第一个对象肯定是静态对象(0依赖),然后其他的对象以这个根对象扩展为射线结构,最终所有对象连起来构成一幅对象图。

大多数程序的第一个对象肯定都是命令行参数

694ae094670138a46ed183091a955380.png

如图所示,对象创建和组装,启动关闭是一个项很费时费力的工作,viego使用fx解决了这个问题,并且扩展了一些后台开发常用的模块,每个模块提供几个有用的对象供用户直接使用,用户只需要关心业务逻辑回调绿色部分的内容即可,需要打日志就引入日志记录器,需要读配置就引入配置文件结构,要什么用什么 ,不用关心这个对象的创建和销毁过程。

内置模块列表

viego模块列表目前内置了一些基础的功能,比如:

  • 创建命令行命令

  • 读取配置文件

  • 创建日志对象

  • 创建http server

  • 创建grpc server

  • 创建http client

  • 创建grpc client

  • 创建北极星服务注册和服务发现

  • 创建mysql对象

  • 创建redis对象

比如举一个例子,你的程序需要使用打日志,只需要配置logger配置就可以config/server.yaml

logger:
  #这里面的配置是zap原生的日志配置,viego读取配置创建*zap.Logger对象
  default: |
    level: debug
    outputPaths: ["stdout"]
    encoding: color
    encoderConfig: 
      timeEncoder: 
        layout: 2006-01-02 15:04:05.000000

main.go

package main


import (
  "git.woa.com/yangynyang/viego"
  "git.woa.com/yangynyang/viego/module/logger"
  "go.uber.org/zap"
)
func main() {
  viego.Run(
    viego.WithName("_test"),
    viego.WithOption(
      fx.Invoke(func(lgdefault *zap.Logger,lgget logger.Get){
        //lgdefault == lgget("default")
        //lgget是一个函数可以通过配置文件logger下面的key获取不同的日志记录器对象
      }),
    )
  }

main.go模版

func main() {
  viego.Run(
    viego.WithName("_test"),
    viego.WithOption(
      fx.Provider(自己的构造方法),
      fx.Invoke(func(自己注册的对象+viego内置对象+fx内置对象...){


      }),
    )
  }

因为本人也不是专业开发,业务场景也比较少,在设计上肯定有很多不足,比如中间件和插件设计,一个人精力有限,欢迎有兴趣的读者使用,一起探索交流。

 作者简介

a34c3b4293b6c1ea8f567885291015f0.png

杨洋

腾讯业务运维工程师

腾讯业务运维工程师,目前负责英雄联盟手游/英雄联盟电竞经理运维和工具开发工作,擅长云原生运维开发技术。

 推荐阅读

带你畅游k8s调度器!

c++异步:asio的scheduler实现!

甜skr人!程序员专属七夕表白神器,成功率100%

浅谈函数调用!

8 月 20 日,「低代码究竟是“银弹”还是“泡沫”」TVP 低代码技术分享会,即将重磅来袭!

扫码立即参会赢好礼

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Golang依赖注入提升开发效率! 的相关文章

随机推荐

  • C++ sort()函数

    C 中的sort 函数是用于对容器 如数组 向量 链表等 中的元素进行排序的标准库函数 它使用了一种称为快速排序 quicksort 的排序算法 通常具有较好的性能 sort 函数位于
  • element 表格二次封装

  • Mac office 2016 word 出现隐藏模块中出现编译错误: link

    try to remove Library Group Containers UBF8T346G9 Office User Content Startup Word linkCreation dotm or whatever in that
  • Redis单机版全面讲解

    目录 常识 什么是redis redis为什么快 redis作为实例安装在系统中 redis数据类型 redis命令 String类型相关命令 list类型相关命令 hash类型相关命令 set类型相关命令 zset类型相关命令 redis
  • LAN8720A网络模块的使用问题

    一 LAN8720A模块驱动电路 最近在调试STM32F4驱动LAN8720A网络模块 在做方案前参考是正点原子的LAN8720A的驱动电路方案 但是从网上买回来的LAN8720A模块用正点原子的例程一直驱动不起来 在windows系统下一
  • vue提示插件[vscode]

    在VSCode Marketplace 搜素Vue 出现关于语法高亮的插件有 vue vue beautify vue color VueHelper vertur等等 比较了下载数量可以了解到 vetur 是目前比较好的语法高亮插件 我们
  • Eclipse使用(Java基础)&Spring boot学习(一)

    Eclipse安装 这个很简单 搜索一下Eclipse下载即可 我是在这里下的 然后选个开发环境 C 的话我会在Visual Studio下写 所以只装了Java 一路next就好 没有什么坑 Hello World Create a Ja
  • HADOOP介绍

    1 HADOOP背景介绍 1 1 什么是HADOOP HADOOP是apache旗下的一套开源软件平台 HADOOP提供的功能 利用服务器集群 根据用户的自定义业务逻辑 对海量数据进行分布式处理 HADOOP的核心组件有 HDFS 分布式文
  • 嵌入式操作系统风云录:历史演进与物联网未来.

    嵌入式操作系统风云录 历史演进与物联网未来 何小庆 著 图书在版编目 CIP 数据 嵌入式操作系统风云录 历史演进与物联网未来 何小庆著 北京 机械工业出版社 2016 10 ISBN 978 7 111 55085 3 嵌 何 实时操作系
  • Java项目结构概述

    文章目录 前言 一 项目结构介绍 1 单模块项目结构 2 多模块项目结构 3 分层结构 4 MVC项目结构 5 插件化结构项目 6 微服务架构结构 总结 前言 构建一个良好的Java项目结构是开发高质量 可扩展和易维护应用程序的重要基础 在
  • skimage图像的读取与保存

    首先 说明用opencv与skimage io imread读取和保存图片的区别 读取和保存后的都是numpy格式 但cv2的读取和存储格式是BGR 而skimage的读取和存储格式是RGB 1 读取图片 skimage读取图片 img s
  • The deduced formulas of Conv1d and ConvTranspose1d

    torch nn Conv1d in channels out channels kernel size stride 1 padding 0 dilation 1 groups 1 bias True In the simplest ca
  • 基于树莓派的python界面开发实例教程

    基于树莓派的python界面开发实例教程 环境测试 添加label实例 时钟程序 添加天气 环境测试 点击树莓派的开始菜单 找到programming Python3 IDLE 点击打开 打开后如下 在home pi下面建立home pi
  • UNIX网络编程卷一 学习笔记 第十七章 ioctl操作

    ioctl函数传统上一直作为那些不适合归入现有已定义的类别的系统接口 POSIX正在通过创建特定的包装函数来代替ioctl函数的某些功能 以取而代之的是那些已被POSIX标准化的函数 例如 Unix终端接口传统上使用ioctl函数访问 而P
  • 测试工程师需要具备哪些技能

    测试工程师需要具备以下几项技能 软件测试方法和技巧 测试工程师需要了解不同的测试方法 如黑盒测试 白盒测试 回归测试等 编程能力 有些测试工程师需要编写自动化测试脚本 因此需要具备一定的编程技能 问题诊断能力 测试工程师需要能够识别和定位软
  • 【leetcode刷题】27、移除元素(C++)

    27 移除元素 原题地址 https leetcode cn problems remove element 给你一个数组 nums 和一个值 val 你需要 原地 移除所有数值等于 val 的元素 并返回移除后数组的新长度 不要使用额外的
  • 华为OD2023(A卷)基础题34【新词挖掘】

    知识图谱新词挖掘 题目描述 小华负责公司知识图谱产品 现在要通过新词挖掘完善知识图谱 新词挖掘 给出一个待挖掘文本内容字符串content和一个词的字符串word 找到content中所有word的新词 新词 使用词word的字符排列形成的
  • 使用HTMLTestRunner没有生成测试报告

    原因 没有执行 main函数中的程序 只执行了测试用例 在main函数上方右键运行的是整个py文件 在main函数下方右键运行的是py文件中的测试用例 如果在上方点击出现的和下方点击的一样需要在右上角设置并添加文件路径
  • element 限制文件上传类型

  • Golang依赖注入提升开发效率!

    导语 依赖注入并不是java独有的 也不是web框架独有的 本文用通俗易懂的语言讲解什么是依赖注入 为什么需要依赖注入 以及go语言如何使用依赖注入来提升开发效率 一 什么依赖注入 依赖注入 Dependency Injection 也叫D
Powered by Hwhale