com.heliosapm.streams.opentsdb.ringbuffer.RingBufferService.java Source code

Java tutorial

Introduction

Here is the source code for com.heliosapm.streams.opentsdb.ringbuffer.RingBufferService.java

Source

/*
 * Copyright 2015 the original author or 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 com.heliosapm.streams.opentsdb.ringbuffer;

import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ThreadFactory;

import org.apache.kafka.clients.producer.ProducerRecord;
import org.apache.kafka.common.serialization.Serdes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.codahale.metrics.Counter;
import com.heliosapm.utils.buffer.BufferManager;
import com.heliosapm.streams.buffers.ByteBufSerde;
import com.heliosapm.streams.common.kafka.producer.KafkaProducerService;
import com.heliosapm.streams.common.metrics.SharedMetricsRegistry;
import com.heliosapm.streams.metrics.StreamedMetricValue;
import com.heliosapm.streams.metrics.ValueType;
import com.heliosapm.utils.config.ConfigurationHelper;
import com.heliosapm.utils.jmx.JMXManagedThreadFactory;
import com.lmax.disruptor.EventFactory;
import com.lmax.disruptor.EventHandler;
import com.lmax.disruptor.RingBuffer;
import com.lmax.disruptor.WaitStrategy;
import com.lmax.disruptor.dsl.Disruptor;
import com.lmax.disruptor.dsl.ProducerType;

import io.netty.buffer.ByteBuf;

/**
 * <p>Title: RingBufferService</p>
 * <p>Description: A shared ring buffer service to support OpenTSDB plugins</p> 
 * <p>Company: Helios Development Group LLC</p>
 * @author Whitehead (nwhitehead AT heliosdev DOT org)
 * <p><code>com.heliosapm.streams.opentsdb.ringbuffer.RingBufferService</code></p>
 */

public class RingBufferService implements EventFactory<ByteBuf>, EventHandler<ByteBuf> {
    /** The singleton instance */
    private static volatile RingBufferService instance = null;
    /** The singleton instance ctor lock */
    private static final Object lock = new Object();

    /** The maximum number of workers */
    public static final int MAX_CAP = 0x7fff; // max #workers - 1
    /** The config prop for the buffer size */
    public static final String RB_CONF_BUFFER_SIZE = "disruptor.buffer.size";
    /** The default buffer size */
    public static final int RB_DEFAULT_BUFFER_SIZE = 8192;

    /** The config prop for the shutdown timeout in seconds. -1 or less means halt. */
    public static final String RB_CONF_STOP_TIMEOUT = "disruptor.shutdown.timeout";
    /** The default shutdown timeout in seconds. -1 or less means halt */
    public static final int RB_DEFAULT_STOP_TIMEOUT = 5;

    /** The config prop for the buffer event factory initial size */
    public static final String RB_CONF_BUFF_INITIAL_SIZE = "event.buff.initial.size";
    /** The default buffer event factory initial size */
    public static final int RB_DEFAULT_BUFF_INITIAL_SIZE = 128;

    /** The config prop for the nameof the topic to send to */
    public static final String RB_CONF_TOPIC_NAME = "topic.endpoint";
    /** The default buffer event factory initial size */
    public static final String RB_DEFAULT_TOPIC_NAME = "bosun.tsdb.rtp.metrics";

    //  "bosun.tsdb.rtp.metrics"

    /** A counter to track the number of event handling errors */
    protected final Counter handleEventExceptions = SharedMetricsRegistry.getInstance()
            .counter("disruptor.handler.exceptions");

    /** The ring buffer thread factory */
    protected final ThreadFactory threadFactory = JMXManagedThreadFactory.newThreadFactory("RingBufferService",
            true);
    /** The ring buffer executor buffer size */
    protected final int bufferSize;

    /** The shutdown timeout for the disruptor in seconds */
    protected final int shutdownTimeout;

    /** The ring buffer dsl configurator */
    protected final Disruptor<ByteBuf> disruptor;
    /** The ring buffer */
    protected final RingBuffer<ByteBuf> ringBuffer;
    /** Instance logger */
    protected final Logger log = LoggerFactory.getLogger(getClass());
    /** The wait strategy */
    protected final WaitStrategy waitStrategy;

    /** The event buffer initial size */
    protected int eventBuffInitSize;

