我有一项服务需要尽快从Amazon SQS读取消息。我们期待繁忙的流量,我希望能够以每秒10K的消息读取数据。不幸的是,我目前大约有10条消息/秒。显然,我有工作要做。
这就是我正在使用的(转换为控制台应用程序以使测试更容易):
private static int _concurrentRequests;
private static int _maxConcurrentRequests;
public static void Main(string[] args) {
_concurrentRequests = 0;
_maxConcurrentRequests = 100;
var timer = new Timer();
timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
timer.Interval = 10;
timer.Enabled = true;
Console.ReadLine();
timer.Dispose();
}
public static void OnTimedEvent(object s, ElapsedEventArgs e) {
if (_concurrentRequests < _maxConcurrentRequests) {
_concurrentRequests++;
ProcessMessages();
}
}
public static async Task ProcessMessages() {
var manager = new MessageManager();
manager.ProcessMessages(); // this is an async method that reads in the messages from SQS
_concurrentRequests--;
}
我没有接近100个并发请求,并且它似乎没有每10毫秒触发OnTimedEvent
。
我不确定Timer
是否是正确的做法。我对这种编码没有多少经验。我愿意在这一点上尝试任何事情。
更新
感谢calebboyd,我更接近实现目标。这是一些非常糟糕的代码:
private static SemaphoreSlim _locker;
public static void Main(string[] args) {
_manager = new MessageManager();
RunBatchProcessingForeverAsync();
}
private static async Task RunBatchProcessingForeverAsync() {
_locker = new SemaphoreSlim(10, 10);
while (true) {
Thread thread = new Thread(new ParameterizedThreadStart(Process));
thread.Start();
}
}
private static async void Process(object args) {
_locker.WaitAsync();
try {
await _manager.ProcessMessages();
}
finally {
_locker.Release();
}
}
我能够接近每秒读取相当数量的消息,但问题是我的ProcessMessages
呼叫永远不会完成(或者可能会在很长一段时间后完成)。我想我可能需要限制我在任何时候运行的线程数。
有关如何改进此代码以便ProcessMessages
有机会完成的任何建议吗?
答案 0 :(得分:3)
因为您的MessageManager对象上的ProcessMessages
方法没有被等待,我将假设它被绑定到它执行的同一个线程。仅仅将函数标记为async
并不会将工作传递给一个新的线程。有了这个假设,这个代码实际上并没有用多个线程执行。您可以使用以下代码在更多的线程池中执行代码。
管理器对象可能无法处理并发使用。所以我在Task.Run lambda中创建它。这也可能是昂贵的,因此是不切实际的。
async Task RunBatchProcessingForeverAsync () {
var lock = new SemaphoreSlim(initialCount: 10);
while (true) {
await lock.WaitAsync();
Task.Run(() => {
try {
var manager = new MessageManager();
manager.ProcessMessages();
} finally {
lock.Release();
}
});
}
}
我暂时没有写过C#,但这应该同时,重复,永久地运行你的方法10次。
答案 1 :(得分:1)
正如@calebboyd建议的那样,您必须首先使您的线程异步。现在,如果你去这里 - Where to use concurrency when calling an API,您将看到一个异步线程足以快速汇集网络资源。如果您能够在单个请求中从amazon获取多条消息,那么您的生产者线程(对亚马逊进行异步调用的线程)就可以了 - 它可以每秒发送数百个请求。这不会是你的瓶颈。但是,处理接收数据的继续任务将被传递给线程池。在这里你有机会获得瓶颈 - 假设每秒响应100个响应,每个响应包含100条消息(达到你的10K msgs / sec近似值)。每秒您有100个新任务,每个任务都需要您的线程处理100条消息。现在有两种选择:(1)这些消息的处理不受CPU限制 - 您只需将它们发送到您的数据库,或者(2),您执行CPU消耗计算,例如科学计算,序列化或一些繁重的业务逻辑。如果(1)是你的情况,那么瓶颈会向后推向你的DB。如果(2),则除了放大/缩小或优化计算之外别无选择。但是你的瓶颈可能不是生产线程 - 如果它的实现正确(参见上面的链接中的例子)。