feat(ruby-client): 新增 Ruby 客户端 SDK 及 demo,移除 C 语言支持
- 新增 kenaito-conf-ruby-client(Ruby 2.5+)及对应 demo - 新增 Dockerfile.ruby - README 技术栈移除 C,项目结构补充 Ruby 客户端 - README_EN.md 同步更新 - README_DOCKERFILE.md 补充 Ruby 构建命令
This commit is contained in:
@@ -0,0 +1,11 @@
|
|||||||
|
FROM ruby:3.3-slim
|
||||||
|
|
||||||
|
WORKDIR /kenaito-conf-ruby-client-demo
|
||||||
|
|
||||||
|
# Copy client library
|
||||||
|
COPY kenaito-conf-ruby-client/ /kenaito-conf-ruby-client/
|
||||||
|
|
||||||
|
# Copy demo
|
||||||
|
COPY kenaito-conf-ruby-client-demo/app.rb /kenaito-conf-ruby-client-demo/app.rb
|
||||||
|
|
||||||
|
ENTRYPOINT ["ruby", "app.rb"]
|
||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
- **后端**: Go + Gin + xorm + MySQL
|
- **后端**: Go + Gin + xorm + MySQL
|
||||||
- **前端**: Vue 2 + Vite 5 + Element UI 2
|
- **前端**: Vue 2 + Vite 5 + Element UI 2
|
||||||
- **客户端**: Go / Java(Spring Boot 2.x)/ Python / Node.js / Perl / Lua / PHP / .NET / C / C++ / Rust / Ruby
|
- **客户端**: Go / Java(Spring Boot 2.x)/ Python / Node.js / Perl / Lua / PHP / .NET / C++ / Rust / Ruby
|
||||||
|
|
||||||
## 项目结构
|
## 项目结构
|
||||||
|
|
||||||
@@ -35,7 +35,9 @@ kenaito-conf/
|
|||||||
├── kenaito-conf-cpp-client/ # C++ 客户端 SDK(C++11+)
|
├── kenaito-conf-cpp-client/ # C++ 客户端 SDK(C++11+)
|
||||||
├── kenaito-conf-cpp-client-demo/ # C++ 客户端示例
|
├── kenaito-conf-cpp-client-demo/ # C++ 客户端示例
|
||||||
├── kenaito-conf-rust-client/ # Rust 客户端 SDK(Rust 2021+)
|
├── kenaito-conf-rust-client/ # Rust 客户端 SDK(Rust 2021+)
|
||||||
└── kenaito-conf-rust-client-demo/ # Rust 客户端示例
|
├── kenaito-conf-rust-client-demo/ # Rust 客户端示例
|
||||||
|
├── kenaito-conf-ruby-client/ # Ruby 客户端 SDK(Ruby 2.5+)
|
||||||
|
└── kenaito-conf-ruby-client-demo/ # Ruby 客户端示例
|
||||||
```
|
```
|
||||||
|
|
||||||
## 快速开始
|
## 快速开始
|
||||||
@@ -423,6 +425,59 @@ $client->syncOnce();
|
|||||||
$client->stop();
|
$client->stop();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ruby 客户端
|
||||||
|
|
||||||
|
适用于 Ruby 2.5+ 项目。
|
||||||
|
|
||||||
|
**1. 安装**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-ruby-client
|
||||||
|
gem build kenaito-conf.gemspec
|
||||||
|
gem install kenaito-conf-1.0.3.gem
|
||||||
|
```
|
||||||
|
|
||||||
|
或在项目 `Gemfile` 中引入:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'kenaito-conf', path: '/path/to/kenaito-conf-ruby-client'
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. 使用**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'kenaito_conf'
|
||||||
|
|
||||||
|
config = Kenaito::Conf::Config.new(
|
||||||
|
base_url: 'http://localhost:8080',
|
||||||
|
app_name: 'my-app',
|
||||||
|
env: 'daily',
|
||||||
|
sync_interval: 10,
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Kenaito::Conf::Client.new(config)
|
||||||
|
|
||||||
|
# 监听配置变更
|
||||||
|
client.on_change do |changed|
|
||||||
|
changed.each do |key, value|
|
||||||
|
puts "config changed: #{key} = #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 启动客户端(拉取配置)
|
||||||
|
client.start
|
||||||
|
|
||||||
|
# 获取配置
|
||||||
|
value = client.get('database.host')
|
||||||
|
all_configs = client.get_all
|
||||||
|
|
||||||
|
# 手动同步一次
|
||||||
|
client.sync_once
|
||||||
|
|
||||||
|
# 停止
|
||||||
|
client.stop_client
|
||||||
|
```
|
||||||
|
|
||||||
## API 概览
|
## API 概览
|
||||||
|
|
||||||
### 管理端 API(需 JWT 认证)
|
### 管理端 API(需 JWT 认证)
|
||||||
|
|||||||
@@ -197,6 +197,22 @@ docker run -it --rm \
|
|||||||
docker run -it --rm -e KENAITO_BASE_URL=http://host.docker.internal:8080 -e KENAITO_APP_NAME=kenaito-springboot-demo -e KENAITO_ENV=daily kenaito-conf-rust-demo
|
docker run -it --rm -e KENAITO_BASE_URL=http://host.docker.internal:8080 -e KENAITO_APP_NAME=kenaito-springboot-demo -e KENAITO_ENV=daily kenaito-conf-rust-demo
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Ruby 客户端
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker build -f Dockerfile.ruby -t kenaito-conf-ruby-demo .
|
||||||
|
|
||||||
|
# linux or mac
|
||||||
|
docker run -it --rm \
|
||||||
|
-e KENAITO_BASE_URL=http://host.docker.internal:8080 \
|
||||||
|
-e KENAITO_APP_NAME=kenaito-springboot-demo \
|
||||||
|
-e KENAITO_ENV=daily \
|
||||||
|
kenaito-conf-ruby-demo
|
||||||
|
|
||||||
|
# windows
|
||||||
|
docker run -it --rm -e KENAITO_BASE_URL=http://host.docker.internal:8080 -e KENAITO_APP_NAME=kenaito-springboot-demo -e KENAITO_ENV=daily kenaito-conf-ruby-demo
|
||||||
|
```
|
||||||
|
|
||||||
## 环境变量说明
|
## 环境变量说明
|
||||||
|
|
||||||
| 变量名 | 说明 | 默认值 |
|
| 变量名 | 说明 | 默认值 |
|
||||||
|
|||||||
+57
-2
@@ -6,7 +6,7 @@ A centralized configuration management platform rebuilt with Go, supporting mult
|
|||||||
|
|
||||||
- **Backend**: Go + Gin + xorm + MySQL
|
- **Backend**: Go + Gin + xorm + MySQL
|
||||||
- **Frontend**: Vue 2 + Vite 5 + Element UI 2
|
- **Frontend**: Vue 2 + Vite 5 + Element UI 2
|
||||||
- **Clients**: Go / Java (Spring Boot 2.x) / Python / Node.js / Perl / Lua / PHP / .NET / C / C++ / Rust / Ruby
|
- **Clients**: Go / Java (Spring Boot 2.x) / Python / Node.js / Perl / Lua / PHP / .NET / C++ / Rust / Ruby
|
||||||
|
|
||||||
## Project Structure
|
## Project Structure
|
||||||
|
|
||||||
@@ -33,7 +33,9 @@ kenaito-conf/
|
|||||||
├── kenaito-conf-cpp-client/ # C++ client SDK (C++11+)
|
├── kenaito-conf-cpp-client/ # C++ client SDK (C++11+)
|
||||||
├── kenaito-conf-cpp-client-demo/ # C++ client demo
|
├── kenaito-conf-cpp-client-demo/ # C++ client demo
|
||||||
├── kenaito-conf-rust-client/ # Rust client SDK (Rust 2021+)
|
├── kenaito-conf-rust-client/ # Rust client SDK (Rust 2021+)
|
||||||
└── kenaito-conf-rust-client-demo/ # Rust client demo
|
├── kenaito-conf-rust-client-demo/ # Rust client demo
|
||||||
|
├── kenaito-conf-ruby-client/ # Ruby client SDK (Ruby 2.5+)
|
||||||
|
└── kenaito-conf-ruby-client-demo/ # Ruby client demo
|
||||||
```
|
```
|
||||||
|
|
||||||
## Quick Start
|
## Quick Start
|
||||||
@@ -417,6 +419,59 @@ $client->syncOnce();
|
|||||||
$client->stop();
|
$client->stop();
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Ruby Client
|
||||||
|
|
||||||
|
For Ruby 2.5+ projects.
|
||||||
|
|
||||||
|
**1. Install**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-ruby-client
|
||||||
|
gem build kenaito-conf.gemspec
|
||||||
|
gem install kenaito-conf-1.0.3.gem
|
||||||
|
```
|
||||||
|
|
||||||
|
Or add to your project `Gemfile`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'kenaito-conf', path: '/path/to/kenaito-conf-ruby-client'
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Usage**
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'kenaito_conf'
|
||||||
|
|
||||||
|
config = Kenaito::Conf::Config.new(
|
||||||
|
base_url: 'http://localhost:8080',
|
||||||
|
app_name: 'my-app',
|
||||||
|
env: 'daily',
|
||||||
|
sync_interval: 10,
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Kenaito::Conf::Client.new(config)
|
||||||
|
|
||||||
|
# Listen for config changes
|
||||||
|
client.on_change do |changed|
|
||||||
|
changed.each do |key, value|
|
||||||
|
puts "config changed: #{key} = #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start client (fetch configs)
|
||||||
|
client.start
|
||||||
|
|
||||||
|
# Get config
|
||||||
|
value = client.get('database.host')
|
||||||
|
all_configs = client.get_all
|
||||||
|
|
||||||
|
# Manual sync once
|
||||||
|
client.sync_once
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
client.stop_client
|
||||||
|
```
|
||||||
|
|
||||||
## API Overview
|
## API Overview
|
||||||
|
|
||||||
### Admin API (JWT Authentication Required)
|
### Admin API (JWT Authentication Required)
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
require_relative '../kenaito-conf-ruby-client/lib/kenaito_conf'
|
||||||
|
|
||||||
|
def get_env_var(key, default)
|
||||||
|
value = ENV[key]
|
||||||
|
(value && !value.empty?) ? value : default
|
||||||
|
end
|
||||||
|
|
||||||
|
# 配置值(可被远程配置覆盖)
|
||||||
|
config_values = {
|
||||||
|
'database.host' => get_env_var('DATABASE_HOST', '192.168.1.1'),
|
||||||
|
'database.port' => get_env_var('DATABASE_PORT', '8888'),
|
||||||
|
'database.name' => get_env_var('DATABASE_NAME', 'xxxx'),
|
||||||
|
'database.user' => get_env_var('DATABASE_USER', 'xxxx'),
|
||||||
|
'database.password' => get_env_var('DATABASE_PASSWORD', 'xxxxxxxx')
|
||||||
|
}
|
||||||
|
|
||||||
|
config = Kenaito::Conf::Config.new(
|
||||||
|
base_url: get_env_var('KENAITO_BASE_URL', 'http://localhost:8080'),
|
||||||
|
app_name: get_env_var('KENAITO_APP_NAME', 'kenaito-ruby-demo'),
|
||||||
|
env: get_env_var('KENAITO_ENV', 'daily'),
|
||||||
|
version: get_env_var('KENAITO_VERSION', 'latest'),
|
||||||
|
token: get_env_var('KENAITO_TOKEN', ''),
|
||||||
|
sync_interval: get_env_var('KENAITO_SYNC_INTERVAL', '10').to_i,
|
||||||
|
cache_dir: get_env_var('KENAITO_CACHE_DIR', '.')
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Kenaito::Conf::Client.new(config)
|
||||||
|
|
||||||
|
# 监听配置变更
|
||||||
|
client.on_change do |changed|
|
||||||
|
puts "Config changed:"
|
||||||
|
changed.each do |key, value|
|
||||||
|
puts " #{key} = #{value}"
|
||||||
|
if config_values.key?(key)
|
||||||
|
config_values[key] = value
|
||||||
|
puts " [updated] #{key} = #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 启动客户端
|
||||||
|
begin
|
||||||
|
client.start
|
||||||
|
remote = client.get_all
|
||||||
|
config_values.each do |key, _|
|
||||||
|
if remote.key?(key)
|
||||||
|
config_values[key] = remote[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
puts "loaded #{remote.size} configs from remote\n\n"
|
||||||
|
rescue => e
|
||||||
|
puts "Failed to init config client, using local defaults: #{e.message}\n\n"
|
||||||
|
end
|
||||||
|
|
||||||
|
# 显示当前配置
|
||||||
|
puts "Current configs:"
|
||||||
|
config_values.keys.sort.each do |key|
|
||||||
|
puts " #{key} = #{config_values[key]}"
|
||||||
|
end
|
||||||
|
|
||||||
|
puts "\nListening for config changes... (press Ctrl+C to stop)\n"
|
||||||
|
|
||||||
|
# 信号处理
|
||||||
|
trap('INT') do
|
||||||
|
puts "\nShutting down..."
|
||||||
|
client.stop_client
|
||||||
|
exit(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
# 同步循环
|
||||||
|
until client.stop
|
||||||
|
client.sync_once
|
||||||
|
sleep(config.sync_interval)
|
||||||
|
end
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Kenaito Config Center Ruby Client
|
||||||
|
|
||||||
|
[English](README_EN.md) | 中文
|
||||||
|
|
||||||
|
Kenaito 配置中心 Ruby 客户端 SDK,适用于 Ruby 2.5+ 项目。
|
||||||
|
|
||||||
|
## 安装
|
||||||
|
|
||||||
|
### 方式一:本地安装
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-ruby-client
|
||||||
|
gem build kenaito-conf.gemspec
|
||||||
|
gem install kenaito-conf-1.0.3.gem
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式二:Bundler
|
||||||
|
|
||||||
|
在你的项目 `Gemfile` 中添加:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'kenaito-conf', path: '/path/to/kenaito-conf-ruby-client'
|
||||||
|
```
|
||||||
|
|
||||||
|
然后执行:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
### 方式三:直接引入
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require_relative '/path/to/kenaito-conf-ruby-client/lib/kenaito_conf'
|
||||||
|
```
|
||||||
|
|
||||||
|
## 使用示例
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'kenaito_conf'
|
||||||
|
|
||||||
|
config = Kenaito::Conf::Config.new(
|
||||||
|
base_url: 'http://localhost:8080',
|
||||||
|
app_name: 'my-app',
|
||||||
|
env: 'daily',
|
||||||
|
sync_interval: 10
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Kenaito::Conf::Client.new(config)
|
||||||
|
|
||||||
|
# 监听配置变更
|
||||||
|
client.on_change do |changed|
|
||||||
|
changed.each do |key, value|
|
||||||
|
puts "config changed: #{key} = #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# 启动客户端(拉取配置)
|
||||||
|
client.start
|
||||||
|
|
||||||
|
# 获取配置
|
||||||
|
value = client.get('database.host')
|
||||||
|
all_configs = client.get_all
|
||||||
|
|
||||||
|
# 手动同步一次
|
||||||
|
client.sync_once
|
||||||
|
|
||||||
|
# 停止
|
||||||
|
client.stop_client
|
||||||
|
```
|
||||||
|
|
||||||
|
## 配置项
|
||||||
|
|
||||||
|
| 配置项 | 说明 | 默认值 |
|
||||||
|
|--------|------|--------|
|
||||||
|
| base_url | 配置中心后端地址 | http://localhost:8080 |
|
||||||
|
| app_name | 应用名称 | 空 |
|
||||||
|
| env | 环境(dev/daily/prod) | dev |
|
||||||
|
| version | 配置版本 | latest |
|
||||||
|
| token | 应用 Token | 空 |
|
||||||
|
| sync_interval | 同步间隔(秒) | 10 |
|
||||||
|
| ignore_global | 是否忽略全局配置 | false |
|
||||||
|
| cache_dir | 缓存目录 | . |
|
||||||
|
|
||||||
|
## 功能特性
|
||||||
|
|
||||||
|
- ✅ 全量拉取配置
|
||||||
|
- ✅ 增量同步变更
|
||||||
|
- ✅ 本地文件缓存容灾
|
||||||
|
- ✅ 配置变更监听回调
|
||||||
|
- ✅ 删除配置感知(deletedKeys)
|
||||||
@@ -0,0 +1,91 @@
|
|||||||
|
# Kenaito Config Center Ruby Client
|
||||||
|
|
||||||
|
[English](README_EN.md) | 中文
|
||||||
|
|
||||||
|
Kenaito Config Center Ruby client SDK, compatible with Ruby 2.5+.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Option 1: Local Install
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd kenaito-conf-ruby-client
|
||||||
|
gem build kenaito-conf.gemspec
|
||||||
|
gem install kenaito-conf-1.0.3.gem
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 2: Bundler
|
||||||
|
|
||||||
|
Add to your project `Gemfile`:
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
gem 'kenaito-conf', path: '/path/to/kenaito-conf-ruby-client'
|
||||||
|
```
|
||||||
|
|
||||||
|
Then run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bundle install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option 3: Direct Require
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require_relative '/path/to/kenaito-conf-ruby-client/lib/kenaito_conf'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage Example
|
||||||
|
|
||||||
|
```ruby
|
||||||
|
require 'kenaito_conf'
|
||||||
|
|
||||||
|
config = Kenaito::Conf::Config.new(
|
||||||
|
base_url: 'http://localhost:8080',
|
||||||
|
app_name: 'my-app',
|
||||||
|
env: 'daily',
|
||||||
|
sync_interval: 10
|
||||||
|
)
|
||||||
|
|
||||||
|
client = Kenaito::Conf::Client.new(config)
|
||||||
|
|
||||||
|
# Listen for config changes
|
||||||
|
client.on_change do |changed|
|
||||||
|
changed.each do |key, value|
|
||||||
|
puts "config changed: #{key} = #{value}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Start client (fetch configs)
|
||||||
|
client.start
|
||||||
|
|
||||||
|
# Get config
|
||||||
|
value = client.get('database.host')
|
||||||
|
all_configs = client.get_all
|
||||||
|
|
||||||
|
# Manual sync once
|
||||||
|
client.sync_once
|
||||||
|
|
||||||
|
# Stop
|
||||||
|
client.stop_client
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
| Option | Description | Default |
|
||||||
|
|--------|-------------|---------|
|
||||||
|
| base_url | Config center backend URL | http://localhost:8080 |
|
||||||
|
| app_name | Application name | empty |
|
||||||
|
| env | Environment (dev/daily/prod) | dev |
|
||||||
|
| version | Config version | latest |
|
||||||
|
| token | Application token | empty |
|
||||||
|
| sync_interval | Sync interval (seconds) | 10 |
|
||||||
|
| ignore_global | Ignore global configs | false |
|
||||||
|
| cache_dir | Cache directory | . |
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
- ✅ Full config fetch
|
||||||
|
- ✅ Incremental sync
|
||||||
|
- ✅ Local file cache for disaster recovery
|
||||||
|
- ✅ Config change listener callback
|
||||||
|
- ✅ Deleted config awareness (deletedKeys)
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
Gem::Specification.new do |spec|
|
||||||
|
spec.name = 'kenaito-conf'
|
||||||
|
spec.version = '1.0.3'
|
||||||
|
spec.authors = ['odboy']
|
||||||
|
spec.email = ['odboy@outlook.com']
|
||||||
|
|
||||||
|
spec.summary = 'Kenaito Config Center Ruby Client'
|
||||||
|
spec.description = 'Ruby client SDK for Kenaito Config Center'
|
||||||
|
spec.homepage = 'https://github.com/odboy/kenaito-conf'
|
||||||
|
spec.license = 'Apache-2.0'
|
||||||
|
spec.required_ruby_version = '>= 2.5.0'
|
||||||
|
|
||||||
|
spec.files = Dir['lib/**/*.rb']
|
||||||
|
spec.require_paths = ['lib']
|
||||||
|
|
||||||
|
spec.add_development_dependency 'rake', '~> 13.0'
|
||||||
|
end
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
require_relative 'kenaito_conf/config'
|
||||||
|
require_relative 'kenaito_conf/cache'
|
||||||
|
require_relative 'kenaito_conf/listener'
|
||||||
|
require_relative 'kenaito_conf/client'
|
||||||
|
|
||||||
|
module Kenaito
|
||||||
|
module Conf
|
||||||
|
VERSION = '1.0.3'
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
require 'json'
|
||||||
|
require 'fileutils'
|
||||||
|
|
||||||
|
module Kenaito
|
||||||
|
module Conf
|
||||||
|
class Cache
|
||||||
|
def initialize(cache_dir)
|
||||||
|
@cache_file = File.join(cache_dir, 'config-cache.json')
|
||||||
|
end
|
||||||
|
|
||||||
|
def read
|
||||||
|
return nil unless File.exist?(@cache_file)
|
||||||
|
|
||||||
|
begin
|
||||||
|
content = File.read(@cache_file)
|
||||||
|
JSON.parse(content)
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "[ConfigClient] cache read error: #{e.message}"
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def write(data)
|
||||||
|
begin
|
||||||
|
FileUtils.mkdir_p(File.dirname(@cache_file))
|
||||||
|
File.write(@cache_file, JSON.pretty_generate(data))
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "[ConfigClient] cache write error: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
require 'net/http'
|
||||||
|
require 'uri'
|
||||||
|
require 'json'
|
||||||
|
|
||||||
|
module Kenaito
|
||||||
|
module Conf
|
||||||
|
class Client
|
||||||
|
attr_reader :configs
|
||||||
|
attr_accessor :stop
|
||||||
|
|
||||||
|
def initialize(config)
|
||||||
|
@config = config
|
||||||
|
@cache = Cache.new(config.cache_dir)
|
||||||
|
@listener = Listener.new
|
||||||
|
@configs = {}
|
||||||
|
@last_time = 0
|
||||||
|
@started = false
|
||||||
|
@stop = false
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_change(&block)
|
||||||
|
@listener.on_change(&block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get(key)
|
||||||
|
@configs[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_all
|
||||||
|
@configs
|
||||||
|
end
|
||||||
|
|
||||||
|
def start
|
||||||
|
return if @started
|
||||||
|
@started = true
|
||||||
|
|
||||||
|
begin
|
||||||
|
fetch_all
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "[ConfigClient] fetch all configs error: #{e.message}, trying cache"
|
||||||
|
cached = @cache.read
|
||||||
|
if cached
|
||||||
|
apply_configs(cached)
|
||||||
|
$stderr.puts "[ConfigClient] loaded #{@configs.size} configs from cache"
|
||||||
|
else
|
||||||
|
raise "fetch configs failed and no cache: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def stop_client
|
||||||
|
@stop = true
|
||||||
|
end
|
||||||
|
|
||||||
|
def sync_once
|
||||||
|
begin
|
||||||
|
fetch_changed
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "[ConfigClient] sync error: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def http_post(url, payload)
|
||||||
|
uri = URI.parse(url)
|
||||||
|
http = Net::HTTP.new(uri.host, uri.port)
|
||||||
|
http.use_ssl = (uri.scheme == 'https')
|
||||||
|
http.open_timeout = 10
|
||||||
|
http.read_timeout = 10
|
||||||
|
|
||||||
|
request = Net::HTTP::Post.new(uri.request_uri, 'Content-Type' => 'application/json')
|
||||||
|
request.body = payload.to_json
|
||||||
|
|
||||||
|
response = http.request(request)
|
||||||
|
|
||||||
|
if response.code.to_i != 200
|
||||||
|
raise "HTTP #{response.code}"
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
JSON.parse(response.body)
|
||||||
|
rescue => e
|
||||||
|
raise "JSON decode error: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_all
|
||||||
|
url = "#{@config.base_url}/api/fetchAllConfigs"
|
||||||
|
payload = {
|
||||||
|
appName: @config.app_name,
|
||||||
|
env: @config.env,
|
||||||
|
version: @config.version,
|
||||||
|
ignoreGlobal: @config.ignore_global,
|
||||||
|
token: @config.token
|
||||||
|
}
|
||||||
|
|
||||||
|
data = http_post(url, payload)
|
||||||
|
|
||||||
|
if data['code'] && data['code'] != 0
|
||||||
|
raise "server error: #{data['msg'] || 'unknown'}"
|
||||||
|
end
|
||||||
|
|
||||||
|
if data['latest.fetch.timestamp']
|
||||||
|
@last_time = data['latest.fetch.timestamp'].to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
@cache.write(data)
|
||||||
|
apply_configs(data)
|
||||||
|
|
||||||
|
$stderr.puts "[ConfigClient] fetched #{@configs.size} configs"
|
||||||
|
end
|
||||||
|
|
||||||
|
def fetch_changed
|
||||||
|
url = "#{@config.base_url}/api/fetchChangedConfigs"
|
||||||
|
payload = {
|
||||||
|
appName: @config.app_name,
|
||||||
|
env: @config.env,
|
||||||
|
version: @config.version,
|
||||||
|
lastTime: @last_time,
|
||||||
|
ignoreGlobal: @config.ignore_global,
|
||||||
|
token: @config.token
|
||||||
|
}
|
||||||
|
|
||||||
|
result = http_post(url, payload)
|
||||||
|
|
||||||
|
if result['code'] && result['code'] != 0
|
||||||
|
raise "server error code: #{result['code']}"
|
||||||
|
end
|
||||||
|
|
||||||
|
changed_data = result['data'] || {}
|
||||||
|
|
||||||
|
if changed_data['latest.fetch.timestamp']
|
||||||
|
@last_time = changed_data['latest.fetch.timestamp'].to_i
|
||||||
|
changed_data.delete('latest.fetch.timestamp')
|
||||||
|
end
|
||||||
|
|
||||||
|
# 处理删除的配置
|
||||||
|
if changed_data['deletedKeys'] && !changed_data['deletedKeys'].empty?
|
||||||
|
deleted_keys = changed_data['deletedKeys'].split(',')
|
||||||
|
deleted_keys.each { |key| @configs.delete(key) }
|
||||||
|
$stderr.puts "[ConfigClient] #{deleted_keys.size} configs deleted: #{deleted_keys.join(', ')}"
|
||||||
|
changed_data.delete('deletedKeys')
|
||||||
|
end
|
||||||
|
|
||||||
|
# 处理变更的配置
|
||||||
|
changed = {}
|
||||||
|
changed_data.each do |key, value|
|
||||||
|
if value.is_a?(String)
|
||||||
|
@configs[key] = value
|
||||||
|
changed[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
unless changed.empty?
|
||||||
|
@listener.notify(changed)
|
||||||
|
$stderr.puts "[ConfigClient] #{changed.size} configs changed: #{changed.keys.join(', ')}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def apply_configs(data)
|
||||||
|
data.each do |key, value|
|
||||||
|
next if key.start_with?('kenaito.') || key == 'latest.fetch.timestamp'
|
||||||
|
if value.is_a?(String)
|
||||||
|
@configs[key] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
module Kenaito
|
||||||
|
module Conf
|
||||||
|
class Config
|
||||||
|
attr_accessor :base_url, :app_name, :env, :version, :token, :sync_interval, :ignore_global, :cache_dir
|
||||||
|
|
||||||
|
def initialize(options = {})
|
||||||
|
@base_url = options[:base_url] || 'http://localhost:8080'
|
||||||
|
@app_name = options[:app_name] || ''
|
||||||
|
@env = options[:env] || 'dev'
|
||||||
|
@version = options[:version] || 'latest'
|
||||||
|
@token = options[:token] || ''
|
||||||
|
@sync_interval = options[:sync_interval] || 10
|
||||||
|
@ignore_global = options[:ignore_global] || false
|
||||||
|
@cache_dir = options[:cache_dir] || '.'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
module Kenaito
|
||||||
|
module Conf
|
||||||
|
class Listener
|
||||||
|
def initialize
|
||||||
|
@handlers = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def on_change(&block)
|
||||||
|
@handlers << block
|
||||||
|
end
|
||||||
|
|
||||||
|
def notify(changed)
|
||||||
|
@handlers.each do |handler|
|
||||||
|
begin
|
||||||
|
handler.call(changed)
|
||||||
|
rescue => e
|
||||||
|
$stderr.puts "[ConfigClient] change handler error: #{e.message}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user