Package org.apache.helix.ipc

Source Code of org.apache.helix.ipc.HelixIPCMessageManager

package org.apache.helix.ipc;

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

import io.netty.buffer.ByteBuf;
import org.apache.helix.resolver.HelixAddress;
import org.apache.helix.resolver.HelixMessageScope;
import org.apache.log4j.Logger;

import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
* A wrapper around a base IPC service that manages message retries / timeouts.
* <p>
* This class manages retries and timeouts, and can be used in the same way as a
* {@link org.apache.helix.ipc.HelixIPCService}.
* </p>
* <p>
* A message will be sent until the max number of retries has been reached (i.e. timed out), or it
* is acknowledged by the recipient. If the max number of retries is -1, it will be retried forever.
* </p>
* <p>
* A callback should be registered for every acknowledgement message type associated with any
* original message type sent by this class.
* </p>
* <p>
* For example, consider we have the two message types defined: DATA_REQ = 1, DATA_ACK = 2. One
* would do the following:
*
* <pre>
* messageManager.registerCallback(DATA_ACK, new HelixIPCCallback() {
*   public void onMessage(HelixMessageScope scope, UUID messageId, ByteBuf message) {
*     // Process ACK
*   }
*
*   public void onError(HelixMessageScope scope, UUID messageId, Throwable cause) {
*     // Message error or timeout
*   }
* });
*
* messageManager.send(destinations, DATA_REQ, messageId, data);
* </pre>
*
* </p>
* <p>
* In send, we note messageId, and retry until we get a DATA_ACK for the same messageId. The
* callback registered with the message manager will only be called once, even if the message is
* acknowledged several times.
* </p>
*/
public class HelixIPCMessageManager implements HelixIPCService {

  private static final Logger LOG = Logger.getLogger(HelixIPCMessageManager.class);

  private final ScheduledExecutorService scheduler;
  private final HelixIPCService baseIpcService;
  private final long messageTimeoutMillis;
  private final int maxNumRetries;
  private final ConcurrentMap<UUID, Boolean> pendingMessages;
  private final ConcurrentMap<UUID, AtomicInteger> retriesLeft;
  private final ConcurrentMap<UUID, ByteBuf> messageBuffers;
  private final ConcurrentMap<Integer, HelixIPCCallback> callbacks;

  public HelixIPCMessageManager(ScheduledExecutorService scheduler, HelixIPCService baseIpcService,
      long messageTimeoutMillis, int maxNumRetries) {
    this.scheduler = scheduler;
    this.baseIpcService = baseIpcService;
    this.maxNumRetries = maxNumRetries;
    this.messageTimeoutMillis = messageTimeoutMillis;
    this.pendingMessages = new ConcurrentHashMap<UUID, Boolean>();
    this.retriesLeft = new ConcurrentHashMap<UUID, AtomicInteger>();
    this.messageBuffers = new ConcurrentHashMap<UUID, ByteBuf>();
    this.callbacks = new ConcurrentHashMap<Integer, HelixIPCCallback>();
  }

  @Override
  public void start() throws Exception {
    baseIpcService.start();
  }

  @Override
  public void shutdown() throws Exception {
    baseIpcService.shutdown();
  }

  @Override
  public void send(final HelixAddress destination, final int messageType, final UUID messageId,
      final ByteBuf message) {
    // State
    pendingMessages.put(messageId, true);
    retriesLeft.putIfAbsent(messageId, new AtomicInteger(maxNumRetries));
    messageBuffers.put(messageId, message);

    // Will free it when we've finally received response
    message.retain();

    // Send initial message
    baseIpcService.send(destination, messageType, messageId, message);

    // Retries
    scheduler.schedule(new Runnable() {
      @Override
      public void run() {
        Boolean isPending = pendingMessages.get(messageId);
        AtomicInteger numLeft = retriesLeft.get(messageId);
        if (numLeft != null && isPending != null && isPending) {
          if (numLeft.decrementAndGet() > 0 || maxNumRetries == -1) {
            // n.b. will schedule another retry
            send(destination, messageType, messageId, message);
          } else {
            LOG.warn("Message " + messageId + " timed out after " + maxNumRetries + " retries");
          }
        }
      }
    }, messageTimeoutMillis, TimeUnit.MILLISECONDS);
  }

  @Override
  public void registerCallback(final int messageType, final HelixIPCCallback callback) {

    // This callback will first check if the message is pending, then delegate to the provided
    // callback if it has not yet done so.
    HelixIPCCallback wrappedCallback = new HelixIPCCallback() {
      @Override
      public void onMessage(HelixMessageScope scope, UUID messageId, ByteBuf message) {
        if (pendingMessages.replace(messageId, true, false)) {
          pendingMessages.remove(messageId);
          ByteBuf originalMessage = messageBuffers.remove(messageId);
          if (originalMessage != null) {
            originalMessage.release();
          }
          retriesLeft.remove(messageId);
          callback.onMessage(scope, messageId, message);
        }
      }
    };

    callbacks.put(messageType, wrappedCallback);
    baseIpcService.registerCallback(messageType, wrappedCallback);
  }
}
TOP

Related Classes of org.apache.helix.ipc.HelixIPCMessageManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.