fxdemo

command module
v1.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 15, 2023 License: MIT Imports: 2 Imported by: 0

README

FXDEMO

该项目是fx-tool项目的模版。
fx-tool是快速搭建go项目的脚手架
所生产的项目模块化,超轻量,少封装



使用指南

运行

# 进入项目中。例如:
cd proj_name

#下载所需的库
go mod tidy

有如下两种方式启动项目,项目启动成功后会运行一个http服务器进程。

#该方式linux和windows皆可使用
go run  . -cnf="./conf/conf.json"

#这种方式运行前会编译成linux版本。所有请确保不是在windows环境
make run 
## windows中测试是否运行成功,打开浏览器测试
http://localhost:8080/echo
http://localhost:8080/hello

## linux中测试是否启动成功访问以下URL测试
curl -X POST -d 'hello' http://localhost:8080/echo
curl -X POST -d 'gopher' http://localhost:8080/hello

## ctrl + c停止服务器

若想修改项目的相关参数,请修改/conf/conf.json文件后,再运行。

{
    "log_level":"日志级别", 
    "log_file":"日志存储位置", 
    "http_addr":"http服务器监听地址", 
    "http_read_over_time":"http读取超时(秒)", 
    "http_write_over_time":"http写入超时(秒)"
}


教程

  • 预先说明下面教程会用到的文件夹:
    http/handler是放handler实现的文件夹。
    http/middleware是放http的拦截器实现的文件夹。
    fx_opt/srv.go是http配置的文件夹。
    fx_opt/var.go是注册fx实例配置的文件夹。

  • 强烈建议阅读以下内容前,先花10分钟查看fx基本教程

  • 我们的fx中将会包含以下实例,所以可以给任何一个注册到fx的实例的New函数传递这些实例作为参数。

*http.Server
*go.uber.org/zap.Logger
*github.com/gorilla/mux.Router
*github.com/luoruofeng/fxdemo/onf.Config



HTTP添加Handler

举例说明,只需两步即可完成:

  1. http/handler中新建文件hello.go
package handler

import (
	"net/http"

	"go.uber.org/zap"
)

type HelloHandler struct {
	log *zap.Logger
}

func NewHelloHandler(log *zap.Logger) *HelloHandler {
	return &HelloHandler{log: log}
}

func (*HelloHandler) Pattern() string {
	return "/hello"
}

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.log.Info("这里可以使用h中的任何属性,这是使用了log属性记录日志")
	w.Write([]byte("hello"))
}
  • HelloHandler定义结构体,可以包含任何需要的其他实例(fx中注册的其他任何provider实例)
  • NewHelloHandler是fx的provider的构造器需的New构造方法,返回的*HelloHandler将会注册到fx中,参数为所需的任何实例(fx中注册的实例)。
  • Pattern是路由url配置。
  • ServeHTTP是handler的具体实现

  1. 打开fx_opt/srv.go,在Setup方法的handlerProv变量一行中的fxhttp.AllAsRoute函数中添加新的参数handler.NewHelloHandler即可(handler.NewHelloHandler是上面的代码创建的函数)。
func (f *FxSrv) Setup() {
	handlerProv := fx.Provide(
		fxhttp.AllAsRoute(handler.NewEchoHandler, handler.NewHelloHandler)...,
	)
//其他代码

  1. 启动服务验证结果
    浏览器中输入http://localhost:8080/hello即可访问。


HTTP添加拦截器

举例说明,若想给http添加新的日志拦截器(middleware)只需以下两步。

  1. http/middleware文件夹中新建log2.go,内容如下:
package middleware

import (
	"net/http"

	"go.uber.org/zap"
)

type LogMiddleware2 struct {
	logger *zap.Logger
}

func NewLogMiddleware2(logger *zap.Logger) *LogMiddleware2 {
	return &LogMiddleware2{logger: logger}
}

func (l *LogMiddleware2) Middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		l.logger.Info("正在执行log2拦截器")
		next.ServeHTTP(w, r)
        l.logger.Info("log2拦截器即将执行完成")

	})
}

  • LogMiddleware2 struct定义结构体,结构体中定义我们所需要的实例(该实例已经在fx中注册),例如我们这里需要*zap.Logger。
  • NewLogMiddleware2定义上面结构体的New函数,参数为我们所需要的实例(该实例已经在fx中注册),例如我们这里需要*zap.Logger。
  • Middleware定义拦截器,该拦截器会在每次HTTP请求中被调用,*next.ServeHTTP(w, r)*为handler方法本身。可以在该方法上下去编写拦截器逻辑。

  1. 打开fx_opt/srv.go,在Setup方法的middlewareProv变量的fxhttp.AllAsMiddleware数中添加新的参数middleware.NewLogMiddleware2即可(middleware.NewLogMiddleware2是上面的代码创建的函数)。
