Apollo Server server resolution

Posted by nutt318 on Sat, 26 Feb 2022 16:53:05 +0100

For reference to source code analysis, please visit: https://gitee.com/lidishan/apollo-code-analysis
Statement before reading: This article does not provide relevant usage instructions, but only analyzes the Apollo source code

Apollo overall architecture diagram

What is the core function of Apollo server? Configuration change and discovery

Configuration change and discovery

Configuration change and discovery process ReleaseMessage based on a table

Implementation steps

  1. After the Admin Service publishes the configuration, insert the data into the ReleaseMessage table

    1. Data format: appid + cluster (cluster name) + namespace (namespace)
    2. An example is shown in the figure below
  2. The Config Service startup thread scans the ReleaseMessage table once per second

    1. Location: ReleaseMessageScanner#afterPropertiesSet()
  3. When a new message is scanned, all listeners will be notified

    1. Location: e.g. NotificationControllerV2
  4. For example, NotificationControllerV2 notifies the corresponding client

Analysis of source code for appeal flow chart

Regularly scan ReleaseMessage changes

  1. Location: ReleaseMessageScanner#afterPropertiesSet()
  2. Start the timed thread pool and scan the configuration changes every 1 second
    1. Step 1: get the current maximum ID of the ReleaseMessage table
    2. Step 2: execute the scheduled task, regularly scan whether there is any change, take it out in batches (500 one page) and notify
public class ReleaseMessageScanner {
   @Override
   public void afterPropertiesSet() throws Exception {
      // Get timing request time
      databaseScanInterval = bizConfig.releaseMessageScanIntervalInMilli();
      // Step 1: get the current maximum ID of the ReleaseMessage table
      maxIdScanned = loadLargestMessageId();
      // Perform scheduled tasks
      executorService.scheduleWithFixedDelay(() -> {
         Transaction transaction = Tracer.newTransaction("Apollo.ReleaseMessageScanner", "scanMessage");
         try {
             // Step 2: execute the scheduled task, regularly scan whether there is any change, take it out in batches (500 one page) and notify
            scanMessages();
            transaction.setStatus(Transaction.SUCCESS);
         } catch (Throwable ex) {
            transaction.setStatus(ex);
            logger.error("Scan and send message failed", ex);
         } finally {
            transaction.complete();
         }
      }, databaseScanInterval, databaseScanInterval, TimeUnit.MILLISECONDS);
   }
   private void scanMessages() {
      boolean hasMoreMessages = true;
      // Pull in batches, 500 in each batch, and judge whether there is more data first
      while (hasMoreMessages && !Thread.currentThread().isInterrupted()) {
         // Scan and send messages
         hasMoreMessages = scanAndSendMessages();
      }
   }
   /** scan messages and send */
   private boolean scanAndSendMessages() {
      // Obtain the current batch is 500 release records after obtaining the current id
      List<ReleaseMessage> releaseMessages =
              releaseMessageRepository.findFirst500ByIdGreaterThanOrderByIdAsc(maxIdScanned);
      if (CollectionUtils.isEmpty(releaseMessages)) {
         return false;
      }
      // Notify listener
      fireMessageScanned(releaseMessages);
      // Quantity obtained
      int messageScanned = releaseMessages.size();
      // The last id (also the largest id)
      maxIdScanned = releaseMessages.get(messageScanned - 1).getId();
      return messageScanned == 500;
   }
}

The above analysis shows how to scan the changed message and notify the listener. But how do you actually notify the listener? How to respond to the client?

Notify listeners and respond to clients

Following the above, firemessagescanned (release messages) can trace the notification listener logic as follows

  1. The fireMessageScanned(releaseMessages) call traverses listeners
    And notify that NotificationControllerV2#handleMessage() has been called
  2. Via deferredresults Get (content) gets the clients to be notified. If there are too many clients, go asynchronously. If it does not exceed the default value of batch, go to the notification,
    The result of the poll will be sent back to the defaultwrapper, and the result of the poll will be notified
    Note: deferredResutls is inserted into the server interface / notifications/v2/pollNotification requested by the client, and the onTimeout and onCompletion callback methods are executed,
    Just wait for the handleMessage to be processed and the result will be returned (this principle requires tomcat to be an asynchronous connection processing mechanism to realize the method of long polling)
public class NotificationControllerV2 {
   @Override
   public void handleMessage(ReleaseMessage message, String channel) {
      String content = message.getMessage();
      // ... Omit Verify legitimacy
      //create a new list to avoid ConcurrentModificationException
      List<DeferredResultWrapper> results = Lists.newArrayList(deferredResults.get(content));
      ApolloConfigNotification configNotification = new ApolloConfigNotification(changedNamespace, message.getId());
      configNotification.addMessage(content, message.getId());
      // There are too many clients. Open a thread to do async notification if too many clients
      if (results.size() > bizConfig.releaseMessageNotificationBatch()) {
         largeNotificationBatchExecutorService.submit(() -> {
            for (int i = 0; i < results.size(); i++) {
               if (i > 0 && i % bizConfig.releaseMessageNotificationBatch() == 0) {
                  try {
                     TimeUnit.MILLISECONDS.sleep(bizConfig.releaseMessageNotificationBatchIntervalInMilli());
                  } catch (InterruptedException e) {}
               }
               results.get(i).setResult(configNotification);
            }
         });
         return;
      }
      // OK, let's go
      for (DeferredResultWrapper result : results) {
         result.setResult(configNotification);
      }
   }
}

Topics: Java apollo