跳到主要内容
版本:3.0.0

Redis 模板Id(Aegis.TemplateIdGenerator.Redis)

Aegis.TemplateIdGenerator.Redis 是模板Id生成的 Redis 分布式实现,适用于多实例共享模板和流水号的场景。模板由配套管理后台维护并自动同步到 Redis,业务代码继续使用 ITemplateIdGenerator,不需要感知运行后端的变化。

如何引入

安装 NuGet

只在 Web API、Worker 等最终宿主项目中安装:

dotnet add package Aegis.TemplateIdGenerator.Redis

当前包会传递引入 Aegis.TemplateIdGeneratorAegis.Caching.Redis,业务层不需要引用 Redis 扩展包。

启用基础组件

宿主项目的 Component.deps.json 只声明基础组件 TemplateIdGenerator

{
"Components": {
"Services": [
"TemplateIdGenerator"
],
"Middlewares": []
}
}

Redis 扩展不在 Component.deps.json 中声明。它由宿主在服务注册阶段通过 AddTemplateIdRedis<T>() 启用。

组件概览

字段说明
组件名称Redis 模板Id
真实类库Aegis.TemplateIdGenerator.Redis
组件定位模板Id的多实例 Redis 实现
引入方式最终宿主安装 NuGet,并调用 AddTemplateIdRedis<T>()
模板来源管理后台创建并自动同步到 Redis
流水号存储Redis
核心能力多实例发号、模板自动同步、跨实例确认与归还、超时回收
核心接口ITemplateIdGenerator

什么时候要用它

适合以下场景:

  • 同一业务服务以多个实例并发发号。
  • 服务重启后仍需要保留流水号状态。
  • 模板需要在管理后台统一维护,并自动同步到各个业务实例。
  • 可归还编号需要跨实例确认、释放和超时回收。

以下场景通常不需要 Redis 实现:

  • 只有一个进程,并且可以接受重启后重新计数。
  • 只需要临时编号,不要求跨实例共享状态。
  • 需要数据库事务内严格连续、无跳号的监管型序列。

最小可运行路径

第一步:定义 RedisSource

using Aegis.Caching.Redis;

namespace MyProject.Infrastructure;

public sealed class AppRedisSource : RedisSourceBase
{
public AppRedisSource(RedisOptions options) : base(options)
{
}
}

第二步:准备配置

{
"Redis": {
"RedisMode": "Standalone",
"ConnectionString": "127.0.0.1:6379,password=change-me,defaultDatabase=0",
"SentinelString": "",
"IsRWSplitting": false,
"ScriptPath": ""
}
}

第三步:在宿主层启用 Redis 扩展

在宿主的 ICustomStartup.ConfigureServices 中先注册具体 RedisSource,再启用模板Id Redis 扩展:

using Aegis.Caching.Redis;
using Aegis.Configuration;
using Aegis.TemplateIdGenerator.Redis;
using MyProject.Infrastructure;

public void ConfigureServices(IServiceCollection services)
{
services.AddRedisSource<AppRedisSource>(
ConfigManager.Get<RedisOptions>("Redis"));

services.AddTemplateIdRedis<AppRedisSource>();
}

AddTemplateIdRedis<AppRedisSource>() 直接使用已经注册的具体数据源,不需要额外把 AppRedisSource 映射为 RedisSourceBase。存在多个 RedisSource 时,泛型参数决定模板Id使用哪一个数据源。

第四步:在管理后台创建模板

进入管理后台的“编码生成”页面即可维护模板,不需要先创建系统。

创建模板

点击模板列表上方的“新增”,填写模板名称、code 和描述:

字段说明
模板名称用于在管理后台识别模板
code业务代码,客户端通过该值选择模板,区分大小写
描述可选的模板用途说明

配置模板规则

选中模板后点击规则区域的“新增”,为模板添加生成规则。一个模板可以包含多个规则,规则顺序决定各部分在最终模板Id中的拼接顺序。

管理后台支持固定值、日期、前缀、数字、字母等规则类型。选择规则类型后填写该类型对应的参数;需要组合编号时,继续点击“添加附加规则”。

数字规则同时启用“根据日期重置”和“允许归还”时,模板必须包含完整的年、月、日日期规则。推荐直接添加完整日期,或者使用同时包含 yyyyMMdd 的日期时间格式;不完整日期会导致模板校验失败。

保存后可以在规则列表中查看各项规则及其顺序:

模板及其规则创建成功后,管理后台会自动同步到 Redis,不需要再执行手动同步。客户端连接到与管理后台相同的 Redis 实例和逻辑数据库后,即可通过模板的 code 使用该模板。

上图以 TEST 为模板 codeOP 为前缀规则示例。

第五步:保持业务调用不变

using Aegis.TemplateIdGenerator;

public sealed class RegistrationNumberService(ITemplateIdGenerator generator)
{
public string Create()
{
return generator.Generate("TEST");
}

public string[] CreateBatch(int count)
{
return generator.GenerateIds("TEST", count);
}
}

生成、确认、归还和模板字段的完整说明见 模板Id生成

与默认实现的差异

维度默认实现Redis 实现
部署方式单进程多实例
流水号状态保存在当前进程多实例共享
重启后的流水号重新初始化保留原有状态
模板来源配置文件、代码或自定义来源管理后台自动同步到 Redis
模板变化需要重启或自行更新管理后台保存后自动同步
业务调用接口ITemplateIdGeneratorITemplateIdGenerator

