在分布式系统开发中,配置中心必不可少。在中通几年时间里,为了配合大数据计算平台,统一管理数百个微小应用,设计了一套轻量级配置中心。星尘配置中心在其理念基础上改进,针对中小团队而全新设计!

源码:https://github.com/NewLifeX/Stardust

试用:http://star.newlifex.com

Nuget包:NewLife.StardustNewLife.Stardust.Extensions


快速预览

三个应用,配置数据如下:

基于标签的变量引用,是星尘配置中心的亮点,请求接口返回数据如下:

配置请求时加上作用域 scope=prod

不同作用域,返回了不同的配置值。prod域不存在,使用了_master的默认空域配置10.0.0.1。

功能特点

配置中心本质上就是一个名值字典,在开发实践中需要解决多环境、多团队协同等问题。

星尘配置中心主要功能特点如下:

  1. 使用简单,设置名值配置后,应用可通过接口获取配置,NewLife组件配置系统集成;
  2. 支持嵌套,${_teamName}即可从本应用或共享应用中获取_teamName进行替换,可以灵活构建各种配置项;
  1. 支持共享,Common应用共享给StarWeb,StarWeb可以直接返回Common的配置,或者作为替换变量,Common就是各团队统一使用的配置值,一次修改多个应用生效;
  2. 支持忽略,下划线开头的配置项仅作为变量,不会返回给客户端;
  1. 支持跨应用引用,StarWeb的conn_shop设置${_master@Db},指定引用Db应用的_master变量,而无需Db共享给StarWeb,只有DBA维护Db和Redis,而几乎所有应用都需要用到;
  2. 支持多作用域,Db有默认空域以及dev作用域的_master,以区分生产环境和开发环境的数据库配置,StarWeb应用使用时只管引用_master,dev作用域将穿透到所有被引用变量;


基本用法

使用 HttpConfigProvider 可以直接访问星尘配置中心,也可以访问阿波罗配置中心,具体参考

示例代码:

var provider = new HttpConfigProvider
{
Server = "http://star.newlifex.com:6600",
//Server = "http://localhost:6600",
AppId = "StarWeb"
};

var str = provider["test1"];
Assert.NotEmpty(str);

var keys = provider.Keys.ToArray();
Assert.NotNull(keys);

var model = provider.Load<Model2>();
Assert.NotNull(model);
Assert.NotEmpty(model.Test);
Assert.Equal(str, model.Test);
Assert.NotEmpty(model.Shop);
Assert.NotEmpty(model.Title);
Assert.Equal("NewLife开发团队", model.Title);

var model2 = new Model2();
provider.Bind(model2);
Assert.Equal(str, model2.Test);
Assert.NotEmpty(model.Shop);
Assert.Equal("NewLife开发团队", model.Title);

provider.LoadAll();

实例化HttpConfigProvider时,需要指定星尘服务端地址和应用AppId,还需要密钥Secret(如果服务端设置了应用密钥)。

得到provider以后,就可以直接访问配置数据了,例如 provider["test1"]。

也可以通过Load方法,把配置数据设置到一个对象的属性上去。

还可以通过Bind方法,把配置数据绑定到一个对象的属性上,这个办法还具备自动刷新功能,也就是说,服务器配置数据变化以后,HttpConfigProvider内部将会更新该对象的属性。这里的更新可能因为网络原因最多有一分钟的延迟。Bind的用法非常适用于配置对象。


集成用法

星尘配置中心的客户端集成在星尘组件包 NewLife.Stardust 中,直接从nuget引用。

实例化星尘工厂后,即可得到配置提供者对象:

var star = new StarFactory(null, "StarWeb", null);

services.AddSingleton(star);
services.AddSingleton(star.Tracer);
services.AddSingleton(star.Config);

// 数据库连接配置
DAL.GetConfig = star.Config.GetConfig;

这里的star.Config,就是上面“基本用法”中的IConfigProvider,实际类型HttpConfigProvider。借助星尘工厂,统一了应用的身份验证。同时,这里不需要自己配置星尘服务端地址,它将会自动读取配置文件appsettings.json的StarServer配置项,或者跟服务器节点本机的StarAgent沟通得到。

DAL是数据中间件的数据层,GetConfig直接建立跟星尘配置中心的绑定关系,此举让XCode能够跟星尘紧密结合在一起。


