在企企云的open api开发当中,订阅实体的cud事件后,订阅对象会把消息放到AWS SQS队列中,该队列采用AWS的标准队列,消息保证会发送一次(反过来说可能会发送多次,处理时需要注意下)。
在处理AWS 队列,AWS给的.net 代码示例用了简单的控制台死循环,实际项目中可以使用一个独立的线程来处理,可以采用订阅模式,这个线程在读取SQS队列的信息后发布event。由订阅event的其他线程来做业务方面的处理。
由于我的代码使用abp框架,所以我使用了AbpBackgroundWorker来处理,在订阅模式方面abp提供了eventbus来处理event。原理还是那个原理,就是abp框架提供了便利。
里面需要注意的点有:
在Worker的StartAsync方法中,不要 await localEventBus.PublishAsync,否则会等待eventbus处理完后才会继续进行,这可能造成线程死掉(这里不做详细的分析了)。示例代码如下:
while (true)
{
var msg = await GetMessage(sqsClient, queueUrl, 10);
if (msg != null && msg.Messages.Count != 0)
{
var msgData =msg.Messages.Select(msg => JsonConvert.DeserializeObject<Q7hubSqsEvent>(msg.Body));
//抛出数据,不要await
localEventBus.PublishAsync(msgData);
//删除已经处理的数据,不要await
DeleteMessage(sqsClient, msg.Messages, queueUrl);
}
}
由于sqs队列可能会重复发送信息,并且由于单据会在几秒内发生多次状态的变化而等待SQS队列的长轮询是有等待事件的,可能在一次读取中遇到单据的信息是冗余和无效的。需要换个思路——只保留实体ID,具体的内容主动去查询。(注意:billTypeId: 单据类型ID,仅在单据时有值;billTypeCode: 单据类型编码, 仅在单据时有值。)示例代码如下:
var ids = eventData.Where(e=>e.objectName=="Project"&&!string.IsNullOrEmpty(e.billTypeId)&&e.billTypeId== "QT3GHL501QD0001").GroupBy(e => e.objectId).Select(eventData => eventData.Key).ToList();