com.clickzetta.platform.connection.ChannelManagerImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of clickzetta-java Show documentation
Show all versions of clickzetta-java Show documentation
The java SDK for clickzetta's Lakehouse
package com.clickzetta.platform.connection;
import com.clickzetta.platform.client.RequestStreamObserver;
import com.clickzetta.platform.util.Pair;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.grpc.ConnectivityState;
import io.grpc.ManagedChannel;
import io.grpc.ManagedChannelBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import scala.Tuple2;
import java.io.IOException;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class ChannelManagerImpl extends AbstractReconnectSupport implements ChannelManager {
private static final Logger LOG = LoggerFactory.getLogger(ChannelManagerImpl.class);
private final Object lock;
private final List> streamObservers;
private final List> hostPorts;
private final AtomicInteger index;
private final Queue cleanerQueue;
private ScheduledExecutorService cleanerService;
protected ChannelManagerImpl() {
this.lock = new Object();
this.streamObservers = new ArrayList<>();
this.hostPorts = new ArrayList<>();
this.cleanerQueue = new LinkedBlockingQueue<>();
this.index = new AtomicInteger(0);
}
@Override
public ChannelData getChannel() throws IOException {
// wait until no reconnect.
waitOnNoInReconnect();
synchronized (lock) {
int rpcIndex = index.getAndUpdate(operand -> operand == streamObservers.size() - 1 ? 0 : operand + 1);
Pair hostPort = hostPorts.get(rpcIndex);
RequestStreamObserver observer = null;
if (rpcIndex < streamObservers.size()) {
observer = streamObservers.get(rpcIndex);
observer.retain();
}
return ChannelData.of(rpcIndex, hostPort, observer);
}
}
@Override
public boolean buildChannelInternal(boolean multiEnable, String host, Integer port, InitCallback callback) {
synchronized (lock) {
if (multiEnable || !this.hostPorts.contains(new Pair<>(host, port))) {
ManagedChannel channel = null;
if (port != null && port != -1) {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext()
.enableRetry()
.keepAliveTime(600, TimeUnit.SECONDS)
.keepAliveTimeout(180, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build();
} else {
channel = ManagedChannelBuilder.forTarget(host).usePlaintext()
.enableRetry()
.keepAliveTime(600, TimeUnit.SECONDS)
.keepAliveTimeout(180, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build();
}
this.hostPorts.add(new Pair<>(host, port));
if (callback != null) {
RequestStreamObserver observer = callback.call(channel);
if (observer != null) {
this.streamObservers.add(observer);
}
}
LOG.info("start to buildChannelInternal with {}:{}", host, port);
return true;
}
}
return false;
}
@Override
public void rebuildChannels(boolean multiEnable, List> hostPorts, InitCallback callback) {
Preconditions.checkArgument(hostPorts != null && hostPorts.size() > 0,
"rebuildChannels need hostPorts is not empty.");
synchronized (lock) {
if (cleanerService == null) {
cleanerService = Executors.newScheduledThreadPool(1, new ThreadFactoryBuilder()
.setNameFormat("cleanerService-%d")
.setDaemon(true).build());
cleanerService.scheduleAtFixedRate(() -> {
if (!cleanerQueue.isEmpty()) {
List> remain = new ArrayList<>();
while (!cleanerQueue.isEmpty()) {
ExpireCleaner cleaner = cleanerQueue.poll();
remain.addAll(cleaner.call());
}
if (!remain.isEmpty()) {
cleanerQueue.offer(new ExpireCleaner(remain));
}
}
}, 30 * 1000, 60 * 1000, TimeUnit.MILLISECONDS);
}
this.cleanerQueue.offer(new ExpireCleaner(this.streamObservers));
this.streamObservers.clear();
this.hostPorts.clear();
this.index.set(0);
for (Tuple2 hostPort : hostPorts) {
String host = hostPort._1;
Integer port = hostPort._2;
if (multiEnable || !this.hostPorts.contains(new Pair<>(host, port))) {
ManagedChannel channel = null;
if (port != null && port != -1) {
channel = ManagedChannelBuilder.forAddress(host, port).usePlaintext()
.enableRetry()
.keepAliveTime(600, TimeUnit.SECONDS)
.keepAliveTimeout(180, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build();
} else {
channel = ManagedChannelBuilder.forTarget(host).usePlaintext()
.enableRetry()
.keepAliveTime(600, TimeUnit.SECONDS)
.keepAliveTimeout(180, TimeUnit.SECONDS)
.keepAliveWithoutCalls(true)
.build();
}
this.hostPorts.add(new Pair<>(host, port));
if (callback != null) {
RequestStreamObserver observer = callback.call(channel);
if (observer != null) {
this.streamObservers.add(observer);
}
}
LOG.info("start to buildChannelInternal with {}:{}", host, port);
}
}
}
}
private final Set activeStateSet = new HashSet() {{
add(ConnectivityState.CONNECTING);
add(ConnectivityState.READY);
}};
@Override
public ChannelData validChannelActiveWithMaxRetry(ChannelData channelData, int maxRetry,
InitCallback callback) throws IOException {
// touch channel if active.
AtomicReference throwable = new AtomicReference<>();
ManagedChannel channel = channelData.streamObserver.getChannel();
ConnectivityState connectState = channel.getState(false);
while (!activeStateSet.contains(connectState) && maxRetry > 0) {
LOG.warn("get uncaught channel connectivity state {}.", connectState);
synchronized (lock) {
if (!activeStateSet.contains(connectState = channel.getState(true))) {
channel.notifyWhenStateChanged(connectState, () -> {
// lock for async callback.
synchronized (lock) {
try {
RequestStreamObserver observer = callback.call(channel);
if (observer != null && channelData.rpcIndex < this.streamObservers.size()) {
this.streamObservers.get(channelData.rpcIndex).replaceWithNew(observer);
}
} catch (Throwable t) {
throwable.set(t);
} finally {
lock.notify();
}
}
});
try {
lock.wait(5 * 1000);
} catch (InterruptedException e) {
throw new IOException(e);
}
if (throwable.get() != null) {
throw new IOException(throwable.get());
}
maxRetry--;
connectState = channel.getState(false);
}
}
}
if (!activeStateSet.contains(connectState)) {
throw new IOException(String.format("valid channel max retry times with connectivity state %s.", connectState));
}
if (throwable.get() != null) {
throw new IOException(throwable.get());
}
return channelData;
}
private class ExpireCleaner {
private List> streamObservers = new ArrayList<>();
public ExpireCleaner(List> streamObservers) {
this.streamObservers.addAll(streamObservers);
}
public List> call() {
List> remain = new ArrayList<>();
streamObservers.forEach(observer -> {
if (observer.refCnt() <= 0) {
observer.onCancel();
LOG.info("RequestStreamObserver expire clean success with {}", observer);
} else {
remain.add(observer);
}
});
streamObservers.clear();
streamObservers = null;
return remain;
}
@Override
public String toString() {
return "ExpireCleaner{" +
"streamObservers=" + streamObservers +
'}';
}
}
@Override
public void close(long wait_time_ms) {
LOG.info("ChannelManager close called...");
super.close(wait_time_ms);
if (cleanerService != null) {
cleanerService.shutdown();
try {
boolean closed = cleanerService.awaitTermination(wait_time_ms, TimeUnit.MILLISECONDS);
if (!closed) {
cleanerService.shutdownNow();
}
} catch (Throwable t) {
// ignore
}
}
for (RequestStreamObserver streamObserver : streamObservers) {
streamObserver.onCompleted(wait_time_ms);
}
for (ExpireCleaner cleaner : cleanerQueue) {
cleaner.streamObservers.forEach(sob -> sob.onCancel(wait_time_ms));
}
this.index.set(0);
this.streamObservers.clear();
this.cleanerQueue.clear();
this.hostPorts.clear();
LOG.info("ChannelManager close success.");
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy