在学习.NET Core的过程中,我通过实际项目练习各种开发技巧,沉淀了两个开源项目。经过多个版本的迭代,我自认为这些代码已达到可在小型项目中使用的程度。诚邀开发者朋友们体验指正,您的反馈将帮助这些项目变得更好,让我们共同进步。
开源日志记录库: Logging
安装教程
在 Nuget 中搜索 Nobita.Logging,安装对应的包。
| 包名 | 版本 | 状态 |
|---|---|---|
| Nobita.Logging.Core | 2.0.0 | 正常 |
| Nobita.Logging.Console | 2.0.0 | 正常 |
| Nobita.Logging.File | 2.0.0 | 正常 |
| Nobita.Logging.Parallel | 2.0.0 | 正常 |
| Nobita.Logging.MySql | 1.3.0 | 已放弃更新 |
| Nobita.Logging.Proxying | 1.3.0 | 已放弃更新 |
使用说明
- 控制台日志记录
// 创建日志记录器
var logger = new ConsoleLogger(Console.Out);
// 记录日志
logger.Information("Test","Hello World!");
- 文件日志记录
// 创建文件管理器
// 目前支持日期文件管理器和默认文件管理器
// 参数:日志文件管理路径
var manager = new DateFileManager("./Logs");
// 创建日志记录器
var logger = new FileLogger(manager);
// 记录日志
logger.Information("Test","Hello World!");
- 并行日志处理
// 假设要同时使用控制台和文件日志记录器
var consoleLogger = new ConsoleLogger(Console.Out);
var fileLogger = new FileLogger(new DateFileManager("./Logs"));
// 创建调度器
// 顺序调度器会利用线程池链式调用,会确保日志存储与输出的顺序一致
// 并行调度器会利用线程池并行调用,可能会导致日志存储与输出的顺序不一致
// 一般推荐使用顺序调度器,可以确保日志存储与输出的顺序一致
var dispatcherManager = new DispatcherManager
{
new OrderedDispatcher(consoleLogger),
new OrderedDispatcher(fileLogger)
};
// 创建假日志记录器
// 假日志器不会实质性的记录日志,只是将日志记录到调度器中,并确保日志的顺序一致。
var fakeLogger = new FakeLogger(dispatchManager);
// 记录日志
fakeLogger.Information("Test","Hello World!");
- 配合依赖注入
// 创建容器
var serviceCollection = new ServiceCollection();
serviceCollection.AddSingleton<Logger>(_ =>
{
var manager = new DispatcherManager
{
new OrderedDispatcher(new ConsoleLogger(Console.Out)),
new OrderedDispatcher(new FileLogger(new DateFileManager("Logs")))
};
return new FakeLogger(manager);
});
// 创建服务提供程序
var serviceProvider = serviceCollection.BuildServiceProvider();
var logger = serviceProvider.GetService<Logger>();
// 记录日志
logger.Information("Test",Hello World!");
开源服务端框架: ServerFramework
安装教程
在 NuGet 中搜索 'Nobita.ServerFramework' 安装相关的包。
| 包名称 | 版本 | 状态 |
|---|---|---|
| Nobita.ServerFramework.Core | 1.0.0 | 正常 |
| Nobita.ServerFramework.Network | 1.0.0 | 正常 |
| Nobita.ServerFramework.Timer | 1.0.0 | 正常 |
| Nobita.ServerFramework.Cluster | 1.0.0 | 正常 |
| Nobita.ServerFramework.Delegates | 1.0.0 | 正常 |
使用说明
- Attribute 说明
| Attribute | 位置 | 作用 |
|---|---|---|
| [Abstract] | Nobita.ServerFramework.Core | 标记抽象服务,在自动反射时忽略 |
| [Singleton] | Nobita.ServerFramework.Core | 标记单例服务,在自动反射时注册单例 |
| [Name] | Nobita.ServerFramework.Core | 命名服务,在自动反射时注册命名服务 |
| [Dependency] | Nobita.ServerFramework.Core | 设置依赖的服务,确定服务启动和关闭的先后顺序 |
| [InstanceCount] | Nobita.ServerFramework.Core | 设置实例数量,用于控制服务实例的数量 |
| [Bind] | Nobita.ServerFramework.Network | 硬编码绑定IP地址和端口 |
| [BindJson] | Nobita.ServerFramework.Network | 通过配置文件绑定IP地址和端口 |
| [Backlog] | Nobita.ServerFramework.Network | 设置TCP连接的缓冲区大小 |
| [BufferSize] | Nobita.ServerFramework.Network | 设置UDP数据包的缓冲区大小 |
| [SocketInfo] | Nobita.ServerFramework.Network | 设置Socket的信息 |
| [Delegate] | Nobita.ServerFramework.Delegates | 设置委托 |
| [DelegateFixture] | Nobita.ServerFramework.Delegates | 设置委托的容器类 |
| [Period] | Nobita.ServerFramework.Timer | 设置定时任务执行周期(毫秒) |
| [Strict] | Nobita.ServerFramework.Timer | 严格模式,任务启动后会暂停定时器,任务结束后恢复(默认为false) |
| [Parallel] | Nobita.ServerFramework.Cluster | 设置集群最大和最小的处理节点数(推荐优先使用线程池,除非要保证先后顺序) |
- BindJson配置文件格式
本框架采用约定大于配置的设计思路,所有使用了BindJsonAttribute的服务会自动按照 'services:serviceName' 路径去搜索。
{
"services": {
"tcpserviceimplement": {
"address": "0.0.0.0",
"port": 8080
},
"udpserviceimplement": {
"address": "0.0.0.0",
"port": 8082
}
}
}
- 创建TCP服务
[Singleton]
[Name(nameof(TcpServiceImplement))]
[BindJson("application.json")]
[Backlog(100)]
[Dependency(typeof(UdpServiceImplement))]
public sealed class TcpServiceImplement : TcpService
{
protected override Task OnProcessAsync(TcpContext context, CancellationToken token)
{
throw new NotImplementedException();
}
}
- 创建UDP服务
[Singleton]
[Name(nameof(UdpServiceImplement))]
[BindJson("application.json")]
[BufferSize(1024)]
public sealed class UdpServiceImplement : UdpService
{
protected override Task OnProcessAsync(UdpContext context, CancellationToken token)
{
throw new NotImplementedException();
}
}
- 创建定时器服务
[Singleton]
[Strict]
[Period(1000)]
[Name(nameof(TimerImplement))]
[Dependency(typeof(TcpServiceImplement))]
[Dependency(typeof(UdpServiceImplement))]
public sealed class TimerImplement(IServiceProvider provider) : TimerService
{
private IServiceProvider Provider { get; } = provider;
protected override void OnTimer(object? state)
{
Console.WriteLine("Provider: " + Provider);
}
}
- 创建集群服务
[Singleton]
[Name(nameof(TestCluster))]
[Parallel(1, 2)]
private class TestCluster : ClusterMaster
{
public TestCluster()
{
OnIncreased += (sender, args) =>
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} Increased: " + args.CurrentCount);
};
OnDecreased += (sender, args) =>
{
Console.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss} Decreased: " + args.CurrentCount);
};
}
protected override void OnAction(object? param, CancellationToken token)
{
throw new NotImplementedException();
}
}
- 启动服务程序
// 创建服务端
var app = new ServerApplication();
// 配置服务端
app.MapDependencies()
.MapServices()
.UsePassiveMode();
// 运行服务端
app.Run();
特点
- 空依赖检测
private void CheckEmptyWaiting()
{
// 检测等待队列中是否存在空等待现象
// 即某服务依赖了某服务,但该服务未在等待队列或者运行队列中
// 因为依赖的服务只能是单例,所以检测类型就行
if (Waiting.Any(x => x.Dependencies.Count > 0 &&
x.Dependencies.Any(dependency =>
Waiting.Any(y => y.GetType() == dependency) == false &&
Running.Any(y => y.GetType() == dependency) == false)))
throw new ArgumentException("Empty waiting detected.");
}
- 循环依赖检测
// 核心拓扑排序
...
// 检测是否存在循环依赖
// 如果结果的长度与入度表长度不一致,则说明存在循环依赖
if (result.Count != graph.Count)
throw new ArgumentException("Circular dependency detected.");
- 群组委托
在本框架中,设计了一套便于维护与扩展的委托反射机制。通过 DelegateFixture 类定义委托容器,并借助群组(Group)配置,可将分散在多个容器中的委托进行统一聚合与管理。
每个容器可根据需要进行独立初始化,系统在运行时自动完成委托的注册与调用,实现了行为的灵活组织与动态执行。
[DelegateFixture("Analyze")]
public class UserHandlers
{
public class UserHandlers()
{
// 进行相关的初始化
}
[Delegate("Login")]
public async Task LoginHandler(object? param, CancellationToken token)
{
throw new NotImplementedException();
}
[Delegate("Register")]
public async Task RegisterHandler(object? param, CancellationToken token)
{
throw new NotImplementedException();
}
[Delegate("Unregister")]
public async Task UnregisterHandler(object? param, CancellationToken token)
{
throw new NotImplementedException();
}
}
[DelegateFixture("Analyze")]
public class OtherHandlers
{
public OtherHandlers()
{
// 进行相关的初始化
}
[Delegate("OtherProtocol")]
public async Task OtherHandler(object? param, CancellationToken token)
{
throw new NotImplementedException();
}
}
public class Program
{
public static void Main(string[] args)
{
// 创建委托注册表
var registry = DelegateHelper.CreateRegistry("Analyze");
// 触发委托
await registry.InvokeAsync("Login", null, CancellationToken.None);
}
}