com.signalfx.signalflow.WebSocketTransport Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of signalfx-codahale Show documentation
Show all versions of signalfx-codahale Show documentation
Dropwizard Codahale metrics plugin for signalfx
The newest version!
/*
* Copyright (C) 2016-2018 SignalFx, Inc. All rights reserved.
*/
package com.signalfx.signalflow;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URI;
import java.nio.ByteBuffer;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.zip.GZIPInputStream;
import com.signalfx.shaded.apache.commons.io.IOUtils;
import com.signalfx.shaded.apache.http.client.utils.URIBuilder;
import com.signalfx.shaded.jetty.util.ssl.SslContextFactory;
import com.signalfx.shaded.jetty.websocket.api.Session;
import com.signalfx.shaded.jetty.websocket.api.WebSocketAdapter;
import com.signalfx.shaded.jetty.websocket.client.WebSocketClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.signalfx.shaded.fasterxml.jackson.core.JsonProcessingException;
import com.signalfx.shaded.fasterxml.jackson.core.type.TypeReference;
import com.signalfx.shaded.fasterxml.jackson.databind.DeserializationFeature;
import com.signalfx.shaded.fasterxml.jackson.databind.ObjectMapper;
import com.signalfx.shaded.google.common.io.BaseEncoding;
import com.signalfx.shaded.google.common.util.concurrent.Uninterruptibles;
import com.signalfx.endpoint.SignalFxEndpoint;
import com.signalfx.signalflow.ChannelMessage.Type;
import com.signalfx.signalflow.StreamMessage.Kind;
/**
* WebSocket based transport.
*
* Uses the SignalFlow WebSocket connection endpoint to interact with SignalFx's SignalFlow API.
* Multiple computation streams can be multiplexed through a single, pre-opened WebSocket
* connection. It also utilizes a more efficient binary encoding for data so it requires less
* bandwidth and has overall less latency.
*
* @author dgriff
*/
public class WebSocketTransport implements SignalFlowTransport {
protected static final Logger log = LoggerFactory.getLogger(WebSocketTransport.class);
public static final int DEFAULT_TIMEOUT = 1; // 1 second
protected final String token;
protected final SignalFxEndpoint endpoint;
protected final String path;
protected final int timeout;
protected final boolean compress;
protected WebSocketClient webSocketClient;
protected TransportConnection transportConnection;
protected WebSocketTransport(String token, SignalFxEndpoint endpoint, int apiVersion,
int timeout, boolean compress, int maxBinaryMessageSize) {
this.token = token;
this.endpoint = endpoint;
this.path = "/v" + apiVersion + "/signalflow/connect";
this.timeout = timeout;
this.compress = compress;
try {
this.transportConnection = new TransportConnection(token);
URI uri = new URIBuilder(String.format("%s://%s:%s%s", endpoint.getScheme(),
endpoint.getHostname(), endpoint.getPort(), path)).build();
this.webSocketClient = new WebSocketClient(new SslContextFactory());
if (maxBinaryMessageSize > 0) {
this.webSocketClient.getPolicy().setMaxBinaryMessageSize(maxBinaryMessageSize);
}
if (timeout > 0) {
this.webSocketClient.setConnectTimeout(TimeUnit.SECONDS.toMillis(timeout));
}
this.webSocketClient.start();
this.webSocketClient.connect(this.transportConnection, uri);
this.transportConnection.awaitConnected(timeout, TimeUnit.SECONDS);
} catch (Exception ex) {
if (this.webSocketClient != null) {
try {
this.webSocketClient.stop();
} catch (Exception e) {
log.warn("error closing websocket client", e);
}
}
throw new SignalFlowException("failed to construct websocket transport", ex);
}
}
@Override
public Channel attach(String handle, Map parameters) {
log.debug("attach: [ {} ] with parameters: {}", handle, parameters);
Channel channel = new TransportChannel(transportConnection);
Map request = new HashMap(parameters);
request.put("type", "attach");
request.put("handle", handle);
request.put("compress", Boolean.toString(compress));
transportConnection.sendMessage(channel, request);
return channel;
}
@Override
public Channel execute(String program, Map parameters) {
log.debug("execute: [ {} ] with parameters: {}", program, parameters);
Channel channel = new TransportChannel(transportConnection);
HashMap request = new HashMap(parameters);
request.put("type", "execute");
request.put("program", program);
request.put("compress", Boolean.toString(compress));
transportConnection.sendMessage(channel, request);
return channel;
}
@Override
public Channel preflight(String program, Map parameters) {
log.debug("preflight: [ {} ] with parameters: {}", program, parameters);
Channel channel = new TransportChannel(transportConnection);
HashMap request = new HashMap(parameters);
request.put("type", "preflight");
request.put("program", program);
transportConnection.sendMessage(channel, parameters);
return channel;
}
@Override
public void start(String program, Map parameters) {
log.debug("start: [ {} ] with parameters: {}", program, parameters);
HashMap request = new HashMap(parameters);
request.put("type", "start");
request.put("program", program);
transportConnection.sendMessage(request);
}
@Override
public void stop(String handle, Map parameters) {
log.debug("stop: [ {} ] with parameters: {}", handle, parameters);
HashMap request = new HashMap(parameters);
request.put("type", "stop");
request.put("handle", handle);
transportConnection.sendMessage(request);
}
@Override
public void close(int code, String reason) {
if (transportConnection.getSession() != null && transportConnection.getSession().isOpen()) {
transportConnection.close(code, reason);
try {
webSocketClient.stop();
} catch (Exception e) {
log.warn("error while close underlying websocket client", e);
}
log.debug("transport closed");
}
}
@Override
public void keepalive(String handle) {
log.debug("keepalive: [ {} ]", handle);
HashMap request = new HashMap();
request.put("type", "keepalive");
request.put("handle", handle);
transportConnection.sendMessage(request);
}
/**
* Builder of WebSocket Transport Instance
*/
public static class TransportBuilder {
private String token;
private String protocol = "wss";
private String host = DEFAULT_HOST;
private int port = 443;
private int timeout = DEFAULT_TIMEOUT;
private int version = 2;
private boolean compress = true;
private int maxBinaryMessageSize = -1;
public TransportBuilder(String token) {
this.token = token;
}
public TransportBuilder setProtocol(String protocol) {
this.protocol = protocol;
return this;
}
public TransportBuilder setHost(String host) {
this.host = host;
return this;
}
public TransportBuilder setPort(int port) {
this.port = port;
return this;
}
public TransportBuilder setTimeout(int timeout) {
this.timeout = timeout;
return this;
}
public TransportBuilder setAPIVersion(int version) {
this.version = version;
return this;
}
public TransportBuilder useCompression(boolean compress) {
this.compress = compress;
return this;
}
public TransportBuilder setMaxBinaryMessageSize(int size) {
this.maxBinaryMessageSize = size;
return this;
}
public WebSocketTransport build() {
SignalFxEndpoint endpoint = new SignalFxEndpoint(this.protocol, this.host, this.port);
WebSocketTransport transport = new WebSocketTransport(this.token, endpoint,
this.version, this.timeout, this.compress, this.maxBinaryMessageSize);
return transport;
}
}
/**
* Special type of StreamMessage for conveying websocket/connection errors to channels
*/
protected static class SignalFlowExceptionStreamMessage extends StreamMessage {
protected SignalFlowException exception;
public SignalFlowExceptionStreamMessage(final SignalFlowException exception) {
super("error", null, exception.getMessage());
this.exception = exception;
}
public SignalFlowException getException() {
return this.exception;
}
}
/**
* WebSocket Transport Connection
*/
protected static class TransportConnection extends WebSocketAdapter {
private static final Logger log = LoggerFactory.getLogger(TransportConnection.class);
private static final Charset ASCII = Charset.forName("US-ASCII");
private static final Charset UTF_8 = Charset.forName("UTF-8");
private static final BaseEncoding base64Encoder = BaseEncoding.base64Url().omitPadding();
private static final TypeReference
© 2015 - 2024 Weber Informatics LLC | Privacy Policy