1 Star 0 Fork 0

woood2 / luca

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

概述

LUCA Enterprise Framework(简称luca)是一款支持多入口、开箱即用的企业级Golang框架。 在设计哲学上,luca更接近于 整洁架构 ,而非 Spring 这种包办一切的大管家。

  • 支持4种作业类型:Restful服务、微服务、定时任务、队列消费。
  • 配套设施整合:注册/配置中心、链路追踪、日志收集、指标监控、熔断器、API文档。
  • 程序设计实践:错误处理、分层、依赖注入、数据库、缓存、多环境、优雅关闭、灰度发布、单元测试。

avatar

初始化

wget https://gitee.com/woood2/luca/raw/master/scripts/gen.sh
chmod +x ./gen.sh
./gen.sh

目录结构

遵循:社区规范

├── cmd
│   ├── backend                     Restful服务
│   │   ├── README.md
│   │   ├── docs                    swagger文档
│   │   │   ├── docs.go
│   │   │   ├── swagger.json
│   │   │   └── swagger.yaml
│   │   ├── internal
│   │   │   ├── assembly            装配器
│   │   │   │   ├── route.go
│   │   │   │   └── sdk.go
│   │   │   ├── controller          C层
│   │   │   │   └── app.go
│   │   │   └── middleware          中间件
│   │   │       ├── auth.go
│   │   │       ├── metrics.go
│   │   │       ├── oplog.go
│   │   │       ├── perm.go
│   │   │       └── trace.go
│   │   └── main.go
│   ├── consumer                    队列消费
│   │   ├── internal
│   │   │   ├── alarm               告警
│   │   │   │   ├── alarm.go
│   │   │   │   └── watcher.go
│   │   │   ├── handler             C层
│   │   │   │   ├── greet_one.go
│   │   │   │   ├── greet_two.go
│   │   │   │   └── groups.go
│   │   │   ├── middleware          中间件
│   │   │   │   ├── logging.go
│   │   │   │   ├── metrics.go
│   │   │   │   └── trace.go
│   │   │   └── subscriber          订阅者
│   │   │       ├── chain.go
│   │   │       ├── msg_handler.go
│   │   │       ├── retry_consumer.go
│   │   │       └── subscriber.go
│   │   └── main.go
│   ├── cron                        定时任务
│   │   ├── internal
│   │   │   ├── handler             C层
│   │   │   │   ├── greet.go
│   │   │   │   ├── hello.go
│   │   │   │   └── set.go
│   │   │   ├── job                 作业
│   │   │   │   ├── chain.go
│   │   │   │   ├── job.go
│   │   │   │   └── list.go
│   │   │   └── middleware          中间件
│   │   │       ├── logging.go
│   │   │       └── trace.go
│   │   └── main.go
│   └── micro                       微服务
│       ├── internal
│       │   ├── endpoint            C层
│       │   │   ├── check_perm.go
│       │   │   ├── check_token.go
│       │   │   └── validate.go
│       │   ├── grpc                通信层
│       │   │   ├── check_perm.go
│       │   │   ├── check_token.go
│       │   │   ├── grpc_health_v1
│       │   │   │   ├── health.pb.go
│       │   │   │   ├── health.proto
│       │   │   │   ├── health_grpc.pb.go
│       │   │   │   ├── health_impl.go
│       │   │   │   └── protoc.sh
│       │   │   ├── pb
│       │   │   │   ├── protoc.sh
│       │   │   │   ├── server.pb.go
│       │   │   │   ├── server.proto
│       │   │   │   └── server_grpc.pb.go
│       │   │   └── set.go
│       │   ├── middleware          中间件
│       │   │   ├── auth
│       │   │   │   ├── context.go
│       │   │   │   ├── middleware.go
│       │   │   │   ├── option.go
│       │   │   │   └── token.go
│       │   │   ├── chain.go
│       │   │   ├── logging
│       │   │   │   └── middleware.go
│       │   │   └── metrics
│       │   │       └── middleware.go
│       │   ├── output              输出
│       │   │   └── format.go
│       │   └── param               参数
│       │       ├── check_perm.go
│       │       └── check_token.go
│       ├── main.go
│       └── pkg                     SDK
│           ├── check_perm.go
│           ├── check_token.go
│           ├── err.go
│           └── sdk.go
├── configs                         配置文件
│   ├── application-example.yml
│   └── application.yml
├── deployments                     部署
│   ├── docker
│   │   ├── docker-compose-example.yml
│   │   ├── docker-compose.yml
│   │   ├── prometheus-example.yml
│   │   └── prometheus.yml
│   ├── nav-example.html
│   └── nav.html
├── docs                            文档
├── go.mod
├── go.sum
├── internal
│   ├── cache                       缓存
│   │   ├── cache.go
│   │   ├── dict.go         
│   │   ├── key.go
│   │   ├── local.go
│   │   ├── local_test.go
│   │   ├── miss.go
│   │   ├── nop.go
│   │   ├── protector.go
│   │   ├── redis.go
│   │   ├── redis_test.go
│   │   └── serialization.go
│   ├── conf                        配置
│   │   └── attr.go
│   ├── db                          数据库
│   │   ├── gorm.go
│   │   ├── mongo.go
│   │   └── zapgorm2
│   │       └── zapgorm2.go
│   ├── discovery                   服务发现
│   │   └── consul.go
│   ├── errcode                     错误
│   │   ├── business.go
│   │   ├── micro.go
│   │   ├── resp.go
│   │   └── restful.go
│   ├── layer                       分层
│   │   ├── controller.go
│   │   ├── repository.go
│   │   └── service.go
│   ├── log                         日志
│   │   └── zap.go
│   ├── mq                          消息
│   │   ├── greet.go
│   │   └── greet_test.go
│   ├── producer                    队列生产
│   │   ├── sarama_producer.go
│   │   └── scram_client.go
│   ├── repository                  DAO层
│   │   ├── app.go
│   │   ├── app_test.go
│   │   ├── student.go
│   │   └── student_test.go
│   ├── sdk                         SDK引用
│   │   └── demo.go
│   ├── service                     service层
│   │   ├── acl.go
│   │   ├── app.go
│   │   └── app_test.go
│   ├── status                      状态
│   │   ├── ginpprof.go
│   │   ├── hystrix.go
│   │   └── prometheus.go
│   ├── trace                       链路追踪
│   │   └── zipkin.go
│   └── util                        工具
│       ├── file.go
│       ├── jsontime.go
│       ├── md5.go
│       └── test.go
├── scripts                         脚本
│   ├── init.sql
│   ├── init.sh
└── test
    └── grpc_client                 微服务本地测试
        └── main.go

