![JAR search and dependency download from the Maven repository](/logo.png)
io.nats.client.impl.NatsFetchConsumer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jnats Show documentation
Show all versions of jnats Show documentation
Client library for working with the NATS messaging system.
The newest version!
// Copyright 2020-2023 The NATS Authors
// Licensed 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 io.nats.client.impl;
import io.nats.client.*;
import io.nats.client.api.ConsumerConfiguration;
import io.nats.client.api.ConsumerInfo;
import java.io.IOException;
import static io.nats.client.BaseConsumeOptions.MIN_EXPIRES_MILLS;
class NatsFetchConsumer extends NatsMessageConsumerBase implements FetchConsumer, PullManagerObserver {
private final boolean isNoWait;
private final boolean isNoWaitNoExpires;
private final long maxWaitNanos;
private final String pullSubject;
private long startNanos;
NatsFetchConsumer(SimplifiedSubscriptionMaker subscriptionMaker,
ConsumerInfo cachedConsumerInfo,
FetchConsumeOptions fetchConsumeOptions) throws IOException, JetStreamApiException
{
super(cachedConsumerInfo);
isNoWait = fetchConsumeOptions.isNoWait();
long expiresInMillis = fetchConsumeOptions.getExpiresInMillis();
isNoWaitNoExpires = isNoWait && expiresInMillis == ConsumerConfiguration.LONG_UNSET;
long inactiveThreshold;
if (expiresInMillis == ConsumerConfiguration.LONG_UNSET) { // can be for noWait
maxWaitNanos = MIN_EXPIRES_MILLS * 1_000_000;
inactiveThreshold = MIN_EXPIRES_MILLS; // no need to do the 10% longer
}
else {
maxWaitNanos = expiresInMillis * 1_000_000;
inactiveThreshold = expiresInMillis * 110 / 100; // 10% longer than the wait
}
PullRequestOptions pro = PullRequestOptions.builder(fetchConsumeOptions.getMaxMessages())
.maxBytes(fetchConsumeOptions.getMaxBytes())
.expiresIn(expiresInMillis)
.idleHeartbeat(fetchConsumeOptions.getIdleHeartbeat())
.noWait(isNoWait)
.build();
initSub(subscriptionMaker.subscribe(null, null, null, inactiveThreshold));
pullSubject = sub._pull(pro, false, this);
startNanos = -1;
}
@Override
public void pendingUpdated() {}
@Override
public void heartbeatError() {
stopped.set(true);
finished.set(true);
}
@Override
public Message nextMessage() throws InterruptedException, JetStreamStatusCheckedException {
try {
if (finished.get()) {
return null;
}
// if the manager thinks it has received everything in the pull, it means
// that all the messages are already in the internal queue and there is
// no waiting necessary
if (pmm.noMorePending()) {
Message m = sub._nextUnmanagedNoWait(pullSubject);
if (m == null) {
// if there are no messages in the internal cache AND there are no more pending,
// they all have been read and we can go ahead and finish
finished.set(true);
lenientClose();
}
return m;
}
// by not starting the timer until the first call, it gives a little buffer around
// the next message to account for latency of incoming messages
if (startNanos == -1) {
startNanos = System.nanoTime();
}
long timeLeftMillis = (maxWaitNanos - (System.nanoTime() - startNanos)) / 1_000_000;
// if the timer has run out, don't allow waiting
// this might happen once, but it should already be noMorePending
if (timeLeftMillis < 1) {
Message m = sub._nextUnmanagedNoWait(pullSubject); // null means don't wait
if (m == null) {
// no message and no time left, go ahead and finish
finished.set(true);
lenientClose();
}
return m;
}
Message m = sub._nextUnmanaged(timeLeftMillis, pullSubject);
if (m == null && isNoWaitNoExpires) {
// no message and no wait, go ahead and finish
finished.set(true);
lenientClose();
}
return m;
}
catch (JetStreamStatusException e) {
throw new JetStreamStatusCheckedException(e);
}
catch (IllegalStateException i) {
// this happens if the consumer is stopped, since it is
// drained/unsubscribed, so don't pass it on if it's expected
return null;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy