Pluggable foundation blocks for building loosely coupled distributed apps.
Includes implementations in Redis, Azure, AWS, RabbitMQ and in memory (for development).
When building several big cloud applications we found a lack of great solutions (that's not to say there isn't solutions out there) for many key pieces to building scalable distributed applications while keeping the development experience simple. Here are a few examples of why we built and use Foundatio:
To summarize, if you want pain free development and testing while allowing your app to scale, use Foundatio!
Foundatio can be installed via the NuGet package manager. If you need help, please open an issue or join our Discord chat room. We’re always here to help if you have any questions!
This section is for development purposes only! If you are trying to use the Foundatio libraries, please get them from NuGet.
Foundatio.sln
Visual Studio solution file.The sections below contain a small subset of what's possible with Foundatio. We recommend taking a peek at the source code for more information. Please let us know if you have any questions or need assistance!
Caching allows you to store and access data lightning fast, saving you exspensive operations to create or get data. We provide four different cache implementations that derive from the ICacheClient
interface:
MaxItems
property. We use this in Exceptionless to only keep the last 250 resolved geoip results.ICacheClient
and the InMemoryCacheClient
and uses an IMessageBus
to keep the cache in sync across processes. This can lead to huge wins in performance as you are saving a serialization operation and a call to the remote cache if the item exists in the local cache.HybridCacheClient
that uses the RedisCacheClient
as ICacheClient
and the RedisMessageBus
as IMessageBus
.ICacheClient
and a string scope
. The scope is prefixed onto every cache key. This makes it really easy to scope all cache keys and remove them with ease.using Foundatio.Caching;
ICacheClient cache = new InMemoryCacheClient();
await cache.SetAsync("test", 1);
var value = await cache.GetAsync<int>("test");
Queues offer First In, First Out (FIFO) message delivery. We provide four different queue implementations that derive from the IQueue
interface:
using Foundatio.Queues;
IQueue<SimpleWorkItem> queue = new InMemoryQueue<SimpleWorkItem>();
await queue.EnqueueAsync(new SimpleWorkItem {
Data = "Hello"
});
var workItem = await queue.DequeueAsync();
Locks ensure a resource is only accessed by one consumer at any given time. We provide two different locking implementations that derive from the ILockProvider
interface:
ILockProvider
and a string scope
. The scope is prefixed onto every lock key. This makes it really easy to scope all locks and release them with ease.It's worth noting that all lock providers take a ICacheClient
. This allows you to ensure your code locks properly across machines.
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();
Allows you to publish and subscribe to messages flowing through your application. We provide four different message bus implementations that derive from the IMessageBus
interface:
using Foundatio.Messaging;
IMessageBus messageBus = new InMemoryMessageBus();
await messageBus.SubscribeAsync<SimpleMessageA>(msg => {
// Got message
});
await messageBus.PublishAsync(new SimpleMessageA { Data = "Hello" });
Allows you to run a long running process (in process or out of process) without worrying about it being terminated prematurely. We provide three different ways of defining a job, based on your use case:
IJob
interface. We also have a JobBase
base class you can derive from which provides a JobContext and logging. You can then run jobs by calling RunAsync()
on the job or by creating a instance of the JobRunner
class and calling one of the Run methods. The JobRunner can be used to easily run your jobs as Azure Web Jobs.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>
class. You can then run jobs by calling RunAsync()
on the job or passing it to the JobRunner
class. The JobRunner can be used to easily run your jobs as Azure Web Jobs.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;
message bus
. The job must derive from the WorkItemHandlerBase
class. You can then run all shared jobs via JobRunner
class. The JobRunner can be used to easily run your jobs as Azure Web Jobs.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" });
We provide different file storage implementations that derive from the IFileStorage
interface:
We recommend using all of the IFileStorage
implementations as singletons.
using Foundatio.Storage;
IFileStorage storage = new InMemoryFileStorage();
await storage.SaveFileAsync("test.txt", "test");
string content = await storage.GetFileContentsAsync("test.txt")
We provide five implementations that derive from the IMetricsClient
interface:
We recommend using all of the IMetricsClient
implementations as singletons.
IMetricsClient metrics = new InMemoryMetricsClient();
metrics.Counter("c1");
metrics.Gauge("g1", 2.534);
metrics.Timer("t1", 50788);
We have both slides and a sample application that shows off how to use Foundatio.
1 kvo 内部实现原理 a kvo 是基于runtime 机制实现的 b 当某个类的对象第一次被观察时 系统就会在运行期动态的创建该类的一个派生类 当这个派生类中重写基类中任何被观察的setterf setter方法 实现真正的通知机制 person ---》nskvonotifying_person) 2 是否可以把比较耗时的操作放在nsnotifaction中 如果在异步线程fade通知 那
Microsoft Windows Workflow Foundation (WWF) 是一个可扩展框架,用于在 Windows 平台上开发工作流解决方案。作为即将问世的 Microsoft WinFX 的组成部分,Windows Workflow Foundation 同时提供了 API 和一些工具,用于开发和执行基于工作流的应用程序。Windows Workflow Foundation 提供
问题内容: 我已经搜索并搜索了所有内容,当然也查阅了相关文档,但是关于Foundation 4的一些事情,我似乎无法理解,涉及Sass的转换方式。 我已使用create project命令使用Compass设置了Foundation,因此Sass正在运行。app.scss文件正在导入“设置”和“基础”,并使用“ watch”进行编译。 我的问题的前半部分:我了解app.css是从gem中的组件文件
本文向大家介绍Team Foundation Server 安装或设置,包括了Team Foundation Server 安装或设置的使用技巧和注意事项,需要的朋友参考一下 示例 有关设置或安装tfs的详细说明。
我正在遵循一位微软云倡导者的教程来设置Azure DevOps和Jenkins集成:https://medium.com/@bbenz/azure-devops-and-jenkins-in-perfect-harmony-8C92FF980723。 当Jenkins成功完成构建时,我无法触发新的Azure DevOps管道发布。 在Jenkins Team Foundation Server(T
我在一个跨度上有一个通过foundation 5的工具提示,如下所示: 这个很管用。然而,我想做的只是在用户单击span(而不是悬停)时显示工具提示。然后单击“关闭”按钮时关闭。当我添加时,我可以做到一半,但这增加了两个工具提示,当我悬停时仍然会感到失望。 任何想法都将不胜感激。Foundation5没有一些交互式内容的弹出,这似乎很奇怪。 谢谢!
问题内容: 我目前正在使用Java进行Selenium Automation,以满足我对测试自动化的需求。到目前为止,我将TestNg用于报告。 早期没有使用任何报告工具进行集成,因此我在JIRA中手动更新了TestNg结果。 客户最近介绍了Visaul Studio Team Foundation Server。我想在这方面了解以下内容。 我们如何在TFS中更新Selenium-TestNG上的
问题内容: 题 是否有可能重复雨燕数值桥接基金会:■ 引用类型,例如,,和类型?具体来说,复制下面介绍的自动预分配桥接。 这种解决方案的预期用法示例: 背景 一些本机Swift数(值)类型可以自动桥接到(引用)类型: 迅数字结构类型的实例,例如,, ,,和,不能由所表示的 类型,因为仅代表一个类类型的实例。但是,启用桥接到时,可以将Swift数值分配给常量和类型变量, 作为 类的 桥接实例。 …
问题内容: 我正在尝试按照有关“使用Xcode 3.2开发PyObjC”的可接受答案的说明进行操作。由于我没有足够的代表对实际问题发表评论,因此我将在此处重新发布它们: 这是让PyObjC在Snow Leopard中工作的方法: 使用Finder,以访客身份访问并连接到http://svn.red-bean.com/pyobjc/trunk/pyobjc/pyobjc-xcode/。 然后,我在本
问题内容: 我正在使用Zurb的Foundation框架修改应用程序以实现响应性和AngularJS。存在一个错误,即根据Foundation的响应规则隐藏/显示具有的表中显示的数据带有的错误。不幸的是,当角度模型更新时,Foundation不会重新流送新渲染的DOM。 我尝试使用 在广泛的Google搜索中发现的方法,但是实际上并没有触发响应折叠表的重排。我还添加了一条指令来触发一个简单的指令,