Foundatio.TestHarness 10.7.1
前缀已保留
dotnet add package Foundatio.TestHarness --version 10.7.1
NuGet\Install-Package Foundatio.TestHarness -Version 10.7.1
<PackageReference Include="Foundatio.TestHarness" Version="10.7.1" />
paket add Foundatio.TestHarness --version 10.7.1
#r "nuget: Foundatio.TestHarness, 10.7.1"
// Install Foundatio.TestHarness as a Cake Addin #addin nuget:?package=Foundatio.TestHarness&version=10.7.1 // Install Foundatio.TestHarness as a Cake Tool #tool nuget:?package=Foundatio.TestHarness&version=10.7.1
构建松散耦合分布式应用的插件式基础块。
包括Redis、Azure、AWS、RabbitMQ、Kafka以及内存(开发用)的实现。
为什么选择Foundatio?
在构建多个大型云应用时,我们发现缺乏构建可扩展分布式应用所需的关键解决方案(这并不意味着没有解决方案),同时保持开发体验简单。以下是构建和为什么使用Foundatio的几个例子:
- 希望针对抽象接口进行构建,以便我们可以轻松更改实现。
- 希望这些块是依赖注入友好的。
- 缓存:我们最初使用开源的Redis缓存客户端,但后来它变成了成本高昂的商业产品。更不用说,没有内存实现,因此每个开发人员都需要设置和配置Redis。
- 消息总线:我们最初考虑了NServiceBus(优秀的产品),但它的许可费用很高(他们也需要吃饭),而且不是开源友好。我们还考虑了MassTransit(另一个优秀的产品),但在当时我们发现Azure支持不足,而且本地设置很痛苦(内存中的)。我们想要一个简单易用的消息总线,无论是在本地还是云端都能工作。
- 存储:我们找不到任何现有的项目既解耦又支持内存、文件存储或Azure Blob存储。
总结一下,如果您想要无痛苦的开发和测试,同时允许您的应用进行扩展,请使用Foundatio!
实现
- Redis - 缓存、存储、队列、消息、锁、指标
- Azure Storage - 存储、队列
- Azure ServiceBus - 队列、消息
- AWS - 存储、队列、指标
- Kafka - 消息
- RabbitMQ - 消息
- Minio - 存储
- Aliyun - 存储
- SshNet - 存储
开始使用(开发)
可以通过NuGet包管理器安装Foundatio。如果您需要帮助,请提出问题或加入我们的Discord聊天室。如果您有任何问题,我们随时在这里为您解答!
本节仅用于开发目的!如果您正在尝试使用Foundatio库,请从NuGet获取。
- 您需要安装Visual Studio Code。
- 打开
Foundatio.sln
Visual Studio解决方案文件。
使用Foundatio
下文包含Foundatio可能实现的小部分功能。我们建议您查看源代码以获取更多信息。如果您有任何问题或需要帮助,请告诉我们!
缓存
缓存可以让您快速存储和访问数据,节省因创建或获取数据而产生的费用。我们提供了来自ICacheClient
接口的四个不同的缓存实现
- InMemoryCacheClient:内存缓存客户端实现。这种缓存实现仅在进程的生命周期内有效。值得注意的是,内存缓存客户端具有通过
MaxItems
属性缓存最后X项的能力。我们在Exceptionless中使用它来仅保留最后250个解析过的geoip结果。 - HybridCacheClient: 这种缓存实现使用了一个
ICacheClient
和一个InMemoryCacheClient
,并使用一个IMessageBus
来保证缓存进程间的同步。这在本地缓存中存在项目时可以避免序列化操作和对远程缓存的调用,从而实现性能的显著提升。 - RedisCacheClient: 一个 Redis 缓存客户端实现。
- RedisHybridCacheClient: 将
RedisCacheClient
用作ICacheClient
并且使用RedisMessageBus
作为IMessageBus
的HybridCacheClient
实现。 - ScopedCacheClient: 这种缓存实现接受一个
ICacheClient
实例和一个字符串scope
。将作用域前缀添加到每个缓存键上,这使得对所有缓存键进行范围划分并轻松删除它们变得非常简单。
示例
using Foundatio.Caching;
ICacheClient cache = new InMemoryCacheClient();
await cache.SetAsync("test", 1);
var value = await cache.GetAsync<int>("test");
队列
队列提供先入先出(FIFO)消息交付。我们提供了四个不同的队列实现,这些实现都继承自 IQueue
接口
- InMemoryQueue: 一个内存队列实现。此队列实现仅对进程的生存周期有效。
- RedisQueue: 一个 Redis 队列实现。
- AzureServiceBusQueue: 一个 Azure Service Bus 队列实现。
- AzureStorageQueue: 一个 Azure Storage 队列实现。
- SQSQueue: 一个 AWS SQS 实现。
示例
using Foundatio.Queues;
IQueue<SimpleWorkItem> queue = new InMemoryQueue<SimpleWorkItem>();
await queue.EnqueueAsync(new SimpleWorkItem {
Data = "Hello"
});
var workItem = await queue.DequeueAsync();
锁
锁确保资源在任何给定时间只被一个消费者访问。我们提供了两个不同的锁定实现,这些实现都继承自 ILockProvider
接口
- CacheLockProvider: 一个使用缓存在进程之间通信的锁实现。
- ThrottlingLockProvider: 一个仅允许一定数量的锁通过的锁实现。您可以将其用于控制外部服务的 API 调用,并且它将在所有请求该锁的进程之间进行节流。
- ScopedLockProvider: 此锁实现接受一个
ILockProvider
实例和一个字符串scope
。作用域被添加到每个锁键之前。这使得对所有锁进行范围划分并轻松释放它们变得非常简单。
值得注意的是,所有锁提供者都接受一个 ICacheClient
。这允许您确保在机器之间代码正确锁定。
示例
using Foundatio.Lock;
ILockProvider locker = new CacheLockProvider(new InMemoryCacheClient(), new InMemoryMessageBus());
var testLock = await locker.AcquireAsync("test");
// ...
await testLock.ReleaseAsync();
ILockProvider throttledLocker = new ThrottlingLockProvider(new InMemoryCacheClient(), 1, TimeSpan.FromMinutes(1));
var throttledLock = await throttledLocker.AcquireAsync("test");
// ...
await throttledLock.ReleaseAsync();
消息传递
允许您发布和订阅通过您的应用程序流动的消息。我们提供了四个不同的消息总线实现,这些实现都继承自 IMessageBus
接口
- InMemoryMessageBus: 一个内存消息总线实现。此消息总线实现仅对进程的生存周期有效。
- RedisMessageBus:一个Redis消息总线实现。
- RabbitMQMessageBus:一个RabbitMQ实现。
- KafkaMessageBus:一个Kafka实现。
- AzureServiceBusMessageBus:一个Azure Service Bus实现。
示例
using Foundatio.Messaging;
IMessageBus messageBus = new InMemoryMessageBus();
await messageBus.SubscribeAsync<SimpleMessageA>(msg => {
// Got message
});
await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" });
作业
允许您运行长时间运行的过程(在进程内或进程外)而不用担心其被提前终止。根据您的用例,我们提供了三种不同的定义作业的方式。
- 作业:所有作业都必须从
IJob
接口继承。我们还有一个JobBase
基类,您可以从中继承提供作业上下文和日志记录。然后您可以通过对作业调用RunAsync()
或创建JobRunner
类实例并调用其中一个运行方法来运行作业。可以使用JobRunner轻松地将您的作业作为Azure Web作业运行。
示例
using Foundatio.Jobs;
public class HelloWorldJob : JobBase {
public int RunCount { get; set; }
protected override Task<JobResult> RunInternalAsync(JobContext context) {
RunCount++;
return Task.FromResult(JobResult.Success);
}
}
var job = new HelloWorldJob();
await job.RunAsync(); // job.RunCount = 1;
await job.RunContinuousAsync(iterationLimit: 2); // job.RunCount = 3;
await job.RunContinuousAsync(cancellationToken: new CancellationTokenSource(10).Token); // job.RunCount > 10;
- 队列处理作业:队列处理作业非常适合处理来自队列的数据驱动的作业。队列处理作业必须从
QueueJobBase<T>
类继承。然后您可以通过对作业调用RunAsync()
或将其传递到JobRunner
类来运行作业。可以使用JobRunner轻松地将您的作业作为Azure Web作业运行。
示例
using Foundatio.Jobs;
public class HelloWorldQueueJob : QueueJobBase<HelloWorldQueueItem> {
public int RunCount { get; set; }
public HelloWorldQueueJob(IQueue<HelloWorldQueueItem> queue) : base(queue) {}
protected override Task<JobResult> ProcessQueueEntryAsync(QueueEntryContext<HelloWorldQueueItem> context) {
RunCount++;
return Task.FromResult(JobResult.Success);
}
}
public class HelloWorldQueueItem {
public string Message { get; set; }
}
// Register the queue for HelloWorldQueueItem.
container.AddSingleton<IQueue<HelloWorldQueueItem>>(s => new InMemoryQueue<HelloWorldQueueItem>());
// To trigger the job we need to queue the HelloWorldWorkItem message.
// This assumes that we injected an instance of IQueue<HelloWorldWorkItem> queue
IJob job = new HelloWorldQueueJob();
await job.RunAsync(); // job.RunCount = 0; The RunCount wasn't incremented because we didn't enqueue any data.
await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
await job.RunAsync(); // job.RunCount = 1;
await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
await job.RunUntilEmptyAsync(); // job.RunCount = 3;
- 工作项目作业:工作项目作业将在工作项池中与其他工作项目一起运行。这种类型的作业非常适合那些不常发生但应该作为作业处理的事情(例如:删除具有许多子实体的实体)。当您在
消息总线
上发布消息时,将触发此作业。作业必须从WorkItemHandlerBase
类继承。然后您可以通过JobRunner
类运行所有共享作业。可以使用JobRunner轻松地将您的作业作为Azure Web作业运行。
示例
using System.Threading.Tasks;
using Foundatio.Jobs;
public class HelloWorldWorkItemHandler : WorkItemHandlerBase {
public override async Task HandleItemAsync(WorkItemContext ctx) {
var workItem = ctx.GetData<HelloWorldWorkItem>();
// We can report the progress over the message bus easily.
// To receive these messages just inject IMessageSubscriber
// and Subscribe to messages of type WorkItemStatus
await ctx.ReportProgressAsync(0, "Starting Hello World Job");
await Task.Delay(TimeSpan.FromSeconds(2.5));
await ctx.ReportProgressAsync(50, "Reading value");
await Task.Delay(TimeSpan.FromSeconds(.5));
await ctx.ReportProgressAsync(70, "Reading value");
await Task.Delay(TimeSpan.FromSeconds(.5));
await ctx.ReportProgressAsync(90, "Reading value.");
await Task.Delay(TimeSpan.FromSeconds(.5));
await ctx.ReportProgressAsync(100, workItem.Message);
}
}
public class HelloWorldWorkItem {
public string Message { get; set; }
}
// Register the shared job.
var handlers = new WorkItemHandlers();
handlers.Register<HelloWorldWorkItem, HelloWorldWorkItemHandler>();
// Register the handlers with dependency injection.
container.AddSingleton(handlers);
// Register the queue for WorkItemData.
container.AddSingleton<IQueue<WorkItemData>>(s => new InMemoryQueue<WorkItemData>());
// The job runner will automatically look for and run all registered WorkItemHandlers.
new JobRunner(container.GetRequiredService<WorkItemJob>(), instanceCount: 2).RunInBackground();
// To trigger the job we need to queue the HelloWorldWorkItem message.
// This assumes that we injected an instance of IQueue<WorkItemData> queue
// NOTE: You may have noticed that HelloWorldWorkItem doesn't derive from WorkItemData.
// Foundatio has an extension method that takes the model you post and serializes it to the
// WorkItemData.Data property.
await queue.EnqueueAsync(new HelloWorldWorkItem { Message = "Hello World" });
文件存储
我们提供了多种文件存储实现,这些实现在IFileStorage
接口的基础上。
- InMemoryFileStorage:一个内存文件实现。此文件存储实现仅对进程的生命周期有效。
- FolderFileStorage:一个使用硬盘进行存储的文件存储实现。
- AzureFileStorage:一个Azure Blob存储实现。
- S3FileStorage:一个AWS S3文件存储实现。
- RedisFileStorage:一个Redis文件存储实现。
- MinioFileStorage:一个Minio文件存储实现。
- AliyunFileStorage:一个阿里云文件存储实现。
- SshNetFileStorage:一个SFTP文件存储实现。
我们建议将所有的 IFileStorage
实现用作单例。
示例
using Foundatio.Storage;
IFileStorage storage = new InMemoryFileStorage();
await storage.SaveFileAsync("test.txt", "test");
string content = await storage.GetFileContentsAsync("test.txt")
度量指标
我们提供了五个派生自 IMetricsClient
接口 的实现。
- InMemoryMetricsClient:内存中指标实现。
- RedisMetricsClient:Redis指标实现。
- StatsDMetricsClient:statsd指标实现。
- MetricsNETClient:一个Metrics.NET实现。
- AppMetricsClient:一个AppMetrics实现。
- CloudWatchMetricsClient:一个AWS CloudWatch实现。
我们建议将所有的 IMetricsClient
实现用作单例。
示例
IMetricsClient metrics = new InMemoryMetricsClient();
metrics.Counter("c1");
metrics.Gauge("g1", 2.534);
metrics.Timer("t1", 50788);
示例应用
我们提供了幻灯片和示例应用,展示了如何使用Foundatio。
感谢所有作出贡献的人
产品 | 版本 兼容和附加计算目标框架版本。 |
---|---|
.NET | net8.0 兼容。 net8.0-android 已计算。 net8.0-browser 已计算。 net8.0-ios 已计算。 net8.0-maccatalyst 已计算。 net8.0-macos 已计算。 net8.0-tvos 已计算。 net8.0-windows 已计算。 |
-
net8.0
- BenchmarkDotNet (>= 0.13.12)
- Exceptionless.RandomData (>= 1.2.2)
- Foundatio (>= 10.7.1)
- Foundatio.Xunit (>= 10.7.1)
- Microsoft.Extensions.Configuration.EnvironmentVariables (>= 8.0.0)
- Microsoft.Extensions.Configuration.Json (>= 8.0.0)
- xunit (>= 2.6.5)
NuGet 包
本包未使用任何 NuGet 包。
GitHub 仓库
本包未使用任何流行的 GitHub 仓库。
版本 | 下载 | 最后更新 |
---|---|---|
10.7.1 | 392 | 3/27/2024 |
10.7.0 | 517 | 1/5/2024 |
10.6.1 | 827 | 6/23/2023 |
10.6.0 | 997 | 1/1/2023 |
10.5.0 | 1,154 | 5/18/2022 |
10.4.0 | 1,174 | 3/7/2022 |
10.3.1 | 1,052 | 1/20/2022 |
10.3.0 | 1,027 | 1/20/2022 |
10.2.5 | 774 | 12/7/2021 |
10.2.4 | 1,140 | 12/3/2021 |
10.2.3 | 766 | 11/22/2021 |
10.2.2 | 956 | 9/23/2021 |
10.2.1 | 927 | 7/19/2021 |
10.2.0 | 904 | 7/8/2021 |
10.1.4 | 909 | 6/16/2021 |
10.1.3 | 857 | 4/23/2021 |
10.1.2 | 814 | 4/23/2021 |
10.1.1 | 870 | 4/15/2021 |
10.1.0 | 904 | 4/13/2021 |
10.0.2 | 1,002 | 1/20/2021 |
10.0.1 | 1,057 | 11/2/2020 |
10.0.0 | 1,188 | 9/16/2020 |
10.0.0-beta9 | 859 | 8/25/2020 |
10.0.0-beta8 | 964 | 8/3/2020 |
10.0.0-beta7 | 896 | 7/29/2020 |
10.0.0-beta6 | 899 | 7/7/2020 |
10.0.0-beta5 | 1,047 | 6/20/2020 |
10.0.0-beta3 | 886 | 6/14/2020 |
10.0.0-beta2 | 976 | 6/6/2020 |
10.0.0-beta10 | 904 | 9/15/2020 |
10.0.0-beta1 | 1,057 | 5/26/2020 |
10.0.0-alpha3 | 825 | 5/5/2020 |
10.0.0-alpha2 | 846 | 4/27/2020 |
10.0.0-alpha1 | 813 | 4/25/2020 |
9.1.1 | 978 | 4/28/2020 |
9.1.0 | 1,030 | 4/28/2020 |
9.0.0 | 1,494 | 1/16/2020 |
8.1.2126 | 1,317 | 8/30/2019 |
8.1.2123 | 1,130 | 8/27/2019 |
8.1.2120 | 1,127 | 8/27/2019 |
8.1.2115 | 1,063 | 8/27/2019 |
8.1.2109 | 1,081 | 8/26/2019 |
8.1.2058 | 1,151 | 5/14/2019 |
8.1.2027 | 1,141 | 4/16/2019 |
8.0.1965 | 1,223 | 2/24/2019 |
8.0.1948 | 1,220 | 2/22/2019 |
7.1.1845 | 1,401 | 11/3/2018 |
7.1.1841 | 1,356 | 11/3/2018 |
7.1.1837 | 1,307 | 11/1/2018 |
7.1.1833 | 1,340 | 11/1/2018 |
7.0.1831 | 1,336 | 11/1/2018 |
7.0.1818 | 1,281 | 10/30/2018 |
7.0.1738 | 1,404 | 9/7/2018 |
7.0.1706 | 1,536 | 5/9/2018 |
6.0.1586 | 1,680 | 11/30/2017 |
5.1.1521 | 1,611 | 9/27/2017 |
5.1.1515 | 1,505 | 9/26/2017 |
5.1.1501 | 1,526 | 9/14/2017 |
5.1.1498 | 1,578 | 8/28/2017 |
5.1.1492 | 1,523 | 8/28/2017 |
5.1.1490 | 1,470 | 8/16/2017 |
5.1.1474 | 1,609 | 8/1/2017 |
5.1.1470 | 1,615 | 7/31/2017 |
5.1.1457 | 1,561 | 6/23/2017 |
5.1.1448 | 1,501 | 5/5/2017 |
5.1.1443 | 1,544 | 5/5/2017 |