前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >挖坑系列之Fx初窥

挖坑系列之Fx初窥

作者头像
用户3904122
发布2022-06-29 14:44:33
5900
发布2022-06-29 14:44:33
举报
文章被收录于专栏:光华路程序猿

当我们谈到web服务时,使用Java的小伙伴第一反应肯定是Spring全家桶,虽然约定大于配置的SpringBoot相比最初Spring版本在易用性、开发效率方便已经有了长足的进步,但是其API还是相对较繁杂。因此欢迎大家来到golang的世界,这里的一切都尽可能简单。

实现一个可以与浏览器交互输出"hello world"的web服务的典型做法是启动一个HTTP服务并运行自定义的路由。以下代码片段,我们通过使用Go内置net/http包不需要外部依赖即可实现。

代码语言:javascript
复制
Filename: server/server.go
import "net/http"

// Handler for http requests
type Handler struct {
   mux *http.ServeMux
}

// New http handler
func New(s *http.ServeMux) *Handler {
   h := Handler{s}
   h.registerRoutes()

   return &h
}

// RegisterRoutes for all http endpoints
func (h *Handler) registerRoutes() {
   h.mux.HandleFunc("/", h.HelloWorld)
}

func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) {
   w.WriteHeader(200)
   w.Write([]byte("Hello World"))
}
---
Filename: main.go
package main

import (
   "net/http"

   "medium/web_server/server"
)

func main() {
   mux := http.NewServeMux()
   server.New(mux)

   http.ListenAndServe(":8080", mux)
}

准备好上述代码,我们通过终端cd到对应目录,执行go run main.go即可发现我们的web服务已经ready。

到目前为止,我们已经完成一个简单的web服务器。但是实际开发过程中我们业务往往比较复杂,我们需要手动处理大量的依赖关系,这个时候我们就需要使用Fx(Uber构建的依赖注入框架)。

什么是依赖注入

依赖注入即使用控制反转来解决依赖的一种软件设计模式。依赖注入中,依赖是一个能够被使用的对象。注入是将依赖项传递给使用它的依赖对象。依赖注入是控制反转的子集。简单讲,依赖注入是对象合成的一种模式,父对象提供了子对象所需要的所有依赖关系。

三种依赖注入:

  • 构造器注入
  • Setter方法注入
  • 基于接口的注入

什么是Fx

根据Uber的官方文档(https://pkg.go.dev/go.uber.org/fx),Fx是一个Go的应用框架,主要解决两个问题:

  • 让依赖更加容易
  • 消除了对全局状态和func init()的需求

Fx使用的是构造器注入模式,现在我们把之前的web服务使用Fx进行重构,看看Fx如何让依赖变容易的。强烈建议可以先阅读下Fx官方文档(https://pkg.go.dev/go.uber.org/fx)

  1. 首先我们安装下Fxgo get go.uber.org/fx
  2. 初始化Fx
代码语言:javascript
复制
func main(){
  fx.New.Run()
}
  1. 现在我们需要通过将HTTP serve Mux加到New方法,这样就可以将它注入到我们的HTTP handler
代码语言:javascript
复制
func main(){
  fx.New(
    fx.Provide(http.NewServeMux),
  ).Run()
}
  1. 更新HTTP handler的代码
代码语言:javascript
复制
package httphandler

import "net/http"

// Handler for http requests
type Handler struct {
 mux *http.ServeMux
}

// New http handler
func New(s *http.ServeMux) *Handler {
 h := Handler{s}
 h.registerRoutes()

 return &h
}

// RegisterRoutes for all http endpoints
func (h *Handler) registerRoutes() {
 h.mux.HandleFunc("/", h.HelloWorld)
}
// HelloWorld handler which recieves the user request
func (h *Handler) HelloWorld(w http.ResponseWriter, r *http.Request) {
 w.WriteHeader(200)
 w.Write([]byte("Hello World"))
}
  1. 最后一步是启动我们的监听器
代码语言:javascript
复制
func main() {
   fx.New(
      fx.Provide(http.NewServeMux),
      fx.Invoke(server.New),
      fx.Invoke(registerHooks),
   ).Run()
}

func registerHooks(
   lifecycle fx.Lifecycle, mux *http.ServeMux,
) {
   lifecycle.Append(
      fx.Hook{
         OnStart: func(ctx context.Context) error {
            go http.ListenAndServe(":8080", mux)
            return nil
         },
      },
   )
}

模块化

实际开发过程中,上面的webserver显然是不够看。你往往需要写大量复杂的业务逻辑,那么为了你的业务职责单一,可复用,模块化或许是个不错的选择,比如你的系统中可能会抽象出日志模块,认证模块,统计模块等等。

而Fx就是基于模块化编程的概念设计的,当Fx创建一个新对象(对象A)时,它将查找对象A所需要的依赖项,

  • 一种情况 不需要依赖项,在应用程序上下文中创建对象A
  • 另一种情况 Fx在应用程序上下文中找到所需的依赖项,将其注入并创建对象A

举个栗子,假如我们现在要为刚刚的webserver提供一个日志记录的能力,我们可以利用zap来构建一个日志模块。

代码语言:javascript
复制
// ProvideLogger to fx
func ProvideLogger() *zap.SugaredLogger {
   logger, _ := zap.NewProduction()
   slogger := logger.Sugar()

   return slogger
}

// Module provided to fx
var Module = fx.Options(
   fx.Provide(ProvideLogger),
)

然后更新main.go

代码语言:javascript
复制
func main() {
   fx.New(
      fx.Provide(http.NewServeMux),
      fx.Invoke(server.New),
      fx.Invoke(registerHooks),
      loggerfx.Module,
   ).Run()
}

func registerHooks(
   lifecycle fx.Lifecycle, mux *http.ServeMux, logger *zap.SugaredLogger,
) {
   lifecycle.Append(
      fx.Hook{
         OnStart: func(context.Context) error {
            logger.Info("Listening on localhost:8080")
            go http.ListenAndServe(":8080", mux)
            return nil
         },
         OnStop: func(context.Context) error {
            return logger.Sync()
         },
      },
   )
}

这样一个简单的module注入就完成了,我们也可以尝试将一个RPC调用模块化 然后应用下DI的思想。

总结

依赖注入给我带来了相当好的价值,但是也存在缺点:

  1. 依赖注入会创建需要构造详细信息的客户端,当明显的默认值可用时,这可能会很困难
  2. 依赖注入将对象的构造和行为分开,代码也难以追踪
  3. 需要更多的前期工作

本文只是简单介绍了下Fx,计划后续有机会可以深入剖析一下,先挖个坑。。。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-02-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 光华路程序猿 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 什么是依赖注入
  • 什么是Fx
  • 模块化
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档