com.cyngn.vertx.opentsdb.service.client.OpenTsDbClient.java Source code

Java tutorial

Introduction

Here is the source code for com.cyngn.vertx.opentsdb.service.client.OpenTsDbClient.java

Source

/*
 * Copyright 2015 Cyanogen Inc.
 * 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.cyngn.vertx.opentsdb.service.client;

import com.cyngn.vertx.opentsdb.client.EventBusMessage;
import com.cyngn.vertx.opentsdb.service.OpenTsDbService;
import io.vertx.core.Vertx;
import io.vertx.core.buffer.Buffer;
import io.vertx.core.eventbus.EventBus;
import io.vertx.core.json.JsonObject;
import io.vertx.core.logging.Logger;
import io.vertx.core.logging.LoggerFactory;
import io.vertx.core.net.NetClient;
import io.vertx.core.net.NetClientOptions;
import io.vertx.core.net.NetSocket;

import java.io.Closeable;
import java.nio.charset.StandardCharsets;
import java.util.function.Consumer;

/**
 * Client for communicating directly with OpenTsDb.
 *
 * @author truelove@cyngn.com (Jeremy Truelove) 4/18/15
 */
public class OpenTsDbClient implements MetricsSender, Closeable {

    private final String host;
    private final int port;
    private final Vertx vertx;
    private final NetClient netClient;
    private final EventBus bus;
    private Logger logger = LoggerFactory.getLogger(OpenTsDbClient.class);

    private NetSocket connection;

    private long initialBackOffMilli = 1000;
    private long lastTimeout = initialBackOffMilli;
    private long maxReconnectTime = 64000;

    private boolean connected;
    private int consecutiveDisconnects;
    private long bytesWrittenForPeriod;
    private int errorsReceived;

    public OpenTsDbClient(String host, int port, Vertx vertx, Consumer<Boolean> onInitialized) {
        NetClientOptions options = new NetClientOptions().setTcpKeepAlive(true);
        netClient = vertx.createNetClient(options);
        connected = false;
        consecutiveDisconnects = 0;
        bytesWrittenForPeriod = 0;
        bus = vertx.eventBus();

        this.vertx = vertx;

        this.host = host;
        this.port = port;

        netClient.connect(port, host, connectResult -> {
            onInitialized.accept(connectResult.succeeded());
            if (connectResult.succeeded()) {
                onConnect(connectResult.result());
            } // if we don't succeed initially we'll fail startup of the reporter
        });
    }

    private void initializeHandlers() {
        connection.closeHandler(this::onClose);
        connection.handler(this::onDataReceived);
        connection.endHandler(this::onReadStreamClosed);
        connection.exceptionHandler(this::onException);
    }

    private void onException(Throwable throwable) {
        logger.error(String.format("Got exception on socket %s, ex: ", connection.remoteAddress()), throwable);
        close();
        processReconnect();
    }

    private long processReconnect() {
        long reconnectIn = lastTimeout < maxReconnectTime ? lastTimeout * 2 : maxReconnectTime;
        lastTimeout = reconnectIn;

        // attempt reconnect after back off time
        vertx.setTimer(reconnectIn, timer -> reconnect());
        return reconnectIn;
    }

    private void onConnect(NetSocket connection) {
        logger.info(String.format("Connected to host: %s port: %d", host, port));
        this.connection = connection;
        lastTimeout = initialBackOffMilli;
        initializeHandlers();
        connected = true;
    }

    private void reconnect() {
        logger.info(String.format("Reconnecting to host: %s port: %d", host, port));
        netClient.connect(port, host, connectResult -> {
            if (connectResult.succeeded()) {
                onConnect(connectResult.result());
            } else {
                long reconnectIn = processReconnect();
                logger.info(String.format("Failed to connect to host: %s port: %d, will re-attempt in %d(ms)", host,
                        port, reconnectIn));
            }
        });
    }

    private void onReadStreamClosed(Void aVoid) {
        logger.warn("Read streamed closed");
        close();
        processReconnect();
    }

    /**
     * Reading data when you haven't sent a command in OpenTsDb means there are errors coming back from an agent
     *
     * @param buffer
     */
    private void onDataReceived(Buffer buffer) {
        String openTsDbError = buffer.toString(StandardCharsets.UTF_8.toString());
        logger.error("Got data from agent: " + openTsDbError + " this is not expected");
        // let the user know if they failed to write because the data is invalid
        bus.send(OpenTsDbService.ERROR_MESSAGE_ADDRESS,
                new JsonObject().put("error", EventBusMessage.INVALID_DATA).put("message", openTsDbError));
        errorsReceived++;
    }

    public boolean write(Buffer metricData) {
        if (connection.writeQueueFull()) {
            logger.error(String.format("Discarding %d bytes write buffer full", metricData.length()));
            return false;
        } else if (!connected) {
            logger.error(String.format("Discarding %d bytes no connection", metricData.length()));
            return false;
        }
        connection.write(metricData);

        if (logger.isDebugEnabled()) {
            logger.debug(metricData);
        }
        bytesWrittenForPeriod += metricData.length();
        return true;
    }

    private void onClose(Void aVoid) {
        logger.info(String.format("Closing socket: %s", connection.remoteAddress()));
        connected = false;
        consecutiveDisconnects++;
    }

    public boolean isConnected() {
        return connected;
    }

    public void dumpStats() {
        int tmpDisconnects = consecutiveDisconnects;
        consecutiveDisconnects = 0;
        long tmpBytes = bytesWrittenForPeriod;
        bytesWrittenForPeriod = 0;
        int tmpErrorsReceived = errorsReceived;
        errorsReceived = 0;

        logger.info(String.format("host: %s port: %d disconnects: %d bytesWritten: %d, errorsReceived: %d", host,
                port, tmpDisconnects, tmpBytes, tmpErrorsReceived));
    }

    public void close() {
        if (isConnected()) {
            connection.close();
        }
    }
}