自己实现高性能IM-初始代码框架搭建并实现依赖注入
本文介绍了基本的kitex项目搭建,实现了log 的使用,使用依赖注入实现控制反转。需要注意的是,在windows上可能会出现问题,推荐在linux或mac开发。
项目主要使用了字节跳动的RPC框架kitex (opens new window)。
# RPC服务-kitex 项目搭建
git clone https://github.com/cloudwego/kitex-examples.git
把这个项目克隆下来,然后单独把hello拿出来,其他的删掉。使用go mod tidy
同步一下依赖,如果没有go.mod
就先go mod init xx
一下。
可以运行一下当前软件包,也就是服务端,然后再运行一下client里面的main.go这个单个文件,如果看到client不断输出response就说明成功了。
如果要实现自己的服务,只需要在thrift 文件中,定义自己的Req 结构体、Resp 结构体以及对应的服务函数即可。例如,编辑idl/auth.thrift
:
namespace go auth
include "base.thrift"
struct LoginReq {
1: required string username
2: required string password
}
struct LoginResp {
1: required base.BaseResp baseResp
2: optional string token
}
service AuthService {
LoginResp Login(LoginReq req)
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
然后运行kitex -module github.com/li1553770945/sheepim-auth-service -service sheepim-auth-service idl/auth.thrift
就可以生成服务端和客户端对应的代码了。
# HTTP 服务-hertz项目搭建
# 依赖注入改造
主要使用wire (opens new window)库实现,这里不在介绍安装方法。
所谓依赖注入,就是如果A需要B,则创建A的时候应该传入一个B的对象,而不是由A自己新建B的对象或者使用全局的B对象。
以下只是我习惯的写法,不一定对每个人都适用。
首先,新建一个container.go文件新建container类,然后把handler里面需要的都写成它的属性,比如一般kitex项目有个handler,handler里面我们需要调用自己的service,就可以把service的接口声明成container的属性。如果是service需要而其他地方不需要一般不需要写在container里面。
container是一个最顶层的类,我们可以把它的对象命名为APP,并且得到它需要使用单例模式,也就是说我们整个项目有且仅有一个container对象,并且整个项目只有这一个全局变量。
package container
import (
"github.com/li1553770945/sheepim-user-service/biz/infra/config"
"github.com/li1553770945/sheepim-user-service/biz/internal/service"
"sync"
)
type Container struct {
Config *config.Config
UserService user.IUserService
}
var APP *Container
var once sync.Once // 通过once实现单例模式
func GetGlobalContainer() *Container {
if APP == nil {
panic("APP在使用前未初始化")
}
return APP
}
func InitGlobalContainer(config *config.Config) {
once.Do(func() {
APP = GetContainer(config)
})
}
func NewContainer(config *config.Config, userService user.IUserService,
) *Container {
return &Container{
Config: config,
UserService: userService,
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
然后新建一个wire.go
实现一个GetContainer
函数:
//go:build wireinject
// +build wireinject
package container
import (
"github.com/google/wire"
"github.com/li1553770945/sheepim-user-service/biz/infra/config"
"github.com/li1553770945/sheepim-user-service/biz/infra/database"
"github.com/li1553770945/sheepim-user-service/biz/internal/repo"
"github.com/li1553770945/sheepim-user-service/biz/internal/service"
)
func GetContainer(env string) *Container {
panic(wire.Build(
//infra
config.NewConfig
//repo
repo.NewRepository,
database.NewDatabase,
//service
user.NewUserService,
NewContainer,
))
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
把所有的类的新建对象的函数写在这里,然后在这个目录下运行wire
命令。就会得到一个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 container
import (
"github.com/li1553770945/sheepim-user-service/biz/infra/config"
"github.com/li1553770945/sheepim-user-service/biz/infra/database"
"github.com/li1553770945/sheepim-user-service/biz/internal/repo"
"github.com/li1553770945/sheepim-user-service/biz/internal/service"
)
// Injectors from wire.go:
func GetContainer(env string) *Container {
cfg := config.NewConfig(env)
db := database.NewDatabase(cfg)
iRepository := repo.NewRepository(db)
iUserService := user.NewUserService(iRepository)
container := NewContainer(cfg, iUserService)
return container
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
我们在main里面调用InitGlobalContainer
就会初始化所有需要初始化的对象,handler里面就可以用GetGlobalContainer
得到全局的container,从而使用service了。