diff --git a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyConsistentHashLoadBalance.java b/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyConsistentHashLoadBalance.java new file mode 100644 index 00000000..fbf41c14 --- /dev/null +++ b/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyConsistentHashLoadBalance.java @@ -0,0 +1,144 @@ +package org.dromara.hmily.tars.loadbalance; + +import com.qq.tars.client.ServantProxyConfig; +import com.qq.tars.client.cluster.ServantInvokerAliveChecker; +import com.qq.tars.client.cluster.ServantInvokerAliveStat; +import com.qq.tars.client.rpc.InvokerComparator; +import com.qq.tars.client.rpc.loadbalance.LoadBalanceHelper; +import com.qq.tars.common.util.CollectionUtils; +import com.qq.tars.common.util.Constants; +import com.qq.tars.common.util.StringUtils; +import com.qq.tars.rpc.common.InvokeContext; +import com.qq.tars.rpc.common.Invoker; +import com.qq.tars.rpc.common.LoadBalance; +import com.qq.tars.rpc.common.exc.NoInvokerException; +import com.qq.tars.support.log.LoggerFactory; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.SortedMap; +import java.util.Collection; +import java.util.concurrent.ConcurrentSkipListMap; + +/** + * add HmilyConsistentHashLoadBalance. + * + * @author tydhot + */ +public class HmilyConsistentHashLoadBalance implements LoadBalance { + + private static final Logger LOGGER = LoggerFactory.getClientLogger(); + + private final ServantProxyConfig config; + + private final InvokerComparator comparator = new InvokerComparator(); + + private volatile ConcurrentSkipListMap> conHashInvokersCache; + + private volatile List> sortedInvokersCache; + + public HmilyConsistentHashLoadBalance(final ServantProxyConfig config) { + this.config = config; + } + + /** + * Use load balancing to select invoker. + * + * @param invocation invocation + * @return Invoker + * @throws NoInvokerException NoInvokerException + */ + @Override + public Invoker select(final InvokeContext invocation) throws NoInvokerException { + long consistentHash = Math.abs(StringUtils.convertLong(invocation.getAttachment(Constants.TARS_CONSISTENT_HASH), 0)); + consistentHash = consistentHash & 0xFFFFFFFFL; + + ConcurrentSkipListMap> conHashInvokers = conHashInvokersCache; + if (conHashInvokers != null && !conHashInvokers.isEmpty()) { + if (!conHashInvokers.containsKey(consistentHash)) { + SortedMap> tailMap = conHashInvokers.tailMap(consistentHash); + if (tailMap.isEmpty()) { + consistentHash = conHashInvokers.firstKey(); + } else { + consistentHash = tailMap.firstKey(); + } + } + + Invoker invoker = conHashInvokers.get(consistentHash); + if (invoker.isAvailable()) { + return invoker; + } + + ServantInvokerAliveStat stat = ServantInvokerAliveChecker.get(invoker.getUrl()); + if (stat.isAlive() || (stat.getLastRetryTime() + (config.getTryTimeInterval() * 1000)) < System.currentTimeMillis()) { + LOGGER.info("try to use inactive invoker|" + invoker.getUrl().toIdentityString()); + stat.setLastRetryTime(System.currentTimeMillis()); + return invoker; + } + } + + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(config.getSimpleObjectName() + " can't find active invoker using consistent hash loadbalance. try to use normal hash"); + } + + List> sortedInvokers = sortedInvokersCache; + if (sortedInvokers == null || sortedInvokers.isEmpty()) { + throw new NoInvokerException("no such active connection invoker"); + } + + List> list = new ArrayList>(); + for (Invoker invoker : sortedInvokers) { + if (!invoker.isAvailable()) { + //Shield then call + ServantInvokerAliveStat stat = ServantInvokerAliveChecker.get(invoker.getUrl()); + if (stat.isAlive() || (stat.getLastRetryTime() + (config.getTryTimeInterval() * 1000)) < System.currentTimeMillis()) { + list.add(invoker); + } + } else { + list.add(invoker); + } + } + //TODO When all is not available. Whether to randomly extract one + if (list.isEmpty()) { + throw new NoInvokerException(config.getSimpleObjectName() + " try to select active invoker, size=" + sortedInvokers.size() + ", no such active connection invoker"); + } + + Invoker invoker = list.get((int) (consistentHash % list.size())); + + if (!invoker.isAvailable()) { + LOGGER.info("try to use inactive invoker|" + invoker.getUrl().toIdentityString()); + ServantInvokerAliveChecker.get(invoker.getUrl()).setLastRetryTime(System.currentTimeMillis()); + } + + return HmilyLoadBalanceUtils.doSelect(invoker, sortedInvokersCache); + } + + /** + * Refresh local invoker. + * + * @param invokers invokers + */ + @Override + public void refresh(final Collection> invokers) { + LOGGER.info(config.getSimpleObjectName() + " try to refresh ConsistentHashLoadBalance's invoker cache, size=" + (invokers == null || invokers.isEmpty() ? 0 : invokers.size())); + if (CollectionUtils.isEmpty(invokers)) { + sortedInvokersCache = null; + conHashInvokersCache = null; + return; + } + + List> sortedInvokersTmp = new ArrayList<>(invokers); + sortedInvokersTmp.sort(comparator); + + sortedInvokersCache = sortedInvokersTmp; + + ConcurrentSkipListMap> concurrentSkipListMap = new ConcurrentSkipListMap>(); + LoadBalanceHelper.buildConsistentHashCircle(sortedInvokersTmp, config).forEach(concurrentSkipListMap::put); + conHashInvokersCache = concurrentSkipListMap; + + LOGGER.info(config.getSimpleObjectName() + " refresh ConsistentHashLoadBalance's invoker cache done, conHashInvokersCache size=" + + (conHashInvokersCache == null || conHashInvokersCache.isEmpty() ? 0 : conHashInvokersCache.size()) + + ", sortedInvokersCache size=" + (sortedInvokersCache == null || sortedInvokersCache.isEmpty() ? 0 : sortedInvokersCache.size())); + } +} diff --git a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyLoadBalance.java b/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyLoadBalance.java index 239a87b2..80967b4f 100644 --- a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyLoadBalance.java +++ b/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/loadbalance/HmilyLoadBalance.java @@ -27,6 +27,10 @@ public class HmilyLoadBalance implements LoadBalance { private final Object hashLoadBalanceLock = new Object(); + private volatile HmilyConsistentHashLoadBalance consistentHashLoadBalance; + + private final Object consistentHashLoadBalanceLock = new Object(); + public HmilyLoadBalance(final HmilyRoundRobinLoadBalance hmilyRoundRobinLoadBalance, final ServantProxyConfig config) { this.hmilyRoundRobinLoadBalance = hmilyRoundRobinLoadBalance; this.config = config; @@ -35,6 +39,20 @@ public class HmilyLoadBalance implements LoadBalance { @Override public Invoker select(final InvokeContext invocation) throws NoInvokerException { long hash = Math.abs(StringUtils.convertLong(invocation.getAttachment(Constants.TARS_HASH), 0)); + long consistentHash = Math.abs(StringUtils.convertLong(invocation.getAttachment(Constants.TARS_CONSISTENT_HASH), 0)); + + if (consistentHash > 0) { + if (consistentHashLoadBalance == null) { + synchronized (consistentHashLoadBalanceLock) { + if (consistentHashLoadBalance == null) { + HmilyConsistentHashLoadBalance tmp = new HmilyConsistentHashLoadBalance(config); + tmp.refresh(lastRefreshInvokers); + consistentHashLoadBalance = tmp; + } + } + } + return consistentHashLoadBalance.select(invocation); + } if (hash > 0) { if (hashLoadBalance == null) { @@ -62,6 +80,12 @@ public class HmilyLoadBalance implements LoadBalance { } } + synchronized (consistentHashLoadBalanceLock) { + if (consistentHashLoadBalance != null) { + consistentHashLoadBalance.refresh(invokers); + } + } + hmilyRoundRobinLoadBalance.refresh(invokers); } diff --git a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyConfiguration.java b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyConfiguration.java similarity index 88% rename from hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyConfiguration.java rename to hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyConfiguration.java index 264a9c9b..f460dce6 100644 --- a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyConfiguration.java +++ b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyConfiguration.java @@ -1,6 +1,7 @@ -package org.dromara.hmily.tars.spring; +package org.dromara.hmily.tars.startup; import com.qq.tars.client.Communicator; +import org.dromara.hmily.tars.spring.TarsHmilyCommunicatorBeanPostProcessor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; diff --git a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyFilterStartup.java b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyFilterStartup.java similarity index 92% rename from hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyFilterStartup.java rename to hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyFilterStartup.java index d0d7c6ea..b2114e90 100644 --- a/hmily-rpc/hmily-tars/src/main/java/org/dromara/hmily/tars/spring/TarsHmilyFilterStartup.java +++ b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/java/org/dromara/hmily/tars/startup/TarsHmilyFilterStartup.java @@ -1,4 +1,4 @@ -package org.dromara.hmily.tars.spring; +package org.dromara.hmily.tars.startup; import com.qq.tars.common.FilterKind; import com.qq.tars.server.core.AppContextManager; diff --git a/hmily-rpc/hmily-tars/src/main/resources/META-INF/hmily/spring.factories b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/resources/META-INF/spring.factories similarity index 55% rename from hmily-rpc/hmily-tars/src/main/resources/META-INF/hmily/spring.factories rename to hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/resources/META-INF/spring.factories index ba6c41ec..43a01571 100644 --- a/hmily-rpc/hmily-tars/src/main/resources/META-INF/hmily/spring.factories +++ b/hmily-spring-boot-starter/hmily-spring-boot-starter-tars/src/main/resources/META-INF/spring.factories @@ -1,2 +1,2 @@ org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ -org.dromara.hmily.tars.spring.TarsHmilyConfiguration \ No newline at end of file +org.dromara.hmily.tars.startup.TarsHmilyConfiguration \ No newline at end of file