func (f *FxSrv) Setup() {
    //其他代码
	middlewareProv := fx.Provide(
		fxhttp.AllAsMiddleware(middleware.NewLogMiddleware, middleware.NewLogMiddleware2)...,

  1. 启动服务验证结果
    浏览器中输入http://localhost:8080/hello即可访问。
# 项目控制台将会输出以下日志
...
{"level":"info","message":"正在执行log2拦截器"}
...
{"level":"info","message":"log2拦截器即将执行完成"}
...


添加新的实例到fx

举例说明,若想在fx中创建实例只需以下两步。

  1. srv/文件夹中新建srv1.go,内容如下:
package srv

import (
	"context"

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

type Abc struct {
	logger *zap.Logger
}

func NewAbc(lc fx.Lifecycle, logger *zap.Logger) Abc {
	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			logger.Info("Abc开始构建")
			return nil
		},
		OnStop: func(ctx context.Context) error {
			logger.Info("Abc开始销毁")
			return nil
		},
	})

	return Abc{logger: logger}
}
  • Abc是该案例中的结构体,该结构体产生的实例最终会被包含到fx中。也可以被fx中的其他实例使用。
  • NewAbc函数的返回值Abc会创建到fx中。
    其中参数是我们已经注册到了fx中的实例。当然我们也可以不同传递任何参数,如果你用不到这些参数。
    传递第一个参数lc fx.Lifecycle的原因是因为要用OnStartOnStop,如果不需要在该实例创建和销毁时候调用这两个函数,第一个参数可以不用传递。
    第二个参数logger *zap.Logger是为了log日志记录传递,不需要也可以不传递。

  1. 打开fx_opt/var.go,在ConstructorFuncs变量中添加新的参数srv.NewAbc,即可(srv.NewAbc是上面的代码创建的函数)。
var ConstructorFuncs = []interface{}{
	srv.NewAbc,
  • 如果NewAbc函数使用了lc fx.Lifecycle,则需要在fx_opt/var.goInvokeFuncs变量中添加func(srv.Abc) {}函数(该函数将注册到fx的invoke中)。如果没有使用lc fx.Lifecycle则不用添加。
var InvokeFuncs = []interface{}{
	func(srv.Abc) {}
//其他代码

  1. 启动服务器然后终止服务器,查看服务器控制台如下输出表示成功。
...
{"level":"info","message":"Abc开始构建"}
...
{"level":"info","message":"Abc开始销毁"}
...


添加新的实例到fx(结构体带不属于fx中实例的属性,New函数带不属于fx中实例的参数)

举例说明,若想在fx中创建实例只需以下两步。

  1. srv/文件夹中新建srv2.go,内容如下:
package srv

import (
	"context"

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

type Abc2 struct {
	logger  *zap.Logger
	Content string
}

func NewAbc2(lc fx.Lifecycle, logger *zap.Logger, content string) Abc2 {
	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			logger.Info("Abc2开始构建", zap.String("content", content))
			return nil
		},
		OnStop: func(ctx context.Context) error {
			logger.Info("Abc2开始销毁")
			return nil
		},
	})

	return Abc2{logger: logger, Content: content}
}
  • 和上一个案例基本相同,唯一不同之处是Abc2结构体中包含了不来自于fx的参数Content string,它同样来源于NewAbc2函数的参数content string,而传递参数的不同之处在下一步中体现。

  1. 打开fx_opt/var.go,在ConstructorFuncs变量中添加两个新的数据项fx.Annotate即可(srv.NewAbc2是上面的代码创建的函数)。
