Repository: NewLifeX/NewLife.RocketMQ
Branch: master
Commit: 88a51ffbfe13
Files: 140
Total size: 1.3 MB
Directory structure:
gitextract_ldvbuhvt/
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── copilot-instructions.md
│ ├── instructions/
│ │ ├── benchmark.instructions.md
│ │ ├── development.instructions.md
│ │ └── net.instructions.md
│ └── workflows/
│ ├── publish-beta.yml
│ ├── publish.yml
│ └── test.yml
├── .gitignore
├── ChangeLog.md
├── DLL/
│ ├── NewLife.Core.xml
│ └── NewLife.RocketMQ.xml
├── Doc/
│ ├── Changelog.md
│ ├── RequestReply_Guide.md
│ ├── newlife.snk
│ ├── rmq_4.9.7.pcap
│ ├── 架构设计.md
│ └── 需求文档.md
├── LICENSE
├── NewLife.RocketMQ/
│ ├── .github/
│ │ └── copilot-instructions.md
│ ├── AclOptions.cs
│ ├── AclProvider.cs
│ ├── AliyunOptions.cs
│ ├── AliyunProvider.cs
│ ├── BrokerClient.cs
│ ├── ClusterClient.cs
│ ├── Common/
│ │ ├── BrokerInfo.cs
│ │ ├── ILoadBalance.cs
│ │ └── WeightRoundRobin.cs
│ ├── Consumer.cs
│ ├── Grpc/
│ │ ├── GrpcClient.cs
│ │ ├── GrpcEnums.cs
│ │ ├── GrpcMessagingService.cs
│ │ ├── GrpcModels.cs
│ │ ├── GrpcServiceMessages.cs
│ │ └── ProtoExtensions.cs
│ ├── Helper.cs
│ ├── HuaweiProvider.cs
│ ├── ICloudProvider.cs
│ ├── MessageTrace/
│ │ ├── AsyncTraceDispatcher.cs
│ │ ├── MessageTraceHook.cs
│ │ └── TraceModel.cs
│ ├── Models/
│ │ ├── ConsumeEventArgs.cs
│ │ ├── ConsumeFromWheres.cs
│ │ ├── ConsumeTypes.cs
│ │ ├── DelayTimeLevels.cs
│ │ └── MessageModels.cs
│ ├── MqBase.cs
│ ├── MqSetting.cs
│ ├── NameClient.cs
│ ├── NewLife.RocketMQ.csproj
│ ├── Producer.cs
│ ├── Properties/
│ │ └── PublishProfiles/
│ │ └── FolderProfile.pubxml
│ ├── Protocol/
│ │ ├── Command.cs
│ │ ├── ConsumerData.cs
│ │ ├── ConsumerRunningInfo.cs
│ │ ├── ConsumerStates/
│ │ │ ├── ConsumerStatesModel.cs
│ │ │ ├── MessageQueueModel.cs
│ │ │ └── OffsetWrapperModel.cs
│ │ ├── EndTransactionRequestHeader.cs
│ │ ├── Header.cs
│ │ ├── HeartbeatData.cs
│ │ ├── LanguageCode.cs
│ │ ├── MQVersion.cs
│ │ ├── Message.cs
│ │ ├── MessageExt.cs
│ │ ├── MessageQueue.cs
│ │ ├── MqCodec.cs
│ │ ├── ProducerData.cs
│ │ ├── PullMessageRequestHeader.cs
│ │ ├── PullResult.cs
│ │ ├── QueryResult.cs
│ │ ├── RequestCode.cs
│ │ ├── ResponseCode.cs
│ │ ├── ResponseException.cs
│ │ ├── SendMessageRequestHeader.cs
│ │ ├── SendResult.cs
│ │ ├── SendStatus.cs
│ │ ├── SerializeType.cs
│ │ ├── ServiceState.cs
│ │ ├── SubscriptionData.cs
│ │ └── TransactionState.cs
│ └── TencentProvider.cs
├── NewLife.RocketMQ.sln
├── Readme.MD
├── Test/
│ ├── Program.cs
│ └── Test.csproj
└── XUnitTestRocketMQ/
├── .github/
│ └── copilot-instructions.md
├── AliyunIssuesTests.cs
├── AliyunTests.cs
├── BasicTest.cs
├── BatchAckTests.cs
├── BatchMessageTests.cs
├── BroadcastOffsetTests.cs
├── BrokerFailoverTests.cs
├── BrokerInfoTests.cs
├── CloudProviderTests.cs
├── CommandTests.cs
├── CompressionTests.cs
├── ConcurrentConsumeTests.cs
├── ConsumeStatsTests.cs
├── ConsumerStatesModelTests.cs
├── ConsumerTests.cs
├── HeaderTests.cs
├── IPv6Tests.cs
├── MQVersionTests.cs
├── MQVersionUpdateTests.cs
├── ManagementTests.cs
├── MessageExtendedTests.cs
├── MessageId5xTests.cs
├── MessageQueueTests.cs
├── MessageTests.cs
├── MessageTraceTests.cs
├── ModelTests.cs
├── MqBasePropertyTests.cs
├── MqSettingTests.cs
├── MultiTopicTests.cs
├── NameClientTests.cs
├── OrderConsumeTests.cs
├── PopConsumeTests.cs
├── ProducerTests.cs
├── ProducerTracerTests.cs
├── ProtoTests.cs
├── ProtocolDataTests.cs
├── PullResultTests.cs
├── QueryMessageTests.cs
├── RequestHeaderTests.cs
├── RequestReplyTests.cs
├── ResponseExceptionTests.cs
├── RetryTests.cs
├── SQL92FilterTests.cs
├── SendResultTests.cs
├── SpanRefactorTests.cs
├── SupportApacheAclTest.cs
├── TraceModelTests.cs
├── TransactionCheckTests.cs
├── VipChannelTests.cs
├── WeightRoundRobinTests.cs
└── XUnitTestRocketMQ.csproj
================================================
FILE CONTENTS
================================================
================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome:http://EditorConfig.org
# https://docs.microsoft.com/en-us/visualstudio/ide/editorconfig-code-style-settings-reference
# top-most EditorConfig file
root = true
# Don't use tabs for indentation.
[*]
indent_style = space
# (Please don't specify an indent_size here; that has too many unintended consequences.)
# Code files
[*.{cs,csx,vb,vbx}]
indent_size = 4
insert_final_newline = false
charset = utf-8-bom
end_of_line = crlf
# Xml project files
[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj}]
indent_size = 2
# Xml config files
[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}]
indent_size = 2
# JSON files
[*.json]
indent_size = 2
# Dotnet code style settings:
[*.{cs,vb}]
# Sort using and Import directives with System.* appearing first
dotnet_sort_system_directives_first = true
csharp_indent_case_contents = true
csharp_indent_switch_labels = true
csharp_indent_labels = flush_left
#csharp_space_after_cast = true
#csharp_space_after_keywords_in_control_flow_statements = true
#csharp_space_between_method_declaration_parameter_list_parentheses = true
#csharp_space_between_method_call_parameter_list_parentheses = true
#csharp_space_between_parentheses = control_flow_statements, type_casts
# 单行放置代码
csharp_preserve_single_line_statements = true
csharp_preserve_single_line_blocks = true
# Avoid "this." and "Me." if not necessary
dotnet_style_qualification_for_field = false:warning
dotnet_style_qualification_for_property = false:warning
dotnet_style_qualification_for_method = false:warning
dotnet_style_qualification_for_event = false:warning
# Use language keywords instead of framework type names for type references
dotnet_style_predefined_type_for_locals_parameters_members = false:suggestion
dotnet_style_predefined_type_for_member_access = false:suggestion
#dotnet_style_require_accessibility_modifiers = for_non_interface_members:none/always:suggestion
# Suggest more modern language features when available
dotnet_style_object_initializer = true:suggestion
dotnet_style_collection_initializer = true:suggestion
dotnet_style_coalesce_expression = true:suggestion
dotnet_style_null_propagation = true:suggestion
dotnet_style_explicit_tuple_names = true:suggestion
dotnet_style_prefer_inferred_tuple_names = true:suggestion
dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion
# CSharp code style settings:
[*.cs]
# Prefer "var" everywhere
csharp_style_var_for_built_in_types = true:warning
csharp_style_var_when_type_is_apparent = true:warning
csharp_style_var_elsewhere = true:warning
# Prefer method-like constructs to have a block body
csharp_style_expression_bodied_methods = when_on_single_line:suggestion
csharp_style_expression_bodied_constructors = when_on_single_line:suggestion
csharp_style_expression_bodied_operators = when_on_single_line:suggestion
# Prefer property-like constructs to have an expression-body
csharp_style_expression_bodied_properties = true:suggestion
csharp_style_expression_bodied_indexers = true:suggestion
#csharp_style_expression_bodied_accessors = true:suggestion
# Suggest more modern language features when available
csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion
csharp_style_pattern_matching_over_as_with_null_check = true:suggestion
csharp_style_inlined_variable_declaration = true:suggestion
csharp_prefer_simple_default_expression = true:suggestion
csharp_style_deconstructed_variable_declaration = true:suggestion
csharp_style_pattern_local_over_anonymous_function = true:suggestion
csharp_style_throw_expression = true:suggestion
csharp_style_conditional_delegate_call = true:suggestion
# 单行不需要大括号
csharp_prefer_braces = false:suggestion
# Newline settings
csharp_new_line_before_open_brace = all
csharp_new_line_before_else = true
csharp_new_line_before_catch = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_between_query_expression_clauses = true
[*.md]
trim_trailing_whitespace = false
================================================
FILE: .gitattributes
================================================
###############################################################################
# Set default behavior to automatically normalize line endings.
###############################################################################
* text=auto
###############################################################################
# Set default behavior for command prompt diff.
#
# This is need for earlier builds of msysgit that does not have it on by
# default for csharp files.
# Note: This is only used by command line
###############################################################################
#*.cs diff=csharp
###############################################################################
# Set the merge driver for project and solution files
#
# Merging from the command prompt will add diff markers to the files if there
# are conflicts (Merging from VS is not affected by the settings below, in VS
# the diff markers are never inserted). Diff markers may cause the following
# file extensions to fail to load in VS. An alternative would be to treat
# these files as binary and thus will always conflict and require user
# intervention with every merge. To do so, just uncomment the entries below
###############################################################################
#*.sln merge=binary
#*.csproj merge=binary
#*.vbproj merge=binary
#*.vcxproj merge=binary
#*.vcproj merge=binary
#*.dbproj merge=binary
#*.fsproj merge=binary
#*.lsproj merge=binary
#*.wixproj merge=binary
#*.modelproj merge=binary
#*.sqlproj merge=binary
#*.wwaproj merge=binary
###############################################################################
# behavior for image files
#
# image files are treated as binary by default.
###############################################################################
#*.jpg binary
#*.png binary
#*.gif binary
###############################################################################
# diff behavior for common document formats
#
# Convert binary document formats to text before diffing them. This feature
# is only available from the command line. Turn it on by uncommenting the
# entries below.
###############################################################################
#*.doc diff=astextplain
#*.DOC diff=astextplain
#*.docx diff=astextplain
#*.DOCX diff=astextplain
#*.dot diff=astextplain
#*.DOT diff=astextplain
#*.pdf diff=astextplain
#*.PDF diff=astextplain
#*.rtf diff=astextplain
#*.RTF diff=astextplain
================================================
FILE: .github/copilot-instructions.md
================================================
# NewLife Copilot 协作指令
适用于 NewLife 系列全部 C#/.NET 仓库,本文件可随仓库/指令目录一起拷贝到其他项目直接复用。存在本文件则必须遵循。**简体中文回复。**
通用 C# 最佳实践(设计模式、SOLID、健壮性等)AI 已知,此处不赘述,**仅列出组织专属规则与反常规约定**。
---
## 1. 专用指令(前置检查,必须执行)
**开始任何任务前,必须先将用户请求与下表触发信号逐行匹配。命中则立即用 `get_file` 读取 `.github/instructions/{指令文件}`,读取成功后遵循其中全部规则。未命中任何行才跳过。**
| 触发信号(用户请求含以下任意关键词即命中) | 指令文件 |
|---------|---------|
| XCode/实体生成/Model.xml/数据库 CRUD/`NewLife.XCode` 引用/`*.xcode.xml`/项目名含 `.Data`/`XCode.*` 命名空间/用户提及修改任意 `.xml` 文件 | `xcode.instructions.md` |
| Cube/魔方/Web开发/`NewLife.Cube` 引用/`NewLife.Cube.*` 命名空间 | `cube.instructions.md` |
| 性能测试/基准测试/压力测试/压测/BenchmarkDotNet/Benchmark/benchmark/吞吐量评估/性能分析/性能对比/性能报告/速度对比/速度测试/内存分配/perf/性能优化测试/做性能/跑分/测试报告 | `benchmark.instructions.md` |
| NetServer/NetSession/网络服务器/网络客户端/Socket服务/TCP服务/UDP服务/`NewLife.Net` 引用/`NewLife.Net.*` 命名空间/ISocketClient/ISocketRemote/CreateRemote/StandardCodec/LengthFieldCodec/管道编解码/网络编程/Echo服务/网络会话/长连接/粘包拆包 | `net.instructions.md` |
| 新建系统/新建项目/新增模块/需求整理/需求文档/需求分析/架构设计/技术方案/功能清单/功能拆分/任务分解/迭代开发/迭代计划/验收/PRD/用户故事/做一个系统/做一个平台/开发流程/全部搞完/批量开发/自治模式/一次性做完/继续处理/接着做 | `development.instructions.md` |
| 缓存/ICache/MemoryCache/Redis缓存/ICacheProvider/缓存设计/`NewLife.Caching` 命名空间 | `caching.instructions.md` |
| 序列化/JSON/Binary/JsonHelper/序列化设计/SpanSerializer/CSV导出/`NewLife.Serialization` 命名空间 | `serialization.instructions.md` |
| 加密/安全/Hash/MD5/SHA/AES/SM4/RSA/JWT/SecurityHelper/TokenProvider/`NewLife.Security` 命名空间 | `security.instructions.md` |
| 远程调用/ApiHttpClient/ApiClient/ApiServer/负载均衡/LoadBalancer/RPC/HTTP客户端/`NewLife.Remoting` 命名空间 | `remoting.instructions.md` |
| 配置/Config/IConfigProvider/HttpConfigProvider/CommandParser/配置中心/`NewLife.Configuration` 命名空间 | `configuration.instructions.md` |
**自动匹配指令**(无需触发,按 `applyTo` 路径自动生效):`caching`、`serialization`、`security`、`remoting`、`configuration` 这 5 个指令文件同时配置了 `applyTo` 模式,编辑对应目录下的文件时 VS Code 会自动加载。
---
## 2. 核心原则
检索优先、风格一致、兼容友好、**主动优化**。
发现明显缺陷(资源泄漏、空引用、逻辑错误)时主动修复;优化请求时深入分析,不做表面工作。
改动较小直接做并说明;改动较大(涉及公共 API 或大范围重构)先列方案询问确认。
---
## 3. 兼容性约束(极重要)
- **语言版本**:当前为 **C# 14**(`latest`),最大化使用最新语法糖(switch 表达式、集合表达式 `[]`、`?.`/`??`/`??=`、模式匹配、目标类型 `new`、record 等)
- **框架版本**:新增 API 前,先查看当前项目 `.csproj` 的 `` 配置,**只需满足已声明版本的兼容性**,无需对所有历史版本降级。若包含 `net45`/`netstandard2.0` 等低版本,再提供条件编译降级实现。
- **禁止高版本专属 BCL API**(低版本项目):❌ `ArgumentNullException.ThrowIfNull()` → ✅ `if (x == null) throw new ArgumentNullException(nameof(x));`
- **条件编译符号**:`NETFRAMEWORK`、`NETSTANDARD2_0`、`NETCOREAPP`、`NET5_0_OR_GREATER`、`NET6_0_OR_GREATER`、`NET8_0_OR_GREATER`
---
## 4. 编码规范
### 4.1 类型名(关键差异)
**必须**使用 .NET 正式名:`String`/`Int32`/`Boolean`/`Int64`/`Double`/`Object` 等。
❌ **禁止**使用 C# 别名:`string`/`int`/`bool`/`long`/`double`/`object`
### 4.2 命名
| 成员类型 | 规则 | 示例 |
|---------|------|------|
| 类型/公共成员 | PascalCase | `UserService`、`GetName()` |
| 参数/局部变量 | camelCase | `userName`、`count` |
| 私有字段 | `_camelCase` | `_cache`、`_instance` |
| 扩展方法类 | `xxxHelper` 或 `xxxExtensions` | `StringHelper`、`CollectionExtensions` |
### 4.3 代码风格
- **命名空间**:file-scoped namespace
- **单文件**:每文件一个主要公共类型;较大平台差异使用 `partial`
- **集合初始化**:优先使用集合表达式 `[]`,如 `List Tags { get; set; } = [];`
- **Null 条件运算符**:优先使用 `?.`/`??` 简化空值检查;**C# 14 空条件赋值 `??=`**:变量为 null 时才赋值,可显著提升可读性
```csharp
// ✅ C#14 空条件赋值(??=):为 null 时才赋值,替代 if (x == null) x = ...
_cache ??= new MemoryCache();
list ??= [];
// ✅ if 内只有单行代码时可不加花括号(单行 if 同行或换行均可)
if (value == null) return;
if (key == null) throw new ArgumentNullException(nameof(key));
// ✅ 语句较长时另起一行,仍不加花括号
if (value == null)
throw new ArgumentNullException(nameof(value), "Value cannot be null");
// ✅ 多分支单语句:不加花括号
if (count > 0)
DoSomething();
else
DoOther();
// ✅ for/foreach/while 循环体必须保留花括号(即使单语句)
foreach (var item in list)
{
Process(item);
}
for (var i = 0; i < count; i++)
{
Process(i);
}
// ✅ using 优先无花括号声明;仅需生命周期(如锁)时用弃元
using var stream = File.OpenRead("file.txt");
using var _ = _lock.AcquireLock();
```
### 4.4 Region 与日志
较长类使用 `#region` 分段,顺序:`属性` → `静态` → `构造` → `方法` → `辅助` → **`日志`**。
含 `ILog Log` 和 `WriteLog` 时:**必须放类末尾**,用名为"日志"的 region 包裹,不放入"辅助"。
关键过程可使用 `Tracer?.NewSpan()` 埋点。
### 4.5 文档注释
- `` **必须同行闭合**:`/// 获取名称`
- 每个参数**必须有** `` 标签,无论方法可见性
- 有返回值**必须有** ``;复杂方法可增加 ``
- `public`/`protected` 成员必须注释;`[Obsolete]` 必须包含迁移建议
### 4.6 异步与性能
- 异步方法后缀 `Async`,库内部默认 `ConfigureAwait(false)`
- 热点路径避免反射/复杂 Linq,优先手写循环/`ArrayPool`/`Span`
- 池化资源明确获取/归还,异常分支不遗失归还
### 4.7 错误处理
- 精准异常类型:`ArgumentNullException`/`InvalidOperationException` 等
- TryXxx 模式:不用异常作常规分支
- 类型转换:优先使用 `Utility` 扩展方法,完整列表:`ToInt()`/`ToLong()`/`ToDouble()`/`ToDecimal()`/`ToBoolean()`/`ToDateTime()`/`ToDateTimeOffset()`
- 对外异常不暴露内部实现/路径
---
## 5. NewLife 内置工具
优先使用项目内置工具而非标准库,**禁止重复造轮子**:
- 字符串构建:`Pool.StringBuilder`(替代 `new StringBuilder()`)
- 时间戳(毫秒级相对时间):`Runtime.TickCount64`;**代码计时(精确耗时测量):`Stopwatch`**
- 类型转换:`Utility` 扩展方法 — `ToInt()`/`ToLong()`/`ToDouble()`/`ToDecimal()`/`ToBoolean()`/`ToDateTime()`/`ToDateTimeOffset()`
- 二进制读写:`SpanReader` / `SpanWriter`(替代手动字节偏移操作)
- 追踪埋点:`Tracer?.NewSpan()`
---
## 6. 防御性注释(禁止删除)
代码中带有说明文字的被注释代码属于**防御性注释**,记录历史踩坑经验。**禁止删除,禁止"恢复"执行**。可补充更详细说明。
```csharp
// 曾经尝试过同步等待,但会导致线程池饥饿和死锁
// var result = task.Result;
// 不要使用 SendAsync 的无超时重载,否则会造成连接泄漏
// await client.SendAsync(data);
```
---
## 7. 工作流
触发检查(第 1 节触发信号表匹配,命中则读取专用指令) → 检索(**优先复用**现有实现) → 评估(公共 API/兼容性/性能) → 方案 → 实施 → 验证 → **AskQuestions 多选确认** → [需调整则循环] → 说明
- **触发检查**:开始工作前必须完成,遗漏专用指令将导致输出不符合要求
- **实施**:完成主任务;顺带修复明显缺陷;顺带简化重复代码;保留原注释与结构
- **验证**:代码变更必须编译通过;找到相关测试则运行;仅文档变更可跳过
- **AskQuestions 多选确认**:使用 `vscode_askQuestions` 工具,多选询问用户是否满意;若需调整则修改后重新编译,循环至满意;确认满意后才进入下一项开发
### 主动优化原则
用户要求**分析/优化代码**时:
| 行动 | 说明 |
|------|------|
| **架构梳理** | 重构不清晰的结构,让代码更易懂 |
| **缺陷修复** | 资源泄漏、空引用、并发问题、逻辑错误 → 直接修复 |
| **代码简化** | 提取重复代码、合并冗余判断、应用现代语法 |
| **性能优化** | 缓存重复计算、池化高频对象、避免无用分配 |
| **注释完善** | 补充缺失的 XML 注释和关键逻辑说明 |
---
## 8. 测试
- 框架 xUnit;类名 `{ClassName}Tests`;方法加 `[DisplayName("中文描述意图")]`
- 网络端口用 `0`/随机,IO 用临时目录
- 先搜索 `{ClassName}` 引用定位测试文件,再找 `{ClassName}Tests.cs`;**未找到需说明**,不自动创建测试项目
---
## 9. 文档与发布
### Markdown 文档
**UTF-8 无 BOM**;存放 `Doc/` 目录;文件名优先中文,内容优先简体中文,避免乱码。**已有文件必须先读取再增量修改,禁止覆盖。**
> 代码注释同样要求 UTF-8 无 BOM,优先简体中文。生成或编辑任何文件时须确保编码正确,防止中文乱码。
### NuGet 版本
| 类型 | 格式 | 示例 |
|------|------|------|
| 正式版 | `{主}.{子}.{年}.{月日}` | `11.9.2025.0701` |
| 测试版 | `{主}.{子}.{年}.{月日}-beta{时分}` | `11.9.2025.0701-beta0906` |
---
## 10. 重要禁止项
以下是 AI 容易犯但在本项目影响严重的错误:
- 将 `String`/`Int32` 改为 `string`/`int`(本项目反 C# 惯例,**必须用正式名**)
- 删除防御性注释(带说明的注释代码)
- 删除 for/foreach/while 循环体的花括号(**循环体必须有花括号,即使只有一行**)
- 将 `` 拆成多行
- 擅自删除 `public`/`protected` 成员
- 擅自新增外部 NuGet 依赖(需说明理由)
- 仅删除空白行/注释制造"格式优化"提交
- 虚构不存在的 API/文件/类型
- 伪造测试结果/性能数据
- 在热点路径添加未缓存反射/复杂 Linq
- 输出敏感凭据/内部地址
- 发现问题却视而不见
- 用户要求优化时仅做注释/测试等表面工作
- **跳过第 1 节触发检查**(命中关键词却未加载专用指令文件,是最严重的遗漏错误)
---
## 11. 变更说明模板
```markdown
## 概述
做了什么 / 为什么
## 影响
- 公共 API:是/否
- 性能影响:无/有(说明)
## 兼容性
降级策略 / 条件编译点
## 风险与后续
潜在回归 / 是否补测试
```
---
## 12. Skills 技能文件
`.github/skills/` 目录下的技能文件提供特定领域的详细使用指南和代码示例,用户可在 Copilot Chat 中通过 `#` 引用。
| 技能文件 | 覆盖领域 |
|---------|---------|
| `caching.skill.md` | ICache/MemoryCache/Redis 统一缓存接口 |
| `logging-tracing.skill.md` | ILog/XTrace 日志与 ITracer/DefaultTracer 链路追踪 |
| `networking.skill.md` | NetServer/NetSession TCP/UDP/WebSocket 网络编程 |
| `serialization.skill.md` | JSON/Binary/Span/CSV 序列化 |
| `configuration.skill.md` | Config<T>/IConfigProvider/HttpConfigProvider 配置管理 |
| `http-client.skill.md` | ApiHttpClient 多节点 HTTP 客户端与负载均衡 |
| `dependency-injection.skill.md` | ObjectContainer/Host/Plugin/Actor 依赖注入与宿主 |
| `timer-scheduling.skill.md` | TimerX/Cron 高级定时调度 |
| `security.skill.md` | Hash/AES/SM4/RSA/JWT/TokenProvider 安全与加密 |
| `type-conversion.skill.md` | ToInt/ToBoolean/StringHelper/Pool.StringBuilder 类型转换与工具 |
---
## 13. Agents 智能代理
`.github/agents/` 目录下定义了专用 AI 代理角色,用户可在 Copilot Chat 中通过 `@` 调用。
| 代理文件 | 用途 |
|---------|------|
| `newlife-expert.agent.md` | NewLife 组件专家:功能查询、组件推荐、编码指导 |
| `code-review.agent.md` | 代码审查:按 NewLife 规范 8 维度检查代码 |
| `project-init.agent.md` | 项目初始化:按模板创建新 NewLife 项目结构 |
---
(完)
================================================
FILE: .github/instructions/benchmark.instructions.md
================================================
---
applyTo: "**/Benchmark/**"
---
# 性能测试指令
适用于性能测试、压力测试、基准测试、BenchmarkDotNet 相关任务。
---
## 1. 项目结构
- 基准测试统一放在 `Benchmark/` 项目,按主题分子目录(如 `PacketBenchmarks/`、`CacheBenchmarks/`)
- 入口 `Program.cs` 使用 `BenchmarkSwitcher` 模式,**不要修改**
- TFM 使用最新稳定版,`latest`
## 2. 代码规范
遵循主指令全部编码规范(类型名用 `String`/`Int32` 等、file-scoped namespace),另有以下补充:
- **命名空间**:`Benchmark.{主题}Benchmarks`
- **类名**:`{被测类型}Benchmark` 或 `{被测主题}Benchmark`
- **必须标注** `[MemoryDiagnoser]` 和 `[SimpleJob]`(需调整迭代次数时用 `[SimpleJob(iterationCount: N)]`)
- **方法描述**:`[Benchmark(Description = "中文描述")]`,方便报告阅读
- **参数化**:用 `[Params]` 或 `[ParamsSource]` 控制数据规模
- **初始化 / 清理**:分别放 `[GlobalSetup]` 和 `[GlobalCleanup]`
- **分组**:同类测试用 `#region` 分组
- **多线程并发**:动态线程数包含 CPU 核心数,推荐模板:
```csharp
public static IEnumerable ThreadCounts
{
get
{
var cores = Environment.ProcessorCount;
var set = new SortedSet { 1, 4, 8, 32 };
set.Add(cores);
return set;
}
}
[ParamsSource(nameof(ThreadCounts))]
public Int32 ThreadCount { get; set; }
```
## 3. 运行要求
- 必须以 **Release 模式**运行,获取有代表性的峰值数据
- 运行全部:`dotnet run -c Release`
- 运行指定类:`dotnet run -c Release -- --filter *ClassName*`
- ❌ 禁止在 Debug 模式下采集数据写入报告
## 4. 测试维度
- **并发维度**:单线程 + 多线程(多线程含与当前 CPU 核心数相同的并发数)
- **操作维度**:单一操作 + 批量操作
## 5. 常见错误
- ❌ 在 `[Benchmark]` 方法内做初始化(应放 `[GlobalSetup]`)
- ❌ 忽略返回值导致 JIT 死码消除(确保返回或赋值给字段)
- ❌ 手动 `Stopwatch` 计时(BDN 自动处理)
- ❌ `using` 的 `Dispose` 开销混入测量(仅在测试 Dispose 本身时才包含)
## 6. 报告存放
`Doc/Benchmark/{测试主题}性能测试.md`(UTF-8 无 BOM)
## 7. 报告结构(顺序固定)
1. **性能概览**(放最前:一句话点明被测功能用途 + 简单语言总结核心发现)
2. 测试环境 → 测试方法 → 测试结果(BDN 原始表格,保留 Mean/Error/StdDev/Allocated)
3. 核心指标(换算 msg/s、QPS 等业务指标)
4. **对比分析**
- 纵向:同场景不同并发趋势,找出最优并发点
- 横向:不同方案同并发差异百分比
5. 性能瓶颈定位(按重要程度排序)→ 优化建议(含预期收益与内存节省预估)
## 8. 性能瓶颈定位规范
性能瓶颈定位章节是报告的核心价值输出,必须遵循以下规范:
### 8.1 瓶颈点结构(每个瓶颈必须包含)
每个瓶颈点必须包含以下要素,缺一不可:
| 要素 | 说明 | 示例 |
|------|------|------|
| **优先级标签** | P0/P1/P2/P3,按影响程度降序 | P0 |
| **瓶颈名称** | 一句话准确描述瓶颈 | VisitTime 写入触发 MESI 缓存行争用 |
| **优化收益占比** | 该瓶颈在总体可优化空间中的占比 | ~35% |
| **现象与数据** | 用 BDN 实测数据量化问题严重程度 | 4T→8T 扩展仅 1.3x,低于预期 2.0x |
| **根因分析** | 从代码执行路径分析到底层硬件行为 | Get 每次写 VisitTime → 缓存行 Modified → 多核 MESI 失效 |
| **开销占比估算** | 在单次操作总耗时中的占比 | 占 Get 总耗时 30%~40% |
| **内存影响** | 每次操作的额外内存分配或 GC 压力 | 48 B/次装箱分配,32 线程累计 3 MB |
| **优化方向** | 具体可落地的优化方案 | 时间窗口内跳过更新(如 1s 内不重复写) |
| **预期收益** | 速度提升倍数 + 内存节省比例 | 多线程吞吐 +20-30%,消除缓存行争用 |
### 8.2 瓶颈分级标准
| 级别 | 定义 | 优化收益占比 | 行动 |
|------|------|------------|------|
| **P0** | 影响核心吞吐或造成 >30% 性能损失 | ≥25% | 必须优化 |
| **P1** | 影响多线程扩展性或造成显著内存压力 | 15%~25% | 建议优化 |
| **P2** | 特定场景下的次要瓶颈 | 5%~15% | 可选优化 |
| **P3** | 微小开销,仅在极端场景有影响 | <5% | 记录备查 |
### 8.3 瓶颈定位表格模板
性能瓶颈定位章节使用以下统一表格格式:
```markdown
### 核心瓶颈点总览
| 优先级 | 瓶颈 | 优化收益占比 | 当前开销 | 优化后预估 | 内存节省 |
|--------|------|------------|---------|-----------|---------|
| P0 | {瓶颈名称} | ~{X}% | {耗时/分配} | {目标值} | {节省比例} |
| P1 | {瓶颈名称} | ~{X}% | {耗时/分配} | {目标值} | {节省比例} |
| ... | ... | ... | ... | ... | ... |
```
### 8.4 内存优化方向表格模板
紧跟瓶颈总览表之后,补充内存优化方向:
```markdown
### 关键内存优化方向
| 优先级 | 优化方向 | 当前分配 | 优化后预估 | 节省比例 | 实施方案 |
|--------|---------|---------|-----------|---------|---------|
| P0 | {方向} | {X} B/op | {Y} B/op | {Z}% | {方案} |
| ... | ... | ... | ... | ... | ... |
```
### 8.5 开销拆解要求
对每个核心操作,必须给出开销来源拆解表:
```markdown
| 开销来源 | 占比估算 | 耗时估算 | 说明 |
|---------|---------|---------|------|
| {来源1} | ~{X}% | ~{N} ns | {原因} |
| {来源2} | ~{X}% | ~{N} ns | {原因} |
```
### 8.6 撰写原则
- **数据驱动**:所有结论必须有 BDN 实测数据支撑,禁止无数据臆测
- **量化优先**:用"快 X 倍"、"省 Y%"、"降 Z B/op"表达,避免"显著"、"明显"等模糊词
- **根因到底**:从应用层代码 → 运行时机制 → CPU 微架构逐层分析
- **可操作**:每个优化建议必须指明具体修改位置和实施方案,而非泛泛建议
- **排序严格**:P0 在前,P3 在后,同级按收益占比降序
================================================
FILE: .github/instructions/development.instructions.md
================================================
---
applyTo: "Doc/**"
---
# AI 辅助开发流程指令
适用于新建应用系统、新增功能模块、需求整理、架构设计等研发全流程任务。
---
## 1. 流程总览
```
需求整理 → 需求评审与拆分 → 技术方案设计 → 任务分解 → 迭代开发 → 集成验证 → 验收回顾
```
**核心原则**:大需求必须拆小,每个迭代交付可验证的最小功能单元。禁止"一次性全做完"。
---
## 2. 各阶段规范
### 2.1 需求整理
用户提供原始描述(口语化、列表、草稿均可),AI 整理为以下结构(需求 + 功能清单 + 验收 合为一个文件):
```markdown
# {项目/模块名}需求
## 1. 背景与目标
- 为什么做(痛点/动机)
- 做到什么程度算成功(可衡量目标)
## 2. 用户角色
| 角色 | 说明 | 核心诉求 |
|------|------|---------|
## 3. 功能需求
### 3.1 {功能模块名}
- **描述**:一句话说明
- **用户故事**:作为{角色},我希望{操作},以便{价值}
- **验收条件**(AC):
- [ ] 条件 1
- [ ] 条件 2
- **优先级**:Must / Should / Could / Won't
## 4. 非功能需求
- 性能 / 安全 / 兼容性(三项必填)
## 5. 边界与约束
- 不做什么(明确排除项)
- 已知限制 / 技术债务
## 6. 功能清单与迭代计划
(需求评审拆分后填写,见 2.2)
## 7. 验收记录
(开发完成后填写,见 2.7)
## 8. 术语表
| 术语 | 定义 |
|------|------|
```
**规则**:每个功能必须有 AC,无 AC 不可进入开发;优先级用 MoSCoW 四级;非功能需求至少覆盖性能、安全、兼容性。
### 2.2 需求评审与拆分
按**纵向切片**(端到端功能,非技术层)拆分,遵循 INVEST 原则,单个功能单元 ≤ 1-2 天工作量,有依赖须标注。
写入需求文档「6. 功能清单与迭代计划」:
```markdown
## 6. 功能清单与迭代计划
### 迭代 1:{主题}(Must 级别)
| 编号 | 功能点 | 验收条件 | 前置依赖 | 预估工作量 |
|------|--------|---------|---------|----------|
| F001 | xxx | AC1, AC2 | 无 | 0.5d |
| F002 | xxx | AC1 | F001 | 1d |
### 迭代 2:{主题}(Should 级别)
...
```
### 2.3 技术方案设计
```markdown
# {项目/模块名}架构
## 1. 架构概览
## 2. 数据模型
## 3. 接口设计
| 接口 | 方法 | 路径/签名 | 入参 | 出参 | 说明 |
|------|------|----------|------|------|------|
## 4. 技术选型
| 领域 | 选型 | 理由 |
|------|------|------|
## 5. 关键设计决策
| 决策点 | 方案 | 备选方案 | 选择理由 |
|--------|------|---------|---------|
## 6. 任务分解
(见 2.4)
## 7. 风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
```
**规则**:优先使用 NewLife 已有组件(XCode、Remoting、Stardust 等);数据模型考虑 XCode 实体规范;接口遵循现有 API 风格。
### 2.4 任务分解
单个任务 = 一次 AI 对话可完成的工作量(编码 + 测试 + 自测通过)。写入技术方案「6. 任务分解」:
```markdown
### 任务 T001:{动词 + 目标}
- **对应功能**:F001
- **输入**:前置条件 / 已有代码
- **产出**:新增/修改哪些文件
- **验收**:怎样算完成
```
**批次编排**(用于自治模式,见第 6 节):按依赖关系编排为批次,每批次 5-8 个任务,同批次内尽量无相互依赖,基础设施任务排在前面,每批次结束设 `[检查点 N]`,标注本批次产出是下批次哪些输入。
### 2.5 迭代开发
流程:`理解任务 → 检索现有实现 → 编码 → 编译通过 → 测试通过 → 提交说明`
- 严格遵守主指令编码规范,每个任务必须编译通过
- 常规模式:遇歧义暂停确认;自治模式:记录跳过继续(见第 6 节)
- 有依赖按顺序执行,不跳跃
### 2.6 集成验证
全部编译通过 → 单元测试通过 → 端到端主流程走通 → 异常场景覆盖 → 性能符合预期
### 2.7 验收与回顾
对照需求文档逐条验收,写入「7. 验收记录」:
```markdown
## 7. 验收记录
### 功能验收
| 编号 | 功能点 | 验收条件 | 状态 | 备注 |
|------|--------|---------|------|------|
### 遗留问题
| 问题 | 影响 | 后续计划 |
|------|------|---------|
### 经验总结
- 做得好的 / 待改进的
```
---
## 3. 文档存放规范
全流程仅产出 **2 个文档**,扁平存放在 `Doc/` 下:
| 文档 | 文件名 | 包含内容 |
|------|--------|--------|
| 需求文档 | `Doc/{项目名}需求.md` | 背景目标 + 功能需求 + 功能清单 + 验收记录 + 术语表 |
| 技术方案 | `Doc/{项目名}架构.md` | 架构 + 数据模型 + 接口 + 技术选型 + 任务分解 + 风险 |
UTF-8 无 BOM;已有文件必须先读取再增量修改,禁止覆盖;各阶段产出追加到对应章节,不新建文件。
---
## 4. AI 协作要点
### 4.1 阶段切换
| 用户说 | 进入阶段 |
|--------|---------|
| "整理需求"/"写需求" | 2.1 需求整理 |
| "拆分"/"拆解"/"排优先级" | 2.2 需求评审与拆分 |
| "技术方案"/"架构设计"/"怎么实现" | 2.3 技术方案设计 |
| "开始开发"/"写代码"/"实现 F001" | 2.5 迭代开发 |
| "全部搞完"/"批量开发"/"自治模式"/"一次性做完"/"继续处理"/"接着做" | 第 6 节自治批处理 |
| "验收"/"检查完成情况" | 2.7 验收与回顾 |
| 一大段描述未指定阶段 | 默认 2.1 需求整理 |
### 4.2 主动引导
每阶段完成后提示下一步:需求整理完 → 拆分? → 技术方案? → 任务分解 → 开发?
### 4.3 大需求防护
功能点 > 5 / 实体 > 3 / 跨 2 层以上 / 描述 > 500 字 → 必须先拆分再开发。
---
## 5. 常见反模式(禁止)
- ❌ 跳过需求直接编码
- ❌ 一次性输出所有代码(大需求必须拆迭代或使用自治模式)
- ❌ 需求文档没有验收条件
- ❌ 功能拆分按技术层而非用户价值
- ❌ 任务没有完成标准就开始编码
- ❌ 完成后不做验收对照
- ❌ 自治模式下遇阻塞问题死等用户(应记录跳过,继续后续)
- ❌ 自治模式下做需要人工决策的架构变更(应记录待确认,现有方案兜底)
- ❌ 跨批次不做编译验证
---
## 6. 自治批处理模式
架构师已确认需求和技术方案后,AI 按任务清单自主执行,最小化人工介入。
### 6.1 进入条件(全部满足)
- [ ] 需求文档已完成且架构师已确认
- [ ] 技术方案已完成且架构师已确认
- [ ] 任务已分解并编排为批次
- [ ] 用户明确触发("全部搞完"/"批量开发"/"自治模式"等)
未满足时提示缺少哪些条件。
### 6.2 计划结构与循环刷新
AI 用 plan 工具创建层次化计划,「前置刷新 + 批次执行」循环:
```
1. [前置] 读取需求文档与技术方案
2. [前置] 读取任务清单与进度状态
3. [前置] 全量编译确认基线
4. [前置] 识别可并行的批次组
5. [批次1] 执行 T001-T005(子步骤展开各任务)
6. [检查点1] 输出批次1报告
7. [刷新] 重读需求文档与技术方案
8. [批次2] 执行 T006-T010
9. [检查点2] 输出批次2报告
...(循环:刷新 → 批次 → 检查点)
N-2. [后置] 全量编译与集成验证
N-1. [后置] 补完被跳过的任务
N. [后置] 生成验收报告
```
**要点**:
- 主步骤 15-25 个(不超过 30),子步骤展开具体任务仅供参考不单独追踪
- 刷新步骤穿插在每两个批次之间,`get_file` 重读文档对抗上下文漂移
- 用 `update_plan_progress` 跟踪主步骤,不为每个子任务调用
- 无依赖的批次可合并为一个主步骤执行,有依赖的必须顺序执行
### 6.3 执行协议
| 情况 | 处理方式 |
|------|----------|
| 任务明确无歧义 | 直接执行:编码 → 编译 → 测试 |
| 小歧义可合理推断 | 执行并在问题日志记录推断依据 |
| 重大歧义或多种等价方案 | 标记 `⏸️ 待确认`,跳过 |
| 前置任务被跳过 | 标记 `⏸️ 依赖阻塞:T0xx`,跳过 |
| 编译失败短时间无法修复 | 回滚改动,记录并跳过 |
| 涉及公共 API / 架构变更 | 标记 `⏸️ 需架构师决策`,兜底或跳过 |
### 6.4 检查点报告
每批次完毕后输出:
```markdown
## 检查点 N 报告
### 完成情况
| 任务 | 状态 | 说明 |
|------|------|------|
| T001 | ✅ 完成 | |
| T003 | ⏸️ 跳过 | 需确认:xxx |
### 编译状态
- 全量编译:✅ 通过 / ❌ 失败(错误详情)
### 问题日志
| 编号 | 类型 | 描述 | 影响任务 | 建议方案 |
|------|------|------|---------|----------|
### 统计
- 本批次 N 个,完成 X 个,跳过 Y 个
- 累计进度:已完成 X / 总计 Z(XX%)
- 上下文预估:{已处理任务数} / {建议上限}
```
### 6.5 用户回复与继续
架构师回来后:AI 呈现检查点报告 → 架构师批量回复问题("Q001 OK,Q002 选 A")→ AI 修正推断 + 执行跳过的任务 + 继续下批次 → 循环至完成。
触发词:"继续"/"继续处理"/"回复完了"/"接着做"
### 6.6 质量护栏(自动执行)
编译门禁(失败即修复或回滚)/ 命名与技术方案一致 / 编码规范严格遵守 / 新增代码前搜索现有实现避免重复 / 不擅自引入新 NuGet 包
### 6.7 会话边界处理
每个检查点后、连续完成 15+ 任务后、搜索结果不准确时 → 评估是否需要新会话。
**新会话续接模板**:
```
我们在做 {项目名} 的自治批处理开发。
- 需求文档:Doc/{项目名}需求.md
- 技术方案:Doc/{项目名}架构.md
- 当前进度:批次 N 已完成,从批次 N+1 的 T0xx 开始继续
- 待解决问题:{问题编号}
请读取以上文档,从 T0xx 继续执行,自治模式。
```
上下文即将耗尽时 AI 主动提醒并生成上述模板。新会话前 4 步仍为前置刷新,已完成批次直接标记完成。
### 6.8 批次大小建议
| 复杂度 | 批次大小 |
|--------|---------|
| 简单(CRUD) | 8-10 |
| 中等(业务逻辑) | 5-7 |
| 复杂(算法、并发) | 3-5 |
单会话上限:3-4 个批次(约 15-25 个任务)。
---
(完)
================================================
FILE: .github/instructions/net.instructions.md
================================================
---
applyTo: "**/Net/**"
---
# 网络编程指令
适用于基于 `NewLife.Net` 的网络服务器(`NetServer`)和客户端(`ISocketClient`)开发任务。
---
## 1. 架构概览
NewLife 网络框架分为两层:
| 层级 | 服务端 | 客户端 | 说明 |
|------|--------|--------|------|
| **应用层** | `NetServer` / `NetServer` | — | 管理监听、会话生命周期、管道 |
| **传输层** | `TcpServer` / `UdpServer` | `TcpSession` / `UdpServer`(客户端模式) | 底层 Socket 收发 |
| **会话** | `NetSession` / `NetSession` | — | 每个连接对应一个会话,业务逻辑入口 |
| **管道** | `IPipeline` + `IPipelineHandler` | 同左 | 编解码、粘包拆包、消息匹配 |
**关键接口**:
- `ISocketClient` — 客户端连接接口(Open/Close/Send/Receive)
- `ISocketRemote` — 远程通信接口(Send/Receive/SendMessageAsync)
- `INetSession` — 网络会话接口(服务端每个连接的业务处理单元)
- `INetHandler` — 网络数据处理器接口(Init/Process)
---
## 2. 服务端开发规范
### 2.1 基本模式
推荐使用泛型 `NetServer` + 自定义 `NetSession` 子类:
```csharp
/// 自定义网络服务器
class MyServer : NetServer { }
/// 自定义会话,每个客户端连接对应一个实例
class MySession : NetSession
{
/// 客户端连接
protected override void OnConnected()
{
base.OnConnected();
WriteLog("客户端已连接 {0}", Remote);
}
/// 收到客户端数据
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
// 业务处理
}
/// 客户端断开
protected override void OnDisconnected(String reason)
{
base.OnDisconnected(reason);
}
}
```
### 2.2 服务器启动配置
```csharp
var server = new MyServer
{
Port = 8080, // 监听端口,0 表示随机
ProtocolType = NetType.Tcp, // Tcp/Udp/Unknown(同时监听)
// AddressFamily = AddressFamily.InterNetwork, // 仅IPv4,默认同时IPv4+IPv6
ServiceProvider = provider, // 依赖注入
Log = XTrace.Log, // 应用日志
SessionLog = XTrace.Log, // 会话日志
Tracer = tracer, // APM 追踪
#if DEBUG
SocketLog = XTrace.Log, // Socket 层日志(仅调试)
LogSend = true,
LogReceive = true,
#endif
};
server.Start();
```
### 2.3 会话生命周期
```
连接建立 → OnConnected() → OnReceive()... → OnDisconnected(reason) → Dispose()
```
- **OnConnected**:初始化会话状态、发送欢迎消息
- **OnReceive**:核心业务处理入口,`e.Packet` 为原始数据,`e.Message` 为管道解码后的消息
- **OnDisconnected**:清理资源、记录日志,`reason` 包含断开原因
- 会话内可通过 `ServiceProvider` 获取 Scoped 服务
### 2.4 服务端发送数据
| 方法 | 说明 |
|------|------|
| `Send(IPacket)` | 直接发送原始数据,不经过管道 |
| `Send(String)` | 发送字符串,默认 UTF-8 |
| `Send(ReadOnlySpan)` | 高性能发送 |
| `SendMessage(Object)` | 通过管道编码后发送,不等待响应 |
| `SendReply(Object, ReceivedEventArgs)` | 发送响应消息,与请求关联(用于 StandardCodec 等协议) |
| `SendMessageAsync(Object)` | 通过管道发送并等待响应 |
### 2.5 群发
```csharp
// 群发数据给所有在线客户端
await server.SendAllAsync(data);
// 带过滤条件群发
await server.SendAllAsync(data, session => session.ID > 100);
// 群发管道消息
server.SendAllMessage(message, session => session["VIP"] is true);
```
群发要求 `UseSession = true`(默认开启)。
### 2.6 事件模式(简单场景)
不需要自定义会话时,可直接使用事件:
```csharp
var server = new NetServer { Port = 8080 };
server.Received += (sender, e) =>
{
if (sender is INetSession session)
session.Send(e.Packet); // Echo
};
server.Start();
```
---
## 3. 客户端开发规范
### 3.1 创建客户端
通过 `NetUri.CreateRemote()` 扩展方法创建:
```csharp
// TCP 客户端
var client = new NetUri("tcp://127.0.0.1:8080").CreateRemote();
// UDP 客户端
var client = new NetUri("udp://127.0.0.1:8080").CreateRemote();
// WebSocket 客户端
var client = new NetUri("ws://127.0.0.1:8080/path").CreateRemote();
```
`CreateRemote` 根据协议自动返回 `TcpSession` / `UdpServer` / `WebSocketClient`。
### 3.2 客户端使用
```csharp
var uri = new NetUri("tcp://127.0.0.1:8080");
var client = uri.CreateRemote();
client.Log = XTrace.Log;
client.Open();
// 发送原始数据(不经过管道)
client.Send("Hello");
// 事件驱动接收
client.Received += (sender, e) =>
{
// e.Packet 原始数据,e.Message 管道解码后的消息
};
// 或同步/异步接收
using var pk = client.Receive();
using var pk = await client.ReceiveAsync(cancellationToken);
client.Close("完成"); // 或 client.Dispose()
```
### 3.3 请求-响应模式(需要管道编解码器)
```csharp
var client = new NetUri("tcp://127.0.0.1:8080").CreateRemote();
client.Add();
client.Open();
var response = await client.SendMessageAsync(payload, cancellationToken); // 等待响应
client.SendMessage(message); // 不等待响应
```
### 3.4 SSL/TLS
```csharp
// 服务端 SSL
var server = new NetServer
{
Port = 443,
SslProtocol = SslProtocols.Tls12,
Certificate = new X509Certificate2("server.pfx", "password"),
};
// 客户端 SSL(自动根据端口判断,或手动指定)
var client = new NetUri("tcp://host:443").CreateRemote();
if (client is TcpSession tcp)
{
tcp.SslProtocol = SslProtocols.Tls12;
// tcp.Certificate = cert; // 客户端证书(如果服务端要求)
}
```
---
## 4. 管道与编解码器
### 4.1 管道机制
管道(`IPipeline`)是处理器链,Read/Write 返回值作为下一个处理器的输入,返回 `null` 截断管道:
```
接收:Socket → [Codec1.Read] → [Codec2.Read] → FireRead → OnReceive
发送:SendMessage → [Codec2.Write] → [Codec1.Write] → FireWrite → Socket
```
Open 正序传播,Close 逆序传播。先添加的在底层(靠近 Socket),后添加的在上层(靠近业务)。
### 4.2 内置编解码器
| 编解码器 | 基类 | 说明 | 典型场景 |
|---------|------|------|---------|
| `StandardCodec` | `MessageCodec` | 4字节头部(Flag+Seq+Length),支持请求-响应匹配 | 自定义 RPC 协议 |
| `LengthFieldCodec` | `MessageCodec` | 长度字段头部,可配置偏移和大小 | MQTT、通用二进制协议 |
| `JsonCodec` | `Handler` | JSON 文本编解码,不处理粘包 | 文本协议(通常与 StandardCodec 级联) |
| `SplitDataCodec` | `Handler` | 分隔符拆包(默认 `\r\n`) | 文本行协议 |
| `WebSocketCodec` | `Handler` | WebSocket 帧编解码 | WebSocket 通信 |
### 4.3 添加编解码器
```csharp
// 服务端添加
server.Add();
// 客户端添加
client.Add();
// 多层管道级联(按添加顺序组成链)
server.Add(); // 底层:粘包拆包 + 请求响应匹配
server.Add(); // 上层:JSON 编解码
```
### 4.4 StandardCodec 请求-响应
StandardCodec 使用 `DefaultMessage`,包含 Flag(1字节)、Sequence(1字节)、Length(2字节),
支持自动序列号分配和请求-响应匹配。
```csharp
// 服务端 Echo 示例
server.Add();
server.Received += (sender, e) =>
{
if (sender is INetSession session && e.Message is IPacket pk)
session.SendReply(pk, e); // 使用 SendReply 关联请求上下文
};
// 客户端请求-响应
client.Add();
var response = await client.SendMessageAsync(payload);
```
### 4.5 基类选择
| 基类 | 适用场景 | 典型代表 |
|------|---------|---------|
| `MessageCodec` | 需要粘包拆包和/或请求-响应匹配(内置 `IMatchQueue`、`Encode`/`Decode`) | `StandardCodec`、`LengthFieldCodec` |
| `Handler` | 简单转换、帧协议、文本协议(轻量,仅 `Read`/`Write`/`Open`/`Close`) | `JsonCodec`、`SplitDataCodec`、`WebSocketCodec` |
### 4.6 编解码器设计规范
#### 4.6.1 粘包拆包(PacketCodec 模式)
TCP 是字节流协议,必须处理粘包拆包。统一模式(完整实现见 4.7 模板):
1. 每个连接独立的 `PacketCodec` 实例,存储在 `ss["Codec"]` 中
2. 通过 `GetLength2` 委托告诉 `PacketCodec` 如何计算完整帧长度
3. `PacketCodec.Parse()` 返回完整帧列表,自动缓存不完整数据
**`GetLength2` 规范**(签名 `Int32 GetLength(ReadOnlySpan span)`):返回帧完整长度(含头部),数据不足时返回 `0`。
```csharp
public static Int32 GetLength(ReadOnlySpan span)
{
if (span.Length < 4) return 0;
var reader = new SpanReader(span) { IsLittleEndian = true };
reader.Advance(2);
return 4 + reader.ReadUInt16(); // 头部4字节 + 负载长度
}
```
#### 4.6.2 编码与内存管理
- **`ExpandHeader(size)`**:编码时优先复用负载缓冲区前置空间写入头部,零拷贝;空间不足时创建 `OwnerPacket`,原包作为 `Next` 链节点
- **`SpanWriter`**:配合 `ExpandHeader` 写入头部字段,注意 `IsLittleEndian` 大小端
- **兜底释放**:`MessageCodec.Write` 基类自动 `TryDispose`;`Handler` 子类需在 `Write` 的 `finally` 中手动调用
- **对象池**:`DefaultMessage.Rent()` / `DefaultMessage.Return()` 减少 GC 压力
#### 4.6.3 请求-响应匹配
`MessageCodec` 内置 `IMatchQueue`,流程:`Write` → `AddToQueue` 入队 → `Decode` 解码 → `Queue.Match` 按 `IsMatch` 匹配 → 唤醒 `SendMessageAsync` 的 `Task`。
- 重载 `AddToQueue`:控制哪些消息入队(通常只有请求消息)
- 重载 `IsMatch`:根据序列号等字段匹配请求和响应(见 4.7 模板)
- `QueueSize`:匹配队列大小,默认 256
- `Timeout`:等待响应超时,默认 30_000ms
- `UserPacket`:为 `true` 时向上层传递 `Payload` 而非整个 `IMessage`,用于编码器级联
#### 4.6.4 Close 清理
**必须**在 `Close` 中执行 `ss["Codec"] = null` 清理 `PacketCodec`,否则 `MemoryStream` 缓存泄漏(见 4.7 模板)。
#### 4.6.5 上下文扩展(IExtend)
管道处理器通过 `IExtend` 在会话/上下文上传递元数据:
| 键 | 用途 | 示例 |
|---|------|------|
| `"Codec"` | 每连接的 `PacketCodec` 实例 | 编解码器的 `Decode`/`Close` 中读写 |
| `"Flag"` | 数据类型标记 `DataKinds` | `JsonCodec.Write` 设置 → `StandardCodec.Write` 消费 |
| `"_raw_message"` | 原始请求消息 | `MessageCodec.Read` 设置 → `Write` 中创建响应时消费 |
| `"TaskSource"` | `TaskCompletionSource` | 框架内部,`AddToQueue` 消费 |
#### 4.6.6 多层管道级联
- 底层编解码器处理粘包拆包和请求-响应匹配,上层处理数据格式转换
- `UserPacket = true` 让底层向上层传递 `Payload` 而非整个 `IMessage`
- 上层通过 `ext["Flag"]` 向底层传递数据类型标记
### 4.7 自定义编解码器模板
#### 方式一:继承 MessageCodec(需要粘包/请求响应匹配)
```csharp
/// 自定义协议编解码器
public class MyCodec : MessageCodec
{
/// 编码消息为数据包
protected override Object? Encode(IHandlerContext context, MyMessage msg)
{
return msg.ToPacket();
}
/// 解码数据包为消息
protected override IEnumerable? Decode(IHandlerContext context, IPacket pk)
{
if (context.Owner is not IExtend ss) yield break;
if (ss["Codec"] is not PacketCodec pc)
{
ss["Codec"] = pc = new PacketCodec
{
GetLength2 = MyMessage.GetLength,
MaxCache = MaxCache,
Tracer = (context.Owner as ISocket)?.Tracer
};
}
foreach (var item in pc.Parse(pk))
{
var msg = new MyMessage();
if (msg.Read(item)) yield return msg;
}
}
/// 是否匹配响应
protected override Boolean IsMatch(Object? request, Object? response) =>
request is MyMessage req && response is MyMessage res
&& req.Sequence == res.Sequence;
/// 连接关闭时清理
public override Boolean Close(IHandlerContext context, String reason)
{
if (context.Owner is IExtend ss) ss["Codec"] = null;
return base.Close(context, reason);
}
}
```
#### 方式二:继承 Handler(简单转换/帧协议)
```csharp
/// 自定义帧编解码器
public class MyFrameCodec : Handler
{
/// 读取数据(接收时)
public override Object? Read(IHandlerContext context, Object message)
{
if (message is IPacket pk)
{
// 解码:二进制 → 业务对象
var frame = MyFrame.Parse(pk);
message = frame;
}
return base.Read(context, message);
}
/// 写入数据(发送时)
public override Object? Write(IHandlerContext context, Object message)
{
IPacket? owner = null;
if (message is MyFrame frame)
{
// 编码:业务对象 → 二进制
message = owner = frame.ToPacket();
}
try
{
return base.Write(context, message);
}
finally
{
owner.TryDispose(); // 兜底释放
}
}
/// 连接关闭时清理缓存
public override Boolean Close(IHandlerContext context, String reason)
{
if (context.Owner is IExtend ss) ss["Codec"] = null;
return base.Close(context, reason);
}
}
```
---
## 5. 常见模式与最佳实践
### 5.1 端口选择
- 测试代码使用端口 `0`(系统自动分配随机端口),避免端口冲突
- 正式服务指定固定端口
- 启动后可通过 `server.Port` 获取实际监听端口
### 5.2 协议选择
| 场景 | 推荐 |
|------|------|
| 可靠传输、长连接 | `NetType.Tcp` |
| 低延迟、广播、允许丢包 | `NetType.Udp` |
| 同时支持(默认) | `NetType.Unknown` |
| Web 浏览器通信 | `NetType.WebSocket` |
### 5.3 会话管理
- `UseSession = true`(默认):维护会话集合,支持群发、按 ID 查找
- `UseSession = false`:不维护会话集合,减少内存开销,适合海量短连接
- `SessionTimeout`:设置会话超时时间(秒),超时无数据自动断开
- 会话中通过 `Items` 字典存储自定义数据
### 5.4 日志分层
| 属性 | 用途 | 建议 |
|------|------|------|
| `Log` | 服务器应用层日志 | 始终设置 |
| `SessionLog` | 会话级别日志 | 调试时设置 |
| `SocketLog` | 底层 Socket 日志 | 仅 DEBUG 时设置 |
| `LogSend` / `LogReceive` | 收发数据内容日志 | 仅 DEBUG 时开启 |
| `Tracer` | 应用层 APM | 生产环境追踪 |
| `SocketTracer` | Socket 层 APM | 排查底层问题 |
### 5.5 资源释放
- 服务端:调用 `server.Stop(reason)` 或 `server.Dispose()`
- 客户端:调用 `client.Close(reason)` 或 `client.Dispose()`
- 会话自动随连接断开释放,无需手动管理
- `ISocketClient` 实现 `IDisposable`,推荐 `using` 模式
### 5.6 INetHandler 业务处理器
通过重载 `NetServer.CreateHandler` 注入自定义业务处理器:
```csharp
class MyServer : NetServer
{
/// 为会话创建网络数据处理器
public override INetHandler? CreateHandler(INetSession session) => new MyHandler();
}
```
处理器在会话 `Start` 时初始化,`OnReceive` 前调用 `Process`,适合前置协议解析。
---
## 6. 常见错误
- ❌ 在 `OnReceive` 中执行长时间阻塞操作(会影响其他连接的数据接收)
- ❌ 不加管道编解码器直接调用 `SendMessageAsync`(无法匹配响应)
- ❌ 混淆 `Send` 与 `SendMessage`:前者直接发原始数据,后者经过管道编码
- ❌ 混淆 `SendMessage` 与 `SendReply`:响应消息必须用 `SendReply` 关联请求上下文
- ❌ 忘记调用 `base.OnConnected()` / `base.OnDisconnected(reason)` / `base.OnReceive(e)`
- ❌ 在会话中使用 `Task.Result` 或 `Task.Wait()`(导致死锁和线程池饥饿)
- ❌ 使用固定端口编写测试(端口冲突),应使用 `Port = 0`
- ❌ 服务端 SSL 未指定证书
---
## 7. 完整示例
### 7.1 带 StandardCodec 的 Echo 服务
```csharp
// 服务端
var server = new NetServer
{
Port = 8080,
ProtocolType = NetType.Tcp,
Log = XTrace.Log,
};
server.Add();
server.Received += (sender, e) =>
{
if (sender is INetSession session && e.Message is IPacket pk)
session.SendReply(pk, e);
};
server.Start();
// 客户端
var client = new NetUri($"tcp://127.0.0.1:{server.Port}").CreateRemote();
client.Add();
client.Open();
var response = await client.SendMessageAsync(new ArrayPacket("Hello".GetBytes()));
```
### 7.2 自定义会话服务器
```csharp
class ChatServer : NetServer { }
class ChatSession : NetSession
{
protected override void OnConnected()
{
base.OnConnected();
Send($"欢迎 [{Remote}] 进入聊天室!\r\n");
}
protected override void OnReceive(ReceivedEventArgs e)
{
base.OnReceive(e);
var msg = e.Packet?.ToStr();
if (msg.IsNullOrEmpty()) return;
// 广播给所有在线用户
var host = (this as INetSession).Host;
host.SendAllMessage($"[{ID}] {msg}");
}
protected override void OnDisconnected(String reason)
{
base.OnDisconnected(reason);
WriteLog("用户离开:{0}", reason);
}
}
```
---
(完)
================================================
FILE: .github/workflows/publish-beta.yml
================================================
name: publish-beta
on:
push:
branches: [ master,dev ]
paths:
- 'NewLife.RocketMQ/**'
workflow_dispatch:
jobs:
build-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.x
7.x
8.x
9.x
10.x
- name: Build
run: |
dotnet pack --version-suffix $(date "+%Y.%m%d-beta%H%M") -c Release -o out NewLife.RocketMQ/NewLife.RocketMQ.csproj
- name: Publish
run: |
# dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://nuget.pkg.github.com/NewLifeX/index.json --api-key ${{ github.token }}
dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.nugetKey }}
================================================
FILE: .github/workflows/publish.yml
================================================
name: publish
on:
push:
tags: [ v* ]
workflow_dispatch:
jobs:
build-publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.x
7.x
8.x
9.x
10.x
- name: Build
run: |
dotnet pack -c Release -o out NewLife.RocketMQ/NewLife.RocketMQ.csproj
- name: Publish
run: |
# dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://nuget.pkg.github.com/NewLifeX/index.json --api-key ${{ github.token }}
dotnet nuget push ./out/*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.nugetKey }}
================================================
FILE: .github/workflows/test.yml
================================================
name: test
on:
push:
branches: [ '*' ]
pull_request:
branches: [ '*' ]
jobs:
build-test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup dotNET
uses: actions/setup-dotnet@v4
with:
dotnet-version: |
6.x
7.x
8.x
9.x
10.x
- name: Build
run: |
dotnet build -c Release
================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.userosscache
*.sln.docstates
# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
# Visual Studio 2015 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/
# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*
# NUNIT
*.VisualState.xml
TestResult.xml
# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c
# DNX
project.lock.json
project.fragment.lock.json
artifacts/
*_i.c
*_p.c
*_i.h
*.ilk
*.meta
*.obj
*.pch
*.pdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*.log
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Chutzpah Test files
_Chutzpah*
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb
# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap
# TFS 2012 Local Workspace
$tf/
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user
# JustCode is a .NET coding add-in
.JustCode
# TeamCity is a build add-in
_TeamCity*
# DotCover is a Code Coverage Tool
*.dotCover
# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*
# MightyMoose
*.mm.*
AutoTest.Net/
# Web workbench (sass)
.sass-cache/
# Installshield output folder
[Ee]xpress/
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html
# Click-Once directory
publish/
# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# TODO: Comment the next line if you want to checkin your web deploy settings
# but database connection strings (with potential passwords) will be unencrypted
#*.pubxml
*.publishproj
# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/
# NuGet Packages
*.nupkg
# The packages folder can be ignored because of Package Restore
**/packages/*
# except build/, which is used as an MSBuild target.
!**/packages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/packages/repositories.config
# NuGet v3's project.json files produces more ignoreable files
*.nuget.props
*.nuget.targets
# Microsoft Azure Build Output
csx/
*.build.csdef
# Microsoft Azure Emulator
ecf/
rcf/
# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!*.[Cc]ache/
# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
node_modules/
orleans.codegen.cs
# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/
# RIA/Silverlight projects
Generated_Code/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
# SQL Server files
*.mdf
*.ldf
# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
# Microsoft Fakes
FakesAssemblies/
# GhostDoc plugin setting file
*.GhostDoc.xml
# Node.js Tools for Visual Studio
.ntvs_analysis.dat
# Visual Studio 6 build log
*.plg
# Visual Studio 6 workspace options file
*.opt
# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions
# Paket dependency manager
.paket/paket.exe
paket-files/
# FAKE - F# Make
.fake/
# JetBrains Rider
.idea/
*.sln.iml
# CodeRush
.cr/
# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc
/BinTest
/BinTest - 副本/Config
/生产
/BinUnitTest
================================================
FILE: ChangeLog.md
================================================
# 更新日志
## v3.0.2026.0501 (2026-05-01)
### 问题修复
- **[fix]** 修复 Pop/Ack/ChangeInvisibleTime 操作缺少 `queueId` 参数导致服务端处理异常的问题
- **便利方法**:`MessageExt` 新增多个便利访问方法,简化消息属性读取
### 依赖更新
- 升级 NewLife.Core 依赖包到最新版本(2026-04-xx)
---
## v3.0.2026.0305 (2026-03-05)
### 云适配重构(重大版本)
- **架构重构**:全面升级为 v3.0 云适配架构,新增 `ICloudProvider` 接口统一阿里云、华为云、腾讯云适配
- **事务消息**:新增 RocketMQ 事务消息发布与回查接口,支持分布式事务场景
- **请求-应答模式**:新增 Request-Reply 同步调用模式,支持消息级 RPC
### gRPC 协议支持
- **gRPC 5.x Proxy**:新增 gRPC 协议支持,零依赖不引入第三方 Protobuf/gRPC 库
- **SpanReader/SpanWriter 重构**:将 gRPC 协议编解码器重构为基于 `SpanReader`/`SpanWriter` 的零分配实现,提升性能
- **gRPC Telemetry**:新增 gRPC Telemetry 链路追踪支持
### 新增功能
- **VIP 通道**:支持 VIP Channel 高优先级消息通道
- **批量确认**:支持批量 Ack 操作,减少网络往返
- **5.x MsgId**:支持 RocketMQ 5.x 消息 ID 格式生成与解析
- **客户端拉取超时**:新增 `Consumer.PullTimeout` 客户端侧应用层超时保护,防止 4.9.8 无响应导致消费线程永久阻塞
### 测试覆盖
- 新增 152 个单元测试,覆盖协议层、模型层、工具类等核心组件
- 完善架构文档与需求文档
---
## v2.7.2026.0301 (2026-03-01)
### 问题修复
- 新增`Consumer.PullTimeout`属性,默认值0表示自动取`SuspendTimeout+10_000ms`,作为客户端拉取消息的应用层超时保护,防止RocketMQ 4.9.8在SuspendTimeout后无响应导致消费线程永久阻塞
## v2.7.2026.0201 (2026-02-01)
### 依赖更新
- 升级 NewLife.Core 依赖包到最新版本(2026-01-24)
- 升级 NewLife.Core 依赖包(2026-01-14)
- 升级 NewLife.Core 依赖包(2026-01-12)
## v2.7.2026.0102 (2026-01-03)
初始发布版本
================================================
FILE: DLL/NewLife.Core.xml
================================================
NewLife.Core
服务类型
服务主函数
服务程序基类
显示名
描述
初始化
服务主函数
显示状态
显示菜单
添加菜单
开始工作
停止服务
服务管理线程封装
检查内存是否超标
是否超标重启
检查服务进程的总线程数是否超标
检查服务进程的句柄数是否超标
服务开始时间
检查自动重启
重启服务
服务启动事件
服务停止事件
在系统关闭时执行。 指定在系统关闭之前应该发生什么。
在计算机的电源状态已发生更改时执行。 这适用于便携式计算机,当他们进入挂起模式,这不是系统关闭相同。
在终端服务器会话中接收的更改事件时执行
看门狗要保护的服务
检查看门狗。
XAgent看门狗功能由管理线程完成,每分钟一次。
检查指定的任务是否已经停止,如果已经停止,则启动它。
安装、卸载 服务
是否安装
获取安装服务的命令参数
日志
写日志
服务助手
Exe程序名
启动、停止 服务
服务对象
执行一个命令
执行SC命令
是否已安装
是否已启动
是否已安装
是否已启动
写日志
服务设置
服务名
显示名
服务描述
最大占用内存。超过最大占用时,整个服务进程将会重启,以释放资源。默认8096M
最大线程数。超过最大占用时,整个服务进程将会重启,以释放资源。默认1000个
最大句柄数。超过最大占用时,整个服务进程将会重启,以释放资源。默认10000
自动重启时间。到达自动重启时间时,整个服务进程将会重启,以释放资源。默认0分,表示无限
看门狗,保护其它服务,每分钟检查一次。多个服务名逗号分隔
IP搜索
数据文件
获取IP地址
获取IP地址
数据流
析构
销毁
索引结构
缓存
默认缓存
名称
默认缓存时间。默认0秒表示不过期
获取和设置缓存,永不过期
缓存个数
所有键
构造函数
初始化配置
是否包含缓存项
设置缓存项
键
值
过期时间,秒。小于0时采用默认缓存时间
设置缓存项
键
值
过期时间
获取缓存项
键
批量移除缓存项
键集合
清空所有缓存项
设置缓存项有效期
键
过期时间,秒
获取缓存项有效期
键
批量获取缓存项
批量设置缓存项
过期时间,秒。小于0时采用默认缓存时间
获取列表
元素类型
键
获取哈希
元素类型
键
获取队列
元素类型
键
获取Set
添加,已存在时不更新
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
设置新值并获取旧值,原子操作
值类型
键
值
累加,原子操作
键
变化量
累加,原子操作
键
变化量
递减,原子操作
键
变化量
递减,原子操作
键
变化量
提交变更。部分提供者需要刷盘
申请分布式锁
多线程性能测试
随机读写
批量操作。默认0不分批
Memory性能测试[顺序],逻辑处理器 32 个 2,000MHz Intel(R) Xeon(R) CPU E5-2640 v2 @ 2.00GHz
测试 10,000,000 项, 1 线程
赋值 10,000,000 项, 1 线程,耗时 3,764ms 速度 2,656,748 ops
读取 10,000,000 项, 1 线程,耗时 1,296ms 速度 7,716,049 ops
删除 10,000,000 项, 1 线程,耗时 1,230ms 速度 8,130,081 ops
测试 20,000,000 项, 2 线程
赋值 20,000,000 项, 2 线程,耗时 3,088ms 速度 6,476,683 ops
读取 20,000,000 项, 2 线程,耗时 1,051ms 速度 19,029,495 ops
删除 20,000,000 项, 2 线程,耗时 1,011ms 速度 19,782,393 ops
测试 40,000,000 项, 4 线程
赋值 40,000,000 项, 4 线程,耗时 3,060ms 速度 13,071,895 ops
读取 40,000,000 项, 4 线程,耗时 1,023ms 速度 39,100,684 ops
删除 40,000,000 项, 4 线程,耗时 994ms 速度 40,241,448 ops
测试 80,000,000 项, 8 线程
赋值 80,000,000 项, 8 线程,耗时 3,124ms 速度 25,608,194 ops
读取 80,000,000 项, 8 线程,耗时 1,171ms 速度 68,317,677 ops
删除 80,000,000 项, 8 线程,耗时 1,199ms 速度 66,722,268 ops
测试 320,000,000 项, 32 线程
赋值 320,000,000 项, 32 线程,耗时 13,857ms 速度 23,093,021 ops
读取 320,000,000 项, 32 线程,耗时 1,950ms 速度 164,102,564 ops
删除 320,000,000 项, 32 线程,耗时 3,359ms 速度 95,266,448 ops
测试 320,000,000 项, 64 线程
赋值 320,000,000 项, 64 线程,耗时 9,648ms 速度 33,167,495 ops
读取 320,000,000 项, 64 线程,耗时 1,974ms 速度 162,107,396 ops
删除 320,000,000 项, 64 线程,耗时 1,907ms 速度 167,802,831 ops
测试 320,000,000 项,256 线程
赋值 320,000,000 项,256 线程,耗时 12,429ms 速度 25,746,238 ops
读取 320,000,000 项,256 线程,耗时 1,907ms 速度 167,802,831 ops
删除 320,000,000 项,256 线程,耗时 2,350ms 速度 136,170,212 ops
使用指定线程测试指定次数
次数
线程
随机读写
批量操作
读取测试
键
次数
线程
随机读写
批量操作
赋值测试
键
次数
线程
随机读写
批量操作
累加测试
键
次数
线程
随机读写
批量操作
删除测试
键
次数
线程
随机读写
已重载。
分布式锁
键
实例化
申请锁
销毁
缓存接口
名称
默认缓存时间。默认0秒表示不过期
获取和设置缓存,永不过期
缓存个数
所有键
是否包含缓存项
设置缓存项
键
值
过期时间,秒。小于0时采用默认缓存时间
设置缓存项
键
值
过期时间
获取缓存项
键
批量移除缓存项
键集合
清空所有缓存项
设置缓存项有效期
键
过期时间
获取缓存项有效期
键
批量获取缓存项
批量设置缓存项
过期时间,秒。小于0时采用默认缓存时间
获取列表
元素类型
键
获取哈希
元素类型
键
获取队列
元素类型
键
获取Set
添加,已存在时不更新
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
设置新值并获取旧值,原子操作
常常配合Increment使用,用于累加到一定数后重置归零,又避免多线程冲突。
值类型
键
值
累加,原子操作
键
变化量
累加,原子操作
键
变化量
递减,原子操作
键
变化量
递减,原子操作
键
变化量
提交变更。部分提供者需要刷盘
申请分布式锁
要锁定的key
多线程性能测试
随机读写
批量操作。默认0不分批
生产者消费者接口
生产添加
消费获取
默认字典缓存
缓存核心
容量。容量超标时,采用LRU机制删除,默认100_000
定时清理时间,默认60秒
实例化一个内存字典缓存
销毁
缓存项。原子计数
所有键。实际返回只读列表新实例,数据量较大时注意性能
初始化配置
获取或添加缓存项
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
是否包含缓存项
添加缓存项,已存在时更新
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
获取缓存项,不存在时返回默认值
键
批量移除缓存项
键集合
实际移除个数
清空所有缓存项
设置缓存项有效期
键
过期时间
设置是否成功
获取缓存项有效期,不存在时返回Zero
键
添加,已存在时不更新,常用于锁争夺
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
设置新值并获取旧值,原子操作
值类型
键
值
累加,原子操作
键
变化量
累加,原子操作
键
变化量
递减,原子操作
键
变化量
递减,原子操作
键
变化量
获取列表
获取哈希
获取队列
获取Set
基于HashSet,非线程安全
获取 或 添加 缓存项
缓存项
数值
过期时间
是否过期
访问时间
构造缓存项
设置数值和过期时间
更新访问时间并返回数值
设置过期时间
递增
递减
清理会话计时器
移除过期的缓存项
使用指定线程测试指定次数
次数
线程
随机读写
批量操作
生产者消费者
实例化内存队列
实例化内存队列
生产添加
消费获取
Redis缓存
创建指定服务器的实例
服务器地址。支持前面加上密码,@分隔
使用的数据库
创建指定服务器的实例,支持密码
服务器地址。支持前面加上密码,@分隔
密码
使用的数据库
服务器
密码
目标数据库。默认0
出错重试次数。如果出现协议解析错误,可以重试的次数,默认3
完全管道。读取操作是否合并进入管道,默认false
自动管道。管道操作达到一定数量时,自动提交,默认0
性能计数器
初始化
销毁
已重载。
为同一服务器创建不同Db的子级库
连接池
执行命令
返回类型
命令key,用于选择集群节点
回调函数
是否写入操作
开始管道模式
结束管道模式
要求结果。默认false
提交变更。处理某些残留在管道里的命令
缓存个数
所有键
单个实体项
键
值
过期时间,秒。小于0时采用默认缓存时间
获取单体
键
批量移除缓存项
键集合
清空所有缓存项
是否存在
键
设置缓存项有效期
键
过期时间
获取缓存项有效期
键
批量获取缓存项
批量设置缓存项
过期时间,秒。小于0时采用默认缓存时间
添加,已存在时不更新
值类型
键
值
过期时间,秒。小于0时采用默认缓存时间
设置新值并获取旧值,原子操作
值类型
键
值
累加,原子操作
键
变化量
累加,原子操作,乘以100后按整数操作
键
变化量
递减,原子操作
键
变化量
递减,原子操作,乘以100后按整数操作
键
变化量
性能测试
Redis性能测试[随机],批大小[100],逻辑处理器 40 个 2,400MHz Intel(R) Xeon(R) CPU E5-2640 v4 @ 2.40GHz
测试 100,000 项, 1 线程
赋值 100,000 项, 1 线程,耗时 418ms 速度 239,234 ops
读取 100,000 项, 1 线程,耗时 520ms 速度 192,307 ops
删除 100,000 项, 1 线程,耗时 125ms 速度 800,000 ops
测试 200,000 项, 2 线程
赋值 200,000 项, 2 线程,耗时 548ms 速度 364,963 ops
读取 200,000 项, 2 线程,耗时 549ms 速度 364,298 ops
删除 200,000 项, 2 线程,耗时 315ms 速度 634,920 ops
测试 400,000 项, 4 线程
赋值 400,000 项, 4 线程,耗时 694ms 速度 576,368 ops
读取 400,000 项, 4 线程,耗时 697ms 速度 573,888 ops
删除 400,000 项, 4 线程,耗时 438ms 速度 913,242 ops
测试 800,000 项, 8 线程
赋值 800,000 项, 8 线程,耗时 1,206ms 速度 663,349 ops
读取 800,000 项, 8 线程,耗时 1,236ms 速度 647,249 ops
删除 800,000 项, 8 线程,耗时 791ms 速度 1,011,378 ops
测试 4,000,000 项, 40 线程
赋值 4,000,000 项, 40 线程,耗时 4,848ms 速度 825,082 ops
读取 4,000,000 项, 40 线程,耗时 5,399ms 速度 740,877 ops
删除 4,000,000 项, 40 线程,耗时 6,281ms 速度 636,841 ops
测试 4,000,000 项, 64 线程
赋值 4,000,000 项, 64 线程,耗时 6,806ms 速度 587,716 ops
读取 4,000,000 项, 64 线程,耗时 5,365ms 速度 745,573 ops
删除 4,000,000 项, 64 线程,耗时 6,716ms 速度 595,592 ops
随机读写
批量操作
使用指定线程测试指定次数
次数
线程
随机读写
批量操作
日志
写日志
Redis客户端
以极简原则进行设计,每个客户端不支持并行命令处理,可通过多客户端多线程解决。
收发共用64k缓冲区,所以命令请求和响应不能超过64k。
客户端
内容类型
密码
是否已登录
登录时间
是否正在处理命令
销毁
异步请求
新建连接
发出请求
接收响应
发出请求
重置。干掉历史残留数据
执行命令。返回字符串、Packet、Packet[]
执行命令。返回基本类型、对象、对象数组
尝试转换类型
管道命令个数
开始管道模式
结束管道模式
要求结果
心跳
选择Db
验证密码
退出
获取信息
设置
超时时间
读取
批量设置
批量获取
数值转字节数组
字节数组转对象
字节数组转对象
获取命令对应的字节数组,全局缓存
日志
写日志
并行哈希集合
主要用于频繁添加删除而又要遍历的场合
是否空集合
元素个数
是否包含元素
尝试添加
尝试删除
字典缓存。当指定键的缓存项不存在时,调用委托获取值,并写入缓存。
常用匿名函数或者Lambda表达式作为委托。
键类型
值类型
过期时间。单位是秒,默认0秒,表示永不过期
定时清理时间,默认0秒,表示不清理过期项
容量。容量超标时,采用LRU机制删除,默认10_000
是否允许缓存控制,避免缓存穿透。默认false
查找数据的方法
实例化一个字典缓存
实例化一个字典缓存
实例化一个字典缓存
销毁
缓存项
数值
过期时间
是否过期
访问时间
更新访问时间并返回数值
重写索引器。取值时如果没有该项则返回默认值;赋值时如果已存在该项则覆盖,否则添加。
获取 GetOrAdd
获取 GetOrAdd
设置 AddOrUpdate
尝试添加,或返回旧值
扩展获取数据项,当数据项不存在时,通过调用委托获取数据项。线程安全。
键
获取值的委托,该委托以键作为参数
移除指定缓存项
清空
缓存项。原子计数
是否包含指定键
赋值到目标缓存
清理会话计时器
移除过期的缓存项
枚举
集群管理
资源列表
打开
关闭
关闭原因。便于日志分析
是否成功
从集群中获取资源
归还
集群异常
资源
实例化
集群助手
借助集群资源处理事务
对集群进行多次调用
对象池接口
对象池大小
获取
归还
清空
对象池扩展
字符串构建器池
归还一个字符串构建器到对象池
是否需要返回结果
字符串构建器池
初始容量。默认100个
最大容量。超过该大小时不进入池内,默认4k
创建
归还
内存流池
归还一个内存流到对象池
是否需要返回结果
内存流池
初始容量。默认1024个
最大容量。超过该大小时不进入池内,默认64k
创建
归还
可空字典。获取数据时如果指定键不存在可返回空而不是抛出异常
实例化一个可空字典
指定比较器实例化一个可空字典
实例化一个可空字典
获取或设置与指定的属性是否有脏数据。
资源池。支持空闲释放,主要用于数据库连接池和网络连接池
名称
空闲个数
繁忙个数
最大个数。默认100,0表示无上限
最小个数。默认1
空闲清理时间。最小个数之上的资源超过空闲时间时被清理,默认10s
完全空闲清理时间。最小个数之下的资源超过空闲时间时被清理,默认0s永不清理
基础空闲集合。只保存最小个数,最热部分
扩展空闲集合。保存最小个数以外部分
借出去的放在这
实例化一个资源池
销毁
数值
过期时间
借出
借出时是否可用
申请资源包装项,Dispose时自动归还到池中
归还
归还时是否可用
清空已有对象
销毁
创建实例
总请求数
成功数
新创建数
释放数
平均耗时。单位ms
日志
写日志
资源池包装项,自动归还资源到池中
数值
池
包装项
销毁
轻量级对象池。数组无锁实现,高性能
内部 1+N 的存储结果,保留最热的一个对象在外层,便于快速存取。
数组具有极快的查找速度,结构体确保没有GC操作。
对象池大小。默认CPU*2,初始化后改变无效
实例化对象池。默认大小CPU*2
获取
归还
清空
创建实例
具有是否已释放和释放后事件的接口
是否已经释放
被销毁时触发事件
具有销毁资源处理的抽象基类
/// <summary>子类重载实现资源释放逻辑时必须首先调用基类方法</summary>
/// <param name="disposing">从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)。
/// 因为该方法只会被调用一次,所以该参数的意义不太大。</param>
protected override void OnDispose(bool disposing)
{
base.OnDispose(disposing);
if (disposing)
{
// 如果是构造函数进来,不执行这里的代码
}
}
释放资源
是否已经释放
被销毁时触发事件
释放资源,参数表示是否由Dispose调用。该方法保证OnDispose只被调用一次!
子类重载实现资源释放逻辑时必须首先调用基类方法
从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)。
因为该方法只会被调用一次,所以该参数的意义不太大。
析构函数
如果忘记调用Dispose,这里会释放非托管资源
如果曾经调用过Dispose,因为GC.SuppressFinalize(this),不会再调用该析构函数
销毁助手。扩展方法专用
尝试销毁对象,如果有则调用
汉字拼音转换类
取拼音第一个字段
取拼音第一个字段
取拼音第一个字段
获取单字拼音
把汉字转换成拼音(全拼)
汉字字符串
转换后的拼音(全拼)字符串
系统设置。提供系统名称、版本等基本设置。
系统设置。提供系统名称、版本等基本设置。泛型基类,可继承扩展。
系统名称
系统版本
显示名称
公司
开发者模式
启用
安装时间
实例化
新建配置
系统主程序集
运行时
是否控制台。用于判断是否可以执行一些控制台操作。
是否Mono环境
是否Web环境
是否Windows环境
是否Linux环境
是否OSX环境
7Zip
实例化
压缩文件
解压缩文件
是否覆盖目标同名文件
日志
写日志
二叉树
遍历所有二叉树
构建表达式树
遍历全排列
从4种运算符中挑选3个运算符
立方根
数学运算
数据行
数据表
行索引
构造数据行
基于列索引访问
基于列名访问
读取指定行的字段值
数据表
数据列
数据列类型
数据行
总函数
读取数据
从数据流读取
读取头部
读取数据
读取
从文件加载
写入数据流
写入头部到数据流
写入数据部分到数据流
转数据包
保存到文件
读取指定行的字段值
尝试读取指定行的字段值
根据名称找字段序号
数据集
克隆
获取枚举
地理地址
名称
坐标
地址
行政区域编码
国家
省份
城市
区县
乡镇
乡镇编码
街道
级别
精确打点
可信度。[0-100]
已重载。
地理区域
编码
名称
父级
中心
边界
级别
已重载。
经纬度坐标
经度
纬度
经纬度坐标
经纬度坐标
已重载
数据帧接口
数据包
远程地址
消息
用户数据
具有扩展数据的接口
数据项
设置 或 获取 数据项
数据过滤器
下一个过滤器
对封包执行过滤器
过滤器上下文
封包
过滤器助手
在链条里面查找指定类型的过滤器
数据过滤器基类
下一个过滤器
对封包执行过滤器
执行过滤
返回是否执行下一个过滤器
数据包
数据
偏移
长度
下一个链式包
总长度
根据数据区实例化
根据数组段实例化
从可扩展内存流实例化,尝试窃取内存流内部的字节数组,失败后拷贝
因数据包内数组窃取自内存流,需要特别小心,避免多线程共用
获取/设置 指定位置的字节
设置新的数据区
数据区
偏移
字节个数
截取子数据区
相对偏移
字节个数
查找目标数组
目标数组
本数组起始偏移
本数组搜索个数
附加一个包到当前包链的末尾
返回字节数组。如果是完整数组直接返回,否则截取
不一定是全新数据,如果需要全新数据请克隆
从封包中读取指定数据
返回数据段
返回数据段集合
获取封包的数据流形式
把封包写入到数据流
把封包写入到目标数组
异步复制到目标数据流
深度克隆一份数据包,拷贝数据区
以字符串表示
字符串编码,默认URF-8
以十六进制编码表示
最大显示多少个字节。默认-1显示全部
分隔符
分组大小,为0时对每个字节应用分隔符,否则对每个分组使用
重载类型转换,字节数组直接转为Packet对象
重载类型转换,一维数组直接转为Packet对象
已重载
分页参数信息
获取 或 设置 排序字段,前台接收,便于做安全性校验
获取 或 设置 是否降序
获取 或 设置 页面索引。从1开始,默认1
如果设定了开始行,分页时将不再使用PageIndex
获取 或 设置 页面大小。默认20,若为0表示不分页
获取 或 设置 总记录数
获取 页数
获取 或 设置 组合起来的排序字句。如果没有设置则取Sort+Desc,后台设置,不经过安全性校验
获取 或 设置 开始行
如果设定了开始行,分页时将不再使用PageIndex
获取 或 设置 是否获取总记录数,默认false
获取 或 设置 状态。用于传递统计等数据
获取 或 设置 是否获取统计,默认false
实例化分页参数
通过另一个分页参数来实例化当前分页参数
从另一个分页参数拷贝到当前分页参数
获取表示分页参数唯一性的键值,可用作缓存键
泛型事件参数
参数
使用参数初始化
弹出
泛型事件参数
参数
参数2
使用参数初始化
弹出
泛型事件参数
参数
参数2
参数3
使用参数初始化
弹出
泛型事件参数
参数
参数2
参数3
参数4
使用参数初始化
弹出
弱引用Action
常见的事件和委托,都包括两部分:对象和方法,当然如果委托到静态方法上,对象是为空的。
如果把事件委托到某个对象的方法上,同时就间接的引用了这个对象,导致其一直无法被回收,从而造成内存泄漏。
弱引用Action,原理就是把委托拆分,然后弱引用对象部分,需要调用委托的时候,再把对象“拉”回来,如果被回收了,就没有必要再调用它的方法了。
目标对象。弱引用,使得调用方对象可以被GC回收
委托方法
经过包装的新的委托
取消注册的委托
是否只使用一次,如果只使用一次,执行委托后马上取消注册
是否可用
实例化
目标对象
目标方法
实例化
目标对象
目标方法
取消注册回调
是否一次性事件
实例化
事件处理器
使用事件处理器、取消注册回调、是否一次性事件来初始化
事件处理器
取消注册回调
是否一次性事件
调用委托
把弱引用事件处理器转换为普通事件处理器
已重载
X组件异常
初始化
初始化
初始化
初始化
初始化
初始化
异常事件参数
发生异常时进行的动作
异常
异常助手
是否对象已被释放异常
数学表达式
加法
减法
乘法
除法
实例化
计算运算符优先级
适配和替换
解逆波兰表达式
标准逆波兰表达式
逆波兰表达式的解
是否有效
转为浮点数
计算逆波兰表达式
最后压入数字堆栈的数字
首先压入数字堆栈的数字
操作运算符
返回计算结果
逆波兰表达式
左括号
右括号
连接符
空格
操作符数组
是否括号
是否括号
计算操作等级
是否括号匹配
适配器和替换
值
将中缀表达式转换为逆波兰表达式
标准中缀表达式
标准逆波兰表达式
是否有效
编译计算
与或非表达式
位与
位或
实例化
操作符等级
适配替换
容器
解逆波兰表达式
标准逆波兰表达式
逆波兰表达式的解
是否有效
所有掩码
停止话音播报
Http请求响应基类
内容长度
内容类型
头部集合
获取/设置 头部
过期时间
是否已完整
主体长度
分析第一行
创建请求响应包
创建头部
Http编解码器
写入数据
读取数据
Http消息
是否响应
是否有错
单向请求
头部数据
负载数据
根据请求创建配对的响应消息
从数据包中读取消息
是否成功
把消息转为封包
Http编码器
编码
解码参数
解码结果
转换为目标类型
创建请求
创建响应
解码 请求/响应
消息
服务动作
错误码
参数或结果
Http帮助类
创建请求包
创建响应包
分析头部
建立握手包
握手
分析WS数据包
创建WS请求包
Http请求
Http方法
资源路径
用户代理
是否压缩
保持连接
可接受内容
接受语言
引用路径
分析第一行
创建头部
Http响应
是否启用SSL
状态码
状态描述
分析第一行
创建头部
验证,如果失败则抛出异常
Http服务器
实例化
迷你Http客户端。不支持https和302跳转
客户端
内容类型
内容长度
保持连接
状态码
超时时间。默认15s
头部集合
销毁
异步请求
异步发出请求,并接收响应
同步请求
异步发出请求,并接收响应
构造请求头
解析响应
根据主机获取对象池
异步获取
地址
同步获取
Csv文件
支持整体读写以及增量式读写,目标是读写超大Csv文件
文件编码
分隔符。默认逗号
数据流实例化
Csv文件实例化
销毁
读取一行
读取所有行
写入全部
写入一行
编码助手
检测文件编码
文件名
检测文件编码
检测数据流编码
数据流
BOM检测失败时用于启发式探索的数据大小
检测字节数组编码
字节数组
检测BOM字节序
检测是否ASCII
启发式探测Unicode编码
是否可见ASCII
检测可能的UTF8序列长度
文件资源
释放文件
释放文件夹
释放文件夹
获取文件资源
Json配置文件基类
标准用法:TConfig.Current
配置实体类通过特性指定配置文件路径以及自动更新时间。
Current将加载配置文件,如果文件不存在或者加载失败,将实例化一个对象返回。
考虑到自动刷新,不提供LoadFile和SaveFile等方法,可通过扩展方法ToXmlFileEntity和ToXmlFile实现。
用户也可以通过配置实体类的静态构造函数修改基类的和来动态配置加载信息。
当前实例。通过置空可以使其重新加载。
一些设置。派生类可以在自己的静态构造函数中指定
是否调试
配置文件路径
重新加载时间。单位:毫秒
没有配置文件时是否保存新配置。默认true
配置文件
最后写入时间
过期时间。如果在这个时间之后再次访问,将检查文件修改时间
是否已更新。通过文件写入时间判断
设置过期重新加载配置的时间
是否新的配置文件
销毁
加载指定配置文件
从配置文件中读取完成后触发
保存到配置文件中去
保存到配置文件中去
异步保存
新创建配置文件时执行
Json配置文件特性
配置文件名
重新加载时间。单位:毫秒
指定配置文件名
指定配置文件名和重新加载时间(毫秒)
依托于动作的日志类
方法
使用指定方法否则动作日志
写日志
已重载
代码性能计时器
参考了老赵(http://www.cnblogs.com/jeffreyzhao/archive/2009/03/10/codetimer.html)和eaglet(http://www.cnblogs.com/eaglet/archive/2009/03/10/1407791.html)两位的作品
为了保证性能比较的公平性,采用了多种指标,并使用计时器重写等手段来避免各种不必要的损耗
计时
次数
需要计时的委托
是否需要预热
计时,并用控制台输出行
标题
次数
需要计时的委托
是否需要预热
显示头部
次数
迭代方法,如不指定,则使用Time(int index)
是否显示控制台进度
进度
CPU周期
线程时间,单位是ms
GC代数
执行时间
实例化一个代码计时器
计时核心方法,处理进程和线程优先级
真正的计时
执行一次迭代,预热所有方法
迭代前执行,计算时间
每一次迭代,计算时间
迭代后执行,计算时间
基准时间
已重载。输出依次分别是:执行时间、CPU线程时间、时钟周期、GC代数
复合日志提供者,多种方式输出
日志提供者集合
实例化
实例化
实例化
添加一个日志提供者
删除日志提供者
写日志
从复合日志提供者中提取指定类型的日志提供者
已重载。
控制台输出日志
是否使用多种颜色,默认使用
写日志
已重载。
性能计数器接口
数值
次数
速度
平均耗时,单位us
增加
增加的数量
耗时,单位us
计数器助手
开始计时
结束计时
日志接口
写日志
日志级别
格式化字符串
格式化参数
调试日志
格式化字符串
格式化参数
信息日志
格式化字符串
格式化参数
警告日志
格式化字符串
格式化参数
错误日志
格式化字符串
格式化参数
严重错误日志
格式化字符串
格式化参数
是否启用日志
日志等级,只输出大于等于该级别的日志,默认Info,打开NewLife.Debug时默认为最低的Debug
日志基类。提供日志的基本实现
调试日志
格式化字符串
格式化参数
信息日志
格式化字符串
格式化参数
警告日志
格式化字符串
格式化参数
错误日志
格式化字符串
格式化参数
严重错误日志
格式化字符串
格式化参数
写日志
写日志
格式化参数,特殊处理异常和时间
是否启用日志。默认true
日志等级,只输出大于等于该级别的日志,默认Info,打开NewLife.Debug时默认为最低的Debug
空日志实现
输出日志头,包含所有环境信息
日志等级
打开所有日志记录
最低调试。细粒度信息事件对调试应用程序非常有帮助
普通消息。在粗粒度级别上突出强调应用程序的运行过程
警告
错误
严重错误
关闭所有日志记录
网络日志
网络套接字
远程服务器地址
实例化网络日志。默认广播到514端口
指定日志服务器地址来实例化网络日志
销毁
写日志
性能计数器。次数、TPS、平均耗时
是否启用。默认true
数值
次数
耗时,单位us
销毁
增加
增加的数量
耗时,单位us
采样间隔,默认1000毫秒
持续采样时间,默认60秒
当前速度
最大速度
最后一个采样周期的平均耗时,单位us
持续采样时间内的最大平均耗时,单位us
定期采样,保存最近60组到数组队列里面
已重载。输出统计信息
文本控件输出日志
文本控件
最大行数,超过该行数讲清空文本控件。默认1000行
写日志
在WinForm控件上输出日志,主要考虑非UI线程操作
不是常用功能,为了避免干扰常用功能,保持UseWinForm开头
要绑定日志输出的WinForm控件
日志
最大行数
文本文件日志类。提供向文本文件写日志的能力
2015-06-01 为了继承TextFileLog,增加了无参构造函数,修改了异步写日志方法为虚方法,可以进行重载
该构造函数没有作用,为了继承而设置
每个目录的日志实例应该只有一个,所以采用静态创建
日志目录或日志文件路径
每个目录的日志实例应该只有一个,所以采用静态创建
日志目录或日志文件路径
销毁
日志文件
日志目录
日志文件格式
是否当前进程的第一次写日志
初始化日志记录文件
写文件
关闭文件
写日志
已重载。
统计代码的时间消耗
名称
最大时间。毫秒
日志输出
指定最大执行时间来构造一个代码时间统计
析构
开始
停止
跟踪流。包装一个基础数据流,主要用于重写Read/Write等行为,跟踪程序操作数据流的过程
基础流
跟踪的成员
是否小端字节序。x86系列则采用Little-Endian方式存储数据;网络协议都是Big-Endian;
网络协议都是Big-Endian;
Java编译的都是Big-Endian;
Motorola的PowerPC是Big-Endian;
x86系列则采用Little-Endian方式存储数据;
ARM同时支持 big和little,实际应用中通常使用Little-Endian。
显示位置的步长,位移超过此长度后输出位置。默认16,设为0不输出位置
写入
缓冲区
偏移
数量
写入一个字节
数值
读取
缓冲区
偏移
数量
读取一个字节
异步开始读
缓冲区
偏移
数量
异步开始写
缓冲区
偏移
数量
异步读结束
异步写结束
设置流位置
偏移
关闭数据流
刷新缓冲区
设置长度
数值
可读
可搜索
可超时
可写
可读
读写超时
长度
位置
实例化跟踪流
实例化跟踪流
操作时触发
是否使用控制台
编码
写日志事件参数
日志等级
日志信息
异常
时间
线程编号
是否线程池线程
是否Web线程
线程名
任务编号
实例化一个日志事件参数
线程专有实例。线程静态,每个线程只用一个,避免GC浪费
初始化为新日志
日志等级
返回自身,链式写法
初始化为新日志
日志
异常
返回自身,链式写法
已重载。
设置当前线程输出日志时的线程名
日志类,包含跟踪调试功能
该静态类包括写日志、写调用栈和Dump进程内存等调试功能。
默认写日志到文本文件,可通过修改属性来增加日志输出方式。
对于控制台工程,可以直接通过UseConsole方法,把日志输出重定向为控制台输出,并且可以为不同线程使用不同颜色。
文本文件日志
日志提供者,默认使用文本文件日志
输出日志
信息
写日志
输出异常日志
异常信息
2012.11.05 修正初次调用的时候,由于同步BUG,导致Log为空的问题。
使用控制台输出日志,只能调用一次
是否使用颜色,默认使用
是否同时使用文件日志,默认使用
拦截WinForm异常并记录日志,可指定是否用显示。
发为捕获异常时,是否显示提示,默认显示
在WinForm控件上输出日志,主要考虑非UI线程操作
不是常用功能,为了避免干扰常用功能,保持UseWinForm开头
要绑定日志输出的WinForm控件
是否同时使用文件日志,默认使用
最大行数
控件绑定到日志,生成混合日志
是否调试。
文本日志目录
临时目录
输出核心库和启动程序的版本号
输出程序集版本
标准消息SRMP
标准网络封包协议:1 Flag + 1 Sequence + 2 Length + N Payload
1个字节标识位,标识请求、响应、错误、加密、压缩等;
1个字节序列号,用于请求响应包配对;
2个字节数据长度N,小端,指示后续负载数据长度(不包含头部4个字节),解决粘包问题;
N个字节负载数据,数据内容完全由业务决定,最大长度65535=64k。
如:
Open => OK
01-01-04-00-"Open" => 81-01-02-00-"OK"
标记位
序列号,匹配请求和响应
根据请求创建配对的响应消息
从数据包中读取消息
是否成功
把消息转为封包
获取数据包长度
消息摘要
消息命令
是否响应
是否有错
单向请求
负载数据
根据请求创建配对的响应消息
从数据包中读取消息
是否成功
把消息转为封包
消息命令基类
是否响应
是否有错
单向请求
负载数据
根据请求创建配对的响应消息
从数据包中读取消息
是否成功
把消息转为封包
收到消息时的事件参数
数据包
消息
用户数据。比如远程地址等
数据包编码器
缓存流
获取长度的委托
最后一次解包成功,而不是最后一次接收
缓存有效期。超过该时间后仍未匹配数据包的缓存数据将被抛弃
最大缓存待处理数据。默认0无限制
分析数据流,得到一帧数据
待分析数据包
检查缓存
无锁并行编程模型
独立线程轮询消息队列,简单设计避免影响默认线程池。
适用于任务颗粒较大的场合,例如IO操作。
添加消息,驱动内部处理
消息
发送者
返回待处理消息数
Actor上下文
发送者
消息
无锁并行编程模型
独立线程轮询消息队列,简单设计避免影响默认线程池。
名称
是否启用
受限容量。最大可堆积的消息数
批大小。每次处理消息数,默认1,大于1表示启用批量处理模式
存放消息的邮箱。默认FIFO实现,外部可覆盖
实例化
销毁
已重载。显示名称
通知开始处理
添加消息时自动触发
开始时,返回执行线程包装任务,默认LongRunning
通知停止添加消息,并等待处理完成
添加消息,驱动内部处理
消息
发送者
返回待处理消息数
循环消费消息
循环消费消息
处理消息
上下文
批量处理消息
上下文集合
延迟队列。缓冲合并对象,批量处理
借助实体字典,缓冲实体对象,定期给字典换新,实现批量处理。
有可能外部拿到对象后,正在修改,内部恰巧执行批量处理,导致外部的部分修改未能得到处理。
解决办法是增加一个提交机制,外部用完后提交修改,内部需要处理时,等待一个时间。
名称
实体字典
跟踪数。达到该值时输出跟踪日志,默认1000
周期。默认10_000毫秒
最大个数。超过该个数时,进入队列将产生堵塞。默认100_000
批大小。默认5_000
等待借出对象确认修改的时间,默认3000ms
保存速度,每秒保存多少个实体
是否异步处理。true表示异步处理,共用DQ定时调度;false表示同步处理,独立线程
合并保存的总次数
实例化
销毁。统计队列销毁时保存数据
初始化
初始化
尝试添加
获取 或 添加 实体对象,在外部修改对象值
外部正在修改对象时,内部不允许执行批量处理
等待确认修改的借出对象数
提交对象的修改,外部不再使用该对象
当前缓存个数
定时处理全部数据
处理一批
发生错误
认证用户接口,具有登录验证、注册、在线等基本信息
密码
在线
登录次数
最后登录
最后登录IP
注册时间
注册IP
保存
用户接口工具类
比较密码相等
比较密码MD5
比较密码RC4
保存登录信息
保存注册信息
用于创建对象的工厂接口
创建对象实例
反射创建对象的工厂
创建对象实例
处理器
上一个处理器
下一个处理器
读取数据,返回结果作为下一个处理器消息
上下文
消息
写入数据,返回结果作为下一个处理器消息
上下文
消息
打开连接
上下文
关闭连接
上下文
原因
发生错误
上下文
异常
处理器
上一个处理器
下一个处理器
读取数据,返回结果作为下一个处理器消息
上下文
消息
写入数据,返回结果作为下一个处理器消息
上下文
消息
打开连接
上下文
关闭连接
上下文
原因
发生错误
上下文
异常
处理器上下文
管道
上下文拥有者
读取管道过滤后最终处理消息
写入管道过滤后最终处理消息
处理器上下文
管道
上下文拥有者
数据项
设置 或 获取 数据项
读取管道过滤后最终处理消息
写入管道过滤后最终处理消息
用户接口
编号
名称
昵称
启用
对象容器接口
1,如果容器里面没有这个类型,则返回空;
2,如果容器里面包含这个类型,返回单例;
3,如果容器里面包含这个类型,创建对象返回多实例;
4,如果有带参数构造函数,则从容器内获取各个参数的实例,最后创建对象返回。
这里有一点跟大多数对象容器非常不同,其它对象容器会控制对象的生命周期,在对象不再使用时收回到容器里面。
这里的对象容器主要是为了用于解耦,所以只有最简单的功能实现。
代码注册的默认优先级是0;
配置注册的默认优先级是1;
自动注册的外部实现(非排除项)的默认优先级是1,排除项的优先级是0;
所以,配置注册的优先级最高
注册类型和名称
接口类型
实现类型
实例
标识
优先级
遍历所有程序集的所有类型,自动注册实现了指定接口或基类的类型。如果没有注册任何实现,则默认注册第一个排除类型
自动注册一般用于单实例功能扩展型接口
接口或基类
要排除的类型,一般是内部默认实现
解析类型指定名称的实例
接口类型
标识
解析类型指定名称的实例
接口类型
标识
解析接口指定名称的实现类型
接口类型
标识
解析接口所有已注册的对象映射
接口类型
对象映射接口
名称
实现类型
对象实例
管道。进站顺序,出站逆序
头部处理器
尾部处理器
添加处理器到开头
处理器
添加处理器到末尾
处理器
添加处理器到指定名称之前
基准处理器
处理器
添加处理器到指定名称之后
基准处理器
处理器
删除处理器
处理器
读取数据,返回结果作为下一个处理器消息
上下文
消息
写入数据,返回结果作为下一个处理器消息
上下文
消息
打开连接
上下文
关闭连接
上下文
原因
发生错误
上下文
异常
管道。进站顺序,出站逆序
头部处理器
尾部处理器
添加处理器到开头
处理器
添加处理器到末尾
处理器
添加处理器到指定名称之前
基准处理器
处理器
添加处理器到指定名称之后
基准处理器
处理器
删除处理器
处理器
读取数据,顺序过滤消息,返回结果作为下一个处理器消息
上下文
消息
写入数据,逆序过滤消息,返回结果作为下一个处理器消息
上下文
消息
打开连接
上下文
关闭连接
上下文
原因
发生错误
上下文
异常
枚举器
通用插件接口
为了方便构建一个简单通用的插件系统,先规定如下:
1,负责加载插件的宿主,在加载插件后会进行插件实例化,此时可在插件构造函数中做一些事情,但不应该开始业务处理,因为宿主的准备工作可能尚未完成
2,宿主一切准备就绪后,会顺序调用插件的Init方法,并将宿主标识传入,插件通过标识区分是否自己的目标宿主。插件的Init应尽快完成。
3,如果插件实现了接口,宿主最后会清理资源。
初始化
插件宿主标识
服务提供者
返回初始化是否成功。如果当前宿主不是所期待的宿主,这里返回false
插件特性。用于判断某个插件实现类是否支持某个宿主
插件宿主标识
实例化
插件管理器
宿主标识,用于供插件区分不同宿主
宿主服务提供者
插件集合
日志提供者
实例化一个插件管理器
使用宿主对象实例化一个插件管理器
子类重载实现资源释放逻辑时必须首先调用基类方法
从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)。
因为该方法只会被调用一次,所以该参数的意义不太大。
加载插件。此时是加载所有插件,无法识别哪些是需要的
开始初始化。初始化之后,不属于当前宿主的插件将会被过滤掉
服务接口。
服务代理XAgent可以附加代理实现了IServer接口的服务。
开始
停止
关闭原因。便于日志分析
实现 接口的对象容器
1,如果容器里面没有这个类型,则返回空;
2,如果容器里面包含这个类型,返回单例;
3,如果容器里面包含这个类型,创建对象返回多实例;
4,如果有带参数构造函数,则从容器内获取各个参数的实例,最后创建对象返回。
这里有一点跟大多数对象容器非常不同,其它对象容器会控制对象的生命周期,在对象不再使用时收回到容器里面。
这里的对象容器主要是为了用于解耦,所以只有最简单的功能实现。
代码注册的默认优先级是0;
配置注册的默认优先级是1;
自动注册的外部实现(非排除项)的默认优先级是1,排除项的优先级是0;
所以,配置注册的优先级最高
当前容器
初始化一个对象容器实例,自动从配置文件中加载注册
不存在又不添加时返回空列表
名称
实现类型
优先级
实例
注册
接口类型
实现类型
实例
标识
优先级
遍历所有程序集的所有类型,自动注册实现了指定接口或基类的类型。如果没有注册任何实现,则默认注册第一个排除类型
自动注册一般用于单实例功能扩展型接口
接口或基类
要排除的类型,一般是内部默认实现
解析类型指定名称的实例
接口类型
标识
解析类型指定名称的实例
接口类型
标识
解析接口指定名称的实现类型
接口类型
标识
解析接口所有已注册的对象映射
接口类型
已重载。
消息匹配队列接口。用于把响应数据包配对到请求包
加入请求队列
拥有者
请求消息
超时取消时间
任务源
检查请求队列是否有匹配该响应的请求
拥有者
响应消息
任务结果
用于检查匹配的回调
清空队列
消息匹配队列。子类可重载以自定义请求响应匹配逻辑
加入请求队列
拥有者
请求的数据
超时取消时间
任务源
检查请求队列是否有匹配该响应的请求
拥有者
响应消息
任务结果
用于检查匹配的回调
定时检查发送队列,超时未收到响应则重发
清空队列
长度字段作为头部
长度所在位置
长度占据字节数,1/2/4个字节,0表示压缩编码整数,默认2
过期时间,超过该时间后按废弃数据处理,默认500ms
编码
解码
连接关闭时,清空粘包编码器
消息封包
消息队列。用于匹配请求响应包
调用超时时间。默认30_000ms
使用数据包,写入时数据包转消息,读取时消息自动解包返回数据负载。默认true
写入数据
编码
加入队列
连接关闭时,清空粘包编码器
读取数据
解码
是否匹配响应
从数据流中获取整帧数据长度
数据帧长度(包含头部长度位)
按指定分割字节来处理粘包的处理器
默认以"0x0D 0x0A"即换行来分割,分割的包包含分割字节本身,使用时请注意。
默认分割方式:ISocket.Add<SplitDataCodec>()
自定义分割方式:ISocket.Add(new SplitDataHandler { SplitData = 自定义分割字节数组 })
自定义最大缓存大小方式:ISocket.Add(new SplitDataHandler { MaxCacheDataLength = 2048 })
自定义方式:ISocket.Add(new SplitDataHandler { MaxCacheDataLength = 2048, SplitData = 自定义分割字节数组 })
粘包分割字节数据(默认0x0D,0x0A)
最大缓存待处理数据(字节)
读取数据
连接关闭时,清空粘包编码器
解码
包
获取包含分割字节在内的数据长度
标准网络封包。头部4字节定长
写入数据
加入队列
解码
是否匹配响应
连接关闭时,清空粘包编码器
网络服务会话接口
所有应用服务器以会话作为业务处理核心。
应用服务器收到新会话请求后,通过启动一个会话处理。
会话进行业务处理的过程中,可以通过多个Send方法向客户端发送数据。
编号
主服务
Socket服务器。当前通讯所在的Socket服务器,其实是TcpServer/UdpServer
客户端。跟客户端通讯的那个Socket,其实是服务端TcpSession/UdpSession
客户端地址
开始会话处理。
发送数据
数据包
发送数据流
发送字符串
异步发送并等待响应
数据到达事件
会话事件参数
会话
基础Socket接口
封装所有基础接口的共有特性!
核心设计理念:事件驱动,接口统一,简单易用!
异常处理理念:确保主流程简单易用,特殊情况的异常通过事件处理!
名称。主要用于日志输出
基础Socket对象
本地地址
端口
管道
是否抛出异常,默认false不抛出。Send/Receive时可能发生异常,该设置决定是直接抛出异常还是通过事件
异步处理接收到的数据。
异步处理有可能造成数据包乱序,特别是Tcp。true利于提升网络吞吐量。false避免拷贝,提升处理速度
发送统计
接收统计
日志提供者
是否输出发送日志。默认false
是否输出接收日志。默认false
已重载。日志加上前缀
错误发生/断开连接时
远程通信Socket,仅具有收发功能
标识
远程地址
通信开始时间
最后一次通信时间,主要表示会话活跃时间,包括收发
缓冲区大小
发送数据
目标地址由决定
数据包
是否成功
接收数据。阻塞当前线程等待返回
数据到达事件
异步发送数据并等待响应
消息
发送消息
消息
处理数据帧
数据帧
远程通信Socket扩展
获取统计信息
发送数据流
会话
数据流
返回是否成功
发送字符串
会话
要发送的字符串
文本编码,默认null表示UTF-8编码
返回自身,用于链式写法
异步多次发送数据
会话
数据包
次数
间隔
接收字符串
会话
文本编码,默认null表示UTF-8编码
添加处理器
会话
添加处理器
会话
处理器
Socket客户端
具备打开关闭
超时。默认3000ms
是否活动
打开
是否成功
关闭
关闭原因。便于日志分析
是否成功
打开后触发。
关闭后触发。可实现掉线重连
Socket服务器接口
是否活动
会话超时时间。默认20*60秒
对于每一个会话连接,如果超过该时间仍然没有收到任何数据,则断开会话连接。
会话统计
会话集合。用地址端口作为标识,业务应用自己维持地址端口与业务主键的对应关系。
新会话时触发
服务端通信Socket扩展
获取统计信息
用于与对方进行通讯的Socket会话,仅具有收发功能,也专用于上层应用收发数据
Socket会话发送数据不需要指定远程地址,因为内部已经具有。
接收数据时,Tcp接收全部数据,而Udp只接受来自所属远方的数据。
Socket会话不具有连接和断开的能力,所以需要外部连接好之后再创建Socket会话。
但是会话可以销毁,来代替断开。
对于Udp额外创建的会话来说,仅仅销毁会话而已。
所以,它必须具有收发数据的能力。
Socket服务器。当前通讯所在的Socket服务器,其实是TcpServer/UdpServer
会话事件参数
会话
帧数据传输接口
实现者确保数据以包的形式传输,屏蔽数据的粘包和拆包
超时
打开
关闭
写入数据
数据包
读取数据
数据到达事件
网络异常
初始化
初始化
初始化
初始化
初始化
网络处理器上下文
远程连接
数据帧
读取管道过滤后最终处理消息
写入管道过滤后最终处理消息
网络服务器。可同时支持多个Socket服务器,同时支持IPv4和IPv6,同时支持Tcp和Udp
网络服务器模型,所有网络应用服务器可以通过继承该类实现。
该类仅实现了业务应用对网络流的操作,与具体网络协议无关。
收到请求后,会建立会话,并加入到会话集合中,然后启动会话处理;
快速用法:
指定端口后直接,NetServer将同时监听Tcp/Udp和IPv4/IPv6(会检查是否支持)四个端口。
简单用法:
重载方法来创建一个SocketServer并赋值给属性,将会在时首先被调用。
标准用法:
使用方法向网络服务器添加Socket服务,其中第一个将作为默认Socket服务。
如果Socket服务集合为空,将依据地址、端口、地址族、协议创建默认Socket服务。
如果地址族指定为IPv4和IPv6以外的值,将同时创建IPv4和IPv6两个Socket服务;
如果协议指定为Tcp和Udp以外的值,将同时创建Tcp和Udp两个Socket服务;
默认情况下,地址族和协议都是其它值,所以一共将会创建四个Socket服务(Tcp、Tcpv6、Udp、Udpv6)。
服务名
本地结点
端口
协议类型
寻址方案
服务器集合
服务器。返回服务器集合中的第一个服务器
是否活动
会话超时时间。默认0秒,使用SocketServer默认值
对于每一个会话连接,如果超过该时间仍然没有收到任何数据,则断开会话连接。
管道
使用会话集合,允许遍历会话。默认true
会话统计
发送统计
接收统计
是否输出发送日志。默认false
是否输出接收日志。默认false
用户会话数据
获取/设置 用户会话数据
实例化一个网络服务器
通过指定监听地址和端口实例化一个网络服务器
通过指定监听地址和端口实例化一个网络服务器
通过指定监听地址和端口,还有协议,实例化一个网络服务器,默认支持Tcp协议和Udp协议
已重载。释放会话集合等资源
添加Socket服务器
添加是否成功
同时添加指定端口的IPv4和IPv6服务器,如果协议不是指定的Tcp或Udp,则同时添加Tcp和Udp服务器
确保建立服务器
添加处理器
添加处理器
处理器
开始服务
开始时调用的方法
停止服务
关闭原因。便于日志分析
停止时调用的方法
新会话,对于TCP是新连接,对于UDP是新客户端
某个会话的数据到达。sender是ISocketSession
接受连接时,对于Udp是收到数据时(同时触发OnReceived)。
收到连接时,建立会话,并挂接数据接收和错误处理事件
收到数据时
收到数据时,最原始的数据处理,但不影响会话内部的数据处理
错误发生/断开连接时。sender是ISocketSession
触发异常
会话集合。用自增的数字ID作为标识,业务应用自己维持ID与业务主键的对应关系。
会话数
最高会话数
添加会话。子类可以在添加会话前对会话进行一些处理
创建会话
根据会话ID查找会话
异步群发
创建Tcp/Udp、IPv4/IPv6服务
获取统计信息
日志提供者
用于内部Socket服务器的日志提供者
用于网络会话的日志提供者
日志前缀
写日志
输出错误日志
已重载。
网络服务器
创建会话
获取指定标识的会话
网络服务的会话
网络服务类型
主服务
网络服务的会话
实际应用可通过重载OnReceive实现收到数据时的业务逻辑。
编号
主服务
客户端。跟客户端通讯的那个Socket,其实是服务端TcpSession/UdpServer
服务端
客户端地址
用户会话数据
获取/设置 用户会话数据
开始会话处理。
子类重载实现资源释放逻辑时必须首先调用基类方法
从Dispose调用(释放所有资源)还是析构函数调用(释放非托管资源)
收到客户端发来的数据,触发事件,重载者可直接处理数据
数据到达事件
发送数据
数据包
发送数据流
发送字符串
异步发送并等待响应
错误处理
日志提供者
是否记录会话日志
日志前缀
写日志
输出错误日志
已重载。
协议类型
未知协议
传输控制协议
用户数据报协议
Http协议
Https协议
WebSocket协议
网络资源标识,指定协议、地址、端口、地址族(IPv4/IPv6)
仅序列化和,其它均是配角!
有可能代表主机域名,而指定主机IP地址。
协议类型
主机
地址
端口
终结点
是否Tcp协议
是否Udp协议
实例化
实例化
实例化
实例化
实例化
分析
分析地址
主机地址
已重载。
重载类型转换,字符串直接转为NetUri对象
收到数据时的事件参数
数据包
远程地址
解码后的消息
用户数据
串口配置
串口名
波特率
数据位
停止位
奇偶校验
文本编码
编码
十六进制显示
十六进制自动换行
十六进制发送
最后更新时间
扩展数据
DtrEnable
RtsEnable
BreakState
串口传输
标准例程:
var st = new SerialTransport();
st.PortName = "COM65"; // 通讯口
st.FrameSize = 16; // 数据帧大小
st.Received += (s, e) =>
{
Console.WriteLine("收到 {0}", e.ToHex());
};
// 开始异步操作
st.Open();
//var buf = "01080000801A".ToHex();
var buf = "0111C02C".ToHex();
for (int i = 0; i < 100; i++)
{
Console.WriteLine("发送 {0}", buf.ToHex());
st.Send(buf);
Thread.Sleep(1000);
}
串口对象
端口名称。默认COM1
波特率。默认115200
奇偶校验位。默认None
数据位。默认8
停止位。默认One
超时时间。超过该大小未收到数据,说明是另一帧。默认10ms
描述信息
字节超时。数据包间隔,默认20ms
串口传输
销毁
确保创建
打开
关闭
写入数据
数据包
异步发送数据并等待响应
接收数据
处理收到的数据。默认匹配同步接收委托
数据到达事件
断开时触发,可能是人为断开,也可能是串口链路断开
检查串口是否已经断开
FX串口异步操作有严重的泄漏缺陷,如果外部硬件长时间断开,
SerialPort.IsOpen检测不到,并且会无限大占用内存。
获取带有描述的串口名,没有时返回空数组
获取串口列表,名称和描述
从串口列表选择串口,支持自动选择关键字
串口名称或者描述符的关键字
日志对象
输出日志
已重载
会话基类
标识
名称
本地绑定信息
端口
远程结点地址
超时。默认3000ms
是否活动
底层Socket
是否抛出异常,默认false不抛出。Send/Receive时可能发生异常,该设置决定是直接抛出异常还是通过事件
发送数据包统计信息
接收数据包统计信息
通信开始时间
最后一次通信时间,主要表示活跃时间,包括收发
是否使用动态端口。如果Port为0则为动态端口
最大并行接收数。Tcp默认1,Udp默认CPU*1.6,0关闭异步接收使用同步接收
异步处理接收到的数据,Tcp默认false,Udp默认true。
异步处理有可能造成数据包乱序,特别是Tcp。true利于提升网络吞吐量。false避免拷贝,提升处理速度
缓冲区大小。默认8k
构造函数,初始化默认名称
销毁
已重载。
打开
是否成功
打开
检查是否动态端口。如果是动态端口,则把随机得到的端口拷贝到Port
关闭
关闭原因。便于日志分析
是否成功
关闭
关闭原因。便于日志分析
打开后触发。
关闭后触发。可实现掉线重连
直接发送数据包 Byte[]/Packet
目标地址由决定
数据包
是否成功
发送数据
目标地址由决定
数据包
是否成功
接收数据
当前异步接收个数
开始异步接收
是否成功
释放一个事件参数
用一个事件参数来开始异步接收
事件参数
是否在IO线程调用
同步或异步收到数据
接收预处理,粘包拆包
预处理
数据包
远程地址
将要处理该数据包的会话
处理收到的数据。默认匹配同步接收委托
接收事件参数
是否已处理,已处理的数据不再向下传递
数据到达事件
触发数据到达事件
接收事件参数
收到异常时如何处理。默认关闭会话
是否当作异常处理并结束会话
消息管道。收发消息都经过管道处理器
创建上下文
远程会话
通过管道发送消息
通过管道发送消息并等待响应
处理数据帧
数据帧
错误发生/断开连接时
触发异常
动作
异常
数据项
设置 或 获取 数据项
日志前缀
日志对象。禁止设为空对象
是否输出发送日志。默认false
是否输出接收日志。默认false
输出日志
会话集合。带有自动清理不活动会话的功能
服务端
清理周期。单位毫秒,默认10秒。
清理会话计时器
添加新会话,并设置会话编号
返回添加新会话是否成功
获取会话,加锁
关闭所有
移除不活动的会话
网络设置
网络调试
会话超时时间。默认20*60秒
缓冲区大小。默认64k
实例化
Socket扩展
异步发送数据
异步发送数据
发送数据流
返回自身,用于链式写法
向指定目的地发送信息
缓冲区
返回自身,用于链式写法
向指定目的地发送信息
文本编码,默认null表示UTF-8编码
返回自身,用于链式写法
广播数据包
缓冲区
广播字符串
接收字符串
文本编码,默认null表示UTF-8编码
检查并开启广播
关闭连接
SafeHandle字段
Socket是否未被关闭
根据异步事件获取可输出异常,屏蔽常见异常
TCP服务器
核心工作:启动服务时,监听端口,并启用多个(逻辑处理器数的10倍)异步接受操作。
服务器完全处于异步工作状态,任何操作都不可能被阻塞。
注意:服务器接受连接请求后,不会开始处理数据,而是由事件订阅者决定何时开始处理数据。
名称
本地绑定信息
端口
会话超时时间
对于每一个会话连接,如果超过该时间仍然没有收到任何数据,则断开会话连接。
异步处理接收到的数据,默认false。
异步处理有可能造成数据包乱序,特别是Tcp。true利于提升网络吞吐量。false避免拷贝,提升处理速度
底层Socket
是否活动
是否抛出异常,默认false不抛出。Send/Receive时可能发生异常,该设置决定是直接抛出异常还是通过事件
最大并行接收连接数。默认CPU*1.6
启用Http,数据处理时截去请求响应头,默认false
管道
会话统计
发送统计
接收统计
构造TCP服务器对象
构造TCP服务器对象
已重载。释放会话集合等资源
开始
停止
关闭原因。便于日志分析
新会话时触发
开启异步接受新连接
是否IO线程
开启异步是否成功
收到新连接时处理
会话集合。用地址端口作为标识,业务应用自己维持地址端口与业务主键的对应关系。
创建会话
错误发生/断开连接时
触发异常
动作
异常
日志前缀
日志对象
是否输出发送日志。默认false
是否输出接收日志。默认false
输出日志
已重载。
增强TCP客户端
收到空数据时抛出异常并断开连接。默认true
Socket服务器。当前通讯所在的Socket服务器,其实是TcpServer/UdpServer。该属性决定本会话是客户端会话还是服务的会话
自动重连次数,默认3。发生异常断开连接时,自动重连服务端。
是否匹配空包。Http协议需要
不延迟直接发送。Tcp为了合并小包而设计,客户端默认false,服务端默认true
实例化增强TCP
使用监听口初始化
用TCP客户端初始化
打开
关闭
关闭原因。便于日志分析
发送数据
目标地址由决定
数据包
是否成功
预处理
数据包
远程地址
将要处理该数据包的会话
处理收到的数据
接收事件参数
重连次数
日志前缀
已重载。
增强的UDP
如果已经打开异步接收,还要使用同步接收,则同步Receive内部不再调用底层Socket,而是等待截走异步数据。
会话超时时间
对于每一个会话连接,如果超过该时间仍然没有收到任何数据,则断开会话连接。
最后一次同步接收数据得到的远程地址
是否接收来自自己广播的环回数据。默认false
会话统计
实例化增强UDP
使用监听口初始化
打开
关闭
发送数据
目标地址由决定
数据包
是否成功
发送消息并等待响应。必须调用会话的发送,否则配对会失败
预处理
数据包
远程地址
将要处理该数据包的会话
处理收到的数据
接收事件参数
收到异常时如何处理。Tcp/Udp客户端默认关闭会话,但是Udp服务端不能关闭服务器,仅关闭会话
是否当作异常处理并结束会话
新会话时触发
会话集合。用地址端口作为标识,业务应用自己维持地址端口与业务主键的对应关系。
创建会话
已重载。
Udp扩展
发送数据流
返回自身,用于链式写法
向指定目的地发送信息
缓冲区
返回自身,用于链式写法
向指定目的地发送信息
文本编码,默认null表示UTF-8编码
返回自身,用于链式写法
广播数据包
缓冲区
广播字符串
接收字符串
文本编码,默认null表示UTF-8编码
Udp会话。仅用于服务端与某一固定远程地址通信
会话编号
名称
服务器
底层Socket
本地地址
端口
远程地址
超时。默认3000ms
管道
Socket服务器。当前通讯所在的Socket服务器,其实是TcpServer/UdpServer
是否抛出异常,默认false不抛出。Send/Receive时可能发生异常,该设置决定是直接抛出异常还是通过事件
异步处理接收到的数据,默认true利于提升网络吞吐量。
异步处理有可能造成数据包乱序,特别是Tcp。false避免拷贝,提升处理速度
发送数据包统计信息
接收数据包统计信息
通信开始时间
最后一次通信时间,主要表示活跃时间,包括收发
缓冲区大小。默认8k
发送消息
发送消息并等待响应
接收数据
处理数据帧
数据帧
错误发生/断开连接时
触发异常
动作
异常
已重载。
数据项
设置 或 获取 数据项
日志提供者
是否输出发送日志。默认false
是否输出接收日志。默认false
日志前缀
输出日志
输出日志
升级更新
自动更新的难点在于覆盖正在使用的exe/dll文件,通过改名可以解决
名称
服务器地址
版本
本地编译时间
更新完成以后自动启动主程序
更新目录
超链接信息,其中第一个为最佳匹配项
更新源文件
实例化一个升级对象实例,获取当前应用信息
获取版本信息,检查是否需要更新
开始更新
检查并执行更新操作
正在使用锁定的文件不可删除,但可以改名
删除备份文件
日志对象
输出日志
程序集辅助类。使用Create创建,保证每个程序集只有一个辅助类
程序集
名称
程序集版本
程序集标题
文件版本
编译时间
编译版本
公司名称
说明
获取包含清单的已加载文件的路径或 UNC 位置。
创建程序集辅助对象
类型集合,当前程序集的所有类型,包括私有和内嵌,非内嵌请直接调用Asm.GetTypes()
是否系统程序集
入口程序集
从程序集中查找指定名称的类型
在程序集中查找类型
查找插件
查找插件,带缓存
类型
查找所有非系统程序集中的所有插件
继承类所在的程序集会引用baseType所在的程序集,利用这一点可以做一定程度的性能优化。
是否从未加载程序集中获取类型。使用仅反射的方法检查目标类型,如果存在,则进行常规加载
指示是否应检查来自所有引用程序集的类型。如果为 false,则检查来自所有引用程序集的类型。 否则,只检查来自非全局程序集缓存 (GAC) 引用的程序集的类型。
是否引用了
程序集
被引用程序集全名
根据名称获取类型
类型名
是否从未加载程序集中获取类型。使用仅反射的方法检查目标类型,如果存在,则进行常规加载
获取指定程序域所有程序集
程序集目录集合
获取当前程序域所有只反射程序集的辅助类
只反射加载指定路径的所有程序集
只反射加载指定路径的所有程序集
获取当前应用程序的所有程序集,不包括系统程序集,仅限本目录
在对程序集的解析失败时发生
已重载。
包装程序集内部类的动态对象
类型转换
成员取值
调用成员
包装
已重载。
动态Xml
节点
实例化
实例化
实例化
设置
获取
索引器接访问口。
该接口用于通过名称快速访问对象属性或字段(属性优先)。
获取/设置 指定名称的属性或字段的值
名称
反射接口
该接口仅用于扩展,不建议外部使用
根据名称获取类型
类型名
是否从未加载程序集中获取类型。使用仅反射的方法检查目标类型,如果存在,则进行常规加载
获取方法
用于具有多个签名的同名方法的场合,不确定是否存在性能问题,不建议普通场合使用
类型
名称
参数类型数组
获取指定名称的方法集合,支持指定参数个数来匹配过滤
参数个数,-1表示不过滤参数个数
获取属性
类型
名称
忽略大小写
获取字段
类型
名称
忽略大小写
获取成员
类型
名称
忽略大小写
获取字段
获取属性
反射创建指定类型的实例
类型
参数数组
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数字典
获取目标对象的属性值
目标对象
属性
获取目标对象的字段值
目标对象
字段
设置目标对象的属性值
目标对象
属性
数值
设置目标对象的字段值
目标对象
字段
数值
从源对象拷贝数据到目标对象
目标对象
源对象
递归深度拷贝,直接拷贝成员值而不是引用
要忽略的成员
从源字典拷贝数据到目标对象
目标对象
源字典
递归深度拷贝,直接拷贝成员值而不是引用
获取一个类型的元素类型
类型
类型转换
数值
获取类型的友好名称
指定类型
是否全名,包含命名空间
是否能够转为指定基类
在指定程序集中查找指定基类或接口的所有子类实现
指定程序集
基类或接口,为空时返回所有类型
在所有程序集中查找指定基类或接口的子类实现
基类或接口
是否加载为加载程序集
默认反射实现
该接口仅用于扩展,不建议外部使用
根据名称获取类型
类型名
是否从未加载程序集中获取类型。使用仅反射的方法检查目标类型,如果存在,则进行常规加载
获取方法
用于具有多个签名的同名方法的场合,不确定是否存在性能问题,不建议普通场合使用
类型
名称
参数类型数组
获取指定名称的方法集合,支持指定参数个数来匹配过滤
参数个数,-1表示不过滤参数个数
获取属性
类型
名称
忽略大小写
获取字段
类型
名称
忽略大小写
获取成员
类型
名称
忽略大小写
获取字段
获取属性
反射创建指定类型的实例
类型
参数数组
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数字典
获取目标对象的属性值
目标对象
属性
获取目标对象的字段值
目标对象
字段
设置目标对象的属性值
目标对象
属性
数值
设置目标对象的字段值
目标对象
字段
数值
从源对象拷贝数据到目标对象
目标对象
源对象
递归深度拷贝,直接拷贝成员值而不是引用
要忽略的成员
从源字典拷贝数据到目标对象
目标对象
源字典
递归深度拷贝,直接拷贝成员值而不是引用
获取一个类型的元素类型
类型
类型转换
数值
获取类型的友好名称
指定类型
是否全名,包含命名空间
是否子类
在指定程序集中查找指定基类的子类
指定程序集
基类或接口,为空时返回所有类型
在所有程序集中查找指定基类或接口的子类实现
基类或接口
是否加载为加载程序集
获取类型,如果target是Type类型,则表示要反射的是静态成员
目标对象
反射工具类
当前反射提供者
根据名称获取类型。可搜索当前目录DLL,自动加载
类型名
是否从未加载程序集中获取类型。使用仅反射的方法检查目标类型,如果存在,则进行常规加载
获取方法
用于具有多个签名的同名方法的场合,不确定是否存在性能问题,不建议普通场合使用
类型
名称
参数类型数组
获取指定名称的方法集合,支持指定参数个数来匹配过滤
参数个数,-1表示不过滤参数个数
获取属性。搜索私有、静态、基类,优先返回大小写精确匹配成员
类型
名称
忽略大小写
获取字段。搜索私有、静态、基类,优先返回大小写精确匹配成员
类型
名称
忽略大小写
获取成员。搜索私有、静态、基类,优先返回大小写精确匹配成员
类型
名称
忽略大小写
获取用于序列化的字段
过滤特性的字段
获取用于序列化的属性
过滤特性的属性和索引器
反射创建指定类型的实例
类型
参数数组
反射调用指定对象的方法。target为类型时调用其静态方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法名
方法参数
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法名
数值
方法参数
反射调用是否成功
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数
反射调用指定对象的方法
要调用其方法的对象,如果要调用静态方法,则target是类型
方法
方法参数字典
获取目标对象指定名称的属性/字段值
目标对象
名称
出错时是否抛出异常
获取目标对象指定名称的属性/字段值
目标对象
名称
数值
是否成功获取数值
获取目标对象的成员值
目标对象
成员
设置目标对象指定名称的属性/字段值,若不存在返回false
目标对象
名称
数值
反射调用是否成功
设置目标对象的成员值
目标对象
成员
数值
从源对象拷贝数据到目标对象
目标对象
源对象
递归深度拷贝,直接拷贝成员值而不是引用
要忽略的成员
从源字典拷贝数据到目标对象
目标对象
源字典
递归深度拷贝,直接拷贝成员值而不是引用
获取一个类型的元素类型
类型
类型转换
数值
类型转换
数值
获取类型的友好名称
指定类型
是否全名,包含命名空间
从参数数组中获取类型数组
获取成员的类型,字段和属性是它们的类型,方法是返回类型,类型是自身
获取类型代码
是否整数
是否泛型列表
是否泛型字典
是否能够转为指定基类
是否能够转为指定基类
在指定程序集中查找指定基类的子类
指定程序集
基类或接口
在所有程序集中查找指定基类或接口的子类实现
基类或接口
是否加载为加载程序集
获取类型,如果target是Type类型,则表示要反射的是静态成员
目标对象
判断某个类型是否可空类型
类型
把一个方法转为泛型委托,便于快速反射调用
脚本引擎
三大用法:
1,单个表达式,根据参数计算表达式结果并返回
2,多个语句,最后有返回语句
3,多个方法,有一个名为Execute的静态方法作为入口方法
脚本引擎禁止实例化,必须通过方法创建,以代码为键进行缓存,避免重复创建反复编译形成泄漏。
其中方法的第二个参数为true表示前两种用法,为false表示第三种用法。
最简单而完整的用法:
// 根据代码创建脚本实例,相同代码只编译一次
var se = ScriptEngine.Create("a+b");
// 如果Method为空说明未编译,可设置参数
if (se.Method == null)
{
se.Parameters.Add("a", typeof(Int32));
se.Parameters.Add("b", typeof(Int32));
}
// 脚本固定返回Object类型,需要自己转换
var n = (Int32)se.Invoke(2, 3);
Console.WriteLine("2+3={0}", n);
无参数快速调用:
var n = (Int32)ScriptEngine.Execute("2*3");
约定参数快速调用:
var n = (Int32)ScriptEngine.Execute("p0*p1", new Object[] { 2, 3 });
Console.WriteLine("2*3={0}", n);
代码
是否表达式
参数集合。编译后就不可修改。
最终代码
编译得到的类型
根据代码编译出来可供直接调用的入口方法,Eval/Main
命名空间集合
引用程序集集合
日志
工作目录。执行时,将会作为环境变量的当前目录和PathHelper目录,执行后还原
构造函数私有,禁止外部越过Create方法直接创建实例
代码片段
是否表达式,表达式将编译成为一个Main方法
为指定代码片段创建脚本引擎实例。采用缓存,避免同一脚本重复创建引擎。
代码片段
是否表达式,表达式将编译成为一个Main方法
执行表达式,返回结果
代码片段
执行表达式,返回结果
代码片段
参数名称
参数类型
参数值
执行表达式,返回结果
代码片段
参数名值对
执行表达式,返回结果。参数名默认为p0/p1/p2/pn
参数数组
生成代码。根据完善得到最终代码
获取完整源代码
编译
编译
按照传入参数执行代码
参数
结果
分析命名空间
Api动作
动作名称
动作所在类型
方法
控制器对象
如果指定控制器对象,则每次调用前不再实例化对象
是否二进制参数
是否二进制返回
实例化
获取名称
已重载。
标识Api
名称
实例化
应用接口客户端
是否已打开
服务端地址集合。负载均衡
客户端连接集群
是否使用连接池。true时建立多个到服务端的连接,默认false使用单一连接
主机
最后活跃时间
所有服务器所有会话,包含自己
发送数据包统计信息
接收数据包统计信息
实例化应用接口客户端
实例化应用接口客户端
服务端地址集合,逗号分隔
销毁
打开客户端
关闭
关闭原因。便于日志分析
是否成功
查找Api动作
创建控制器实例
异步调用,等待返回结果
返回类型
服务操作
参数
标识
异步调用,等待返回结果
服务操作
参数
标识
同步调用,阻塞等待
服务操作
参数
标识
单向发送。同步调用,不等待返回
服务操作
参数
标识
指定客户端的异步调用,等待返回结果
常用于在OnLoginAsync中实现连接后登录功能
客户端
服务操作
参数
标识
新会话。客户端每次连接或断线重连后,可用InvokeWithClientAsync做登录
会话
状态。客户端ISocketClient
连接后自动登录
客户端
强制登录
登录
创建客户端之后,打开连接之前
显示统计信息的周期。默认600秒,0表示不显示统计信息
远程调用异常
代码
实例化远程调用异常
实例化远程调用异常
Api主机
名称
编码器
处理器
调用超时时间。请求发出后,等待响应的最大时间,默认15_000ms
发送数据包统计信息
接收数据包统计信息
用户会话数据
获取/设置 用户会话数据
接口动作管理器
注册服务提供类。该类的所有公开方法将直接暴露
注册服务
控制器对象
动作名称。为空时遍历控制器所有公有成员方法
显示可用服务
获取消息编码器。重载以指定不同的封包协议
处理消息
执行
新会话。服务端收到新连接,客户端每次连接或断线重连后,可用于做登录
会话
状态。客户端ISocketClient
日志
编码器日志
显示调用和处理错误。默认false
写日志
已重载。返回具有本类特征的字符串
String
初始化
主机
当前服务器所有会话
调用超时时间。默认30_000ms
初始化
主机
最后活跃时间
所有服务器所有会话,包含自己
开始会话处理
查找Api动作
创建控制器实例
远程调用
服务操作
参数
标识
应用接口服务器
是否正在工作
端口
服务器
实例化一个应用接口服务器
使用指定端口实例化网络服务应用接口提供者
实例化
销毁时停止服务
添加服务器
确保已创建服务器对象
开始服务
停止服务
关闭原因。便于日志分析
显示统计信息的周期。默认600秒,0表示不显示统计信息
客户端连接池负载均衡集群
服务器地址列表
创建回调
连接池
实例化连接池集群
打开
关闭
关闭原因。便于日志分析
是否成功
从集群中获取资源
归还
Round-Robin 负载均衡
为连接池创建连接
客户端单连接故障转移集群
服务器地址列表
创建回调
打开
关闭
关闭原因。便于日志分析
是否成功
从集群中获取资源
归还
Round-Robin 负载均衡
为连接池创建连接
API控制器
主机
获取所有接口
服务器信息,用户健康检测
状态信息
控制器上下文
控制器实例
处理动作
真实动作名称
会话
请求
请求参数
获取或设置操作方法参数。
获取或设置由操作方法返回的结果。
获取或设置在操作方法的执行过程中发生的异常(如果有)。
获取或设置一个值,该值指示是否处理异常。
实例化
当前线程上下文
重置为默认状态
定义操作筛选器中使用的方法。
在执行操作方法之前调用。
在执行操作方法后调用。
Api接口
会话
Api处理器
执行
默认处理器
Api接口主机
执行
准备上下文
获取参数
Api主机
编码器
处理器
接口动作管理器
获取消息编码器。重载以指定不同的封包协议
处理消息
发送统计
接收统计
日志
写日志
Api主机助手
调用
结果类型
服务操作
参数
标识
调用
服务操作
参数
标识
创建控制器实例
获取统计信息
接口管理器
可提供服务的方法
注册服务提供类。该类的所有公开方法将直接暴露
注册服务
控制器对象
动作名称。为空时遍历控制器所有公有成员方法
查找服务
可提供服务的方法
注册服务提供类。该类的所有公开方法将直接暴露
注册服务
控制器对象
动作名称。为空时遍历控制器所有公有成员方法
查找服务
应用接口服务器接口
主机
当前服务器所有会话
初始化
开始
停止
关闭原因。便于日志分析
日志
Api会话
主机
最后活跃时间
所有服务器所有会话,包含自己
获取/设置 用户会话数据
查找Api动作
创建控制器实例
发送消息。低级接口,由框架使用
发送消息。低级接口,由框架使用
远程调用
服务操作
参数
标识
编码器
创建请求
创建响应
解码 请求/响应
消息
服务动作
错误码
参数或结果
解码参数
解码结果
转换为目标类型
日志提供者
编码器基类
解码 请求/响应
消息
服务动作
错误码
参数或结果
日志提供者
写日志
Json编码器
编码 请求/响应
编码
解码参数
解码结果
转换为目标类型
创建请求
创建响应
证书
http://blogs.msdn.com/b/dcook/archive/2008/11/25/creating-a-self-signed-certificate-in-c.aspx
建立自签名证书
建立自签名证书
建立自签名证书
建立自签名证书
例如CN=SelfSignCertificate;C=China;OU=NewLife;O=Development Team;E=nnhy@vip.qq.com,其中CN是显示名
建立自签名证书
CRC16校验
CRC16表
校验值
重置清零
添加整数进行校验
the byte is taken as the lower 8 bits of value
添加字节数组进行校验 CRC16-CCITT x16+x12+x5+1 1021 ISO HDLC, ITU X.25, V.34/V.41/V.42, PPP-FCS
字符串123456789的Crc16是31C3
数据缓冲区
偏移量
字节个数
添加数据流进行校验 CRC-16 x16+x15+x2+1 8005 IBM SDLC
数量
计算校验码
计算数据流校验码
计算数据流校验码,指定起始位置和字节数偏移量
一般用于计算数据包校验码,需要回过头去开始校验,并且可能需要跳过最后的校验码长度。
position小于0时,数据流从当前位置开始计算校验;
position大于等于0时,数据流移到该位置开始计算校验,最后由count决定可能差几个字节不参与计算;
如果大于等于0,则表示从该位置开始计算
字节数偏移量,一般用负数表示
CRC32校验
Generate a table for a byte-wise 32-bit CRC calculation on the polynomial:
x^32+x^26+x^23+x^22+x^16+x^12+x^11+x^10+x^8+x^7+x^5+x^4+x^2+x+1.
Polynomials over GF(2) are represented in binary, one bit per coefficient,
with the lowest powers in the most significant bit. Then adding polynomials
is just exclusive-or, and multiplying a polynomial by x is a right shift by
one. If we call the above polynomial p, and represent a byte as the
polynomial q, also with the lowest power in the most significant bit (so the
byte 0xb1 is the polynomial x^7+x^3+x+1), then the CRC is (q*x^32) mod p,
where a mod b means the remainder after dividing a by b.
This calculation is done using the shift-register method of multiplying and
taking the remainder. The register is initialized to zero, and for each
incoming bit, x^32 is added mod p to the register if the bit is a one (where
x^32 mod p is p+x^32 = x^26+...+1), and the register is multiplied mod p by
x (which is shifting right by one and adding x^32 mod p if the bit shifted
out is a one). We start with the highest power (least significant bit) of
q and repeat for all eight bits of q.
The table is simply the CRC of all possible eight bit values. This is all
the information needed to generate CRC's on data a byte at a time for all
combinations of CRC register values and incoming bytes.
校验表
校验值
校验值
重置清零
添加整数进行校验
the byte is taken as the lower 8 bits of value
添加字节数组进行校验
The buffer which contains the data
The offset in the buffer where the data starts
The number of data bytes to update the CRC with.
添加数据流进行校验
数量
计算校验码
计算数据流校验码
计算数据流校验码,指定起始位置和字节数偏移量
一般用于计算数据包校验码,需要回过头去开始校验,并且可能需要跳过最后的校验码长度。
position小于0时,数据流从当前位置开始计算校验;
position大于等于0时,数据流移到该位置开始计算校验,最后由count决定可能差几个字节不参与计算;
如果大于等于0,则表示从该位置开始计算
字节数偏移量,一般用负数表示
DSA算法
产生非对称密钥对(私钥和公钥)
密钥长度,默认1024位强密钥
私钥和公钥
签名
验证
从Xml加载DSA密钥
保存DSA密钥到Xml
随机数
返回一个小于所指定最大值的非负随机数
返回的随机数的上界(随机数不能取该上界值)
返回一个指定范围内的随机数
调用平均耗时37.76ns,其中GC耗时77.56%
返回的随机数的下界(随机数可取该下界值)
返回的随机数的上界(随机数不能取该上界值)
返回指定长度随机字节数组
调用平均耗时5.46ns,其中GC耗时15%
返回指定长度随机字符串
长度
是否包含符号
RC4对称加密算法
RC4于1987年提出,和DES算法一样,是一种对称加密算法,也就是说使用的密钥为单钥(或称为私钥)。
但不同于DES的是,RC4不是对明文进行分组处理,而是字节流的方式依次加密明文中的每一个字节,解密的时候也是依次对密文中的每一个字节进行解密。
RC4算法的特点是算法简单,运行速度快,而且密钥长度是可变的,可变范围为1-256字节(8-2048比特),
在如今技术支持的前提下,当密钥长度为128比特时,用暴力法搜索密钥已经不太可行,所以可以预见RC4的密钥范围任然可以在今后相当长的时间里抵御暴力搜索密钥的攻击。
实际上,如今也没有找到对于128bit密钥长度的RC4加密算法的有效攻击方法。
加密
数据
密码
打乱密码
密码
密码箱长度
打乱后的密码
RSA算法
RSA加密或签名小数据块时,密文长度128,速度也很快。
产生非对称密钥对
RSAParameters的各个字段采用大端字节序,转为BigInteger的之前一定要倒序。
RSA加密后密文最小长度就是密钥长度,所以1024密钥最小密文长度是128字节。
密钥长度,默认1024位强密钥
RSA加密
如果为 true,则使用 OAEP 填充(仅可用于运行 Windows XP 及更高版本的计算机)执行直接 System.Security.Cryptography.RSA加密;否则,如果为 false,则使用 PKCS#1 v1.5 填充。
RSA解密
如果为 true,则使用 OAEP 填充(仅可用于运行 Microsoft Windows XP 及更高版本的计算机)执行直接 System.Security.Cryptography.RSA解密;否则,如果为 false 则使用 PKCS#1 v1.5 填充。
配合DES加密
配合DES解密
配合对称算法加密
配合对称算法解密
签名
验证
使用随机数设置
访问器基类
从数据流中读取消息
数据流
上下文
是否成功
把消息写入到数据流中
数据流
上下文
消息转为字节数组
创建序列化器
输出消息实体
获取成员输出
获取用于输出的成员值
访问器泛型基类
从流中读取消息
从字节数组中读取消息
二进制编码解码器
使用7位编码整数。默认true使用
对象转二进制
二进制转对象
二进制编码解码器
使用7位编码整数。默认true使用
对象转二进制
二进制转对象
二进制序列化
使用7位编码整数。默认false不使用
小端字节序。默认false大端
使用指定大小的FieldSizeAttribute特性,默认false
使用对象引用,默认true
大小宽度。可选0/1/2/4,默认0表示压缩编码整数
要忽略的成员
处理器列表
实例化
添加处理器
添加处理器
获取处理器
写入一个对象
目标对象
类型
写入字节
将字节数组部分写入当前流,不写入数组长度。
包含要写入的数据的字节数组。
buffer 中开始写入的起始点。
要写入的字节数。
写入大小,如果有FieldSize则返回,否则写入编码的大小
写7位压缩编码整数
以7位压缩格式写入32位整数,小于7位用1个字节,小于14位用2个字节。
由每次写入的一个字节的第一位标记后面的字节是否还是当前数据,所以每个字节实际可利用存储空间只有后7位。
数值
实际写入字节数
读取指定类型对象
读取指定类型对象
尝试读取指定类型对象
读取字节
从当前流中将 count 个字节读入字节数组
要读取的字节数。
读取大小
读取整数的字节数组,某些写入器(如二进制写入器)可能需要改变字节顺序
数量
从当前流中读取 2 字节有符号整数,并使流的当前位置提升 2 个字节。
从当前流中读取 4 字节有符号整数,并使流的当前位置提升 4 个字节。
以压缩格式读取16位整数
以压缩格式读取32位整数
以压缩格式读取64位整数
使用跟踪流。实际上是重新包装一次Stream,必须在设置Stream后,使用之前
快速读取
数据流
使用7位编码整数
快速写入
对象
使用7位编码整数
颜色处理器。
实例化
写入对象
目标对象
类型
尝试读取指定类型对象
复合对象处理器
实例化
写入对象
目标对象
类型
尝试读取指定类型对象
获取成员
字典数据编码
初始化
写入一个对象
目标对象
类型
尝试读取指定类型对象
字体处理器。
实例化
写入对象
目标对象
类型
尝试读取指定类型对象
二进制基础类型处理器
实例化
写入一个对象
目标对象
类型
是否处理成功
尝试读取指定类型对象
将一个无符号字节写入
要写入的无符号字节。
将字节数组写入,如果设置了UseSize,则先写入数组长度。
包含要写入的数据的字节数组。
将字节数组部分写入当前流,不写入数组长度。
包含要写入的数据的字节数组。
buffer 中开始写入的起始点。
要写入的字节数。
写入字节数组,自动计算长度
缓冲区
数量
将 2 字节有符号整数写入当前流,并将流的位置提升 2 个字节。
要写入的 2 字节有符号整数。
将 4 字节有符号整数写入当前流,并将流的位置提升 4 个字节。
要写入的 4 字节有符号整数。
将 8 字节有符号整数写入当前流,并将流的位置提升 8 个字节。
要写入的 8 字节有符号整数。
判断字节顺序
缓冲区
将 2 字节无符号整数写入当前流,并将流的位置提升 2 个字节。
要写入的 2 字节无符号整数。
将 4 字节无符号整数写入当前流,并将流的位置提升 4 个字节。
要写入的 4 字节无符号整数。
将 8 字节无符号整数写入当前流,并将流的位置提升 8 个字节。
要写入的 8 字节无符号整数。
将 4 字节浮点值写入当前流,并将流的位置提升 4 个字节。
要写入的 4 字节浮点值。
将 8 字节浮点值写入当前流,并将流的位置提升 8 个字节。
要写入的 8 字节浮点值。
将一个十进制值写入当前流,并将流位置提升十六个字节。
要写入的十进制值。
将 Unicode 字符写入当前流,并根据所使用的 Encoding 和向流中写入的特定字符,提升流的当前位置。
要写入的非代理项 Unicode 字符。
将字符数组部分写入当前流,并根据所使用的 Encoding(可能还根据向流中写入的特定字符),提升流的当前位置。
包含要写入的数据的字符数组。
chars 中开始写入的起始点。
要写入的字符数。
写入字符串
要写入的值。
从当前流中读取下一个字节,并使流的当前位置提升 1 个字节。
从当前流中将 count 个字节读入字节数组,如果count小于0,则先读取字节数组长度。
要读取的字节数。
读取整数的字节数组,某些写入器(如二进制写入器)可能需要改变字节顺序
数量
从当前流中读取 2 字节有符号整数,并使流的当前位置提升 2 个字节。
从当前流中读取 4 字节有符号整数,并使流的当前位置提升 4 个字节。
从当前流中读取 8 字节有符号整数,并使流的当前位置向前移动 8 个字节。
使用 Little-Endian 编码从当前流中读取 2 字节无符号整数,并将流的位置提升 2 个字节。
从当前流中读取 4 字节无符号整数并使流的当前位置提升 4 个字节。
从当前流中读取 8 字节无符号整数并使流的当前位置提升 8 个字节。
从当前流中读取 4 字节浮点值,并使流的当前位置提升 4 个字节。
从当前流中读取 8 字节浮点值,并使流的当前位置提升 8 个字节。
从当前流中读取下一个字符,并根据所使用的 Encoding 和从流中读取的特定字符,提升流的当前位置。
从当前流中读取一个字符串。字符串有长度前缀,一次 7 位地被编码为整数。
从当前流中读取十进制数值,并将该流的当前位置提升十六个字节。
以压缩格式读取16位整数
以压缩格式读取32位整数
以压缩格式读取64位整数
以7位压缩格式写入16位整数,小于7位用1个字节,小于14位用2个字节。
由每次写入的一个字节的第一位标记后面的字节是否还是当前数据,所以每个字节实际可利用存储空间只有后7位。
数值
实际写入字节数
以7位压缩格式写入32位整数,小于7位用1个字节,小于14位用2个字节。
由每次写入的一个字节的第一位标记后面的字节是否还是当前数据,所以每个字节实际可利用存储空间只有后7位。
数值
实际写入字节数
以7位压缩格式写入64位整数,小于7位用1个字节,小于14位用2个字节。
由每次写入的一个字节的第一位标记后面的字节是否还是当前数据,所以每个字节实际可利用存储空间只有后7位。
数值
实际写入字节数
列表数据编码
初始化
写入
读取
常用类型编码
初始化
写入
写入字节数组,自动计算长度
缓冲区
数量
读取
从当前流中将 count 个字节读入字节数组,如果count小于0,则先读取字节数组长度。
要读取的字节数。
从当前流中读取 count 个字符,以字符数组的形式返回数据,并根据所使用的 Encoding 和从流中读取的特定字符,提升当前位置。
要读取的字符数。
二进制名值对
初始化
写入一个对象
目标对象
类型
尝试读取指定类型对象
写入名值对
读取原始名值对
读取原始名值对
数据流
编码
从原始名值对读取数据
获取成员
内部对象处理器。对于其它处理器无法支持的类型,一律由该处理器解决
实例化
写入对象
目标对象
类型
尝试读取指定类型对象
字段大小特性。
可以通过Size指定字符串或数组的固有大小,为0表示自动计算;也可以通过指定参考字段ReferenceName,然后从其中获取大小。
支持_Header._Questions形式的多层次引用字段
大小。使用时,作为偏移量;0表示自动计算大小
参考大小字段名
通过Size指定字符串或数组的固有大小,为0表示自动计算
指定参考字段ReferenceName,然后从其中获取大小
指定参考字段ReferenceName,然后从其中获取大小
在参考字段值基础上的增量,可以是正数负数
找到所引用的参考字段
目标对象
目标对象的成员
数值
设置目标对象的引用大小值
目标对象
获取目标对象的引用大小值
目标对象
二进制序列化接口
编码整数
小端字节序。默认false大端
使用指定大小的FieldSizeAttribute特性,默认false
要忽略的成员
处理器列表
写入字节
将字节数组部分写入当前流,不写入数组长度。
包含要写入的数据的字节数组。
buffer 中开始写入的起始点。
要写入的字节数。
写入大小
要写入的大小值
返回特性指定的固定长度,如果没有则返回-1
读取字节
从当前流中将 count 个字节读入字节数组
要读取的字节数。
读取大小
二进制读写处理器接口
二进制读写处理器基类
序列化访问器。接口实现者可以在这里完全自定义序列化行为
从数据流中读取消息
数据流
上下文
是否成功
把消息写入到数据流中
数据流
上下文
是否成功
访问器助手
支持访问器的对象转数据包
访问器
上下文
通过访问器读取
上下文
通过访问器转换数据包为实体对象
序列化接口
数据流
主对象
成员
文本编码
序列化属性而不是字段。默认true
写入一个对象
目标对象
类型
读取指定类型对象
读取指定类型对象
尝试读取指定类型对象
日志提供者
序列化处理器接口
宿主读写器
优先级
写入一个对象
目标对象
类型
尝试读取指定类型对象
序列化接口
数据流。默认实例化一个内存数据流
主对象
成员
字符串编码,默认Default
序列化属性而不是字段。默认true
实例化
获取流里面的数据
日志提供者
输出日志
读写处理器基类
宿主读写器
优先级
写入一个对象
目标对象
类型
尝试读取指定类型对象
输出日志
成员序列化访问器。接口实现者可以在这里完全自定义序列化行为
从数据流中读取消息
序列化
成员
是否成功
把消息写入到数据流中
序列化
成员
Json编码解码器
对象转Json
Json转对象
Json编码解码器
对象转Json
Json转对象
IJson序列化接口
是否缩进
处理器列表
写入字符串
写入
读取
读取字节
IJson读写处理器接口
获取对象的Json字符串表示形式。
返回null表示不支持
IJson读写处理器基类
获取对象的Json字符串表示形式。
返回null表示不支持
写入一个对象
目标对象
类型
是否处理成功
Json序列化接口
写入对象,得到Json字符串
是否缩进。默认false
是否写控制。默认true
是否驼峰命名。默认false
从Json字符串中读取对象
类型转换
Json助手
默认实现
写入对象,得到Json字符串
是否缩进
写入对象,得到Json字符串
是否缩进。默认false
是否写控制。默认true
是否驼峰命名。默认false
从Json字符串中读取对象
从Json字符串中读取对象
格式化Json文本
Json类型对象转换实体类
Json序列化
是否缩进
处理器列表
实例化
添加处理器
添加处理器
获取处理器
写入一个对象
目标对象
类型
写入字符串
写入
读取指定类型对象
读取指定类型对象
尝试读取指定类型对象
读取
读取字节
列表数据编码
初始化
获取对象的Json字符串表示形式。
返回null表示不支持
写入
读取
复合对象处理器
要忽略的成员
实例化
获取对象的Json字符串表示形式。
返回null表示不支持
写入对象
目标对象
类型
尝试读取指定类型对象
获取成员
Json基础类型处理器
实例化
获取对象的Json字符串表示形式。
返回null表示不支持
尝试读取指定类型对象
Json分析器
标识符
左大括号
右大括号
左方括号
右方括号
冒号
逗号
字符串
数字
布尔真
布尔真
空值
实例化
解码
读取一个Token
Json读取器
是否使用UTC时间
读取Json到指定类型
读取Json到指定类型
Json字典或列表转为具体类型对象
Json对象
模板类型
目标对象
转为泛型列表
目标对象
转为数组
目标对象
转为泛型字典
目标对象
字典转复杂对象,反射属性赋值
目标对象
创建时间
Json写入器
使用UTC时间。默认false
使用小写名称
使用驼峰命名
写入空值。默认true
实例化
对象序列化为Json字符串
是否缩进。默认false
是否写控制。默认true
是否驼峰命名。默认false
根据小写和驼峰格式化名称
序列化助手
获取序列化名称
二进制序列化接口
处理器列表
使用注释
写入一个对象
目标对象
名称
类型
获取Xml写入器
获取Xml读取器
二进制读写处理器接口
Xml读写处理器基类
Xml序列化
深度
处理器列表
使用特性
使用注释
当前名称
实例化
添加处理器
添加处理器
写入一个对象
目标对象
名称
类型
写入开头
写入结尾
获取Xml写入器
读取指定类型对象
读取指定类型对象
尝试读取指定类型对象
读取开始
读取结束
获取Xml读取器
获取字符串
Xml复合对象处理器
实例化
写入对象
目标对象
类型
尝试读取
获取成员
Xml基础类型处理器
实例化
写入一个对象
目标对象
类型
是否处理成功
尝试读取
列表数据编码
初始化
写入
读取
核心设置
是否启用全局调试。默认启用
日志等级,只输出大于等于该级别的日志,All/Debug/Info/Warn/Error/Fatal,默认Info
文件日志目录
网络日志。本地子网日志广播255.255.255.255:514
日志文件格式
临时目录
插件目录
插件服务器。将从该网页上根据关键字分析链接并下载插件
加载完成后
获取插件目录
轻量级线程池。无等待和调度逻辑,直接创建线程竞争处理器资源
初始化线程池
带异常处理的线程池任务调度,不允许异常抛出,以免造成应用程序退出
带异常处理的线程池任务调度,不允许异常抛出,以免造成应用程序退出
静态实例
内部池
实例化
创建实例
把委托放入线程池执行
在线程池中异步执行任务
在线程池中异步执行任务
在线程池中异步执行任务
线程任务项
编号
线程
主机线程池
活跃
实例化
销毁
已重载。
执行委托
定时器调度器
创建指定名称的调度器
默认调度器
当前调度器
名称
定时器个数
最大耗时。超过时报警告日志,默认500ms
把定时器加入队列
从队列删除定时器
唤醒处理
调度主程序
检查定时器是否到期
处理每一个定时器
已重载。
是否开启调试,输出更多信息
不可重入的定时器。
为了避免系统的Timer可重入的问题,差别在于本地调用完成后才开始计算时间间隔。这实际上也是经常用到的。
因为挂载在静态列表上,必须从外部主动调用才能销毁定时器。
该定时器不能放入太多任务,否则适得其反!
TimerX必须维持对象,否则很容易被GC回收。
所属调度器
获取/设置 回调
获取/设置 用户数据
获取/设置 下一次调用时间
获取/设置 调用次数
获取/设置 间隔周期。毫秒,设为0或-1则只调用一次
获取/设置 异步执行任务。默认false
获取/设置 绝对精确时间执行。默认false
调用中
平均耗时。毫秒
判断任务是否执行的委托。一般跟异步配合使用,避免频繁从线程池借出线程
当前定时器
实例化一个不可重入的定时器
委托
用户数据
多久之后开始。毫秒
间隔周期。毫秒
调度器
实例化一个绝对定时器
委托
用户数据
绝对开始时间
间隔周期。毫秒
调度器
销毁定时器
是否已设置下一次时间
设置下一次运行时间
小于等于0表示马上调度
延迟执行一个委托
当前时间。定时读取系统时间,避免频繁读取系统时间造成性能瓶颈
已重载
超链接
名称
全名
超链接
原始超链接
标题
版本
时间
原始Html
分析HTML中的链接
Html文本
基础Url,用于生成超链接的完整Url
用于基础过滤的过滤器
分解文件
已重载。
页面压缩模块
初始化模块,准备拦截请求。
网页压缩文件
是否可压缩文件
检查请求头,确认客户端是否支持压缩编码
添加压缩编码到响应头
全局错误处理模块
初始化模块,准备拦截请求。
是否需要处理
错误处理方法
页面执行时间模块
初始化模块,准备拦截请求。
初始化
执行时间字符串
输出运行时间
输出
OAuth 2.0 客户端
名称
验证服务器地址
令牌服务地址。可以不同于验证地址的内网直达地址
应用Key
安全码
验证地址
访问令牌地址
响应类型
验证服务器跳转回来子系统时的类型,默认code,此时还需要子系统服务端请求验证服务器换取AccessToken;
可选token,此时验证服务器直接返回AccessToken,子系统不需要再次请求。
作用域
授权码
访问令牌
刷新令牌
统一标识
过期时间
访问项
实例化
根据名称创建客户端
应用参数设置
应用参数设置
构建跳转验证地址
验证完成后调整的目标地址
用户状态数据
相对地址的基地址
根据授权码获取访问令牌
OpenID地址
根据授权码获取访问令牌
用户信息地址
用户ID
用户名
昵称
头像
获取用户信息
填充用户,登录成功并获取用户信息之后
注销地址
注销
完成后调整的目标地址
用户状态数据
相对地址的基地址
替换地址模版参数
获取名值字典
最后一次请求的响应内容
创建客户端
路径
从响应数据中获取信息
日志
写日志
配置
调试开关。默认true
应用地址。域名和端口,应用系统经过反向代理重定向时指定外部地址
配置项
已加载
获取
获取或添加
开放验证服务器配置项
服务地址
验证服务地址
令牌服务地址。可以不同于验证地址的内网直达地址
应用标识
密钥
授权范围
单点登录服务端
缓存
令牌提供者
令牌有效期。默认24小时
实例
验证用户身份
子系统需要验证访问者身份时,引导用户跳转到这里。
用户登录完成后,得到一个独一无二的code,并跳转回去子系统。
应用标识
回调地址
响应类型。默认code
授权域
用户状态数据
根据验证结果获取跳转回子系统的Url
根据Code获取令牌
解码令牌
日志
写日志
身份验证提供者
实例化
从响应数据中获取信息
身份验证提供者
实例化
从响应数据中获取信息
创建客户端
路径
身份验证提供者
实例化
从响应数据中获取信息
淘宝身份验证提供者
实例化
从响应数据中获取信息
身份验证提供者
实例化
从响应数据中获取信息
插件助手
加载插件
提供下载地址的多个目标页面
令牌提供者
密钥。签发方用私钥,验证方用公钥
读取密钥
文件
是否生成
编码用户和有效期得到令牌
用户
有效期
令牌解码得到用户和有效期
令牌
有效期
扩展的Web客户端
Cookie容器
可接受类型
可接受语言
引用页面
超时,默认15000毫秒
自动解压缩模式。
User-Agent 标头,指定有关客户端代理的信息
编码。网络时代,绝大部分使用utf8编码
保持连接
代理服务器地址
网页代理
实例化
初始化常用的东西
是否模拟ie
是否压缩
销毁
请求
响应
创建客户端会话
发送请求,获取响应
下载数据
下载字符串
下载文件
异步上传数据
异步上传表单
异步上传字符串
异步上传Json对象
获取指定地址的Html,自动处理文本编码
获取指定地址的Html,分析所有超链接
分析指定页面指定名称的链接,并下载到目标目录,返回目标文件
根据版本或时间降序排序选择
指定页面
页面上指定名称的链接
要下载到的目标目录
返回已下载的文件,无效时返回空
分析指定页面指定名称的链接,并下载到目标目录,解压Zip后返回目标文件
提供下载地址的多个目标页面
页面上指定名称的链接
要下载到的目标目录
是否覆盖目标同名文件
根据Http响应设置本地Cookie
从本地获取Cookie并设置到Http请求头
默认连接池
访问地址获取字符串
设置是否允许不安全头部
微软WebClient默认要求严格的Http头部,否则报错
日志
提供网页下载支持,在服务端把一个数据流作为附件传给浏览器,带有断点续传和限速的功能
数据流
文件名
内容类型
附件配置模式,是在浏览器直接打开,还是提示另存为
速度,每秒传输字节数,根据包大小,每响应一个包后睡眠指定毫秒数,0表示不限制
是否启用浏览器缓存 默认禁用
浏览器最大缓存时间 默认30天。通过Cache-Control头控制max-age,直接使用浏览器缓存,不会发出Http请求,对F5无效
文件数据最后修改时间,浏览器缓存时用
附件配置模式
不设置
内联模式,在浏览器直接打开
附件模式,提示另存为
分析模式
构造函数
构造函数
构造函数
检查浏览器缓存是否依然有效,如果有效则跳过Render
输出数据流
语音识别
系统名称。用于引导前缀
最后一次进入引导前缀的时间。
是否可用
销毁
当前实例
获取已注册的所有键值
注册要语音识别的关键字到委托
语音接口
语音识别事件
初始化
设置识别短语
识别事件参数
获取识别器分配的值,此值表示与给定输入匹配的可能性
获取语音识别器从识别的输入生成的规范化文本
实例化
支持Xml序列化的泛型字典类
读取Xml
Xml读取器
写入Xml
Xml写入器
Xml配置文件基类
标准用法:TConfig.Current
配置实体类通过特性指定配置文件路径以及自动更新时间。
Current将加载配置文件,如果文件不存在或者加载失败,将实例化一个对象返回。
考虑到自动刷新,不提供LoadFile和SaveFile等方法,可通过扩展方法ToXmlFileEntity和ToXmlFile实现。
用户也可以通过配置实体类的静态构造函数修改基类的和来动态配置加载信息。
当前实例。通过置空可以使其重新加载。
一些设置。派生类可以在自己的静态构造函数中指定
是否调试
配置文件路径
重新加载时间。单位:毫秒
没有配置文件时是否保存新配置。默认true
配置文件
最后写入时间
过期时间。如果在这个时间之后再次访问,将检查文件修改时间
是否已更新。通过文件写入时间判断
设置过期重新加载配置的时间
是否新的配置文件
销毁
加载指定配置文件
从配置文件中读取完成后触发
保存到配置文件中去
保存到配置文件中去
异步保存
新创建配置文件时执行
Xml配置文件特性
配置文件名
重新加载时间。单位:毫秒
指定配置文件名
指定配置文件名和重新加载时间(毫秒)
Xml辅助类
序列化为Xml字符串
要序列化为Xml的对象
编码
是否附加注释,附加成员的Description和DisplayName注释
是否使用特性输出
Xml字符串
序列化为Xml数据流
要序列化为Xml的对象
目标数据流
编码
是否附加注释,附加成员的Description和DisplayName注释
是否使用特性输出
序列化为Xml文件
要序列化为Xml的对象
目标Xml文件
编码
是否附加注释,附加成员的Description和DisplayName注释
Xml字符串
字符串转为Xml实体对象
实体类型
Xml字符串
Xml实体对象
字符串转为Xml实体对象
Xml字符串
实体类型
Xml实体对象
数据流转为Xml实体对象
实体类型
数据流
编码
Xml实体对象
数据流转为Xml实体对象
数据流
实体类型
编码
Xml实体对象
Xml文件转为Xml实体对象
实体类型
Xml文件
编码
Xml实体对象
简单Xml转为字符串字典
字符串字典转为Xml
高德地图
参考地址 http://lbs.amap.com/api/webservice/guide/api/georegeo/#geo
高德地图
远程调用
目标Url
结果字段
查询地址的经纬度坐标
查询地址获取坐标
地址
城市
是否格式化地址。高德地图默认已经格式化地址
根据坐标获取地址
http://lbs.amap.com/api/webservice/guide/api/georegeo/#regeo
根据坐标获取地址
计算距离和驾车时间
http://lbs.amap.com/api/webservice/guide/api/direction
type:
0:直线距离
1:驾车导航距离(仅支持国内坐标)。
必须指出,当为1时会考虑路况,故在不同时间请求返回结果可能不同。
此策略和driving接口的 strategy = 4策略一致
2:公交规划距离(仅支持同城坐标)
3:步行规划距离(仅支持5km之间的距离)
distance 路径距离,单位:米
duration 预计行驶时间,单位:秒
路径计算的方式和方法
行政区划
http://lbs.amap.com/api/webservice/guide/api/district
查询关键字
设置显示下级行政区级数
按照指定行政区划进行过滤,填入后则只返回该省/直辖市信息
是否无效Key。可能禁用或超出限制
百度地图
参考手册 http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-geocoding
高德地图
远程调用
目标Url
结果字段
查询地址的经纬度坐标
查询地址获取坐标
地址
城市
是否格式化地址
根据坐标获取地址
根据坐标获取地址
计算距离和驾车时间
http://lbsyun.baidu.com/index.php?title=webapi/route-matrix-api-v2
路径计算的方式和方法
行政区划区域检索
http://lbsyun.baidu.com/index.php?title=webapi/guide/webservice-placeapi
是否无效Key。可能禁用或超出限制
驾车距离和时间
距离。单位千米
路线耗时。单位秒
地图提供者接口
应用密钥
异步获取字符串
查询地址的经纬度坐标
查询地址获取坐标
地址
城市
是否格式化地址
根据坐标获取地址
根据坐标获取地址
计算距离和驾车时间
路径计算的方式和方法
日志
地图提供者
应用密钥。多个key逗号分隔
应用密码参数名
最后密钥
坐标系
最后网址
最后响应
最后结果
收到异常响应时是否抛出异常
销毁
异步获取字符串
远程调用
目标Url
结果字段
申请密钥
移除不可用密钥
是否无效Key。可能禁用或超出限制
日志
写日志
集合扩展
集合转为数组
集合转为数组
集合转为数组
目标匿名参数对象转为字典
合并字典参数
字典
目标对象
是否覆盖同名参数
排除项
转为可空字典
从队列里面获取指定个数元素
消费集合
元素个数
从消费集合里面获取指定个数元素
消费集合
元素个数
工具类
采用静态架构,允许外部重载工具类的各种实现。
所有类型转换均支持默认值,默认值为该default(T),在转换失败时返回默认值。
类型转换提供者
重载默认提供者并赋值给可改变所有类型转换的行为
转为整数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)、时间(Unix秒)
Int16/UInt32/Int64等,可以先转为最常用的Int32后再二次处理
待转换对象
默认值。待转换对象无效时使用
转为长整数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)、时间(Unix毫秒)
待转换对象
默认值。待转换对象无效时使用
转为浮点数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)
Single可以先转为最常用的Double后再二次处理
待转换对象
默认值。待转换对象无效时使用
转为布尔型,转换失败时返回默认值。支持大小写True/False、0和非零
待转换对象
默认值。待转换对象无效时使用
转为时间日期,转换失败时返回最小时间。支持字符串、整数(Unix秒)
待转换对象
转为时间日期,转换失败时返回默认值
不是常量无法做默认值
待转换对象
默认值。待转换对象无效时使用
时间日期转为yyyy-MM-dd HH:mm:ss完整字符串
最常用的时间日期格式,可以无视各平台以及系统自定义的时间格式
待转换对象
时间日期转为yyyy-MM-dd HH:mm:ss完整字符串,支持指定最小时间的字符串
最常用的时间日期格式,可以无视各平台以及系统自定义的时间格式
待转换对象
字符串空值时(DateTime.MinValue)显示的字符串,null表示原样显示最小时间,String.Empty表示不显示
时间日期转为指定格式字符串
待转换对象
格式化字符串
字符串空值时显示的字符串,null表示原样显示最小时间,String.Empty表示不显示
获取内部真实异常
获取异常消息
异常
默认转换
转为整数,转换失败时返回默认值。支持字符串、全角、字节数组(小端)、时间(Unix秒)
待转换对象
默认值。待转换对象无效时使用
转为长整数。支持字符串、全角、字节数组(小端)、时间(Unix毫秒)
待转换对象
默认值。待转换对象无效时使用
转为浮点数
待转换对象
默认值。待转换对象无效时使用
转为布尔型。支持大小写True/False、0和非零
待转换对象
默认值。待转换对象无效时使用
转为时间日期,转换失败时返回最小时间。支持字符串、整数(Unix秒)
待转换对象
默认值。待转换对象无效时使用
全角为半角
全角半角的关系是相差0xFEE0
时间日期转为yyyy-MM-dd HH:mm:ss完整字符串
待转换对象
字符串空值时显示的字符串,null表示原样显示最小时间,String.Empty表示不显示
时间日期转为指定格式字符串
待转换对象
格式化字符串
字符串空值时显示的字符串,null表示原样显示最小时间,String.Empty表示不显示
获取内部真实异常
获取异常消息
异常
字节单位字符串
数值
格式化字符串
数据位助手
设置数据位
数值
设置数据位
数值
设置数据位
数值
获取数据位
数值
获取数据位
数值
获取数据位
数值
并行字典扩展
从并行字典中删除
网络结点扩展
枚举类型助手类
枚举变量是否包含指定标识
枚举变量
要判断的标识
设置标识位
数值
获取枚举字段的注释
数值
获取枚举类型的所有字段注释
获取枚举类型的所有字段注释
扩展List,支持遍历中修改元素
线程安全,搜索并返回第一个,支持遍历中修改元素
实体列表
条件
线程安全,搜索并返回第一个,支持遍历中修改元素
实体列表
条件
字符串助手类
忽略大小写的字符串相等比较,判断是否以任意一个待比较字符串相等
字符串
待比较字符串数组
忽略大小写的字符串开始比较,判断是否以任意一个待比较字符串开始
字符串
待比较字符串数组
忽略大小写的字符串结束比较,判断是否以任意一个待比较字符串结束
字符串
待比较字符串数组
指示指定的字符串是 null 还是 String.Empty 字符串
字符串
是否空或者空白字符串
字符串
拆分字符串,过滤空格,无效时返回空数组
字符串
分组分隔符,默认逗号分号
拆分字符串成为整型数组,默认逗号分号分隔,无效时返回空数组
过滤空格、过滤无效、不过滤重复
字符串
分组分隔符,默认逗号分号
拆分字符串成为不区分大小写的可空名值字典。逗号分号分组,等号分隔
字符串
名值分隔符,默认等于号
分组分隔符,默认逗号分号
拆分字符串成为不区分大小写的可空名值字典。逗号分组,等号分隔
字符串
名值分隔符,默认等于号
分组分隔符,默认分号
去掉括号
在.netCore需要区分该部分内容
把一个列表组合成为一个字符串,默认逗号分隔
组合分隔符,默认逗号
把一个列表组合成为一个字符串,默认逗号分隔
组合分隔符,默认逗号
把对象转为字符串的委托
把一个列表组合成为一个字符串,默认逗号分隔
组合分隔符,默认逗号
把对象转为字符串的委托
追加分隔符字符串,忽略开头,常用于拼接
字符串构造者
分隔符
字符串转数组
字符串
编码,默认utf-8无BOM
格式化字符串。特别支持无格式化字符串的时间参数
格式字符串
参数
确保字符串以指定的另一字符串开始,不区分大小写
字符串
确保字符串以指定的另一字符串结束,不区分大小写
字符串
从当前字符串开头移除另一字符串,不区分大小写,循环多次匹配前缀
当前字符串
另一字符串
从当前字符串结尾移除另一字符串,不区分大小写,循环多次匹配后缀
当前字符串
另一字符串
从字符串中检索子字符串,在指定头部字符串之后,指定尾部字符串之前
常用于截取xml某一个元素等操作
目标字符串
头部字符串,在它之后
尾部字符串,在它之前
搜索的开始位置
位置数组,两个元素分别记录头尾位置
根据最大长度截取字符串,并允许以指定空白填充末尾
字符串
截取后字符串的最大允许长度,包含后面填充
需要填充在后面的字符串,比如几个圆点
从当前字符串开头移除另一字符串以及之前的部分
当前字符串
另一字符串
从当前字符串结尾移除另一字符串以及之后的部分
当前字符串
另一字符串
编辑距离搜索,从词组中找到最接近关键字的若干匹配项
算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
关键字
词组
编辑距离
又称Levenshtein距离(也叫做Edit Distance),是指两个字串之间,由一个转成另一个所需的最少编辑操作次数。
许可的编辑操作包括将一个字符替换成另一个字符,插入一个字符,删除一个字符。
算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
最长公共子序列搜索,从词组中找到最接近关键字的若干匹配项
算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
最长公共子序列问题是寻找两个或多个已知数列最长的子序列。
一个数列 S,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。
The longest common subsequence (LCS) problem is to find the longest subsequence common to all sequences in a set of sequences (often just two). Note that subsequence is different from a substring, see substring vs. subsequence. It is a classic computer science problem, the basis of diff (a file comparison program that outputs the differences between two files), and has applications in bioinformatics.
算法代码由@Aimeast 独立完成。http://www.cnblogs.com/Aimeast/archive/2011/09/05/2167844.html
多个关键字。长度必须大于0,必须按照字符串长度升序排列。
根据列表项成员计算距离
在列表项中进行模糊搜索
模糊匹配
模糊匹配
模糊匹配
列表项
关键字
匹配字符串选择
获取个数
权重阀值
调用语音引擎说出指定话
异步调用语音引擎说出指定话。可能导致后来的调用打断前面的语音
启用语音提示
语音提示操作
停止所有语音播报
以隐藏窗口执行命令行
文件名
命令参数
等待毫秒数
进程输出内容。默认为空时输出到日志
进程退出时执行
进程退出代码
IO工具类
压缩数据流
输入流
输出流。如果不指定,则内部实例化一个内存流
返回输出流,注意此时指针位于末端
解压缩数据流
输入流
输出流。如果不指定,则内部实例化一个内存流
返回输出流,注意此时指针位于末端
压缩字节数组
字节数组
解压缩字节数组
字节数组
压缩数据流
输入流
输出流。如果不指定,则内部实例化一个内存流
返回输出流,注意此时指针位于末端
解压缩数据流
输入流
输出流。如果不指定,则内部实例化一个内存流
返回输出流,注意此时指针位于末端
复制数据流
源数据流
目的数据流
缓冲区大小,也就是每次复制的大小
最大复制字节数
返回复制的总字节数
把一个数据流写入到另一个数据流
目的数据流
源数据流
缓冲区大小,也就是每次复制的大小
最大复制字节数
把一个字节数组写入到一个数据流
目的数据流
源数据流
写入字节数组,先写入压缩整数表示的长度
读取字节数组,先读取压缩整数表示的长度
写入Unix格式时间,1970年以来秒数,绝对时间,非UTC
读取Unix格式时间,1970年以来秒数,绝对时间,非UTC
复制数组
源数组
起始位置
复制字节数
返回复制的总字节数
向字节数组写入一片数据
目标数组
目标偏移
源数组
源数组偏移
数量
返回实际写入的字节个数
合并两个数组
源数组
目标数组
起始位置
字节数
数据流转为字节数组
针对MemoryStream进行优化。内存流的Read实现是一个个字节复制,而ToArray是调用内部内存复制方法
如果要读完数据,又不支持定位,则采用内存流搬运
如果指定长度超过数据流长度,就让其报错,因为那是调用者所期望的值
数据流
长度,0表示读到结束
数据流转为字节数组,从0开始,无视数据流的当前位置
数据流
从数据流中读取字节数组,直到遇到指定字节数组
数据流
字节数组
字节数组中的偏移
字节数组中的查找长度
未找到时返回空,0位置范围大小为0的字节数组
从数据流中读取字节数组,直到遇到指定字节数组
数据流
从数据流中读取一行,直到遇到换行
数据流
未找到返回null,0位置返回String.Empty
流转换为字符串
目标流
编码格式
字节数组转换为字符串
字节数组
编码格式
字节数组中的偏移
字节数组中的查找长度
从字节数据指定位置读取一个无符号16位整数
偏移
是否小端字节序
从字节数据指定位置读取一个无符号32位整数
偏移
是否小端字节序
从字节数据指定位置读取一个无符号64位整数
偏移
是否小端字节序
向字节数组的指定位置写入一个无符号16位整数
数字
偏移
是否小端字节序
向字节数组的指定位置写入一个无符号32位整数
数字
偏移
是否小端字节序
向字节数组的指定位置写入一个无符号64位整数
数字
偏移
是否小端字节序
整数转为字节数组,注意大小端字节序
整数转为字节数组,注意大小端字节序
整数转为字节数组,注意大小端字节序
整数转为字节数组,注意大小端字节序
整数转为字节数组,注意大小端字节序
整数转为字节数组,注意大小端字节序
以压缩格式读取32位整数
数据流
以压缩格式读取32位整数
数据流
尝试读取压缩编码整数
以7位压缩格式写入32位整数,小于7位用1个字节,小于14位用2个字节。
由每次写入的一个字节的第一位标记后面的字节是否还是当前数据,所以每个字节实际可利用存储空间只有后7位。
数据流
数值
实际写入字节数
获取压缩编码整数
在数据流中查找字节数组的位置,流指针会移动到结尾
数据流
字节数组
字节数组中的偏移
字节数组中的查找长度
在字节数组中查找另一个字节数组的位置,不存在则返回-1
字节数组
另一个字节数组
偏移
查找长度
在字节数组中查找另一个字节数组的位置,不存在则返回-1
字节数组
源数组起始位置
查找长度
另一个字节数组
偏移
查找长度
比较两个字节数组大小。相等返回0,不等则返回不等的位置,如果位置为0,则返回1。
缓冲区
比较两个字节数组大小。相等返回0,不等则返回不等的位置,如果位置为0,则返回1。
数量
缓冲区
偏移
字节数组分割
一个数据流是否以另一个数组开头。如果成功,指针移到目标之后,否则保持指针位置不变。
缓冲区
一个数据流是否以另一个数组结尾。如果成功,指针移到目标之后,否则保持指针位置不变。
缓冲区
一个数组是否以另一个数组开头
缓冲区
一个数组是否以另一个数组结尾
缓冲区
倒序、更换字节序
字节数组
把字节数组编码为十六进制字符串
字节数组
偏移
数量。超过实际数量时,使用实际数量
把字节数组编码为十六进制字符串,带有分隔符和分组功能
字节数组
分隔符
分组大小,为0时对每个字节应用分隔符,否则对每个分组使用
最大显示多少个字节。默认-1显示全部
解密
Hex编码的字符串
起始位置
长度
字节数组转为Base64编码
是否换行显示
字节数组转为Url改进型Base64编码
Base64字符串转为字节数组
路径操作帮助
基础目录。GetFullPath依赖于此,默认为当前应用程序域基础目录
获取文件或目录的全路径,过滤相对目录
不确保目录后面一定有分隔符,是否有分隔符由原始路径末尾决定
文件或目录
获取文件或目录基于应用程序域基目录的全路径,过滤相对目录
不确保目录后面一定有分隔符,是否有分隔符由原始路径末尾决定
文件或目录
获取文件或目录基于当前目录的全路径,过滤相对目录
不确保目录后面一定有分隔符,是否有分隔符由原始路径末尾决定
文件或目录
确保目录存在,若不存在则创建
斜杠结尾的路径一定是目录,无视第二参数;
默认是文件,这样子只需要确保上一层目录存在即可,否则如果把文件当成了目录,目录的创建会导致文件无法创建。
文件路径或目录路径,斜杠结尾的路径一定是目录,无视第二参数
该路径是否是否文件路径。文件路径需要取目录部分
合并多段路径
文件路径作为文件信息
从文件中读取数据
把数据写入文件指定位置
读取所有文本,自动检测编码
性能较File.ReadAllText略慢,可通过提前检测BOM编码来优化
把文本写入文件,自动检测编码
复制到目标文件,目标文件必须已存在,且源文件较新
源文件
目标文件
打开并读取
返回类型
文件信息
要对文件流操作的委托
打开并写入
返回类型
文件信息
要对文件流操作的委托
解压缩
是否覆盖目标同名文件
压缩文件
路径作为目录信息
获取目录内所有符合条件的文件,支持多文件扩展匹配
目录
文件扩展列表。比如*.exe;*.dll;*.config
是否包含所有子孙目录文件
复制目录中的文件
源目录
目标目录
文件扩展列表。比如*.exe;*.dll;*.config
是否包含所有子孙目录文件
复制每一个文件之前的回调
对比源目录和目标目录,复制双方都存在且源目录较新的文件
源目录
目标目录
文件扩展列表。比如*.exe;*.dll;*.config
是否包含所有子孙目录文件
复制每一个文件之前的回调
从多个目标目录复制较新文件到当前目录
当前目录
多个目标目录
文件扩展列表。比如*.exe;*.dll;*.config
是否包含所有子孙目录文件
压缩
模型扩展
获取指定类型的服务对象
对象容器助手。扩展方法专用
注册类型和名称
接口类型
实现类型
对象容器
标识
优先级
注册类型指定名称的实例
接口类型
对象容器
实例
标识
优先级
解析类型指定名称的实例
接口类型
对象容器
标识
解析类型指定名称的实例
接口类型
对象容器
标识
遍历所有程序集的所有类型,自动注册实现了指定接口或基类的类型。如果没有注册任何实现,则默认注册第一个排除类型
自动注册一般用于单实例功能扩展型接口
接口类型
要排除的类型,一般是内部默认实现
对象容器
解析接口指定名称的实现类型
接口类型
对象容器
标识
网络工具类
设置超时检测时间和检测间隔
要设置的Socket对象
是否启用Keep-Alive
多长时间后开始第一次探测(单位:毫秒)
探测时间间隔(单位:毫秒)
分析地址,根据IP或者域名得到IP地址,缓存60秒,异步更新
分析网络终结点
地址,可以不带端口
地址不带端口时指定的默认端口
针对IPv4和IPv6获取合适的Any地址
除了Any地址以为,其它地址不具备等效性
是否Any地址,同时处理IPv4和IPv6
是否Any结点
是否IPv4地址
是否本地地址
获取相对于指定远程地址的本地地址
获取相对于指定远程地址的本地地址
指定地址的指定端口是否已被使用,似乎没办法判断IPv6地址
检查该协议的地址端口是否已经呗使用
获取活动的接口信息
获取可用的DHCP地址
获取可用的DNS地址
获取可用的网关地址
获取可用的IP地址
获取本机可用IP地址,缓存60秒,异步更新
获取可用的多播地址
获取以太网MAC地址
获取本地第一个IPv4地址
获取本地第一个IPv6地址
唤醒指定MAC地址的计算机
根据IP地址获取MAC地址
获取IP地址的物理地址位置
根据字符串形式IP地址转为物理地址
IP地址提供者接口
获取IP地址的物理地址位置
根据本地网络标识创建客户端
根据远程网络标识创建客户端
特性辅助类
获取自定义属性,带有缓存功能,避免因.Net内部GetCustomAttributes没有缓存而带来的损耗
获取成员绑定的显示名,优先DisplayName,然后Description
获取成员绑定的显示名,优先DisplayName,然后Description
获取自定义属性的值。可用于ReflectionOnly加载的程序集
获取自定义属性的值。可用于ReflectionOnly加载的程序集
目标对象
是否递归
安全算法
MD5散列
MD5散列
字符串编码,默认Default
MD5散列
字符串编码,默认Default
Crc散列
Crc16散列
SHA128
SHA256
SHA384
SHA512
对称加密算法扩展
注意:CryptoStream会把 outstream 数据流关闭
对称加密算法扩展
算法
数据
密码
模式。.Net默认CBC,Java默认ECB
填充算法。默认PKCS7,等同Java的PKCS5
对称解密算法扩展
注意:CryptoStream会把 instream 数据流关闭
对称解密算法扩展
算法
数据
密码
模式。.Net默认CBC,Java默认ECB
填充算法。默认PKCS7,等同Java的PKCS5
RC4对称加密算法
控件助手
执行无参委托
执行单一参数无返回值的委托
执行二参数无返回值的委托
附加文本到文本控件末尾。主要解决非UI线程以及滚动控件等问题
控件
消息
最大行数。超过该行数讲清空控件
滚动控件的滚动条
指定控件
是否底端,或者顶端
处理回车,移到行首
设置默认样式,包括字体、前景色、背景色
控件
字体大小
设置字体大小
采用默认着色方案进行着色
文本控件
开始位置
改变C++类名方法名颜色
着色文本控件的内容
文本控件
正则表达式
开始位置
颜色数组
着色文本控件的内容
文本控件
正则表达式
开始位置
颜色数组
当前Dpi
修正ListView的Dpi
修正窗体的Dpi
================================================
FILE: DLL/NewLife.RocketMQ.xml
================================================
NewLife.RocketMQ
代理客户端
服务器地址
实例化代理客户端
启动
注销客户端
心跳
获取运行时信息
集群客户端
维护到一个集群的客户端连接,内部采用负载均衡调度算法。
编号
名称
超时。默认3000ms
服务器地址集合
配置
实例化
销毁
开始
确保创建连接
发送命令
发送指定类型的命令
建立命令时,处理头部
收到命令时
收到命令
日志
写日志
权限
写入
读取
代理信息
名称
地址集合
权限
读队列数
写队列数
主题同步标记
相等比较
计算哈希
带权重负载均衡算法
权重集合
最小权重
状态值
次数
实例化
根据权重选择,并返回该项是第几次选中
根据权重选择
消费者
数据
消费间隔。默认15_000ms
持久化消费偏移间隔。默认5_000ms
拉取的批大小。默认32
启动时间
从最后偏移开始消费。默认true
消费委托
销毁
启动
从指定队列拉取消息
查询指定队列的偏移量
查询“队列”最大偏移量,不是消费提交的最后偏移量
获取最小偏移量
根据时间戳查询偏移
更新队列的偏移
获取消费者下所有消费者
启动消费者时自动开始调度。默认true
开始调度
拉取到一批消息
当前所需要消费的队列。由均衡算法产生
相等比较
计算哈希
重新平衡消费队列
收到命令
业务基类
名称服务器地址
消费组
主题
本地IP地址
实例名
拉取名称服务器间隔。默认30_000ms
Broker心跳间隔。默认30_000ms
单元名称
单元模式
是否可用
代理集合
名称服务器
获取名称服务器地址的http地址
访问令牌
访问密钥
阿里云MQ通道
客户端标识
实例化
销毁
友好字符串
开始
获取代理客户端
Broker客户端集合
收到命令
日志
写日志
连接名称服务器的客户端
Broker集合
代理改变时触发
实例化
启动
获取主题的路由信息,含登录验证
生产者
启动
发送消息
发布消息
创建主题
选择队列
命令
头部
主体
是否响应
从数据流中读取
读取Body作为Json返回
写入命令到数据流
命令转字节数组
创建响应
友好字符串
头部
请求/响应码
扩展字段
这个字段不通的请求/响应不一样,完全自定义。数据结构上是java的hashmap。
在Java的每个RemotingCammand中,其实都带有一个CommandCustomHeader的属性成员,可以认为他是一个强类型的extFields,
再最后传输的时候,这个CommandCustomHeader会被忽略,而传输前会把其中的所有字段全部都原封不动塞到extFields中,以作传输。
标识
第0位标识是这次通信是request还是response,0标识request, 1 标识response。
第1位标识是否是oneway请求,1标识oneway。应答方在处理oneway请求的时候,不会做出响应,请求方也无需等待应答方响应。
由于要支持多语言,所以这一字段可以给通信双方知道对方通信层锁使用的开发语言
这里必须是JAVA,不能是CSharp,甚至Java都不行
请求标识码。在Java版的通信层中,这个只是一个不断自增的整形,为了收到应答方响应的的时候找到对应的请求。
序列化类型
给通信层知道对方的版本号,响应方可以以此做兼容老版本等的特殊操作
附带的文本信息。常见的如存放一些broker/nameserver返回的一些异常信息,方便开发人员定位问题。
获取扩展字段。如果为空则创建
心跳数据
客户端编号
消费数据集
生产者数据集
生产者数据
组名
消费者数据
从哪里开始消费
消费类型
组名
消息模型。广播/集群
订阅数据集
单元模式
订阅者数据
主题
表达式类型
子字符串
标签集合
代码集合
过滤模式
过滤源
子版本
消息
主题
标签
键
标记
消息体
消息体。字符串格式
等待存储消息
延迟时间等级
友好字符串
获取属性
设置数据
消息扩展
队列编号
存储大小
CRC校验
队列偏移
提交日志偏移
系统标记
生产时间
生产主机
存储时间
存储主机
重新消费次数
准备事务偏移
属性
消息编号
友好字符串
从数据流中读取
读取所有消息
写入命令到数据流
消息队列
主题
代理名称
队列编号
相等比较
计算哈希
友好字符串
编码器
实例化编码器
编码
加入队列
解码
连接关闭时,清空粘包编码器
是否匹配响应
拉取信息请求头
消费组
主题
订阅表达式
挂起超时时间。默认20_000ms
子版本
队列
队列偏移
最大消息数
提交偏移
系统标记
获取属性字典
拉取状态
已发现
没有新的消息
没有匹配消息
偏移量非法
未知类型
拉取结果
状态
最小偏移
最大偏移
下一轮拉取偏移
消息
友好字符串
读取数据
查询结果
最后更新时间
消息列表
请求代码
发消息
收消息
查询消费偏移
用于向brokers查询所有的topic和它们的配置
获取代理配置
获取代理运行时信息,包括broker版本、磁盘容量、系统负载等
获取topic/队列偏移量的最大值
获取topic/队列偏移量的最小值
发送心跳
注销
当consumer客户端无法处理消息时,将这些消息发送回brokers,以便将来将这些消息重新发送给consumers
查询每个consumer group的存活成员
当broker得知一个consumer宕机时,它会通知其他工作的consumers尽快重新平衡
获取所有的消费的偏移量
获取延迟topic的偏移量
获取topic路由信息
获取群集信息
创建新的consumer group或更新现有的consumer group以更改属性
查询所有已知的consumer group配置
查询topic相关的统计信息
查询所有topic
要求broker从consumer客户端按给定的时间戳重置偏移量
update the config of name server
get config from name server
批处理模式发送消息
响应码
响应异常
响应代码
实例化响应异常
发送消息请求头
生产组
主题
默认主题
默认主题队列数
队列编号
系统标记
生产时间。毫秒
标记
属性。Tags/Keys等
重新消费次数
单元模式
获取属性字典
发送状态
成功
刷盘超时
刷从机超时
从机不可用
发送结果
状态
消息编号
队列
队列偏移
事务编号
偏移消息编号
区域
读取结果
服务状态
刚刚建立
运行中
已经关闭
启动失败
================================================
FILE: Doc/Changelog.md
================================================
# NewLife.RocketMQ 更新日志 2026
## v3.0.2026.0304 (2026-03-04)
### 架构优化
* 重构 RocketMQ gRPC 协议为 SpanReader/SpanWriter 实现,提升性能
* 重构架构文档,优化编解码器实现
* 优化项目文件描述及标题信息
### 文档完善
* 文档体系重构:新增架构与需求文档,优化 Readme
* 新增架构设计文档和功能分析文档
### 依赖升级
* 升级 NewLife.Core 依赖到最新版本
---
## v3.0.2026.0228 (2026-02-28)
### 问题修复
* 修复逻辑缺陷并补充单元测试
* 新增客户端拉取超时机制,防止 RocketMQ 4.9.8 消费者线程卡死
---
## v3.0.2026.0216 (2026-02-16)
### 重大更新:v3.0 云适配重构
* 云适配重构与功能全面单元测试覆盖
* 完善兼容性梳理与优化进展
### 新增特性
* 新增 VIP 通道支持
* 新增批量消息确认机制
* 新增 5.x MsgId 支持
* 新增 gRPC Telemetry 遥测支持
* 完整实现**事务消息**功能(PR #108)
- 增加 RocketMQ 事务消息发布与结束接口
- 完善事务消息实现并通过审查与安全扫描
* 完整实现**请求-应答模式** (Request-Reply) 功能(PR #107)
- 完善 RocketMQ 5.0 特性支持
- 通过单元测试和文档验证
### 文档完善
* 新增功能分析与架构设计文档
* Phase 6 文档总结:兼容性梳理与优化进展
### 依赖升级
* 升级 NuGet 依赖包
---
## v3.0.2026.0211 (2026-02-11)
### 依赖升级
* 升级 NewLife.Core 依赖到最新版本
================================================
FILE: Doc/RequestReply_Guide.md
================================================
# RocketMQ Request-Reply 使用指南
## 概述
从 RocketMQ 4.6.0 版本开始,引入了 Request-Reply 特性,该特性允许生产者在发送消息后同步或异步等待消费者消费完消息并返回响应消息,实现类似 RPC 调用的效果。
NewLife.RocketMQ 现已支持此特性,并兼容 RocketMQ 5.0+。
## 主要特性
- **同步请求**:发送请求后阻塞等待响应
- **异步请求**:发送请求后异步等待响应
- **超时控制**:支持设置请求超时时间
- **自动关联**:自动管理请求和响应的关联
- **简单易用**:API 设计简洁,易于集成
## 使用示例
### 1. 生产者端 - 发送请求
#### 同步请求
```csharp
using NewLife.RocketMQ;
// 创建生产者
var producer = new Producer
{
Topic = "request_topic",
NameServerAddress = "127.0.0.1:9876",
RequestTimeout = 3000 // 设置默认超时时间为3秒
};
producer.Start();
try
{
// 发送请求并同步等待响应
var requestBody = "这是一个请求消息";
var response = producer.Request(requestBody, timeout: 5000);
Console.WriteLine($"收到响应: {response.BodyString}");
}
finally
{
producer.Stop();
producer.Dispose();
}
```
#### 异步请求
```csharp
using NewLife.RocketMQ;
// 创建生产者
var producer = new Producer
{
Topic = "request_topic",
NameServerAddress = "127.0.0.1:9876",
RequestTimeout = 3000
};
producer.Start();
try
{
// 异步发送请求并等待响应
var requestBody = "这是一个异步请求消息";
var response = await producer.RequestAsync(requestBody, timeout: 5000);
Console.WriteLine($"收到响应: {response.BodyString}");
}
finally
{
producer.Stop();
producer.Dispose();
}
```
### 2. 消费者端 - 处理请求并发送回复
#### 同步处理
```csharp
using NewLife.RocketMQ;
// 创建消费者
var consumer = new Consumer
{
Topic = "request_topic",
Group = "request_consumer_group",
NameServerAddress = "127.0.0.1:9876",
FromLastOffset = false
};
// 设置消息处理回调
consumer.OnConsume = (queue, messages) =>
{
foreach (var message in messages)
{
Console.WriteLine($"收到请求: {message.BodyString}");
// 检查是否是请求消息
if (!String.IsNullOrEmpty(message.CorrelationId))
{
// 处理业务逻辑
var result = ProcessRequest(message.BodyString);
// 发送回复
consumer.SendReply(message, result);
Console.WriteLine($"已发送回复: {result}");
}
}
return true;
};
consumer.Start();
// 保持运行
Console.WriteLine("消费者已启动,按任意键退出...");
Console.ReadKey();
consumer.Stop();
consumer.Dispose();
string ProcessRequest(string request)
{
// 实现你的业务逻辑
return $"处理结果: {request}";
}
```
#### 异步处理
```csharp
using NewLife.RocketMQ;
// 创建消费者
var consumer = new Consumer
{
Topic = "request_topic",
Group = "request_consumer_group",
NameServerAddress = "127.0.0.1:9876",
FromLastOffset = false
};
// 设置异步消息处理回调
consumer.OnConsumeAsync = async (queue, messages, cancellationToken) =>
{
foreach (var message in messages)
{
Console.WriteLine($"收到请求: {message.BodyString}");
// 检查是否是请求消息
if (!String.IsNullOrEmpty(message.CorrelationId))
{
// 异步处理业务逻辑
var result = await ProcessRequestAsync(message.BodyString, cancellationToken);
// 异步发送回复
await consumer.SendReplyAsync(message, result, cancellationToken);
Console.WriteLine($"已发送回复: {result}");
}
}
return true;
};
consumer.Start();
// 保持运行
Console.WriteLine("消费者已启动,按任意键退出...");
Console.ReadKey();
consumer.Stop();
consumer.Dispose();
async Task ProcessRequestAsync(string request, CancellationToken ct)
{
// 实现你的异步业务逻辑
await Task.Delay(100, ct); // 模拟异步操作
return $"处理结果: {request}";
}
```
### 3. 超时处理
```csharp
using NewLife.RocketMQ;
var producer = new Producer
{
Topic = "request_topic",
NameServerAddress = "127.0.0.1:9876",
RequestTimeout = 1000 // 默认超时1秒
};
producer.Start();
try
{
var response = await producer.RequestAsync("请求消息");
Console.WriteLine($"收到响应: {response.BodyString}");
}
catch (TimeoutException ex)
{
Console.WriteLine($"请求超时: {ex.Message}");
}
finally
{
producer.Stop();
producer.Dispose();
}
```
## API 参考
### Producer 类
#### 属性
- `RequestTimeout`:请求超时时间(毫秒),默认 3000ms
#### 方法
- `MessageExt Request(Message message, Int32 timeout = -1)`
- 发送请求消息,同步等待响应
- 参数:
- `message`:请求消息
- `timeout`:超时时间(毫秒),-1 表示使用默认超时时间
- 返回:响应消息
- 异常:`TimeoutException` - 请求超时
- `MessageExt Request(Object body, Int32 timeout = -1)`
- 发送请求消息,同步等待响应(简化版本)
- 参数:
- `body`:消息体内容
- `timeout`:超时时间(毫秒)
- 返回:响应消息
- `Task RequestAsync(Message message, Int32 timeout = -1, CancellationToken cancellationToken = default)`
- 异步发送请求消息并等待响应
- 参数:
- `message`:请求消息
- `timeout`:超时时间(毫秒)
- `cancellationToken`:取消令牌
- 返回:响应消息
- 异常:`TimeoutException` - 请求超时
- `Task RequestAsync(Object body, Int32 timeout = -1, CancellationToken cancellationToken = default)`
- 异步发送请求消息并等待响应(简化版本)
### Consumer 类
#### 方法
- `SendResult SendReply(MessageExt requestMessage, Object replyBody)`
- 发送回复消息
- 参数:
- `requestMessage`:原始请求消息
- `replyBody`:回复消息内容
- 返回:发送结果
- 异常:
- `ArgumentNullException` - 参数为空
- `InvalidOperationException` - 请求消息缺少必要属性
- `Task SendReplyAsync(MessageExt requestMessage, Object replyBody, CancellationToken cancellationToken = default)`
- 异步发送回复消息
- 参数:
- `requestMessage`:原始请求消息
- `replyBody`:回复消息内容
- `cancellationToken`:取消令牌
- 返回:发送结果
### Message 类新增属性
- `ReplyToClient`:回复地址,指示回复消息应发送到的客户端ID
- `CorrelationId`:关联ID,用于将回复消息与请求消息关联
- `MessageType`:消息类型,用于区分普通消息和回复消息("REQUEST"/"REPLY")
- `RequestTimeout`:请求超时时间(毫秒)
## 注意事项
1. **版本要求**:需要 RocketMQ 服务器版本 4.6.0 或更高
2. **超时设置**:合理设置超时时间,避免长时间阻塞
3. **异常处理**:务必捕获 `TimeoutException` 处理超时情况
4. **资源释放**:使用完毕后及时释放 Producer 和 Consumer 资源
5. **Topic 规划**:建议为 Request-Reply 使用独立的 Topic
6. **消费者处理**:消费者必须检查 `CorrelationId` 属性来判断是否为请求消息
## 兼容性
- 支持 .NET Framework 4.5+
- 支持 .NET Standard 2.0+
- 支持 .NET Core 2.0+
- 支持 .NET 5.0+
- 兼容 RocketMQ 4.6.0 或以上版本
- 兼容 RocketMQ 5.0 或以上版本
## 性能建议
1. 复用 Producer 和 Consumer 实例,避免频繁创建销毁
2. 合理设置超时时间,避免资源浪费
3. 对于高并发场景,建议使用异步 API
4. 监控回复消息的处理时间,及时优化业务逻辑
## 故障排查
### 请求超时
- 检查消费者是否正常运行
- 检查网络连接是否正常
- 检查消费者处理逻辑是否耗时过长
- 适当增加超时时间
### 收不到回复
- 确认消费者正确调用了 `SendReply` 或 `SendReplyAsync`
- 检查消费者日志,确认是否有异常
- 确认消息的 `CorrelationId` 属性正确设置
- 检查 Topic 配置是否正确
## 更多示例
更多使用示例请参考项目源码中的单元测试:`XUnitTestRocketMQ/RequestReplyTests.cs`
================================================
FILE: Doc/架构设计.md
================================================
# NewLife.RocketMQ 架构
## 1. 架构概览
NewLife.RocketMQ 采用**分层清晰、职责单一、可扩展**的架构设计,纯 C# 实现,零外部依赖。
### 1.1 设计理念
- **纯托管实现**:完全使用 C# 实现,无需 Java、gRPC、Protobuf 第三方库
- **双协议支持**:同时支持 Remoting 协议(4.x)和 gRPC 协议(5.x)
- **云厂商适配**:统一 `ICloudProvider` 接口,轻松接入各云服务商
- **高性能优化**:连接复用、对象池、VIP 通道、消息压缩等优化手段
- **可测试性**:30+ 测试类覆盖核心功能和边缘场景
### 1.2 层次结构
```
┌──────────────────────────────────────────────────────────┐
│ 业务层 (MqBase) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Producer │ │ Consumer │ │
│ │ 生产者业务逻辑 │ │ 消费者业务逻辑 │ │
│ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────┐
│ 通信层 (Client) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ ClusterClient │ │ GrpcClient │ │
│ │ TCP 长连接管理 │ │ HTTP/2 gRPC │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ NameClient │ │GrpcMessaging │ │
│ │ 路由发现 │ │Service │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ │
│ │ BrokerClient │ │
│ │ 心跳/注销 │ │
│ └─────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────┐
│ 协议层 (Protocol) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ Command │ │ GrpcModels │ │
│ │ Remoting 帧 │ │ gRPC 消息模型 │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ MessageExt │ │ ProtoWriter │ │
│ │ 消息模型 │ │ Protobuf 编码 │ │
│ └─────────────────┘ └─────────────────┘ │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ RequestCode │ │ ProtoReader │ │
│ │ 请求码枚举 │ │ Protobuf 解码 │ │
│ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────┐
│ 传输层 (Transport) │
│ ┌─────────────────┐ ┌─────────────────┐ │
│ │ NewLife.Net │ │ HttpClient │ │
│ │ TCP Socket │ │ HTTP/2 │ │
│ └─────────────────┘ └─────────────────┘ │
└──────────────────────────────────────────────────────────┘
│
┌──────────────────────────────────────────────────────────┐
│ 云厂商适配层 (CloudProvider) │
│ ┌────────┐ ┌────────┐ ┌────────┐ ┌────────┐ │
│ │Aliyun │ │Huawei │ │Tencent │ │ ACL │ │
│ │Provider│ │Provider│ │Provider│ │Provider│ │
│ └────────┘ └────────┘ └────────┘ └────────┘ │
└──────────────────────────────────────────────────────────┘
```
### 1.3 代码组织结构
```
MqBase (业务基类,NameServer连接/Broker管理/Topic与消费组CRUD/消息查询)
├── Producer (生产者:普通/异步/单向/延迟/事务/批量/Request-Reply/gRPC)
└── Consumer (消费者:Pull/调度/Rebalance/多Topic/顺序/重试/Pop/gRPC)
ClusterClient (集群客户端/通信层,Remoting协议)
├── NameClient (名称服务器客户端:路由发现/定时轮询/多Topic路由/Broker主从解析)
└── BrokerClient (Broker客户端:心跳/注销/命令收发)
Grpc/ (gRPC传输层,RocketMQ 5.x Proxy协议,netstandard2.1+)
├── GrpcClient (HTTP/2 gRPC客户端,帧编解码,Unary + Server Streaming)
├── GrpcMessagingService (消息服务:路由/发送/接收/确认/心跳/事务/延迟/死信)
├── ProtoWriter / ProtoReader (轻量级Protobuf编解码器,无外部依赖)
├── GrpcModels (Resource/Endpoints/Message/SystemProperties/MessageQueue等)
├── GrpcServiceMessages (Request/Response消息类型,约25个,含Telemetry)
└── GrpcEnums (GrpcCode/GrpcMessageType/GrpcClientType/AddressScheme等)
Protocol/
├── Command (命令帧,Remoting协议编解码)
├── MqCodec (网络编解码器)
├── Message / MessageExt (消息模型,含批量解码/ZLIB解压/IPv4+IPv6/5.x MessageId)
├── RequestCode (约60个指令码) / ResponseCode (约20个响应码)
├── MQVersion (V3.0 ~ V5.9.9 + HIGHER_VERSION,约450个版本)
└── 各类 Header / State / Enum
CloudProvider/
├── ICloudProvider (统一云厂商接口)
├── AliyunProvider (阿里云适配)
├── AclProvider (Apache ACL适配)
├── HuaweiProvider (华为云适配)
└── TencentProvider (腾讯云适配)
```
---
## 2. 数据模型
### 2.1 消息模型
| 类型 | 说明 | 字段 |
|------|------|------|
| Message | 基础消息 | Topic, Tags, Keys, Body, Properties, DelayTimeLevel |
| MessageExt | 扩展消息(接收端) | MsgId, QueueId, QueueOffset, BornTimestamp, StoreTimestamp, BornHost, StoreHost, SysFlag, ReconsumeTimes |
| GrpcMessage | gRPC 消息(5.x) | Topic(GrpcResource), UserProperties, SystemProperties(GrpcSystemProperties), Body |
### 2.2 路由模型
| 类型 | 说明 | 字段 |
|------|------|------|
| BrokerInfo | Broker 信息 | BrokerName, MasterAddress, SlaveAddresses, Weight |
| MessageQueue | 消息队列 | BrokerName, QueueId, ReadQueueNums, WriteQueueNums |
### 2.3 协议帧模型
**Remoting 协议帧**:
```
┌────────┬────────────┬────────────┬─────────────────┐
│ Length │HeaderLength│ Header │ Body │
│ 4 bytes│ 4 bytes │ N bytes │ M bytes │
└────────┴────────────┴────────────┴─────────────────┘
Length = 4 + N + M
HeaderLength 高 8 位:SerializeType (0=JSON, 1=ROCKETMQ)
HeaderLength 低 24 位:实际 Header 长度
```
**gRPC 消息帧**:
```
┌────────┬────────────┬──────────────────────────────┐
│ Comp │ Length │ Body │
│ 1 byte │ 4 bytes │ N bytes │
└────────┴────────────┴──────────────────────────────┘
Comp: 0=不压缩, 1=gzip
Length: 大端序,Protobuf 消息体长度
```
---
## 3. 接口设计
### 3.1 生产者 API
| 接口 | 方法 | 签名 | 入参 | 出参 | 说明 |
|------|------|------|------|------|------|
| Producer | Publish | `SendResult Publish(Object message, String tags)` | message: 消息体, tags: 标签 | SendResult | 同步发送 |
| Producer | PublishAsync | `Task PublishAsync(Object message, String tags)` | 同上 | Task\ | 异步发送 |
| Producer | PublishOneway | `void PublishOneway(Object message, String tags)` | 同上 | 无 | 单向发送 |
| Producer | PublishBatch | `SendResult PublishBatch(IList messages)` | messages: 消息列表 | SendResult | 批量发送 |
| Producer | PublishDelay | `SendResult PublishDelay(Object message, DelayTimeLevels delay)` | message, delay: 延迟等级 | SendResult | 延迟消息 |
| Producer | PublishDelayViaGrpcAsync | `Task PublishDelayViaGrpcAsync(String body, DateTime deliveryTime)` | body, deliveryTime: 投递时间 | Task | gRPC 任意延迟 |
| Producer | PublishTransaction | `SendResult PublishTransaction(Object message)` | message: 消息体 | SendResult | 事务半消息 |
| Producer | EndTransaction | `void EndTransaction(SendResult result, TransactionState state)` | result, state: 提交/回滚 | 无 | 结束事务 |
| Producer | Request | `MessageExt Request(Object message, Int32 timeout)` | message, timeout: 超时 | MessageExt | Request-Reply |
### 3.2 消费者 API
| 接口 | 方法 | 签名 | 入参 | 出参 | 说明 |
|------|------|------|------|------|------|
| Consumer | OnConsume | `Func` | 委托 | — | 消费回调 |
| Consumer | Pull | `PullResult Pull(MessageQueue queue, Int64 offset, Int32 maxNums)` | queue, offset, maxNums | PullResult | 拉取消息 |
| Consumer | PopMessageAsync | `Task> PopMessageAsync(...)` | timeout 等 | IList\ | Pop 消费 |
| Consumer | AckMessageAsync | `Task AckMessageAsync(MessageExt msg)` | msg: 消息 | Task | 确认消费 |
| Consumer | BatchAckMessageAsync | `Task BatchAckMessageAsync(IList msgs)` | msgs: 消息列表 | Task | 批量确认 |
### 3.3 云厂商适配接口
| 接口 | 方法 | 签名 | 入参 | 出参 | 说明 |
|------|------|------|------|------|------|
| ICloudProvider | TransformTopic | `String TransformTopic(String topic)` | topic: 原始主题名 | 转换后主题名 | 主题名转换 |
| ICloudProvider | TransformGroup | `String TransformGroup(String group)` | group: 原始组名 | 转换后组名 | 消费组名转换 |
| ICloudProvider | GetNameServerAddress | `String GetNameServerAddress()` | 无 | NameServer 地址 | 获取 NameServer |
### 3.4 核心 RequestCode
| 分类 | RequestCode | 值 | 说明 |
|------|------------|:--:|------|
| 消息发送 | SEND_MESSAGE_V2 | 310 | 发送消息V2 |
| | SEND_BATCH_MESSAGE | 320 | 批量发送 |
| | SEND_REPLY_MESSAGE_V2 | 325 | 回复消息 |
| 消息拉取 | PULL_MESSAGE | 11 | 拉取消息 |
| | POP_MESSAGE | 200050 | Pop 消费 |
| | ACK_MESSAGE | 200051 | Pop 确认 |
| | BATCH_ACK_MESSAGE | 200151 | 批量 Pop 确认 |
| 事务消息 | END_TRANSACTION | 37 | 结束事务 |
| | CHECK_TRANSACTION_STATE | 39 | 事务回查 |
| 偏移管理 | QUERY_CONSUMER_OFFSET | 14 | 查询偏移 |
| | UPDATE_CONSUMER_OFFSET | 15 | 更新偏移 |
| | SEARCH_OFFSET_BY_TIMESTAMP | 29 | 按时间戳搜索 |
| 顺序消费 | LOCK_BATCH_MQ | 41 | 锁定队列 |
| | UNLOCK_BATCH_MQ | 42 | 解锁队列 |
| 路由管理 | GET_ROUTEINTO_BY_TOPIC | 105 | Topic 路由 |
| | GET_BROKER_CLUSTER_INFO | 106 | 集群信息 |
| 心跳 | HEART_BEAT | 34 | 心跳 |
| | UNREGISTER_CLIENT | 35 | 注销客户端 |
### 3.5 gRPC RPC 方法
| 方法 | 类型 | 说明 |
|------|------|------|
| QueryRoute | Unary | 查询主题路由 |
| SendMessage | Unary | 发送消息(普通/延迟/FIFO/事务) |
| QueryAssignment | Unary | 查询队列分配 |
| ReceiveMessage | Server Streaming | 接收消息(长轮询) |
| AckMessage | Unary | 确认消息消费 |
| Heartbeat | Unary | 心跳 |
| EndTransaction | Unary | 结束事务 |
| ForwardToDeadLetterQueue | Unary | 转发到死信队列 |
| ChangeInvisibleDuration | Unary | 修改不可见时间 |
| NotifyClientTermination | Unary | 通知客户端终止 |
| Telemetry | Bidirectional Streaming | 客户端资源上报 |
---
## 4. 技术选型
| 领域 | 选型 | 理由 |
|------|------|------|
| 网络通信(Remoting) | NewLife.Net | 高性能 TCP 库,单机千万级吞吐,内置连接池 |
| 网络通信(gRPC) | System.Net.Http.HttpClient | .NET 原生 HTTP/2 支持,无需第三方库 |
| 序列化(Remoting) | NewLife.Serialization | 内置 JSON 序列化,无外部依赖 |
| 序列化(gRPC) | 自研 ProtoWriter/ProtoReader | 轻量级 Protobuf 编解码,零外部依赖 |
| 加密签名 | System.Security.Cryptography | .NET 内置 HMAC-SHA1 |
| SSL/TLS | System.Net.Security.SslStream | .NET 内置 SSL/TLS 支持 |
| 日志 | NewLife.Log.ILog | NewLife 核心日志组件 |
| 追踪 | NewLife.Model.ITracer | NewLife APM 追踪组件 |
| 负载均衡 | 自研 WeightRoundRobin | 加权轮询算法,支持 Broker 权重 |
| 压缩 | System.IO.Compression.DeflateStream | .NET 内置 ZLIB 压缩 |
---
## 5. 关键设计决策
| 决策点 | 方案 | 备选方案 | 选择理由 |
|--------|------|---------|---------|
| Protobuf 编解码 | 自研 ProtoWriter/ProtoReader | Google.Protobuf / protobuf-net | 零外部依赖,减少包体积和部署复杂度 |
| gRPC 通信 | 原生 HttpClient + HTTP/2 | Grpc.Net.Client | 零外部依赖,仅需 netstandard2.1+ |
| 连接管理 | ConcurrentDictionary 连接池 | 第三方连接池库 | 简单高效,满足 Broker 少连接场景 |
| 消费者负载均衡 | 客户端平均分配 | 服务端 Rebalance | 兼容 4.x,5.x 服务端 Rebalance 需 Broker 配合 |
| 云厂商适配 | ICloudProvider 接口 + 策略模式 | 硬编码各厂商逻辑 | 易扩展,新增厂商只需实现接口 |
| 多目标框架 | net45/net461/netstandard2.0/netstandard2.1 | 仅 netstandard2.0 | 覆盖企业遗留系统和最新平台 |
| 消息压缩 | 发送端 ZLIB 自动压缩 | 不压缩 / 手动压缩 | 大消息体自动优化带宽 |
| VIP 通道 | BrokerPort - 2 优先级连接 | 仅标准端口 | 提升高优先级消息性能 |
---
## 6. 任务分解
所有任务已按迭代完成,详见需求文档「6. 功能清单与迭代计划」。
### 已完成批次
| 批次 | 任务范围 | 状态 |
|------|---------|:----:|
| 批次 1 | T001~T008:核心通信与基础消息 | ✅ |
| 批次 2 | T009~T016:生产可靠性增强 | ✅ |
| 批次 3 | T017~T027:功能完善 | ✅ |
| 批次 4 | T028~T032:云厂商适配 | ✅ |
| 批次 5 | T033~T038:gRPC 协议与 5.x 新特性 | ✅ |
| 批次 6 | T039~T044:功能增强与优化 | ✅ |
---
## 7. 风险与缓解
| 风险 | 影响 | 缓解措施 |
|------|------|---------|
| 各云厂商 5.x gRPC 接入未验证 | 阿里云 Serverless / 华为云 5.x / 腾讯云可能不兼容 | 客户端已具备完整 gRPC 能力,待实际环境逐步验证 |
| Language 标识为 DOTNET | 部分 Broker 可能不识别 | DOTNET 为合理标识,暂无实际影响报告 |
| 自研 Protobuf 编解码与官方不一致 | 极端数据格式可能解析失败 | 30+ 测试覆盖核心编解码场景,持续补充边界用例 |
| RocketMQ 新版本协议变更 | 新增 RequestCode / Header 字段 | MQVersion 枚举已扩展到 V5.9.9 + HIGHER_VERSION,跟进社区更新 |
| 阿里云公网版 BrokerName 不匹配 | 消费者偏移匹配可能失败 | 已用容错代码处理,不影响核心消费流程 |
| 连接泄漏或超时 | 长时间运行可能资源不足 | 心跳保活 30s + 连接池管理 + Dispose 释放 |
---
## 附录 A:核心文件清单
| 文件 | 行数 | 说明 |
|------|:----:|------|
| MqBase.cs | ~1500 | 业务基类 |
| Producer.cs | ~1200 | 生产者实现 |
| Consumer.cs | ~1800 | 消费者实现 |
| ClusterClient.cs | ~423 | TCP 连接管理 |
| NameClient.cs | ~243 | 路由发现 |
| BrokerClient.cs | ~142 | 心跳/注销 |
| Command.cs | ~333 | Remoting 帧编解码 |
| MessageExt.cs | ~244 | 消息扩展模型 |
| RequestCode.cs | ~277 | 60+ 请求码 |
| MQVersion.cs | ~909 | 450+ 协议版本 |
| GrpcClient.cs | ~310 | gRPC HTTP/2 客户端 |
| ProtoWriter.cs | ~343 | Protobuf 编码器 |
| ProtoReader.cs | ~308 | Protobuf 解码器 |
| GrpcModels.cs | ~520 | gRPC 消息模型 |
| GrpcServiceMessages.cs | ~883 | gRPC 服务消息 |
| GrpcMessagingService.cs | ~417 | 11 个 RPC 方法 |
## 附录 B:双协议特性对比
| 特性 | Remoting 协议 | gRPC 协议 |
|------|--------------|-----------|
| 传输层 | TCP 长连接(NewLife.Net) | HTTP/2(HttpClient) |
| 编解码 | 自定义二进制帧 + JSON | Protobuf(自研编解码器) |
| 支持版本 | RocketMQ 4.x / 5.x Broker | RocketMQ 5.x Proxy |
| 目标框架 | net45+ | netstandard2.1+ / net5+ |
| 连接复用 | 单连接 Opaque 复用 | HTTP/2 多路复用 |
| SSL/TLS | 支持 | 原生支持(HTTPS) |
| VIP 通道 | 支持(BrokerPort - 2) | N/A |
| 签名认证 | HMAC-SHA1 | HTTP Header |
| 消息压缩 | ZLIB(SysFlag 标记) | gzip(HTTP Content-Encoding) |
## 附录 C:各厂商 RocketMQ 产品对比
| 厂商 | 产品 | 协议 | 认证方式 | NameServer 发现 | 适配器 |
|------|------|------|---------|----------------|--------|
| Apache | RocketMQ 4.x | Remoting (TCP) | ACL (AccessKey) | 直连/HTTP | AclProvider |
| Apache | RocketMQ 5.x | Remoting + gRPC | ACL | 直连/HTTP/Proxy | AclProvider |
| 阿里云 | 消息队列 RocketMQ 4.x | Remoting | AK/SK + HMAC-SHA1 | HTTP 接口 | AliyunProvider |
| 阿里云 | 消息队列 RocketMQ 5.x | gRPC 为主 | AK/SK | SDK/HTTP | AliyunProvider |
| 华为云 | DMS for RocketMQ | Remoting | SASL / AK/SK | 实例地址 | HuaweiProvider |
| 腾讯云 | TDMQ RocketMQ | Remoting | HMAC-SHA1 | VPC 内网 | TencentProvider |
## 附录 D:测试覆盖
测试框架:xUnit,目标框架 net10.0,共 30 个测试文件。
| 分类 | 测试文件 | 覆盖功能 |
|------|---------|---------|
| 核心功能 | ProducerTests, ConsumerTests, CommandTests, MessageTests, NameClientTests | 基础收发、协议编解码、路由发现 |
| 高级特性 | TransactionCheckTests, BatchMessageTests, RetryTests, OrderConsumeTests, PopConsumeTests | 事务回查、批量消息、重试、顺序消费、Pop |
| 协议兼容 | IPv6Tests, MessageId5xTests, MQVersionTests, ProtoTests | IPv6、5.x MessageId、协议版本、Protobuf |
| 云厂商 | AliyunTests, AliyunIssuesTests, CloudProviderTests | 阿里云适配、云厂商接口 |
| 性能优化 | CompressionTests, ConcurrentConsumeTests, VipChannelTests | 压缩、并发控制、VIP 通道 |
| 管理功能 | ManagementTests, ConsumeStatsTests, QueryMessageTests | 管理 API、消费统计、消息查询 |
| 扩展功能 | MultiTopicTests, RequestReplyTests, MessageTraceTests, SQL92FilterTests | 多 Topic、Request-Reply、轨迹、SQL92 |
================================================
FILE: Doc/需求文档.md
================================================
# NewLife.RocketMQ 需求
## 1. 背景与目标
### 1.1 背景与痛点
Apache RocketMQ 是国内使用最广泛的分布式消息中间件之一,广泛应用于电商交易、金融支付、物联网、大数据等领域。然而 .NET 生态中缺乏成熟、轻量、功能完整的 RocketMQ 客户端:
- **官方 C# 客户端(rocketmq-client-csharp)**:仅支持 gRPC 协议(5.x),依赖 Google.Protobuf 和 Grpc.Net.Client 等第三方库,不支持 Remoting 协议(4.x),且更新缓慢(最新 commit 10个月前)
- **社区第三方客户端**:功能不完整,多数已停止维护,缺乏云厂商适配
- **.NET Framework 兼容**:大量企业 .NET Framework 4.5+ 项目无法使用现有客户端
### 1.2 目标
打造企业级**纯托管 .NET RocketMQ 客户端**,实现以下可衡量目标:
- 完整支持 RocketMQ Remoting 协议(4.x/5.x Broker)和 gRPC Proxy 协议(5.x Proxy)
- 零外部依赖(无需 Java、gRPC、Protobuf 第三方库),NuGet 一键安装
- 覆盖生产者、消费者全部核心功能及企业级特性(重试、死信、事务、顺序消费等)
- 统一云厂商适配接口,支持阿里云、华为云、腾讯云及 Apache ACL 认证
- 兼容 .NET Framework 4.5+ 到 .NET 10 全版本
## 2. 用户角色
| 角色 | 说明 | 核心诉求 |
|------|------|---------|
| .NET 开发者 | 使用 .NET 平台开发业务系统的开发人员 | 简单易用、功能完整、文档清晰的 RocketMQ 客户端 |
| 架构师 | 负责技术选型和系统架构设计 | 高性能、高可靠、可扩展、多云适配的消息中间件方案 |
| 运维工程师 | 负责系统部署与运维 | 零外部依赖、易于部署、可观测性(日志/轨迹/监控) |
| 云平台用户 | 使用阿里云/华为云/腾讯云消息队列服务 | 无缝接入云厂商 RocketMQ 实例,支持认证和路由转换 |
## 3. 功能需求
### 3.1 Remoting 协议通信层
- **描述**:实现 RocketMQ 4.x/5.x Broker 的 TCP 私有协议通信
- **用户故事**:作为 .NET 开发者,我希望通过 Remoting 协议连接 RocketMQ 4.x/5.x Broker,以便使用成熟稳定的通信方式
- **验收条件**(AC):
- [x] 支持 Remoting 二进制帧编解码(Length + HeaderLength + Header + Body)
- [x] 支持 JSON 和 ROCKETMQ 两种序列化格式
- [x] 支持 TCP 长连接管理与复用(ConcurrentDictionary 连接池)
- [x] 支持 Opaque 请求-响应匹配
- [x] 支持 HMAC-SHA1 统一签名
- [x] 支持 SSL/TLS 加密传输
- [x] 支持 VIP 通道(BrokerPort - 2)
- [x] 实现 60+ RequestCode 指令
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.2 gRPC Proxy 协议通信层
- **描述**:实现 RocketMQ 5.x Proxy 的 gRPC 协议通信(HTTP/2 + Protobuf)
- **用户故事**:作为 .NET 开发者,我希望通过 gRPC 协议连接 RocketMQ 5.x Proxy,以便使用 5.x 新架构的特性
- **验收条件**(AC):
- [x] 自研轻量级 Protobuf 编解码器(ProtoWriter/ProtoReader),无外部依赖
- [x] 支持 gRPC 帧格式(Comp + Length + Body)
- [x] 支持 HTTP/2 多路复用
- [x] 支持 Unary(请求-响应)调用
- [x] 支持 Server Streaming(服务端流式)调用
- [x] 实现 11 个核心 RPC 方法(QueryRoute/SendMessage/ReceiveMessage/AckMessage/Heartbeat/EndTransaction/ForwardToDeadLetterQueue/ChangeInvisibleDuration/NotifyClientTermination/Telemetry/QueryAssignment)
- [x] 仅在 netstandard2.1+ / net5+ 目标框架可用
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.3 NameServer 路由发现
- **描述**:实现 Topic 路由发现与定时轮询更新
- **用户故事**:作为 .NET 开发者,我希望客户端自动发现 Topic 路由并定时刷新,以便无需手动管理 Broker 连接
- **验收条件**(AC):
- [x] 支持 Topic 路由查询(GET_ROUTEINTO_BY_TOPIC)
- [x] 支持定时轮询路由更新(30s 间隔)
- [x] 支持多 Topic 路由管理
- [x] 支持 Broker 主从地址解析
- [x] 支持 HTTP 方式获取 NameServer 地址(阿里云)
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.4 生产者核心功能
- **描述**:实现消息生产者全部核心功能
- **用户故事**:作为 .NET 开发者,我希望使用丰富的消息发送方式,以便满足各种业务场景
- **验收条件**(AC):
- [x] 同步发送(Publish)
- [x] 异步发送(PublishAsync)
- [x] 单向发送(PublishOneway)
- [x] 批量消息发送(PublishBatch / SEND_BATCH_MESSAGE)
- [x] 延迟消息发送(PublishDelay,18 级预设延迟)
- [x] 任意时间延迟消息(PublishDelayViaGrpcAsync,gRPC 协议)
- [x] 事务消息(PublishTransaction / EndTransaction)
- [x] 事务回查回调(OnCheckTransaction,同步/异步委托)
- [x] 顺序消息(指定 MessageQueue 发送)
- [x] Request-Reply 模式(Request / RequestAsync)
- [x] 消息压缩(CompressOverBytes 阈值自动 ZLIB 压缩)
- [x] 发送重试(RetryTimesWhenSendFailed)
- [x] 发送端钩子(ISendMessageHook)
- [x] 消息轨迹追踪(AsyncTraceDispatcher / MessageTraceHook)
- [x] 加权轮询负载均衡(ILoadBalance / WeightRoundRobin)
- [x] gRPC 发送(SendMessageViaGrpcAsync / PublishTransactionViaGrpcAsync)
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.5 消费者核心功能
- **描述**:实现消息消费者全部核心功能
- **用户故事**:作为 .NET 开发者,我希望使用灵活的消息消费方式,以便满足不同业务消费模式需求
- **验收条件**(AC):
- [x] Pull 模式消费(长轮询拉取)
- [x] 消费调度(自动分配队列并启动消费线程)
- [x] 集群消费模式(Clustering,平均分配 Rebalance)
- [x] 广播消费模式(Broadcasting,本地 JSON 文件持久化偏移)
- [x] Tag 过滤
- [x] SQL92 表达式过滤
- [x] 多 Topic 订阅(Topics 属性,按 Topic 分别 Rebalance)
- [x] 消费重试(EnableRetry + MaxReconsumeTimes,自动回退到 RETRY Topic)
- [x] 死信队列(超过最大重试次数自动进入 %DLQ% Topic)
- [x] 消费回退(SendMessageBackAsync / CONSUMER_SEND_MSG_BACK)
- [x] 顺序消费(LockBatchMQAsync / UnlockBatchMQAsync / OrderConsume)
- [x] Pop 消费模式(PopMessageAsync / AckMessageAsync / ChangeInvisibleTimeAsync)
- [x] 批量确认 Pop 消息(BatchAckMessageAsync / BATCH_ACK_MESSAGE)
- [x] 按时间戳消费(SearchOffset / SEARCH_OFFSET_BY_TIMESTAMP)
- [x] 消费限流(MaxConcurrentConsume 信号量控制)
- [x] 消费者变更通知(NOTIFY_CONSUMER_IDS_CHANGED 触发重平衡)
- [x] Request-Reply 回复(SendReply / SendReplyAsync)
- [x] 消费端钩子(IConsumeMessageHook)
- [x] gRPC 消费(ReceiveMessageViaGrpcAsync / AckMessageViaGrpcAsync / HeartbeatViaGrpcAsync)
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.6 管理与运维功能
- **描述**:实现 RocketMQ 管理 API
- **用户故事**:作为运维工程师,我希望通过客户端管理 Topic、消费组、查询消息,以便方便运维操作
- **验收条件**(AC):
- [x] Topic 创建/更新/删除(CreateTopic / DeleteTopic)
- [x] 消费组创建/更新/删除(CreateSubscriptionGroup / DeleteSubscriptionGroup)
- [x] 消息查询(按 ID:ViewMessage / 按 Key:QueryMessageByKey)
- [x] 消费统计查询(GetConsumeStats / GetTopicStatsInfo)
- [x] 集群信息查询(GetClusterInfo)
- [x] 消费者连接列表查询(GetConsumerConnectionList)
- [x] 偏移量管理与重置(QueryOffset / UpdateOffset / ResetConsumerOffset)
- [x] Broker 运行信息(GetRuntimeInfo)
- [x] 消息过滤服务器注册(RegisterFilterServer)
- **优先级**:Should
- **完成状态**:✅ 已完成
### 3.7 云厂商适配
- **描述**:统一云厂商适配接口,支持多云平台接入
- **用户故事**:作为云平台用户,我希望通过统一接口接入不同云厂商的 RocketMQ 实例,以便减少适配工作量
- **验收条件**(AC):
- [x] 统一 ICloudProvider 接口(SignatureTransformTopic/TransformGroup/GetNameServerAddress)
- [x] 阿里云适配(AliyunProvider:实例 ID 前缀路由 + HTTP NameServer 发现 + HMAC-SHA1 签名)
- [x] 华为云适配(HuaweiProvider:SSL/TLS + 实例 ID 路由)
- [x] 腾讯云适配(TencentProvider:Namespace 前缀路由)
- [x] Apache ACL 适配(AclProvider:HMAC-SHA1 签名,不转换 Topic/Group)
- [x] 旧版参数兼容(AliyunOptions / AclOptions 自动桥接到新适配器)
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.8 消息编解码
- **描述**:完整实现消息二进制编解码
- **用户故事**:作为 .NET 开发者,我希望客户端正确处理各种消息格式,以便与不同版本的 RocketMQ 兼容
- **验收条件**(AC):
- [x] 标准 4.x 消息二进制格式编解码
- [x] ZLIB 消息解压缩(SysFlag 第 0 位标识)
- [x] IPv4/IPv6 地址自动识别和解析(SysFlag 第 2 位标识)
- [x] 批量消息解码(DecodeBatch,SysFlag 第 4 位标识)
- [x] 5.x MessageId 新格式编解码(CreateMessageId5x / TryParseMessageId5x / IsMessageId5x)
- [x] 消息属性分隔符与 Java 官方一致(\x01 和 \x02)
- **优先级**:Must
- **完成状态**:✅ 已完成
### 3.9 可观测性
- **描述**:提供日志、轨迹、监控等可观测手段
- **用户故事**:作为运维工程师,我希望能追踪消息链路和监控客户端运行状态,以便快速定位问题
- **验收条件**(AC):
- [x] 结构化日志(ILog)
- [x] 消息轨迹追踪(AsyncTraceDispatcher / MessageTraceHook)
- [x] 性能追踪集成(Tracer / NewSpan)
- [x] 客户端资源上报(gRPC Telemetry)
- [x] 消费者运行信息上报(GetConsumerRunningInfo)
- **优先级**:Should
- **完成状态**:✅ 已完成
## 4. 非功能需求
### 4.1 性能
- 基于 NewLife.Net 高性能网络层,支持连接复用(单连接 Opaque 复用 / HTTP/2 多路复用)
- 支持 VIP 通道(BrokerPort - 2 优先级连接)
- 支持消息压缩(发送端 ZLIB,超阈值自动压缩)
- 支持消费限流(信号量控制最大并发)
- 对象池和内存池优化(Pool.StringBuilder、ArrayPool 等)
### 4.2 安全
- 支持 HMAC-SHA1 签名认证(统一由 ICloudProvider 实现)
- 支持 SSL/TLS 加密传输(SslProtocol + Certificate 配置)
- 支持 Apache ACL 权限控制
- 不暴露内部实现路径和敏感凭据
### 4.3 兼容性
- 目标框架:net45 / net461 / netstandard2.0 / netstandard2.1(gRPC 功能 netstandard2.1+)
- RocketMQ 服务端兼容:4.0 ~ 5.x(Remoting 协议)/ 5.x Proxy(gRPC 协议)
- 云厂商兼容:阿里云 4.x/5.x、华为云 DMS、腾讯云 TDMQ
- 新增 API 评估各框架降级实现
## 5. 边界与约束
### 5.1 不做什么(明确排除项)
- **Compaction Topic**:RocketMQ 5.1+ KV 语义 Topic,属于小众特性,暂不实现
- **服务端 Rebalance**:需 RocketMQ 5.0+ Broker 端深度配合,属于 Broker 端特性,客户端暂不实现
- **SASL 认证**:华为云可能使用的 SASL/PLAIN 或 SASL/SCRAM 认证,待有明确需求再实现
- **非 .NET 平台支持**:仅支持 .NET 平台
### 5.2 已知限制
- Controller 模式对客户端透明,无需特殊适配
- 阿里云 Serverless 实例仅支持 gRPC 接入,客户端已具备能力但待实际环境验证
- 华为云/腾讯云 gRPC 接入待实际环境验证
- Language 标识为 DOTNET,不在 Java 官方枚举中,部分 Broker 可能不识别
## 6. 功能清单与迭代计划
### 迭代 1:核心通信与基础消息(Must)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F001 | Remoting 协议帧编解码 | 正确编解码二进制帧 | 无 | ✅ |
| F002 | TCP 连接管理与复用 | 支持连接池、Opaque 匹配 | F001 | ✅ |
| F003 | NameServer 路由发现 | 30s 定时轮询、多 Topic 路由 | F002 | ✅ |
| F004 | Broker 心跳机制 | 30s 心跳、注销 | F002 | ✅ |
| F005 | 消息发送(同步/异步/单向) | 三种发送模式均可用 | F003 | ✅ |
| F006 | Pull 消费 | 长轮询拉取消息 | F003 | ✅ |
| F007 | 消费调度与 Rebalance | 自动分配队列、平均分配算法 | F006 | ✅ |
| F008 | 偏移量管理 | 查询/更新/搜索偏移 | F006 | ✅ |
### 迭代 2:生产可靠性增强(Must)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F009 | 延迟消息(18 级定时) | 支持预设延迟等级 | F005 | ✅ |
| F010 | 事务消息(半消息) | 发送/提交/回滚 | F005 | ✅ |
| F011 | 事务回查回调 | 响应 CHECK_TRANSACTION_STATE | F010 | ✅ |
| F012 | 批量消息发送 | SEND_BATCH_MESSAGE | F005 | ✅ |
| F013 | 顺序消息发送 | 指定 MessageQueue | F005 | ✅ |
| F014 | 消费重试 | RETRY Topic + 自动回退 | F007 | ✅ |
| F015 | 死信队列 | 超过重试次数进入 %DLQ% | F014 | ✅ |
| F016 | 顺序消费锁定 | LOCK/UNLOCK_BATCH_MQ | F007 | ✅ |
### 迭代 3:功能完善(Should)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F017 | Tag 过滤 | 支持 Tag 表达式 | F006 | ✅ |
| F018 | SQL92 过滤 | ExpressionType=SQL92 | F006 | ✅ |
| F019 | 消息压缩 | 发送端 ZLIB 压缩 | F005 | ✅ |
| F020 | IPv6 支持 | 自动识别 IPv4/IPv6 | F006 | ✅ |
| F021 | 多 Topic 订阅 | Topics 属性分别 Rebalance | F007 | ✅ |
| F022 | 广播模式本地偏移 | OffsetStorePath JSON 持久化 | F008 | ✅ |
| F023 | Request-Reply 模式 | 同步/异步请求回复 | F005 | ✅ |
| F024 | 消费限流 | MaxConcurrentConsume 信号量 | F007 | ✅ |
| F025 | Pop 消费模式 | Pop/Ack/ChangeInvisibleTime | F006 | ✅ |
| F026 | 消息轨迹 | AsyncTraceDispatcher 分发 | F005 | ✅ |
| F027 | 消息查询 | 按 ID / 按 Key 查询 | F003 | ✅ |
### 迭代 4:云厂商适配(Must)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F028 | ICloudProvider 统一接口 | 7 个方法/属性定义 | 无 | ✅ |
| F029 | 阿里云适配 | 实例 ID 路由 + HTTP NameServer | F028 | ✅ |
| F030 | 华为云适配 | SSL/TLS + 实例 ID 路由 | F028 | ✅ |
| F031 | 腾讯云适配 | Namespace 前缀路由 | F028 | ✅ |
| F032 | Apache ACL 适配 | HMAC-SHA1 签名 | F028 | ✅ |
### 迭代 5:gRPC 协议与 5.x 新特性(Must)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F033 | Protobuf 编解码器 | ProtoWriter/ProtoReader 自研 | 无 | ✅ |
| F034 | gRPC 客户端 | HTTP/2 Unary + Server Streaming | F033 | ✅ |
| F035 | gRPC 消息服务 | 11 个 RPC 方法实现 | F034 | ✅ |
| F036 | 任意时间延迟消息 | PublishDelayViaGrpcAsync | F035 | ✅ |
| F037 | 5.x MessageId | 新格式编解码 | 无 | ✅ |
| F038 | 客户端资源上报 | gRPC Telemetry | F035 | ✅ |
### 迭代 6:功能增强与优化(Should)— ✅ 已完成
| 编号 | 功能点 | 验收条件 | 前置依赖 | 完成状态 |
|------|--------|---------|---------|:-------:|
| F039 | VIP 通道 | VipChannelEnabled 属性 | F002 | ✅ |
| F040 | 批量确认 Pop 消息 | BatchAckMessageAsync | F025 | ✅ |
| F041 | 消费统计完整 API | GetConsumeStats / GetTopicStatsInfo | F003 | ✅ |
| F042 | 消息过滤服务器注册 | RegisterFilterServer | F003 | ✅ |
| F043 | Broker 主从切换 | 消费失败自动切换从节点 | F003 | ✅ |
| F044 | 管理功能 | Topic/消费组 CRUD、偏移重置 | F003 | ✅ |
### 待验证功能(各云厂商 5.x 环境)
| 编号 | 功能点 | 状态 | 说明 |
|------|--------|:----:|------|
| F045 | 阿里云 5.x gRPC 接入 | ⚠️ 待验证 | 客户端已具备能力,待实际环境测试 |
| F046 | 阿里云 Serverless 实例 | ⚠️ 待验证 | 仅支持 gRPC 接入 |
| F047 | 华为云 5.x gRPC 接入 | ⚠️ 待验证 | HuaweiProvider + gRPC 理论可行 |
| F048 | 腾讯云 TDMQ 验证 | ⚠️ 待验证 | TencentProvider 待生产环境验证 |
### 暂不实现功能
| 编号 | 功能点 | 原因 |
|------|--------|------|
| F049 | 服务端 Rebalance | Broker 端特性,客户端需深度配合 |
| F050 | Compaction Topic | 5.1+ 小众特性,Broker 端支持 |
| F051 | SASL 认证 | 华为云特有,待有明确需求 |
## 7. 竞品分析
### 7.1 .NET 生态 RocketMQ 客户端对比
| 维度 | NewLife.RocketMQ | Apache rocketmq-client-csharp | 其他社区客户端 |
|------|:----------------:|:---------------------------:|:------------:|
| **协议支持** | Remoting + gRPC 双协议 | 仅 gRPC(5.x) | 仅 Remoting(部分) |
| **4.x 兼容** | ✅ 完整支持 | ❌ 不支持 | ⚠️ 部分支持 |
| **5.x 支持** | ✅ Remoting + gRPC | ✅ gRPC | ❌ 不支持 |
| **外部依赖** | ✅ 零依赖 | ❌ Google.Protobuf / Grpc.Net.Client / NLog 等 | ⚠️ 部分依赖 |
| **Protobuf 实现** | 自研 ProtoWriter/ProtoReader | Google.Protobuf | 无 |
| **目标框架** | net45 ~ netstandard2.1 | .NET 5+ / .NET Core 3.1 | 不一致 |
| **.NET Framework 支持** | ✅ 4.5+ | ❌ 不支持 | ⚠️ 部分 |
| **多云适配** | ✅ 统一 ICloudProvider 接口 | ❌ 需自行适配 | ❌ 无 |
| **阿里云适配** | ✅ AliyunProvider | ❌ | ❌ |
| **华为云适配** | ✅ HuaweiProvider | ❌ | ❌ |
| **腾讯云适配** | ✅ TencentProvider | ❌ | ❌ |
| **事务消息** | ✅ 发送+回查 | ✅ 发送+回查 | ⚠️ 部分 |
| **消费重试/死信** | ✅ 完整 | ✅ 内置 | ❌ 无 |
| **顺序消费** | ✅ 锁定机制 | ✅ FIFO | ❌ 无 |
| **批量消息** | ✅ 发送+解码 | ❌ 不支持 | ❌ 无 |
| **Pop 消费** | ✅ Pop/Ack/BatchAck | ✅ SimpleConsumer | ❌ 无 |
| **Request-Reply** | ✅ 同步/异步 | ❌ 不支持 | ❌ 无 |
| **消息轨迹** | ✅ MessageTraceHook | ⚠️ OpenTelemetry 集成 | ❌ 无 |
| **VIP 通道** | ✅ | N/A(gRPC) | ❌ 无 |
| **消息压缩** | ✅ ZLIB 自动压缩 | ✅ ZLib | ❌ 无 |
| **管理 API** | ✅ Topic/消费组 CRUD、消息查询、偏移重置 | ❌ 无 | ❌ 无 |
| **测试覆盖** | 30+ 测试类 | 有单元测试 | 少量或无 |
| **维护活跃度** | ✅ 持续维护(新生命团队) | ⚠️ 更新较慢(最新 10 个月前) | ❌ 多数已停止 |
| **文档语言** | 中文为主 | 中英文 | 不一致 |
| **NuGet 包名** | NewLife.RocketMQ | RocketMQ.Client | — |
| **开源协议** | MIT | Apache 2.0 | 不一致 |
### 7.2 跨语言 RocketMQ 客户端对比
| 维度 | NewLife.RocketMQ (.NET) | 官方 Java 客户端 | 官方 Go 客户端 | 官方 C++ 客户端 |
|------|:----------------------:|:----------------:|:--------------:|:--------------:|
| **语言生态** | .NET 原生 | Java 原生 | Go 原生 | C++ 原生 |
| **部署复杂度** | ✅ 单一 DLL,零依赖 | ⚠️ 需要 JRE | ✅ 单一二进制 | ⚠️ 需编译 |
| **4.x Remoting** | ✅ 完整 | ✅ 完整 | ❌ 仅 5.x gRPC | ❌ 仅 5.x gRPC |
| **5.x gRPC** | ✅ 自研编解码 | ✅ 官方实现 | ✅ 官方实现 | ✅ 官方实现 |
| **消息类型** | Normal/Delay/FIFO/Transaction | 全部 | 全部 | 全部 |
| **消费者类型** | Pull/Push(模拟)/Pop/Simple | 全部 | SimpleConsumer/PushConsumer | SimpleConsumer |
| **功能完整度** | ✅ 4.x 100%,5.x ~90% | ✅ 100% | ✅ 5.x 功能完整 | ✅ 5.x 功能完整 |
| **消息批量** | ✅ 发送+解码 | ✅ 发送+解码 | ❌ | ❌ |
| **管理 API** | ✅ 完整 | ✅ 完整 | ❌ 无 | ❌ 无 |
| **社区活跃度** | ⚠️ 新生命团队维护 | ✅ Apache 官方 | ✅ Apache 官方 | ✅ Apache 官方 |
### 7.3 与同类消息队列 .NET 客户端对比
| 维度 | NewLife.RocketMQ | Confluent.Kafka | RabbitMQ.Client | NewLife.Redis (队列) |
|------|:----------------:|:--------------:|:--------------:|:------------------:|
| **消息中间件** | RocketMQ | Kafka | RabbitMQ | Redis Stream/List |
| **协议** | Remoting + gRPC | Kafka 协议 | AMQP | RESP |
| **外部依赖** | ✅ 零依赖 | ⚠️ librdkafka | ✅ 零依赖 | ✅ 零依赖 |
| **事务消息** | ✅ | ✅(幂等生产者) | ⚠️ 确认机制 | ❌ |
| **延迟消息** | ✅ 18 级 + 任意时间 | ❌ 不支持 | ✅ TTL + 死信 | ✅ 延迟队列 |
| **顺序消息** | ✅ 队列锁定 | ✅ 分区有序 | ❌ 不保证 | ❌ 不保证 |
| **消费重试** | ✅ 内置 | ❌ 需自行实现 | ✅ 死信交换器 | ❌ 需自行实现 |
| **消息轨迹** | ✅ 内置 | ⚠️ 需集成 | ⚠️ 需集成 | ❌ |
| **多云适配** | ✅ 统一接口 | ⚠️ Confluent Cloud | ❌ | ❌ |
| **消息规模** | 万亿级 | 万亿级 | 百亿级 | 十亿级 |
| **适用场景** | 企业级业务消息 | 大数据流处理 | 微服务解耦 | 轻量级队列/缓存 |
### 7.4 核心竞争优势总结
1. **零外部依赖**:纯 C# 实现,无需 Java、gRPC、Protobuf 第三方库,部署运维成本最低
2. **双协议支持**:唯一同时支持 Remoting(4.x)和 gRPC(5.x)的 .NET 客户端,向后兼容且面向未来
3. **最广框架覆盖**:.NET Framework 4.5+ 到 .NET 10,覆盖企业遗留系统和最新平台
4. **统一多云适配**:唯一内置阿里云/华为云/腾讯云/Apache ACL 四家适配器的客户端
5. **生产级特性**:完整的企业级功能(事务回查、消费重试、死信队列、顺序消费、Pop 消费、消息轨迹等)
6. **管理 API 完整**:Topic/消费组 CRUD、消息查询、消费统计、偏移重置等运维功能
7. **持续维护**:新生命团队持续迭代更新,紧跟 RocketMQ 社区演进
## 8. 验收记录
### 功能验收
| 编号 | 功能点 | 验收条件 | 状态 | 备注 |
|------|--------|---------|:----:|------|
| F001~F008 | 核心通信与基础消息 | 协议编解码、连接管理、路由发现、消费调度 | ✅ 通过 | — |
| F009~F016 | 生产可靠性增强 | 延迟/事务/批量/顺序/重试/死信 | ✅ 通过 | — |
| F017~F027 | 功能完善 | 过滤/压缩/IPv6/多 Topic/Pop/轨迹/查询 | ✅ 通过 | — |
| F028~F032 | 云厂商适配 | 四家适配器全部实现 | ✅ 通过 | 云厂商 5.x 环境待验证 |
| F033~F038 | gRPC 协议与 5.x 新特性 | Protobuf 编解码/gRPC 通信/11 个 RPC | ✅ 通过 | netstandard2.1+ |
| F039~F044 | 功能增强与优化 | VIP 通道/批量 Ack/管理 API | ✅ 通过 | — |
### 遗留问题
| 问题 | 影响 | 后续计划 |
|------|------|---------|
| 阿里云公网版 BrokerName 不匹配 | 消费者状态中偏移匹配需容错 | 已用 `?? new OffsetWrapperModel()` 容错处理 |
| 各云厂商 5.x gRPC 接入未验证 | 阿里云 Serverless/华为云 5.x/腾讯云 | 客户端已具备能力,待实际环境测试 |
| Language 标识为 DOTNET | 部分 Broker 可能不识别 | 暂无影响,DOTNET 为合理标识 |
### 经验总结
- **做得好的**:零依赖的 Protobuf 编解码器设计、统一云厂商适配接口、50+ 测试类高覆盖率
- **待改进的**:云厂商 5.x 环境的实际验证、性能基准测试数据补充
### 单元测试覆盖统计
| 测试类 | 测试数 | 覆盖目标 | 状态 |
|--------|:------:|---------|:----:|
| CommandTests | 16 | Command 协议帧编解码 | ✅ 通过 |
| SpanRefactorTests | 20+ | 二进制协议序列化、MessageExt 5.x ID | ✅ 通过 |
| ProtoTests | 30+ | Protobuf 编解码(Varint/Fixed/Map/嵌套/gRPC) | ✅ 通过 |
| NameClientTests | 8 | 路由发现解析、缓存、主从地址 | ✅ 通过 |
| BatchMessageTests | 10 | 批量消息编解码、SysFlag、自动展开 | ✅ 通过 |
| MessageTests | 5 | 消息体设置、属性解析 | ✅ 通过 |
| MessageExtendedTests | 20 | Request-Reply 属性、延迟/等待/事务/UserProperty | ✅ 通过 |
| MessageId5xTests | 11 | 5.x 消息ID生成与解析 | ✅ 通过 |
| IPv6Tests | 5 | IPv6 消息解码、SysFlag 位判断 | ✅ 通过 |
| CloudProviderTests | 25 | Aliyun/ACL/Huawei/Tencent 适配器 | ✅ 通过 |
| HeaderTests | 9 | 协议头默认值、GetExtFields、CreateException | ✅ 通过 |
| SendResultTests | 8 | 发送结果 Read 方法、ToString、枚举值 | ✅ 通过 |
| PullResultTests | 8 | 拉取结果 Read 方法、ToString、枚举值 | ✅ 通过 |
| MessageQueueTests | 10 | 消息队列 Equals/GetHashCode/ToString | ✅ 通过 |
| ResponseExceptionTests | 5 | 响应异常构造与捕获 | ✅ 通过 |
| RequestHeaderTests | 7 | 三种请求头 GetProperties 反射映射 | ✅ 通过 |
| WeightRoundRobinTests | 8 | 负载均衡算法(等/不等权重分配、初始化) | ✅ 通过 |
| BrokerInfoTests | 9 | 代理信息属性、Equals/GetHashCode、Permissions | ✅ 通过 |
| ProtocolDataTests | 12 | HeartbeatData/ProducerData/ConsumerData/SubscriptionData/QueryResult | ✅ 通过 |
| TraceModelTests | 9 | TraceContext/TraceBean/SendMessageContext/ConsumeMessageContext | ✅ 通过 |
| ModelTests | 11 | 枚举值验证(DelayTimeLevels/RequestCode/ResponseCode 等) | ✅ 通过 |
| MqBasePropertyTests | 15 | MqBase 默认值、ClientId、属性设置、Active 状态 | ✅ 通过 |
| ConsumerStatesModelTests | 7 | ConsumerStatesModel/MessageQueueModel/OffsetWrapperModel | ✅ 通过 |
| MqSettingTests | 1 | MqSetting 配置属性 | ✅ 通过 |
| MQVersionTests | 1 | MQVersion 版本转换 | ✅ 通过 |
| MQVersionUpdateTests | 4 | 版本枚举 5.x、默认版本 | ✅ 通过 |
| VipChannelTests | 6 | VIP 通道启用/禁用 | ✅ 通过 |
| SQL92FilterTests | 5 | SQL92 过滤表达式类型 | ✅ 通过 |
| CompressionTests | 3 | 消息压缩阈值 | ✅ 通过 |
| BroadcastOffsetTests | 4 | 广播模式消息模型与偏移存储 | ✅ 通过 |
| MultiTopicTests | 6 | 多主题消费 | ✅ 通过 |
| RetryTests | 7 | 重试机制参数 | ✅ 通过 |
| ConcurrentConsumeTests | 3 | 并发消费参数 | ✅ 通过 |
| TransactionCheckTests | 3 | 事务回查回调 | ✅ 通过 |
| OrderConsumeTests | 6 | 顺序消费与队列锁 | ✅ 通过 |
| PopConsumeTests | 5 | Pop 消费异常处理与请求码 | ✅ 通过 |
| BatchAckTests | 6 | 批量确认异常处理与请求码 | ✅ 通过 |
| QueryMessageTests | 3 | 消息查询参数校验 | ✅ 通过 |
| RequestReplyTests | 1 | Request-Reply 消息属性 | ✅ 通过 |
| BrokerFailoverTests | 5 | Broker 主从切换 | ✅ 通过 |
| ConsumeStatsTests | 2 | 消费统计请求码 | ✅ 通过 |
| **合计** | **344** | — | **✅ 全部通过** |
> 另有 30 个集成测试标记为 Skip(需要 RocketMQ 服务器支持),包括 ProducerTests、ConsumerTests、AliyunTests、ManagementTests 等。
## 9. 术语表
| 术语 | 定义 |
|------|------|
| Remoting 协议 | RocketMQ 4.x 的经典 TCP 私有协议,采用二进制帧格式 + JSON/二进制序列化 |
| gRPC 协议 | RocketMQ 5.x 引入的新协议,基于 HTTP/2 + Protobuf |
| Proxy | RocketMQ 5.x 架构中的代理层,接收 gRPC 请求并转发到 Broker |
| NameServer | RocketMQ 的名称服务器,提供 Topic 路由发现 |
| Broker | RocketMQ 的消息存储和转发服务器 |
| Rebalance | 消费者负载均衡,将队列均匀分配给消费者 |
| Pop 消费 | RocketMQ 4.9+ 引入的新消费模式,不自动提交偏移,需手动确认 |
| VIP 通道 | 使用 BrokerPort - 2 的优先级端口,提供更高性能的连接 |
| ICloudProvider | 统一云厂商适配接口,封装签名、路由转换、NameServer 发现等逻辑 |
| HMAC-SHA1 | 基于哈希的消息认证码,用于请求签名 |
| Opaque | 请求唯一标识符,用于 TCP 连接上的请求-响应匹配 |
| SysFlag | 消息系统标志位,标识压缩、IPv6、批量等特征 |
| 死信队列(DLQ) | Dead Letter Queue,超过最大重试次数后消息进入的队列 |
================================================
FILE: LICENSE
================================================
MIT License
Copyright (c) 2018 新生命开发团队
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
================================================
FILE: NewLife.RocketMQ/.github/copilot-instructions.md
================================================
# NewLife Copilot 协作指令
本说明适用于新生命团队(NewLife)及其全部开源/衍生项目,规范 Copilot 及类似智能助手在 C#/.NET 项目中的协作行为。
> 目标:把"每次请求必须携带的通用规则"控制在可接受体积;组件/业务专项流程放在 `.github/instructions/`,按需读取。
---
## 1. 核心原则
| 原则 | 说明 |
|------|------|
| **提效** | 减少机械样板,聚焦业务/核心算法 |
| **一致** | 风格、结构、命名、API 行为稳定 |
| **可控** | 限制改动影响面,可审计,兼容友好 |
| **可靠** | 先检索再生成,不虚构,不破坏现有合约 |
| **主动** | 发现问题主动修复,不回避合理优化 |
---
## 2. 适用范围
- 含 NewLife 组件或衍生的全部 C#/.NET 仓库
- 不含纯前端/非 .NET/市场文案
- 存在本文件 → 必须遵循
---
## 3. 组件专用指令索引(按需加载)
以下专用指令**仅在相关任务时**才需要读取,避免每次请求都携带大段流程/示例。
### 3.1 XCode / Cube(数据库 & Web 快速开发)
当任务涉及以下任一信号时,请**先搜索并检查当前仓库** `.github/instructions/xcode.instructions.md` **是否存在**,若存在则读取并遵循:
- 需求包含:XCode/Cube/魔方/实体生成/模型 XML/数据类库/数据库 CRUD/Controller 生成/`xcodetool`/`xcode` 命令
- 解决方案/项目中出现:`NewLife.XCode` 包引用
- 存在:`Model.xml`、`*.xcode.xml`、`*.Data.csproj`(或项目名以 `.Data` 结尾)
- 代码出现命名空间/类型:`XCode.*`、`Entity`(XCode 实体基类)、XCode 相关特性/接口
- **用户提到修改任意 `.xml` 文件**(如 `member.xml`、`area.xml` 等配置文件),应**主动搜索** `xcode.instructions.md` 判断是否需要引入
**主动检测策略**:当用户提及 XML 文件修改时,即使未明确提到 XCode 关键字,也应先用 `file_search` 搜索 `xcode.instructions.md`,若存在则读取,以确定该 XML 文件是否属于 XCode/Cube 体系。
未满足以上条件时,**不要**引入 XCode/Cube 初始化流程,避免干扰其它仓库的常规开发。
---
## 4. 工作流
```
需求分类 → 检索 → 评估 → 设计 → 实施 → 验证 → 说明
```
1. **需求分类**:功能/修复/性能/重构/文档
2. **检索**:相关类型、目录、方法、已有扩展/工具(**优先复用**)
3. **评估**:是否公共 API?是否性能热点?**是否存在潜在问题?**
4. **设计**:列出改动点 + 兼容/降级策略
5. **实施**:
- 完成用户请求的核心任务
- **顺带修复**发现的明显缺陷(资源泄漏、空引用、逻辑错误)
- **顺带优化**可简化的重复代码
- 保留原注释与结构,除非注释本身有误
6. **验证**:
- 代码变更:必须编译通过;运行相关单元测试(未找到需说明)
- 仅文档变更(未修改任何代码文件):可跳过编译与单元测试
7. **说明**:变更摘要/影响范围/风险点
### 4.1 主动优化原则
当用户请求分析或优化代码时,**应主动**:
| 类型 | 行动 |
|------|------|
| **架构梳理** | 梳理代码架构并进行重构,让代码结构更清晰易懂 |
| **语法现代化** | 使用最新的 C# 语法来简化代码,提升可读性 |
| **缺陷修复** | 资源泄漏、空引用风险、并发问题、逻辑错误 → 直接修复,让代码更健壮 |
| **性能优化** | 无用分配、重复计算、可池化资源 → 通过缓存减少耗时的重复计算 |
| **代码简化** | 重复代码提取、冗余判断合并、现代语法替换 → 在不影响可读性前提下简化 |
| **注释完善** | 补充类、接口、属性、方法头部的注释,以及方法内部重要代码的注释 |
| **架构参考** | 参考网络上同类功能的优秀架构,给出架构调整建议 |
**架构调整策略**:
- **改动较小**:直接调整,完成后说明变更内容
- **改动较大**:先列出调整方案,询问用户意见,待确认后再修改
**不应过度保守**:
- ❌ 仅添加注释而忽略明显的代码问题
- ❌ 发现资源泄漏却不修复
- ❌ 看到重复代码却不提取
- ❌ 用户要求优化时只做表面工作
**保持谨慎的场景**:
- 公共 API 签名变更 → 需说明兼容性影响
- 性能关键路径 → 需有依据或说明推理
- 大范围重构 → 需先与用户确认范围
### 4.2 防御性注释规则
在旧有代码中,经常可以看到**被注释掉的代码**,这些注释代码前面通常带有说明文字。
**这些是防御性注释**:
- 记录了过去曾经踩过的坑
- 目的是告诉后来人不要按照注释代码去写,否则会有问题
- **禁止删除此类防御性注释**,用于警示后人
**识别特征**:
```csharp
// 曾经尝试过 xxx 方案,但会导致 yyy 问题
// var result = DoSomethingWrong();
// 不要使用 xxx,否则会造成 yyy
// await client.SendAsync(data);
// 这里不能用 xxx,因为 yyy
// stream.Flush();
```
**处理原则**:
- ✅ 保留这类带说明的注释代码
- ✅ 可以补充更详细的说明,解释为什么不能这样做
- ❌ 不要删除这类防御性注释
- ❌ 不要尝试"恢复"这些被注释的代码
---
## 5. 编码规范
### 5.1 基础规范
| 项目 | 规范 |
|------|------|
| 语言版本 | `latest`,所有目标框架均使用最新 C# 语法 |
| 命名空间 | file-scoped namespace |
| 类型名 | **必须**使用 .NET 正式名 `String`/`Int32`/`Boolean` 等,避免 `string`/`int`/`bool` |
| 兼容性 | 代码需兼容 .NET 4.5+;**禁止**使用 `ArgumentNullException.ThrowIfNull`,改用 `if (value == null) throw new ArgumentNullException(nameof(value));` |
| 单文件 | 每文件一个主要公共类型;较大平台差异使用 `partial` |
### 5.2 命名规范
| 成员类型 | 命名规则 | 示例 |
|---------|---------|------|
| 类型/公共成员 | PascalCase | `UserService`、`GetName()` |
| 参数/局部变量 | camelCase | `userName`、`count` |
| 私有字段(实例/静态) | `_camelCase` | `_cache`、`_instance` |
| 属性/方法(实例/静态) | PascalCase | `Name`、`Default`、`Create()` |
| 扩展方法类 | `xxxHelper` 或 `xxxExtensions` | `StringHelper`、`CollectionExtensions` |
### 5.3 代码风格
```csharp
// ✅ 单行 if:单语句且整行不过长时同行
if (value == null) return;
if (key == null) throw new ArgumentNullException(nameof(key));
// ✅ 单行 if:语句较长时另起一行
if (value == null)
throw new ArgumentNullException(nameof(value), "Value cannot be null");
// ✅ 多分支单语句:不加花括号
if (count > 0)
DoSomething();
else
DoOther();
// ✅ 循环必须保留花括号(即使单语句)
foreach (var item in list)
{
Process(item);
}
```
### 5.4 Region 组织结构
较长的类使用 `#region` 分段组织,顺序为:`属性` → `静态`(如有)→ `构造` → `方法` → `辅助`(如有)→ `日志`。
**日志 Region 规则**:
- 类代码中如果带有 `ILog Log { get; set; }` 和 `WriteLog` 方法
- **必须放在类代码的最后**
- **必须用名为"日志"的 region 包裹**
- 不要放在"辅助" region 中,应单独作为"日志" region
### 5.5 现代 C# 语法
优先使用最新语法(switch 表达式、模式匹配、目标类型 `new`、record 等),即使目标框架是 net45。
### 5.6 集合表达式
优先使用集合表达式 `[]` 初始化集合:`List Tags { get; set; } = [];`
### 5.7 Null 条件运算符
优先使用 `?.` / `??` 简化空值检查:`span?.AppendTag("test");` `var name = user?.Profile?.Name ?? "";`
---
## 6. 多目标框架
NewLife 支持 `net45` 到 `net10`,常用条件符号:`NETFRAMEWORK`、`NETSTANDARD2_0`、`NETCOREAPP`、`NET5_0_OR_GREATER`、`NET6_0_OR_GREATER`、`NET8_0_OR_GREATER`。
新增 API 时需评估各框架兼容性,必要时提供降级实现。
---
## 7. 文档注释
| 规则 | 说明 |
|------|------|
| `` | **必须同一行闭合**,简短描述方法用途 |
| `` | **必须为每个参数添加**,无论方法可见性如何 |
| `` | 有返回值时必须添加 |
| `` | 复杂方法可增加详细说明(可多行) |
| 覆盖范围 | `public`/`protected` 成员必须注释;`private`/`internal` 建议添加 |
| `[Obsolete]` | 必须包含迁移建议 |
**正确示例**:`/// 获取名称` `/// 编号`
**禁止**:`` 拆成多行;缺少 ``;有参数但无 param 标签。
---
## 8. 异步与性能
| 规范 | 说明 |
|------|------|
| 方法命名 | 异步方法后缀 `Async` |
| ConfigureAwait | 库内部默认 `ConfigureAwait(false)` |
| 高频路径 | 优先对象池/`ArrayPool`/`Span`,避免多余分配 |
| 反射/Linq | 仅用于非热点路径;热点使用手写循环/缓存 |
| 池化资源 | 明确获取/归还;异常分支不遗失归还 |
**内置工具优先**:`Pool.StringBuilder`、`Runtime.TickCount64`、`ToInt()`/`ToBoolean()` 等扩展方法。
---
## 9. 日志与追踪
规则:若类包含 `ILog Log` 与 `WriteLog`,必须放在类末尾,并用名为"日志"的 `#region` 包裹;关键过程可使用 `Tracer?.NewSpan()` 埋点。
---
## 10. 错误处理
- **精准异常类型**:`ArgumentNullException`/`InvalidOperationException` 等
- **参数校验**:空/越界/格式
- **TryXxx 模式**:不用异常作常规分支
- **类型转换**:优先使用 `ToInt()`/`ToBoolean()` 等扩展方法
- **对外异常**:不暴露内部实现/路径
---
## 11. 测试规范
| 项目 | 规范 |
|------|------|
| 框架 | xUnit |
| 命名 | `{ClassName}Tests` |
| 描述 | `[DisplayName("中文描述意图")]` |
| IO | 使用临时目录;端口用 0/随机 |
| 覆盖 | 正常/边界/异常/并发(必要时) |
### 测试执行策略
1. 优先检索 `{ClassName}` 引用,若落入测试项目则运行
2. 未命中则查找 `{ClassName}Tests.cs`
3. **未发现相关测试需明确说明**,不自动创建测试项目
---
## 12. NuGet 发布规范
| 类型 | 命名规则 | 示例 |
|------|---------|------|
| 正式版 | `{主版本}.{子版本}.{年}.{月日}` | `11.9.2025.0701` |
| 测试版 | `{主版本}.{子版本}.{年}.{月日}-beta{时分}` | `11.9.2025.0701-beta0906` |
- **正式版**:每月月初发布
- **测试版**:提交代码到 GitHub 时自动发布
---
## 13. Markdown 文档规范
| 项目 | 规范 |
|------|------|
| 文件编码 | **必须** UTF-8,**禁止** GB2312/GBK/UTF-8 BOM |
| 默认存放 | 代码库根目录下的 `Doc` 目录 |
| 文件命名 | 优先**中文文件名**,简洁描述内容 |
**注意**:已有文件**必须先读取**再增量修改,**禁止直接覆盖**。
---
## 14. Copilot 行为守则
### 必须
- 简体中文回复
- 输出前检索现有实现,**禁止重复造轮子**
- 先列方案再实现
- 标记不确定上下文为"需查看文件"
- **发现明显缺陷时主动修复**(资源泄漏、空引用、逻辑错误)
- **用户要求优化时深入分析**,不做表面工作
### 鼓励
- 提取重复代码为公共方法
- 简化冗余的条件判断
- 使用现代 C# 语法改进可读性
- 补充缺失的资源释放逻辑
- 修正错误或过时的注释
### 禁止
- 虚构 API/文件/类型
- 伪造测试结果/性能数据
- 擅自删除公共/受保护成员
- 擅自删除已有代码注释(除非注释本身错误)
- **删除防御性注释**(带说明的注释代码,记录历史踩坑经验)
- 仅删除空白行制造"格式优化"提交
- 删除循环体的花括号
- 将 `` 拆成多行
- 将 `String`/`Int32` 改为 `string`/`int`
- 新增外部依赖(除非说明理由并给出权衡)
- 在热点路径添加未缓存反射/复杂 Linq
- 输出敏感凭据/内部地址
- **发现问题却视而不见**
- **用户要求优化时仅做注释/测试等表面工作**
---
## 15. 变更说明模板
提交或答复需包含:
```markdown
## 概述
做了什么 / 为什么
## 影响
- 公共 API:是/否
- 性能影响:无/有(说明)
## 兼容性
降级策略 / 条件编译点
## 风险
潜在回归 / 性能开销
## 后续
是否补测试 / 文档
```
---
## 16. 术语说明
| 术语 | 定义 |
|------|------|
| **热点路径** | 经性能分析或高频调用栈确认的关键执行段 |
| **基线** | 变更前的功能/性能参考数据 |
| **顺带修复** | 在完成主任务过程中,修复发现的相关问题 |
| **防御性注释** | 被注释掉的代码,前面带有说明,记录历史踩坑经验,用于警示后人 |
---
## 17. 代码优化检查清单
当进行代码优化时,按以下清单逐项检查:
### 架构与结构
- [ ] 代码架构是否清晰?是否需要重构?
- [ ] 类的职责是否单一?是否需要拆分?
- [ ] 是否有重复代码可以提取为公共方法?
- [ ] Region 组织是否符合规范(属性→静态→构造→方法→辅助→日志)?
### 语法现代化
- [ ] 是否可以使用更简洁的 C# 语法?(switch 表达式、模式匹配等)
- [ ] 集合初始化是否使用了集合表达式 `[]`?
- [ ] 是否可以使用 null 条件运算符 `?.` 简化代码?
### 健壮性
- [ ] 是否存在空引用风险?
- [ ] 资源是否正确释放?(IDisposable、流、连接等)
- [ ] 异常处理是否完善?
- [ ] 并发场景是否线程安全?
### 性能
- [ ] 是否存在可以缓存的重复计算?
- [ ] 是否有不必要的对象分配?
- [ ] 热点路径是否避免了反射和复杂 Linq?
- [ ] 是否使用了对象池/ArrayPool 等池化技术?
### 注释与文档
- [ ] 类、接口是否有 `` 注释?
- [ ] 公共方法是否有完整的参数和返回值注释?
- [ ] 方法内重要逻辑是否有注释说明?
- [ ] 防御性注释是否保留?
### 日志
- [ ] `ILog Log` 和 `WriteLog` 是否放在类的最后?
- [ ] 是否用名为"日志"的 region 包裹?
---
(完)
================================================
FILE: NewLife.RocketMQ/AclOptions.cs
================================================
namespace NewLife.RocketMQ
{
///
/// 支持 Apache RocketMQ ACL机制
///
public class AclOptions
{
/// Acl访问令牌
public String AccessKey { get; set; }
/// Acl访问密钥
public String SecretKey { get; set; }
/// 通道
public String OnsChannel { get; set; } = "LOCAL";
}
}
================================================
FILE: NewLife.RocketMQ/AclProvider.cs
================================================
namespace NewLife.RocketMQ;
/// Apache RocketMQ ACL 适配器
public class AclProvider : ICloudProvider
{
/// 提供者名称
public String Name => "ACL";
/// 访问令牌
public String AccessKey { get; set; }
/// 访问密钥
public String SecretKey { get; set; }
/// 通道标识。默认空
public String OnsChannel { get; set; } = "";
/// 转换主题名。ACL模式不转换
public String TransformTopic(String topic) => topic;
/// 转换消费组名。ACL模式不转换
public String TransformGroup(String group) => group;
/// 获取 NameServer 地址。ACL模式不从HTTP获取
public String GetNameServerAddress() => null;
/// 从旧版 AclOptions 创建
/// 旧版ACL选项
///
public static AclProvider FromOptions(AclOptions options)
{
if (options == null) return null;
return new AclProvider
{
AccessKey = options.AccessKey,
SecretKey = options.SecretKey,
OnsChannel = options.OnsChannel ?? "",
};
}
}
================================================
FILE: NewLife.RocketMQ/AliyunOptions.cs
================================================
using System;
namespace NewLife.RocketMQ
{
///
/// 阿里云选项
///
public class AliyunOptions
{
#region 阿里云属性
/// 获取名称服务器地址的http地址。阿里云专用
public String Server { get; set; } = "http://onsaddr-internet.aliyun.com/rocketmq/nsaddr4client-internet";
/// 访问令牌。阿里云专用
public String AccessKey { get; set; }
/// 访问密钥。阿里云专用
public String SecretKey { get; set; }
/// 实例ID。阿里云专用
public String InstanceId { get; set; }
/// 阿里云MQ通道。阿里云专用
public String OnsChannel { get; set; } = "ALIYUN";
#endregion
}
}
================================================
FILE: NewLife.RocketMQ/AliyunProvider.cs
================================================
namespace NewLife.RocketMQ;
/// 阿里云 RocketMQ 适配器
public class AliyunProvider : ICloudProvider
{
/// 提供者名称
public String Name => "Aliyun";
/// 访问令牌
public String AccessKey { get; set; }
/// 访问密钥
public String SecretKey { get; set; }
/// 通道标识。默认ALIYUN
public String OnsChannel { get; set; } = "ALIYUN";
/// 实例ID。MQ_INST_xxx
public String InstanceId { get; set; }
/// NameServer HTTP 发现地址
public String Server { get; set; }
/// 转换主题名。阿里云需要加实例ID前缀
/// 原始主题名
///
public String TransformTopic(String topic)
{
var ins = InstanceId;
if (!String.IsNullOrEmpty(ins) && !topic.StartsWith(ins))
return $"{ins}%{topic}";
return topic;
}
/// 转换消费组名。阿里云需要加实例ID前缀
/// 原始消费组名
///
public String TransformGroup(String group)
{
var ins = InstanceId;
if (!String.IsNullOrEmpty(ins) && !group.StartsWith(ins))
return $"{ins}%{group}";
return group;
}
/// 获取 NameServer 地址。从阿里云 HTTP 接口获取
///
public String GetNameServerAddress()
{
var addr = Server;
if (String.IsNullOrEmpty(addr) || !addr.StartsWith("http", StringComparison.OrdinalIgnoreCase))
return null;
var http = new System.Net.Http.HttpClient();
var html = http.GetStringAsync(addr).ConfigureAwait(false).GetAwaiter().GetResult();
return String.IsNullOrWhiteSpace(html) ? null : html.Trim();
}
/// 从旧版 AliyunOptions 创建
/// 旧版阿里云选项
///
public static AliyunProvider FromOptions(AliyunOptions options)
{
if (options == null) return null;
return new AliyunProvider
{
AccessKey = options.AccessKey,
SecretKey = options.SecretKey,
OnsChannel = options.OnsChannel ?? "ALIYUN",
InstanceId = options.InstanceId,
Server = options.Server,
};
}
}
================================================
FILE: NewLife.RocketMQ/BrokerClient.cs
================================================
using NewLife.Log;
using NewLife.Net;
using NewLife.RocketMQ.Protocol;
using NewLife.Threading;
namespace NewLife.RocketMQ;
/// 代理客户端
public class BrokerClient : ClusterClient
{
#region 属性
/// 服务器地址
private readonly String[] _Servers;
#endregion
#region 构造
/// 实例化代理客户端
///
public BrokerClient(String[] servers) => _Servers = servers;
#endregion
#region 方法
/// 启动
protected override void OnStart()
{
//Servers = _Servers.Select(e => new NetUri(e)).ToArray();
var list = new List();
foreach (var item in _Servers)
{
var uri = new NetUri(item);
if (uri.Type == NetType.Unknown) uri.Type = NetType.Tcp;
// VIP通道使用端口-2
if (Config != null && Config.VipChannelEnabled && uri.Port > 2)
uri.Port -= 2;
list.Add(uri);
}
Servers = list.ToArray();
base.OnStart();
// 心跳
StartPing();
}
#endregion
#region 注销
/// 注销客户端
///
public virtual Command UnRegisterClient(String group)
{
if (group.IsNullOrEmpty()) group = "CLIENT_INNER_PRODUCER";
return Invoke(RequestCode.UNREGISTER_CLIENT, new
{
ClientId = Id,
ProducerGroup = group,
ConsumerGroup = group,
});
}
///
protected override void Dispose(Boolean disposing)
{
if (disposing)
_timer?.Dispose();
base.Dispose(disposing);
}
#endregion
#region 心跳
private TimerX _timer;
private void StartPing()
{
if (_timer == null)
{
var period = Config.HeartbeatBrokerInterval;
_timer = new TimerX(OnPing, null, 100, period) { Async = true };
}
}
private void OnPing(Object state)
{
DefaultSpan.Current = null;
Ping();
}
/// 心跳
public void Ping()
{
using var span = Tracer?.NewSpan($"mq:{Name}:Ping");
try
{
var cfg = Config;
var body = new HeartbeatData { ClientID = Id };
// 生产者 和 消费者 略有不同
if (cfg is Producer pd)
{
body.ProducerDataSet = [
new ProducerData { GroupName = pd.Group },
new ProducerData { GroupName = "CLIENT_INNER_PRODUCER" },
];
body.ConsumerDataSet = [];
}
else if (cfg is Consumer cm)
{
body.ProducerDataSet = [new ProducerData { GroupName = "CLIENT_INNER_PRODUCER" }];
body.ConsumerDataSet = cm.Data.ToArray();
}
span?.AppendTag(body);
// 心跳忽略错误。有时候报40错误
Invoke(RequestCode.HEART_BEAT, body, null, true);
}
catch (Exception ex)
{
span?.SetError(ex, null);
if (ex.GetTrue() is not TaskCanceledException)
throw;
}
}
#endregion
#region 运行信息
/// 获取运行时信息
///
public IDictionary GetRuntimeInfo()
{
var rs = Invoke(RequestCode.GET_BROKER_RUNTIME_INFO, null);
if (rs == null || rs.Payload == null) return null;
var dic = rs.ReadBodyAsJson();
return dic?["table"] as IDictionary;
}
#endregion
}
================================================
FILE: NewLife.RocketMQ/ClusterClient.cs
================================================
using System.Net.Sockets;
using System.Security.Cryptography;
using NewLife.Data;
using NewLife.Log;
using NewLife.Net;
using NewLife.RocketMQ.Client;
using NewLife.RocketMQ.Protocol;
using NewLife.Serialization;
namespace NewLife.RocketMQ;
/// 集群客户端
///
/// 维护到一个集群的客户端连接,内部采用负载均衡调度算法。
///
public abstract class ClusterClient : DisposeBase
{
#region 属性
/// 编号
public String Id { get; set; }
/// 名称
public String Name { get; set; }
/// 超时。默认3000ms
public Int32 Timeout { get; set; } = 3_000;
/// 服务器地址集合
public NetUri[] Servers { get; set; }
/// 配置
public MqBase Config { get; set; }
/// 性能跟踪
public ITracer Tracer { get; set; }
private ISocketClient _Client;
private SerializeType _serializeType = SerializeType.JSON;
#endregion
#region 构造
/// 实例化
public ClusterClient()
{
//_Pool = new MyPool { Client = this };
}
/// 销毁
///
protected override void Dispose(Boolean disposing)
{
base.Dispose(disposing);
//_Pool.TryDispose();
_Client.TryDispose();
}
#endregion
#region 方法
/// 开始
public void Start()
{
using var span = Tracer?.NewSpan($"mq:{Name}:Start", Servers);
OnStart();
}
/// 开始
protected virtual void OnStart()
{
WriteLog("集群地址:{0}", Servers.Join(";"));
if (Config != null)
_serializeType = Config.SerializeType;
EnsureCreate();
}
/// 确保创建连接
protected void EnsureCreate()
{
var client = _Client;
if (client != null && client.Active && !client.Disposed) return;
lock (this)
{
client = _Client;
if (client != null && client.Active && !client.Disposed) return;
_Client = null;
foreach (var uri in Servers)
{
WriteLog("正在连接[{0}]", uri);
if (uri.Type == NetType.Unknown) uri.Type = NetType.Tcp;
client = uri.CreateRemote();
client.Timeout = Timeout;
client.Log = Log;
if (Log != null && Log.Level <= LogLevel.Debug) client.Tracer = Tracer;
client.Add(new MqCodec { Timeout = Timeout });
// 关闭Tcp延迟以合并小包的算法,降低延迟
if (client is TcpSession tcp)
{
tcp.SslProtocol = Config.SslProtocol;
tcp.Certificate = Config.Certificate;
tcp.NoDelay = true;
}
try
{
if (client.Open())
{
client.Received += Client_Received;
_Client = client;
break;
}
}
catch { }
}
if (_Client == null) throw new XException("[{0}]集群所有地址[{1}]连接失败!", Name, Servers.Length);
}
}
private Int32 g_id;
/// 发送命令
///
///
/// 取消通知
///
protected virtual async Task SendAsync(Command cmd, Boolean waitResult, CancellationToken cancellationToken = default)
{
if (cmd.Header.Opaque == 0) cmd.Header.Opaque = Interlocked.Increment(ref g_id);
if (Log != null && Log.Level <= LogLevel.Debug) WriteLog("=> {0}", cmd);
var code = (RequestCode)cmd.Header.Code;
using var span = Tracer?.NewSpan($"mq:{Name}:SendAsync:{code}");
// 签名
SetSignature(cmd);
EnsureCreate();
var client = _Client;
try
{
if (span is DefaultSpan ds && ds.TraceFlag > 0)
{
span.AppendTag(cmd);
span.AppendTag(cmd.Payload?.ToStr());
}
if (waitResult)
{
var rs = await client.SendMessageAsync(cmd, cancellationToken).ConfigureAwait(false);
if (Log != null && Log.Level <= LogLevel.Debug) WriteLog("<= {0}", rs as Command);
var result = rs as Command;
if (rs != null && span is DefaultSpan ds2 && ds2.TraceFlag > 0)
{
span.AppendTag(Environment.NewLine);
span.AppendTag(rs);
span.AppendTag(result?.Payload?.ToStr());
}
return result;
}
else
{
var row = client.SendMessage(cmd);
return new Command
{
Reply = true,
Header = new Header() { Code = (Int32)ResponseCode.SUCCESS }
};
}
}
catch (Exception ex)
{
// 拉取消息超时,不记录错误日志
if (code == RequestCode.PULL_MESSAGE && ex is TaskCanceledException)
span?.AppendTag(ex.Message);
else
span?.SetError(ex, null);
// 销毁,下次使用另一个地址
if (ex is SocketException or IOException)
client.TryDispose();
throw;
}
}
private void SetSignature(Command cmd)
{
// 各云厂商和Apache ACL统一使用HMAC-SHA1签名:
// 将扩展字段按ASCII排序拼接值,再加上body,计算HmacSHA1
String accessKey;
String secretKey;
String onsChannel;
// 优先使用新的 CloudProvider 接口
var provider = Config.CloudProvider;
if (provider != null && !provider.AccessKey.IsNullOrEmpty())
{
accessKey = provider.AccessKey;
secretKey = provider.SecretKey;
onsChannel = provider.OnsChannel;
}
else
{
// 兼容旧版:依次检查 Aliyun / AclOptions
#pragma warning disable CS0618
var aliyun = Config.Aliyun;
if (aliyun != null && !aliyun.AccessKey.IsNullOrEmpty())
{
accessKey = aliyun.AccessKey;
secretKey = aliyun.SecretKey;
onsChannel = aliyun.OnsChannel;
}
else
{
var acl = Config.AclOptions;
if (acl == null || acl.AccessKey.IsNullOrEmpty()) return;
accessKey = acl.AccessKey;
secretKey = acl.SecretKey;
onsChannel = acl.OnsChannel;
}
#pragma warning restore CS0618
}
var sha = new HMACSHA1(secretKey.GetBytes());
var ms = new MemoryStream();
var dic = cmd.Header.GetExtFields();
dic["AccessKey"] = accessKey;
dic["OnsChannel"] = onsChannel;
// 按照 asscii 排序已有 key
var comparer = Comparer.Create(string.CompareOrdinal);
foreach (var item in dic.OrderBy(e => e.Key, comparer).ToDictionary(e => e.Key, e => e.Value))
{
if (item.Value != null)
{
ms.Write(item.Value.GetBytes());
}
}
// Body
cmd.Payload?.CopyTo(ms);
var sign = sha.ComputeHash(ms.ToArray());
dic["Signature"] = sign.ToBase64();
}
/// 发送指定类型的命令
///
///
///
///
///
public virtual Command Invoke(RequestCode request, Object body, Object extFields = null, Boolean ignoreError = false)
{
var cmd = CreateCommand(request, body, extFields);
// 避免UI死锁
var rs = SendAsync(cmd, true).ConfigureAwait(false).GetAwaiter().GetResult();
// 判断异常响应
if (!ignoreError && rs.Header != null && rs.Header.Code != 0) throw rs.Header.CreateException();
return rs;
}
/// 发送指定类型的命令
public virtual async Task InvokeAsync(RequestCode request, Object body, Object extFields = null,
Boolean ignoreError = false, CancellationToken cancellationToken = default)
{
var cmd = CreateCommand(request, body, extFields);
var rs = await SendAsync(cmd, true, cancellationToken).ConfigureAwait(false);
// 判断异常响应
if (!ignoreError && rs.Header != null && rs.Header.Code != 0)
{
throw rs.Header.CreateException();
}
return rs;
}
/// 发送指定类型的命令
///
///
///
///
public virtual Command InvokeOneway(RequestCode request, Object body, Object extFields = null)
{
var cmd = CreateCommand(request, body, extFields);
cmd.OneWay = true;
// 避免UI死锁
var rs = Task.Run(() => SendAsync(cmd, false)).Result;
return rs;
}
/// 创建命令
///
///
///
///
public virtual Command CreateCommand(RequestCode request, Object body, Object extFields)
{
var header = new Header
{
Code = (Int32)request,
SerializeTypeCurrentRPC = _serializeType + "",
Remark = request + "",
};
if (Config != null && Config.Version > 0) header.Version = Config.Version;
var cmd = new Command
{
Header = header,
};
// 主体
if (body is IPacket pk)
cmd.Payload = pk;
else if (body is Byte[] buf)
cmd.Payload = (ArrayPacket)buf;
else if (body != null)
cmd.Payload = (ArrayPacket)Config.JsonHost.Write(body, false, false, false).GetBytes();
if (extFields != null)
{
var dic = header.GetExtFields();
foreach (var item in extFields.ToDictionary())
{
if (item.Value != null && item.Value.GetType() == typeof(Boolean))
dic[item.Key] = item.Value.ToBoolean() ? "true" : "false";
else
dic[item.Key] = item.Value + "";
}
}
OnBuild(header);
return cmd;
}
/// 建立命令时,处理头部
///
protected virtual void OnBuild(Header header)
{
header.Language = "DOTNET";
}
#endregion
#region 接收数据
private void Client_Received(Object sender, ReceivedEventArgs e)
{
if (e.Message is not Command cmd) return;
if (cmd.Reply)
{
//if (cmd.Header != null) _serializeType = cmd.Header.SerializeTypeCurrentRPC.ToEnum(SerializeType.JSON);
return;
}
var rs = OnReceive(cmd);
if (rs != null)
{
var ss = sender as ISocketRemote;
ss.SendMessage(rs);
}
}
/// 收到命令时
public event EventHandler> Received;
/// 收到命令
///
protected virtual Command OnReceive(Command cmd)
{
var code = !cmd.Reply ? (RequestCode)cmd.Header.Code + "" : (ResponseCode)cmd.Header.Code + "";
WriteLog("收到:Code={0} {1}", code, cmd.Header.ToJson());
using var span = Tracer?.NewSpan($"mq:{Name}:Receive:{code}", cmd);
span?.AppendTag(cmd.Payload?.ToStr());
try
{
if (Received == null) return null;
var e = new EventArgs(cmd);
Received.Invoke(this, e);
return e.Arg;
}
catch (Exception ex)
{
span?.SetError(ex, null);
throw;
}
}
#endregion
#region 日志
/// 日志
public ILog Log { get; set; }
/// 写日志
///
///
public void WriteLog(String format, params Object[] args) => Log?.Info($"[{Name}]{format}", args);
#endregion
}
================================================
FILE: NewLife.RocketMQ/Common/BrokerInfo.cs
================================================
namespace NewLife.RocketMQ;
/// 权限
[Flags]
public enum Permissions
{
/// 写入
Write = 2,
/// 读取
Read = 4,
}
/// 代理信息
public class BrokerInfo
{
#region 属性
/// 名称
public String Name { get; set; }
/// 集群
public String Cluster { get; set; }
/// 地址集合
public String[] Addresses { get; set; }
/// 主节点地址(BrokerId=0)
public String MasterAddress { get; set; }
/// 从节点地址集合(BrokerId>0)
public String[] SlaveAddresses { get; set; }
/// 权限
public Permissions Permission { get; set; }
/// 读队列数
public Int32 ReadQueueNums { get; set; }
/// 写队列数
public Int32 WriteQueueNums { get; set; }
/// 主题同步标记
public Int32 TopicSynFlag { get; set; }
/// 是否主节点
public Boolean IsMaster { get; set; }
#endregion
#region 相等
/// 相等比较
///
///
public override Boolean Equals(Object obj)
{
var x = this;
if (obj is not BrokerInfo y) return false;
return x.Name == y.Name && (x.Addresses == y.Addresses || x.Addresses != null && y.Addresses != null && x.Addresses.SequenceEqual(y.Addresses))
&& x.Permission == y.Permission && x.TopicSynFlag == y.TopicSynFlag
&& x.ReadQueueNums == y.ReadQueueNums && x.WriteQueueNums == y.WriteQueueNums;
}
/// 计算哈希
///
public override Int32 GetHashCode()
{
var obj = this;
return obj.Name.GetHashCode() ^ obj.Addresses.GetHashCode()
^ obj.Permission.GetHashCode() ^ obj.TopicSynFlag
^ obj.ReadQueueNums ^ obj.WriteQueueNums;
}
#endregion
}
================================================
FILE: NewLife.RocketMQ/Common/ILoadBalance.cs
================================================
using System;
namespace NewLife.RocketMQ.Common
{
/// 负载均衡接口
public interface ILoadBalance
{
/// 已就绪
Boolean Ready { get; set; }
/// 设置每个选项的权重数据
///
void Set(Int32[] weights);
/// 根据权重选择,并返回该项是第几次选中
/// 第几次选中
///
Int32 Get(out Int32 times);
}
}
================================================
FILE: NewLife.RocketMQ/Common/WeightRoundRobin.cs
================================================
namespace NewLife.RocketMQ.Common;
/// 带权重负载均衡算法
public class WeightRoundRobin : ILoadBalance
{
#region 属性
/// 已就绪
public Boolean Ready { get; set; }
/// 权重集合
public Int32[] Weights { get; private set; }
/// 最小权重
private Int32 minWeight;
/// 状态值
private Int32[] _states;
/// 次数
private Int32[] _times;
#endregion
#region 方法
/// 设置每个选项的权重数据
///
public void Set(Int32[] weights)
{
if (weights == null) throw new ArgumentNullException(nameof(weights));
if (Weights != null && Weights.SequenceEqual(weights)) return;
Weights = weights;
minWeight = weights.Min();
_states = new Int32[weights.Length];
_times = new Int32[weights.Length];
Ready = true;
}
/// 根据权重选择,并返回该项是第几次选中
/// 第几次选中
///
public Int32 Get(out Int32 times)
{
times = 1;
var ts = _states;
if (ts == null) return 0;
// 选择状态最大值
var cur = GetMax(ts, out var idx);
// 如果所有状态都不达标,则集体加盐
if (cur < minWeight)
{
for (var i = 0; i < Weights.Length; i++)
{
ts[i] += Weights[i];
}
// 重新选择状态最大值
cur = GetMax(ts, out idx);
}
// 已选择,减状态
ts[idx] -= minWeight;
times = ++_times[idx];
return idx;
}
/// 根据权重选择
///
public Int32 Get() => Get(out var n);
private Int32 GetMax(Int32[] ds, out Int32 idx)
{
var n = Int32.MinValue;
idx = 0;
for (var i = 0; i < ds.Length; i++)
{
if (ds[i] > n)
{
n = ds[i];
idx = i;
}
}
return n;
}
#endregion
}
================================================
FILE: NewLife.RocketMQ/Consumer.cs
================================================
using System.Reflection;
using System.Text;
using System.Xml.Serialization;
using NewLife.Data;
using NewLife.Log;
using NewLife.Reflection;
using NewLife.RocketMQ.Client;
using NewLife.RocketMQ.Models;
using NewLife.RocketMQ.Protocol;
using NewLife.RocketMQ.Protocol.ConsumerStates;
using NewLife.Serialization;
using NewLife.Threading;
using NewLife.RocketMQ.MessageTrace;
namespace NewLife.RocketMQ;
/// 消费者
public class Consumer : MqBase
{
#region 属性
/// 数据
public IList Data { get; set; }
/// 标签集合
public String[] Tags { get; set; }
/// 多主题订阅列表。设置后一个Consumer可同时消费多个Topic,每个Topic使用相同的Tags和ExpressionType
///
/// 设置 Topics 后,原 Topic 属性作为默认主题,Topics 中的所有主题都会被订阅。
/// 如果 Topics 未设置,则保持原有单 Topic 行为不变。
///
public String[] Topics { get; set; }
/// 消费挂起超时。每次拉取消息,服务端如果没有消息时的挂起时间,默认15_000ms
public Int32 SuspendTimeout { get; set; } = 15_000;
/// 客户端拉取消息超时。默认0表示自动使用SuspendTimeout加10_000ms,防止服务端超时后无响应导致消费线程永久阻塞
public Int32 PullTimeout { get; set; } = 0;
/// 拉取的批大小。默认32
public Int32 BatchSize { get; set; } = 32;
/// 启动时间
private DateTime StartTime { get; set; } = DateTime.Now;
/// 首次消费时的消费策略,默认值false,表示从头开始收,等同于Java版的COMSUME_FROM_FIRST_OFFSET
public Boolean FromLastOffset { get; set; } = false;
///
/// 订阅表达式 TAG
///
public String Subscription { get; set; } = "*";
/// 表达式类型。TAG或SQL92,默认TAG。使用SQL92时Subscription填写SQL表达式
public String ExpressionType { get; set; } = "TAG";
/// 启动消费者时自动开始调度。默认true
public Boolean AutoSchedule { get; set; } = true;
/// 消息模型。广播/集群
public MessageModels MessageModel { get; set; } = MessageModels.Clustering;
/// 消费类型。CONSUME_PASSIVELY/CONSUME_ACTIVELY
public String ConsumeType { get; set; } = "CONSUME_PASSIVELY";
/// 最大重试次数。默认16次,超过后进入死信队列
public Int32 MaxReconsumeTimes { get; set; } = 16;
/// 是否启用消费重试。默认true,消费失败时自动将消息发回Broker的RETRY Topic
public Boolean EnableRetry { get; set; } = true;
/// 重试延迟等级。默认0表示由Broker根据重试次数决定延迟,大于0时使用指定等级
public Int32 RetryDelayLevel { get; set; }
/// 是否顺序消费。启用后消费前自动锁定队列,确保同一时刻只有一个消费者
public Boolean OrderConsume { get; set; }
/// 最大并发消费数。0表示不限制,每个队列一个消费线程。大于0时使用信号量控制所有队列的总并发
public Int32 MaxConcurrentConsume { get; set; }
/// 消费委托
public Func OnConsume;
/// 异步消费委托
public Func> OnConsumeAsync;
/// 消费事件
public event EventHandler Consumed;
private readonly IList _consumeMessageHooks = new List();
private AsyncTraceDispatcher _traceDispatcher;
/// 本地偏移存储路径。广播模式时使用,默认当前目录下的 .offsets/{Group}.json
public String OffsetStorePath { get; set; }
private SemaphoreSlim _concurrentSemaphore;
/// 获取所有有效的订阅主题
private String[] GetEffectiveTopics()
{
var topics = Topics;
if (topics != null && topics.Length > 0) return topics;
return [Topic];
}
#endregion
#region 构造
/// 销毁
///
protected override void Dispose(Boolean disposing)
{
// 停止并保存偏移
Stop();
base.Dispose(disposing);
_source.TryDispose();
_source = null;
_timer.TryDispose();
_timer = null;
}
#endregion
#region 方法
/// 启动
///
protected override void OnStart()
{
var allTopics = GetEffectiveTopics();
WriteLog("正在准备消费 {0}", allTopics.Join(","));
if (EnableMessageTrace)
{
_traceDispatcher = new AsyncTraceDispatcher();
_traceDispatcher.Start(NameServerAddress);
_consumeMessageHooks.Add(new MessageTraceHook(_traceDispatcher));
}
var list = Data;
if (list == null)
{
// 为每个Topic建立订阅数据
var sds = allTopics.Select(t => new SubscriptionData
{
Topic = t,
TagsSet = Tags
}).ToArray();
var cd = new ConsumerData
{
GroupName = Group,
ConsumeFromWhere = FromLastOffset ? "CONSUME_FROM_LAST_OFFSET" : "CONSUME_FROM_FIRST_OFFSET",
MessageModel = MessageModel.ToString().ToUpper(),
SubscriptionDataSet = sds,
ConsumeType = ConsumeType,
};
list = new[] { cd };
Data = list;
}
base.OnStart();
// 多Topic时,通知NameClient轮询额外主题的路由
if (allTopics.Length > 1 && _NameServer != null)
{
var extras = allTopics.Where(t => t != Topic).ToArray();
_NameServer.ExtraTopics = extras;
// 首次获取额外主题的路由
foreach (var topic in extras)
{
try
{
_NameServer.GetRouteInfo(topic);
}
catch (Exception ex)
{
WriteLog("获取主题[{0}]路由失败:{1}", topic, ex.Message);
}
}
}
// 默认自动开始调度
if (AutoSchedule) StartSchedule();
}
///
/// 停止
///
protected override void OnStop()
{
// 停止并保存偏移
StopSchedule();
PersistAll(_Queues).Wait();
base.OnStop();
}
/// 创建Broker客户端,已重载,设置更大的超时时间
///
///
///
protected override BrokerClient CreateBroker(String name, String[] addrs)
{
var client = base.CreateBroker(name, addrs);
if (client.Timeout < SuspendTimeout) client.Timeout = SuspendTimeout;
return client;
}
#endregion
#region 拉取消息
/// 从指定队列拉取消息
///
///
///
///
/// 取消通知
///
public async Task Pull(MessageQueue mq, Int64 offset, Int32 maxNums, Int32 msTimeout = -1, CancellationToken cancellationToken = default)
{
var header = new PullMessageRequestHeader
{
ConsumerGroup = Group,
Topic = mq.Topic ?? Topic,
Subscription = Subscription,
ExpressionType = ExpressionType,
QueueId = mq.QueueId,
QueueOffset = offset,
MaxMsgNums = maxNums,
SysFlag = 6,
SubVersion = StartTime.ToLong(),
};
if (msTimeout >= 0) header.SuspendTimeoutMillis = msTimeout;
var st = _Queues.FirstOrDefault(e => e.Queue == mq);
if (st != null) header.CommitOffset = st.CommitOffset;
var dic = header.GetProperties();
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.PULL_MESSAGE, null, dic, true, cancellationToken).ConfigureAwait(false);
if (rs?.Header == null) return null;
var pr = new PullResult();
if (rs.Header.Code == 0)
pr.Status = PullStatus.Found;
else if (rs.Header.Code == (Int32)ResponseCode.PULL_NOT_FOUND)
pr.Status = PullStatus.NoNewMessage;
else if (rs.Header.Code == (Int32)ResponseCode.PULL_OFFSET_MOVED || rs.Header.Code == (Int32)ResponseCode.PULL_RETRY_IMMEDIATELY)
pr.Status = PullStatus.OffsetIllegal;
else
{
pr.Status = PullStatus.Unknown;
Log.Warn("[{0}]{1} 序列编号:{2} 序列偏移量:{3}", (ResponseCode)rs.Header.Code, rs.Header.Remark, mq.QueueId, offset);
}
pr.Read(rs.Header?.ExtFields);
// 读取内容
var pk = rs.Payload;
if (pk != null) pr.Messages = MessageExt.ReadAll(pk).ToArray();
return pr;
}
#endregion
#region 业务方法
/// 查询指定队列的偏移量
///
/// 取消通知
///
public async Task QueryOffset(MessageQueue mq, CancellationToken cancellationToken = default)
{
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.QUERY_CONSUMER_OFFSET, null, new
{
consumerGroup = Group,
topic = mq.Topic ?? Topic,
queueId = mq.QueueId,
}, true, cancellationToken).ConfigureAwait(false);
var dic = rs.Header?.ExtFields;
if (dic == null) return -1;
return dic.TryGetValue("offset", out var str) ? str.ToLong() : -1;
}
///
/// 查询“队列”最大偏移量,不是消费提交的最后偏移量
///
///
/// 取消通知
///
public async Task QueryMaxOffset(MessageQueue mq, CancellationToken cancellationToken = default)
{
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.GET_MAX_OFFSET, null, new
{
consumerGroup = Group,
topic = mq.Topic ?? Topic,
queueId = mq.QueueId,
}, true, cancellationToken).ConfigureAwait(false);
var dic = rs.Header?.ExtFields;
if (dic == null) return -1;
return dic.TryGetValue("offset", out var str) ? str.ToLong() : -1;
}
///
/// 获取最小偏移量
///
///
/// 取消通知
///
public async Task QueryMinOffset(MessageQueue mq, CancellationToken cancellationToken = default)
{
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.GET_MIN_OFFSET, null, new
{
consumerGroup = Group,
topic = mq.Topic ?? Topic,
queueId = mq.QueueId,
}, true, cancellationToken).ConfigureAwait(false);
var dic = rs.Header?.ExtFields;
if (dic == null) return -1;
return dic.TryGetValue("offset", out var str) ? str.ToLong() : -1;
}
/// 根据时间戳查询偏移
/// 队列
/// 时间戳(毫秒)
/// 取消通知
///
public async Task SearchOffset(MessageQueue mq, Int64 timestamp, CancellationToken cancellationToken = default)
{
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.SEARCH_OFFSET_BY_TIMESTAMP, null, new
{
topic = mq.Topic ?? Topic,
queueId = mq.QueueId,
timestamp,
}, true, cancellationToken).ConfigureAwait(false);
var dic = rs.Header?.ExtFields;
if (dic == null) return -1;
return dic.TryGetValue("offset", out var str) ? str.ToLong() : -1;
}
/// 更新队列的偏移
///
///
/// 取消通知
///
public async Task UpdateOffset(MessageQueue mq, Int64 commitOffset, CancellationToken cancellationToken = default)
{
var bk = GetBroker(mq.BrokerName);
var rs = await bk.InvokeAsync(RequestCode.UPDATE_CONSUMER_OFFSET, null, new
{
commitOffset,
consumerGroup = Group,
queueId = mq.QueueId,
topic = mq.Topic ?? Topic,
}, false, cancellationToken).ConfigureAwait(false);
//var dic = rs?.Header?.ExtFields;
//if (dic == null) return false;
return true;
}
/// 获取消费者下所有消费者
///
public async Task> GetConsumers(String group = null)
{
if (group.IsNullOrEmpty()) group = Group;
var header = new { consumerGroup = group, };
var cs = new HashSet();
// 在所有Broker上查询
foreach (var item in Brokers)
{
using var span = Tracer?.NewSpan($"mq:{Name}:GetConsumers", item.Name);
try
{
var bk = GetBroker(item.Name);
//bk.Ping();
var rs = await bk.InvokeAsync(RequestCode.GET_CONSUMER_LIST_BY_GROUP, null, header).ConfigureAwait(false);
span?.AppendTag(rs.Payload?.ToStr());
//WriteLog(rs.Header.ExtFields?.ToJson());
var js = rs.ReadBodyAsJson();
if (js != null && js["consumerIdList"] is IList