多入口

cmd/ 默认拥有4个子目录,对应4个入口、4种作业类型。

├── cmd
│   ├── backend                     Restful服务
│   │   ├── README.md
│   │   ├── docs                    
│   │   ├── internal
│   │   └── main.go
│   ├── consumer                    队列消费
│   │   ├── internal
│   │   └── main.go
│   ├── cron                        定时任务
│   │   ├── internal
│   │   └── main.go
│   └── micro                       微服务
│       ├── internal
│       ├── main.go
│       └── pkg                     

入口专用的代码存放于 cmd/ 的子目录中,跨入口公用的代码存放于 internal/ 下,例如:

同一种作业类型,可以创建多个入口。例如:项目中允许存在backendfrontend两个Restful服务。

示例1:添加frontend入口

  1. 复制目录:cp -a cmd/backend cmd/frontend
  2. cmd/frontend 路径下批量替换:backend=>frontendBackend=>Frontend
  3. 修改配置:internal/conf/attr.goconfigs/application-example.yml

示例2:删除frontend入口

  1. 删除目录:rm -rf cmd/frontend
  2. 修改配置:internal/conf/attr.goconfigs/application-example.yml

运行

步骤1:安装Go环境以及所依赖的服务,详见 deployments/README.md

步骤2:执行init脚本,键入本机IP,生成 configs/application.yml

cd scripts
./init.sh

步骤3:按需修改 configs/application.yml

步骤4:执行 go run cmd/{xyz}/main.go

配置

文件路径

  • 本地开发:configs/application.yml
  • 远程部署:程序运行目录/application.yml

文件结构

  • stub:用于设置本机host以及配置类型。
  • detail:可供多个go程序实例复用的详细配置,根据stub设置决定其存放位置。

stub详解

一级 二级 说明 示例
host 本机IP地址 192.168.1.100
configType file:本地文件 consul:配置中心 consul
dataKey consul kv键名 config/luca/attr
consul
host consul主机 192.168.1.100
port consul端口 8500

本地文件示例

application.yml

# === stub part ===
host: {ip}
configType: file
dataKey: 
consul:
  host: 
  port: 
# === detail part ===
project: luca
mysql:
  user: root
  pwd: '123456'
  host: {ip}
  port: 3306
  db: luca

配置中心示例

application.yml

# === stub part ===
host: {ip}
configType: consul
dataKey: config/luca/attr
consul:
  host: {ip}
  port: 8500