var ConstructorFuncs = []interface{}{
	//其他代码
	fx.Annotate(
		func() string {
			return "这是Abc2的Content参数"
		},
		fx.ResultTags(`name:"abc2content"`),
	),

	fx.Annotate(
		srv.NewAbc2,
		fx.ParamTags(``, ``, `name:"abc2content"`),
	),
  • 上面第一个fx.Annotate声明将会作为srv.NewAbc2函数的content string参数,进行依赖注入。
  • 上面第一个fx.Annotate中的fx.ResultTags(`name:"abc2content"`)会匹配第二个fx.Annotate对象的fx.ParamTags(``, ``, `name:"abc2content"`)的第三项。
  • 第二个fx.Annotate中的参数``, ``, `name:"abc2content"`按顺序匹配了NewAbc2(lc fx.Lifecycle, logger zap.Logger, content string)的三个参数,第一项,第二项fx不需要name匹配,第三项则是第一个fx.Annotate*实例的第一个参数的内容。

  • 如果NewAbc2函数使用了lc fx.Lifecycle,则需要在fx_opt/var.goInvokeFuncs变量中添加func(srv.Abc2) {}函数(该函数将注册到fx的invoke中)。如果没有使用lc fx.Lifecycle则不用添加。
var InvokeFuncs = []interface{}{
//其他代码
	func(srv.Abc2) {}
//其他代码

  1. 启动服务器然后终止服务器,查看服务器控制台如下输出表示成功。
...
{"level":"info","message":"Abc2开始构建","content":"这是Abc2的Content参数"}
...
{"level":"info","message":"Abc2开始销毁"}
...


添加新的实例到fx(结构体包含刚在fx中注册的实例作为属性,New函数将刚在fx中注册的实例作为参数)

举例说明,将上一个例子中的Abc2作为NewAbc3的参数,若想在fx中创建实例只需以下两步。

  1. srv/文件夹中新建srv2.go,内容如下:
package srv

import (
	"context"

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

type Abc3 struct {
	logger  *zap.Logger
	Abc2   Abc2
}

func NewAbc3(lc fx.Lifecycle, logger *zap.Logger, abc2 Abc2) Abc3 {
	lc.Append(fx.Hook{
		OnStart: func(context.Context) error {
			logger.Info("Abc3开始构建", zap.Any("abc2", abc2))
			return nil
		},
		OnStop: func(ctx context.Context) error {
			logger.Info("Abc3开始销毁")
			return nil
		},
	})

	return Abc3{logger: logger, Abc2: abc2}
}
  • 和上两个案例基本相同,唯一不同之处是Abc3结构体中包含了刚在fx中注册的"Abc2"实例,它同样来源于NewAbc3函数,而传递参数的不同之处在下一步中体现。

  1. 打开fx_opt/var.go,在ConstructorFuncs变量中添加新的参数srv.NewAbc3,即可(srv.NewAbc是上面的代码创建的函数)。
var ConstructorFuncs = []interface{}{
	//其他代码
	srv.NewAbc3,
  • 如果NewAbc3函数使用了lc fx.Lifecycle,则需要在fx_opt/var.goInvokeFuncs变量中添加func(srv.Abc2) {}函数(该函数将注册到fx的invoke中)。如果没有使用lc fx.Lifecycle则不用添加。
var InvokeFuncs = []interface{}{
//其他代码
	func(srv.Abc3) {}
//其他代码

  1. 启动服务器然后终止服务器,查看服务器控制台如下输出表示成功。
...
{"level":"info","message":"Abc3开始构建","abc2":{"Content":"这是Abc2的Content参数"}}
...
{"level":"info","message":"Abc3开始销毁"}
...


其他说明

项目依赖的三方库

UBER开源的fx框架作为实例管理以及依赖注入框架
日志管理使用了UBER开源日志框架的zap
HTTP服务使用了gorilla/mux


项目结构

用户需要关注的文件夹以 * 提示

├── component *存放集成到项目的三方模块*
├── conf
│   ├── conf.json *配置文件*
│   └── model.go 
├── http
│   ├── handler *http的handler文件夹*
│   │   ├── echo.go *案例*
│   │   └── ...
│   └── middleware *http请求的拦截器文件夹*
│       ├── log.go *案例*
│       └── ...
└── srv *需要注册到fx中的服务*
    ├── srv1.go *案例*
    ├── ...
├── fx_opt fx操作文件夹
│   ├── component
│   │   ├── invoke fx的invoke
│   │   │   └── router.go
│   │   └── provide fx的provide
│   │       ├── conf.go 
│   │       ├── http
│   │       │   ├── http_middleware.go
│   │       │   ├── http_mux.go
│   │       │   ├── http_route.go
│   │       │   └── http_server.go
│   │       └── logger.go
│   │       └── context.go
│   ├── srv.go fx服务
│   └── var.go fx变量
├── LICENSE 项目开源声明
├── main.go 主函数
├── cmd 项目启动命令行
│   └── config.go
├── Makefile 
├── README.md 
├── go.mod
├── go.sum

master分支提供的功能

  • HTTP Server Router gorilla/muxl路由支持,可自定义middleware。
  • Logger UBER/zap日志系统。
  • Flag go原生的flag支持。
  • Config File 支持读取配置文件
  • 将来会开辟不同分支来将常用的三方中间件与Fx框架融合。

编译

若需要二开可以进行编译

# 使用makefile可以对linux和windows进行编译
make build-linux
make build-windows

分支说明

项目中的init_project, http_server, register_handler, many_handlers, logger, decouple_registration, many_handlers分支为Fx的教学示例。

master分支最fx-tool脚手架所搭建的基础版本项目。

Fx的基础使用方法还可以参考官网docs(https://uber-go.github.io/fx/get-started/)。

Documentation

The Go Gopher

There is no documentation for this package.

Directories

Path Synopsis
http

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL
JackTT - Gopher 🇻🇳