order
This paper mainly studies the latency fault tolerance of rocketmq
LatencyFaultTolerance
rocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/latency/LatencyFaultTolerance.java
public interface LatencyFaultTolerance<T> { void updateFaultItem(final T name, final long currentLatency, final long notAvailableDuration); boolean isAvailable(final T name); void remove(final T name); T pickOneAtLeast(); }
- The LatencyFaultTolerance interface defines the updateFaultItem, isAvailable, remove, pickOneAtLeast methods
LatencyFaultToleranceImpl
rocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java
public class LatencyFaultToleranceImpl implements LatencyFaultTolerance<String> { private final ConcurrentHashMap<String, FaultItem> faultItemTable = new ConcurrentHashMap<String, FaultItem>(16); private final ThreadLocalIndex whichItemWorst = new ThreadLocalIndex(); @Override public void updateFaultItem(final String name, final long currentLatency, final long notAvailableDuration) { FaultItem old = this.faultItemTable.get(name); if (null == old) { final FaultItem faultItem = new FaultItem(name); faultItem.setCurrentLatency(currentLatency); faultItem.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration); old = this.faultItemTable.putIfAbsent(name, faultItem); if (old != null) { old.setCurrentLatency(currentLatency); old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration); } } else { old.setCurrentLatency(currentLatency); old.setStartTimestamp(System.currentTimeMillis() + notAvailableDuration); } } @Override public boolean isAvailable(final String name) { final FaultItem faultItem = this.faultItemTable.get(name); if (faultItem != null) { return faultItem.isAvailable(); } return true; } @Override public void remove(final String name) { this.faultItemTable.remove(name); } @Override public String pickOneAtLeast() { final Enumeration<FaultItem> elements = this.faultItemTable.elements(); List<FaultItem> tmpList = new LinkedList<FaultItem>(); while (elements.hasMoreElements()) { final FaultItem faultItem = elements.nextElement(); tmpList.add(faultItem); } if (!tmpList.isEmpty()) { Collections.shuffle(tmpList); Collections.sort(tmpList); final int half = tmpList.size() / 2; if (half <= 0) { return tmpList.get(0).getName(); } else { final int i = this.whichItemWorst.getAndIncrement() % half; return tmpList.get(i).getName(); } } return null; } @Override public String toString() { return "LatencyFaultToleranceImpl{" + "faultItemTable=" + faultItemTable + ", whichItemWorst=" + whichItemWorst + '}'; } //...... }
- Latency fault tolerance impl implements the latency fault tolerance interface; it maintains a faultItemTable with the key of name and value of FaultItem; its updatefaulteitem method will update the currentLatency and notAvailableDuration of the corresponding name to the corresponding FaultItem, otherwise it will create
- The isAvailable method first obtains the faultItem from the faultItemTable. If it is not null, it returns faultItem.isAvailable(), if it is null, it returns true; if it is remove, it executes faultItemTable.remove(name)
- The pickOneAtLeast method first copies a list of faultitems of the faultItemTable. If the list is empty, null is returned. If it is not empty, shuffle and sort the tmpList, and then take half value (tmpList.size() / 2). If half is less than or equal to 0, tmpList.get(0).getName(), otherwise tmpList.get(i).getName(), where i is calculated by which itemworst. Getandincrement()% half
FaultItem
rocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/latency/LatencyFaultToleranceImpl.java
class FaultItem implements Comparable<FaultItem> { private final String name; private volatile long currentLatency; private volatile long startTimestamp; public FaultItem(final String name) { this.name = name; } @Override public int compareTo(final FaultItem other) { if (this.isAvailable() != other.isAvailable()) { if (this.isAvailable()) return -1; if (other.isAvailable()) return 1; } if (this.currentLatency < other.currentLatency) return -1; else if (this.currentLatency > other.currentLatency) { return 1; } if (this.startTimestamp < other.startTimestamp) return -1; else if (this.startTimestamp > other.startTimestamp) { return 1; } return 0; } public boolean isAvailable() { return (System.currentTimeMillis() - startTimestamp) >= 0; } @Override public int hashCode() { int result = getName() != null ? getName().hashCode() : 0; result = 31 * result + (int) (getCurrentLatency() ^ (getCurrentLatency() >>> 32)); result = 31 * result + (int) (getStartTimestamp() ^ (getStartTimestamp() >>> 32)); return result; } @Override public boolean equals(final Object o) { if (this == o) return true; if (!(o instanceof FaultItem)) return false; final FaultItem faultItem = (FaultItem) o; if (getCurrentLatency() != faultItem.getCurrentLatency()) return false; if (getStartTimestamp() != faultItem.getStartTimestamp()) return false; return getName() != null ? getName().equals(faultItem.getName()) : faultItem.getName() == null; } @Override public String toString() { return "FaultItem{" + "name='" + name + '\'' + ", currentLatency=" + currentLatency + ", startTimestamp=" + startTimestamp + '}'; } public String getName() { return name; } public long getCurrentLatency() { return currentLatency; } public void setCurrentLatency(final long currentLatency) { this.currentLatency = currentLatency; } public long getStartTimestamp() { return startTimestamp; } public void setStartTimestamp(final long startTimestamp) { this.startTimestamp = startTimestamp; } }
- FaultItem first defines the compatible < FaultItem > interface, which defines the name, currentLatency and startTimestamp attributes. The calculation formula of its isAvailable method is (system. Currenttimemillis() - startTimestamp) > = 0. Its compareTo method is sorted according to isAvailable(), currentLatency and startTimestamp in turn
ThreadLocalIndex
rocketmq-client-4.6.0-sources.jar!/org/apache/rocketmq/client/common/ThreadLocalIndex.java
public class ThreadLocalIndex { private final ThreadLocal<Integer> threadLocalIndex = new ThreadLocal<Integer>(); private final Random random = new Random(); public int getAndIncrement() { Integer index = this.threadLocalIndex.get(); if (null == index) { index = Math.abs(random.nextInt()); if (index < 0) index = 0; this.threadLocalIndex.set(index); } index = Math.abs(index + 1); if (index < 0) index = 0; this.threadLocalIndex.set(index); return index; } @Override public String toString() { return "ThreadLocalIndex{" + "threadLocalIndex=" + threadLocalIndex.get() + '}'; } }
- Threadlocalindex defines threadlocalindex and random properties. Its getAndIncrement method first obtains the index from threadLocalIndex.get(). If it is null, it is initialized with Math.abs(random.nextInt()). If the result is less than 0, it is reset to 0. Then it uses Math.abs(index + 1) to threadLocalIndex.set(index). If the index is less than 0, it is also reset to 0
Summary
- The latenciefaulttolerance interface defines the updateFaultItem, isAvailable, remove and pickOneAtLeast methods; the latenciefaulttolerance impl implements the latenciefaulttolerance interface; it maintains a faultItemTable whose key is name and value is FaultItem; its updateFaultItem method updates the currentLatency and notAvailableDuration of the corresponding name to the corresponding FaultItem, Create if not
- The isAvailable method first obtains the faultItem from the faultItemTable. If it is not null, it returns faultItem.isAvailable(), if it is null, it returns true; if it is remove, it executes faultItemTable.remove(name)
- The pickOneAtLeast method first copies a list of faultitems of the faultItemTable. If the list is empty, null is returned. If it is not empty, shuffle and sort the tmpList, and then take half value (tmpList.size() / 2). If half is less than or equal to 0, tmpList.get(0).getName(), otherwise tmpList.get(i).getName(), where i is calculated by which itemworst. Getandincrement()% half