访问consul后台,配置KV字典如下:
< Key/Values < config < luca 
attr 

Value 
    # === detail part ===
    project: luca
    mysql:
      user: root
      pwd: '123456'
      host: {ip}
      port: 3306
      db: luca

detail详解

一级 二级 说明 示例
project 项目名 luca
env 环境:dev/demo/test/pre/pro dev
consoleLog true:控制台日志
false:文件日志
true
pprof
user 用户名 qjs
pwd 密码 731
mysql
user 用户名 root
pwd 密码 123456
host 主机 192.168.1.100
port 端口 3306
db 数据库 luca
maxOpenConns 最大连接数 30
maxIdleConns 最大空闲连接数 10
mongo
user 用户名 woood2
pwd 密码 123456
host 主机 192.168.1.100
port 端口 27017
db 数据库 luca
maxPoolSize 最大连接数 30
minPoolSize 最小连接数 10
redis
addr 地址 192.168.1.100:6379
pwd 密码
poolSize 最大连接数 30
minIdleConns 最小空闲连接数 10
zipkin
reporterAddr 上报地址(允许为空) 192.168.1.100:6379
kafka
version 版本 2.5.0
brokers broker地址(允许多个) 192.168.1.100:9092
enableSASL 是否开启SASL true
user 用户名 qjs
password 密码 731
algorithm 算法 sha256
backend
addr Restful服务地址 :8080
register 是否注册到consul true
pprofAddr pprof地址 :6059
metricsAddr metrics地址 :8082
hystrixPort hystrix端口 8282
allowOrigins 允许来源(允许多个) *
micro
port 端口 8081
register 是否注册到consul true
pprofAddr pprof地址 :6060
metricsAddr metrics地址 :6061
hystrixPort hystrix端口 8283
consumer
pprofAddr pprof地址 :6064
metricsAddr metrics地址 :6063
hystrixPort hystrix端口 8084
cron
pprofAddr pprof地址 :6065
hystrixPort hystrix端口 8085

错误处理

luca倡导在项目中基于 github.com/pkg/errors 传递 error,而非使用 panic-recover。

统一的错误字典

为了对调用者友好,微服务SDK冗余了一份错误字典

更新方式

cd cmd/micro/pkg
./update_err.sh

Resp结构体:internal/errcode/resp.go

package errcode
//4 restful & micro service
type Resp struct {
	ErrCode string      `json:"errCode"`
	ErrMsg  string      `json:"errMsg"`
	Data    interface{} `json:"data"`
	TraceID string      `json:"traceID"` //only 4 restful
	Stack   string      `json:"stack"`   //only 4 restful, non-pro env
}

一次Restful请求的错误处理

  • 当程序走入错误分支或者发生内部错误,一个error对象被逐层传递到最上层的controller中。
  • 持有这个error对象的controller前往错误字典查找对应的错误代码,并封装成Resp对象作为http返回值。
  • 每个错误代码对应一条程序分支,通常由用户的错误操作引发,例如"用户名或密码错误",无需记录日志,http code为200。
  • 找不到错误代码error统一归为errcode.ServerErr并且记录一条error等级的日志,http code为500。
  • 顺利走完程序主分支的成功请求,Resp.ErrCodeResp.ErrMsg均为空值,Resp.Data携带返回值正文,http code为200。

一次微服务调用的错误处理

  • 当程序走入错误分支或者发生内部错误,一个error对象被逐层传递到最上层的endpoint中。
  • 持有这个error对象的endpoint前往错误字典查找对应的错误代码,并封装成Resp对象作为返回值。
  • 每个错误代码对应一条程序分支,通常由client的非法调用引发,例如"非法的鉴权凭证",无需记录日志,无需对接hystrix
  • 找不到错误代码error需要记录一条error等级的日志,并且对接hystrix
  • 顺利走完程序主分支的成功调用,Resp.ErrCodeResp.ErrMsg均为空值,Resp.Data携带返回值正文。

Crash-free

  • 尽管luca不主张使用panic-recover,但无法保证第三方库没有panic,必须在goroutine级别进行recover,保障整个进程的运行安全。
  • 捕获panic后,任何作业类型均会记录一条error等级的日志。
  • Restful请求中发生panichttp code为500。
  • 微服务调用中发生panic,需要对接hystrix,error日志由调用端(SDK)负责记录。

Restful服务

luca基于 gin 提供Restful服务。

示例

基于 gin-contrib/cors 支持CORS。

允许多个来源:

backend:  
  allowOrigins:
    - "http://google.com"
    - "http://facebook.com"    

