@Slf4j
@Component
public class MessageConsumer {
@Autowired
private PpcRequestMessageListener ppcRequestMessageListener;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private MessageConverter messageConverter;
@Value("${app.rabbitmq.schedule.queue.ppc-request}")
private String ppcRequestQueue;
private ExecutorService executor = new ThreadPoolExecutor(1, 4, 60, TimeUnit.SECONDS,
new SynchronousQueue<>(), Executors.defaultThreadFactory(),new ThreadPoolExecutor.AbortPolicy());
public void start() {
Executors.newSingleThreadExecutor().execute(() -> {
try {
while (true) {
ChannelCallbackImpl<PpcRequestMessage> callback = new ChannelCallbackImpl<>(ppcRequestQueue, 4);
List<PpcRequestMessage> list = rabbitTemplate.execute(callback);
if (list == null || list.isEmpty()) {
TimeUnit.MILLISECONDS.sleep(1000);
}
}
} catch (Exception e) {
log.error("MessageConsumer handle error", e);
}
});
}
private class ChannelCallbackImpl<T> implements ChannelCallback<List<T>> {
private final MessagePropertiesConverter messagePropertiesConverter = new DefaultMessagePropertiesConverter();
private final String queueName;
private final int maxCount;
public ChannelCallbackImpl(String queueName, int maxCount) {
this.queueName = queueName;
this.maxCount = maxCount;
}
@Override
public List<T> doInRabbit(Channel channel) throws Exception {
GetResponse response = channel.basicGet(queueName, false);
if(response == null){
return null;
}
//如果没有空闲进程,睡眠直至有可用线程
Integer tid = getSetIdleTid();
int total = response.getMessageCount() + 1;
if(total > maxCount){
total = maxCount;
}
long[] tags = new long[total];
List<T> messages = new ArrayList<>(total);
for (int i = 0; i < total; i++) {
MessageProperties props = messagePropertiesConverter.toMessageProperties(response.getProps(), response.getEnvelope(), "UTF-8");
if (response.getMessageCount() >= 0) {
props.setMessageCount(response.getMessageCount());
}
Message message = new Message(response.getBody(), props);
T t = (T) messageConverter.fromMessage(message);
messages.add(t);
tags[i] = response.getEnvelope().getDeliveryTag();
response = channel.basicGet(queueName, false);
}
//accept
for (int i = 0; i < messages.size(); i++) {
executor.execute(new ConsumerAcceptRunnable(tid, channel, tags[i], messages.get(i)));
tid = getSetIdleTid();
}
//ack
for (long tag : tags){
channel.basicAck(tag, false);
}
return messages;
}
private Integer getSetIdleTid() throws Exception {
Integer tid = PpcProcessStatus.getSetIdleTid();
while (tid == null) {
log.info("ppc process has no idle thread, sleep");
TimeUnit.MILLISECONDS.sleep(500);
continue;
}
return tid;
}
public class ConsumerAcceptRunnable implements Runnable{
private final Integer tid;
private final Channel channel;
private final long tag;
private final T t;
public ConsumerAcceptRunnable(Integer tid, Channel channel, long tag, T t) {
this.tid = tid;
this.channel = channel;
this.tag = tag;
this.t = t;
}
@Override
public void run() {
try {
ppcRequestMessageListener.handleMessage(tid, t);
} catch (Exception e) {
log.error("accept message error {}", t, e);
try {
channel.basicNack(tag, false, true);
} catch (Exception ioe) {
log.error("basicNack error", ioe);
}
}
}
}
}
}