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

com.aliyun.datahub.clientlibrary.consumer.ShardCoordinator Maven / Gradle / Ivy

package com.aliyun.datahub.clientlibrary.consumer;

import com.aliyun.datahub.client.DatahubClient;
import com.aliyun.datahub.client.exception.DatahubClientException;
import com.aliyun.datahub.client.exception.ResourceNotFoundException;
import com.aliyun.datahub.client.exception.SubscriptionOffsetResetException;
import com.aliyun.datahub.client.model.JoinGroupResult;
import com.aliyun.datahub.clientlibrary.common.ClientManager;
import com.aliyun.datahub.clientlibrary.common.ClientManagerFactory;
import com.aliyun.datahub.clientlibrary.common.Constants;
import com.aliyun.datahub.clientlibrary.config.ConsumerConfig;
import com.aliyun.datahub.clientlibrary.exception.ExceptionRetryer;
import com.aliyun.datahub.clientlibrary.models.Assignment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class ShardCoordinator {
    private static final Logger LOG = LoggerFactory.getLogger(ShardCoordinator.class);

    private String projectName;
    private String topicName;
    private String subId;
    private String consumerId;
    private long versionId;

    private ConsumerConfig config;
    private ClientManager clientManager;
    private DatahubClient client;

    private Heartbeat heartbeat;
    private ExecutorService executor;
    private Set currentAssignment = new HashSet<>();
    private final AtomicBoolean closed = new AtomicBoolean(false);

    ShardCoordinator(String projectName, String topicName, String subId, ConsumerConfig config) {
        this.config = config;
        this.projectName = projectName;
        this.topicName = topicName;
        this.subId = subId;

        this.clientManager = ClientManagerFactory.getClientManager(projectName, topicName, config.getDatahubConfig());
        this.client = clientManager.getClient();
        this.heartbeat = new Heartbeat(client, projectName, topicName, subId);
        this.executor = new ThreadPoolExecutor(1, 1,
                0L, TimeUnit.MILLISECONDS, new SynchronousQueue());
        init();
    }

    void syncGroup(List releaseShardList, List readEndShardList) {
        if (!heartbeat.checkRunning()) {
            return;
        }

        if (releaseShardList.isEmpty() && readEndShardList.isEmpty()) {
            return;
        }

        heartbeat.updateReadEndShardList(readEndShardList);

        try {
            client.syncGroup(projectName, topicName, subId, consumerId, versionId, releaseShardList, readEndShardList);
            LOG.info("Sync group success, Project: {}, Topic: {}, SubId: {}, ConsumerId: {}, ReleaseShardList: {}, ReadEndShardList: {}",
                    projectName, topicName, subId, consumerId, releaseShardList.toString(), readEndShardList.toString());
        } catch (ResourceNotFoundException e) {
            LOG.warn("Stop heartbeat and rejoin group, Project: {}, Topic: {}, SubId: {}, ConsumerId: {}, Exception: {}",
                    projectName, topicName, subId, consumerId, e.getMessage());
            heartbeat.stop();
        } catch (SubscriptionOffsetResetException e) {
            LOG.warn("Sync group failed, Project: {}, Topic: {}, SubId: {}, ConsumerId: {}, Exception: {}",
                    projectName, topicName, subId, consumerId, e.getMessage());
            throw e;
        } catch (DatahubClientException e) {
            LOG.warn("Sync group failed, Project: {}, Topic: {}, SubId: {}, ConsumerId: {}, Exception: {}",
                    projectName, topicName, subId, consumerId, e.getMessage());
        }
    }

    void rejoinIfNeeded() {
        if (heartbeat.checkRunning()) {
            return;
        }

        joinGroup();
    }

    void close() {
        if (closed.compareAndSet(false, true)) {
            try {
                client.leaveGroup(projectName, topicName, subId, consumerId, versionId);
            } catch (Throwable ignored) {
            }
            if (heartbeat != null) {
                heartbeat.close();
            }
            if (executor != null) {
                executor.shutdownNow();
            }
            if (clientManager != null) {
                clientManager.close();
            }
        }
    }

    Assignment getNewAssignment() {
        Set newAssignment = heartbeat.getShards();
        if (newAssignment == currentAssignment) {
            // is the same object
            return Assignment.emptyAssignment;
        }

        Assignment result = new Assignment();

        // find release shard
        for (String shardId : currentAssignment) {
            if (!newAssignment.contains(shardId)) {
                result.getReleaseShardList().add(shardId);
            }
        }

        // find new shard
        for (String shardId : newAssignment) {
            if (!currentAssignment.contains(shardId)) {
                result.getNewShardList().add(shardId);
            }
        }

        currentAssignment = newAssignment;
        return result;
    }

    private void joinGroup() {
        new ExceptionRetryer() {
            @Override
            protected Void func() {
                JoinGroupResult joinGroupResult = client.joinGroup(projectName, topicName, subId, config.getSessionTimeoutMs());
                consumerId = joinGroupResult.getConsumerId();
                versionId = joinGroupResult.getVersionId();
                long heartbeatIntervalMs = (long) (joinGroupResult.getSessionTimeout() * Constants.HEARTBEAT_INTERVAL_SCALE);
                heartbeat.start(consumerId, versionId, heartbeatIntervalMs);
                LOG.info("Join group success, Project: {}, Topic: {}, SubId: {}, ConsumerId: {}, VersionId: {}, SessionTimeout: {}",
                        projectName, topicName, subId, consumerId, versionId, joinGroupResult.getSessionTimeout());
                return null;
            }

            @Override
            protected void failLog(String message) {
                LOG.error("Join group failed, Project: {}, Topic: {}, SubId: {}, Exception: {}",
                        projectName, topicName, subId, message);
            }
        }.run(Constants.RETRY_TIMES, Constants.RETRY_INTERVAL_MS);
    }

    private void init() {
        try {
            joinGroup();
        } catch (Exception e) {
            close();
            throw e;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy