Java tutorial
/* * 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(); } }