gitee.com/woood2/luca@v1.0.4/docs/README.md (about) 1 2 ### 概述 3 LUCA Enterprise Framework(简称luca)是一款支持多入口、开箱即用的企业级Golang框架。 4 在设计哲学上,luca更接近于 [整洁架构](https://book.douban.com/subject/30333919/) ,而非 [Spring](https://spring.io/) 这种包办一切的大管家。 5 - 支持4种作业类型:Restful服务、微服务、定时任务、队列消费。 6 - 配套设施整合:注册/配置中心、链路追踪、日志收集、指标监控、熔断器、API文档。 7 - 程序设计实践:错误处理、分层、依赖注入、数据库、缓存、多环境、优雅关闭、灰度发布、单元测试。 8 9  10 11 ### 初始化 12 ``` 13 wget https://gitee.com/woood2/luca/raw/master/scripts/gen.sh 14 chmod +x ./gen.sh 15 ./gen.sh 16 ``` 17 18 ### 目录结构 19 遵循:[社区规范](https://github.com/golang-standards/project-layout/blob/master/README_zh.md) 20 21 ``` 22 ├── cmd 23 │ ├── backend Restful服务 24 │ │ ├── README.md 25 │ │ ├── docs swagger文档 26 │ │ │ ├── docs.go 27 │ │ │ ├── swagger.json 28 │ │ │ └── swagger.yaml 29 │ │ ├── internal 30 │ │ │ ├── assembly 装配器 31 │ │ │ │ ├── route.go 32 │ │ │ │ └── sdk.go 33 │ │ │ ├── controller C层 34 │ │ │ │ └── app.go 35 │ │ │ └── middleware 中间件 36 │ │ │ ├── auth.go 37 │ │ │ ├── metrics.go 38 │ │ │ ├── oplog.go 39 │ │ │ ├── perm.go 40 │ │ │ └── trace.go 41 │ │ └── main.go 42 │ ├── consumer 队列消费 43 │ │ ├── internal 44 │ │ │ ├── alarm 告警 45 │ │ │ │ ├── alarm.go 46 │ │ │ │ └── watcher.go 47 │ │ │ ├── handler C层 48 │ │ │ │ ├── greet_one.go 49 │ │ │ │ ├── greet_two.go 50 │ │ │ │ └── groups.go 51 │ │ │ ├── middleware 中间件 52 │ │ │ │ ├── logging.go 53 │ │ │ │ ├── metrics.go 54 │ │ │ │ └── trace.go 55 │ │ │ └── subscriber 订阅者 56 │ │ │ ├── chain.go 57 │ │ │ ├── msg_handler.go 58 │ │ │ ├── retry_consumer.go 59 │ │ │ └── subscriber.go 60 │ │ └── main.go 61 │ ├── cron 定时任务 62 │ │ ├── internal 63 │ │ │ ├── handler C层 64 │ │ │ │ ├── greet.go 65 │ │ │ │ ├── hello.go 66 │ │ │ │ └── set.go 67 │ │ │ ├── job 作业 68 │ │ │ │ ├── chain.go 69 │ │ │ │ ├── job.go 70 │ │ │ │ └── list.go 71 │ │ │ └── middleware 中间件 72 │ │ │ ├── logging.go 73 │ │ │ └── trace.go 74 │ │ └── main.go 75 │ └── micro 微服务 76 │ ├── internal 77 │ │ ├── endpoint C层 78 │ │ │ ├── check_perm.go 79 │ │ │ ├── check_token.go 80 │ │ │ └── validate.go 81 │ │ ├── grpc 通信层 82 │ │ │ ├── check_perm.go 83 │ │ │ ├── check_token.go 84 │ │ │ ├── grpc_health_v1 85 │ │ │ │ ├── health.pb.go 86 │ │ │ │ ├── health.proto 87 │ │ │ │ ├── health_grpc.pb.go 88 │ │ │ │ ├── health_impl.go 89 │ │ │ │ └── protoc.sh 90 │ │ │ ├── pb 91 │ │ │ │ ├── protoc.sh 92 │ │ │ │ ├── server.pb.go 93 │ │ │ │ ├── server.proto 94 │ │ │ │ └── server_grpc.pb.go 95 │ │ │ └── set.go 96 │ │ ├── middleware 中间件 97 │ │ │ ├── auth 98 │ │ │ │ ├── context.go 99 │ │ │ │ ├── middleware.go 100 │ │ │ │ ├── option.go 101 │ │ │ │ └── token.go 102 │ │ │ ├── chain.go 103 │ │ │ ├── logging 104 │ │ │ │ └── middleware.go 105 │ │ │ └── metrics 106 │ │ │ └── middleware.go 107 │ │ ├── output 输出 108 │ │ │ └── format.go 109 │ │ └── param 参数 110 │ │ ├── check_perm.go 111 │ │ └── check_token.go 112 │ ├── main.go 113 │ └── pkg SDK 114 │ ├── check_perm.go 115 │ ├── check_token.go 116 │ ├── err.go 117 │ └── sdk.go 118 ├── configs 配置文件 119 │ ├── application-example.yml 120 │ └── application.yml 121 ├── deployments 部署 122 │ ├── docker 123 │ │ ├── docker-compose-example.yml 124 │ │ ├── docker-compose.yml 125 │ │ ├── prometheus-example.yml 126 │ │ └── prometheus.yml 127 │ ├── nav-example.html 128 │ └── nav.html 129 ├── docs 文档 130 ├── go.mod 131 ├── go.sum 132 ├── internal 133 │ ├── cache 缓存 134 │ │ ├── cache.go 135 │ │ ├── dict.go 136 │ │ ├── key.go 137 │ │ ├── local.go 138 │ │ ├── local_test.go 139 │ │ ├── miss.go 140 │ │ ├── nop.go 141 │ │ ├── protector.go 142 │ │ ├── redis.go 143 │ │ ├── redis_test.go 144 │ │ └── serialization.go 145 │ ├── conf 配置 146 │ │ └── attr.go 147 │ ├── db 数据库 148 │ │ ├── gorm.go 149 │ │ ├── mongo.go 150 │ │ └── zapgorm2 151 │ │ └── zapgorm2.go 152 │ ├── discovery 服务发现 153 │ │ └── consul.go 154 │ ├── errcode 错误 155 │ │ ├── business.go 156 │ │ ├── micro.go 157 │ │ ├── resp.go 158 │ │ └── restful.go 159 │ ├── layer 分层 160 │ │ ├── controller.go 161 │ │ ├── repository.go 162 │ │ └── service.go 163 │ ├── log 日志 164 │ │ └── zap.go 165 │ ├── mq 消息 166 │ │ ├── greet.go 167 │ │ └── greet_test.go 168 │ ├── producer 队列生产 169 │ │ ├── sarama_producer.go 170 │ │ └── scram_client.go 171 │ ├── repository DAO层 172 │ │ ├── app.go 173 │ │ ├── app_test.go 174 │ │ ├── student.go 175 │ │ └── student_test.go 176 │ ├── sdk SDK引用 177 │ │ └── demo.go 178 │ ├── service service层 179 │ │ ├── acl.go 180 │ │ ├── app.go 181 │ │ └── app_test.go 182 │ ├── status 状态 183 │ │ ├── ginpprof.go 184 │ │ ├── hystrix.go 185 │ │ └── prometheus.go 186 │ ├── trace 链路追踪 187 │ │ └── zipkin.go 188 │ └── util 工具 189 │ ├── file.go 190 │ ├── jsontime.go 191 │ ├── md5.go 192 │ └── test.go 193 ├── scripts 脚本 194 │ ├── init.sql 195 │ ├── init.sh 196 └── test 197 └── grpc_client 微服务本地测试 198 └── main.go 199 ``` 200 201 ### 多入口 202 [cmd/](https://gitee.com/woood2/luca/tree/master/cmd) 默认拥有4个子目录,对应4个入口、4种作业类型。 203 ``` 204 ├── cmd 205 │ ├── backend Restful服务 206 │ │ ├── README.md 207 │ │ ├── docs 208 │ │ ├── internal 209 │ │ └── main.go 210 │ ├── consumer 队列消费 211 │ │ ├── internal 212 │ │ └── main.go 213 │ ├── cron 定时任务 214 │ │ ├── internal 215 │ │ └── main.go 216 │ └── micro 微服务 217 │ ├── internal 218 │ ├── main.go 219 │ └── pkg 220 ``` 221 222 入口专用的代码存放于 [cmd/](https://gitee.com/woood2/luca/tree/master/cmd) 的子目录中,跨入口公用的代码存放于 [internal/](https://gitee.com/woood2/luca/tree/master/internal) 下,例如: 223 - backend Controller层代码:[cmd/backend/internal/controller/](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/controller) 224 - Service层代码:[internal/service/](https://gitee.com/woood2/luca/tree/master/internal/service) 225 - Repository层代码:[internal/repository/](https://gitee.com/woood2/luca/tree/master/internal/repository) 226 227 同一种作业类型,可以创建多个入口。例如:项目中允许存在`backend`与`frontend`两个Restful服务。 228 229 示例1:添加`frontend`入口 230 1. 复制目录:`cp -a cmd/backend cmd/frontend` 231 2. 在 `cmd/frontend` 路径下批量替换:`backend=>frontend`、`Backend=>Frontend` 232 3. 修改配置:[internal/conf/attr.go](https://gitee.com/woood2/luca/tree/master/internal/conf/attr.go) 、[configs/application-example.yml](https://gitee.com/woood2/luca/tree/master/configs/application-example.yml) 233 234 示例2:删除`frontend`入口 235 1. 删除目录:`rm -rf cmd/frontend` 236 2. 修改配置:[internal/conf/attr.go](https://gitee.com/woood2/luca/tree/master/internal/conf/attr.go) 、[configs/application-example.yml](https://gitee.com/woood2/luca/tree/master/configs/application-example.yml) 237 238 ### 运行 239 步骤1:安装Go环境以及所依赖的服务,详见 [deployments/README.md](https://gitee.com/woood2/luca/tree/master/deployments/README.md) 。 240 241 步骤2:执行init脚本,键入本机IP,生成 `configs/application.yml`。 242 ``` 243 cd scripts 244 ./init.sh 245 ``` 246 247 步骤3:按需修改 `configs/application.yml`。 248 249 步骤4:执行 `go run cmd/{xyz}/main.go`。 250 251 ### 配置 252 文件路径 253 - 本地开发:`configs/application.yml` 254 - 远程部署:`程序运行目录/application.yml` 255 256 文件结构 257 - stub:用于设置本机host以及配置类型。 258 - detail:可供多个go程序实例复用的详细配置,根据stub设置决定其存放位置。 259 260 stub详解 261 262 | 一级 | 二级 | 说明 | 示例 | 263 | ---- | ---- | ---- | ---- | 264 | host | | 本机IP地址 | 192.168.1.100 | 265 | configType | | file:本地文件 consul:配置中心 | consul | 266 | dataKey | | consul kv键名 | config/luca/attr | 267 | consul | | | | 268 | | host | consul主机 | 192.168.1.100 | 269 | | port | consul端口 | 8500 | 270 271 本地文件示例 272 ``` 273 application.yml 274 275 # === stub part === 276 host: {ip} 277 configType: file 278 dataKey: 279 consul: 280 host: 281 port: 282 # === detail part === 283 project: luca 284 mysql: 285 user: root 286 pwd: '123456' 287 host: {ip} 288 port: 3306 289 db: luca 290 ``` 291 292 配置中心示例 293 ``` 294 application.yml 295 296 # === stub part === 297 host: {ip} 298 configType: consul 299 dataKey: config/luca/attr 300 consul: 301 host: {ip} 302 port: 8500 303 304 访问consul后台,配置KV字典如下: 305 < Key/Values < config < luca 306 attr 307 308 Value 309 # === detail part === 310 project: luca 311 mysql: 312 user: root 313 pwd: '123456' 314 host: {ip} 315 port: 3306 316 db: luca 317 ``` 318 319 detail详解 320 321 | 一级 | 二级 | 说明 | 示例 | 322 | ---- | ---- | ---- | ---- | 323 | project | | 项目名 | luca | 324 | env | | 环境:dev/demo/test/pre/pro | dev | 325 | consoleLog | | true:控制台日志<br/>false:文件日志 | true | 326 | pprof | | | | 327 | | user | 用户名 | qjs | 328 | | pwd | 密码 | 731 | 329 | mysql | | | | 330 | | user | 用户名 | root | 331 | | pwd | 密码 | 123456 | 332 | | host | 主机 | 192.168.1.100 | 333 | | port | 端口 | 3306 | 334 | | db | 数据库 | luca | 335 | | maxOpenConns | 最大连接数 | 30 | 336 | | maxIdleConns | 最大空闲连接数 | 10 | 337 | mongo | | | | 338 | | user | 用户名 | woood2 | 339 | | pwd | 密码 | 123456 | 340 | | host | 主机 | 192.168.1.100 | 341 | | port | 端口 | 27017 | 342 | | db | 数据库 | luca | 343 | | maxPoolSize | 最大连接数 | 30 | 344 | | minPoolSize | 最小连接数 | 10 | 345 | redis | | | | 346 | | addr | 地址 | 192.168.1.100:6379 | 347 | | pwd | 密码 | | 348 | | poolSize | 最大连接数 | 30 | 349 | | minIdleConns | 最小空闲连接数 | 10 | 350 | zipkin | | | | 351 | | reporterAddr | 上报地址(允许为空)| 192.168.1.100:6379 | 352 | kafka | | | | 353 | | version | 版本 | 2.5.0 | 354 | | brokers | broker地址(允许多个) | 192.168.1.100:9092 | 355 | | enableSASL | 是否开启SASL | true | 356 | | user | 用户名 | qjs | 357 | | password | 密码 | 731 | 358 | | algorithm | 算法 | sha256 | 359 | backend | | | | 360 | | addr | Restful服务地址 | :8080 | 361 | | register | 是否注册到consul | true | 362 | | pprofAddr | pprof地址 | :6059 | 363 | | metricsAddr | metrics地址 | :8082 | 364 | | hystrixPort | hystrix端口 | 8282 | 365 | | allowOrigins | 允许来源(允许多个) | * | 366 | micro | | | | 367 | | port | 端口 | 8081 | 368 | | register | 是否注册到consul | true | 369 | | pprofAddr | pprof地址 | :6060 | 370 | | metricsAddr | metrics地址 | :6061 | 371 | | hystrixPort | hystrix端口 | 8283 | 372 | consumer | | | | 373 | | pprofAddr | pprof地址 | :6064 | 374 | | metricsAddr | metrics地址 | :6063 | 375 | | hystrixPort | hystrix端口 | 8084 | 376 | cron | | | | 377 | | pprofAddr | pprof地址 | :6065 | 378 | | hystrixPort | hystrix端口 | 8085 | 379 380 ### 错误处理 381 luca倡导在项目中基于 [github.com/pkg/errors](https://github.com/pkg/errors) 传递 error,而非使用 panic-recover。 382 383 统一的错误字典 384 - Restful服务通用错误:[internal/errcode/restful.go](https://gitee.com/woood2/luca/tree/master/internal/errcode/restful.go) 385 - 微服务通用错误:[internal/errcode/micro.go](https://gitee.com/woood2/luca/tree/master/internal/errcode/micro.go) 386 - 业务逻辑错误:[internal/errcode/business.go](https://gitee.com/woood2/luca/tree/master/internal/errcode/business.go) 387 388 为了对调用者友好,微服务SDK冗余了一份错误字典 389 - 微服务通用错误:[cmd/micro/pkg/err_micro.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/pkg/err_micro.go) 390 - 业务逻辑错误:[cmd/micro/pkg/err_business.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/pkg/err_business.go) 391 392 更新方式 393 ``` 394 cd cmd/micro/pkg 395 ./update_err.sh 396 ``` 397 398 Resp结构体:[internal/errcode/resp.go](https://gitee.com/woood2/luca/tree/master/internal/errcode/resp.go) 399 ``` 400 package errcode 401 //4 restful & micro service 402 type Resp struct { 403 ErrCode string `json:"errCode"` 404 ErrMsg string `json:"errMsg"` 405 Data interface{} `json:"data"` 406 TraceID string `json:"traceID"` //only 4 restful 407 Stack string `json:"stack"` //only 4 restful, non-pro env 408 } 409 ``` 410 411 一次`Restful请求`的错误处理 412 - 当程序走入错误分支或者发生内部错误,一个`error`对象被逐层传递到最上层的`controller`中。 413 - 持有这个`error`对象的`controller`前往`错误字典`查找对应的`错误代码`,并封装成`Resp`对象作为http返回值。 414 - 每个`错误代码`对应一条程序分支,通常由用户的错误操作引发,例如"用户名或密码错误",无需记录日志,`http code`为200。 415 - 找不到`错误代码`的`error`统一归为`errcode.ServerErr`并且记录一条error等级的日志,`http code`为500。 416 - 顺利走完程序主分支的成功请求,`Resp.ErrCode`与`Resp.ErrMsg`均为空值,`Resp.Data`携带返回值正文,`http code`为200。 417 418 一次`微服务调用`的错误处理 419 - 当程序走入错误分支或者发生内部错误,一个`error`对象被逐层传递到最上层的`endpoint`中。 420 - 持有这个`error`对象的`endpoint`前往`错误字典`查找对应的`错误代码`,并封装成`Resp`对象作为返回值。 421 - 每个`错误代码`对应一条程序分支,通常由client的非法调用引发,例如"非法的鉴权凭证",无需记录日志,无需对接`hystrix`。 422 - 找不到`错误代码`的`error`需要记录一条error等级的日志,并且对接`hystrix`。 423 - 顺利走完程序主分支的成功调用,`Resp.ErrCode`与`Resp.ErrMsg`均为空值,`Resp.Data`携带返回值正文。 424 425 Crash-free 426 - 尽管luca不主张使用`panic-recover`,但无法保证第三方库没有`panic`,必须在`goroutine`级别进行`recover`,保障整个进程的运行安全。 427 - 捕获`panic`后,任何作业类型均会记录一条error等级的日志。 428 - Restful请求中发生`panic`,`http code`为500。 429 - 微服务调用中发生`panic`,需要对接`hystrix`,error日志由调用端(SDK)负责记录。 430 431 ### Restful服务 432 luca基于 [gin](https://github.com/gin-gonic/gin) 提供Restful服务。 433 434 示例 435 - 认证:[cmd/backend/internal/middleware/auth.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/middleware/auth.go) 436 - 授权:[cmd/backend/internal/middleware/perm.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/middleware/perm.go) 437 - validator:[cmd/backend/internal/controller/app.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/controller/app.go) 438 - CRUD:[cmd/backend/internal/controller/app.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/controller/app.go) 439 440 基于 [gin-contrib/cors](https://github.com/gin-contrib/cors) 支持CORS。 441 442 允许多个来源: 443 ``` 444 backend: 445 allowOrigins: 446 - "http://google.com" 447 - "http://facebook.com" 448 ``` 449 允许任意来源: 450 ``` 451 backend: 452 allowOrigins: 453 - "*" 454 ``` 455 456 基于 [swaggo/swag](https://github.com/swaggo/swag/blob/master/README_zh-CN.md) 支持API文档。 457 示例 458 - 全局注释:[cmd/backend/main.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/main.go) 459 - API注释:[cmd/backend/internal/controller/app.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/controller/app.go) 460 - 更新命令:`cd cmd/backend && swag init` 461 462 ### 微服务 463 luca 基于 [go-kit](https://github.com/go-kit/kit) 提供micro service。 464 465 详细选型 466 - 通信:[gRPC](https://doc.oschina.net/grpc) 467 - 注册/配置中心:[consul](https://developer.hashicorp.com/consul/docs/architecture) 468 - 熔断器:[hystrix](https://pkg.go.dev/github.com/afex/hystrix-go/hystrix) 469 - validator:[go-playground/validator](https://github.com/go-playground/validator) 470 471 API示例 472 - [cmd/micro/internal/grpc/check_perm.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/internal/grpc/check_perm.go) 473 - [cmd/micro/internal/grpc/check_token.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/internal/grpc/check_token.go) 474 475 SDK示例 476 - [cmd/micro/pkg/check_perm.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/pkg/check_perm.go) 477 - [cmd/micro/pkg/check_token.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/pkg/check_token.go) 478 479 本地测试 480 - [test/grpc_client/main.go](https://gitee.com/woood2/luca/tree/master/test/grpc_client/main.go) 481 482 ### 定时任务 483 luca基于 [robfig-cron](https://github.com/robfig/cron) 提供定时任务。 484 485 基于redis实现`Secondary Nodes`模式的高可用,详见 [main.go](https://gitee.com/woood2/luca/tree/master/cmd/cron/main.go) 中的`campaign()`与`renew()`。 486 487 job示例 488 - [cmd/cron/internal/handler/greet.go](https://gitee.com/woood2/luca/tree/master/cmd/cron/internal/handler/greet.go) 489 - [cmd/cron/internal/handler/hello.go](https://gitee.com/woood2/luca/tree/master/cmd/cron/internal/handler/hello.go) 490 491 ### 消息队列 492 luca 基于 [Shopify/sarama](https://github.com/Shopify/sarama) 支持 Kafka。 493 494 application.yml 495 ``` 496 kafka: 497 version: 2.5.0 498 brokers: 499 - {ip}:9092 500 enableSASL: true 501 user: qjs 502 password: '731' 503 algorithm: sha256 504 ``` 505 506 示例 507 - topic:[internal/mq/greet.go](https://gitee.com/woood2/luca/tree/master/internal/mq/greet.go) 508 - 生产:[internal/mq/greet_test.go](https://gitee.com/woood2/luca/tree/master/internal/mq/greet_test.go) 509 - 消费:[cmd/consumer/internal/handler/greet_one.go](https://gitee.com/woood2/luca/tree/master/cmd/consumer/internal/handler/greet_one.go) 510 511 ### 分层 512 推荐方案 513  514 515 controller层包括: 516 - Restful服务中的 [Controller](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/controller) 517 - 微服务中的 [Endpoint](https://gitee.com/woood2/luca/tree/master/cmd/micro/internal/endpoint) 518 - 定时任务中的 [Handler](https://gitee.com/woood2/luca/tree/master/cmd/cron/internal/handler) 519 - 队列消费中的 [Handler](https://gitee.com/woood2/luca/tree/master/cmd/consumer/internal/handler) 520 521 [service](https://gitee.com/woood2/luca/tree/master/internal/service) 层涵盖: 522 - Service: CRUD场景常见的服务层 523 - Domain: [《领域驱动设计》](https://book.douban.com/subject/5344973/) 描绘的领域层 524 525 [repository](https://gitee.com/woood2/luca/tree/master/internal/repository) 层负责操作ORM层,luca选择 [GORM](https://gorm.io/zh_CN/docs/index.html) 作为ORM框架。 526 527 [sdk](https://gitee.com/woood2/luca/tree/master/internal/sdk) 或 [kafka producer](https://gitee.com/woood2/luca/tree/master/internal/producer/sarama_producer.go) 可视作远程service。 528 529 ### 依赖注入 530 在luca中,依赖注入随处可见: 531 532 | 被依赖的对象 | 接收注入的对象 | 533 | ---- | ---- | 534 | logger | 任何需要记录日志的代码 | 535 | db(mysql、redis、mongo等) | repository | 536 | consul client | sdk | 537 | kafka producer | controller、service | 538 | sdk | controller、service | 539 | service | controller | 540 | repository | service | 541 542 在Spring的世界中,使用`@Autowired`即可自动装配。但是在luca中,我们需要在以下位置手动装配: 543 - [cmd/backend/internal/assembly/route.go](https://gitee.com/woood2/luca/tree/master/cmd/backend/internal/assembly/route.go) 544 - [cmd/micro/internal/grpc/set.go](https://gitee.com/woood2/luca/tree/master/cmd/micro/internal/grpc/set.go) 545 - [cmd/cron/internal/handler/set.go](https://gitee.com/woood2/luca/tree/master/cmd/cron/internal/handler/set.go) 546 - [cmd/consumer/internal/handler/groups.go](https://gitee.com/woood2/luca/tree/master/cmd/consumer/internal/handler/groups.go) 547 548 ### Mysql 549 luca选择 [GORM](https://gorm.io/zh_CN/docs/index.html) 作为ORM框架。 550 551 application.yml 552 ``` 553 mysql: 554 user: root 555 pwd: '123456' 556 host: {ip} 557 port: 3306 558 db: luca 559 maxOpenConns: 30 560 maxIdleConns: 10 561 ``` 562 563 示例 564 - CRUD:[internal/repository/app.go ](https://gitee.com/woood2/luca/tree/master/internal/repository/app.go) 565 - 事务:[AppServiceImpl.Twins](https://gitee.com/woood2/luca/tree/master/internal/service/app.go) 566 567 ### MongoDB 568 luca 基于 [mongo-go-driver](https://github.com/mongodb/mongo-go-driver) 操作MongoDB。 569 570 application.yml 571 ``` 572 mongo: 573 user: woood2 574 pwd: '123456' 575 host: {ip} 576 port: 27017 577 db: luca 578 maxPoolSize: 30 579 minPoolSize: 10 580 ``` 581 582 示例 583 - [internal/repository/student.go](https://gitee.com/woood2/luca/tree/master/internal/repository/student.go) 584 585 ### 缓存 586 luca提供了统一的 [Cache](https://gitee.com/woood2/luca/tree/master/internal/cache/cache.go) 接口以及3种默认实现: 587 - [LocalCache](https://gitee.com/woood2/luca/tree/master/internal/cache/local.go):基于本地内存的cache实现,性能水平100w/sec。 588 - [RedisCache](https://gitee.com/woood2/luca/tree/master/internal/cache/redis.go) :基于redis的cache实现,性能水平1w/sec。 589 - [NopCache](https://gitee.com/woood2/luca/tree/master/internal/cache/nop.go):基于「无」的cache实现。 590 591 其中,[RedisCache](https://gitee.com/woood2/luca/tree/master/internal/cache/redis.go) 基于 [go-redis](https://github.com/go-redis/redis) 操作Redis,对应配置: 592 ``` 593 redis: 594 addr: {ip}:6379 595 pwd: '' 596 poolSize: 30 597 minIdleConns: 10 598 ``` 599 600 默认采用json序列化,允许修改 [serialization.go](https://gitee.com/woood2/luca/tree/master/internal/cache/serialization.go) 替换成其它序列化算法。 601 602 示例 603 - Local: [local_test.go](https://gitee.com/woood2/luca/tree/master/internal/cache/local_test.go) 604 - Redis: [redis_test.go](https://gitee.com/woood2/luca/tree/master/internal/cache/redis_test.go) 605 - 防雪崩:[AppServiceImpl.GetByKeyFromCacheSafely](https://gitee.com/woood2/luca/tree/master/internal/service/app.go) 606 607 ### logger 608 日志维度 609 610 | 日志维度 | 说明 | 日志库 | 输出 | 接入 [zipkin](https://zipkin.io/) | 接入 [EFK](https://blog.csdn.net/easylife206/article/details/112057417) | 611 | ---- | ---- | ---- | ---- | ---- | ---- | 612 | 服务日志 | 服务运行周期中的启动、关闭和状态日志 | 标准库 [log](https://pkg.go.dev/log) | stdout/stderr | 否 | 否 | 613 | 请求日志 | 请求/作业周期内产生的日志 | [uber-go/zap](https://github.com/uber-go/zap) | stdout/stderr或者./zap.log文件 | 是 | 是 | 614 615 application.yml(zap配置) 616 617 | 名称 | 说明 | 618 | ---- | ---- | 619 | env | pro: 日志等级Info<br/>其它: 日志等级Debug | 620 | consoleLog | true: 控制台日志<br/>false: 文件日志(./zap.log) | 621 622 内置日志点(zap logger) 623 624 | 围绕 | 类型 | 说明 | 等级 | 625 | ---- | ---- | ---- | ---- | 626 | 请求/作业 | | | | 627 | | access日志 | 覆盖所有作业类型 | info | 628 | | error日志 | 覆盖所有作业类型 | error | 629 | | oplog日志 | 用于Restful服务 | info | 630 | GORM | | | | 631 | | sql日志 | | debug | 632 | | slow日志 | | warn | 633 | | error日志 | | error | 634 | SDK | | | | 635 | | call日志 | | debug | 636 | | error日志 | | error | 637 638 ### 链路追踪 639 luca 选择 [zipkin](https://zipkin.io/) 作为链路追踪的实现引擎。 640 641 跟踪范围 642 - Restful请求 643 - 微服务调用 644 - 定时任务作业 645 - 队列消费作业 646 - 其它,如单元测试 647 648 从 gin.Context 提取 TraceID: 649 ``` 650 func (ctr AnyController) Demo(c *gin.Context) { 651 traceID := trace.GinID(c) 652 ... 653 } 654 ``` 655 656 从 context.Context 提取 TraceID: 657 ``` 658 func(ctx context.Context){ 659 traceID := trace.ID(ctx) 660 ... 661 } 662 ``` 663 664 向 GORM logger 传递 TraceID: 665 ``` 666 func (repo *AppRepoImpl) Create(ctx context.Context, app *App) (int, error) { 667 result := repo.DB.WithContext(ctx).Create(app) 668 ... 669 } 670 ``` 671 672 向 Restful 请求返回 TraceID: 673 ``` 674 package errcode 675 676 type Resp struct { 677 ErrCode string `json:"errCode"` 678 ErrMsg string `json:"errMsg"` 679 Data interface{} `json:"data"` 680 TraceID string `json:"traceID"` 681 Stack string `json:"stack"` 682 } 683 ``` 684 685 application.yml 686 687 | 一级 | 二级 | 说明 | 示例 | 688 | ---- | ---- | ---- | ---- | 689 | zipkin | | | | 690 | | reporterAddr | 上报地址(允许为空) | http://192.168.0.100:9411/api/v2/spans | 691 692 zipkin server 693 - 安装:[deployments/README.md](https://gitee.com/woood2/luca/tree/master/deployments/README.md) 694 - 访问:`deployments/nav.html` 695 - 为性能考虑,生产环境建议关闭zipkin server,只需要将Zipkin.ReporterAddr设置为空。 696 697 ### 日志收集 698 luca 选择 [EFK](https://blog.csdn.net/easylife206/article/details/112057417) 日志收集方案: 699 - 通过 [fluentd](https://docs.fluentd.org) 从 ./zap.log 文件中读取日志。 700 - 将收集来的日志数据存入 [elastic search](https://www.elastic.co/cn/what-is/elasticsearch) 。 701 - 通过 [Kibana](https://www.elastic.co/cn/kibana/) 界面查询日志。 702 703 EFK 安装、使用 704 - 详见 [deployments/EFK.md](https://gitee.com/woood2/luca/tree/master/deployments/EFK.md) 705 706 ### pprof 707 支持 [pprof](https://pkg.go.dev/net/http/pprof) 的Go进程: 708 - Restful服务 709 - 微服务 710 - 定时任务 711 - 队列消费 712 713 application.yml 714 ``` 715 pprof: 716 user: qjs 717 pwd: '731' 718 719 backend: 720 pprofAddr: ":6059" 721 722 micro: 723 pprofAddr: ":6060" 724 725 consumer: 726 pprofAddr: ":6064" 727 728 cron: 729 pprofAddr: ":6065" 730 ``` 731 732 访问 733 - 详见 `deployments/nav.html` 734 735 ### metrics 736 luca 为3种作业类型接入 [prometheus](https://prometheus.io/) 指标统计: 737 - Restful请求 738 - 微服务调用 739 - 队列消费作业 740 741 application.yml 742 ``` 743 backend: 744 metricsAddr: ":8082" 745 746 micro: 747 metricsAddr: ":6061" 748 749 consumer: 750 metricsAddr: ":6063" 751 ``` 752 753 安装 prometheus 754 - 详见 [deployments/README.md](https://gitee.com/woood2/luca/tree/master/deployments/README.md) 755 756 访问 prometheus 757 - 详见 `deployments/nav.html` 758 759 ### 多环境 760 推荐方案 761 762 | 环境 | env | 部署位置 | 部署套数 | 用途 |git分支 | zap等级 | swagger | 763 | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 764 | 本机 | dev | 本机 | 1/每人 | 开发 | 个人、feature、fixbug | Debug | 开启 | 765 | dev | dev | 远程 | 1 | 联调 | dev | Debug | 开启 | 766 | demo | demo | 远程 | 1 | 对客演示 | demo | Debug | 开启 | 767 | test | test | 远程 | \>=1 | 测试 | test | Debug | 开启 | 768 | pre | pre | 远程 | 1 | 发版演练 | pre | Debug | 开启 | 769 | pro | pro | 远程 | \>=1 | 生产 | master | Info | 关闭 | 770 771 本机 & dev 772 - 允许本机环境与dev环境共用同一套资源,如数据库、消息队列等。 773 - 允许本机程序单向依赖dev环境的微服务,只需在本机配置dev环境的consul地址,并且将Backend.Register、Micro.Register设置为false。 774 775 demo 776 - 假如demo环境的使用频率较低,建议按需构建。 777 - 考虑到demo环境的不确定性,暂不纳入 `git 分支策略`。 778 779 git 分支策略 780  781 782 git 分支命名 783 784 | 分支 | 命名规则 | 示例 | 785 | ---- | ---- | ---- | 786 | 主分支 | master | - | 787 | 开发 | dev | - | 788 | 演示 | demo | - | 789 | 测试 | test | - | 790 | 预生产 | pre | - | 791 | feature | {name}-feature | first-feature、second-feature | 792 | fixbug | {name}-fixbug | stackoverflow-fixbug | 793 | 个人 | 姓名全拼、缩写 | qiujiashu、qjs | 794 795 ### 优雅关闭 796 支持优雅关闭的Go进程 797 - Restful服务 798 - 微服务 799 - 定时任务 800 - 队列消费 801 802 工作原理 803 1. 接收来自kill命令的信号量。 804 2. 停止接收新的作业。 805 3. 等待正在进行中的作业完成或者超时(默认:5秒)。 806 4. 关闭进程。 807 808 ### 灰度发布 809 luca支持微服务及Restful服务的灰度发布,以实现程序的不停服更新。 810 811 前提 812 - 不论是Restful服务还是微服务,运行的实例数量必须是两个及以上,单机模式不支持灰度发布。 813 - Restful服务需要前置一个nginx反向代理,并且借助consul-template监视实例列表的变化。详见:[Load Balancing with NGINX and Consul Template](https://developer.hashicorp.com/consul/tutorials/load-balancing/load-balancing-nginx) 814 815 工作原理 816 - 发布程序的新版本时,分批重启集群中的实例。 817 - 实例重启前通知consul注销自己,将流量导向其它运行中的实例。 818 - 等待实例重启完成,再次注册到consul上迎回流量。 819 - 发布过程中,始终有实例运行,提供连续服务。 820 821 ### 单元测试 822 luca鼓励开发者采用主流的`table driven`模式进行单元测试。 823 824 单元测试的优先目标是计算逻辑,而不是IO。对于一般的CRUD,单元测试意义不大,API测试是更适合的选择。 825 826 luca支持在CI/CD中跳过指定的单元测试: 827 ``` 828 CLI 829 CI=true go test -v -cover ./... 830 831 internal/util/test.go 832 package util 833 834 import ( 835 "os" 836 "testing" 837 ) 838 839 func CI() bool { 840 return os.Getenv("CI") != "" 841 } 842 843 func SkipCI(t *testing.T) { 844 if CI() { 845 t.Skip("Skipping testing in CI environment") 846 } 847 } 848 849 xxx_test.go 850 func TestMain(m *testing.M) { 851 if !util.CI() { 852 attr := conf.Load("application.yml", "configs/application.yml") 853 .. 854 } 855 m.Run() 856 } 857 858 func TestXXX(t *testing.T) { 859 util.SkipCI(t) 860 .. 861 }