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

io.nats.examples.jetstream.simple.QueueExample Maven / Gradle / Ivy

// Copyright 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.examples.jetstream.simple;

import io.nats.client.*;
import io.nats.client.api.ConsumerConfiguration;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static io.nats.examples.jetstream.NatsJsUtils.createOrReplaceStream;

/**
 * This example will demonstrate simplified next
 */
public class QueueExample {
    private static final String STREAM = "q-stream";
    private static final String SUBJECT = "q-subject";
    private static final String CONSUMER_NAME = "q-consumer";
    private static final int MESSAGE_COUNT = 10000;
    private static final int CONSUMER_COUNT = 6;

    public static String SERVER = "nats://localhost:4222";

    public static void main(String[] args) {
        Options options = Options.builder().server(SERVER).build();
        try (Connection nc = Nats.connect(options)) {
            JetStream js = nc.jetStream();
            createOrReplaceStream(nc.jetStreamManagement(), STREAM, SUBJECT);

            // get stream context and create a durable consumer
            // NOTE: It does not necessarily have to be a durable consumer,
            // it could be a named consumer with a long enough inactive threshold
            // to allow ConsumerContexts to be created from the name.
            StreamContext streamContext;
            try {
                streamContext = nc.getStreamContext(STREAM);
                streamContext.createOrUpdateConsumer(ConsumerConfiguration.builder().durable(CONSUMER_NAME).build());
            }
            catch (JetStreamApiException | IOException e) {
                // JetStreamApiException:
                //      the stream or consumer did not exist
                // IOException:
                //      likely a connection problem
                return;
            }

            // publish messages for the queue consumers to read
            for (int x = 1; x <= MESSAGE_COUNT; x++) {
                try {
                    js.publish(SUBJECT, ("message-" + x).getBytes());
                }
                catch (JetStreamApiException | IOException e) {
                    // JetStreamApiException:
                    //      The "publish" somehow was rejected by the server.
                    //      Unlikely w/o publish option expectations.
                    // IOException:
                    //      likely a connection problem
                    // InterruptedException:
                    //      developer interrupted this thread?
                    throw new RuntimeException(e);
                }
            }

            int readCount = MESSAGE_COUNT / CONSUMER_COUNT;
            System.out.println("Each of the " + MESSAGE_COUNT + " queue subscriptions will read about " + readCount + " messages.");

            CountDownLatch latch = new CountDownLatch(MESSAGE_COUNT);
            List holders = new ArrayList<>();
            for (int id = 1; id <= CONSUMER_COUNT; id++) {
                try {
                    if (id % 2 == 0) {
                        holders.add(new HandlerConsumerHolder(id, streamContext, latch));
                    }
                    else {
                        holders.add(new IterableConsumerHolder(id, streamContext, latch));
                    }
                }
                catch (JetStreamApiException e) {
                    // the stream or consumer did not exist
                    throw new RuntimeException(e);
                }
            }

            // wait for all messages to be received
            latch.await(20, TimeUnit.SECONDS);

            // report
            for (ConsumerHolder holder : holders) {
                holder.stop();
                holder.report();
            }

            System.out.println();

            // delete the stream since we are done with it.
            nc.jetStreamManagement().deleteStream(STREAM);
        }
        catch (IOException | InterruptedException | JetStreamApiException e) {
            // IOException:
            //      problem making the connection
            // InterruptedException:
            //      thread interruption in the body of the example
            // JetStreamApiException:
            //      the stream did not exist on delete
        }
    }

    static class HandlerConsumerHolder extends ConsumerHolder {
        MessageConsumer messageConsumer;

        public HandlerConsumerHolder(int id, StreamContext sc, CountDownLatch latch) throws JetStreamApiException, IOException {
            super(id, sc, latch);
            messageConsumer = consumerContext.consume(msg -> {
                thisReceived.incrementAndGet();
                latch.countDown();
                String data = new String(msg.getData(), StandardCharsets.US_ASCII);
                System.out.printf("Handler  # %d message # %d %s\n", id, thisReceived.get(), data);
                msg.ack();
            });
        }

        @Override
        public void stop() throws InterruptedException {
            messageConsumer.stop();
        }
    }

    static class IterableConsumerHolder extends ConsumerHolder {
        IterableConsumer iterableConsumer;
        Thread t;
        CountDownLatch finished = new CountDownLatch(1);

        public IterableConsumerHolder(int id, StreamContext sc, CountDownLatch latch) throws JetStreamApiException, IOException {
            super(id, sc, latch);
            iterableConsumer = consumerContext.iterate();
            t = new Thread(() -> {
                while (latch.getCount() > 0) {
                    try {
                        Message msg = iterableConsumer.nextMessage(1000);
                        if (msg != null) {
                            thisReceived.incrementAndGet();
                            latch.countDown();
                            String data = new String(msg.getData(), StandardCharsets.US_ASCII);
                            System.out.printf("Iterable # %d message # %d %s\n", id, thisReceived.get(), data);
                            msg.ack();
                        }
                    }
                    catch (Exception e) {
                    }
                }
                finished.countDown();
            });
            t.start();
        }

        @Override
        public void stop() throws InterruptedException {
            finished.await(2, TimeUnit.SECONDS); // ensures the next loop realized it could stop
        }
    }

    static abstract class ConsumerHolder {
        int id;
        ConsumerContext consumerContext;
        AtomicInteger thisReceived;
        CountDownLatch latch;

        public ConsumerHolder(int id, StreamContext sc, CountDownLatch latch) throws JetStreamApiException, IOException {
            this.id = id;
            thisReceived = new AtomicInteger();
            this.latch = latch;
            consumerContext = sc.getConsumerContext(CONSUMER_NAME);
        }

        public abstract void stop() throws InterruptedException;
        public void report() {
            System.out.printf("Instance # %d handled %d messages.\n", id, thisReceived.get());
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy