|
5 | 5 | [](https://github.com/infraboard/mcube/v2/releases) |
6 | 6 | [](https://github.com/infraboard/mcube/v2/blob/master/LICENSE) |
7 | 7 |
|
8 | | -mcube是一款用于构建微服务的工具箱, 通过Ioc来为微服务提供通用功能 |
| 8 | +[官方文档](https://www.mcube.top/docs/framework/) |
| 9 | + |
| 10 | +mcube是一款用于构建渐进式微服务(单体-->微服务)的框架, 让应用从单体无缝过渡到微服务, 同时提供丰富的配置即用的功能配置, |
| 11 | +只需简单配置就可拥有: |
| 12 | ++ Log: 支持文件滚动和Trace的日志打印 |
| 13 | ++ Metric: 支持应用自定义指标监控 |
| 14 | ++ Trace: 集成支持完整的全链路追踪(HTTP Server/GRPC Server/数据库...)以及自定义埋点 |
| 15 | ++ CORS: 资源跨域共享 |
| 16 | ++ Health Check: HTTP 和 GRPC 健康检查 |
| 17 | ++ API DOC: 基于Swagger的 API 文档 |
| 18 | + |
| 19 | +除了上面这些功能配置,还会用到很多三方工具, 也是配置即用: |
| 20 | ++ MySQL: Grom集成 |
| 21 | ++ MongoDB 官方驱动集成 |
| 22 | ++ Redis: go-redis集成 |
| 23 | ++ Kafka: kafka-go集成 |
| 24 | ++ 分布式缓存: 当前只适配了Redis |
| 25 | ++ 分布式锁: 当前只适配了Redis |
9 | 26 |
|
10 | 27 |  |
11 | 28 |
|
12 | | -下面将mysql的配置托管给ioc 可以大大简化配置,让我们快速获取到GORM DB对象 |
| 29 | +## 快速开始 |
| 30 | + |
| 31 | +下面是演示一个TestObject对象的注册与获取的基础功能: |
13 | 32 | ```go |
14 | | -package test_test |
| 33 | +package main |
15 | 34 |
|
16 | 35 | import ( |
17 | | - "testing" |
| 36 | + "context" |
| 37 | + "net/http" |
18 | 38 |
|
| 39 | + "github.com/gin-gonic/gin" |
19 | 40 | "github.com/infraboard/mcube/v2/ioc" |
20 | 41 | "github.com/infraboard/mcube/v2/ioc/config/datasource" |
| 42 | + "github.com/infraboard/mcube/v2/ioc/server" |
| 43 | + "gorm.io/gorm" |
21 | 44 | ) |
22 | 45 |
|
23 | | -// 具体对象 |
24 | | -func TestIocLoad(t *testing.T) { |
25 | | - // 查询注册的对象列表, 通过导入datasource库 完成注册 |
26 | | - t.Log(ioc.Config().List()) |
27 | | - |
28 | | - // 加载配置 |
29 | | - req := ioc.NewLoadConfigRequest() |
30 | | - req.ConfigFile.Enabled = true |
31 | | - req.ConfigFile.Path = "../etc/application.toml" |
32 | | - err := ioc.ConfigIocObject(req) |
33 | | - if err != nil { |
34 | | - panic(err) |
35 | | - } |
36 | | - |
37 | | - // 使用ioc对象(datasource配置 衍生对象), |
38 | | - // 这里为了避免Database配置被外部访问到 并没有直接选择暴露托管对象, 而是提供方法使用 |
39 | | - // ioc.Config().Get(DATASOURCE).(*dataSource).db |
40 | | - t.Log(datasource.DB()) |
41 | | -} |
42 | | -``` |
| 46 | +func main() { |
| 47 | + // 注册HTTP接口类 |
| 48 | + ioc.Api().Registry(&ApiHandler{}) |
43 | 49 |
|
44 | | -## 快速开始 |
| 50 | + // 开启配置文件读取配置 |
| 51 | + server.DefaultConfig.ConfigFile.Enabled = true |
| 52 | + server.DefaultConfig.ConfigFile.Path = "etc/application.toml" |
45 | 53 |
|
46 | | -下面是演示一个TestObject对象的注册与获取的基础功能: |
47 | | -```go |
48 | | -// 匿名对象 |
49 | | -func TestObjectLoad(t *testing.T) { |
50 | | - // 将*TestObject对象 注册到默认空间 |
51 | | - conf := ioc.Default() |
52 | | - conf.Registry(&TestObject{}) |
53 | | - fmt.Println(ioc.Default().List()) |
54 | | - |
55 | | - // 通过环境变量配置TestObject对象 |
56 | | - os.Setenv("ATTR1", "a1") |
57 | | - os.Setenv("ATTR2", "a2") |
58 | | - ioc.DevelopmentSetup() |
59 | | - |
60 | | - // 除了采用Get直接获取对象, 也可以通过Load动态加载, 等价于获取后赋值 |
61 | | - // ioc.Default().Get("*ioc_test.TestObject").(*TestObject) |
62 | | - obj := &TestObject{} |
63 | | - err := ioc.Default().Load(obj) |
| 54 | + // 启动应用 |
| 55 | + err := server.Run(context.Background()) |
64 | 56 | if err != nil { |
65 | | - t.Fatal(err) |
| 57 | + panic(err) |
66 | 58 | } |
67 | | - t.Log(obj) |
68 | 59 | } |
69 | 60 |
|
70 | | -type TestObject struct { |
71 | | - Attr1 string `toml:"attr1" env:"ATTR1"` |
72 | | - Attr2 string `toml:"attr2" env:"ATTR2"` |
| 61 | +type ApiHandler struct { |
| 62 | + // 继承自Ioc对象 |
73 | 63 | ioc.ObjectImpl |
74 | | -} |
75 | | -``` |
76 | | - |
77 | | -## 应用开发 |
78 | 64 |
|
79 | | -下面将演示一个上图中 应用开发区的一个HelloWorld应用, 完整代码请参考: [样例代码](./docs/example/) |
80 | | - |
81 | | -1. 定义Hello业务: helloworld包 |
82 | | -```go |
83 | | -// 1. 业务定义 |
84 | | -type HelloService interface { |
85 | | - Hello() string |
86 | | -} |
87 | | -``` |
88 | | - |
89 | | -2. 实现Hello业务: helloworld/impl包 |
90 | | -```go |
91 | | -func init() { |
92 | | - ioc.Controller().Registry(&HelloServiceImpl{}) |
93 | | -} |
94 | | - |
95 | | -// 业务逻辑实现类 |
96 | | -type HelloServiceImpl struct { |
| 65 | + // mysql db依赖 |
97 | 66 | db *gorm.DB |
| 67 | +} |
98 | 68 |
|
99 | | - ioc.ObjectImpl |
| 69 | +// 覆写对象的名称, 该名称名称会体现在API的路径前缀里面 |
| 70 | +// 比如: /simple/api/v1/module_a/db_stats |
| 71 | +// 其中/simple/api/v1/module_a 就是对象API前缀, 命名规则如下: |
| 72 | +// <service_name>/<path_prefix>/<object_version>/<object_name> |
| 73 | +func (h *ApiHandler) Name() string { |
| 74 | + return "module_a" |
100 | 75 | } |
101 | 76 |
|
102 | | -// 控制器初始化 |
103 | | -func (i *HelloServiceImpl) Init() error { |
104 | | - // 从Ioc总获取GORM DB对象, GORM相关配置已经托管给Ioc |
105 | | - // Ioc会负责GORM的配置读取和为你初始化DB对象实例,以及关闭 |
106 | | - i.db = datasource.DB() |
| 77 | +// 初始化db属性, 从ioc的配置区域获取共用工具 gorm db对象 |
| 78 | +func (h *ApiHandler) Init() error { |
| 79 | + h.db = datasource.DB() |
107 | 80 | return nil |
108 | 81 | } |
109 | 82 |
|
110 | | -// 具体业务逻辑 |
111 | | -func (i *HelloServiceImpl) Hello() string { |
112 | | - return "hello world" |
| 83 | +// API路由 |
| 84 | +func (h *ApiHandler) Registry(r gin.IRouter) { |
| 85 | + r.GET("/db_stats", func(ctx *gin.Context) { |
| 86 | + db, _ := h.db.DB() |
| 87 | + ctx.JSON(http.StatusOK, gin.H{ |
| 88 | + "data": db.Stats(), |
| 89 | + }) |
| 90 | + }) |
113 | 91 | } |
114 | 92 | ``` |
115 | 93 |
|
116 | | -3. 定义Helloworl API接口: helloword/api包 |
117 | | -```go |
118 | | -func init() { |
119 | | - ioc.Api().Registry(&HelloServiceApiHandler{}) |
120 | | -} |
| 94 | +## 应用开发 |
121 | 95 |
|
122 | | -// 3. 暴露HTTP接口 |
123 | | -type HelloServiceApiHandler struct { |
124 | | - // 依赖业务控制器 |
125 | | - // 使用ioc注解来从自动加载依赖对象, 等同于手动执行: |
126 | | - // h.svc = ioc.Controller().Get("*impl.HelloService").(helloworld.HelloService) |
127 | | - Svc helloworld.HelloService `ioc:"autowire=true;namespace=controllers"` |
| 96 | +### 标准化工程配置 |
128 | 97 |
|
129 | | - // 日志相关配置已经托管到Ioc中, 由于是私有属性,所有受到注入, 具体见下边初始化方法 |
130 | | - log *zerolog.Logger |
| 98 | +统一了项目的配置加载方式: |
131 | 99 |
|
132 | | - // 继承自Ioc对象 |
133 | | - ioc.ObjectImpl |
134 | | -} |
| 100 | +环境变量 |
| 101 | +配置文件 |
| 102 | +TOML |
| 103 | +YAML |
| 104 | +JSON |
| 105 | +下面是项目配置文件(etc/application.toml)内容: |
135 | 106 |
|
136 | | -// 对象自定义初始化 |
137 | | -func (h *HelloServiceApiHandler) Init() error { |
138 | | - h.log = log.Sub("helloworld.api") |
139 | | - return nil |
140 | | -} |
| 107 | +```toml |
| 108 | +[app] |
| 109 | +name = "simple" |
| 110 | +key = "this is your app key" |
141 | 111 |
|
142 | | -// API路由 |
143 | | -func (h *HelloServiceApiHandler) Registry(r gin.IRouter) { |
144 | | - r.GET("/", h.Hello) |
145 | | -} |
| 112 | +[http] |
| 113 | +host = "127.0.0.1" |
| 114 | +port = 8020 |
146 | 115 |
|
147 | | -// API接口具体实现 |
148 | | -func (h *HelloServiceApiHandler) Hello(c *gin.Context) { |
149 | | - // 业务处理 |
150 | | - resp := h.Svc.Hello() |
151 | | - h.log.Debug().Msg(resp) |
| 116 | +[datasource] |
| 117 | +host = "127.0.0.1" |
| 118 | +port = 3306 |
| 119 | +username = "root" |
| 120 | +password = "123456" |
| 121 | +database = "test" |
152 | 122 |
|
153 | | - // 业务响应 |
154 | | - c.JSON(http.StatusOK, gin.H{ |
155 | | - "data": resp, |
156 | | - }) |
157 | | -} |
| 123 | +[log] |
| 124 | +level = "debug" |
| 125 | + |
| 126 | +[log.file] |
| 127 | +enable = true |
| 128 | +file_path = "logs/app.log" |
158 | 129 | ``` |
159 | 130 |
|
160 | | -4. 加载业务包 启动服务: main |
| 131 | +### 即插即用的组件 |
| 132 | + |
| 133 | +通过简单的配置就能为项目添加: |
| 134 | + |
| 135 | +检查检查(Health Chcek) |
| 136 | +应用指标监控(Metric) |
| 137 | + |
161 | 138 | ```go |
162 | 139 | import ( |
163 | | - ... |
164 | | - // 加载业务模块 |
165 | | - _ "github.com/infraboard/mcube/v2/docs/example/helloworld/api" |
166 | | - _ "github.com/infraboard/mcube/v2/docs/example/helloworld/impl" |
| 140 | + // 开启Health健康检查 |
| 141 | + _ "github.com/infraboard/mcube/v2/ioc/apps/health/gin" |
| 142 | + // 开启Metric |
| 143 | + _ "github.com/infraboard/mcube/v2/ioc/apps/metric/gin" |
167 | 144 | ) |
168 | | - |
169 | | -func main() { |
170 | | - req := ioc.NewLoadConfigRequest() |
171 | | - // 配置文件默认路径: etc/applicaiton.toml |
172 | | - req.ConfigFile.Enabled = true |
173 | | - err := ioc.ConfigIocObject(req) |
174 | | - if err != nil { |
175 | | - panic(err) |
176 | | - } |
177 | | - |
178 | | - // 启动应用, 应用会自动加载 刚才实现的Gin Api Handler |
179 | | - err = application.App().Start(context.Background()) |
180 | | - if err != nil { |
181 | | - panic(err) |
182 | | - } |
183 | | -} |
184 | 145 | ``` |
185 | 146 |
|
186 | | -5. 启动程序, 配置文件请参考: [程序配置](https://github.com/infraboard/mcube/v2/blob/master/docs/example/etc/application.toml) |
| 147 | +启动过后, 在日志里就能看到这2个功能开启了: |
187 | 148 | ```sh |
188 | | -$ go run main.go |
189 | | -2023-11-14T17:40:32+08:00 INFO config/application/application.go:93 > loaded configs: [log.v1 app.v1 datasource.v1] component:APPLICATION |
190 | | -2023-11-14T17:40:32+08:00 INFO config/application/application.go:94 > loaded controllers: [log.v1 app.v1 datasource.v1] component:APPLICATION |
191 | | -2023-11-14T17:40:32+08:00 INFO config/application/application.go:95 > loaded apis: [*api.HelloServiceApiHandler.v1] component:APPLICATION |
192 | | -[GIN-debug] [WARNING] Creating an Engine instance with the Logger and Recovery middleware already attached. |
193 | | - |
194 | | -[GIN-debug] [WARNING] Running in "debug" mode. Switch to "release" mode in production. |
195 | | - - using env: export GIN_MODE=release |
196 | | - - using code: gin.SetMode(gin.ReleaseMode) |
197 | | - |
198 | | -[GIN-debug] GET /exapmle/api/v1/ --> github.com/infraboard/mcube/v2/docs/example/helloworld/api.(*HelloServiceApiHandler).Hello-fm (3 handlers) |
199 | | -2023-11-14T17:40:32+08:00 INFO config/application/http.go:165 > HTTP服务启动成功, 监听地址: 127.0.0.1:8020 component:HTTP |
| 149 | +2024-01-05T11:30:00+08:00 INFO health/gin/check.go:52 > Get the Health using http://127.0.0.1:8020/healthz component:HEALTH_CHECK |
| 150 | +2024-01-05T11:30:00+08:00 INFO metric/gin/metric.go:51 > Get the Metric using http://127.0.0.1:8020/metrics component:METRIC |
| 151 | +``` |
| 152 | + |
| 153 | +当然你也可以通过配置来修改功能的URL路径: |
| 154 | +```toml |
| 155 | +[health] |
| 156 | + path = "/healthz" |
| 157 | + |
| 158 | +[metric] |
| 159 | + enable = true |
| 160 | + provider = "prometheus" |
| 161 | + endpoint = "/metrics" |
200 | 162 | ``` |
0 commit comments