允许任意来源:

backend:  
  allowOrigins:
    - "*"       

基于 swaggo/swag 支持API文档。
示例

微服务

luca 基于 go-kit 提供micro service。

详细选型

API示例

SDK示例

本地测试

定时任务

luca基于 robfig-cron 提供定时任务。

基于redis实现Secondary Nodes模式的高可用,详见 main.go 中的campaign()renew()

job示例

消息队列

luca 基于 Shopify/sarama 支持 Kafka。

application.yml

kafka:
  version: 2.5.0
  brokers:
    - {ip}:9092
  enableSASL: true
  user: qjs
  password: '731'
  algorithm: sha256

示例

分层

推荐方案 avatar

controller层包括:

service 层涵盖:

repository 层负责操作ORM层,luca选择 GORM 作为ORM框架。

sdkkafka producer 可视作远程service。

依赖注入

在luca中,依赖注入随处可见:

被依赖的对象 接收注入的对象
logger 任何需要记录日志的代码
db(mysql、redis、mongo等) repository
consul client sdk
kafka producer controller、service
sdk controller、service
service controller
repository service

在Spring的世界中,使用@Autowired即可自动装配。但是在luca中,我们需要在以下位置手动装配:

Mysql

luca选择 GORM 作为ORM框架。

application.yml

mysql:
  user: root
  pwd: '123456'
  host: {ip}
  port: 3306
  db: luca
  maxOpenConns: 30
  maxIdleConns: 10

示例

MongoDB

luca 基于 mongo-go-driver 操作MongoDB。

application.yml

mongo:
  user: woood2
  pwd: '123456'
  host: {ip}
  port: 27017
  db: luca
  maxPoolSize: 30
  minPoolSize: 10

示例

缓存

luca提供了统一的 Cache 接口以及3种默认实现:

  • LocalCache:基于本地内存的cache实现,性能水平100w/sec。
  • RedisCache :基于redis的cache实现,性能水平1w/sec。
  • NopCache:基于「无」的cache实现。

其中,RedisCache 基于 go-redis 操作Redis,对应配置:

redis:
  addr: {ip}:6379
  pwd: ''
  poolSize: 30
  minIdleConns: 10

默认采用json序列化,允许修改 serialization.go 替换成其它序列化算法。

示例

logger

日志维度

日志维度 说明 日志库 输出 接入 zipkin 接入 EFK
服务日志 服务运行周期中的启动、关闭和状态日志 标准库 log stdout/stderr
请求日志 请求/作业周期内产生的日志 uber-go/zap stdout/stderr或者./zap.log文件

application.yml(zap配置)

名称 说明
env pro: 日志等级Info
其它: 日志等级Debug
consoleLog true: 控制台日志
false: 文件日志(./zap.log)

内置日志点(zap logger)

围绕 类型 说明 等级
请求/作业
access日志 覆盖所有作业类型 info
error日志 覆盖所有作业类型 error
oplog日志 用于Restful服务 info
GORM
sql日志 debug
slow日志 warn
error日志 error
SDK
call日志 debug
error日志 error

链路追踪

luca 选择 zipkin 作为链路追踪的实现引擎。

跟踪范围

  • Restful请求
  • 微服务调用
  • 定时任务作业
  • 队列消费作业
  • 其它,如单元测试

从 gin.Context 提取 TraceID:

func (ctr *AnyController) Demo(c *gin.Context) {
    traceID := trace.GinID(c)
    ...
}

将 gin.Context 转换为 context.Context

func (ctr *AnyController) Demo(c *gin.Context) {
    ctx:=trace.Ctx(c)
    ...
}

从 context.Context 提取 TraceID:

func demo(ctx context.Context) {
    traceID := trace.ID(ctx)
    ...
}

向 GORM logger 传递 TraceID:

func (repo *AnyRepoImpl) Create(ctx context.Context, entity *Any) (int, error) {
    result := repo.DB.WithContext(ctx).Create(entity)
    ...
}

从 Restful 响应读取 TraceID:

package errcode

type Resp struct {
    ErrCode string      `json:"errCode"`
    ErrMsg  string      `json:"errMsg"`
    Data    interface{} `json:"data"`
    TraceID string      `json:"traceID"` 
    Stack   string      `json:"stack"`   
}

application.yml

一级 二级 说明 示例
zipkin
reporterAddr 上报地址(允许为空) http://192.168.0.100:9411/api/v2/spans

zipkin server

  • 安装:deployments/README.md
  • 访问:deployments/nav.html
  • 为性能考虑,生产环境建议关闭zipkin server,只需要将Zipkin.ReporterAddr设置为空。