这里把star.Config注入到ioc中,业务代码使用的时候,就可以通过IConfigProvider得到实际配置对象。

消费使用例子如下,JobHost解析使用了IConfigProvider,SyncOrder等作业类内部也可以解析使用。

using System;
using System.Threading;
using System.Threading.Tasks;
using AntJob;
using AntJob.Providers;
using NewLife;
using NewLife.Configuration;
using NewLife.Log;
using NewLife.Model;
using OrderCenter.Api.JobServices;

namespace OrderCenter.Tasks.JobServices
{
public class JobHost : BackgroundService
{
private readonly IServiceProvider _provider;
private readonly IConfigProvider _config;
private readonly OrderStatService _stat;
private Scheduler _scheduler;

public JobHost(IServiceProvider provider, IConfigProvider config, OrderStatService service)
{
_provider = provider;
_config = config;
_stat = service;
}

protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
var set = AntSetting.Current;
var services = ObjectContainer.Current;

var server = _config["antServer"];
if (!server.IsNullOrEmpty())
{
set.Server = server;
set.Save();
}

// 实例化调度器
var sc = new Scheduler
{
Tracer = DefaultTracer.Instance,

// 使用分布式调度引擎替换默认的本地文件调度
Provider = new NetworkJobProvider
{
Server = set.Server,
AppID = set.AppID,
Secret = set.Secret,
Debug = false
}
};

// 订单冷热同步。热表订单同步到冷表
sc.AddHandler<SyncOrder>();
sc.AddHandler<SyncOrder2>();

// 启动调度引擎,调度器内部多线程处理
sc.Start();
_scheduler = sc;

return Task.CompletedTask;
}

public override Task StopAsync(CancellationToken cancellationToken)
{
_scheduler.TryDispose();
_scheduler = null;

return Task.CompletedTask;
}
}
}


.NET Core 高度集成用法

在Asp.NET Core应用中,可以整体引入星尘全家桶,除了配置中心,还有监控中心、注册中心(服务注册与发现)、日志中心等。

只需要在Startup的ConfigureServices中使用一句:services.AddStardust("StarWeb")

这里不需要自己配置星尘服务端地址和密钥,它将会自动读取配置文件appsettings.json的StarServer配置项,或者跟服务器节点本机的StarAgent沟通得到。

using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using NewLife.Cube;
using Stardust.Data;
using Stardust.Server.Services;
using XCode.DataAccessLayer;

namespace Stardust.Web
{
public class Startup
{
public Startup(IConfiguration configuration) => Configuration = configuration;

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
var star = services.AddStardust("StarWeb");

services.AddControllersWithViews();
services.AddCube();
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// 使用Cube前添加自己的管道
if (env.IsDevelopment())
app.UseDeveloperExceptionPage();
else
app.UseExceptionHandler("/CubeHome/Error");

app.UseCube(env);

app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=CubeHome}/{action=Index}/{id?}");
});

// 发布服务到星尘注册中心,需要指定服务名
app.RegisterService("StarWeb");
}
}
}


配置管理

应用引入NewLife.Stardust包接入星尘配置中心以后,配置中心将会自动创建应用(要求StarServer下的config/starserver.config的AutoRegister设置为true)。

应用配置

