All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xiaomi.infra.galaxy.emq.client.AutoRetryClient Maven / Gradle / Ivy

The newest version!
package com.xiaomi.infra.galaxy.emq.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.xiaomi.infra.galaxy.emq.thrift.ErrorCode;
import com.xiaomi.infra.galaxy.emq.thrift.RetryType;

/**
 * Copyright 2015, Xiaomi.
 * All rights reserved.
 * Author: [email protected]
 */

public class AutoRetryClient {
  //Auto retry client proxy.

  private static class AutoRetryHandler implements InvocationHandler {
    private static final Logger LOG = LoggerFactory.getLogger(AutoRetryHandler.class);
    private final Object instance;
    private final boolean isRetry;
    private final int maxRetry;
    private ThreadLocal lastPauseTime = new ThreadLocal() {
      public Long initialValue() {
        return 0l;
      }
    };

    public AutoRetryHandler(Class interfaceClass, Object instance, boolean isRetry,
        int maxRetry) {
      this.instance = instance;
      this.isRetry = isRetry;
      this.maxRetry = maxRetry;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      int retry = 0;
      long pauseTime = ThrottleUtils.getPauseTime(lastPauseTime.get());
      do {
        try {
          // sleep when entering a new method invoke
          if (retry == 0) {
            ThrottleUtils.sleepPauseTime(pauseTime);
          }
          Object ret = method.invoke(instance, args);
          lastPauseTime.set(pauseTime < 0 ? 0 : pauseTime);
          return ret;
        } catch (InvocationTargetException e) {
          LOG.debug("sdk: invocation exception", e);
          Throwable cause = e.getCause();
          LOG.debug("get cause is:" + cause.toString());
          LOG.debug("max retry = " + maxRetry);
          if (maxRetry < 0 || retry >= maxRetry) {
            lastPauseTime.set(pauseTime < 0 ? 0 : pauseTime);
            LOG.debug("reach max retry number, retry = {}", retry);
            throw cause;
          }

          ErrorCode code = RetryUtils.getErrorCode(cause);
          RetryType retryType = RetryUtils.getRetryType(code, method.getName());
          pauseTime = ThrottleUtils.getPauseTime(code, retry);
          if (!(isRetry || (retryType != null && retryType.equals(RetryType.SAFE)))
              || pauseTime < 0) {
            lastPauseTime.set(pauseTime < 0 ? 0 : pauseTime);
            LOG.debug("Won't retry, retry = {}, ex={}", retry, cause.toString());
            throw cause;
          }

          if (pauseTime >= 0) {
            ThrottleUtils.sleepPauseTime(pauseTime);
            LOG.debug("sleep for {} ms in the {} retry", pauseTime, retry);
          }
          ++retry;
          LOG.debug("Auto retrying RPC call {} for {} time", method.getName(), retry);
        }
      } while (true);
    }

  }

  /**
   * Create client wrapper which automatically retry the RPC calls for retryable errors until
   * success or reaches max retry time.
   */
  @SuppressWarnings("unchecked")
  public static  T getAutoRetryClient(Class interfaceClass, Object instance, boolean isRetry,
      int maxRetry) {
    return (T) Proxy.newProxyInstance(AutoRetryClient.class.getClassLoader(),
        new Class[]{interfaceClass},
        new AutoRetryHandler(interfaceClass, instance, isRetry, maxRetry));
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy