A scenario

If the service is deployed on multiple nodes, the configuration data is stored in the local cache because the configuration is rarely changed and is frequently queried. After the configuration is modified, the cache is updated. However, only the cache of the currently accessed node is updated, but the cache of other nodes is not modified.

The second analysis

How do you update the cache on one node and also update the cache on the other nodes? Can message queues solve this problem?

Three solutions

Using the Rabbit broadcast mode, this can be achieved by using switch binding queues, which generate a unique queue to bind to the switch after each service node is started. This enables multiple different queues to process messages when they are found on the switch

Four implementation

  1. Create configurations, declare switches, dynamic queues, bind switch queues, bind listeners

    /** * @author * @description Broadcast mode Clear local cache * @date 2021/8/27 */ @configuration public class CacheRabbitConfig {public static final String MY_FANOUTEXCHANGE_NAME = "local-cache-exchange"; /** * create queue name with UUID, Public static final String MY_QUEUE_NAME = uid.randomuuid ().toString(); @Autowired RabbitTemplate rabbitTemplate; /** * create dynamic queue automatically delete queue, Public Queue myQueue() {return new Queue(MY_QUEUE_NAME, true,false,true); /** * create Exchange * @return */ @bean public FanoutExchange FanoutExchange () {return new FanoutExchange(MY_FANOUTEXCHANGE_NAME, true, false); } /** * bind current queue to Exchange * @return */ @bean public Binding bindingExchangeMyQueue() {return BindingBuilder.bind(myQueue()).to(fanoutExchange()); } / set message processing * * * * @ return * / @ Bean public SimpleMessageListenerContainer mqMessageContainer (ClearCacheListener clearCacheListener) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(rabbitTemplate.getConnectionFactory()); container.setQueueNames(MY_QUEUE_NAME); container.setExposeListenerChannel(true); / / set the number of messages every consumer get the biggest container. SetPrefetchCount (1); / / consumer number container. SetConcurrentConsumers (1); / / set the confirm mode to manually confirm the container. The setAcknowledgeMode (AcknowledgeMode. MANUAL); / / message processing class container. SetMessageListener (clearCacheListener); return container; }}Copy the code
  2. Setting up a listening implementation

    @date 2021/8/27 */ @component public class ClearCacheListener implements ChannelAwareMessageListener { private static final Logger log = LoggerFactory.getLogger(ClearCacheListener.class); @Autowired private SysConfigService sysConfigService; @override public void onMessage(Message Message, Channel Channel){try {log.info(" correlationId:[{}]",new String(message.getMessageProperties().getCorrelationId())); / / delete local cache sysConfigService. CleanLocal (); channel.basicAck(message.getMessageProperties().getDeliveryTag(), false); }catch (Exception e) {log.error(" failed to clear cached message ",e); }}}Copy the code
  3. Establishing message producers

    /** * @author * @descript */ @component public class ClearCacheProducer extends AbstractRabbitProducer { protected static final Logger logger = LogManager.getCurrentClassLogger(); public ClearCacheProducer(RabbitTemplate rabbitTemplate) { super(rabbitTemplate, MQConstant.APP_ID); } public void sendDelSystemCacheMessage() { CorrelationData correlationData = getCorrelationData(); Logger. info(" Send message to cache queue,correlationId:{} message :[{}]", correlationdata.getid ()," Clear system configuration cache "); this.sendMsg("local-cache-exchange", "", "", correlationData); Logger. info(" Message sent successfully correlationId:{}", correlationdata.getid ()); }}Copy the code
  4. Call to clear the node cache

    Public void clean() {this.cache.clean(); this.cache.clean(); / / remove internal and external node system configuration cache clearCacheProducer sendDelSystemCacheMessage (); }Copy the code

Five epilogue

The overall implementation is relatively simple, the main one is more difficult to think of is that each node will create a different queue, that is, different nodes will produce different results, and note that the queue must be dynamic queue

return new Queue(MY_QUEUE_NAME, true,false,true);

``` /** * Construct a new queue, given a name, durability, exclusive and auto-delete flags. * @param name the name of the queue. * @param durable true if we are declaring a durable queue (the queue will survive a server restart) * @param exclusive true if we are declaring an exclusive queue (the queue will only be used by the declarer's * connection) * @param autoDelete true if the server should delete the queue when it is no longer in use */ public Queue(String name, boolean durable, boolean exclusive, boolean autoDelete) { this(name, durable, exclusive, autoDelete, null); } ` ` `Copy the code

Durable Indicates whether to maintain a persistent queue. If durable, data in the service restart queue is in an exclusive queue. If true, the queue is visible only to the connection. This time I set automatic deletion, because if you do not set automatic deletion, a new node will be created and the original node will not be deleted, causing rabbitMQ queues to pile up.