进入星尘StarWeb管理平台(例如http://star.newlifex.com/),配置中心子系统,应用配置页面,即可管理各个应用的配置数据。

每个应用的基本信息,包括:

  • 启用。是否禁止该应用使用配置中心;
  • 版本。该应用正在使用的配置版本号,客户端请求配置数据时,如果传入相同版本号,则认为配置没有改变,不返回任何配置数据,意为让客户端继续使用上一次的配置数据;
  • 下一版本。下一个未发布版本,该应用的配置数据有改变,但是还没有发布,此时下一版本号会比当前版本号要大,同时“发布”按钮显示出来;
  • 定时发布。设置定时发布下一个版本的时间,到期后自动发布下一版本;
  • 可被依赖。指定应用是否可以被其它应用依赖,例如项目组内部的工控配置,可以写到一起,设置为可被依赖,然后其它项目即可直接依赖该应用,从而得到该应用的全部配置数据,融入自有配置数据;
  • 依赖应用。当前应用依赖其它的应用;
  • 全局。全局应用意为该应用的配置数据融入所有其它应用的配置数据中,在其它应用拉取时一起返回;


管理配置

从应用配置找到应用,点击“配置”进入管理页面。

在这里可以添加配置数据,也可以修改维护配置。不用担心对线上系统造成影响,因为在发布修改之前,所做修改并不会被各个应用客户端拉取。

配置名称以下划线开头时,仅用于内嵌的变量引用,并不会返回给客户端。这是星尘配置中心的一个特色。


在星尘配置中心,某个应用之下,配置名称并不是唯一的,配置名称加上作用域Scope才是唯一。也就是说,同样一个配置中,不同的作用域可以有不同的值。作用域Scope常用于dev/test/prod/uat等环境区分。

如果某个配置数据作用域为空,表示该配置的默认值,当该配置名称没有任何作用域配置客户端请求时,使用空域配置数据。如果没有空域配置,客户端指定的配置域又找不到时,配置中心将会给客户端返回错误信息。

作用域不仅仅适用于某个应用,而是能够穿透到内嵌应用。因此,conn_shop配置值是${_master@Db};database=shop,引用了Db应用的_master,而Db里面配置了多个_master,分别对应不同的配置值。conn_shop可以在不同环境下使用不同的数据库连接字符串。


全局应用

每个公司可以指定一个应用作为全局应用,存放所有应用都需要使用的配置数据,例如CompanyName=新生命。各个应用就不再需要配置CompanyName,而应用端可以直接使用。

当然,系统并没有限制只能使用一个全局应用,设置多个全局应用也是可以的,只是可能带来管理上的麻烦。

演示系统中的Common就是全局应用,它内部的_teamName可被所有应用直接使用,而无需建立依赖关系。

需要注意的是,_teamName以下划线开头,表示内部配置,仅用于变量引用,不会作为配置数据返回给应用客户端。

同名配置优先级:应用内该Scope配置>应用内空域配置>依赖应用>全局应用

依赖应用

考虑到团队内部的公共应用需要,可以设置某个应用为团队内部的公共应用。团队内部各个应用可以直接引用该依赖引用。一个公司可能有多个开发团队,不同的团队有自己的公共应用。

演示系统中的Db就是可被依赖的应用,然后StarWeb应用依赖了该Db应用,多选。

因此StarWeb自动“获得”了Db的所有配置项,这里只有_master。

需要注意的是,_master以下划线开头,表示内部配置,仅用于变量引用,不会作为配置数据返回给应用客户端。

同名配置优先级:应用内该Scope配置>应用内空域配置>依赖应用>全局应用

变量引用

基于公共配置抽象的需要,星尘配置中心支持变量引用。允许一个配置数据引用其它配置内容。配置标签以美元符号和大括号组成,例如conn_shop的配置值${_master@Db};database=shop,表示引用Db应用的_master配置值,然后在后面加上一些自己的内容。

这里的例子跟依赖应用有点关系,如果建立了依赖引用,那么这里可以直接写${_master};database=shop。因此,依赖引用不是必须的,可以通过@Db的形式,强行引用任意其它应用的配置数据。但是强烈不建议这么做,因为这样子会让被引用者根本分不清楚自己被谁引用了,从而带来管理风险。

同名配置优先级:应用内该Scope配置>应用内空域配置>依赖应用>全局应用

版本发布

新增或者修改配置数据以后,配置数据版本号将会变成当前应用版本号加一,表示这是未发布新版本。

此时,在应用内部,或者外面列表页,点击“发布”,都将会把当前版本号改为下一版本号,通知客户端该应用的配置数据有改变。

作用域规则

配置客户端默认并不会向配置中心提交scope,而是由配置中心根据来源IP地址计算得到scope。

当IP满足某个匹配条件时,产生某个结果,scope等于指定值。


同步阿波罗配置

星尘配置中心支持从阿波罗同步指定应用同步配置数据。

阿波罗接入,并不需要密钥验证,所以……


总结

星尘配置中心客户端可以紧密结合星尘,通知紧密结合XCode数据中间件、魔方快速开发平台、蚂蚁调度系统、Redis消息队列等等NewLife系列产品组件。

同时,该客户端还可以对接阿波罗配置中心,使用方法不变。


星尘配置中心为了简化使用,提升用户体验,不再设置命名空间等概念。建议普通用户直接给各个应用设置配置数据即可,少数高级用户可以使用全局、依赖、引用等高级特性。