NServiceBus能否以不同的速率将事件处理到不同的队列?
我有两个订户参加同一个活动。一个订阅服务器写入数据库,另一个在内存中缓存数据。前者比后者需要更长的时间,这没关系,因为缓存比写入数据库更需要时间。有时候DB编写器落后了,它的队列开始增长,这没关系(只要它最终赶上)。但是,缓存订阅服务器落后是不可接受的。当DB编写器跟不上时,它可以超越DB编写器。我希望尽快处理这两个队列,而不让一个队列的处理影响另一个队列的处理 但是,我看到的是,当DB编写器队列增长时,缓存订户的队列也会增长。这两个队列具有相同数量的挂起项目。他们似乎步调一致 分析表明,DB写入所需的时间大约是内存缓存的500倍(这并不奇怪)。因此,缓存订阅服务器可以很容易地跟上,但它似乎被DB writer订阅服务器阻止了 我会为前面的代码道歉。如果没有端点包装器代码,则更容易理解 此方法将端点创建为事件发布者。(我的包装器代码为不同的目的提供了不同的端点类型。我发现缺少NSB端点抽象): 以下是要点:NServiceBus能否以不同的速率将事件处理到不同的队列?,nservicebus,Nservicebus,我有两个订户参加同一个活动。一个订阅服务器写入数据库,另一个在内存中缓存数据。前者比后者需要更长的时间,这没关系,因为缓存比写入数据库更需要时间。有时候DB编写器落后了,它的队列开始增长,这没关系(只要它最终赶上)。但是,缓存订阅服务器落后是不可接受的。当DB编写器跟不上时,它可以超越DB编写器。我希望尽快处理这两个队列,而不让一个队列的处理影响另一个队列的处理 但是,我看到的是,当DB编写器队列增长时,缓存订户的队列也会增长。这两个队列具有相同数量的挂起项目。他们似乎步调一致 分析表明,DB写
public sealed class EventPublisherEndpoint : Endpoint, IEventPublisherEndpoint
{
private EventPublisherEndpoint(IEndpointInstance nsbEndpoint, string name) : base(nsbEndpoint, name) { }
/// <summary>
/// Create and start endpoint.
/// </summary>
public static async Task<IEventPublisherEndpoint> Start(string endpointName)
{
var ep = await ConfigureEndpoint(new EndpointConfig(), endpointName);
return new EventPublisherEndpoint(ep, endpointName);
}
public async Task Publish(object message)
{
await NsbEndpoint.Publish(message);
}
}
public sealed class EventSubscriberEndpoint<T> : Endpoint, IEventSubscriberEndpoint where T : IEvent
{
private EventSubscriberEndpoint(IEndpointInstance nsbEndpoint, string name) : base(nsbEndpoint, name) {}
public async Task Unsubscribe()
{
await NsbEndpoint.Unsubscribe(typeof(T), new UnsubscribeOptions());
}
/// <summary>
/// Create and start endpoint.
/// </summary>
public static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type[] excludeSubscriberTypes = null)
{
return await Start(endpointName, publisherEndpointName, typeof(T), excludeSubscriberTypes);
}
/// <summary>
/// Non-generic version of Start.
/// </summary>
private static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
return await Start(new EndpointConfig(), endpointName, publisherEndpointName, messageType, excludeSubscriberTypes);
}
private static async Task<IEventSubscriberEndpoint> Start(
IEndpointConfig config,
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
if (publisherEndpointName == null)
throw new ArgumentNullException(nameof(publisherEndpointName));
if (messageType == null)
throw new ArgumentNullException(nameof(messageType));
var ep = await ConfigureEndpoint(config,
endpointName,
endpointConfiguration =>
{
if (excludeSubscriberTypes != null)
endpointConfiguration.AssemblyScanner().ExcludeTypes(excludeSubscriberTypes);
}, transport =>
{
transport.Routing().RegisterPublisher(messageType, GetFullName(publisherEndpointName));
});
return new EventSubscriberEndpoint<T>(ep, endpointName);
}
}
以下是事件类:
public sealed class Datagram : DeviceOutput, IEvent
{
public Datagram(long deviceID, DatagramType payloadType, string payload) : base(deviceID)
{
Payload = payload;
PayloadType = payloadType;
}
public string Payload { get; set; }
public DatagramType PayloadType { get; set; }
public override string ToString()
{
return base.ToString() + Environment.NewLine + $"PayloadType:{PayloadType} Payload:{Payload.Truncate()}";
}
}
以下是订阅服务器端点配置之一:
public async Task Start()
{
if (_endpoint == null)
{
_endpoint = await EventSubscriberEndpoint<Datagram>.Start(
endpointName: "datagram-store-subscriber",
publisherEndpointName: DatagramPublisher.EndpointName);
}
}
public async Task Start()
{
if (_endpoint == null)
{
_endpoint = await EventSubscriberEndpoint<Datagram>.Start(
endpointName: "datagram-live-subscriber",
publisherEndpointName: DatagramPublisher.EndpointName);
}
}
以下是另一个订阅服务器端点配置:
public async Task Start()
{
if (_endpoint == null)
{
_endpoint = await EventSubscriberEndpoint<Datagram>.Start(
endpointName: "datagram-store-subscriber",
publisherEndpointName: DatagramPublisher.EndpointName);
}
}
public async Task Start()
{
if (_endpoint == null)
{
_endpoint = await EventSubscriberEndpoint<Datagram>.Start(
endpointName: "datagram-live-subscriber",
publisherEndpointName: DatagramPublisher.EndpointName);
}
}
以下是EventSubscriberEndpoint:
public sealed class EventPublisherEndpoint : Endpoint, IEventPublisherEndpoint
{
private EventPublisherEndpoint(IEndpointInstance nsbEndpoint, string name) : base(nsbEndpoint, name) { }
/// <summary>
/// Create and start endpoint.
/// </summary>
public static async Task<IEventPublisherEndpoint> Start(string endpointName)
{
var ep = await ConfigureEndpoint(new EndpointConfig(), endpointName);
return new EventPublisherEndpoint(ep, endpointName);
}
public async Task Publish(object message)
{
await NsbEndpoint.Publish(message);
}
}
public sealed class EventSubscriberEndpoint<T> : Endpoint, IEventSubscriberEndpoint where T : IEvent
{
private EventSubscriberEndpoint(IEndpointInstance nsbEndpoint, string name) : base(nsbEndpoint, name) {}
public async Task Unsubscribe()
{
await NsbEndpoint.Unsubscribe(typeof(T), new UnsubscribeOptions());
}
/// <summary>
/// Create and start endpoint.
/// </summary>
public static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type[] excludeSubscriberTypes = null)
{
return await Start(endpointName, publisherEndpointName, typeof(T), excludeSubscriberTypes);
}
/// <summary>
/// Non-generic version of Start.
/// </summary>
private static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
return await Start(new EndpointConfig(), endpointName, publisherEndpointName, messageType, excludeSubscriberTypes);
}
private static async Task<IEventSubscriberEndpoint> Start(
IEndpointConfig config,
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
if (publisherEndpointName == null)
throw new ArgumentNullException(nameof(publisherEndpointName));
if (messageType == null)
throw new ArgumentNullException(nameof(messageType));
var ep = await ConfigureEndpoint(config,
endpointName,
endpointConfiguration =>
{
if (excludeSubscriberTypes != null)
endpointConfiguration.AssemblyScanner().ExcludeTypes(excludeSubscriberTypes);
}, transport =>
{
transport.Routing().RegisterPublisher(messageType, GetFullName(publisherEndpointName));
});
return new EventSubscriberEndpoint<T>(ep, endpointName);
}
}
公共密封类EventSubscriberEndpoint:Endpoint,IEventSubscriberEndpoint其中T:IEvent
{
private EventSubscriberEndpoint(IEndpointInstance nsbEndpoint,字符串名称):基(nsbEndpoint,名称){}
公共异步任务取消订阅()
{
等待NsbEndpoint.Unsubscribe(typeof(T),new Unsubscribe options());
}
///
///创建和启动端点。
///
公共静态异步任务启动(
字符串端点名称,
字符串名称,
类型[]excludeSubscriberTypes=null)
{
返回等待开始(endpointName、publisherEndpointName、typeof(T)、excludeSubscriberTypes);
}
///
///启动的非通用版本。
///
专用静态异步任务启动(
字符串端点名称,
字符串名称,
类型messageType,
类型[]excludeSubscriberTypes=null)
{
返回等待启动(new EndpointConfig()、endpointName、publisherEndpointName、messageType、excludeSubscriberTypes);
}
专用静态异步任务启动(
IEndpointConfig配置,
字符串端点名称,
字符串名称,
类型messageType,
类型[]excludeSubscriberTypes=null)
{
if(publisherEndpointName==null)
抛出新ArgumentNullException(nameof(publisherEndpointName));
if(messageType==null)
抛出新ArgumentNullException(nameof(messageType));
var ep=等待配置端点(配置,
端点名称,
endpointConfiguration=>
{
if(excludeSubscriberTypes!=null)
endpointConfiguration.AssemblyScanner().ExcludeTypes(excludeSubscriberTypes);
},运输=>
{
transport.Routing().RegisterPublisher(messageType,GetFullName(publisherEndpointName));
});
返回新的EventSubscriberEndpoint(ep,endpointName);
}
}
是否有允许队列相互独立处理的特殊配置
NServiceBus 6.2.1这里的主要问题是如何发布事件(如果是实际事件?)以及如何处理这些事件。你能把代码也发到发布者/订阅者的位子上吗?从理论上讲,同一事件的两个处理程序之间没有耦合,如果你真的要引发一个事件,那么它们应该分别处理消息的副本,但我认为你想要的不是这样?因为有一个固有的顺序(先写数据库,然后缓存),所以最好在数据库更新后发出一个命令来同步缓存?为您添加了很多代码。。。msg作为事件发布。我不知道“如何处理事件”是什么意思,因为我只知道一种方法:定义处理程序方法。。。当DB写入备份时,我希望live处理程序保持最新状态。不希望处理程序同步。。。顺便说一句,我通过将处理程序放在单独的进程中解决了我的问题。幸运的是,该系统已经有了一个适合这一目的的流程。尽管如此,我认为NSB应该提供一种在同一进程中以不同速率处理事件的方法。我是否正确理解有两种类型的消息进入同一队列?很长一段时间以来,NSB端点都绑定到AppDomain,因此即使可以在一个进程中运行多个端点,这样做也不是很简单。如果希望并行运行事件订阅处理程序,则将它们放在两个进程中的操作是正确的。这显示了如何在一个项目/流程/应用域中承载多个端点:Nope。在队列中输入一个类型。一个端点用于发布,两个端点用于处理事件
public sealed class EventSubscriberEndpoint<T> : Endpoint, IEventSubscriberEndpoint where T : IEvent
{
private EventSubscriberEndpoint(IEndpointInstance nsbEndpoint, string name) : base(nsbEndpoint, name) {}
public async Task Unsubscribe()
{
await NsbEndpoint.Unsubscribe(typeof(T), new UnsubscribeOptions());
}
/// <summary>
/// Create and start endpoint.
/// </summary>
public static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type[] excludeSubscriberTypes = null)
{
return await Start(endpointName, publisherEndpointName, typeof(T), excludeSubscriberTypes);
}
/// <summary>
/// Non-generic version of Start.
/// </summary>
private static async Task<IEventSubscriberEndpoint> Start(
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
return await Start(new EndpointConfig(), endpointName, publisherEndpointName, messageType, excludeSubscriberTypes);
}
private static async Task<IEventSubscriberEndpoint> Start(
IEndpointConfig config,
string endpointName,
string publisherEndpointName,
Type messageType,
Type[] excludeSubscriberTypes = null)
{
if (publisherEndpointName == null)
throw new ArgumentNullException(nameof(publisherEndpointName));
if (messageType == null)
throw new ArgumentNullException(nameof(messageType));
var ep = await ConfigureEndpoint(config,
endpointName,
endpointConfiguration =>
{
if (excludeSubscriberTypes != null)
endpointConfiguration.AssemblyScanner().ExcludeTypes(excludeSubscriberTypes);
}, transport =>
{
transport.Routing().RegisterPublisher(messageType, GetFullName(publisherEndpointName));
});
return new EventSubscriberEndpoint<T>(ep, endpointName);
}
}