    /** The target topic */
    protected final String targetTopic;

    /** The kafka producer */
    protected final KafkaProducerService<String, ByteBuf> producer;

    /** The buffer factory */
    protected final BufferManager bufferManager = BufferManager.getInstance();

    /**
     * Acquires and returns the RingBufferService singleton
     * @param config The optional configuration
     * @return the RingBufferService singleton
     */
    public static RingBufferService getInstance(final Properties config) {
        if (instance == null) {
            synchronized (lock) {
                if (instance == null) {
                    instance = new RingBufferService(config);
                }
            }
        }
        return instance;
    }

    @SuppressWarnings("unchecked")
    private RingBufferService(final Properties config) {
        config.put(KafkaProducerService.CONFIG_PREFIX + "value.serializer",
                ByteBufSerde.ByteBufSerializer.class.getName());
        config.put(KafkaProducerService.CONFIG_PREFIX + "key.serializer", Serdes.String().serializer());
        producer = KafkaProducerService.getInstance(config);
        bufferSize = ConfigurationHelper.getIntSystemThenEnvProperty(RB_CONF_BUFFER_SIZE, RB_DEFAULT_BUFFER_SIZE,
                config);
        targetTopic = ConfigurationHelper.getSystemThenEnvProperty(RB_CONF_TOPIC_NAME, RB_DEFAULT_TOPIC_NAME,
                config);
        eventBuffInitSize = ConfigurationHelper.getIntSystemThenEnvProperty(RB_CONF_BUFF_INITIAL_SIZE,
                RB_DEFAULT_BUFF_INITIAL_SIZE, config);
        shutdownTimeout = ConfigurationHelper.getIntSystemThenEnvProperty(RB_CONF_STOP_TIMEOUT,
                RB_DEFAULT_STOP_TIMEOUT, config);
        waitStrategy = RBWaitStrategy.getConfiguredStrategy(config);
        disruptor = new Disruptor<ByteBuf>(this, bufferSize, threadFactory, ProducerType.MULTI, waitStrategy);
        disruptor.handleEventsWith(this); // FIXME: need to able to supply several handlers
        disruptor.start();
        ringBuffer = disruptor.getRingBuffer();
        log.info("<<<<< RawRingBufferDispatcher Started.");
    }

    /**
     * {@inheritDoc}
     * @see com.lmax.disruptor.EventFactory#newInstance()
     */
    @Override
    public ByteBuf newInstance() {
        return bufferManager.buffer(eventBuffInitSize); // FIXME: config 
    }

    /**
     * Sends a metric to the configured end point topic
     * @param metric The metric name
     * @param timestamp The metric timestamp
     * @param value The metric value
     * @param tags The metric tags
     */
    public void publishDataPoint(final String metric, final long timestamp, final double value,
            final Map<String, String> tags) {
        final long seq = ringBuffer.next();
        StreamedMetricValue.write(ringBuffer.get(seq), ValueType.STRAIGHTTHROUGH, metric, timestamp, value, tags);
        ringBuffer.publish(seq);
    }

    /**
     * Sends a metric to the configured end point topic
     * @param metric The metric name
     * @param timestamp The metric timestamp
     * @param value The metric value
     * @param tags The metric tags
     */
    public void publishDataPoint(final String metric, final long timestamp, final long value,
            final Map<String, String> tags) {
        final long seq = ringBuffer.next();
        StreamedMetricValue.write(ringBuffer.get(seq), ValueType.STRAIGHTTHROUGH, metric, timestamp, value, tags);
        ringBuffer.publish(seq);
    }

    /**
     * {@inheritDoc}
     * @see com.lmax.disruptor.EventHandler#onEvent(java.lang.Object, long, boolean)
     */
    @Override
    public void onEvent(final ByteBuf event, final long sequence, final boolean endOfBatch) throws Exception {
        try {
            producer.send(new ProducerRecord<String, ByteBuf>(targetTopic, null,
                    StreamedMetricValue.timestamp(event), StreamedMetricValue.metricName(event), event));
        } catch (Exception x) {
            handleEventExceptions.inc();
            log.error("Failed to handle event", x);
        } finally {
            if (endOfBatch)
                producer.flush();
            event.clear();
        }
    }

    /**
     * Stops the ring buffer service
     */
    public void shutdown() {
        disruptor.shutdown();
    }
}