日志收集

luca 选择 EFK 日志收集方案:

  • 通过 fluentd 从 ./zap.log 文件中读取日志。
  • 将收集来的日志数据存入 elastic search
  • 通过 Kibana 界面查询日志。

EFK 安装、使用

pprof

支持 pprof 的Go进程:

  • Restful服务
  • 微服务
  • 定时任务
  • 队列消费

application.yml

pprof:
  user: qjs
  pwd: '731'

backend:  
  pprofAddr: ":6059"
  
micro:
  pprofAddr: ":6060"
  
consumer:
  pprofAddr: ":6064"

cron:
  pprofAddr: ":6065"

访问

  • 详见 deployments/nav.html

metrics

luca 为3种作业类型接入 prometheus 指标统计:

  • Restful请求
  • 微服务调用
  • 队列消费作业

application.yml

backend:  
  metricsAddr: ":8082"
  
micro:
  metricsAddr: ":6061"
  
consumer:  
  metricsAddr: ":6063"  

安装 prometheus

访问 prometheus

  • 详见 deployments/nav.html

多环境

推荐方案

环境 env 部署位置 部署套数 用途 git分支 zap等级 swagger
本机 dev 本机 1/每人 开发 个人、feature、fixbug Debug 开启
dev dev 远程 1 联调 dev Debug 开启
demo demo 远程 1 对客演示 demo Debug 开启
test test 远程 >=1 测试 test Debug 开启
pre pre 远程 1 发版演练 pre Debug 开启
pro pro 远程 >=1 生产 master Info 关闭

本机 & dev

  • 允许本机环境与dev环境共用同一套资源,如数据库、消息队列等。
  • 允许本机程序单向依赖dev环境的微服务,只需在本机配置dev环境的consul地址,并且将Backend.Register、Micro.Register设置为false。

demo

  • 假如demo环境的使用频率较低,建议按需构建。
  • 考虑到demo环境的不确定性,暂不纳入 git 分支策略

git 分支策略 avatar

git 分支命名

分支 命名规则 示例
主分支 master -
开发 dev -
演示 demo -
测试 test -
预生产 pre -
feature {name}-feature first-feature、second-feature
fixbug {name}-fixbug stackoverflow-fixbug
个人 姓名全拼、缩写 qiujiashu、qjs

优雅关闭

支持优雅关闭的Go进程

  • Restful服务
  • 微服务
  • 定时任务
  • 队列消费

工作原理

  1. 接收来自kill命令的信号量。
  2. 停止接收新的作业。
  3. 等待正在进行中的作业完成或者超时(默认:5秒)。
  4. 关闭进程。

灰度发布

luca支持微服务及Restful服务的灰度发布,以实现程序的不停服更新。

前提

  • 不论是Restful服务还是微服务,运行的实例数量必须是两个及以上,单机模式不支持灰度发布。
  • Restful服务需要前置一个nginx反向代理,并且借助consul-template监视实例列表的变化。详见:Load Balancing with NGINX and Consul Template

工作原理

  • 发布程序的新版本时,分批重启集群中的实例。
  • 实例重启前通知consul注销自己,将流量导向其它运行中的实例。
  • 等待实例重启完成,再次注册到consul上迎回流量。
  • 发布过程中,始终有实例运行,提供连续服务。

单元测试

luca鼓励开发者采用主流的table driven模式进行单元测试。

单元测试的优先目标是计算逻辑,而不是IO。对于一般的CRUD,单元测试意义不大,API测试是更适合的选择。

luca支持在CI/CD中跳过指定的单元测试:

CLI
    CI=true go test -v -cover ./...

internal/util/test.go
    package util

    import (
        "os"
        "testing"
    )

    func CI() bool {
        return os.Getenv("CI") != ""
    }

    func SkipCI(t *testing.T) {
        if CI() {
            t.Skip("Skipping testing in CI environment")
        }
    }

xxx_test.go
    func TestMain(m *testing.M) {
        if !util.CI() {
            attr := conf.Load("application.yml", "configs/application.yml")
            ..
        }
        m.Run()
    }

    func TestXXX(t *testing.T) {
        util.SkipCI(t)
        ..
    }

空文件

简介

一个支持多入口、开箱即用的企业级Golang框架 展开 收起
Go 等 4 种语言
取消

发行版 (2)

全部

贡献者

全部

近期动态

加载更多
不能加载更多了
Go
1
https://gitee.com/woood2/luca.git
git@gitee.com:woood2/luca.git
woood2
luca
luca
master

搜索帮助