切换到 Redis 实现只改变最终宿主的扩展包引用、服务注册和基础设施配置,业务服务的注入类型及调用方式保持不变。

生成、确认与归还

Redis 实现沿用与默认实现相同的业务 API:

var number = generator.Generate("TEST");
var batch = generator.GenerateIds("TEST", 10);

generator.ConfirmId("TEST", number);
generator.ConfirmIds("TEST", batch);

var released = generator.ReleaseId("TEST", number);

每次单号生成只使用一个生成时间,Redis 流水桶与最终编号中的日期来自同一次时间捕获。批量生成的所有编号共享同一个生成时间和日期桶,即使执行过程跨过午夜也不会拆分到两个日期。

可归还模板仍然遵循“领取编号 → 业务持久化 → 确认”的顺序。持久化失败时不确认,也不手动归还,等待超时回收;已经确认的业务后来取消时,再调用 ReleaseId。按日重置模板会保留号码签发时的原始日期桶和流水号,主动归还与超时回收不会把历史号码放入执行当天的归还队列。

业务数据提交与确认调用不在同一个事务中。确认失败时,应通过重试、Outbox 或等价的可靠投递方式保证最终成功。

接入后怎么确认生效

  1. 启动宿主,确认没有配置校验或 Redis 连接异常。
  2. 从 DI 解析 ITemplateIdGenerator,对已有模板生成单个和批量编号。
  3. 启动两个宿主实例,对同一模板并发发号,确认没有重复编号。
  4. 修改模板后,确认业务实例无需重启即可使用新规则。
  5. 使用可归还模板发号但不确认,等待回收时间后确认编号可以再次使用。
  6. 对同时启用按日重置和归还的模板,确认格式包含完整日期,并验证跨日归还或超时回收不会进入新一天的流水桶。

边界与限制

  • Redis 是分布式发号的运行前提,不提供脱离 Redis 的本地发号降级。
  • 管理后台与所有客户端必须连接同一个 Redis 实例和逻辑数据库。
  • 模板变化会自动同步,但不承诺所有实例在同一时刻立即生效。
  • 模板Id不保证严格连续,失败、归还复用和容量边界都可能产生跳号。
  • 当前配置使用 IOptions,不支持运行期间热更新。
  • 当前未把 Redis Cluster 列入已验证支持范围。
  • 业务提交与编号确认不在同一事务中,调用方需要保证确认最终成功。
  • ReleaseId 不是幂等操作,同一编号只能归还一次。
  • 确认和归还调用只携带模板与最终编号,调用方必须阻止上一代签发的延迟操作作用于已经重新签发的同一编号;严格的跨代隔离需要后续增加签发凭证。
  • {Letter} 模板不能启用 IsReturnable
  • ResetByDate = trueIsReturnable = true 可以同时使用,但模板必须包含完整的年、月、日;推荐使用 {Date} 对应的日期规则。
  • 模板开始发号后不要修改 ResetByDate;需要切换重置策略时应创建新的模板 code,避免新旧流水桶生成相同编号。
  • 按日可归还编号确认后仍需保留后续归还所需的签发状态,直到主动归还;如果大量已确认编号长期不再归还,需要评估 Redis 容量并明确业务取消期限。

常见问题

模板已配置,但 Generate 仍提示不存在

按顺序检查:

  1. 客户端传入的业务代码是否与管理后台模板的 code 完全一致,并核对大小写。
  2. 管理后台与宿主是否连接到同一个 Redis 实例和逻辑数据库。
  3. 模板和规则是否已经保存成功,管理后台自动同步时是否出现异常。
  4. 应用启动时是否出现模板加载或配置校验异常。

Pub/Sub 短暂失效后模板如何恢复

扩展会保留最近一次有效模板,并周期性执行全量校准。Redis 数据命令恢复可用后,遗漏的模板变更会在后续校准周期内收敛;Redis 整体不可用期间无法分配新的分布式流水号。

为什么 appsettings.json 中的本地模板没有生效

调用 AddTemplateIdRedis<AppRedisSource>() 后,模板来源会切换为 Redis,不再读取 TemplateIdGenerator:Templates 中的本地模板。需要使用本地模板时,删除这行 Redis 扩展注册,Component.deps.json 中仍然保留 TemplateIdGenerator

其他层是否需要安装 Redis 扩展

不需要。Redis 扩展只安装在最终宿主;业务层继续引用 Aegis.TemplateIdGenerator 并注入 ITemplateIdGenerator

切换到 Redis 后,业务调用需要修改吗

不需要。单个生成、批量生成、确认和归还的调用方式保持不变。

升级修复版本需要修改配置或迁移 Redis 吗

不需要。业务代码继续使用原有 ITemplateIdGenerator API,Component.deps.jsonAddTemplateIdRedis<T>() 和现有 Redis 配置都不需要调整。现有流水号及归还队列数据保持兼容,新签发状态由扩展自动维护。

当前部署不存在“按日重置且可归还”的历史签发数据,因此不需要补录或迁移 Redis 数据。连接同一 Redis 的所有发号宿主应升级到同一版本;升级尚未完成时,旧实例仍可能按旧逻辑发号。

配套阅读