diff --git a/Dockerfile.dotnet b/Dockerfile.dotnet new file mode 100644 index 0000000..ef0e590 --- /dev/null +++ b/Dockerfile.dotnet @@ -0,0 +1,23 @@ +# Build stage +FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build + +WORKDIR /src + +# Copy client library +COPY kenaito-conf-dotnet-client/ ./kenaito-conf-dotnet-client/ + +# Copy demo project +COPY kenaito-conf-dotnet-client-demo/ ./kenaito-conf-dotnet-client-demo/ + +# Build +WORKDIR /src/kenaito-conf-dotnet-client-demo +RUN dotnet restore +RUN dotnet publish -c Release -o /app + +# Runtime stage +FROM mcr.microsoft.com/dotnet/aspnet:6.0 + +WORKDIR /app +COPY --from=build /app . + +ENTRYPOINT ["dotnet", "KenaitoConfDemo.dll"] diff --git a/Dockerfile.go b/Dockerfile.go new file mode 100644 index 0000000..5e28ed0 --- /dev/null +++ b/Dockerfile.go @@ -0,0 +1,25 @@ +# Build stage +FROM golang:1.25-alpine AS build + +# 设置国内 Go 代理 +ENV GOPROXY=https://goproxy.cn,direct + +WORKDIR /src + +# Copy client library +COPY kenaito-conf-go-client/ ./kenaito-conf-go-client/ + +# Copy demo project +COPY kenaito-conf-go-client-demo/ ./kenaito-conf-go-client-demo/ + +# Build +WORKDIR /src/kenaito-conf-go-client-demo +RUN go mod tidy && go build -o /app/demo . + +# Runtime stage +FROM alpine:3.20 + +WORKDIR /app +COPY --from=build /app/demo . + +ENTRYPOINT ["./demo"] diff --git a/Dockerfile.nodejs b/Dockerfile.nodejs new file mode 100644 index 0000000..f8c37fb --- /dev/null +++ b/Dockerfile.nodejs @@ -0,0 +1,27 @@ +# Build stage +FROM node:18-alpine AS build + +WORKDIR /src + +# Copy client library +COPY kenaito-conf-nodejs-client/ ./kenaito-conf-nodejs-client/ + +# Copy demo project +COPY kenaito-conf-nodejs-client-demo/ ./kenaito-conf-nodejs-client-demo/ + +# Install client locally, then demo dependencies +WORKDIR /src/kenaito-conf-nodejs-client +RUN npm link + +WORKDIR /src/kenaito-conf-nodejs-client-demo +RUN npm link kenaito-conf && npm install + +# Runtime stage +FROM node:18-alpine + +WORKDIR /app +COPY --from=build /src/kenaito-conf-nodejs-client-demo . + +EXPOSE 8083 + +CMD ["node", "app.js"] diff --git a/Dockerfile.python b/Dockerfile.python new file mode 100644 index 0000000..bd39b4b --- /dev/null +++ b/Dockerfile.python @@ -0,0 +1,29 @@ +# Build stage +FROM python:3.12-slim AS build + +WORKDIR /src + +# Copy client library +COPY kenaito-conf-python-client/ ./kenaito-conf-python-client/ + +# Copy demo project +COPY kenaito-conf-python-client-demo/ ./kenaito-conf-python-client-demo/ + +# Install client library +WORKDIR /src/kenaito-conf-python-client +RUN pip install --no-cache-dir . + +# Install demo dependencies +WORKDIR /src/kenaito-conf-python-client-demo +RUN pip install --no-cache-dir -r requirements.txt + +# Runtime stage +FROM python:3.12-slim + +WORKDIR /app +COPY --from=build /usr/local/lib/python3.12/site-packages /usr/local/lib/python3.12/site-packages +COPY --from=build /src/kenaito-conf-python-client-demo . + +EXPOSE 8082 + +CMD ["python", "app.py"] diff --git a/Dockerfile.springboot2 b/Dockerfile.springboot2 new file mode 100644 index 0000000..993c493 --- /dev/null +++ b/Dockerfile.springboot2 @@ -0,0 +1,24 @@ +# Build stage +FROM maven:3.9-eclipse-temurin-8 AS build + +WORKDIR /src + +# Copy client library and install to local Maven repo +COPY kenaito-conf-springboot2-client/ ./kenaito-conf-springboot2-client/ +WORKDIR /src/kenaito-conf-springboot2-client +RUN mvn clean install -DskipTests + +# Copy demo project and build +COPY kenaito-conf-springboot2-client-demo/ ./kenaito-conf-springboot2-client-demo/ +WORKDIR /src/kenaito-conf-springboot2-client-demo +RUN mvn clean package -DskipTests + +# Runtime stage +FROM eclipse-temurin:8-jre + +WORKDIR /app +COPY --from=build /src/kenaito-conf-springboot2-client-demo/target/*.jar app.jar + +EXPOSE 8081 + +ENTRYPOINT ["java", "-jar", "app.jar"] diff --git a/README.md b/README.md index 78b217c..381140a 100644 --- a/README.md +++ b/README.md @@ -24,8 +24,8 @@ kenaito-conf/ ├── kenaito-conf-nodejs-client-demo/ # Node.js 客户端示例(端口 8083) ├── kenaito-conf-php-client/ # (未支持)PHP 客户端 SDK(PHP 8.0+) ├── kenaito-conf-php-client-demo/ # (未支持)PHP 客户端示例 -├── kenaito-conf-dotnet-client/ # (未支持).NET 客户端 SDK(.NET 6.0+) -├── kenaito-conf-dotnet-client-demo/ # (未支持).NET 客户端示例 +├── kenaito-conf-dotnet-client/ # .NET 客户端 SDK(.NET 6.0+) +├── kenaito-conf-dotnet-client-demo/ # .NET 客户端示例 ├── kenaito-conf-c-client/ # (未支持)C 客户端 SDK(libcurl + OpenSSL) ├── kenaito-conf-c-client-demo/ # (未支持)C 客户端示例 ├── kenaito-conf-cpp-client/ # (未支持)C++ 客户端 SDK(libcurl + OpenSSL) diff --git a/README_DOCKERFILE.md b/README_DOCKERFILE.md new file mode 100644 index 0000000..505142e --- /dev/null +++ b/README_DOCKERFILE.md @@ -0,0 +1,152 @@ +# Kenaito Config Center 客户端 Docker 部署指南 + +本文档说明如何使用 Dockerfile 构建和测试各语言客户端 demo。 + +## 前置条件 + +- Docker 已安装 +- 配置中心服务端已启动(默认 `http://localhost:8080`) +- 已在管理后台创建对应应用和环境 + +## 快速构建 + +在项目根目录执行: + +```bash +# Go 客户端 +docker build -f Dockerfile.go -t kenaito-conf-go-demo . + +# Node.js 客户端 +docker build -f Dockerfile.nodejs -t kenaito-conf-nodejs-demo . + +# Python 客户端 +docker build -f Dockerfile.python -t kenaito-conf-python-demo . + +# .NET 客户端 +docker build -f Dockerfile.dotnet -t kenaito-conf-dotnet-demo . + +# Spring Boot 2 客户端 +docker build -f Dockerfile.springboot2 -t kenaito-conf-springboot2-demo . +``` + +## 运行测试 + +### Go 客户端 + +```bash +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-go-demo +``` + +### Node.js 客户端 + +```bash +docker run -d --rm --name nodejs-demo \ + -p 8083:8083 \ + -e KENAITO_BASE_URL=http://host.docker.internal:8080 \ + -e KENAITO_APP_NAME=kenaito-springboot-demo \ + -e KENAITO_ENV=daily \ + kenaito-conf-nodejs-demo + +# 查看日志 +docker logs -f nodejs-demo + +# 测试接口 +curl http://localhost:8083/ +curl http://localhost:8083/health + +# 停止 +docker stop nodejs-demo +``` + +### Python 客户端 + +```bash +docker run -d --rm --name python-demo \ + -p 8082:8082 \ + -e KENAITO_BASE_URL=http://host.docker.internal:8080 \ + -e KENAITO_APP_NAME=kenaito-springboot-demo \ + -e KENAITO_ENV=daily \ + kenaito-conf-python-demo + +# 查看日志 +docker logs -f python-demo + +# 测试接口 +curl http://localhost:8082/ +curl http://localhost:8082/health + +# 停止 +docker stop python-demo +``` + +### .NET 客户端 + +```bash +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-dotnet-demo +``` + +### Spring Boot 2 客户端 + +```bash +docker run -d --rm --name springboot2-demo \ + -p 8081:8081 \ + -e KENAITO_BASE_URL=http://host.docker.internal:8080 \ + -e KENAITO_APP_NAME=kenaito-springboot-demo \ + -e KENAITO_ENV=daily \ + kenaito-conf-springboot2-demo + +# 查看日志 +docker logs -f springboot2-demo + +# 测试接口 +curl http://localhost:8081/ +curl http://localhost:8081/health + +# 停止 +docker stop springboot2-demo +``` + +## 环境变量说明 + +| 变量名 | 说明 | 默认值 | +|--------|------|--------| +| `KENAITO_BASE_URL` | 配置中心地址 | `http://localhost:8080` | +| `KENAITO_APP_NAME` | 应用名称 | `kenaito-springboot-demo` | +| `KENAITO_ENV` | 环境标识 | `daily` | +| `KENAITO_VERSION` | 配置版本 | `latest` | +| `KENAITO_TOKEN` | 应用 Token | 空 | +| `KENAITO_SYNC_INTERVAL` | 同步间隔(秒) | `10` | +| `KENAITO_CACHE_DIR` | 缓存目录 | `.` | + +## 注意事项 + +1. **host.docker.internal**:容器内访问宿主机服务时使用 `host.docker.internal:8080`,Linux 系统可能需要添加 `--add-host host.docker.internal:host-gateway` + +2. **网络模式**:如果配置中心也运行在 Docker 中,建议使用自定义网络: + ```bash + docker network create kenaito-net + docker run --network kenaito-net --name kenaito-server ... + docker run --network kenaito-net -e KENAITO_BASE_URL=http://kenaito-server:8080 ... + ``` + +3. **配置缓存**:各客户端会在运行目录生成 `config-cache.json` 文件,容器重启后可从缓存加载配置 + +4. **日志查看**: + - Go/.NET:直接输出到 stdout + - Node.js/Python/Spring Boot:使用各自日志框架 + +## 测试配置变更 + +1. 启动客户端容器 +2. 在管理后台修改对应应用的配置 +3. 等待同步间隔(默认 10 秒) +4. 观察客户端日志是否输出配置变更 +5. 访问 HTTP 接口验证配置是否更新(Node.js/Python/Spring Boot) diff --git a/kenaito-conf-dotnet-client-demo/KenaitoConfDemo.csproj b/kenaito-conf-dotnet-client-demo/KenaitoConfDemo.csproj new file mode 100644 index 0000000..f1a9f54 --- /dev/null +++ b/kenaito-conf-dotnet-client-demo/KenaitoConfDemo.csproj @@ -0,0 +1,14 @@ + + + + Exe + net6.0 + enable + enable + + + + + + + diff --git a/kenaito-conf-dotnet-client-demo/Program.cs b/kenaito-conf-dotnet-client-demo/Program.cs new file mode 100644 index 0000000..46e19ee --- /dev/null +++ b/kenaito-conf-dotnet-client-demo/Program.cs @@ -0,0 +1,52 @@ +using Kenaito.Conf.Client; + +var config = new Config +{ + BaseUrl = "http://localhost:8080", + AppName = "kenaito-springboot-demo", + Env = "daily", + Version = "latest", + SyncInterval = 10, + CacheDir = "." +}; + +var client = new Client(config); + +client.OnChange(changed => +{ + Console.WriteLine("Config changed:"); + foreach (var kv in changed) + { + Console.WriteLine($" {kv.Key} = {kv.Value}"); + } +}); + +await client.StartAsync(); + +Console.WriteLine("Current configs:"); +foreach (var kv in client.GetAll()) +{ + Console.WriteLine($" {kv.Key} = {kv.Value}"); +} + +var dbHost = client.Get("database.host"); +Console.WriteLine($"\ndatabase.host = {dbHost}"); + +Console.WriteLine("\nListening for config changes... (press Ctrl+C to stop)"); +var cts = new CancellationTokenSource(); +Console.CancelKeyPress += (s, e) => +{ + e.Cancel = true; + cts.Cancel(); +}; + +try +{ + await Task.Delay(Timeout.Infinite, cts.Token); +} +catch (TaskCanceledException) +{ +} + +Console.WriteLine("\nShutting down..."); +client.Stop(); diff --git a/kenaito-conf-dotnet-client-demo/README.md b/kenaito-conf-dotnet-client-demo/README.md new file mode 100644 index 0000000..ac7dd73 --- /dev/null +++ b/kenaito-conf-dotnet-client-demo/README.md @@ -0,0 +1,41 @@ +# Kenaito Config Center .NET Demo + +演示如何使用 kenaito-conf-dotnet-client 客户端库。 + +## 本地运行 + +```bash +cd kenaito-conf-dotnet-client-demo +dotnet run +``` + +## Docker 运行 + +```bash +# 在项目根目录执行 +docker build -f Dockerfile.dotnet -t kenaito-conf-dotnet-demo . + +docker run -it --rm \ + -e CONFIG_CENTER_BASE_URL=http://host.docker.internal:8080 \ + kenaito-conf-dotnet-demo +``` + +## 配置项 + +修改 `Program.cs` 中的配置: + +```csharp +var config = new Config +{ + BaseUrl = "http://localhost:8080", // 配置中心地址 + AppName = "kenaito-dotnet-demo", // 应用名称 + Env = "daily", // 环境 + SyncInterval = 10 // 同步间隔(秒) +}; +``` + +## 功能演示 + +- 启动时拉取全量配置并打印 +- 监听配置变更,实时输出变更内容 +- 按 Ctrl+C 优雅退出 diff --git a/kenaito-conf-dotnet-client/Cache.cs b/kenaito-conf-dotnet-client/Cache.cs new file mode 100644 index 0000000..ae2032b --- /dev/null +++ b/kenaito-conf-dotnet-client/Cache.cs @@ -0,0 +1,41 @@ +using Newtonsoft.Json; + +namespace Kenaito.Conf.Client; + +internal class Cache +{ + private readonly string _cacheFile; + + public Cache(string cacheDir) + { + _cacheFile = Path.Combine(cacheDir, "config-cache.json"); + } + + public Dictionary? Read() + { + try + { + if (!File.Exists(_cacheFile)) + return null; + var json = File.ReadAllText(_cacheFile); + return JsonConvert.DeserializeObject>(json); + } + catch + { + return null; + } + } + + public void Write(Dictionary data) + { + try + { + var json = JsonConvert.SerializeObject(data, Formatting.Indented); + File.WriteAllText(_cacheFile, json); + } + catch + { + // ignore cache write errors + } + } +} diff --git a/kenaito-conf-dotnet-client/Client.cs b/kenaito-conf-dotnet-client/Client.cs new file mode 100644 index 0000000..85f4447 --- /dev/null +++ b/kenaito-conf-dotnet-client/Client.cs @@ -0,0 +1,222 @@ +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Kenaito.Conf.Client; + +public class Client +{ + private readonly Config _config; + private readonly Cache _cache; + private readonly Listener _listener; + private readonly HttpClient _httpClient; + private readonly Dictionary _configs; + private readonly object _lock = new(); + private long _lastTime; + private bool _started; + private CancellationTokenSource? _cts; + + public Client(Config config) + { + _config = config; + if (string.IsNullOrEmpty(_config.Version)) + _config.Version = "latest"; + if (_config.SyncInterval <= 0) + _config.SyncInterval = 10; + if (string.IsNullOrEmpty(_config.CacheDir)) + _config.CacheDir = "."; + + _cache = new Cache(_config.CacheDir); + _listener = new Listener(); + _configs = new Dictionary(); + _httpClient = new HttpClient { Timeout = TimeSpan.FromSeconds(10) }; + } + + public void OnChange(ChangeHandler handler) + { + _listener.OnChange(handler); + } + + public string? Get(string key) + { + lock (_lock) + { + return _configs.TryGetValue(key, out var value) ? value : null; + } + } + + public Dictionary GetAll() + { + lock (_lock) + { + return new Dictionary(_configs); + } + } + + public async Task StartAsync() + { + if (_started) + return; + _started = true; + + try + { + await FetchAllAsync(); + } + catch (Exception ex) + { + Console.WriteLine($"[ConfigClient] fetch all configs error: {ex.Message}, trying cache"); + var cached = _cache.Read(); + if (cached != null) + { + ApplyConfigs(cached); + Console.WriteLine($"[ConfigClient] loaded {CountConfigs(cached)} configs from cache"); + } + else + { + throw new Exception("fetch configs failed and no cache", ex); + } + } + + _cts = new CancellationTokenSource(); + _ = SyncLoopAsync(_cts.Token); + } + + public void Stop() + { + _cts?.Cancel(); + } + + private async Task FetchAllAsync() + { + var url = $"{_config.BaseUrl.TrimEnd('/')}/api/fetchAllConfigs"; + var payload = new + { + appName = _config.AppName, + env = _config.Env, + version = _config.Version, + ignoreGlobal = _config.IgnoreGlobal, + token = _config.Token + }; + var content = new StringContent(JsonConvert.SerializeObject(payload), System.Text.Encoding.UTF8, "application/json"); + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + + var body = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject>(body); + if (result == null) + throw new Exception("empty response"); + + if (result.TryGetValue("code", out var codeObj)) + { + var code = Convert.ToInt64(codeObj); + if (code != 0) + { + var msg = result.TryGetValue("msg", out var msgObj) ? msgObj?.ToString() : "unknown error"; + throw new Exception($"server error: {msg}"); + } + } + + if (result.TryGetValue("latest.fetch.timestamp", out var tsObj)) + { + long.TryParse(tsObj?.ToString(), out _lastTime); + } + + _cache.Write(result); + ApplyConfigs(result); + + Console.WriteLine($"[ConfigClient] fetched {CountConfigs(result)} configs"); + } + + private async Task FetchChangedAsync() + { + var url = $"{_config.BaseUrl.TrimEnd('/')}/api/fetchChangedConfigs"; + var payload = new + { + appName = _config.AppName, + env = _config.Env, + version = _config.Version, + lastTime = _lastTime, + ignoreGlobal = _config.IgnoreGlobal + }; + var content = new StringContent(JsonConvert.SerializeObject(payload), System.Text.Encoding.UTF8, "application/json"); + var response = await _httpClient.PostAsync(url, content); + response.EnsureSuccessStatusCode(); + + var body = await response.Content.ReadAsStringAsync(); + var wrapper = JsonConvert.DeserializeObject(body); + if (wrapper == null) + return; + + if (wrapper.Code != 0) + throw new Exception($"server error code: {wrapper.Code}"); + + if (wrapper.Data == null) + return; + + if (wrapper.Data.TryGetValue("latest.fetch.timestamp", out var ts)) + { + long.TryParse(ts, out _lastTime); + wrapper.Data.Remove("latest.fetch.timestamp"); + } + + if (wrapper.Data.Count > 0) + { + lock (_lock) + { + foreach (var kv in wrapper.Data) + { + _configs[kv.Key] = kv.Value; + } + } + _listener.Notify(wrapper.Data); + Console.WriteLine($"[ConfigClient] {wrapper.Data.Count} configs changed"); + } + } + + private void ApplyConfigs(Dictionary data) + { + lock (_lock) + { + foreach (var kv in data) + { + if (kv.Key.StartsWith("kenaito.") || kv.Key == "latest.fetch.timestamp") + continue; + if (kv.Value is string strVal) + { + _configs[kv.Key] = strVal; + } + } + } + } + + private int CountConfigs(Dictionary data) + { + return data.Count(kv => !kv.Key.StartsWith("kenaito.") && kv.Key != "latest.fetch.timestamp" && kv.Value is string); + } + + private async Task SyncLoopAsync(CancellationToken ct) + { + while (!ct.IsCancellationRequested) + { + try + { + await Task.Delay(_config.SyncInterval * 1000, ct); + await FetchChangedAsync(); + } + catch (OperationCanceledException) + { + break; + } + catch (Exception ex) + { + Console.WriteLine($"[ConfigClient] sync error: {ex.Message}"); + } + } + } + + private class FetchChangedResponse + { + public int Code { get; set; } + public Dictionary? Data { get; set; } + } +} diff --git a/kenaito-conf-dotnet-client/Config.cs b/kenaito-conf-dotnet-client/Config.cs new file mode 100644 index 0000000..b0a8f31 --- /dev/null +++ b/kenaito-conf-dotnet-client/Config.cs @@ -0,0 +1,13 @@ +namespace Kenaito.Conf.Client; + +public class Config +{ + public string BaseUrl { get; set; } = ""; + public string AppName { get; set; } = ""; + public string Env { get; set; } = "dev"; + public string Version { get; set; } = "latest"; + public string? Token { get; set; } + public int SyncInterval { get; set; } = 10; + public string CacheDir { get; set; } = "."; + public bool IgnoreGlobal { get; set; } = false; +} diff --git a/kenaito-conf-dotnet-client/KenaitoConfClient.csproj b/kenaito-conf-dotnet-client/KenaitoConfClient.csproj new file mode 100644 index 0000000..584cbed --- /dev/null +++ b/kenaito-conf-dotnet-client/KenaitoConfClient.csproj @@ -0,0 +1,18 @@ + + + + net6.0 + enable + enable + Kenaito.Conf.Client + 1.0.0 + odboy + www.odboy.cn + Kenaito Config Center .NET Client + + + + + + + diff --git a/kenaito-conf-dotnet-client/Listener.cs b/kenaito-conf-dotnet-client/Listener.cs new file mode 100644 index 0000000..082273e --- /dev/null +++ b/kenaito-conf-dotnet-client/Listener.cs @@ -0,0 +1,28 @@ +namespace Kenaito.Conf.Client; + +public delegate void ChangeHandler(Dictionary changed); + +internal class Listener +{ + private readonly List _handlers = new(); + + public void OnChange(ChangeHandler handler) + { + _handlers.Add(handler); + } + + public void Notify(Dictionary changed) + { + foreach (var handler in _handlers) + { + try + { + handler(changed); + } + catch + { + // ignore handler errors + } + } + } +} diff --git a/kenaito-conf-dotnet-client/README.md b/kenaito-conf-dotnet-client/README.md new file mode 100644 index 0000000..7cac6f1 --- /dev/null +++ b/kenaito-conf-dotnet-client/README.md @@ -0,0 +1,75 @@ +# Kenaito Config Center .NET Client + +kenaito-conf 配置中心的 .NET 客户端 SDK。 + +## 安装 + +### 本地编译 + +```bash +cd kenaito-conf-dotnet-client +dotnet build +``` + +### 作为本地依赖 + +在 demo 项目的 csproj 中引用: + +```xml + + + +``` + +## 快速开始 + +```csharp +using Kenaito.Conf.Client; + +var config = new Config +{ + BaseUrl = "http://localhost:8080", + AppName = "kenaito-dotnet-demo", + Env = "daily", + SyncInterval = 10 +}; + +var client = new Client(config); + +client.OnChange(changed => +{ + Console.WriteLine("Config changed:"); + foreach (var kv in changed) + { + Console.WriteLine($" {kv.Key} = {kv.Value}"); + } +}); + +await client.StartAsync(); + +var dbHost = client.Get("database.host"); +Console.WriteLine($"database.host = {dbHost}"); + +Console.ReadLine(); +client.Stop(); +``` + +## 配置项 + +| 配置项 | 说明 | 默认值 | 必填 | +|--------|------|--------|------| +| BaseUrl | 配置中心地址 | - | 是 | +| AppName | 应用名称 | - | 是 | +| Env | 环境标识 | dev | 否 | +| Version | 配置版本 | latest | 否 | +| Token | 认证令牌 | null | 否 | +| SyncInterval | 同步间隔(秒) | 10 | 否 | +| CacheDir | 缓存目录 | . | 否 | +| IgnoreGlobal | 是否忽略全局配置 | false | 否 | + +## 功能特性 + +- 启动时拉取全量配置 +- 定时轮询增量配置变更 +- 本地文件缓存,服务不可用时从缓存加载 +- 配置变更回调通知 diff --git a/kenaito-conf-go-client-demo/go.mod b/kenaito-conf-go-client-demo/go.mod index 24afd60..d512ad4 100644 --- a/kenaito-conf-go-client-demo/go.mod +++ b/kenaito-conf-go-client-demo/go.mod @@ -2,6 +2,9 @@ module kenaito-conf-go-client-demo go 1.25.0 -require kenaito-conf/go-client v0.0.0 +require ( + kenaito-conf/go-client v0.0.0 + github.com/joho/godotenv v1.5.1 +) replace kenaito-conf/go-client => ../kenaito-conf-go-client diff --git a/kenaito-conf-go-client-demo/go.sum b/kenaito-conf-go-client-demo/go.sum new file mode 100644 index 0000000..ce90c15 --- /dev/null +++ b/kenaito-conf-go-client-demo/go.sum @@ -0,0 +1 @@ +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= diff --git a/kenaito-conf-go-client-demo/main.go b/kenaito-conf-go-client-demo/main.go index 2ecd341..45e9ab6 100644 --- a/kenaito-conf-go-client-demo/main.go +++ b/kenaito-conf-go-client-demo/main.go @@ -6,19 +6,40 @@ import ( "log" "os" "os/signal" + "strconv" "syscall" + + "github.com/joho/godotenv" ) +func getEnv(key, defaultValue string) string { + if value := os.Getenv(key); value != "" { + return value + } + return defaultValue +} + +func getEnvInt(key string, defaultValue int) int { + if value := os.Getenv(key); value != "" { + if i, err := strconv.Atoi(value); err == nil { + return i + } + } + return defaultValue +} + func main() { - // create config + _ = godotenv.Load() + + // create config from environment variables cfg := &client.Config{ - BaseURL: "http://localhost:8080", - AppName: "kenaito-springboot-demo", - Env: "daily", - Version: "latest", - //Token: "6f6966fe196572954b176c0e2a97bc399b050c6be31454b8d6b0f7f622b5d6f1", - SyncInterval: 10, // sync every 10 seconds - CacheDir: ".", + BaseURL: getEnv("KENAITO_BASE_URL", "http://localhost:8080"), + AppName: getEnv("KENAITO_APP_NAME", "kenaito-springboot-demo"), + Env: getEnv("KENAITO_ENV", "daily"), + Version: getEnv("KENAITO_VERSION", "latest"), + Token: getEnv("KENAITO_TOKEN", ""), + SyncInterval: getEnvInt("KENAITO_SYNC_INTERVAL", 10), + CacheDir: getEnv("KENAITO_CACHE_DIR", "."), } // create client