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

org.apache.kafka.clients.consumer.internals.Fetcher Maven / Gradle / Ivy

There is a newer version: 3.9.0
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements. See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.kafka.clients.consumer.internals;

import org.apache.kafka.clients.ClientRequest;
import org.apache.kafka.clients.ApiVersions;
import org.apache.kafka.clients.ClientResponse;
import org.apache.kafka.clients.FetchSessionHandler;
import org.apache.kafka.clients.NetworkClient;
import org.apache.kafka.common.Node;
import org.apache.kafka.common.TopicPartition;
import org.apache.kafka.common.internals.IdempotentCloser;
import org.apache.kafka.common.requests.FetchRequest;
import org.apache.kafka.common.utils.LogContext;
import org.apache.kafka.common.utils.Time;
import org.apache.kafka.common.utils.Timer;
import org.slf4j.Logger;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

/**
 * This class manages the fetching process with the brokers.
 * 

* Thread-safety: * Requests and responses of Fetcher may be processed by different threads since heartbeat * thread may process responses. Other operations are single-threaded and invoked only from * the thread polling the consumer. *

    *
  • If a response handler accesses any shared state of the Fetcher (e.g. FetchSessionHandler), * all access to that state must be synchronized on the Fetcher instance.
  • *
  • If a response handler accesses any shared state of the coordinator (e.g. SubscriptionState), * it is assumed that all access to that state is synchronized on the coordinator instance by * the caller.
  • *
  • At most one request is pending for each node at any time. Nodes with pending requests are * tracked and updated after processing the response. This ensures that any state (e.g. epoch) * updated while processing responses on one thread are visible while creating the subsequent request * on a different thread.
  • *
*/ public class Fetcher extends AbstractFetch { private final Logger log; private final ConsumerNetworkClient client; private final FetchCollector fetchCollector; public Fetcher(LogContext logContext, ConsumerNetworkClient client, ConsumerMetadata metadata, SubscriptionState subscriptions, FetchConfig fetchConfig, Deserializers deserializers, FetchMetricsManager metricsManager, Time time, ApiVersions apiVersions) { super(logContext, metadata, subscriptions, fetchConfig, new FetchBuffer(logContext), metricsManager, time, apiVersions); this.log = logContext.logger(Fetcher.class); this.client = client; this.fetchCollector = new FetchCollector<>(logContext, metadata, subscriptions, fetchConfig, deserializers, metricsManager, time); } @Override protected boolean isUnavailable(Node node) { return client.isUnavailable(node); } @Override protected void maybeThrowAuthFailure(Node node) { client.maybeThrowAuthFailure(node); } public void clearBufferedDataForUnassignedPartitions(Collection assignedPartitions) { fetchBuffer.retainAll(new HashSet<>(assignedPartitions)); } /** * Set up a fetch request for any node that we have assigned partitions for which doesn't already have * an in-flight fetch or pending fetch data. * @return number of fetches sent */ public synchronized int sendFetches() { final Map fetchRequests = prepareFetchRequests(); sendFetchesInternal( fetchRequests, (fetchTarget, data, clientResponse) -> { synchronized (Fetcher.this) { handleFetchSuccess(fetchTarget, data, clientResponse); } }, (fetchTarget, data, error) -> { synchronized (Fetcher.this) { handleFetchFailure(fetchTarget, data, error); } }); return fetchRequests.size(); } protected void maybeCloseFetchSessions(final Timer timer) { final List> requestFutures = sendFetchesInternal( prepareCloseFetchSessionRequests(), this::handleCloseFetchSessionSuccess, this::handleCloseFetchSessionFailure ); // Poll to ensure that request has been written to the socket. Wait until either the timer has expired or until // all requests have received a response. while (timer.notExpired() && !requestFutures.stream().allMatch(RequestFuture::isDone)) { client.poll(timer, null, true); timer.update(); } if (!requestFutures.stream().allMatch(RequestFuture::isDone)) { // we ran out of time before completing all futures. It is ok since we don't want to block the shutdown // here. log.debug("All requests couldn't be sent in the specific timeout period {}ms. " + "This may result in unnecessary fetch sessions at the broker. Consider increasing the timeout passed for " + "KafkaConsumer.close(Duration timeout)", timer.timeoutMs()); } } public Fetch collectFetch() { return fetchCollector.collectFetch(fetchBuffer); } /** * This method is called by {@link #close(Timer)} which is guarded by the {@link IdempotentCloser}) such as to only * be executed once the first time that any of the {@link #close()} methods are called. Subclasses can override * this method without the need for extra synchronization at the instance level. * *

* * Note: this method is synchronized to reinstitute the 3.5 behavior: * *

* Shared states (e.g. sessionHandlers) could be accessed by multiple threads (such as heartbeat thread), hence, * it is necessary to acquire a lock on the fetcher instance before modifying the states. *
* * @param timer Timer to enforce time limit */ // Visible for testing protected synchronized void closeInternal(Timer timer) { // we do not need to re-enable wake-ups since we are closing already client.disableWakeups(); maybeCloseFetchSessions(timer); super.closeInternal(timer); } /** * Creates the {@link FetchRequest.Builder fetch request}, * {@link NetworkClient#send(ClientRequest, long) enqueues/sends it, and adds the {@link RequestFuture callback} * for the response. * * @param fetchRequests {@link Map} of {@link Node nodes} to their * {@link FetchSessionHandler.FetchRequestData request data} * @param successHandler {@link ResponseHandler Handler for successful responses} * @param errorHandler {@link ResponseHandler Handler for failure responses} * @return List of {@link RequestFuture callbacks} */ private List> sendFetchesInternal(Map fetchRequests, ResponseHandler successHandler, ResponseHandler errorHandler) { final List> requestFutures = new ArrayList<>(); for (Map.Entry entry : fetchRequests.entrySet()) { final Node fetchTarget = entry.getKey(); final FetchSessionHandler.FetchRequestData data = entry.getValue(); final FetchRequest.Builder request = createFetchRequest(fetchTarget, data); final RequestFuture responseFuture = client.send(fetchTarget, request); responseFuture.addListener(new RequestFutureListener() { @Override public void onSuccess(ClientResponse resp) { successHandler.handle(fetchTarget, data, resp); } @Override public void onFailure(RuntimeException e) { errorHandler.handle(fetchTarget, data, e); } }); requestFutures.add(responseFuture); } return requestFutures; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy