楼主在项目中需要实现分布式lucene查询,由于lucene的索引是存放在本地的。网上有很多方案实现起来相对比较复杂,故楼主为了简单化针对索引同步问题采用的方案是,如果某一结点发生索引的增删改,通过rabbitmq通知所有lucene节点也进行本地的索引的更改。
fanout类型的exchange虽然可以通知所有队列,但一个队列只能绑定一个消费者(如果绑定多个消费者,只要有一个消费者消费了这个消息,其他的消费者就获得不到这个消息了,至于手动ask经测试不行,具体原因没有细看),然后受spring cloud bus的启发,每个lucene实例都绑定一个独立的队列,所有的队列绑定到同一个exchange,从而实现一对多通知的功能。
同时,考虑到上线和拓展的便捷性,要求队列的添加和绑定全部动态自动完成。代码逻辑不复杂,只是有些类不常用,网上的资料相对较少,故把完整源码贴出来,供大家学习交流。代码质量不高,如有错误欢迎大家及时指正!
package com.unionpay.wash.lucene_service.MQListener;
import com.rabbitmq.http.client.domain.QueueInfo;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.*;
import org.springframework.amqp.rabbit.core.RabbitManagementTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.UUID;
@Component
public class MQListener implements CommandLineRunner {
@Autowired
private RabbitTemplate template;
@Autowired
private AmqpAdmin admin;
private static String queueName;
@Override
public void run(String... args) throws Exception {
RabbitManagementTemplate managementTemplate = new RabbitManagementTemplate();
String exchangeName = "lucene_exchange";
Exchange exchange = managementTemplate.getExchange(exchangeName);
if (exchange == null) {
managementTemplate.addExchange(new FanoutExchange(exchangeName));
}
List<QueueInfo> queues = managementTemplate.getClient().getQueues();
for (QueueInfo queueInfo : queues){
String s = queueInfo.getName();
String idleSince = queueInfo.getIdleSince();
if (queueInfo.getConsumerCount() == 0 && s.contains("lucene_index") && idleSince != null){
managementTemplate.deleteQueue(new Queue(s));
}
}
queueName = "lucene_index_"+UUID.randomUUID();
managementTemplate.addQueue(new Queue(queueName));
Binding binding = BindingBuilder.bind(new Queue(queueName)).to(new FanoutExchange(exchangeName));
admin.declareBinding(binding);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (true){
try{
Object o = template.receiveAndConvert(queueName,100);
if (o == null){
Thread.sleep(1000);
}else{
//TODO
}
}catch (InterruptedException e){
e.printStackTrace();
}catch (AmqpException e){
managementTemplate.addQueue(new Queue(queueName));
BindingBuilder.bind(new Queue(queueName)).to(new FanoutExchange(exchangeName));
}
}
}
});
thread.start();
}
}