diff --git a/.env.example b/.env.example index e63aa54..c47ceb0 100644 --- a/.env.example +++ b/.env.example @@ -8,7 +8,7 @@ MOON_RABBIT_ENVIRONMENT=PROD MOON_RABBIT_ENABLE_CLIENT_CONFIG=true MOON_RABBIT_ENABLE_SWAGGER=true MOON_RABBIT_ENABLE_METRICS=true -MOON_RABBIT_USE_DATABASE=true +MOON_RABBIT_USE_DATABASE=false # ============================================================================= # Server Configuration @@ -16,6 +16,9 @@ MOON_RABBIT_USE_DATABASE=true MOON_RABBIT_NAME=moon.rabbit MOON_RABBIT_USE_RANDOM_ID=false MOON_RABBIT_METADATA_TAG=rabbit +MOON_RABBIT_METADATA_REPOSITORY=https://github.com/aide-family/rabbit +MOON_RABBIT_METADATA_AUTHOR=Aide Family +MOON_RABBIT_METADATA_EMAIL=aidecloud@163.com # HTTP Server MOON_RABBIT_HTTP_ADDRESS=0.0.0.0:8080 @@ -27,10 +30,10 @@ MOON_RABBIT_GRPC_ADDRESS=0.0.0.0:9090 MOON_RABBIT_GRPC_NETWORK=tcp MOON_RABBIT_GRPC_TIMEOUT=10s -# eventBus Server -MOON_RABBIT_EVENT_BUS_ADDRESS=0.0.0.0:9091 -MOON_RABBIT_EVENT_BUS_NETWORK=grpc -MOON_RABBIT_EVENT_BUS_TIMEOUT=10s +# Job Server (EventBus) +MOON_RABBIT_JOB_ADDRESS=0.0.0.0:9091 +MOON_RABBIT_JOB_NETWORK=grpc +MOON_RABBIT_JOB_TIMEOUT=10s # ============================================================================= # JWT Configuration @@ -51,11 +54,11 @@ MOON_RABBIT_MAIN_DEBUG=false MOON_RABBIT_MAIN_USE_SYSTEM_LOGGER=true # ============================================================================= -# Event Bus Configuration +# Job Core Configuration # ============================================================================= -MOON_RABBIT_EVENT_BUS_CORE_WORKER_TOTAL=10 -MOON_RABBIT_EVENT_BUS_CORE_TIMEOUT=10s -MOON_RABBIT_EVENT_BUS_CORE_BUFFER_SIZE=1000 +MOON_RABBIT_JOB_CORE_WORKER_TOTAL=10 +MOON_RABBIT_JOB_CORE_TIMEOUT=10s +MOON_RABBIT_JOB_CORE_BUFFER_SIZE=1000 # ============================================================================= # Registry Configuration @@ -65,8 +68,8 @@ MOON_RABBIT_REGISTRY_TYPE= # ============================================================================= # Cluster Configuration # ============================================================================= -MOON_RABBIT_CLUSTER_NAME=rabbit -MOON_RABBIT_CLUSTER_ENDPOINTS=localhost:9090 +MOON_RABBIT_CLUSTER_NAME=moon.rabbit +MOON_RABBIT_CLUSTER_ENDPOINTS= MOON_RABBIT_CLUSTER_PROTOCOL=GRPC MOON_RABBIT_CLUSTER_TIMEOUT=10s @@ -100,9 +103,9 @@ MOON_RABBIT_METRICS_BASIC_AUTH_PASSWORD=rabbit.metrics # ============================================================================= # Logging Configuration # ============================================================================= -MOON_RABBIT_CONFIG_PATHS=./datasource +MOON_RABBIT_CONFIG_PATHS= # ============================================================================= # Message Log Path Configuration # ============================================================================= -MOON_RABBIT_MESSAGE_LOG_PATH=./messages \ No newline at end of file +MOON_RABBIT_MESSAGE_LOG_PATH= diff --git a/.gitignore b/.gitignore index 45a1bc1..52c667d 100644 --- a/.gitignore +++ b/.gitignore @@ -42,4 +42,6 @@ go.work.sum deploy/*/data description.txt .rabbit/ -*.log \ No newline at end of file +*.log +messages +message_logs \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d0167a9..6040fc0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,6 +19,9 @@ RUN make init # 复制源代码 COPY . . +RUN git clone https://github.com/aide-family/magicbox.git ../magicbox +RUN git clone https://github.com/aide-family/kratos.git ../kratos + # 构建应用 RUN make build @@ -44,4 +47,4 @@ EXPOSE 8080 9090 # 运行应用 ENTRYPOINT ["/usr/local/bin/rabbit"] -CMD ["run"] \ No newline at end of file +CMD ["run", "all"] \ No newline at end of file diff --git a/Makefile b/Makefile index 0a65fd2..6978322 100644 --- a/Makefile +++ b/Makefile @@ -170,7 +170,7 @@ build: all # run the rabbit binary in development mode dev: @echo "Running rabbit in development mode" - go run . run + go run . run all .PHONY: test # run the tests diff --git a/README-zh_CN.md b/README-zh_CN.md index eaa4a5a..68faa50 100644 --- a/README-zh_CN.md +++ b/README-zh_CN.md @@ -46,7 +46,7 @@ make init make build # 运行服务 -./bin/rabbit run +./bin/rabbit run all ``` #### 使用 Docker @@ -80,6 +80,12 @@ rabbit config --path ./config --name server.yaml # 或使用自定义路径 rabbit config -p ./config -n server.yaml + +# 强制覆盖已存在的文件 +rabbit config -p ./config -n server.yaml --force + +# 生成客户端配置文件 +rabbit config -p ./config -n client.yaml --client ``` ## 📦 部署 @@ -127,14 +133,18 @@ kubectl apply -k deploy/server/k8s/ 4. **运行服务**: ```bash - ./bin/rabbit run -c ./config/server.yaml + ./bin/rabbit run all -c ./config/server.yaml ``` ## ⚙️ 配置说明 ### 配置文件 -默认配置文件为 `config/server.yaml`。可以使用 `--config` 或 `-c` 参数指定自定义路径。 +默认配置文件为 `config/server.yaml`。可以使用 `--config` 或 `-c` 参数指定自定义路径(可多次使用)。 + +**注意**:`--use-database` 和 `--datasource-paths` 选项互斥: +- 使用 `--use-database true` 启用数据库存储模式(推荐用于生产环境) +- 使用 `--datasource-paths` 启用基于文件的存储模式(适用于开发和测试) ### 环境变量 @@ -146,22 +156,33 @@ Rabbit 支持通过环境变量进行配置。所有环境变量遵循 `MOON_RAB |------|--------|------| | `MOON_RABBIT_ENVIRONMENT` | `PROD` | 环境:DEV, TEST, PREVIEW, PROD | | `MOON_RABBIT_NAME` | `moon.rabbit` | 服务名称 | +| `MOON_RABBIT_USE_RANDOM_ID` | `false` | 使用随机服务 ID | +| `MOON_RABBIT_METADATA_TAG` | `rabbit` | 服务元数据标签 | +| `MOON_RABBIT_METADATA_REPOSITORY` | `https://github.com/aide-family/rabbit` | 服务元数据仓库 | +| `MOON_RABBIT_METADATA_AUTHOR` | `Aide Family` | 服务元数据作者 | +| `MOON_RABBIT_METADATA_EMAIL` | `aidecloud@163.com` | 服务元数据邮箱 | | `MOON_RABBIT_HTTP_ADDRESS` | `0.0.0.0:8080` | HTTP 服务器地址 | -| `MOON_RABBIT_GRPC_ADDRESS` | `0.0.0.0:9090` | gRPC 服务器地址 | +| `MOON_RABBIT_HTTP_NETWORK` | `tcp` | HTTP 服务器网络 | | `MOON_RABBIT_HTTP_TIMEOUT` | `10s` | HTTP 请求超时时间 | +| `MOON_RABBIT_GRPC_ADDRESS` | `0.0.0.0:9090` | gRPC 服务器地址 | +| `MOON_RABBIT_GRPC_NETWORK` | `tcp` | gRPC 服务器网络 | | `MOON_RABBIT_GRPC_TIMEOUT` | `10s` | gRPC 请求超时时间 | +| `MOON_RABBIT_JOB_ADDRESS` | `0.0.0.0:9091` | Job 服务器地址 | +| `MOON_RABBIT_JOB_NETWORK` | `grpc` | Job 服务器网络 | +| `MOON_RABBIT_JOB_TIMEOUT` | `10s` | Job 请求超时时间 | #### 数据库配置 | 变量 | 默认值 | 说明 | |------|--------|------| -| `MOON_RABBIT_USE_DATABASE` | `false` | 启用数据库存储模式 | +| `MOON_RABBIT_USE_DATABASE` | `false` | 启用数据库存储模式(与 MOON_RABBIT_DATASOURCE_PATHS 互斥) | | `MOON_RABBIT_MAIN_HOST` | `localhost` | MySQL 主机地址 | | `MOON_RABBIT_MAIN_PORT` | `3306` | MySQL 端口 | | `MOON_RABBIT_MAIN_DATABASE` | `rabbit` | 数据库名称 | | `MOON_RABBIT_MAIN_USERNAME` | `root` | MySQL 用户名 | | `MOON_RABBIT_MAIN_PASSWORD` | `123456` | MySQL 密码 | | `MOON_RABBIT_MAIN_DEBUG` | `false` | 启用数据库调试模式 | +| `MOON_RABBIT_MAIN_USE_SYSTEM_LOGGER` | `true` | 数据库使用系统日志 | #### JWT 配置 @@ -182,14 +203,48 @@ Rabbit 支持通过环境变量进行配置。所有环境变量遵循 `MOON_RAB | `MOON_RABBIT_KUBERNETES_NAMESPACE` | `moon` | Kubernetes 命名空间 | | `MOON_RABBIT_KUBERNETES_KUBECONFIG` | `~/.kube/config` | Kubernetes kubeconfig 路径 | +#### 集群配置 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `MOON_RABBIT_CLUSTER_NAME` | `moon.rabbit` | 集群名称 | +| `MOON_RABBIT_CLUSTER_ENDPOINTS` | `` | 集群端点 | +| `MOON_RABBIT_CLUSTER_PROTOCOL` | `GRPC` | 集群协议:GRPC, HTTP | +| `MOON_RABBIT_CLUSTER_TIMEOUT` | `10s` | 集群请求超时时间 | + +#### Job 配置 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `MOON_RABBIT_JOB_CORE_WORKER_TOTAL` | `10` | Job 工作线程总数 | +| `MOON_RABBIT_JOB_CORE_TIMEOUT` | `10s` | Job 核心超时时间 | +| `MOON_RABBIT_JOB_CORE_BUFFER_SIZE` | `1000` | Job 核心缓冲区大小 | + #### 功能开关 | 变量 | 默认值 | 说明 | |------|--------|------| -| `MOON_RABBIT_ENABLE_CLIENT_CONFIG` | `true` | 启用客户端配置 | -| `MOON_RABBIT_ENABLE_SWAGGER` | `true` | 启用 Swagger UI | -| `MOON_RABBIT_ENABLE_METRICS` | `true` | 启用指标端点 | -| `MOON_RABBIT_CONFIG_PATHS` | `./datasource` | 配置文件路径(逗号分隔) | +| `MOON_RABBIT_ENABLE_CLIENT_CONFIG` | `false` | 启用客户端配置 | +| `MOON_RABBIT_ENABLE_SWAGGER` | `false` | 启用 Swagger UI | +| `MOON_RABBIT_ENABLE_METRICS` | `false` | 启用指标端点 | +| `MOON_RABBIT_DATASOURCE_PATHS` | `` | 数据源文件路径(逗号分隔,与 MOON_RABBIT_USE_DATABASE 互斥) | +| `MOON_RABBIT_MESSAGE_LOG_PATH` | `` | 消息日志文件路径 | + +#### Swagger 基础认证 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_ENABLED` | `true` | 启用 Swagger 基础认证 | +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_USERNAME` | `moon.rabbit` | Swagger 基础认证用户名 | +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_PASSWORD` | `rabbit.swagger` | Swagger 基础认证密码 | + +#### Metrics 基础认证 + +| 变量 | 默认值 | 说明 | +|------|--------|------| +| `MOON_RABBIT_METRICS_BASIC_AUTH_ENABLED` | `true` | 启用 Metrics 基础认证 | +| `MOON_RABBIT_METRICS_BASIC_AUTH_USERNAME` | `moon.rabbit` | Metrics 基础认证用户名 | +| `MOON_RABBIT_METRICS_BASIC_AUTH_PASSWORD` | `rabbit.metrics` | Metrics 基础认证密码 | ### 命令行参数 @@ -199,37 +254,122 @@ Rabbit 支持通过环境变量进行配置。所有环境变量遵循 `MOON_RAB |------|------|--------|------| | `--namespace` | `-n` | `` | 服务命名空间 | | `--rabbit-config` | | `./.rabbit/` | Rabbit 配置文件目录 | +| `--log-format` | | `TEXT` | 日志格式:TEXT, JSON | +| `--log-level` | | `DEBUG` | 日志级别:DEBUG, INFO, WARN, ERROR | + +#### Config 命令参数 + +| 参数 | 简写 | 默认值 | 说明 | +|------|------|--------|------| +| `--path`, `-p` | | `.` | 配置文件输出路径 | +| `--name` | | `config.yaml` | 输出文件名 | +| `--force`, `-f` | | `false` | 强制覆盖已存在的文件 | +| `--client` | | `false` | 生成客户端配置文件而非服务器配置 | #### Run 命令参数 | 参数 | 默认值 | 说明 | |------|--------|------| -| `--config`, `-c` | `` | 配置文件路径 | +| `--config`, `-c` | `` | 配置文件路径(可多次使用) | | `--environment` | `PROD` | 环境:DEV, TEST, PREVIEW, PROD | +| `--use-database` | `false` | 启用数据库存储模式(与 --datasource-paths 互斥) | +| `--datasource-paths` | `` | 数据源文件路径(逗号分隔,与 --use-database 互斥) | +| `--message-log-path` | `` | 消息日志文件路径 | +| `--jwt-secret` | `xxx` | JWT 密钥 | +| `--jwt-expire` | `600s` | JWT 过期时间 | +| `--jwt-issuer` | `rabbit` | JWT 签发者 | +| `--main-host` | `localhost` | MySQL 主机地址 | +| `--main-port` | `3306` | MySQL 端口 | +| `--main-database` | `rabbit` | 数据库名称 | +| `--main-username` | `root` | MySQL 用户名 | +| `--main-password` | `123456` | MySQL 密码 | +| `--main-debug` | `false` | 启用数据库调试模式 | +| `--main-use-system-logger` | `true` | 数据库使用系统日志 | +| `--registry-type` | `` | 注册中心类型:etcd, kubernetes | +| `--etcd-endpoints` | `127.0.0.1:2379` | etcd 端点 | +| `--etcd-username` | `` | etcd 用户名 | +| `--etcd-password` | `` | etcd 密码 | +| `--kubernetes-namespace` | `moon` | Kubernetes 命名空间 | +| `--kubernetes-kubeconfig` | `~/.kube/config` | Kubernetes kubeconfig 路径 | + +#### Run All 命令参数 + +| 参数 | 默认值 | 说明 | +|------|--------|------| | `--http-address` | `0.0.0.0:8080` | HTTP 服务器地址 | +| `--http-network` | `tcp` | HTTP 服务器网络 | +| `--http-timeout` | `10s` | HTTP 请求超时时间 | | `--grpc-address` | `0.0.0.0:9090` | gRPC 服务器地址 | -| `--use-database` | `false` | 启用数据库存储模式 | -| `--config-paths` | `./datasource` | 配置文件路径 | +| `--grpc-network` | `tcp` | gRPC 服务器网络 | +| `--grpc-timeout` | `10s` | gRPC 请求超时时间 | +| `--job-address` | `0.0.0.0:9091` | Job 服务器地址 | +| `--job-network` | `grpc` | Job 服务器网络 | +| `--job-timeout` | `10s` | Job 请求超时时间 | +| `--job-core-worker-total` | `10` | Job 工作线程总数 | +| `--job-core-timeout` | `10s` | Job 核心超时时间 | +| `--job-core-buffer-size` | `1000` | Job 核心缓冲区大小 | +| `--enable-swagger` | `false` | 启用 Swagger UI | +| `--enable-swagger-basic-auth` | `true` | 启用 Swagger 基础认证 | +| `--swagger-basic-auth-username` | `moon.rabbit` | Swagger 基础认证用户名 | +| `--swagger-basic-auth-password` | `rabbit.swagger` | Swagger 基础认证密码 | +| `--enable-metrics` | `false` | 启用指标端点 | +| `--enable-metrics-basic-auth` | `true` | 启用 Metrics 基础认证 | +| `--metrics-basic-auth-username` | `moon.rabbit` | Metrics 基础认证用户名 | +| `--metrics-basic-auth-password` | `rabbit.metrics` | Metrics 基础认证密码 | +| `--enable-client-config` | `false` | 启用客户端配置 | + +#### GORM 命令参数 -更多参数请使用 `rabbit run --help` 查看。 +| 参数 | 简写 | 默认值 | 说明 | +|------|------|--------|------| +| `--config`, `-c` | | `./config` | 配置文件路径 | +| `--force-gen`, `-f` | | `false` | 强制生成代码,覆盖已存在的代码 | +| `--username` | | `root` | MySQL 用户名 | +| `--password` | | `123456` | MySQL 密码 | +| `--host` | | `localhost` | MySQL 主机地址 | +| `--port` | | `3306` | MySQL 端口 | +| `--database` | | `rabbit` | MySQL 数据库 | +| `--params` | | `charset=utf8mb4,parseTime=true,loc=Asia/Shanghai` | MySQL 连接参数 | +| `--biz`, `-b` | | `false` | 使用 biz 命名空间配置 | + +更多参数请使用 `rabbit run --help` 和 `rabbit run all --help` 查看。 ### 使用示例 ```bash -# 使用自定义配置文件运行 -rabbit run -c ./config/server.yaml +# 运行所有服务(HTTP、gRPC、Job)使用自定义配置文件 +rabbit run all -c ./config/server.yaml + +# 仅运行 HTTP 服务器 +rabbit run http -c ./config/server.yaml + +# 仅运行 gRPC 服务器 +rabbit run grpc -c ./config/server.yaml + +# 仅运行 Job 服务器 +rabbit run job -c ./config/server.yaml + +# 使用多个配置文件运行 +rabbit run all -c ./config/server.yaml -c ./config/override.yaml # 使用环境变量运行 MOON_RABBIT_HTTP_ADDRESS=0.0.0.0:8080 \ MOON_RABBIT_USE_DATABASE=true \ -rabbit run +rabbit run all -# 使用命令行参数运行 -rabbit run \ +# 使用数据库存储模式运行 +rabbit run all \ --http-address 0.0.0.0:8080 \ --grpc-address 0.0.0.0:9090 \ - --use-database true \ - --config-paths ./datasource,./config + --job-address 0.0.0.0:9091 \ + --use-database true + +# 使用基于文件的存储模式运行 +rabbit run all \ + --http-address 0.0.0.0:8080 \ + --grpc-address 0.0.0.0:9090 \ + --job-address 0.0.0.0:9091 \ + --datasource-paths ./datasource,./config ``` ## 📚 命令说明 @@ -251,11 +391,13 @@ rabbit run \ ### 服务命令 - `rabbit run` - 启动 Rabbit 服务 - -### 数据库命令 - -- `rabbit gorm migrate` - 迁移数据库表结构 -- `rabbit gorm gen` - 生成 GORM 查询代码 + - `rabbit run all` - 启动所有服务(HTTP、gRPC、Job) + - `rabbit run http` - 仅启动 HTTP 服务器 + - `rabbit run grpc` - 仅启动 gRPC 服务器 + - `rabbit run job` - 仅启动 Job 服务器 +- `rabbit gorm` - GORM 代码生成和数据库迁移工具 + - `rabbit gorm gen` - 生成 GORM 查询代码 + - `rabbit gorm migrate` - 迁移数据库表结构 详细命令说明请使用 `rabbit --help` 查看。 diff --git a/README.md b/README.md index 0bd3be4..907425a 100644 --- a/README.md +++ b/README.md @@ -46,7 +46,7 @@ make init make build # Run the service -./bin/rabbit run +./bin/rabbit run all ``` #### Using Docker @@ -80,6 +80,12 @@ rabbit config --path ./config --name server.yaml # Or with custom path rabbit config -p ./config -n server.yaml + +# Force overwrite existing file +rabbit config -p ./config -n server.yaml --force + +# Generate client configuration file +rabbit config -p ./config -n client.yaml --client ``` ## 📦 Deployment @@ -127,14 +133,18 @@ kubectl apply -k deploy/server/k8s/ 4. **Run the service**: ```bash - ./bin/rabbit run -c ./config/server.yaml + ./bin/rabbit run all -c ./config/server.yaml ``` ## ⚙️ Configuration ### Configuration File -The default configuration file is `config/server.yaml`. You can specify a custom path using the `--config` or `-c` flag. +The default configuration file is `config/server.yaml`. You can specify custom paths using the `--config` or `-c` flag (can be used multiple times). + +**Note**: The `--use-database` and `--datasource-paths` options are mutually exclusive: +- Use `--use-database true` for database storage mode (recommended for production) +- Use `--datasource-paths` for file-based storage mode (useful for development and testing) ### Environment Variables @@ -146,22 +156,33 @@ Rabbit supports configuration through environment variables. All environment var |----------|---------|-------------| | `MOON_RABBIT_ENVIRONMENT` | `PROD` | Environment: DEV, TEST, PREVIEW, PROD | | `MOON_RABBIT_NAME` | `moon.rabbit` | Service name | +| `MOON_RABBIT_USE_RANDOM_ID` | `false` | Use random service ID | +| `MOON_RABBIT_METADATA_TAG` | `rabbit` | Service metadata tag | +| `MOON_RABBIT_METADATA_REPOSITORY` | `https://github.com/aide-family/rabbit` | Service metadata repository | +| `MOON_RABBIT_METADATA_AUTHOR` | `Aide Family` | Service metadata author | +| `MOON_RABBIT_METADATA_EMAIL` | `aidecloud@163.com` | Service metadata email | | `MOON_RABBIT_HTTP_ADDRESS` | `0.0.0.0:8080` | HTTP server address | -| `MOON_RABBIT_GRPC_ADDRESS` | `0.0.0.0:9090` | gRPC server address | +| `MOON_RABBIT_HTTP_NETWORK` | `tcp` | HTTP server network | | `MOON_RABBIT_HTTP_TIMEOUT` | `10s` | HTTP request timeout | +| `MOON_RABBIT_GRPC_ADDRESS` | `0.0.0.0:9090` | gRPC server address | +| `MOON_RABBIT_GRPC_NETWORK` | `tcp` | gRPC server network | | `MOON_RABBIT_GRPC_TIMEOUT` | `10s` | gRPC request timeout | +| `MOON_RABBIT_JOB_ADDRESS` | `0.0.0.0:9091` | Job server address | +| `MOON_RABBIT_JOB_NETWORK` | `grpc` | Job server network | +| `MOON_RABBIT_JOB_TIMEOUT` | `10s` | Job request timeout | #### Database Configuration | Variable | Default | Description | |----------|---------|-------------| -| `MOON_RABBIT_USE_DATABASE` | `false` | Enable database storage mode | +| `MOON_RABBIT_USE_DATABASE` | `false` | Enable database storage mode (mutually exclusive with MOON_RABBIT_DATASOURCE_PATHS) | | `MOON_RABBIT_MAIN_HOST` | `localhost` | MySQL host | | `MOON_RABBIT_MAIN_PORT` | `3306` | MySQL port | | `MOON_RABBIT_MAIN_DATABASE` | `rabbit` | Database name | | `MOON_RABBIT_MAIN_USERNAME` | `root` | MySQL username | | `MOON_RABBIT_MAIN_PASSWORD` | `123456` | MySQL password | | `MOON_RABBIT_MAIN_DEBUG` | `false` | Enable database debug mode | +| `MOON_RABBIT_MAIN_USE_SYSTEM_LOGGER` | `true` | Use system logger for database | #### JWT Configuration @@ -182,14 +203,48 @@ Rabbit supports configuration through environment variables. All environment var | `MOON_RABBIT_KUBERNETES_NAMESPACE` | `moon` | Kubernetes namespace | | `MOON_RABBIT_KUBERNETES_KUBECONFIG` | `~/.kube/config` | Kubernetes kubeconfig path | +#### Cluster Configuration + +| Variable | Default | Description | +|----------|---------|-------------| +| `MOON_RABBIT_CLUSTER_NAME` | `moon.rabbit` | Cluster name | +| `MOON_RABBIT_CLUSTER_ENDPOINTS` | `` | Cluster endpoints | +| `MOON_RABBIT_CLUSTER_PROTOCOL` | `GRPC` | Cluster protocol: GRPC, HTTP | +| `MOON_RABBIT_CLUSTER_TIMEOUT` | `10s` | Cluster request timeout | + +#### Job Configuration + +| Variable | Default | Description | +|----------|---------|-------------| +| `MOON_RABBIT_JOB_CORE_WORKER_TOTAL` | `10` | Total number of job workers | +| `MOON_RABBIT_JOB_CORE_TIMEOUT` | `10s` | Job core timeout | +| `MOON_RABBIT_JOB_CORE_BUFFER_SIZE` | `1000` | Job core buffer size | + #### Feature Flags | Variable | Default | Description | |----------|---------|-------------| -| `MOON_RABBIT_ENABLE_CLIENT_CONFIG` | `true` | Enable client configuration | -| `MOON_RABBIT_ENABLE_SWAGGER` | `true` | Enable Swagger UI | -| `MOON_RABBIT_ENABLE_METRICS` | `true` | Enable metrics endpoint | -| `MOON_RABBIT_CONFIG_PATHS` | `./datasource` | Configuration file paths (comma-separated) | +| `MOON_RABBIT_ENABLE_CLIENT_CONFIG` | `false` | Enable client configuration | +| `MOON_RABBIT_ENABLE_SWAGGER` | `false` | Enable Swagger UI | +| `MOON_RABBIT_ENABLE_METRICS` | `false` | Enable metrics endpoint | +| `MOON_RABBIT_DATASOURCE_PATHS` | `` | Data source file paths (comma-separated, mutually exclusive with MOON_RABBIT_USE_DATABASE) | +| `MOON_RABBIT_MESSAGE_LOG_PATH` | `` | Message log file path | + +#### Swagger Basic Auth + +| Variable | Default | Description | +|----------|---------|-------------| +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_ENABLED` | `true` | Enable Swagger basic authentication | +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_USERNAME` | `moon.rabbit` | Swagger basic auth username | +| `MOON_RABBIT_SWAGGER_BASIC_AUTH_PASSWORD` | `rabbit.swagger` | Swagger basic auth password | + +#### Metrics Basic Auth + +| Variable | Default | Description | +|----------|---------|-------------| +| `MOON_RABBIT_METRICS_BASIC_AUTH_ENABLED` | `true` | Enable metrics basic authentication | +| `MOON_RABBIT_METRICS_BASIC_AUTH_USERNAME` | `moon.rabbit` | Metrics basic auth username | +| `MOON_RABBIT_METRICS_BASIC_AUTH_PASSWORD` | `rabbit.metrics` | Metrics basic auth password | ### Command Line Arguments @@ -199,37 +254,122 @@ Rabbit supports configuration through environment variables. All environment var |------|-------|---------|-------------| | `--namespace` | `-n` | `` | The namespace of the service | | `--rabbit-config` | | `./.rabbit/` | The config file directory of the rabbit | +| `--log-format` | | `TEXT` | Log format: TEXT, JSON | +| `--log-level` | | `DEBUG` | Log level: DEBUG, INFO, WARN, ERROR | + +#### Config Command Flags + +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--path`, `-p` | | `.` | Output path for the config file | +| `--name` | | `config.yaml` | Output file name | +| `--force`, `-f` | | `false` | Overwrite existing file if it exists | +| `--client` | | `false` | Generate client config file instead of server config | #### Run Command Flags | Flag | Default | Description | |------|---------|-------------| -| `--config`, `-c` | `` | Configuration file path | +| `--config`, `-c` | `` | Configuration file path (can be used multiple times) | | `--environment` | `PROD` | Environment: DEV, TEST, PREVIEW, PROD | +| `--use-database` | `false` | Enable database storage mode (mutually exclusive with --datasource-paths) | +| `--datasource-paths` | `` | Data source file paths (comma-separated, mutually exclusive with --use-database) | +| `--message-log-path` | `` | Message log file path | +| `--jwt-secret` | `xxx` | JWT secret key | +| `--jwt-expire` | `600s` | JWT expiration time | +| `--jwt-issuer` | `rabbit` | JWT issuer | +| `--main-host` | `localhost` | MySQL host | +| `--main-port` | `3306` | MySQL port | +| `--main-database` | `rabbit` | Database name | +| `--main-username` | `root` | MySQL username | +| `--main-password` | `123456` | MySQL password | +| `--main-debug` | `false` | Enable database debug mode | +| `--main-use-system-logger` | `true` | Use system logger for database | +| `--registry-type` | `` | Registry type: etcd, kubernetes | +| `--etcd-endpoints` | `127.0.0.1:2379` | etcd endpoints | +| `--etcd-username` | `` | etcd username | +| `--etcd-password` | `` | etcd password | +| `--kubernetes-namespace` | `moon` | Kubernetes namespace | +| `--kubernetes-kubeconfig` | `~/.kube/config` | Kubernetes kubeconfig path | + +#### Run All Command Flags + +| Flag | Default | Description | +|------|---------|-------------| | `--http-address` | `0.0.0.0:8080` | HTTP server address | +| `--http-network` | `tcp` | HTTP server network | +| `--http-timeout` | `10s` | HTTP request timeout | | `--grpc-address` | `0.0.0.0:9090` | gRPC server address | -| `--use-database` | `false` | Enable database storage mode | -| `--config-paths` | `./datasource` | Configuration file paths | +| `--grpc-network` | `tcp` | gRPC server network | +| `--grpc-timeout` | `10s` | gRPC request timeout | +| `--job-address` | `0.0.0.0:9091` | Job server address | +| `--job-network` | `grpc` | Job server network | +| `--job-timeout` | `10s` | Job request timeout | +| `--job-core-worker-total` | `10` | Total number of job workers | +| `--job-core-timeout` | `10s` | Job core timeout | +| `--job-core-buffer-size` | `1000` | Job core buffer size | +| `--enable-swagger` | `false` | Enable Swagger UI | +| `--enable-swagger-basic-auth` | `true` | Enable Swagger basic authentication | +| `--swagger-basic-auth-username` | `moon.rabbit` | Swagger basic auth username | +| `--swagger-basic-auth-password` | `rabbit.swagger` | Swagger basic auth password | +| `--enable-metrics` | `false` | Enable metrics endpoint | +| `--enable-metrics-basic-auth` | `true` | Enable metrics basic authentication | +| `--metrics-basic-auth-username` | `moon.rabbit` | Metrics basic auth username | +| `--metrics-basic-auth-password` | `rabbit.metrics` | Metrics basic auth password | +| `--enable-client-config` | `false` | Enable client configuration | + +#### GORM Command Flags -See `rabbit run --help` for all available flags. +| Flag | Short | Default | Description | +|------|-------|---------|-------------| +| `--config`, `-c` | | `./config` | Config file path | +| `--force-gen`, `-f` | | `false` | Force generate code, overwrite existing | +| `--username` | | `root` | MySQL username | +| `--password` | | `123456` | MySQL password | +| `--host` | | `localhost` | MySQL host | +| `--port` | | `3306` | MySQL port | +| `--database` | | `rabbit` | MySQL database | +| `--params` | | `charset=utf8mb4,parseTime=true,loc=Asia/Shanghai` | MySQL connection parameters | +| `--biz`, `-b` | | `false` | Use biz namespace configuration | + +See `rabbit run --help` and `rabbit run all --help` for all available flags. ### Example Usage ```bash -# Run with custom configuration file -rabbit run -c ./config/server.yaml +# Run all services (HTTP, gRPC, Job) with custom configuration file +rabbit run all -c ./config/server.yaml + +# Run only HTTP server +rabbit run http -c ./config/server.yaml + +# Run only gRPC server +rabbit run grpc -c ./config/server.yaml + +# Run only Job server +rabbit run job -c ./config/server.yaml + +# Run with multiple configuration files +rabbit run all -c ./config/server.yaml -c ./config/override.yaml # Run with environment variables MOON_RABBIT_HTTP_ADDRESS=0.0.0.0:8080 \ MOON_RABBIT_USE_DATABASE=true \ -rabbit run +rabbit run all -# Run with command line flags -rabbit run \ +# Run with database storage mode +rabbit run all \ --http-address 0.0.0.0:8080 \ --grpc-address 0.0.0.0:9090 \ - --use-database true \ - --config-paths ./datasource,./config + --job-address 0.0.0.0:9091 \ + --use-database true + +# Run with file-based storage mode +rabbit run all \ + --http-address 0.0.0.0:8080 \ + --grpc-address 0.0.0.0:9090 \ + --job-address 0.0.0.0:9091 \ + --datasource-paths ./datasource,./config ``` ## 📚 Commands @@ -251,11 +391,13 @@ rabbit run \ ### Service Commands - `rabbit run` - Start the Rabbit service - -### Database Commands - -- `rabbit gorm migrate` - Migrate database schema -- `rabbit gorm gen` - Generate GORM query code + - `rabbit run all` - Start all services (HTTP, gRPC, Job) + - `rabbit run http` - Start only HTTP server + - `rabbit run grpc` - Start only gRPC server + - `rabbit run job` - Start only Job server +- `rabbit gorm` - GORM code generation and database migration tools + - `rabbit gorm gen` - Generate GORM query code + - `rabbit gorm migrate` - Migrate database schema See `rabbit --help` for detailed command information. diff --git a/cmd/apply/flags.go b/cmd/apply/flags.go index 6bdb442..9d3da78 100644 --- a/cmd/apply/flags.go +++ b/cmd/apply/flags.go @@ -7,7 +7,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags } var flags Flags diff --git a/cmd/cmd.go b/cmd/cmd.go index 6238a22..907fa40 100644 --- a/cmd/cmd.go +++ b/cmd/cmd.go @@ -76,7 +76,6 @@ func NewCmd() *cobra.Command { filterLogger := klog.NewFilter(logger, klog.FilterLevel(klog.ParseLevel(globalFlags.LogLevel))) helper := klog.NewHelper(filterLogger) klog.SetLogger(helper.Logger()) - helper.Debugw("msg", "logger initialized", "log-format", globalFlags.LogFormat, "log-level", globalFlags.LogLevel) }, } globalFlags.addFlags(rootCmd) diff --git a/cmd/config/flags.go b/cmd/config/flags.go index 3047526..8b06fe4 100644 --- a/cmd/config/flags.go +++ b/cmd/config/flags.go @@ -11,7 +11,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags path string name string force bool diff --git a/cmd/delete/flags.go b/cmd/delete/flags.go index 2e3e1e4..998ac35 100644 --- a/cmd/delete/flags.go +++ b/cmd/delete/flags.go @@ -7,7 +7,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags } var flags Flags diff --git a/cmd/get/flags.go b/cmd/get/flags.go index 1f8f770..7fd5b65 100644 --- a/cmd/get/flags.go +++ b/cmd/get/flags.go @@ -7,7 +7,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags } var flags Flags diff --git a/cmd/global.go b/cmd/global.go index 3b1d561..2f6015c 100644 --- a/cmd/global.go +++ b/cmd/global.go @@ -16,8 +16,8 @@ type GlobalFlags struct { Description string `json:"description" yaml:"description"` Version string `json:"version" yaml:"version"` Built string `json:"built" yaml:"built"` - Hostname string `json:"-" yaml:"-"` + Hostname string `json:"-" yaml:"-"` Namespace string `json:"-" yaml:"-"` RabbitConfigPath string `json:"-" yaml:"-"` LogFormat string `json:"-" yaml:"-"` @@ -28,22 +28,15 @@ func (g *GlobalFlags) addFlags(cmd *cobra.Command) { cmd.PersistentFlags().StringVarP(&g.Namespace, "namespace", "n", "", "The namespace of the service") cmd.PersistentFlags().StringVar(&g.RabbitConfigPath, "rabbit-config", "./.rabbit/", "The config file of the rabbit") cmd.PersistentFlags().StringVar(&g.LogFormat, "log-format", "TEXT", "The format of the log") - cmd.PersistentFlags().StringVar(&g.LogLevel, "log-level", "DEBUG", "The level of the log") + cmd.PersistentFlags().StringVar(&g.LogLevel, "log-level", "INFO", "The level of the log") } type GlobalOption func(*GlobalFlags) -var globalFlags GlobalFlags = GlobalFlags{ - Name: "moon.rabbit", - Author: "", - Email: "", - Repo: "https://github.com/aide-family/rabbit", - Description: "", - Hostname: hostname, -} +var globalFlags GlobalFlags -func GetGlobalFlags() GlobalFlags { - return globalFlags +func GetGlobalFlags() *GlobalFlags { + return &globalFlags } func SetGlobalFlags(opts ...GlobalOption) { @@ -87,3 +80,15 @@ func WithGlobalFlagsREPO(repo string) GlobalOption { g.Repo = repo } } + +func WithGlobalFlagsName(name string) GlobalOption { + return func(g *GlobalFlags) { + g.Name = name + } +} + +func WithGlobalFlagsHostname(hostname string) GlobalOption { + return func(g *GlobalFlags) { + g.Hostname = hostname + } +} diff --git a/cmd/gorm/flags.go b/cmd/gorm/flags.go index 2923276..efb0bb9 100644 --- a/cmd/gorm/flags.go +++ b/cmd/gorm/flags.go @@ -14,7 +14,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags configPath string forceGen bool username string diff --git a/cmd/gorm/gorm.go b/cmd/gorm/gorm.go index 81f6aa5..805ccc2 100644 --- a/cmd/gorm/gorm.go +++ b/cmd/gorm/gorm.go @@ -74,7 +74,7 @@ func initDB() (*gorm.DB, error) { c := config.New(config.WithSource( env.NewSource(), file.NewSource(flags.configPath), - )) + ), config.WithPrintLoadedDebugLog(false)) if err := c.Load(); err != nil { klog.Errorw("msg", "load config failed", "error", err) return nil, err diff --git a/cmd/run/all/all.go b/cmd/run/all/all.go new file mode 100644 index 0000000..2a59a5e --- /dev/null +++ b/cmd/run/all/all.go @@ -0,0 +1,99 @@ +// Package all is the all command for the Rabbit service +package all + +import ( + "github.com/aide-family/magicbox/hello" + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd" + "github.com/aide-family/rabbit/cmd/run" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/server" +) + +const cmdAllLong = `Start the Rabbit messaging service with all services (HTTP, gRPC, and Job). + +The server command starts all services together: + • HTTP service: Provides RESTful API interfaces for message delivery and management + • gRPC service: Provides high-performance gRPC API interfaces for inter-service communication + • Job service: Provides asynchronous message processing capabilities via EventBus + +Rabbit is a distributed messaging platform built on the Kratos framework, supporting unified +management and delivery of multiple message channels (email, Webhook, SMS, etc.). It implements +multi-tenant isolation through namespaces and supports both file-based and database storage modes +to meet different deployment requirements. + +Key Features: + • Multi-channel messaging: Unified management of email, Webhook, SMS, and other message channels + • Template-based delivery: Support for message template configuration with dynamic content rendering and reuse + • Asynchronous processing: Queue-based asynchronous message delivery for improved throughput and reliability + • Configuration management: Centralized management of channel configurations (email servers, Webhook endpoints, etc.) + • Multi-tenant isolation: Namespace-based isolation of configurations and data for different businesses or tenants + +Use Cases: + • All-in-one deployment: Deploy all services together for simple deployment scenarios + • Development and testing: Quick start for development and testing environments + • Small to medium deployments: Suitable for deployments that don't require service separation + +Note: For production environments requiring service separation, consider using the http, grpc, or job +commands to start services independently for better scalability and resource management. + +After starting the service, Rabbit will listen on the configured ports: + • HTTP: Default 0.0.0.0:8080 (configurable via --http-address) + • gRPC: Default 0.0.0.0:9090 (configurable via --grpc-address) + • Job: Default 0.0.0.0:9091 (configurable via --job-address)` + +func NewCmd() *cobra.Command { + runCmd := &cobra.Command{ + Use: "all", + Short: "Run the Rabbit all services", + Long: cmdAllLong, + Annotations: map[string]string{ + "group": cmd.ServiceCommands, + }, + Run: runAll, + } + + flags.addFlags(runCmd) + return runCmd +} + +func runAll(_ *cobra.Command, _ []string) { + flags.applyToBootstrap() + + run.StartServer("all", wireApp) +} + +func newApp(d *data.Data, srvs server.Servers, bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, error) { + defer hello.Hello() + opts := []kratos.Option{ + kratos.Logger(helper.Logger()), + kratos.Server(srvs...), + kratos.Version(hello.Version()), + kratos.ID(hello.ID()), + kratos.Name(hello.Name()), + kratos.Metadata(hello.Metadata()), + } + + if registry := d.Registry(); registry != nil { + opts = append(opts, kratos.Registrar(registry)) + } + + for _, srv := range srvs { + if httpSrv, ok := srv.(*http.Server); ok { + server.BindSwagger(httpSrv, bc, helper) + server.BindMetrics(httpSrv, bc, helper) + } + } + + // 生成客户端配置 + if err := run.GenerateClientConfig(bc, srvs, helper); err != nil { + helper.Warnw("msg", "generate client config failed", "error", err) + } + + return kratos.New(opts...), nil +} diff --git a/cmd/run/all/flags.go b/cmd/run/all/flags.go new file mode 100644 index 0000000..6a15dad --- /dev/null +++ b/cmd/run/all/flags.go @@ -0,0 +1,84 @@ +package all + +import ( + "strconv" + "time" + + "github.com/aide-family/magicbox/pointer" + "github.com/aide-family/magicbox/strutil" + "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/aide-family/rabbit/cmd/run" +) + +type Flags struct { + *run.RunFlags + + httpTimeout string + grpcTimeout string + jobTimeout string + jobCoreTimeout string + enableSwagger bool + enableSwaggerBasicAuth bool + enableMetrics bool + enableMetricsBasicAuth bool +} + +var flags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.RunFlags = run.GetRunFlags() + c.Flags().StringVar(&f.Server.Http.Address, "http-address", f.Server.Http.Address, `Example: --http-address="0.0.0.0:8080", --http-address=":8080"`) + c.Flags().StringVar(&f.Server.Http.Network, "http-network", f.Server.Http.Network, `Example: --http-network="tcp"`) + c.Flags().StringVar(&f.httpTimeout, "http-timeout", f.Server.Http.Timeout.AsDuration().String(), `Example: --http-timeout="10s", --http-timeout="1m", --http-timeout="1h", --http-timeout="1d"`) + enableSwagger, _ := strconv.ParseBool(f.SwaggerBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableSwagger, "enable-swagger", enableSwagger, `Example: --enable-swagger`) + enableSwaggerBasicAuth, _ := strconv.ParseBool(f.SwaggerBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableSwaggerBasicAuth, "enable-swagger-basic-auth", enableSwaggerBasicAuth, `Example: --enable-swagger-basic-auth`) + c.Flags().StringVar(&f.SwaggerBasicAuth.Username, "swagger-basic-auth-username", f.SwaggerBasicAuth.Username, `Example: --swagger-basic-auth-username="username"`) + c.Flags().StringVar(&f.SwaggerBasicAuth.Password, "swagger-basic-auth-password", f.SwaggerBasicAuth.Password, `Example: --swagger-basic-auth-password="password"`) + enableMetrics, _ := strconv.ParseBool(f.MetricsBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableMetrics, "enable-metrics", enableMetrics, `Example: --enable-metrics`) + enableMetricsBasicAuth, _ := strconv.ParseBool(f.MetricsBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableMetricsBasicAuth, "enable-metrics-basic-auth", enableMetricsBasicAuth, `Example: --enable-metrics-basic-auth`) + c.Flags().StringVar(&f.MetricsBasicAuth.Username, "metrics-basic-auth-username", f.MetricsBasicAuth.Username, `Example: --metrics-basic-auth-username="username"`) + c.Flags().StringVar(&f.MetricsBasicAuth.Password, "metrics-basic-auth-password", f.MetricsBasicAuth.Password, `Example: --metrics-basic-auth-password="password"`) + + c.Flags().StringVar(&f.Server.Grpc.Address, "grpc-address", f.Server.Grpc.Address, `Example: --grpc-address="0.0.0.0:9090", --grpc-address=":9090"`) + c.Flags().StringVar(&f.Server.Grpc.Network, "grpc-network", f.Server.Grpc.Network, `Example: --grpc-network="tcp"`) + c.Flags().StringVar(&f.grpcTimeout, "grpc-timeout", f.Server.Grpc.Timeout.AsDuration().String(), `Example: --grpc-timeout="10s", --grpc-timeout="1m", --grpc-timeout="1h", --grpc-timeout="1d"`) + + c.Flags().StringVar(&f.Server.Job.Address, "job-address", f.Server.Job.Address, `Example: --job-address="0.0.0.0:9091", --job-address=":9091"`) + c.Flags().StringVar(&f.Server.Job.Network, "job-network", f.Server.Job.Network, `Example: --job-network="tcp"`) + c.Flags().StringVar(&f.jobTimeout, "job-timeout", f.Server.Job.Timeout.AsDuration().String(), `Example: --job-timeout="10s", --job-timeout="1m", --job-timeout="1h", --job-timeout="1d"`) + + c.Flags().Int32Var(&f.JobCore.WorkerTotal, "job-core-worker-total", f.JobCore.WorkerTotal, `Example: --job-core-worker-total=10"`) + c.Flags().StringVar(&f.jobCoreTimeout, "job-core-timeout", f.JobCore.Timeout.AsDuration().String(), `Example: --job-core-timeout="10s", --job-core-timeout="1m", --job-core-timeout="1h", --job-core-timeout="1d"`) + c.Flags().Uint32Var(&f.JobCore.BufferSize, "job-core-buffer-size", f.JobCore.BufferSize, `Example: --job-core-buffer-size=1000"`) +} + +func (f *Flags) applyToBootstrap() { + f.ApplyToBootstrap() + if strutil.IsNotEmpty(f.httpTimeout) { + if timeout, err := time.ParseDuration(f.httpTimeout); pointer.IsNil(err) { + f.Server.Http.Timeout = durationpb.New(timeout) + } + } + if strutil.IsNotEmpty(f.grpcTimeout) { + if timeout, err := time.ParseDuration(f.grpcTimeout); pointer.IsNil(err) { + f.Server.Grpc.Timeout = durationpb.New(timeout) + } + } + if strutil.IsNotEmpty(f.jobTimeout) { + if timeout, err := time.ParseDuration(f.jobTimeout); pointer.IsNil(err) { + f.Server.Job.Timeout = durationpb.New(timeout) + } + } + + if strutil.IsNotEmpty(f.jobCoreTimeout) { + if timeout, err := time.ParseDuration(f.jobCoreTimeout); pointer.IsNil(err) { + f.JobCore.Timeout = durationpb.New(timeout) + } + } +} diff --git a/cmd/run/wire.go b/cmd/run/all/wire.go similarity index 87% rename from cmd/run/wire.go rename to cmd/run/all/wire.go index 9960d78..790d91a 100644 --- a/cmd/run/wire.go +++ b/cmd/run/all/wire.go @@ -1,8 +1,8 @@ //go:build wireinject // +build wireinject -// Package run is the run command for the Rabbit service -package run +// Package all is the all command for the Rabbit service +package all import ( "github.com/go-kratos/kratos/v2" @@ -19,7 +19,7 @@ import ( func wireApp(bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, func(), error) { panic(wire.Build( - server.ProviderSetServer, + server.ProviderSetServerAll, service.ProviderSetService, biz.ProviderSetBiz, impl.ProviderSetImpl, diff --git a/cmd/run/client.go b/cmd/run/client.go index 1a4b1dd..dc60fec 100644 --- a/cmd/run/client.go +++ b/cmd/run/client.go @@ -7,29 +7,22 @@ import ( "strings" "time" + "github.com/aide-family/magicbox/load" + "github.com/aide-family/magicbox/strutil" + "github.com/aide-family/magicbox/strutil/cnst" "github.com/go-kratos/kratos/v2/encoding" klog "github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/transport/grpc" "github.com/go-kratos/kratos/v2/transport/http" - jwtv5 "github.com/golang-jwt/jwt/v5" "google.golang.org/protobuf/types/known/durationpb" - "github.com/aide-family/magicbox/load" - "github.com/aide-family/magicbox/strutil" - "github.com/aide-family/magicbox/strutil/cnst" "github.com/aide-family/rabbit/internal/conf" "github.com/aide-family/rabbit/internal/server" "github.com/aide-family/rabbit/pkg/config" - "github.com/aide-family/rabbit/pkg/middler" ) -const ( - defaultClientUsername = "root" - defaultClientUserUID = 1 -) - -// generateClientConfig 生成 ClientConfig 并写入 .rabbit/config.yaml -func generateClientConfig( +// GenerateClientConfig 生成 ClientConfig 并写入 指定路径 默认.rabbit/config.yaml +func GenerateClientConfig( bc *conf.Bootstrap, srvs server.Servers, helper *klog.Helper, @@ -38,19 +31,22 @@ func generateClientConfig( helper.Debugw("msg", "client config is not enabled") return nil } - // 获取服务器 endpoint - var httpEndpoint, grpcEndpoint string + var clusterEndpoint string + var clusterProtocol config.ClusterConfig_Protocol for _, srv := range srvs { - switch s := srv.(type) { - case *http.Server: - endpoint, err := s.Endpoint() + if grpcSrv, ok := srv.(*grpc.Server); ok { + endpoint, err := grpcSrv.Endpoint() if err == nil { - httpEndpoint = endpoint.String() + clusterEndpoint = endpoint.String() + clusterProtocol = config.ClusterConfig_GRPC + break } - case *grpc.Server: - endpoint, err := s.Endpoint() + } + if httpSrv, ok := srv.(*http.Server); ok { + endpoint, err := httpSrv.Endpoint() if err == nil { - grpcEndpoint = endpoint.String() + clusterEndpoint = endpoint.String() + clusterProtocol = config.ClusterConfig_HTTP } } } @@ -76,43 +72,24 @@ func generateClientConfig( } else { // 如果没有配置,使用默认值 var endpoint string - var protocol config.ClusterConfig_Protocol if registryType == config.RegistryType_ETCD || registryType == config.RegistryType_KUBERNETES { // 如果使用 etcd 或 k8s 注册,使用 discovery:///服务名称 格式 endpoint = strings.Join([]string{"discovery://", serverName}, "/") - protocol = config.ClusterConfig_GRPC } else { - protocol = config.ClusterConfig_GRPC - endpoint = normalizeAddress(grpcEndpoint) - if endpoint == "" { - endpoint = normalizeAddress(httpEndpoint) - protocol = config.ClusterConfig_HTTP - } + endpoint = normalizeAddress(clusterEndpoint) } clientConfig.Cluster = &config.ClusterConfig{ Name: serverName, Endpoints: endpoint, - Protocol: protocol, + Protocol: clusterProtocol, Timeout: durationpb.New(10 * time.Second), } } - // 2. 生成 root jwtToken - jwtConf := bc.GetJwt() - if jwtConf != nil { - rootClaims := middler.NewJwtClaims(jwtConf, middler.BaseInfo{ - UserID: defaultClientUserUID, - Username: defaultClientUsername, - }) - // 设置较长的过期时间(1年) - rootClaims.ExpiresAt = jwtv5.NewNumericDate(time.Now().Add(365 * 24 * time.Hour)) - if token, err := rootClaims.GenerateToken(); err == nil { - clientConfig.JwtToken = strings.Join([]string{cnst.HTTPHeaderBearerPrefix, token}, " ") - } - } + clientConfig.JwtToken = strings.Join([]string{cnst.HTTPHeaderBearerPrefix, ""}, " ") // 写入配置文件到当前目录的 .rabbit/client_config.yaml - configDir := filepath.Dir(load.ExpandHomeDir(flags.RabbitConfigPath)) + configDir := filepath.Dir(load.ExpandHomeDir(runFlags.RabbitConfigPath)) if err := os.MkdirAll(configDir, 0o755); err != nil { return fmt.Errorf("create config directory failed: %w", err) } @@ -139,17 +116,15 @@ func normalizeAddress(addr string) string { if addr == "" { return addr } - // 移除协议前缀 - protocols := []string{"https://", "http://", "grpcs://", "grpc://"} - for _, protocol := range protocols { - if strings.HasPrefix(addr, protocol) { - addr = strings.TrimPrefix(addr, protocol) - break - } + // 移除协议前缀:去除 // 前面的所有字符 + if idx := strings.Index(addr, "//"); idx != -1 { + addr = addr[idx+2:] } + // 移除末尾的 / 字符 + addr = strings.TrimSpace(strings.TrimSuffix(addr, "/")) // 将 0.0.0.0 转换为 localhost - if strings.HasPrefix(addr, "0.0.0.0:") { - addr = strings.Replace(addr, "0.0.0.0:", "localhost:", 1) + if strings.HasPrefix(addr, "0.0.0.0") { + addr = strings.Replace(addr, "0.0.0.0", "localhost", 1) } return addr } diff --git a/cmd/run/flags.go b/cmd/run/flags.go index c86e1a4..318c063 100644 --- a/cmd/run/flags.go +++ b/cmd/run/flags.go @@ -1,83 +1,76 @@ package run import ( + "strconv" + "strings" "time" + "github.com/aide-family/magicbox/hello" + "github.com/aide-family/magicbox/load" + "github.com/aide-family/magicbox/pointer" + "github.com/aide-family/magicbox/strutil" + "github.com/go-kratos/kratos/v2" + kconfig "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/env" + "github.com/go-kratos/kratos/v2/config/file" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/middleware/tracing" "github.com/spf13/cobra" "google.golang.org/protobuf/types/known/durationpb" - "github.com/aide-family/magicbox/pointer" - "github.com/aide-family/magicbox/strutil" "github.com/aide-family/rabbit/cmd" "github.com/aide-family/rabbit/internal/conf" "github.com/aide-family/rabbit/pkg/config" "github.com/aide-family/rabbit/pkg/enum" ) -type Flags struct { - cmd.GlobalFlags - configPaths []string - useEnv bool - +type RunFlags struct { *conf.Bootstrap - environment string - httpTimeout string - grpcTimeout string - eventBusTimeout string - jwtExpire string - eventBusCoreTimeout string - registryType string + *cmd.GlobalFlags + + configPaths []string + dataSourcePaths []string + environment string + jwtExpire string + registryType string + enableClientConfig bool } -var flags Flags +var runFlags RunFlags -func (f *Flags) addFlags(c *cobra.Command, bc *conf.Bootstrap) { +func (f *RunFlags) addFlags(c *cobra.Command, bc *conf.Bootstrap) { + f.GlobalFlags = cmd.GetGlobalFlags() f.Bootstrap = bc - c.Flags().StringSliceVarP(&f.configPaths, "config", "c", []string{}, `Example: -c=./config1/ -c=./config2/`) - c.Flags().BoolVar(&f.useEnv, "use-env", false, `Example: --use-env or --use-env=true`) - - c.Flags().StringVar(&f.environment, "environment", f.Environment.String(), `Example: --environment="DEV", --environment="TEST", --environment="PREVIEW", --environment="PROD"`) - c.Flags().StringVar(&f.Server.Http.Address, "http-address", f.Server.Http.Address, `Example: --http-address="0.0.0.0:8080", --http-address=":8080"`) - c.Flags().StringVar(&f.Server.Http.Network, "http-network", f.Server.Http.Network, `Example: --http-network="tcp"`) - c.Flags().StringVar(&f.httpTimeout, "http-timeout", f.Server.Http.Timeout.AsDuration().String(), `Example: --http-timeout="10s", --http-timeout="1m", --http-timeout="1h", --http-timeout="1d"`) - c.Flags().StringVar(&f.Server.Grpc.Address, "grpc-address", f.Server.Grpc.Address, `Example: --grpc-address="0.0.0.0:9090", --grpc-address=":9090"`) - c.Flags().StringVar(&f.Server.Grpc.Network, "grpc-network", f.Server.Grpc.Network, `Example: --grpc-network="tcp"`) - c.Flags().StringVar(&f.grpcTimeout, "grpc-timeout", f.Server.Grpc.Timeout.AsDuration().String(), `Example: --grpc-timeout="10s", --grpc-timeout="1m", --grpc-timeout="1h", --grpc-timeout="1d"`) - c.Flags().StringVar(&f.Server.EventBus.Address, "event-bus-address", f.Server.EventBus.Address, `Example: --event-bus-address="0.0.0.0:9091", --event-bus-address=":9091"`) - c.Flags().StringVar(&f.Server.EventBus.Network, "event-bus-network", f.Server.EventBus.Network, `Example: --event-bus-network="tcp"`) - c.Flags().StringVar(&f.eventBusTimeout, "event-bus-timeout", f.Server.EventBus.Timeout.AsDuration().String(), `Example: --event-bus-timeout="10s", --event-bus-timeout="1m", --event-bus-timeout="1h", --event-bus-timeout="1d"`) - c.Flags().StringVar(&f.Jwt.Secret, "jwt-secret", f.Jwt.Secret, `Example: --jwt-secret="xxx"`) - c.Flags().StringVar(&f.jwtExpire, "jwt-expire", f.Jwt.Expire.AsDuration().String(), `Example: --jwt-expire="10s", --jwt-expire="1m", --jwt-expire="1h", --jwt-expire="1d"`) - c.Flags().StringVar(&f.Jwt.Issuer, "jwt-issuer", f.Jwt.Issuer, `Example: --jwt-issuer="xxx"`) - c.Flags().StringVar(&f.Main.Username, "main-username", f.Main.Username, `Example: --main-username="root"`) - c.Flags().StringVar(&f.Main.Password, "main-password", f.Main.Password, `Example: --main-password="123456"`) - c.Flags().StringVar(&f.Main.Host, "main-host", f.Main.Host, `Example: --main-host="localhost"`) - c.Flags().Int32Var(&f.Main.Port, "main-port", f.Main.Port, `Example: --main-port=3306"`) - c.Flags().StringVar(&f.Main.Database, "main-database", f.Main.Database, `Example: --main-database="rabbit"`) - c.Flags().StringVar(&f.Main.Debug, "main-debug", f.Main.Debug, `Example: --main-debug="false"`) - c.Flags().StringVar(&f.Main.UseSystemLogger, "main-use-system-logger", f.Main.UseSystemLogger, `Example: --main-use-system-logger="true"`) - c.Flags().Int32Var(&f.EventBusCore.WorkerTotal, "event-bus-core-worker-total", f.EventBusCore.WorkerTotal, `Example: --event-bus-core-worker-total=10"`) - c.Flags().StringVar(&f.eventBusCoreTimeout, "event-bus-core-timeout", f.EventBusCore.Timeout.AsDuration().String(), `Example: --event-bus-core-timeout="10s", --event-bus-core-timeout="1m", --event-bus-core-timeout="1h", --event-bus-core-timeout="1d"`) - c.Flags().Uint32Var(&f.EventBusCore.BufferSize, "event-bus-core-buffer-size", f.EventBusCore.BufferSize, `Example: --event-bus-core-buffer-size=1000"`) - c.Flags().StringVar(&f.registryType, "registry-type", f.RegistryType.String(), `Example: --registry-type="etcd"`) - c.Flags().StringVar(&f.Etcd.Endpoints, "etcd-endpoints", f.Etcd.Endpoints, `Example: --etcd-endpoints="127.0.0.1:2379"`) - c.Flags().StringVar(&f.Etcd.Username, "etcd-username", f.Etcd.Username, `Example: --etcd-username="root"`) - c.Flags().StringVar(&f.Etcd.Password, "etcd-password", f.Etcd.Password, `Example: --etcd-password="123456"`) - c.Flags().StringVar(&f.Kubernetes.Namespace, "kubernetes-namespace", f.Kubernetes.Namespace, `Example: --kubernetes-namespace="moon"`) - c.Flags().StringVar(&f.Kubernetes.KubeConfig, "kubernetes-kubeconfig", f.Kubernetes.KubeConfig, `Example: --kubernetes-kubeconfig="~/.kube/config"`) - c.Flags().StringVar(&f.SwaggerBasicAuth.Username, "swagger-basic-auth-username", f.SwaggerBasicAuth.Username, `Example: --swagger-basic-auth-username="root"`) - c.Flags().StringVar(&f.SwaggerBasicAuth.Password, "swagger-basic-auth-password", f.SwaggerBasicAuth.Password, `Example: --swagger-basic-auth-password="123456"`) - c.Flags().StringVar(&f.MetricsBasicAuth.Username, "metrics-basic-auth-username", f.MetricsBasicAuth.Username, `Example: --metrics-basic-auth-username="root"`) - c.Flags().StringVar(&f.MetricsBasicAuth.Password, "metrics-basic-auth-password", f.MetricsBasicAuth.Password, `Example: --metrics-basic-auth-password="123456"`) - c.Flags().StringVar(&f.EnableClientConfig, "enable-client-config", f.EnableClientConfig, `Example: --enable-client-config="true"`) - c.Flags().StringVar(&f.EnableSwagger, "enable-swagger", f.EnableSwagger, `Example: --enable-swagger="true"`) - c.Flags().StringVar(&f.EnableMetrics, "enable-metrics", f.EnableMetrics, `Example: --enable-metrics="true"`) - c.Flags().StringVar(&f.UseDatabase, "use-database", f.UseDatabase, `Example: --use-database="true"`) - c.Flags().StringVar(&f.ConfigPaths, "config-paths", f.ConfigPaths, `Example: --config-paths="./datasource" --config-paths="./config,./datasource"`) - c.Flags().StringVar(&f.MessageLogPath, "message-log-path", f.MessageLogPath, `Example: --message-log-path="./messages/"`) + + c.PersistentFlags().StringSliceVarP(&f.configPaths, "config", "c", []string{}, `Example: -c=./config1/ -c=./config2/`) + enableClientConfig, _ := strconv.ParseBool(f.EnableClientConfig) + c.PersistentFlags().BoolVar(&f.enableClientConfig, "enable-client-config", enableClientConfig, `Example: --enable-client-config`) + + c.PersistentFlags().StringVar(&f.environment, "environment", f.Environment.String(), `Example: --environment="DEV", --environment="TEST", --environment="PREVIEW", --environment="PROD"`) + c.PersistentFlags().StringVar(&f.Jwt.Secret, "jwt-secret", f.Jwt.Secret, `Example: --jwt-secret="xxx"`) + c.PersistentFlags().StringVar(&f.jwtExpire, "jwt-expire", f.Jwt.Expire.AsDuration().String(), `Example: --jwt-expire="10s", --jwt-expire="1m", --jwt-expire="1h", --jwt-expire="1d"`) + c.PersistentFlags().StringVar(&f.Jwt.Issuer, "jwt-issuer", f.Jwt.Issuer, `Example: --jwt-issuer="xxx"`) + c.PersistentFlags().StringVar(&f.Main.Username, "main-username", f.Main.Username, `Example: --main-username="root"`) + c.PersistentFlags().StringVar(&f.Main.Password, "main-password", f.Main.Password, `Example: --main-password="123456"`) + c.PersistentFlags().StringVar(&f.Main.Host, "main-host", f.Main.Host, `Example: --main-host="localhost"`) + c.PersistentFlags().Int32Var(&f.Main.Port, "main-port", f.Main.Port, `Example: --main-port=3306"`) + c.PersistentFlags().StringVar(&f.Main.Database, "main-database", f.Main.Database, `Example: --main-database="rabbit"`) + c.PersistentFlags().StringVar(&f.Main.Debug, "main-debug", f.Main.Debug, `Example: --main-debug="false"`) + c.PersistentFlags().StringVar(&f.Main.UseSystemLogger, "main-use-system-logger", f.Main.UseSystemLogger, `Example: --main-use-system-logger="true"`) + c.PersistentFlags().StringVar(&f.registryType, "registry-type", f.RegistryType.String(), `Example: --registry-type="ETCD"`) + c.PersistentFlags().StringVar(&f.Etcd.Endpoints, "etcd-endpoints", f.Etcd.Endpoints, `Example: --etcd-endpoints="127.0.0.1:2379"`) + c.PersistentFlags().StringVar(&f.Etcd.Username, "etcd-username", f.Etcd.Username, `Example: --etcd-username="root"`) + c.PersistentFlags().StringVar(&f.Etcd.Password, "etcd-password", f.Etcd.Password, `Example: --etcd-password="123456"`) + c.PersistentFlags().StringVar(&f.Kubernetes.Namespace, "kubernetes-namespace", f.Kubernetes.Namespace, `Example: --kubernetes-namespace="moon"`) + c.PersistentFlags().StringVar(&f.Kubernetes.KubeConfig, "kubernetes-kubeconfig", f.Kubernetes.KubeConfig, `Example: --kubernetes-kubeconfig="~/.kube/config"`) + c.PersistentFlags().StringVar(&f.UseDatabase, "use-database", f.UseDatabase, `Example: --use-database="true"`) + c.PersistentFlags().StringSliceVar(&f.dataSourcePaths, "datasource-paths", strutil.SplitSkipEmpty(f.DataSourcePaths, ","), `Example: --datasource-paths="./datasource" --datasource-paths="./config,./datasource"`) + c.PersistentFlags().StringVar(&f.MessageLogPath, "message-log-path", f.MessageLogPath, `Example: --message-log-path="./messages/"`) } -func (f *Flags) applyToBootstrap() { +func (f *RunFlags) ApplyToBootstrap() { + f.EnableClientConfig = strconv.FormatBool(f.enableClientConfig) + metadata := f.Server.Metadata if pointer.IsNil(metadata) { metadata = make(map[string]string) @@ -86,21 +79,6 @@ func (f *Flags) applyToBootstrap() { metadata["author"] = f.Author metadata["email"] = f.Email f.Server.Metadata = metadata - if strutil.IsNotEmpty(f.httpTimeout) { - if timeout, err := time.ParseDuration(f.httpTimeout); pointer.IsNil(err) { - f.Server.Http.Timeout = durationpb.New(timeout) - } - } - if strutil.IsNotEmpty(f.grpcTimeout) { - if timeout, err := time.ParseDuration(f.grpcTimeout); pointer.IsNil(err) { - f.Server.Grpc.Timeout = durationpb.New(timeout) - } - } - if strutil.IsNotEmpty(f.eventBusTimeout) { - if timeout, err := time.ParseDuration(f.eventBusTimeout); pointer.IsNil(err) { - f.Server.EventBus.Timeout = durationpb.New(timeout) - } - } if strutil.IsNotEmpty(f.environment) { f.Environment = enum.Environment(enum.Environment_value[f.environment]) @@ -111,13 +89,69 @@ func (f *Flags) applyToBootstrap() { f.Jwt.Expire = durationpb.New(expire) } } - if strutil.IsNotEmpty(f.eventBusCoreTimeout) { - if timeout, err := time.ParseDuration(f.eventBusCoreTimeout); pointer.IsNil(err) { - f.EventBusCore.Timeout = durationpb.New(timeout) - } - } if strutil.IsNotEmpty(f.registryType) { f.RegistryType = config.RegistryType(config.RegistryType_value[f.registryType]) } + + if len(f.configPaths) > 0 { + var bc conf.Bootstrap + sourceOpts := make([]kconfig.Source, 0, len(f.configPaths)) + sourceOpts = append(sourceOpts, env.NewSource()) + for _, configPath := range f.configPaths { + if strutil.IsNotEmpty(configPath) { + sourceOpts = append(sourceOpts, file.NewSource(load.ExpandHomeDir(strings.TrimSpace(configPath)))) + } + } + if len(sourceOpts) > 0 { + if err := conf.Load(&bc, sourceOpts...); err != nil { + klog.Errorw("msg", "load config failed", "error", err) + return + } + f.Bootstrap = &bc + } + } + if len(f.dataSourcePaths) > 0 { + f.DataSourcePaths = strings.Join(f.configPaths, ",") + } +} + +func GetRunFlags() *RunFlags { + return &runFlags +} + +type WireApp func(bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, func(), error) + +func StartServer(serviceName string, wireApp WireApp) { + serverConf := runFlags.GetServer() + envOpts := []hello.Option{ + hello.WithVersion(runFlags.Version), + hello.WithID(runFlags.Hostname), + hello.WithName(serverConf.GetName()), + hello.WithEnv(runFlags.Environment.String()), + hello.WithMetadata(serverConf.GetMetadata()), + } + if strings.EqualFold(serverConf.GetUseRandomID(), "true") { + envOpts = append(envOpts, hello.WithID(strutil.RandomID())) + } + hello.SetEnvWithOption(envOpts...) + helper := klog.NewHelper(klog.With(klog.GetLogger(), + "cmd", serviceName, + "service.name", hello.Name(), + "service.id", hello.ID(), + "caller", klog.DefaultCaller, + "trace.id", tracing.TraceID(), + "span.id", tracing.SpanID()), + ) + + app, cleanup, err := wireApp(runFlags.Bootstrap, helper) + if err != nil { + klog.Errorw("msg", "wireApp failed", "error", err) + return + } + defer cleanup() + if err := app.Run(); err != nil { + klog.Errorw("msg", "app run failed", "error", err) + return + } } diff --git a/cmd/run/grpc/flags.go b/cmd/run/grpc/flags.go new file mode 100644 index 0000000..e94d899 --- /dev/null +++ b/cmd/run/grpc/flags.go @@ -0,0 +1,35 @@ +package grpc + +import ( + "time" + + "github.com/aide-family/magicbox/pointer" + "github.com/aide-family/magicbox/strutil" + "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/aide-family/rabbit/cmd/run" +) + +type Flags struct { + *run.RunFlags + grpcTimeout string +} + +var flags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.RunFlags = run.GetRunFlags() + c.Flags().StringVar(&f.Server.Grpc.Address, "grpc-address", f.Server.Grpc.Address, `Example: --grpc-address="0.0.0.0:9090", --grpc-address=":9090"`) + c.Flags().StringVar(&f.Server.Grpc.Network, "grpc-network", f.Server.Grpc.Network, `Example: --grpc-network="tcp"`) + c.Flags().StringVar(&f.grpcTimeout, "grpc-timeout", f.Server.Grpc.Timeout.AsDuration().String(), `Example: --grpc-timeout="10s", --grpc-timeout="1m", --grpc-timeout="1h", --grpc-timeout="1d"`) +} + +func (f *Flags) applyToBootstrap() { + f.ApplyToBootstrap() + if strutil.IsNotEmpty(f.grpcTimeout) { + if timeout, err := time.ParseDuration(f.grpcTimeout); pointer.IsNil(err) { + f.Server.Grpc.Timeout = durationpb.New(timeout) + } + } +} diff --git a/cmd/run/grpc/grpc.go b/cmd/run/grpc/grpc.go new file mode 100644 index 0000000..655bb3f --- /dev/null +++ b/cmd/run/grpc/grpc.go @@ -0,0 +1,86 @@ +// Package grpc is the grpc command for the Rabbit service +package grpc + +import ( + "github.com/aide-family/magicbox/hello" + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd" + "github.com/aide-family/rabbit/cmd/run" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/server" +) + +const cmdGRPCLong = `Start the Rabbit gRPC service only, providing high-performance gRPC API interfaces for message delivery and management. + +The grpc command starts only the gRPC server component, which provides: + • Message sending: Send messages through various channels (email, Webhook, SMS, etc.) + • Configuration management: Manage channel configurations and templates + • Message query: Query message logs and delivery status + • Health check: Service health status monitoring + +Key Features: + • gRPC API: High-performance gRPC API interfaces with Protocol Buffers + • Efficient serialization: Binary Protocol Buffers for efficient data transfer + • Streaming support: Support for streaming RPC calls for real-time data + • Multi-tenant support: Namespace-based isolation + • JWT authentication: Secure API access with JWT tokens + +Use Cases: + • Microservices communication: Deploy gRPC service for inter-service communication in microservices architecture + • High-performance scenarios: Use gRPC for high-throughput message delivery with low latency + • Service mesh: Integrate gRPC service into service mesh architecture (Istio, Linkerd, etc.) + • Internal services: Provide gRPC API for internal service-to-service communication + +Note: This command only starts the gRPC service. For asynchronous message processing, you need to +start the job service separately using the "rabbit job" command. + +After starting the service, Rabbit gRPC will listen on the configured gRPC port (default: 0.0.0.0:9090, +configurable via --grpc-address) and provide gRPC API interfaces for client access.` + +func NewCmd() *cobra.Command { + runCmd := &cobra.Command{ + Use: "grpc", + Short: "Run the Rabbit gRPC service only", + Long: cmdGRPCLong, + Annotations: map[string]string{ + "group": cmd.ServiceCommands, + }, + Run: runGRPCServer, + } + + flags.addFlags(runCmd) + return runCmd +} + +func runGRPCServer(_ *cobra.Command, _ []string) { + flags.applyToBootstrap() + + run.StartServer("grpc", wireApp) +} + +func newApp(d *data.Data, srvs server.Servers, bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, error) { + defer hello.Hello() + opts := []kratos.Option{ + kratos.Logger(helper.Logger()), + kratos.Server(srvs...), + kratos.Version(hello.Version()), + kratos.ID(hello.ID()), + kratos.Name(hello.Name()), + kratos.Metadata(hello.Metadata()), + } + + if registry := d.Registry(); registry != nil { + opts = append(opts, kratos.Registrar(registry)) + } + + // 生成客户端配置 + if err := run.GenerateClientConfig(bc, srvs, helper); err != nil { + helper.Warnw("msg", "generate client config failed", "error", err) + } + + return kratos.New(opts...), nil +} diff --git a/cmd/run/grpc/wire.go b/cmd/run/grpc/wire.go new file mode 100644 index 0000000..64da720 --- /dev/null +++ b/cmd/run/grpc/wire.go @@ -0,0 +1,30 @@ +//go:build wireinject +// +build wireinject + +// Package grpc is the grpc command for the Rabbit service +package grpc + +import ( + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + + "github.com/aide-family/rabbit/internal/biz" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/data/impl" + "github.com/aide-family/rabbit/internal/server" + "github.com/aide-family/rabbit/internal/service" +) + +func wireApp(bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, func(), error) { + panic(wire.Build( + server.ProviderSetServerGRPC, + service.ProviderSetService, + biz.ProviderSetBiz, + impl.ProviderSetImpl, + data.ProviderSetData, + newApp, + )) +} + diff --git a/cmd/run/http/flags.go b/cmd/run/http/flags.go new file mode 100644 index 0000000..bb52072 --- /dev/null +++ b/cmd/run/http/flags.go @@ -0,0 +1,59 @@ +package http + +import ( + "strconv" + "time" + + "github.com/aide-family/magicbox/pointer" + "github.com/aide-family/magicbox/strutil" + "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/aide-family/rabbit/cmd/run" +) + +type Flags struct { + *run.RunFlags + + httpTimeout string + enableSwagger bool + enableSwaggerBasicAuth bool + enableMetrics bool + enableMetricsBasicAuth bool +} + +var flags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.RunFlags = run.GetRunFlags() + c.Flags().StringVar(&f.Server.Http.Address, "http-address", f.Server.Http.Address, `Example: --http-address="0.0.0.0:8080", --http-address=":8080"`) + c.Flags().StringVar(&f.Server.Http.Network, "http-network", f.Server.Http.Network, `Example: --http-network="tcp"`) + c.Flags().StringVar(&f.httpTimeout, "http-timeout", f.Server.Http.Timeout.AsDuration().String(), `Example: --http-timeout="10s", --http-timeout="1m", --http-timeout="1h", --http-timeout="1d"`) + + enableSwagger, _ := strconv.ParseBool(f.SwaggerBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableSwagger, "enable-swagger", enableSwagger, `Example: --enable-swagger`) + enableSwaggerBasicAuth, _ := strconv.ParseBool(f.SwaggerBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableSwaggerBasicAuth, "enable-swagger-basic-auth", enableSwaggerBasicAuth, `Example: --enable-swagger-basic-auth`) + c.Flags().StringVar(&f.SwaggerBasicAuth.Username, "swagger-basic-auth-username", f.SwaggerBasicAuth.Username, `Example: --swagger-basic-auth-username="username"`) + c.Flags().StringVar(&f.SwaggerBasicAuth.Password, "swagger-basic-auth-password", f.SwaggerBasicAuth.Password, `Example: --swagger-basic-auth-password="password"`) + enableMetrics, _ := strconv.ParseBool(f.MetricsBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableMetrics, "enable-metrics", enableMetrics, `Example: --enable-metrics`) + enableMetricsBasicAuth, _ := strconv.ParseBool(f.MetricsBasicAuth.Enabled) + c.Flags().BoolVar(&f.enableMetricsBasicAuth, "enable-metrics-basic-auth", enableMetricsBasicAuth, `Example: --enable-metrics-basic-auth`) + c.Flags().StringVar(&f.MetricsBasicAuth.Username, "metrics-basic-auth-username", f.MetricsBasicAuth.Username, `Example: --metrics-basic-auth-username="username"`) + c.Flags().StringVar(&f.MetricsBasicAuth.Password, "metrics-basic-auth-password", f.MetricsBasicAuth.Password, `Example: --metrics-basic-auth-password="password"`) +} + +func (f *Flags) applyToBootstrap() { + f.ApplyToBootstrap() + if strutil.IsNotEmpty(f.httpTimeout) { + if timeout, err := time.ParseDuration(f.httpTimeout); pointer.IsNil(err) { + f.Server.Http.Timeout = durationpb.New(timeout) + } + } + + f.EnableSwagger = strconv.FormatBool(f.enableSwagger) + f.SwaggerBasicAuth.Enabled = strconv.FormatBool(f.enableSwaggerBasicAuth) + f.EnableMetrics = strconv.FormatBool(f.enableMetrics) + f.MetricsBasicAuth.Enabled = strconv.FormatBool(f.enableMetricsBasicAuth) +} diff --git a/cmd/run/http/http.go b/cmd/run/http/http.go new file mode 100644 index 0000000..9d30245 --- /dev/null +++ b/cmd/run/http/http.go @@ -0,0 +1,94 @@ +// Package http is the http command for the Rabbit service +package http + +import ( + "github.com/aide-family/magicbox/hello" + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport/http" + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd" + "github.com/aide-family/rabbit/cmd/run" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/server" +) + +const cmdHTTPLong = `Start the Rabbit HTTP service only, providing RESTful API interfaces for message delivery and management. + +The http command starts only the HTTP server component, which provides: + • Message sending: Send messages through various channels (email, Webhook, SMS, etc.) + • Configuration management: Manage channel configurations and templates + • Message query: Query message logs and delivery status + • Health check: Service health status monitoring + +Key Features: + • RESTful API: Standard HTTP/REST API interfaces + • Swagger documentation: Automatic API documentation (if enabled via --enable-swagger) + • Metrics endpoint: Prometheus metrics endpoint at /metrics (if enabled via --enable-metrics) + • Multi-tenant support: Namespace-based isolation + • JWT authentication: Secure API access with JWT tokens + +Use Cases: + • API gateway: Deploy HTTP service separately as an API gateway for external clients + • Load balancing: Deploy multiple HTTP instances behind a load balancer for horizontal scaling + • Microservices: Integrate HTTP service into microservices architecture + • Web applications: Provide HTTP API for web frontend applications + +Note: This command only starts the HTTP service. For asynchronous message processing, you need to +start the job service separately using the "rabbit job" command. + +After starting the service, Rabbit HTTP will listen on the configured HTTP port (default: 0.0.0.0:8080, +configurable via --http-address) and provide RESTful API interfaces for client access.` + +func NewCmd() *cobra.Command { + runCmd := &cobra.Command{ + Use: "http", + Short: "Run the Rabbit HTTP service only", + Long: cmdHTTPLong, + Annotations: map[string]string{ + "group": cmd.ServiceCommands, + }, + Run: runHTTPServer, + } + + flags.addFlags(runCmd) + return runCmd +} + +func runHTTPServer(_ *cobra.Command, _ []string) { + flags.applyToBootstrap() + + run.StartServer("http", wireApp) +} + +func newApp(d *data.Data, srvs server.Servers, bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, error) { + defer hello.Hello() + opts := []kratos.Option{ + kratos.Logger(helper.Logger()), + kratos.Server(srvs...), + kratos.Version(hello.Version()), + kratos.ID(hello.ID()), + kratos.Name(hello.Name()), + kratos.Metadata(hello.Metadata()), + } + + if registry := d.Registry(); registry != nil { + opts = append(opts, kratos.Registrar(registry)) + } + + for _, srv := range srvs { + if httpSrv, ok := srv.(*http.Server); ok { + server.BindSwagger(httpSrv, bc, helper) + server.BindMetrics(httpSrv, bc, helper) + } + } + + // 生成客户端配置 + if err := run.GenerateClientConfig(bc, srvs, helper); err != nil { + helper.Warnw("msg", "generate client config failed", "error", err) + } + + return kratos.New(opts...), nil +} diff --git a/cmd/run/http/wire.go b/cmd/run/http/wire.go new file mode 100644 index 0000000..1b83ee1 --- /dev/null +++ b/cmd/run/http/wire.go @@ -0,0 +1,29 @@ +//go:build wireinject +// +build wireinject + +// Package http is the http command for the Rabbit service +package http + +import ( + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + + "github.com/aide-family/rabbit/internal/biz" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/data/impl" + "github.com/aide-family/rabbit/internal/server" + "github.com/aide-family/rabbit/internal/service" +) + +func wireApp(bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, func(), error) { + panic(wire.Build( + server.ProviderSetServerHTTP, + service.ProviderSetService, + biz.ProviderSetBiz, + impl.ProviderSetImpl, + data.ProviderSetData, + newApp, + )) +} diff --git a/cmd/run/job/flags.go b/cmd/run/job/flags.go new file mode 100644 index 0000000..41fdba8 --- /dev/null +++ b/cmd/run/job/flags.go @@ -0,0 +1,46 @@ +package job + +import ( + "time" + + "github.com/spf13/cobra" + "google.golang.org/protobuf/types/known/durationpb" + + "github.com/aide-family/magicbox/pointer" + "github.com/aide-family/magicbox/strutil" + "github.com/aide-family/rabbit/cmd/run" +) + +type Flags struct { + *run.RunFlags + + jobTimeout string + jobCoreTimeout string +} + +var flags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.RunFlags = run.GetRunFlags() + c.Flags().StringVar(&f.Server.Job.Address, "job-address", f.Server.Job.Address, `Example: --job-address="0.0.0.0:9091", --job-address=":9091"`) + c.Flags().StringVar(&f.Server.Job.Network, "job-network", f.Server.Job.Network, `Example: --job-network="tcp"`) + c.Flags().StringVar(&f.jobTimeout, "job-timeout", f.Server.Job.Timeout.AsDuration().String(), `Example: --job-timeout="10s", --job-timeout="1m", --job-timeout="1h", --job-timeout="1d"`) + c.Flags().Int32Var(&f.JobCore.WorkerTotal, "job-core-worker-total", f.JobCore.WorkerTotal, `Example: --job-core-worker-total=10"`) + c.Flags().StringVar(&f.jobCoreTimeout, "job-core-timeout", f.JobCore.Timeout.AsDuration().String(), `Example: --job-core-timeout="10s", --job-core-timeout="1m", --job-core-timeout="1h", --job-core-timeout="1d"`) + c.Flags().Uint32Var(&f.JobCore.BufferSize, "job-core-buffer-size", f.JobCore.BufferSize, `Example: --job-core-buffer-size=1000"`) +} + +func (f *Flags) applyToBootstrap() { + f.ApplyToBootstrap() + if strutil.IsNotEmpty(f.jobTimeout) { + if timeout, err := time.ParseDuration(f.jobTimeout); pointer.IsNil(err) { + f.Server.Job.Timeout = durationpb.New(timeout) + } + } + + if strutil.IsNotEmpty(f.jobCoreTimeout) { + if timeout, err := time.ParseDuration(f.jobCoreTimeout); pointer.IsNil(err) { + f.JobCore.Timeout = durationpb.New(timeout) + } + } +} diff --git a/cmd/run/job/job.go b/cmd/run/job/job.go new file mode 100644 index 0000000..1df078d --- /dev/null +++ b/cmd/run/job/job.go @@ -0,0 +1,85 @@ +// Package job is the job command for the Rabbit service +package job + +import ( + "github.com/aide-family/magicbox/hello" + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd" + "github.com/aide-family/rabbit/cmd/run" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/server" +) + +const cmdJobLong = `Start the Rabbit job service (EventBus) only, providing asynchronous message processing capabilities. + +The job command starts only the EventBus/Job service component, which handles: + • Message queue processing: Process messages from the queue asynchronously + • Background tasks: Handle background message delivery tasks (email, Webhook, SMS, etc.) + • Event-driven processing: Process messages based on events and triggers + • Worker pool management: Manage worker pools for concurrent message processing + +Key Features: + • Asynchronous processing: Process messages asynchronously from the queue without blocking API calls + • Configurable worker pool: Adjustable worker pool size via --job-core-worker-total for optimal performance + • Event-driven architecture: Event-driven message processing for scalable and resilient message delivery + • High throughput: Support for high-throughput message processing with configurable buffer size + • Timeout control: Configurable timeout for message processing via --job-core-timeout + +Use Cases: + • Message queue processing: Deploy job service separately for dedicated message queue processing + • Background tasks: Handle background message delivery tasks independently from API services + • Scalability: Scale job service independently for better performance and resource utilization + • Microservices: Deploy job service as a separate microservice in distributed architectures + • High-volume processing: Dedicate resources to message processing for high-volume scenarios + +Note: This command only starts the job service. For API access (HTTP/gRPC), you need to start the +http or grpc service separately. The job service processes messages that are submitted through +the HTTP or gRPC APIs. + +After starting the service, Rabbit job will: + • Listen on the configured job port (default: 0.0.0.0:9091, configurable via --job-address) + • Start processing messages from the queue asynchronously + • Handle background message delivery tasks with the configured worker pool` + +func NewCmd() *cobra.Command { + runCmd := &cobra.Command{ + Use: "job", + Short: "Run the Rabbit job service (EventBus) only", + Long: cmdJobLong, + Annotations: map[string]string{ + "group": cmd.ServiceCommands, + }, + Run: runJobServer, + } + + flags.addFlags(runCmd) + return runCmd +} + +func runJobServer(_ *cobra.Command, _ []string) { + flags.applyToBootstrap() + + run.StartServer("job", wireApp) +} + +func newApp(d *data.Data, srvs server.Servers, bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, error) { + defer hello.Hello() + opts := []kratos.Option{ + kratos.Logger(helper.Logger()), + kratos.Server(srvs...), + kratos.Version(hello.Version()), + kratos.ID(hello.ID()), + kratos.Name(hello.Name()), + kratos.Metadata(hello.Metadata()), + } + + if registry := d.Registry(); registry != nil { + opts = append(opts, kratos.Registrar(registry)) + } + + return kratos.New(opts...), nil +} diff --git a/cmd/run/job/wire.go b/cmd/run/job/wire.go new file mode 100644 index 0000000..4669351 --- /dev/null +++ b/cmd/run/job/wire.go @@ -0,0 +1,29 @@ +//go:build wireinject +// +build wireinject + +// Package job is the job command for the Rabbit service +package job + +import ( + "github.com/go-kratos/kratos/v2" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/google/wire" + + "github.com/aide-family/rabbit/internal/biz" + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/data" + "github.com/aide-family/rabbit/internal/data/impl" + "github.com/aide-family/rabbit/internal/server" + "github.com/aide-family/rabbit/internal/service" +) + +func wireApp(bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, func(), error) { + panic(wire.Build( + server.ProviderSetServerJob, + service.ProviderSetService, + biz.ProviderSetBiz, + impl.ProviderSetImpl, + data.ProviderSetData, + newApp, + )) +} diff --git a/cmd/run/run.go b/cmd/run/run.go deleted file mode 100644 index 01cb9be..0000000 --- a/cmd/run/run.go +++ /dev/null @@ -1,156 +0,0 @@ -// Package run is the run command for the Rabbit service -package run - -import ( - "strings" - - "github.com/aide-family/magicbox/hello" - "github.com/aide-family/magicbox/load" - "github.com/aide-family/magicbox/strutil" - "github.com/go-kratos/kratos/v2" - "github.com/go-kratos/kratos/v2/config" - "github.com/go-kratos/kratos/v2/config/env" - "github.com/go-kratos/kratos/v2/config/file" - klog "github.com/go-kratos/kratos/v2/log" - "github.com/go-kratos/kratos/v2/middleware/tracing" - "github.com/spf13/cobra" - - "github.com/aide-family/rabbit/cmd" - "github.com/aide-family/rabbit/internal/conf" - "github.com/aide-family/rabbit/internal/data" - "github.com/aide-family/rabbit/internal/server" -) - -const cmdRunLong = `Start the Rabbit messaging service, providing unified message delivery and management capabilities. - -Rabbit is a distributed messaging platform built on the Kratos framework, supporting unified -management and delivery of multiple message channels (email, Webhook, SMS, etc.). It implements -multi-tenant isolation through namespaces and supports both file-based and database storage modes -to meet different deployment requirements. - -Key Features: - • Multi-channel messaging: Unified management of email, Webhook, SMS, and other message channels - • Template-based delivery: Support for message template configuration with dynamic content rendering and reuse - • Asynchronous processing: Queue-based asynchronous message delivery for improved throughput and reliability - • Configuration management: Centralized management of channel configurations (email servers, Webhook endpoints, etc.) - • Multi-tenant isolation: Namespace-based isolation of configurations and data for different businesses or tenants - -Use Cases: - • Enterprise notification system: Unified management of business notifications (orders, alerts, system messages, etc.) - • Microservices message center: Provide unified messaging capabilities for microservices architecture - • Multi-channel push platform: Integrate multiple message channels for unified message delivery and management - -After starting the service, Rabbit will listen on the configured ports and provide HTTP/gRPC API -interfaces for client access.` - -func NewCmd(defaultServerConfigBytes []byte) *cobra.Command { - runCmd := &cobra.Command{ - Use: "run", - Short: "Run the Rabbit service", - Long: cmdRunLong, - Annotations: map[string]string{ - "group": cmd.ServiceCommands, - }, - Run: runServer, - } - var bc conf.Bootstrap - c := config.New(config.WithSource( - env.NewSource(), - conf.NewBytesSource(defaultServerConfigBytes), - ) /*config.WithPrintLoadedDebugLog(false)*/) - if err := c.Load(); err != nil { - klog.Errorw("msg", "load config failed", "error", err) - panic(err) - } - - if err := c.Scan(&bc); err != nil { - klog.Errorw("msg", "scan config failed", "error", err) - panic(err) - } - - flags.addFlags(runCmd, &bc) - return runCmd -} - -func runServer(_ *cobra.Command, _ []string) { - flags.GlobalFlags = cmd.GetGlobalFlags() - flags.applyToBootstrap() - var bc conf.Bootstrap - sourceOpts := make([]config.Source, 0, len(flags.configPaths)) - if flags.useEnv { - sourceOpts = append(sourceOpts, env.NewSource()) - } - if len(flags.configPaths) > 0 { - for _, configPath := range flags.configPaths { - if strutil.IsNotEmpty(configPath) { - sourceOpts = append(sourceOpts, file.NewSource(load.ExpandHomeDir(strings.TrimSpace(configPath)))) - } - } - } - if len(sourceOpts) > 0 { - if err := conf.Load(&bc, sourceOpts...); err != nil { - klog.Errorw("msg", "load config failed", "error", err) - return - } - flags.Bootstrap = &bc - } - - serverConf := flags.GetServer() - envOpts := []hello.Option{ - hello.WithVersion(flags.Version), - hello.WithID(flags.Hostname), - hello.WithName(serverConf.GetName()), - hello.WithEnv(flags.Environment.String()), - hello.WithMetadata(serverConf.GetMetadata()), - } - if serverConf.GetUseRandomID() == "true" { - envOpts = append(envOpts, hello.WithID(strutil.RandomID())) - } - hello.SetEnvWithOption(envOpts...) - - helper := klog.NewHelper(klog.With(klog.GetLogger(), - "cmd", "run", - "service.name", hello.Name(), - "service.id", hello.ID(), - "caller", klog.DefaultCaller, - "trace.id", tracing.TraceID(), - "span.id", tracing.SpanID()), - ) - - app, cleanup, err := wireApp(flags.Bootstrap, helper) - if err != nil { - klog.Errorw("msg", "wireApp failed", "error", err) - return - } - defer cleanup() - if err := app.Run(); err != nil { - klog.Errorw("msg", "app run failed", "error", err) - return - } -} - -func newApp(d *data.Data, srvs server.Servers, bc *conf.Bootstrap, helper *klog.Helper) (*kratos.App, error) { - defer hello.Hello() - opts := []kratos.Option{ - kratos.Logger(helper.Logger()), - kratos.Server(srvs...), - kratos.Version(hello.Version()), - kratos.ID(hello.ID()), - kratos.Name(hello.Name()), - kratos.Metadata(hello.Metadata()), - } - - if registry := d.Registry(); registry != nil { - opts = append(opts, kratos.Registrar(registry)) - } - - srvs.BindSwagger(bc, helper) - srvs.BindMetrics(bc, helper) - - // 生成客户端配置 - if err := generateClientConfig(bc, srvs, helper); err != nil { - helper.Warnw("msg", "generate client config failed", "error", err) - } - - return kratos.New(opts...), nil -} diff --git a/cmd/run/server.go b/cmd/run/server.go new file mode 100644 index 0000000..de65458 --- /dev/null +++ b/cmd/run/server.go @@ -0,0 +1,38 @@ +// Package run is the run command for the Rabbit service +package run + +import ( + "github.com/go-kratos/kratos/v2/config" + "github.com/go-kratos/kratos/v2/config/env" + klog "github.com/go-kratos/kratos/v2/log" + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/internal/conf" +) + +const cmdRunLong = `Run the Rabbit services` + +func NewCmd(defaultServerConfigBytes []byte) *cobra.Command { + runCmd := &cobra.Command{ + Use: "run", + Short: "Run the Rabbit services", + Long: cmdRunLong, + } + var bc conf.Bootstrap + c := config.New(config.WithSource( + env.NewSource(), + conf.NewBytesSource(defaultServerConfigBytes), + ), config.WithPrintLoadedDebugLog(false)) + if err := c.Load(); err != nil { + klog.Errorw("msg", "load config failed", "error", err) + panic(err) + } + + if err := c.Scan(&bc); err != nil { + klog.Errorw("msg", "scan config failed", "error", err) + panic(err) + } + runFlags.addFlags(runCmd, &bc) + + return runCmd +} diff --git a/cmd/send/email/flags.go b/cmd/send/email/flags.go index f18a38d..995418b 100644 --- a/cmd/send/email/flags.go +++ b/cmd/send/email/flags.go @@ -4,7 +4,7 @@ import ( "strings" "github.com/aide-family/magicbox/strutil" - "github.com/aide-family/rabbit/cmd" + "github.com/aide-family/rabbit/cmd/send" apiv1 "github.com/aide-family/rabbit/pkg/api/v1" "github.com/aide-family/rabbit/pkg/config" "github.com/go-kratos/kratos/v2/encoding" @@ -12,7 +12,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + send.SendFlags UID int64 `json:"uid" yaml:"uid"` Subject string `json:"subject" yaml:"subject"` @@ -29,6 +29,7 @@ type Flags struct { var flags Flags func (f *Flags) addFlags(c *cobra.Command) { + f.SendFlags = send.GetSendFlags() c.Flags().Int64VarP(&f.UID, "uid", "u", 0, "The uid of the email") c.Flags().StringVarP(&f.Subject, "subject", "s", "", "The subject of the email") c.Flags().StringVarP(&f.Body, "body", "b", "", "The body of the email") diff --git a/cmd/send/email/run.go b/cmd/send/email/run.go index ea831d4..6bb241f 100644 --- a/cmd/send/email/run.go +++ b/cmd/send/email/run.go @@ -16,7 +16,6 @@ import ( "github.com/spf13/cobra" clientV3 "go.etcd.io/etcd/client/v3" - "github.com/aide-family/rabbit/cmd" "github.com/aide-family/rabbit/internal/conf" apiv1 "github.com/aide-family/rabbit/pkg/api/v1" "github.com/aide-family/rabbit/pkg/config" @@ -25,7 +24,6 @@ import ( ) func run(_ *cobra.Command, _ []string) { - flags.GlobalFlags = cmd.GetGlobalFlags() var bc config.ClientConfig if err := conf.Load(&bc, env.NewSource(), file.NewSource(flags.RabbitConfigPath)); err != nil { klog.Errorw("msg", "load config failed", "error", err) diff --git a/cmd/send/feishu/feishu.go b/cmd/send/feishu/feishu.go index 2acc796..c53083d 100644 --- a/cmd/send/feishu/feishu.go +++ b/cmd/send/feishu/feishu.go @@ -2,8 +2,6 @@ package feishu import ( - "fmt" - "github.com/spf13/cobra" "github.com/aide-family/rabbit/cmd" @@ -31,7 +29,7 @@ configured through configuration files or API. Sent messages are processed immed making it suitable for testing and urgent scenarios.` func NewCmd() *cobra.Command { - return &cobra.Command{ + feishuCmd := &cobra.Command{ Use: "feishu", Short: "Send Feishu messages", Long: cmdLong, @@ -40,8 +38,10 @@ func NewCmd() *cobra.Command { }, Run: run, } + feishuFlags.addFlags(feishuCmd) + return feishuCmd } func run(cmd *cobra.Command, args []string) { - fmt.Println("feishu called") + cmd.Help() } diff --git a/cmd/send/feishu/flags.go b/cmd/send/feishu/flags.go new file mode 100644 index 0000000..65c5d4e --- /dev/null +++ b/cmd/send/feishu/flags.go @@ -0,0 +1,21 @@ +package feishu + +import ( + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd/send" +) + +type Flags struct { + send.SendFlags +} + +var feishuFlags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.SendFlags = send.GetSendFlags() +} + +func GetFeishuFlags() Flags { + return feishuFlags +} diff --git a/cmd/send/flags.go b/cmd/send/flags.go index 526a5a3..0df65f1 100644 --- a/cmd/send/flags.go +++ b/cmd/send/flags.go @@ -6,12 +6,16 @@ import ( "github.com/aide-family/rabbit/cmd" ) -type Flags struct { - cmd.GlobalFlags +type SendFlags struct { + *cmd.GlobalFlags } -var flags Flags +var sendFlags SendFlags -func (f *Flags) addFlags(c *cobra.Command) { +func (f *SendFlags) addFlags(c *cobra.Command) { f.GlobalFlags = cmd.GetGlobalFlags() } + +func GetSendFlags() SendFlags { + return sendFlags +} diff --git a/cmd/send/send.go b/cmd/send/send.go index cf6befd..606669f 100644 --- a/cmd/send/send.go +++ b/cmd/send/send.go @@ -5,9 +5,6 @@ import ( "github.com/spf13/cobra" "github.com/aide-family/rabbit/cmd" - "github.com/aide-family/rabbit/cmd/send/email" - "github.com/aide-family/rabbit/cmd/send/feishu" - "github.com/aide-family/rabbit/cmd/send/sms" ) const cmdLong = `Send messages to specified channels, supporting multiple message types and delivery methods. @@ -36,7 +33,7 @@ Messages sent through this command are processed immediately, making it suitable testing and urgent scenarios. For bulk message sending, it is recommended to use the apply command to submit messages to the queue for asynchronous processing.` -func NewCmd() *cobra.Command { +func NewCmd(children ...*cobra.Command) *cobra.Command { sendCmd := &cobra.Command{ Use: "send", Short: "Send messages to specified channels", @@ -48,14 +45,9 @@ func NewCmd() *cobra.Command { cmd.Help() }, } - commands := []*cobra.Command{ - sms.NewCmd(), - feishu.NewCmd(), - email.NewCmd(), - } - sendCmd.AddCommand(commands...) - flags.addFlags(sendCmd) + sendFlags.addFlags(sendCmd) + sendCmd.AddCommand(children...) return sendCmd } diff --git a/cmd/send/sms/flags.go b/cmd/send/sms/flags.go new file mode 100644 index 0000000..e0d295f --- /dev/null +++ b/cmd/send/sms/flags.go @@ -0,0 +1,21 @@ +package sms + +import ( + "github.com/spf13/cobra" + + "github.com/aide-family/rabbit/cmd/send" +) + +type Flags struct { + send.SendFlags +} + +var smsFlags Flags + +func (f *Flags) addFlags(c *cobra.Command) { + f.SendFlags = send.GetSendFlags() +} + +func GetSmsFlags() Flags { + return smsFlags +} diff --git a/cmd/send/sms/sms.go b/cmd/send/sms/sms.go index 5ace257..c516f63 100644 --- a/cmd/send/sms/sms.go +++ b/cmd/send/sms/sms.go @@ -2,18 +2,11 @@ package sms import ( - "fmt" - "github.com/aide-family/rabbit/cmd" "github.com/spf13/cobra" ) -func NewCmd() *cobra.Command { - return &cobra.Command{ - Use: "sms", - Short: "Send SMS messages", - Long: `Send SMS messages, supporting multiple SMS service providers and template-based delivery. - +const cmdLong = `Send SMS messages, supporting multiple SMS service providers and template-based delivery. The sms command enables direct SMS message sending, supporting configuration of SMS service providers, recipient phone numbers, message content, and other parameters. It allows quick single message delivery or template-based sending. @@ -32,14 +25,22 @@ Use Cases: SMS sending requires prior configuration of SMS service providers (API Key, Secret, etc.), which can be configured through configuration files or API. Sent SMS messages are processed -immediately, making it suitable for testing and urgent scenarios.`, +immediately, making it suitable for testing and urgent scenarios.` + +func NewCmd() *cobra.Command { + smsCmd := &cobra.Command{ + Use: "sms", + Short: "Send SMS messages", + Long: cmdLong, Annotations: map[string]string{ "group": cmd.MessageCommands, }, Run: run, } + smsFlags.addFlags(smsCmd) + return smsCmd } func run(cmd *cobra.Command, args []string) { - fmt.Println("sms called") + cmd.Help() } diff --git a/cmd/version/flags.go b/cmd/version/flags.go index 2f83580..0f0bf89 100644 --- a/cmd/version/flags.go +++ b/cmd/version/flags.go @@ -7,7 +7,7 @@ import ( ) type Flags struct { - cmd.GlobalFlags + *cmd.GlobalFlags format string } diff --git a/config/server.yaml b/config/server.yaml index dddf760..512fed4 100644 --- a/config/server.yaml +++ b/config/server.yaml @@ -1,7 +1,7 @@ environment: ${MOON_RABBIT_ENVIRONMENT:PROD} -enableClientConfig: ${MOON_RABBIT_ENABLE_CLIENT_CONFIG:true} -enableSwagger: ${MOON_RABBIT_ENABLE_SWAGGER:true} -enableMetrics: ${MOON_RABBIT_ENABLE_METRICS:true} +enableClientConfig: ${MOON_RABBIT_ENABLE_CLIENT_CONFIG:false} +enableSwagger: ${MOON_RABBIT_ENABLE_SWAGGER:false} +enableMetrics: ${MOON_RABBIT_ENABLE_METRICS:false} useDatabase: ${MOON_RABBIT_USE_DATABASE:false} server: @@ -20,10 +20,10 @@ server: address: "${MOON_RABBIT_GRPC_ADDRESS:0.0.0.0:9090}" network: "${MOON_RABBIT_GRPC_NETWORK:tcp}" timeout: "${MOON_RABBIT_GRPC_TIMEOUT:10s}" - eventBus: - address: "${MOON_RABBIT_EVENT_BUS_ADDRESS:0.0.0.0:9091}" - network: "${MOON_RABBIT_EVENT_BUS_NETWORK:grpc}" - timeout: "${MOON_RABBIT_EVENT_BUS_TIMEOUT:10s}" + job: + address: "${MOON_RABBIT_JOB_ADDRESS:0.0.0.0:9091}" + network: "${MOON_RABBIT_JOB_NETWORK:grpc}" + timeout: "${MOON_RABBIT_JOB_TIMEOUT:10s}" jwt: secret: "${MOON_RABBIT_JWT_SECRET:xxx}" @@ -43,10 +43,10 @@ main: parseTime: "true" loc: Asia/Shanghai -eventBusCore: - workerTotal: ${MOON_RABBIT_EVENT_BUS_CORE_WORKER_TOTAL:10} - timeout: "${MOON_RABBIT_EVENT_BUS_CORE_TIMEOUT:10s}" - bufferSize: ${MOON_RABBIT_EVENT_BUS_CORE_BUFFER_SIZE:1000} +jobCore: + workerTotal: ${MOON_RABBIT_JOB_CORE_WORKER_TOTAL:10} + timeout: "${MOON_RABBIT_JOB_CORE_TIMEOUT:10s}" + bufferSize: ${MOON_RABBIT_JOB_CORE_BUFFER_SIZE:1000} registryType: ${MOON_RABBIT_REGISTRY_TYPE:} diff --git a/datasource/email.yaml b/datasource/email.yaml deleted file mode 100644 index 6063ee8..0000000 --- a/datasource/email.yaml +++ /dev/null @@ -1,25 +0,0 @@ -emails: - - id: 1 - uid: 1986338680076115968 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115968 - namespace: default - name: default1 - host: smtp.example.com - port: 587 - username: user@example.com - password: password - status: ENABLED - - id: 2 - uid: 1986338680076115969 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115969 - namespace: default2 - name: default2 - host: smtp.example.com - port: 587 - username: user@example.com - password: password - status: ENABLED \ No newline at end of file diff --git a/datasource/namespace.yaml b/datasource/namespace.yaml deleted file mode 100644 index f799244..0000000 --- a/datasource/namespace.yaml +++ /dev/null @@ -1,28 +0,0 @@ -namespaces: - - id: 1 - uid: 1986338680076115968 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115968 - name: default - metadata: - key: value - status: ENABLED - - id: 2 - uid: 1986338680076115969 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115969 - name: default2 - metadata: - key: value2 - status: ENABLED - - id: 3 - uid: 1986338680076115970 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115970 - name: default3 - metadata: - key: value3 - status: ENABLED \ No newline at end of file diff --git a/datasource/template.yaml b/datasource/template.yaml deleted file mode 100644 index 1da0059..0000000 --- a/datasource/template.yaml +++ /dev/null @@ -1,99 +0,0 @@ -templates: - - id: 1 - uid: 1986338680076115968 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115968 - namespace: default - name: template1 - app: TEMPLATE_APP_EMAIL - jsonData: | - { - "subject": "Hello, World!", - "body": "This is a test email.", - "content_type": "text/html", - "headers": { - "X-Custom-Header": ["value1"] - } - } - status: ENABLED - - id: 2 - uid: 1986338680076115969 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115969 - namespace: default - name: template2 - app: TEMPLATE_APP_SMS - jsonData: | - { - "content": "This is a test SMS.", - "params": { - "key": "value" - } - } - status: ENABLED - - id: 3 - uid: 1986338680076115970 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115970 - namespace: default - name: template3 - app: TEMPLATE_APP_WEBHOOK_OTHER - jsonData: | - { - "content": "This is a test webhook.", - "params": { - "key": "value" - } - } - status: ENABLED - - id: 4 - uid: 1986338680076115971 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115971 - namespace: default - name: template4 - app: TEMPLATE_APP_WEBHOOK_DINGTALK - jsonData: | - { - "content": "This is a test webhook.", - "params": { - "key": "value" - } - } - status: ENABLED - - id: 5 - uid: 1986338680076115972 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115972 - namespace: default - name: template5 - app: TEMPLATE_APP_WEBHOOK_WECHAT - jsonData: | - { - "content": "This is a test webhook.", - "params": { - "key": "value" - } - } - status: ENABLED - - id: 6 - uid: 1986338680076115973 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115973 - namespace: default - name: template6 - app: TEMPLATE_APP_WEBHOOK_FEISHU - jsonData: | - { - "content": "This is a test webhook.", - "params": { - "key": "value" - } - } - status: ENABLED diff --git a/datasource/webhook.yaml b/datasource/webhook.yaml deleted file mode 100644 index 33076d3..0000000 --- a/datasource/webhook.yaml +++ /dev/null @@ -1,55 +0,0 @@ -webhooks: - - id: 1 - uid: 1986338680076115968 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115968 - namespace: default - app: OTHER - name: webhook1 - url: https://example.com - method: POST - headers: - key: value - secret: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - status: ENABLED - - id: 2 - uid: 1986338680076115969 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115969 - namespace: default - app: DINGTALK - name: webhook2 - url: https://example.com - method: POST - headers: - key: value - secret: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" - status: ENABLED - - id: 3 - uid: 1986338680076115970 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115970 - namespace: default - app: WECHAT - name: webhook3 - url: https://example.com - method: POST - headers: - key: value - status: ENABLED - - id: 4 - uid: 1986338680076115971 - createdAt: 2021-01-01 00:00:00 - updatedAt: 2021-01-01 00:00:00 - creator: 1986338680076115971 - namespace: default - app: FEISHU - name: webhook4 - url: https://example.com - method: POST - headers: - key: value - status: ENABLED \ No newline at end of file diff --git a/deploy/server/docker/.gitignore b/deploy/server/docker/.gitignore new file mode 100644 index 0000000..ec01229 --- /dev/null +++ b/deploy/server/docker/.gitignore @@ -0,0 +1 @@ +datasource \ No newline at end of file diff --git a/deploy/server/docker/README-docker-compose.md b/deploy/server/docker/README-docker-compose.md deleted file mode 100644 index e99f79d..0000000 --- a/deploy/server/docker/README-docker-compose.md +++ /dev/null @@ -1,160 +0,0 @@ -# Rabbit Docker Compose 使用指南 - -## 快速开始 - -### 1. 使用文件配置模式(默认,无需数据库) - -```bash -# 启动服务 -docker-compose up -d - -# 查看日志 -docker-compose logs -f rabbit - -# 停止服务 -docker-compose down -``` - -### 2. 使用数据库模式 - -```bash -# 1. 创建 .env 文件并设置 -echo "MOON_RABBIT_USE_DATABASE=true" >> .env - -# 2. 启动服务(包括 MySQL) -docker-compose up -d - -# 3. 等待 MySQL 就绪后,Rabbit 会自动连接 -docker-compose logs -f rabbit -``` - -## 自定义启动命令 - -### 方式一:使用 docker-compose.override.yml(推荐) - -```bash -# 1. 复制示例文件 -cp docker-compose.override.yml.example docker-compose.override.yml - -# 2. 编辑 docker-compose.override.yml,修改 command 字段 -# 例如:使用数据库模式启动 -command: ["run", "--use-database", "true", "--main-host", "mysql"] - -# 3. 启动服务 -docker-compose up -d -``` - -### 方式二:使用命令行参数 - -```bash -# 使用数据库模式启动 -docker-compose run --rm rabbit run --use-database true --main-host mysql - -# 查看版本信息 -docker-compose run --rm rabbit version - -# 生成配置文件 -docker-compose run --rm rabbit config --path /moon/config - -# 发送测试邮件 -docker-compose run --rm rabbit send email --uid 1 --to test@example.com --subject "Test" --body "Test email" -``` - -### 方式三:修改 docker-compose.yml - -直接编辑 `docker-compose.yml` 文件中的 `command` 字段: - -```yaml -services: - rabbit: - command: ["run", "--use-database", "true", "--main-host", "mysql"] -``` - -## 常用命令示例 - -### 启动服务 -```bash -# 默认启动(文件配置模式) -docker-compose up -d - -# 使用数据库模式启动 -docker-compose up -d --env-file .env -``` - -### 查看服务状态 -```bash -# 查看所有服务状态 -docker-compose ps - -# 查看 Rabbit 日志 -docker-compose logs -f rabbit -``` - -### 执行 Rabbit 命令 -```bash -# 查看版本 -docker-compose exec rabbit rabbit version - -# 生成配置文件 -docker-compose exec rabbit rabbit config --path /moon/config - -# 发送邮件 -docker-compose exec rabbit rabbit send email --uid 1 --to test@example.com --subject "Test" --body "Test" -``` - -### 停止和清理 -```bash -# 停止服务 -docker-compose stop - -# 停止并删除容器 -docker-compose down - -# 停止并删除容器和卷(会删除数据) -docker-compose down -v -``` - -## 配置说明 - -### 环境变量配置 - -所有配置项都可以通过环境变量设置,详见 `.env.example` 文件。 - -### 配置文件挂载 - -- `./datasource` → `/moon/datasource`:文件配置模式的配置文件目录 -- `./config` → `/moon/config`:自定义配置文件目录(可选) - -### 数据持久化 - -- `rabbit_data`:Rabbit 数据卷 - -## 访问服务 - -启动成功后,可以通过以下地址访问: - -- HTTP API: http://localhost:8080 -- gRPC API: localhost:9090 -- Swagger UI: http://localhost:8080/swagger-ui/ -- Metrics: http://localhost:8080/metrics - -## 故障排查 - -### 查看日志 -```bash -# Rabbit 日志 -docker-compose logs rabbit -``` - -### 检查服务健康状态 -```bash -# 检查 Rabbit 健康状态 -docker-compose exec rabbit rabbit version -``` - -### 重启服务 -```bash -# 重启 Rabbit -docker-compose restart rabbit -``` - diff --git a/deploy/server/k8s/README.md b/deploy/server/k8s/README.md deleted file mode 100644 index e7e68b6..0000000 --- a/deploy/server/k8s/README.md +++ /dev/null @@ -1,338 +0,0 @@ -# Rabbit Kubernetes 部署配置 - -本目录包含 Rabbit 消息服务的 Kubernetes 部署配置文件,按照资源类型拆分为多个文件,便于管理和维护。 - -## 文件结构 - -``` -k8s/ -├── namespace.yaml # Namespace 定义 -├── configmap.yaml # ConfigMap 配置 -├── secret.yaml.example # Secret 配置示例(需要手动创建) -├── serviceaccount.yaml # ServiceAccount 定义 -├── rbac.yaml # RBAC 权限配置(Role 和 RoleBinding) -├── deployment.yaml # Deployment 部署配置 -├── service.yaml # Service 服务配置 -├── ingress.yaml # Ingress 入口配置(可选) -├── kustomization.yaml # Kustomize 配置文件 -└── README.md # 本文档 -``` - -## 快速部署 - -### 方式一:使用 kubectl 逐个部署 - -```bash -# 1. 创建 Namespace -kubectl apply -f namespace.yaml - -# 2. 创建 Secret(必须先创建,否则 Deployment 会失败) -kubectl create secret generic rabbit-secrets \ - --from-literal=jwt-secret='your-jwt-secret-key' \ - --from-literal=mysql-username='root' \ - --from-literal=mysql-password='your-mysql-password' \ - --namespace=rabbit - -# 3. 创建 ConfigMap -kubectl apply -f configmap.yaml - -# 4. 创建 ServiceAccount 和 RBAC -kubectl apply -f serviceaccount.yaml -kubectl apply -f rbac.yaml - -# 5. 创建 Deployment -kubectl apply -f deployment.yaml - -# 6. 创建 Service -kubectl apply -f service.yaml - -# 7. 创建 Ingress(可选) -kubectl apply -f ingress.yaml -``` - -### 方式二:使用 Kustomize 统一部署(推荐) - -```bash -# 1. 先创建 Secret -kubectl create secret generic rabbit-secrets \ - --from-literal=jwt-secret='your-jwt-secret-key' \ - --from-literal=mysql-username='root' \ - --from-literal=mysql-password='your-mysql-password' \ - --namespace=rabbit - -# 2. 使用 Kustomize 部署所有资源 -kubectl apply -k deploy/server/k8s/ -``` - -### 方式三:使用脚本一键部署 - -```bash -# 创建部署脚本 -cat > deploy.sh << 'EOF' -#!/bin/bash -set -e - -# 创建 Namespace -kubectl apply -f namespace.yaml - -# 创建 Secret(如果不存在) -if ! kubectl get secret rabbit-secrets -n rabbit &>/dev/null; then - echo "Creating secret..." - kubectl create secret generic rabbit-secrets \ - --from-literal=jwt-secret='change-me-in-production' \ - --from-literal=mysql-username='root' \ - --from-literal=mysql-password='your-mysql-password' \ - --namespace=rabbit -fi - -# 部署其他资源 -kubectl apply -f configmap.yaml -kubectl apply -f serviceaccount.yaml -kubectl apply -f rbac.yaml -kubectl apply -f deployment.yaml -kubectl apply -f service.yaml -kubectl apply -f ingress.yaml - -echo "Deployment completed!" -EOF - -chmod +x deploy.sh -./deploy.sh -``` - -## 配置说明 - -### 1. Namespace - -定义了 `rabbit` 命名空间,用于隔离 Rabbit 服务的所有资源。 - -### 2. ConfigMap - -包含 Rabbit 服务的主配置文件 `server.yaml`,支持环境变量替换。 - -**重要配置项:** -- `useDatabase`: 是否使用数据库模式(false 使用文件配置,true 使用数据库) -- `main.host`: MySQL 服务地址(如果使用数据库模式) -- `configPaths`: 文件配置模式的配置文件路径 - -### 3. Secret - -包含敏感信息,如 JWT 密钥、MySQL 密码等。 - -**创建方式:** - -```bash -# 方式一:使用 kubectl 命令 -kubectl create secret generic rabbit-secrets \ - --from-literal=jwt-secret='your-jwt-secret-key' \ - --from-literal=mysql-username='root' \ - --from-literal=mysql-password='your-mysql-password' \ - --namespace=rabbit - -# 方式二:使用 YAML 文件(需要 base64 编码) -# 参考 secret.yaml.example 文件 -``` - -**生产环境建议:** -- 使用 Sealed Secrets 或 External Secrets Operator -- 使用密钥管理服务(如 AWS Secrets Manager、HashiCorp Vault) - -### 4. ServiceAccount 和 RBAC - -为 Rabbit 服务创建了专用的 ServiceAccount 和 RBAC 权限,用于 Kubernetes 服务发现和注册。 - -**权限说明:** -- 可以获取、列出、监听 endpoints 和 services 资源 -- 用于 Kubernetes 注册中心的服务发现 - -### 5. Deployment - -定义了 Rabbit 服务的部署配置。 - -**关键配置:** -- `replicas`: 副本数(默认 2) -- `image`: 容器镜像(默认 `ghcr.io/aide-family/rabbit:v0.0.1`) -- `resources`: 资源限制和请求 -- `livenessProbe` 和 `readinessProbe`: 健康检查 - -**自定义启动命令:** - -如果需要自定义启动命令,可以修改 Deployment 的 `command` 字段: - -```yaml -containers: -- name: rabbit - command: ["/usr/local/bin/rabbit"] - args: ["run", "--use-database", "true", "--main-host", "mysql-service"] -``` - -### 6. Service - -定义了 ClusterIP 类型的 Service,用于集群内部访问。 - -**端口:** -- `8080`: HTTP API -- `9090`: gRPC API - -### 7. Ingress - -定义了外部访问入口(可选)。 - -**配置说明:** -- 默认使用 Nginx Ingress Controller -- 需要根据实际环境修改 `host` 和 `ingress.class` -- 支持 SSL/TLS 配置(需要取消注释相关配置) - -## 环境配置 - -### 文件配置模式(默认) - -不需要数据库,使用配置文件: - -```yaml -# 在 deployment.yaml 中设置 -env: -- name: MOON_RABBIT_USE_DATABASE - value: "false" -- name: MOON_RABBIT_CONFIG_PATHS - value: "./datasource" -``` - -### 数据库模式 - -需要 MySQL 数据库: - -```yaml -# 在 deployment.yaml 中设置 -env: -- name: MOON_RABBIT_USE_DATABASE - value: "true" -- name: MOON_RABBIT_MAIN_HOST - value: "mysql-service" # MySQL Service 名称 -``` - -## 验证部署 - -```bash -# 查看 Pod 状态 -kubectl get pods -n rabbit - -# 查看 Service -kubectl get svc -n rabbit - -# 查看 Deployment -kubectl get deployment -n rabbit - -# 查看日志 -kubectl logs -f deployment/rabbit -n rabbit - -# 测试服务 -kubectl port-forward svc/rabbit-service 8080:8080 -n rabbit -curl http://localhost:8080/health -``` - -## 更新部署 - -```bash -# 更新镜像版本 -kubectl set image deployment/rabbit rabbit=ghcr.io/aide-family/rabbit:v0.0.2 -n rabbit - -# 更新配置 -kubectl apply -f configmap.yaml -kubectl rollout restart deployment/rabbit -n rabbit - -# 查看更新状态 -kubectl rollout status deployment/rabbit -n rabbit -``` - -## 扩缩容 - -```bash -# 扩容到 3 个副本 -kubectl scale deployment/rabbit --replicas=3 -n rabbit - -# 或者修改 deployment.yaml 中的 replicas 字段 -``` - -## 卸载 - -```bash -# 删除所有资源 -kubectl delete -k deploy/server/k8s/ - -# 或者逐个删除 -kubectl delete -f ingress.yaml -kubectl delete -f service.yaml -kubectl delete -f deployment.yaml -kubectl delete -f rbac.yaml -kubectl delete -f serviceaccount.yaml -kubectl delete -f configmap.yaml -kubectl delete secret rabbit-secrets -n rabbit -kubectl delete -f namespace.yaml -``` - -## 故障排查 - -### 查看 Pod 状态 - -```bash -kubectl describe pod -n rabbit -``` - -### 查看事件 - -```bash -kubectl get events -n rabbit --sort-by='.lastTimestamp' -``` - -### 查看日志 - -```bash -# 查看所有 Pod 日志 -kubectl logs -l app=rabbit -n rabbit - -# 查看特定 Pod 日志 -kubectl logs -n rabbit - -# 实时查看日志 -kubectl logs -f deployment/rabbit -n rabbit -``` - -### 常见问题 - -1. **Pod 无法启动** - - 检查 Secret 是否已创建 - - 检查 ConfigMap 是否正确 - - 查看 Pod 日志和事件 - -2. **健康检查失败** - - 确认 `/health` 端点可用 - - 检查端口配置是否正确 - -3. **无法连接数据库** - - 确认 MySQL Service 存在且可访问 - - 检查 Secret 中的数据库密码是否正确 - -## 生产环境建议 - -1. **安全性** - - 使用强密码和密钥 - - 启用 TLS/SSL - - 使用 NetworkPolicy 限制网络访问 - - 定期轮换 Secret - -2. **高可用** - - 设置多个副本(至少 2 个) - - 使用 PodDisruptionBudget - - 配置多可用区部署 - -3. **监控** - - 配置 Prometheus 监控 - - 设置告警规则 - - 启用日志聚合 - -4. **资源管理** - - 根据实际负载调整资源限制 - - 使用 HPA(Horizontal Pod Autoscaler)自动扩缩容 - - 配置资源配额 - diff --git a/deploy/server/k8s/configmap.yaml b/deploy/server/k8s/configmap.yaml deleted file mode 100644 index 50143b9..0000000 --- a/deploy/server/k8s/configmap.yaml +++ /dev/null @@ -1,79 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: rabbit-config - namespace: rabbit - labels: - app: rabbit -data: - server.yaml: | - environment: PROD - enableClientConfig: true - enableSwagger: true - enableMetrics: true - useDatabase: false - - server: - name: moon.rabbit - useRandomID: false - metadata: - tag: rabbit - repository: https://github.com/aide-family/rabbit - author: Aide Family - email: aidecloud@163.com - http: - address: "0.0.0.0:8080" - network: "tcp" - timeout: "10s" - grpc: - address: "0.0.0.0:9090" - network: "tcp" - timeout: "10s" - - jwt: - secret: "${MOON_RABBIT_JWT_SECRET:change-me-in-production}" - expire: "${MOON_RABBIT_JWT_EXPIRE:600s}" - issuer: "${MOON_RABBIT_JWT_ISSUER:rabbit}" - - main: - username: "${MOON_RABBIT_MAIN_USERNAME:root}" - password: "${MOON_RABBIT_MAIN_PASSWORD:}" - host: "${MOON_RABBIT_MAIN_HOST:mysql-service}" - port: ${MOON_RABBIT_MAIN_PORT:3306} - database: ${MOON_RABBIT_MAIN_DATABASE:rabbit} - debug: false - useSystemLogger: true - parameters: - charset: utf8mb4 - parseTime: "true" - loc: Asia/Shanghai - - eventBus: - workerCount: 1 - timeout: 10s - - registryType: KUBERNETES - - cluster: - name: ${MOON_RABBIT_CLUSTER_NAME:moon.rabbit} - endpoints: ${MOON_RABBIT_CLUSTER_ENDPOINTS:} - protocol: GRPC - timeout: 10s - - kubernetes: - namespace: "${MOON_RABBIT_KUBERNETES_NAMESPACE:rabbit}" - kubeConfig: "${MOON_RABBIT_KUBERNETES_KUBECONFIG:}" - - swaggerBasicAuth: - enabled: true - username: "${MOON_RABBIT_SWAGGER_BASIC_AUTH_USERNAME:moon.rabbit}" - password: "${MOON_RABBIT_SWAGGER_BASIC_AUTH_PASSWORD:rabbit.swagger}" - - metricsBasicAuth: - enabled: true - username: "${MOON_RABBIT_METRICS_BASIC_AUTH_USERNAME:moon.rabbit}" - password: "${MOON_RABBIT_METRICS_BASIC_AUTH_PASSWORD:rabbit.metrics}" - - configPaths: - - "${MOON_RABBIT_CONFIG_PATHS:./datasource}" - diff --git a/deploy/server/k8s/deploy.sh b/deploy/server/k8s/deploy.sh deleted file mode 100755 index 040b41c..0000000 --- a/deploy/server/k8s/deploy.sh +++ /dev/null @@ -1,102 +0,0 @@ -#!/bin/bash -# Rabbit Kubernetes 部署脚本 -# 使用方法: ./deploy.sh [环境] -# 环境: dev, test, prod (默认: dev) - -set -e - -ENVIRONMENT=${1:-dev} -NAMESPACE="rabbit" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -echo "==========================================" -echo "Rabbit Kubernetes 部署脚本" -echo "环境: ${ENVIRONMENT}" -echo "命名空间: ${NAMESPACE}" -echo "==========================================" - -# 检查 kubectl 是否可用 -if ! command -v kubectl &> /dev/null; then - echo "错误: kubectl 未安装或不在 PATH 中" - exit 1 -fi - -# 检查是否已连接到 Kubernetes 集群 -if ! kubectl cluster-info &> /dev/null; then - echo "错误: 无法连接到 Kubernetes 集群" - exit 1 -fi - -echo "" -echo "步骤 1/7: 创建 Namespace..." -kubectl apply -f "${SCRIPT_DIR}/namespace.yaml" - -echo "" -echo "步骤 2/7: 检查并创建 Secret..." -if ! kubectl get secret rabbit-secrets -n "${NAMESPACE}" &>/dev/null; then - echo "Secret 不存在,正在创建..." - echo "提示: 生产环境请使用强密码和密钥!" - read -sp "请输入 JWT Secret (默认: change-me-in-production): " JWT_SECRET - JWT_SECRET=${JWT_SECRET:-change-me-in-production} - - read -sp "请输入 MySQL 用户名 (默认: root): " MYSQL_USER - MYSQL_USER=${MYSQL_USER:-root} - - read -sp "请输入 MySQL 密码: " MYSQL_PASSWORD - MYSQL_PASSWORD=${MYSQL_PASSWORD:-your-mysql-password} - - kubectl create secret generic rabbit-secrets \ - --from-literal=jwt-secret="${JWT_SECRET}" \ - --from-literal=mysql-username="${MYSQL_USER}" \ - --from-literal=mysql-password="${MYSQL_PASSWORD}" \ - --namespace="${NAMESPACE}" - echo "" - echo "Secret 创建成功" -else - echo "Secret 已存在,跳过创建" -fi - -echo "" -echo "步骤 3/7: 创建 ConfigMap..." -kubectl apply -f "${SCRIPT_DIR}/configmap.yaml" - -echo "" -echo "步骤 4/7: 创建 ServiceAccount..." -kubectl apply -f "${SCRIPT_DIR}/serviceaccount.yaml" - -echo "" -echo "步骤 5/7: 创建 RBAC 配置..." -kubectl apply -f "${SCRIPT_DIR}/rbac.yaml" - -echo "" -echo "步骤 6/7: 创建 Deployment..." -kubectl apply -f "${SCRIPT_DIR}/deployment.yaml" - -echo "" -echo "步骤 7/7: 创建 Service..." -kubectl apply -f "${SCRIPT_DIR}/service.yaml" - -echo "" -read -p "是否创建 Ingress? (y/N): " CREATE_INGRESS -if [[ "${CREATE_INGRESS}" =~ ^[Yy]$ ]]; then - echo "创建 Ingress..." - kubectl apply -f "${SCRIPT_DIR}/ingress.yaml" -fi - -echo "" -echo "==========================================" -echo "部署完成!" -echo "==========================================" -echo "" -echo "查看部署状态:" -echo " kubectl get pods -n ${NAMESPACE}" -echo " kubectl get svc -n ${NAMESPACE}" -echo " kubectl get deployment -n ${NAMESPACE}" -echo "" -echo "查看日志:" -echo " kubectl logs -f deployment/rabbit -n ${NAMESPACE}" -echo "" -echo "端口转发(本地测试):" -echo " kubectl port-forward svc/rabbit-service 8080:8080 -n ${NAMESPACE}" -echo "" - diff --git a/deploy/server/k8s/deployment.yaml b/deploy/server/k8s/deployment.yaml deleted file mode 100644 index 73fd473..0000000 --- a/deploy/server/k8s/deployment.yaml +++ /dev/null @@ -1,129 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: rabbit - namespace: rabbit - labels: - app: rabbit -spec: - replicas: 2 - selector: - matchLabels: - app: rabbit - template: - metadata: - labels: - app: rabbit - spec: - serviceAccountName: rabbit-service-account - containers: - - name: rabbit - image: ghcr.io/aide-family/rabbit:v0.0.1 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - protocol: TCP - - name: grpc - containerPort: 9090 - protocol: TCP - env: - # 服务配置 - - name: MOON_RABBIT_HTTP_ADDRESS - value: "0.0.0.0:8080" - - name: MOON_RABBIT_GRPC_ADDRESS - value: "0.0.0.0:9090" - - name: MOON_RABBIT_ENVIRONMENT - value: "PROD" - - name: MOON_RABBIT_ENABLE_CLIENT_CONFIG - value: "true" - - name: MOON_RABBIT_ENABLE_SWAGGER - value: "true" - - name: MOON_RABBIT_ENABLE_METRICS - value: "true" - - name: MOON_RABBIT_USE_DATABASE - value: "false" - - # JWT 配置(从 Secret 读取) - - name: MOON_RABBIT_JWT_SECRET - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: jwt-secret - optional: true - - name: MOON_RABBIT_JWT_EXPIRE - value: "600s" - - name: MOON_RABBIT_JWT_ISSUER - value: "rabbit" - - # 数据库配置(从 Secret 读取) - - name: MOON_RABBIT_MAIN_USERNAME - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: mysql-username - optional: true - - name: MOON_RABBIT_MAIN_PASSWORD - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: mysql-password - optional: true - - name: MOON_RABBIT_MAIN_HOST - value: "mysql-service" - - name: MOON_RABBIT_MAIN_PORT - value: "3306" - - name: MOON_RABBIT_MAIN_DATABASE - value: "rabbit" - - # Kubernetes 注册配置 - - name: MOON_RABBIT_KUBERNETES_NAMESPACE - value: "rabbit" - - name: MOON_RABBIT_KUBERNETES_KUBECONFIG - value: "" - - # 配置文件路径 - - name: MOON_RABBIT_CONFIG_PATHS - value: "./datasource" - - volumeMounts: - - name: config - mountPath: /moon/config - readOnly: true - - name: datasource - mountPath: /moon/datasource - readOnly: true - - resources: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 - - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 3 - - volumes: - - name: config - configMap: - name: rabbit-config - - name: datasource - emptyDir: {} - diff --git a/deploy/server/k8s/ingress.yaml b/deploy/server/k8s/ingress.yaml deleted file mode 100644 index bd75bec..0000000 --- a/deploy/server/k8s/ingress.yaml +++ /dev/null @@ -1,44 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: rabbit-ingress - namespace: rabbit - labels: - app: rabbit - annotations: - # Ingress Controller 类型(根据实际环境调整) - kubernetes.io/ingress.class: nginx - # 或者使用新的注解(Kubernetes 1.18+) - # ingressClassName: nginx - - # Nginx Ingress 配置 - nginx.ingress.kubernetes.io/rewrite-target: / - nginx.ingress.kubernetes.io/ssl-redirect: "false" - nginx.ingress.kubernetes.io/proxy-body-size: "10m" - nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" - nginx.ingress.kubernetes.io/proxy-send-timeout: "60" - nginx.ingress.kubernetes.io/proxy-read-timeout: "60" - - # 如果需要启用 SSL,取消下面的注释并配置证书 - # cert-manager.io/cluster-issuer: "letsencrypt-prod" -spec: - # 如果使用 Kubernetes 1.18+,使用 ingressClassName - # ingressClassName: nginx - - rules: - - host: rabbit.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: rabbit-service - port: - number: 8080 - # 如果需要启用 SSL,取消下面的注释 - # tls: - # - hosts: - # - rabbit.example.com - # secretName: rabbit-tls - diff --git a/deploy/server/k8s/kustomization.yaml b/deploy/server/k8s/kustomization.yaml deleted file mode 100644 index 5cd20cb..0000000 --- a/deploy/server/k8s/kustomization.yaml +++ /dev/null @@ -1,41 +0,0 @@ -# Kustomize 配置文件 -# 用于统一管理和部署所有资源 -# 使用方式: kubectl apply -k deploy/server/k8s/ - -apiVersion: kustomize.config.k8s.io/v1beta1 -kind: Kustomization - -namespace: rabbit - -resources: - - namespace.yaml - - configmap.yaml - - serviceaccount.yaml - - rbac.yaml - - deployment.yaml - - service.yaml - - ingress.yaml - -# 如果需要使用 Secret,请先创建 Secret,然后取消下面的注释 -# resources: -# - secret.yaml - -# 可以在这里添加通用标签和注解 -commonLabels: - app: rabbit - managed-by: kustomize - -# 可以在这里添加通用注解 -# commonAnnotations: -# description: "Rabbit messaging service" - -# 镜像替换(用于不同环境) -# images: -# - name: ghcr.io/aide-family/rabbit -# newTag: v0.0.2 - -# 副本数替换(用于不同环境) -# replicas: -# - name: rabbit -# count: 3 - diff --git a/deploy/server/k8s/namespace.yaml b/deploy/server/k8s/namespace.yaml deleted file mode 100644 index 512e12d..0000000 --- a/deploy/server/k8s/namespace.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - name: rabbit - labels: - name: rabbit - app: rabbit - diff --git a/deploy/server/k8s/rabbit.yaml b/deploy/server/k8s/rabbit.yaml deleted file mode 100644 index 33fad50..0000000 --- a/deploy/server/k8s/rabbit.yaml +++ /dev/null @@ -1,278 +0,0 @@ -# ============================================ -# 注意:此文件已按资源类型拆分为多个文件 -# 建议使用拆分后的文件进行部署 -# -# 文件结构: -# - namespace.yaml -# - configmap.yaml -# - secret.yaml.example -# - serviceaccount.yaml -# - rbac.yaml -# - deployment.yaml -# - service.yaml -# - ingress.yaml -# - kustomization.yaml -# -# 快速部署: -# ./deploy.sh -# 或 -# kubectl apply -k . -# -# 详细说明请查看 README.md -# ============================================ ---- -apiVersion: v1 -kind: Namespace -metadata: - name: rabbit ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: rabbit-config - namespace: rabbit - labels: - app: rabbit -data: - server.yaml: | - environment: PROD - server: - name: moon.rabbit - useRandomID: false - metadata: - tag: rabbit - http: - address: "0.0.0.0:8080" - network: "tcp" - timeout: "10s" - grpc: - address: "0.0.0.0:9090" - network: "tcp" - timeout: "10s" - - jwt: - secret: "${MOON_RABBIT_JWT_SECRET:change-me-in-production}" - expire: "${MOON_RABBIT_JWT_EXPIRE:600s}" - issuer: "${MOON_RABBIT_JWT_ISSUER:rabbit}" - - main: - username: "${MOON_RABBIT_MAIN_USERNAME:root}" - password: "${MOON_RABBIT_MAIN_PASSWORD:}" - host: "${MOON_RABBIT_MAIN_HOST:mysql-service}" - port: ${MOON_RABBIT_MAIN_PORT:3306} - database: ${MOON_RABBIT_MAIN_DATABASE:rabbit} - debug: false - useSystemLogger: true - parameters: - charset: utf8mb4 - parseTime: "true" - loc: Asia/Shanghai - - eventBus: - workerCount: 1 - timeout: 10s - - registryType: KUBERNETES - - clusters: - - name: rabbit01 - endpoint: discovery:///moon.rabbit - protocol: GRPC - timeout: 10s - - kubernetes: - namespace: "${MOON_RABBIT_KUBERNETES_NAMESPACE:rabbit}" - kubeConfig: "${MOON_RABBIT_KUBERNETES_KUBECONFIG:}" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: rabbit - namespace: rabbit - labels: - app: rabbit -spec: - replicas: 2 - selector: - matchLabels: - app: rabbit - template: - metadata: - labels: - app: rabbit - spec: - serviceAccountName: rabbit-service-account - containers: - - name: rabbit - image: ghcr.io/aide-family/rabbit:v0.0.1 - imagePullPolicy: IfNotPresent - ports: - - name: http - containerPort: 8080 - protocol: TCP - - name: grpc - containerPort: 9090 - protocol: TCP - env: - - name: MOON_RABBIT_HTTP_ADDRESS - value: "0.0.0.0:8080" - - name: MOON_RABBIT_GRPC_ADDRESS - value: "0.0.0.0:9090" - - name: MOON_RABBIT_JWT_SECRET - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: jwt-secret - optional: true - - name: MOON_RABBIT_MAIN_USERNAME - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: mysql-username - optional: true - - name: MOON_RABBIT_MAIN_PASSWORD - valueFrom: - secretKeyRef: - name: rabbit-secrets - key: mysql-password - optional: true - - name: MOON_RABBIT_MAIN_HOST - value: "mysql-service" - - name: MOON_RABBIT_MAIN_PORT - value: "3306" - - name: MOON_RABBIT_MAIN_DATABASE - value: "rabbit" - - name: MOON_RABBIT_KUBERNETES_NAMESPACE - value: "rabbit" - - name: MOON_RABBIT_KUBERNETES_KUBECONFIG - value: "" - volumeMounts: - - name: config - mountPath: /moon/config - readOnly: true - resources: - requests: - memory: "256Mi" - cpu: "100m" - limits: - memory: "512Mi" - cpu: "500m" - livenessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 30 - periodSeconds: 10 - timeoutSeconds: 5 - failureThreshold: 3 - readinessProbe: - httpGet: - path: /health - port: 8080 - initialDelaySeconds: 10 - periodSeconds: 5 - timeoutSeconds: 3 - failureThreshold: 3 - volumes: - - name: config - configMap: - name: rabbit-config ---- -apiVersion: v1 -kind: Service -metadata: - name: rabbit-service - namespace: rabbit - labels: - app: rabbit -spec: - type: ClusterIP - ports: - - name: http - port: 8080 - targetPort: 8080 - protocol: TCP - - name: grpc - port: 9090 - targetPort: 9090 - protocol: TCP - selector: - app: rabbit ---- -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: rabbit-ingress - namespace: rabbit - labels: - app: rabbit - annotations: - kubernetes.io/ingress.class: nginx - nginx.ingress.kubernetes.io/rewrite-target: / - nginx.ingress.kubernetes.io/ssl-redirect: "false" - nginx.ingress.kubernetes.io/proxy-body-size: "10m" - nginx.ingress.kubernetes.io/proxy-connect-timeout: "60" - nginx.ingress.kubernetes.io/proxy-send-timeout: "60" - nginx.ingress.kubernetes.io/proxy-read-timeout: "60" -spec: - rules: - - host: rabbit.example.com - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: rabbit-service - port: - number: 8080 ---- -apiVersion: v1 -kind: Secret -metadata: - name: rabbit-secrets - namespace: rabbit - labels: - app: rabbit -type: Opaque -stringData: - jwt-secret: "change-me-in-production" - mysql-username: "root" - mysql-password: "your-mysql-password" ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: rabbit-service-account - namespace: rabbit - labels: - app: rabbit ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: rabbit-registry-role - namespace: rabbit - labels: - app: rabbit -rules: -- apiGroups: [""] - resources: ["endpoints", "services"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: rabbit-registry-rolebinding - namespace: rabbit - labels: - app: rabbit -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: rabbit-registry-role -subjects: -- kind: ServiceAccount - name: rabbit-service-account - namespace: rabbit - diff --git a/deploy/server/k8s/rbac.yaml b/deploy/server/k8s/rbac.yaml deleted file mode 100644 index bc4793a..0000000 --- a/deploy/server/k8s/rbac.yaml +++ /dev/null @@ -1,28 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: rabbit-registry-role - namespace: rabbit - labels: - app: rabbit -rules: -- apiGroups: [""] - resources: ["endpoints", "services"] - verbs: ["get", "list", "watch"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: rabbit-registry-rolebinding - namespace: rabbit - labels: - app: rabbit -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: rabbit-registry-role -subjects: -- kind: ServiceAccount - name: rabbit-service-account - namespace: rabbit - diff --git a/deploy/server/k8s/secret.yaml.example b/deploy/server/k8s/secret.yaml.example deleted file mode 100644 index a31af58..0000000 --- a/deploy/server/k8s/secret.yaml.example +++ /dev/null @@ -1,34 +0,0 @@ -# Secret 配置示例文件 -# 注意:此文件仅作为示例,实际部署时请使用以下方式之一创建 Secret: -# -# 方式一:使用 kubectl 命令创建 -# kubectl create secret generic rabbit-secrets \ -# --from-literal=jwt-secret='your-jwt-secret-key' \ -# --from-literal=mysql-username='root' \ -# --from-literal=mysql-password='your-mysql-password' \ -# --namespace=rabbit -# -# 方式二:使用 base64 编码后创建(推荐用于 CI/CD) -# echo -n 'your-jwt-secret-key' | base64 -# echo -n 'root' | base64 -# echo -n 'your-mysql-password' | base64 -# 然后使用下面的 YAML 格式创建 -# -# 方式三:使用 Sealed Secrets 或 External Secrets Operator(生产环境推荐) - -apiVersion: v1 -kind: Secret -metadata: - name: rabbit-secrets - namespace: rabbit - labels: - app: rabbit -type: Opaque -stringData: - # JWT 密钥 - 生产环境请使用强随机密钥 - jwt-secret: "change-me-in-production" - # MySQL 用户名 - mysql-username: "root" - # MySQL 密码 - 生产环境请使用强密码 - mysql-password: "your-mysql-password" - diff --git a/deploy/server/k8s/service.yaml b/deploy/server/k8s/service.yaml deleted file mode 100644 index 5296363..0000000 --- a/deploy/server/k8s/service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: rabbit-service - namespace: rabbit - labels: - app: rabbit -spec: - type: ClusterIP - ports: - - name: http - port: 8080 - targetPort: 8080 - protocol: TCP - - name: grpc - port: 9090 - targetPort: 9090 - protocol: TCP - selector: - app: rabbit - diff --git a/deploy/server/k8s/serviceaccount.yaml b/deploy/server/k8s/serviceaccount.yaml deleted file mode 100644 index 88b276a..0000000 --- a/deploy/server/k8s/serviceaccount.yaml +++ /dev/null @@ -1,8 +0,0 @@ -apiVersion: v1 -kind: ServiceAccount -metadata: - name: rabbit-service-account - namespace: rabbit - labels: - app: rabbit - diff --git a/deploy/server/k8s/undeploy.sh b/deploy/server/k8s/undeploy.sh deleted file mode 100755 index 03fbb39..0000000 --- a/deploy/server/k8s/undeploy.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/bin/bash -# Rabbit Kubernetes 卸载脚本 -# 使用方法: ./undeploy.sh - -set -e - -NAMESPACE="rabbit" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" - -echo "==========================================" -echo "Rabbit Kubernetes 卸载脚本" -echo "命名空间: ${NAMESPACE}" -echo "==========================================" - -# 确认操作 -read -p "确定要删除 Rabbit 服务吗?这将删除所有相关资源 (y/N): " CONFIRM -if [[ ! "${CONFIRM}" =~ ^[Yy]$ ]]; then - echo "操作已取消" - exit 0 -fi - -echo "" -echo "正在删除资源..." - -# 删除资源(按依赖顺序) -echo "删除 Ingress..." -kubectl delete -f "${SCRIPT_DIR}/ingress.yaml" --ignore-not-found=true - -echo "删除 Service..." -kubectl delete -f "${SCRIPT_DIR}/service.yaml" --ignore-not-found=true - -echo "删除 Deployment..." -kubectl delete -f "${SCRIPT_DIR}/deployment.yaml" --ignore-not-found=true - -echo "删除 RBAC..." -kubectl delete -f "${SCRIPT_DIR}/rbac.yaml" --ignore-not-found=true - -echo "删除 ServiceAccount..." -kubectl delete -f "${SCRIPT_DIR}/serviceaccount.yaml" --ignore-not-found=true - -echo "删除 ConfigMap..." -kubectl delete -f "${SCRIPT_DIR}/configmap.yaml" --ignore-not-found=true - -echo "删除 Secret..." -kubectl delete secret rabbit-secrets -n "${NAMESPACE}" --ignore-not-found=true - -read -p "是否删除 Namespace? (y/N): " DELETE_NS -if [[ "${DELETE_NS}" =~ ^[Yy]$ ]]; then - echo "删除 Namespace..." - kubectl delete -f "${SCRIPT_DIR}/namespace.yaml" --ignore-not-found=true -fi - -echo "" -echo "==========================================" -echo "卸载完成!" -echo "==========================================" - diff --git a/go.mod b/go.mod index 1ea16d4..078b957 100644 --- a/go.mod +++ b/go.mod @@ -26,9 +26,9 @@ require ( k8s.io/client-go v0.24.3 ) -// replace github.com/aide-family/magicbox => ../magicbox +replace github.com/aide-family/magicbox => ../magicbox -// replace github.com/go-kratos/kratos/v2 => ../kratos +replace github.com/go-kratos/kratos/v2 => ../kratos require ( buf.build/go/protovalidate v1.0.0 // indirect diff --git a/go.sum b/go.sum index 0c9520d..3164568 100644 --- a/go.sum +++ b/go.sum @@ -64,8 +64,6 @@ github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tN github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/aide-family/magicbox v0.0.4 h1:OREj1GVST4X3x3n/OkjgFFkNSUg16XDBSG6Qa61tyiY= -github.com/aide-family/magicbox v0.0.4/go.mod h1:PkFsi8ADP8Esbw8F2BX1fHq/7A8Ep6wRrsLWG77cCnA= github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= @@ -140,8 +138,6 @@ github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20251015020953-cdff2 github.com/go-kratos/kratos/contrib/registry/etcd/v2 v2.0.0-20251015020953-cdff24709025/go.mod h1:m6EZSMUBZDxtGxzIFaR5lrr8GRp/8llV3wI2ywYvEnk= github.com/go-kratos/kratos/contrib/registry/kubernetes/v2 v2.0.0-20251015020953-cdff24709025 h1:NB9w3VPlIrdxhvyP2kYr2M69oRVW1Wi0iXtRGjO0GeY= github.com/go-kratos/kratos/contrib/registry/kubernetes/v2 v2.0.0-20251015020953-cdff24709025/go.mod h1:JOseK7NaDUPM2LEOiBm4BfoWjmE14630qZNGCkR9zY8= -github.com/go-kratos/kratos/v2 v2.9.1 h1:EGif6/S/aK/RCR5clIbyhioTNyoSrii3FC118jG40Z0= -github.com/go-kratos/kratos/v2 v2.9.1/go.mod h1:a1MQLjMhIh7R0kcJS9SzJYR43BRI7EPzzN0J1Ksu2bA= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= diff --git a/internal/biz/biz.go b/internal/biz/biz.go index b36e9d6..f24dfe6 100644 --- a/internal/biz/biz.go +++ b/internal/biz/biz.go @@ -13,5 +13,5 @@ var ProviderSetBiz = wire.NewSet( NewWebhook, NewTemplate, NewMessage, - NewEventBus, + NewJob, ) diff --git a/internal/biz/email.go b/internal/biz/email.go index 3cedb01..0536e90 100644 --- a/internal/biz/email.go +++ b/internal/biz/email.go @@ -13,13 +13,13 @@ func NewEmail( emailConfigBiz *EmailConfig, templateBiz *Template, messageLogBiz *MessageLog, - eventBusBiz *EventBus, + jobBiz *Job, helper *klog.Helper, ) *Email { return &Email{ emailConfigBiz: emailConfigBiz, messageLogBiz: messageLogBiz, - eventBusBiz: eventBusBiz, + jobBiz: jobBiz, templateBiz: templateBiz, helper: klog.NewHelper(klog.With(helper.Logger(), "biz", "email")), } @@ -29,7 +29,7 @@ type Email struct { emailConfigBiz *EmailConfig templateBiz *Template messageLogBiz *MessageLog - eventBusBiz *EventBus + jobBiz *Job helper *klog.Helper } @@ -49,7 +49,7 @@ func (e *Email) AppendEmailMessage(ctx context.Context, req *bo.SendEmailBo) err return merr.ErrorInternal("create message log failed").WithCause(err) } - if err := e.eventBusBiz.appendMessage(ctx, messageLog.UID); err != nil { + if err := e.jobBiz.appendMessage(ctx, messageLog.UID); err != nil { e.helper.Errorw("msg", "append email message failed", "error", err, "uid", messageLog.UID) return merr.ErrorInternal("append email message failed").WithCause(err) } diff --git a/internal/biz/eventbus.go b/internal/biz/job.go similarity index 64% rename from internal/biz/eventbus.go rename to internal/biz/job.go index 416a1e9..0245c1a 100644 --- a/internal/biz/eventbus.go +++ b/internal/biz/job.go @@ -9,29 +9,29 @@ import ( "github.com/aide-family/rabbit/internal/biz/repository" ) -func NewEventBus( +func NewJob( messageRepo repository.Message, helper *klog.Helper, -) *EventBus { - return &EventBus{ +) *Job { + return &Job{ messageRepo: messageRepo, - helper: klog.NewHelper(klog.With(helper.Logger(), "biz", "eventBus")), + helper: klog.NewHelper(klog.With(helper.Logger(), "biz", "job")), } } -type EventBus struct { +type Job struct { helper *klog.Helper messageRepo repository.Message } -func (e *EventBus) appendMessage(ctx context.Context, messageUID snowflake.ID) error { +func (e *Job) appendMessage(ctx context.Context, messageUID snowflake.ID) error { return e.messageRepo.AppendMessage(ctx, messageUID) } -func (e *EventBus) Start(ctx context.Context) error { +func (e *Job) Start(ctx context.Context) error { return e.messageRepo.Start(ctx) } -func (e *EventBus) Stop(ctx context.Context) error { +func (e *Job) Stop(ctx context.Context) error { return e.messageRepo.Stop(ctx) } diff --git a/internal/biz/message_log.go b/internal/biz/message_log.go index f1c47a1..a3ae466 100644 --- a/internal/biz/message_log.go +++ b/internal/biz/message_log.go @@ -15,12 +15,12 @@ import ( func NewMessageLog( messageLogRepo repository.MessageLog, - eventBusBiz *EventBus, + jobBiz *Job, helper *klog.Helper, ) *MessageLog { return &MessageLog{ messageLogRepo: messageLogRepo, - eventBusBiz: eventBusBiz, + jobBiz: jobBiz, helper: klog.NewHelper(klog.With(helper.Logger(), "biz", "messageLog")), } } @@ -28,7 +28,7 @@ func NewMessageLog( type MessageLog struct { helper *klog.Helper messageLogRepo repository.MessageLog - eventBusBiz *EventBus + jobBiz *Job } func (m *MessageLog) ListMessageLog(ctx context.Context, req *bo.ListMessageLogBo) (*bo.PageResponseBo[*bo.MessageLogItemBo], error) { @@ -68,7 +68,7 @@ func (m *MessageLog) RetryMessage(ctx context.Context, uid snowflake.ID) error { if messageLog.Status.IsSent() || messageLog.Status.IsSending() || messageLog.Status.IsCancelled() { return nil } - if err := m.eventBusBiz.appendMessage(ctx, uid); err != nil { + if err := m.jobBiz.appendMessage(ctx, uid); err != nil { m.helper.Errorw("msg", "append message failed", "error", err, "uid", uid) return merr.ErrorInternal("append message failed") } diff --git a/internal/biz/repository/sender.go b/internal/biz/repository/sender.go index 9083b48..a508a8c 100644 --- a/internal/biz/repository/sender.go +++ b/internal/biz/repository/sender.go @@ -3,14 +3,14 @@ package repository import ( "context" - "github.com/aide-family/rabbit/internal/biz/do" + "github.com/aide-family/rabbit/internal/biz/bo" "github.com/aide-family/rabbit/internal/biz/vobj" ) // MessageSender 定义消息发送器接口 type MessageSender interface { // Send 发送消息 - Send(ctx context.Context, messageLog *do.MessageLog) error + Send(ctx context.Context, messageLog *bo.MessageLogItemBo) error // Type 返回发送器支持的消息类型 Type() vobj.MessageType } diff --git a/internal/biz/webhook.go b/internal/biz/webhook.go index 60fb033..dad835b 100644 --- a/internal/biz/webhook.go +++ b/internal/biz/webhook.go @@ -14,14 +14,14 @@ import ( func NewWebhook( webhookConfigBiz *WebhookConfig, messageLogBiz *MessageLog, - eventBusBiz *EventBus, + jobBiz *Job, templateBiz *Template, helper *klog.Helper, ) *Webhook { return &Webhook{ webhookConfigBiz: webhookConfigBiz, messageLogBiz: messageLogBiz, - eventBusBiz: eventBusBiz, + jobBiz: jobBiz, templateBiz: templateBiz, helper: klog.NewHelper(klog.With(helper.Logger(), "biz", "webhook")), } @@ -30,7 +30,7 @@ func NewWebhook( type Webhook struct { webhookConfigBiz *WebhookConfig messageLogBiz *MessageLog - eventBusBiz *EventBus + jobBiz *Job templateBiz *Template helper *klog.Helper } @@ -55,7 +55,7 @@ func (w *Webhook) AppendWebhookMessage(ctx context.Context, req *bo.SendWebhookB return merr.ErrorInternal("create message log failed").WithCause(err) } - if err := w.eventBusBiz.appendMessage(ctx, messageLog.UID); err != nil { + if err := w.jobBiz.appendMessage(ctx, messageLog.UID); err != nil { w.helper.Errorw("msg", "append webhook message failed", "error", err, "uid", messageLog.UID) return merr.ErrorInternal("append webhook message failed").WithCause(err) } diff --git a/internal/conf/conf.go b/internal/conf/conf.go index b678ff0..e9b1b62 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -78,7 +78,7 @@ func (w *noOpWatcher) Stop() error { } func Load(bc any, sources ...config.Source) error { - c := config.New(config.WithSource(sources...)) + c := config.New(config.WithSource(sources...), config.WithPrintLoadedDebugLog(false)) if err := c.Load(); err != nil { return merr.ErrorInternal("load config failed").WithCause(err) } diff --git a/internal/conf/conf.proto b/internal/conf/conf.proto index e662a1c..daac500 100644 --- a/internal/conf/conf.proto +++ b/internal/conf/conf.proto @@ -11,39 +11,26 @@ option go_package = "github.com/aide-family/rabbit/internal/conf;conf"; message Bootstrap { rabbit.enum.Environment environment = 1; Server server = 2; - // GlobalEmail globalEmail = 3; - JWT jwt = 4; - map biz = 5; - rabbit.config.MySQLConfig main = 6; - EventBusCore eventBusCore = 7; - rabbit.config.ClusterConfig cluster = 8; - rabbit.config.RegistryType registryType = 9; - rabbit.config.ETCDConfig etcd = 10; - rabbit.config.KubernetesConfig kubernetes = 11; - rabbit.config.BasicAuthConfig swaggerBasicAuth = 12; - rabbit.config.BasicAuthConfig metricsBasicAuth = 13; - string enableClientConfig = 14; - string enableSwagger = 15; - string enableMetrics = 16; - string useDatabase = 17; - string configPaths = 18; - string messageLogPath = 19; + JWT jwt = 3; + map biz = 4; + rabbit.config.MySQLConfig main = 5; + JobCore jobCore = 6; + rabbit.config.ClusterConfig cluster = 7; + rabbit.config.RegistryType registryType = 8; + rabbit.config.ETCDConfig etcd = 9; + rabbit.config.KubernetesConfig kubernetes = 10; + rabbit.config.BasicAuthConfig swaggerBasicAuth = 11; + rabbit.config.BasicAuthConfig metricsBasicAuth = 12; + string enableClientConfig = 13; + string enableSwagger = 14; + string enableMetrics = 15; + string useDatabase = 16; + string dataSourcePaths = 17; + string messageLogPath = 18; } message Server { - message HTTPServer { - string address = 1; - string network = 2; - google.protobuf.Duration timeout = 3; - } - - message GRPCServer { - string address = 1; - string network = 2; - google.protobuf.Duration timeout = 3; - } - - message EventBusServer { + message ServerConfig { string address = 1; string network = 2; google.protobuf.Duration timeout = 3; @@ -51,26 +38,19 @@ message Server { string name = 1; map metadata = 2; - HTTPServer http = 3; - GRPCServer grpc = 4; - EventBusServer eventBus = 5; + ServerConfig http = 3; + ServerConfig grpc = 4; + ServerConfig job = 5; string useRandomID = 6; } - -message GlobalEmail { - string host = 1; - int32 port = 2; - string username = 3; - string password = 4; -} - + message JWT { string secret = 1; google.protobuf.Duration expire = 2; string issuer = 3; } -message EventBusCore { +message JobCore { int32 workerTotal = 1; google.protobuf.Duration timeout = 2; uint32 bufferSize = 3; diff --git a/internal/data/data.go b/internal/data/data.go index fff2d23..78bacca 100644 --- a/internal/data/data.go +++ b/internal/data/data.go @@ -3,6 +3,7 @@ package data import ( "context" + "strings" "time" "github.com/aide-family/magicbox/plugin/cache" @@ -34,7 +35,7 @@ func New(c *conf.Bootstrap, helper *klog.Helper) (*Data, func(), error) { c: c, dbs: safety.NewSyncMap(make(map[string]*gorm.DB)), closes: make(map[string]func() error), - useDatabase: strutil.IsNotEmpty(c.GetUseDatabase()) && c.GetUseDatabase() == "true", + useDatabase: strutil.IsNotEmpty(c.GetUseDatabase()) && strings.EqualFold(c.GetUseDatabase(), "true"), reloadFuncs: safety.NewSyncMap(make(map[string]func())), } diff --git a/internal/data/file_config.go b/internal/data/file_config.go index b20b74d..28bb6de 100644 --- a/internal/data/file_config.go +++ b/internal/data/file_config.go @@ -1,6 +1,7 @@ package data import ( + "strings" sync "sync" "github.com/aide-family/magicbox/load" @@ -46,29 +47,29 @@ func (d *Data) LoadFileConfig(bc *conf.Bootstrap, helper *klog.Helper) error { } var err error fileConfigOnce.Do(func() { - if bc.GetUseDatabase() == "true" { + if strings.EqualFold(bc.GetUseDatabase(), "true") { helper.Debugw("msg", "database mode is enabled, skipping file config loading") return } - configPaths := bc.GetConfigPaths() - if len(configPaths) == 0 { - helper.Debugw("msg", "no configPaths specified, skipping file config loading") + dataSourcePaths := bc.GetDataSourcePaths() + if len(dataSourcePaths) == 0 { + helper.Debugw("msg", "no dataSourcePaths specified, skipping file config loading") return } // Collect all file paths - fileSources := make([]config.Source, 0, len(configPaths)) + fileSources := make([]config.Source, 0, len(dataSourcePaths)) fileSources = append(fileSources, env.NewSource()) - for _, configPath := range strutil.SplitSkipEmpty(configPaths, ",") { - fileSources = append(fileSources, file.NewSource(load.ExpandHomeDir(configPath))) + for _, dataSourcePath := range strutil.SplitSkipEmpty(dataSourcePaths, ",") { + fileSources = append(fileSources, file.NewSource(load.ExpandHomeDir(dataSourcePath))) } if len(fileSources) == 0 { - helper.Debugw("msg", "no config files found in configPaths") + helper.Debugw("msg", "no dataSource files found in dataSourcePaths") return } - c := config.New(config.WithSource(fileSources...)) + c := config.New(config.WithSource(fileSources...), config.WithPrintLoadedDebugLog(false)) if err = c.Load(); err != nil { helper.Errorw("msg", "load config failed", "error", err) return diff --git a/internal/data/impl/message.go b/internal/data/impl/message.go index 135b675..aee63d5 100644 --- a/internal/data/impl/message.go +++ b/internal/data/impl/message.go @@ -11,7 +11,7 @@ import ( "github.com/bwmarrin/snowflake" klog "github.com/go-kratos/kratos/v2/log" - "github.com/aide-family/rabbit/internal/biz/do" + "github.com/aide-family/rabbit/internal/biz/bo" "github.com/aide-family/rabbit/internal/biz/repository" "github.com/aide-family/rabbit/internal/biz/vobj" "github.com/aide-family/rabbit/internal/conf" @@ -30,7 +30,7 @@ func NewMessageRepository( messageLogRepo repository.MessageLog, helper *klog.Helper, ) repository.Message { - eventBusCoreConf := bc.GetEventBusCore() + jobCoreConf := bc.GetJobCore() clusterConfig := bc.GetCluster() clusterEndpoints := strutil.SplitSkipEmpty(clusterConfig.GetEndpoints(), ",") clusterProtocol := clusterConfig.GetProtocol() @@ -40,19 +40,18 @@ func NewMessageRepository( d: d, transactionRepo: transactionRepo, messageLogRepo: messageLogRepo, - helper: klog.NewHelper(klog.With(helper.Logger(), "component", "message_bus")), - messageChan: make(chan *messageTask, eventBusCoreConf.GetBufferSize()), + helper: klog.NewHelper(klog.With(helper.Logger(), "component", "message")), + messageChan: make(chan *messageTask, jobCoreConf.GetBufferSize()), senders: safety.NewSyncMap(make(map[vobj.MessageType]repository.MessageSender)), stopChan: make(chan struct{}), wg: sync.WaitGroup{}, - workerTotal: int(eventBusCoreConf.GetWorkerTotal()), - timeout: eventBusCoreConf.GetTimeout().AsDuration(), + workerTotal: int(jobCoreConf.GetWorkerTotal()), + timeout: jobCoreConf.GetTimeout().AsDuration(), clusters: make([]sender.Sender, 0, len(clusterEndpoints)), } // 注册发送器 - messageRepo.senders.Set(vobj.MessageTypeEmail, sender.NewEmailSender(helper)) - messageRepo.senders.Set(vobj.MessageTypeWebhook, sender.NewWebhookSender(helper)) + messageRepo.registerSenders(sender.NewEmailSender(helper), sender.NewWebhookSender(helper)) safety.Go(context.Background(), "message_repository_init_clusters", func(ctx context.Context) error { time.Sleep(3 * time.Second) @@ -161,7 +160,7 @@ func (m *messageRepositoryImpl) waitProcessMessage(ctx context.Context, messageU func (m *messageRepositoryImpl) SendMessage(ctx context.Context, messageUID snowflake.ID) error { // 在事务中使用 SELECT FOR UPDATE 获取分布式锁 - var newMessage *do.MessageLog + var newMessage *bo.MessageLogItemBo err := m.transactionRepo.Transaction(ctx, func(transactionCtx context.Context) error { // 使用 SELECT FOR UPDATE 获取行锁,确保同一时间只有一个节点能处理该消息 lockedMessage, err := m.messageLogRepo.GetMessageLogWithLock(transactionCtx, messageUID) @@ -187,7 +186,7 @@ func (m *messageRepositoryImpl) SendMessage(ctx context.Context, messageUID snow return nil } - newMessage = lockedMessage + newMessage = bo.NewMessageLogItemBo(lockedMessage) return nil }) if err != nil { @@ -204,7 +203,7 @@ func (m *messageRepositoryImpl) SendMessage(ctx context.Context, messageUID snow } // processMessage 处理消息 -func (m *messageRepositoryImpl) processMessage(ctx context.Context, message *do.MessageLog) error { +func (m *messageRepositoryImpl) processMessage(ctx context.Context, message *bo.MessageLogItemBo) error { if message.Status.IsSent() || message.Status.IsSending() { return nil } @@ -287,3 +286,9 @@ func (m *messageRepositoryImpl) initClusters(clusterEndpoints []string, clusterP } } } + +func (m *messageRepositoryImpl) registerSenders(senders ...repository.MessageSender) { + for _, sender := range senders { + m.senders.Set(sender.Type(), sender) + } +} diff --git a/internal/data/impl/sender/email.go b/internal/data/impl/sender/email.go index b61cb73..9da4ccb 100644 --- a/internal/data/impl/sender/email.go +++ b/internal/data/impl/sender/email.go @@ -13,7 +13,6 @@ import ( klog "github.com/go-kratos/kratos/v2/log" "github.com/aide-family/rabbit/internal/biz/bo" - "github.com/aide-family/rabbit/internal/biz/do" "github.com/aide-family/rabbit/internal/biz/repository" "github.com/aide-family/rabbit/internal/biz/vobj" "github.com/aide-family/rabbit/pkg/merr" @@ -40,7 +39,7 @@ func (e *emailSender) Type() vobj.MessageType { } // Send 发送邮件 -func (e *emailSender) Send(ctx context.Context, messageLog *do.MessageLog) error { +func (e *emailSender) Send(ctx context.Context, messageLog *bo.MessageLogItemBo) error { msg, err := e.buildEmailMessage([]byte(string(messageLog.Message))) if err != nil { return merr.ErrorInternal("convert to email message failed").WithCause(err) diff --git a/internal/data/impl/sender/webhook.go b/internal/data/impl/sender/webhook.go index b7bc10b..4fe0b0a 100644 --- a/internal/data/impl/sender/webhook.go +++ b/internal/data/impl/sender/webhook.go @@ -16,7 +16,6 @@ import ( klog "github.com/go-kratos/kratos/v2/log" "github.com/aide-family/rabbit/internal/biz/bo" - "github.com/aide-family/rabbit/internal/biz/do" "github.com/aide-family/rabbit/internal/biz/repository" "github.com/aide-family/rabbit/internal/biz/vobj" "github.com/aide-family/rabbit/pkg/merr" @@ -51,7 +50,7 @@ func (w *webhookSender) Type() vobj.MessageType { } // Send 发送Webhook请求 -func (w *webhookSender) Send(ctx context.Context, messageLog *do.MessageLog) error { +func (w *webhookSender) Send(ctx context.Context, messageLog *bo.MessageLogItemBo) error { webhookMessage, err := w.buildWebhookMessage([]byte(string(messageLog.Message))) if err != nil { return err diff --git a/internal/server/eventbus.go b/internal/server/eventbus.go deleted file mode 100644 index 1068195..0000000 --- a/internal/server/eventbus.go +++ /dev/null @@ -1,101 +0,0 @@ -package server - -import ( - "context" - "net/url" - "strings" - - klog "github.com/go-kratos/kratos/v2/log" - "github.com/go-kratos/kratos/v2/transport" - - "github.com/aide-family/rabbit/internal/conf" - "github.com/aide-family/rabbit/internal/service" -) - -var _ transport.Server = (*EventBus)(nil) - -type EventBusServer interface { - transport.Server - transport.Endpointer -} - -func NewEventBus(bc *conf.Bootstrap, namespaceService *service.NamespaceService, helper *klog.Helper) *EventBus { - return newEventBus(bc.GetServer().GetEventBus(), bc.GetJwt(), namespaceService, helper) -} - -func newEventBus(eventBusConf conf.ServerConfig, jwtConf conf.JWTConfig, namespaceService *service.NamespaceService, helper *klog.Helper) *EventBus { - var server EventBusServer - switch strings.ToLower(eventBusConf.GetNetwork()) { - case string(transport.KindHTTP): - httpConf := &conf.Server_HTTPServer{ - Address: eventBusConf.GetAddress(), - Timeout: eventBusConf.GetTimeout(), - } - server = newHTTPServer(httpConf, jwtConf, namespaceService, helper) - default: - grpcConf := &conf.Server_GRPCServer{ - Address: eventBusConf.GetAddress(), - Timeout: eventBusConf.GetTimeout(), - } - server = newGRPCServer(grpcConf, jwtConf, namespaceService, helper) - } - return &EventBus{ - helper: klog.NewHelper(klog.With(helper.Logger(), "server", "event_bus")), - server: server, - } -} - -type EventBus struct { - eventBusService *service.EventBusService - server EventBusServer - helper *klog.Helper -} - -func (e *EventBus) RegisterHandler(eventBusService *service.EventBusService) { - e.eventBusService = eventBusService -} - -// Start implements transport.Server. -func (e *EventBus) Start(ctx context.Context) error { - if err := e.eventBusService.Start(ctx); err != nil { - e.helper.Errorw("msg", "start event bus failed", "error", err) - return err - } - endpoint, err := e.server.Endpoint() - if err != nil { - e.helper.Errorw("msg", "get event bus endpoint failed", "error", err) - return err - } - e.helper.Infow("msg", "[EventBus] started", "address", endpoint) - if err := e.server.Start(ctx); err != nil { - e.helper.Errorw("msg", "start server failed", "error", err) - return err - } - return nil -} - -// Stop implements transport.Server. -func (e *EventBus) Stop(ctx context.Context) error { - if err := e.server.Stop(ctx); err != nil { - e.helper.Errorw("msg", "stop server failed", "error", err) - return err - } - if err := e.eventBusService.Stop(ctx); err != nil { - e.helper.Errorw("msg", "stop event bus failed", "error", err) - return err - } - e.helper.Infow("msg", "[EventBus] stopped") - return nil -} - -// Endpoint implements transport.Server. -func (e *EventBus) Endpoint() (*url.URL, error) { - endpoint, err := e.server.Endpoint() - if err != nil { - return nil, err - } - return &url.URL{ - Scheme: "moon", - Host: endpoint.Host, - }, nil -} diff --git a/internal/server/job.go b/internal/server/job.go new file mode 100644 index 0000000..a32dfb3 --- /dev/null +++ b/internal/server/job.go @@ -0,0 +1,96 @@ +package server + +import ( + "context" + "net/url" + "strings" + + klog "github.com/go-kratos/kratos/v2/log" + "github.com/go-kratos/kratos/v2/transport" + + "github.com/aide-family/rabbit/internal/conf" + "github.com/aide-family/rabbit/internal/service" +) + +var _ transport.Server = (*Job)(nil) + +type JobServer interface { + transport.Server + transport.Endpointer +} + +func NewJob(bc *conf.Bootstrap, namespaceService *service.NamespaceService, helper *klog.Helper) *Job { + return newJob(bc.GetServer().GetJob(), bc.GetJwt(), namespaceService, helper) +} + +func newJob(jobConf conf.ServerConfig, jwtConf conf.JWTConfig, namespaceService *service.NamespaceService, helper *klog.Helper) *Job { + var server JobServer + serverConf := &conf.Server_ServerConfig{ + Address: jobConf.GetAddress(), + Timeout: jobConf.GetTimeout(), + } + if strings.EqualFold(jobConf.GetNetwork(), string(transport.KindHTTP)) { + server = newHTTPServer(serverConf, jwtConf, namespaceService, helper) + } else { + server = newGRPCServer(serverConf, jwtConf, namespaceService, helper) + } + return &Job{ + helper: klog.NewHelper(klog.With(helper.Logger(), "server", "job")), + server: server, + } +} + +type Job struct { + jobService *service.JobService + server JobServer + helper *klog.Helper +} + +func (e *Job) RegisterHandler(jobService *service.JobService) { + e.jobService = jobService +} + +// Start implements transport.Server. +func (e *Job) Start(ctx context.Context) error { + if err := e.jobService.Start(ctx); err != nil { + e.helper.Errorw("msg", "start job failed", "error", err) + return err + } + endpoint, err := e.server.Endpoint() + if err != nil { + e.helper.Errorw("msg", "get job endpoint failed", "error", err) + return err + } + e.helper.Infow("msg", "[Job] started", "address", endpoint) + if err := e.server.Start(ctx); err != nil { + e.helper.Errorw("msg", "start server failed", "error", err) + return err + } + return nil +} + +// Stop implements transport.Server. +func (e *Job) Stop(ctx context.Context) error { + if err := e.server.Stop(ctx); err != nil { + e.helper.Errorw("msg", "stop server failed", "error", err) + return err + } + if err := e.jobService.Stop(ctx); err != nil { + e.helper.Errorw("msg", "stop job failed", "error", err) + return err + } + e.helper.Infow("msg", "[Job] stopped") + return nil +} + +// Endpoint implements transport.Server. +func (e *Job) Endpoint() (*url.URL, error) { + endpoint, err := e.server.Endpoint() + if err != nil { + return nil, err + } + return &url.URL{ + Scheme: "moon", + Host: endpoint.Host, + }, nil +} diff --git a/internal/server/server.go b/internal/server/server.go index 879d5d5..20ae133 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -4,6 +4,7 @@ package server import ( "embed" nethttp "net/http" + "strings" "buf.build/go/protoyaml" "github.com/go-kratos/kratos/v2/encoding" @@ -70,7 +71,12 @@ func (c *protoYAMLCodec) Name() string { return "yaml" } -var ProviderSetServer = wire.NewSet(NewHTTPServer, NewGRPCServer, RegisterService, NewEventBus) +var ( + ProviderSetServerAll = wire.NewSet(NewHTTPServer, NewGRPCServer, RegisterService, NewJob) + ProviderSetServerHTTP = wire.NewSet(NewHTTPServer, RegisterHTTPService) + ProviderSetServerGRPC = wire.NewSet(NewGRPCServer, RegisterGRPCService) + ProviderSetServerJob = wire.NewSet(NewJob, RegisterJobService) +) // init initializes the json.MarshalOptions. func init() { @@ -84,58 +90,52 @@ func init() { type Servers []transport.Server -func (s Servers) BindSwagger(bc *conf.Bootstrap, helper *klog.Helper) { - if bc.GetEnableSwagger() != "true" { - helper.Debugw("msg", "swagger is not enabled", "enableSwagger", bc.GetEnableSwagger()) - return - } - - httSrv, ok := s[0].(*http.Server) - if !ok { +func BindSwagger(httpSrv *http.Server, bc *conf.Bootstrap, helper *klog.Helper) { + if strings.EqualFold(bc.GetEnableSwagger(), "true") { + helper.Debug("swagger is not enabled") return } - endpoint, err := httSrv.Endpoint() + endpoint, err := httpSrv.Endpoint() if err != nil { + helper.Errorw("msg", "get http server endpoint failed", "error", err) return } // Create file server handler authHandler := nethttp.StripPrefix("/doc/", nethttp.FileServer(nethttp.FS(docFS))) basicAuth := bc.GetSwaggerBasicAuth() - if basicAuth.GetEnabled() == "true" { + if strings.EqualFold(basicAuth.GetEnabled(), "true") { authHandler = middler.BasicAuthMiddleware(basicAuth.GetUsername(), basicAuth.GetPassword())(authHandler) helper.Debugf("[Swagger] endpoint: %s/doc/swagger (Basic Auth: %s:%s)", endpoint, basicAuth.GetUsername(), basicAuth.GetPassword()) } else { helper.Debugf("[Swagger] endpoint: %s/doc/swagger (No Basic Auth)", endpoint) } - httSrv.HandlePrefix("/doc/", authHandler) + httpSrv.HandlePrefix("/doc/", authHandler) } -func (s Servers) BindMetrics(bc *conf.Bootstrap, helper *klog.Helper) { - if bc.GetEnableMetrics() != "true" { - helper.Debugw("msg", "metrics is not enabled", "enableMetrics", bc.GetEnableMetrics()) - return - } - httSrv, ok := s[0].(*http.Server) - if !ok { +func BindMetrics(httpSrv *http.Server, bc *conf.Bootstrap, helper *klog.Helper) { + if strings.EqualFold(bc.GetEnableMetrics(), "true") { + helper.Debug("metrics is not enabled") return } - endpoint, err := httSrv.Endpoint() + endpoint, err := httpSrv.Endpoint() if err != nil { + helper.Errorw("msg", "get http server endpoint failed", "error", err) return } + basicAuth := bc.GetMetricsBasicAuth() authHandler := promhttp.Handler() - if basicAuth.GetEnabled() == "true" { + if strings.EqualFold(basicAuth.GetEnabled(), "true") { authHandler = middler.BasicAuthMiddleware(basicAuth.GetUsername(), basicAuth.GetPassword())(authHandler) helper.Debugf("[Metrics] endpoint: %s/metrics (Basic Auth: %s:%s)", endpoint, basicAuth.GetUsername(), basicAuth.GetPassword()) } else { helper.Debugf("[Metrics] endpoint: %s/metrics (No Basic Auth)", endpoint) } - httSrv.Handle("/metrics", authHandler) + httpSrv.Handle("/metrics", authHandler) } // RegisterService registers the service. @@ -143,7 +143,7 @@ func RegisterService( c *conf.Bootstrap, httpSrv *http.Server, grpcSrv *grpc.Server, - eventBusSrv *EventBus, + jobSrv *Job, healthService *service.HealthService, emailService *service.EmailService, webhookService *service.WebhookService, @@ -151,16 +151,46 @@ func RegisterService( namespaceService *service.NamespaceService, messageLogService *service.MessageLogService, templateService *service.TemplateService, - eventBusService *service.EventBusService, + jobService *service.JobService, ) Servers { - apiv1.RegisterHealthServer(grpcSrv, healthService) - apiv1.RegisterEmailServer(grpcSrv, emailService) - apiv1.RegisterWebhookServer(grpcSrv, webhookService) - apiv1.RegisterSenderServer(grpcSrv, senderService) - apiv1.RegisterNamespaceServer(grpcSrv, namespaceService) - apiv1.RegisterMessageLogServer(grpcSrv, messageLogService) - apiv1.RegisterTemplateServer(grpcSrv, templateService) + var srvs Servers + + srvs = append(srvs, RegisterHTTPService(c, httpSrv, + healthService, + emailService, + webhookService, + senderService, + namespaceService, + messageLogService, + templateService, + )...) + srvs = append(srvs, RegisterGRPCService(c, grpcSrv, + healthService, + emailService, + webhookService, + senderService, + namespaceService, + messageLogService, + templateService, + )...) + srvs = append(srvs, RegisterJobService(c, jobSrv, + jobService, + )...) + return srvs +} +// RegisterHTTPService registers only HTTP service. +func RegisterHTTPService( + c *conf.Bootstrap, + httpSrv *http.Server, + healthService *service.HealthService, + emailService *service.EmailService, + webhookService *service.WebhookService, + senderService *service.SenderService, + namespaceService *service.NamespaceService, + messageLogService *service.MessageLogService, + templateService *service.TemplateService, +) Servers { apiv1.RegisterHealthHTTPServer(httpSrv, healthService) apiv1.RegisterEmailHTTPServer(httpSrv, emailService) apiv1.RegisterWebhookHTTPServer(httpSrv, webhookService) @@ -168,9 +198,39 @@ func RegisterService( apiv1.RegisterNamespaceHTTPServer(httpSrv, namespaceService) apiv1.RegisterMessageLogHTTPServer(httpSrv, messageLogService) apiv1.RegisterTemplateHTTPServer(httpSrv, templateService) + return Servers{httpSrv} +} - eventBusSrv.RegisterHandler(eventBusService) - return Servers{httpSrv, grpcSrv, eventBusSrv} +// RegisterGRPCService registers only gRPC service. +func RegisterGRPCService( + c *conf.Bootstrap, + grpcSrv *grpc.Server, + healthService *service.HealthService, + emailService *service.EmailService, + webhookService *service.WebhookService, + senderService *service.SenderService, + namespaceService *service.NamespaceService, + messageLogService *service.MessageLogService, + templateService *service.TemplateService, +) Servers { + apiv1.RegisterHealthServer(grpcSrv, healthService) + apiv1.RegisterEmailServer(grpcSrv, emailService) + apiv1.RegisterWebhookServer(grpcSrv, webhookService) + apiv1.RegisterSenderServer(grpcSrv, senderService) + apiv1.RegisterNamespaceServer(grpcSrv, namespaceService) + apiv1.RegisterMessageLogServer(grpcSrv, messageLogService) + apiv1.RegisterTemplateServer(grpcSrv, templateService) + return Servers{grpcSrv} +} + +// RegisterJobService registers only Job service. +func RegisterJobService( + c *conf.Bootstrap, + jobSrv *Job, + jobService *service.JobService, +) Servers { + jobSrv.RegisterHandler(jobService) + return Servers{jobSrv} } var namespaceAllowList = []string{ diff --git a/internal/service/eventbus.go b/internal/service/eventbus.go deleted file mode 100644 index 639fe33..0000000 --- a/internal/service/eventbus.go +++ /dev/null @@ -1,25 +0,0 @@ -package service - -import ( - "context" - - "github.com/aide-family/rabbit/internal/biz" -) - -func NewEventBusService(eventBusBiz *biz.EventBus) *EventBusService { - return &EventBusService{ - eventBusBiz: eventBusBiz, - } -} - -type EventBusService struct { - eventBusBiz *biz.EventBus -} - -func (s *EventBusService) Start(ctx context.Context) error { - return s.eventBusBiz.Start(ctx) -} - -func (s *EventBusService) Stop(ctx context.Context) error { - return s.eventBusBiz.Stop(ctx) -} diff --git a/internal/service/job.go b/internal/service/job.go new file mode 100644 index 0000000..45202f0 --- /dev/null +++ b/internal/service/job.go @@ -0,0 +1,25 @@ +package service + +import ( + "context" + + "github.com/aide-family/rabbit/internal/biz" +) + +func NewJobService(jobBiz *biz.Job) *JobService { + return &JobService{ + jobBiz: jobBiz, + } +} + +type JobService struct { + jobBiz *biz.Job +} + +func (s *JobService) Start(ctx context.Context) error { + return s.jobBiz.Start(ctx) +} + +func (s *JobService) Stop(ctx context.Context) error { + return s.jobBiz.Stop(ctx) +} diff --git a/internal/service/service.go b/internal/service/service.go index bece03b..3db80f1 100644 --- a/internal/service/service.go +++ b/internal/service/service.go @@ -12,5 +12,5 @@ var ProviderSetService = wire.NewSet( NewNamespaceService, NewMessageLogService, NewTemplateService, - NewEventBusService, + NewJobService, ) diff --git a/main.go b/main.go index 797c042..b9336d0 100644 --- a/main.go +++ b/main.go @@ -6,6 +6,7 @@ package main import ( _ "embed" + "os" "github.com/spf13/cobra" @@ -15,16 +16,25 @@ import ( "github.com/aide-family/rabbit/cmd/delete" "github.com/aide-family/rabbit/cmd/get" "github.com/aide-family/rabbit/cmd/run" + "github.com/aide-family/rabbit/cmd/run/all" + "github.com/aide-family/rabbit/cmd/run/grpc" + "github.com/aide-family/rabbit/cmd/run/http" + "github.com/aide-family/rabbit/cmd/run/job" "github.com/aide-family/rabbit/cmd/send" + "github.com/aide-family/rabbit/cmd/send/email" + "github.com/aide-family/rabbit/cmd/send/feishu" + "github.com/aide-family/rabbit/cmd/send/sms" "github.com/aide-family/rabbit/cmd/version" ) var ( - Version = "latest" - BuildTime = "now" - Author = "Aide Family" - Email = "" - Repo = "https://github.com/aide-family/rabbit" + Name = "moon.rabbit" + Version = "latest" + BuildTime = "now" + Author = "Aide Family" + Email = "aidecloud@163.com" + Repo = "https://github.com/aide-family/rabbit" + hostname, _ = os.Hostname() ) //go:embed description.txt @@ -35,6 +45,8 @@ var defaultServerConfig []byte func main() { cmd.SetGlobalFlags( + cmd.WithGlobalFlagsName(Name), + cmd.WithGlobalFlagsHostname(hostname), cmd.WithGlobalFlagsVersion(Version), cmd.WithGlobalFlagsBuildTime(BuildTime), cmd.WithGlobalFlagsAuthor(Author), @@ -42,16 +54,19 @@ func main() { cmd.WithGlobalFlagsREPO(Repo), cmd.WithGlobalFlagsDescription(Description), ) - rootCmd := cmd.NewCmd() + + sendCmd := send.NewCmd(sms.NewCmd(), feishu.NewCmd(), email.NewCmd()) + runCmd := run.NewCmd(defaultServerConfig) + runCmd.AddCommand(grpc.NewCmd(), http.NewCmd(), job.NewCmd(), all.NewCmd()) children := []*cobra.Command{ apply.NewCmd(), config.NewCmd(defaultServerConfig), delete.NewCmd(), get.NewCmd(), - run.NewCmd(defaultServerConfig), - send.NewCmd(), + sendCmd, + runCmd, version.NewCmd(), } - cmd.Execute(rootCmd, children...) + cmd.Execute(cmd.NewCmd(), children...) } diff --git a/pkg/connect/gorm.go b/pkg/connect/gorm.go index 937414e..e799252 100644 --- a/pkg/connect/gorm.go +++ b/pkg/connect/gorm.go @@ -3,6 +3,7 @@ package connect import ( "fmt" "net/url" + "strings" klog "github.com/go-kratos/kratos/v2/log" "gorm.io/driver/mysql" @@ -22,14 +23,14 @@ func NewGorm(mysqlConf *config.MySQLConfig, logger *klog.Helper) (*gorm.DB, erro gormConfig := &gorm.Config{ DisableForeignKeyConstraintWhenMigrating: true, } - if mysqlConf.UseSystemLogger == "true" { + if strings.EqualFold(mysqlConf.UseSystemLogger, "true") { gormConfig.Logger = gormlog.New(logger.Logger()) } db, err := gorm.Open(mysql.Open(dsn), gormConfig) if err != nil { return nil, fmt.Errorf("open mysql connection failed: %w, dsn: %s", err, dsn) } - if mysqlConf.Debug == "true" { + if strings.EqualFold(mysqlConf.Debug, "true") { db = db.Debug() }