mirror of
https://github.com/NationalSecurityAgency/timely.git
synced 2026-11-10 03:41:25 -10:00
Compare commits
67
Commits
0.0.4
...
TIMELY-163
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ccb5643d77 | ||
|
|
4d8966dedb | ||
|
|
bb1afd6cda | ||
|
|
a1656e0f1d | ||
|
|
ddc18b57aa | ||
|
|
49a4f6573b | ||
|
|
22b3a1e918 | ||
|
|
5daf1ef6f5 | ||
|
|
436fe806aa | ||
|
|
3d80b880d9 | ||
|
|
2fd1496e9a | ||
|
|
f8cc045b3e | ||
|
|
d481c629c2 | ||
|
|
24823c7fe6 | ||
|
|
8e6cc4e0b4 | ||
|
|
3a3d98ec81 | ||
|
|
7599a2ac60 | ||
|
|
3d33ba2e31 | ||
|
|
bbbb453818 | ||
|
|
54951c05e6 | ||
|
|
b63914e02c | ||
|
|
1f386db00a | ||
|
|
49222ac76a | ||
|
|
38f2c6cdfa | ||
|
|
3926e6f066 | ||
|
|
30800a0b5c | ||
|
|
92b23a4c51 | ||
|
|
8030ea59a5 | ||
|
|
a4c8158003 | ||
|
|
acba454ee3 | ||
|
|
30790608ca | ||
|
|
f2f3788f99 | ||
|
|
326b9dc8f1 | ||
|
|
3f3b605c63 | ||
|
|
5580a2fa66 | ||
|
|
1a54c3ada3 | ||
|
|
2080764e0d | ||
|
|
e7dade04ee | ||
|
|
7ed337483e | ||
|
|
b070bc3c47 | ||
|
|
51f9103741 | ||
|
|
a97fa83e2f | ||
|
|
0b5c3544db | ||
|
|
186b60756a | ||
|
|
133a6bf5cf | ||
|
|
b684d7eefe | ||
|
|
73f1603f1e | ||
|
|
9267e0c915 | ||
|
|
1ce49628e5 | ||
|
|
acad3af083 | ||
|
|
b4635f1a55 | ||
|
|
4c1ede04df | ||
|
|
2841b0a3e9 | ||
|
|
6efa13909f | ||
|
|
368c572c67 | ||
|
|
554baf6af7 | ||
|
|
1ef11799be | ||
|
|
16b664297d | ||
|
|
4277b63bc3 | ||
|
|
5a9ad3cb27 | ||
|
|
f990e89ab3 | ||
|
|
c01ac80f23 | ||
|
|
35e8f99d09 | ||
|
|
847144d236 | ||
|
|
b3bfbc1e1f | ||
|
|
8d86e3894a | ||
|
|
99b9181902 |
No files matched your search
@@ -6,4 +6,5 @@ target/
|
||||
*.iml
|
||||
logs/
|
||||
server/pki/
|
||||
client/src/main/python/setup.py
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
[![Apache License][li]][ll]
|
||||
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo] (http://accumulo.apache.org/) and [Grafana] (http://www.grafana.org). Documentation is located [here] (https://nationalsecurityagency.github.io/timely/).
|
||||
Timely is a time series database application that provides secure access to time series data. Timely is written in Java and designed to work with [Apache Accumulo](http://accumulo.apache.org/) and [Grafana](http://www.grafana.org). Documentation is located [here](https://nationalsecurityagency.github.io/timely/).
|
||||
|
||||
[li]: http://img.shields.io/badge/license-ASL-blue.svg
|
||||
[ll]: https://www.apache.org/licenses/LICENSE-2.0
|
||||
+1
-1
@@ -23,7 +23,7 @@ under the License.
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<flink.version>1.1.4</flink.version>
|
||||
|
||||
@@ -51,6 +51,7 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
|
||||
private final String[] metrics;
|
||||
private final SummarizationJobParameters jp;
|
||||
private final long window;
|
||||
private final AtomicInteger subscriptionsRemaining;
|
||||
|
||||
public SubscriptionSource(SummarizationJobParameters jp) {
|
||||
this.jp = jp;
|
||||
@@ -58,6 +59,7 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
|
||||
end = jp.getEndTime();
|
||||
metrics = jp.getMetrics();
|
||||
window = jp.getSummarizationInterval().toMilliseconds();
|
||||
subscriptionsRemaining = new AtomicInteger(metrics.length);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -102,7 +104,8 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
|
||||
|
||||
@Override
|
||||
public void onMessage(final String message) {
|
||||
LOG.info("Message received on Websocket session {}: {}", session.getId(), message);
|
||||
LOG.info("Message received on Websocket session {}, length: {}", session.getId(),
|
||||
message.length());
|
||||
try {
|
||||
// Deserialize in this thread
|
||||
final MetricResponses responses = JsonSerializer.getObjectMapper().readValue(message,
|
||||
@@ -113,9 +116,10 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
|
||||
if (response.isComplete()) {
|
||||
LOG.info("Received last message.");
|
||||
ctx.emitWatermark(new Watermark(Long.MAX_VALUE));
|
||||
subscriptionsRemaining.getAndDecrement();
|
||||
return;
|
||||
}
|
||||
LOG.trace("Sending metric: {}", response);
|
||||
LOG.trace("Received metric: {}", response);
|
||||
long time = response.getTimestamp();
|
||||
ctx.collectWithTimestamp(response, time);
|
||||
dateTimeAccumulator.add(formatter.format(new Date(response.getTimestamp())));
|
||||
@@ -178,7 +182,7 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
|
||||
LOG.info("Adding subscription for {}", m);
|
||||
client.addSubscription(m, null, start, end, 5000);
|
||||
}
|
||||
while (!client.isClosed()) {
|
||||
while (!client.isClosed() && subscriptionsRemaining.get() > 0) {
|
||||
try {
|
||||
Thread.sleep(5000);
|
||||
} catch (InterruptedException e) {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
export TRUSTSTORE_FILE=
|
||||
export TRUSTSTORE_TYPE="jks";
|
||||
export TRUSTSTORE_PASS=
|
||||
export KEYSTORE_FILE=
|
||||
export KEYSTORE_TYPE="pkcs12";
|
||||
export KEYSTORE_PASS=
|
||||
|
||||
export NUM_QUERY_THREADS=4
|
||||
export NUM_INGEST_THREADS=4
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
BASE_DIR=${THIS_DIR}/../..
|
||||
TMP_DIR="${BASE_DIR}/tmp"
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
|
||||
. ${THIS_DIR}/load-test-env.sh
|
||||
|
||||
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
|
||||
JVM_ARGS="-Xmx1G -Xms1G"
|
||||
|
||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*"
|
||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricPutLoadTest $*
|
||||
@@ -0,0 +1,21 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
BASE_DIR=${THIS_DIR}/../..
|
||||
TMP_DIR="${BASE_DIR}/tmp"
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
|
||||
. ${THIS_DIR}/load-test-env.sh
|
||||
|
||||
export CLASSPATH="${CONF_DIR}:${THIS_DIR}:${LIB_DIR}/*"
|
||||
JVM_ARGS="-Xmx1G -Xms1G"
|
||||
|
||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*"
|
||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.testing.MetricQueryLoadTest $*
|
||||
Executable
+11
@@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
fi
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
|
||||
sed -i -- "s/#.*#/#$1#/g" $THIS_DIR/load-test-data.txt
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash -vx
|
||||
|
||||
if [[ `uname` == "Darwin" ]]; then
|
||||
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
|
||||
TCNATIVE_SUFFIX="tcnative.jnilib"
|
||||
else
|
||||
THIS_SCRIPT=`readlink -f $0`
|
||||
TCNATIVE_SUFFIX="tcnative.so"
|
||||
fi
|
||||
|
||||
# netty tcnative file reference
|
||||
#netty-tcnative-1.1.33.Fork18-linux-x86_64-fedora.jar -> libnetty-tcnative.so
|
||||
#netty-tcnative-1.1.33.Fork18-linux-x86_64.jar -> libnetty-tcnative.so
|
||||
#netty-tcnative-1.1.33.Fork18-osx-x86_64.jar -> libnetty-tcnative.jnilib
|
||||
|
||||
THIS_DIR="${THIS_SCRIPT%/*}"
|
||||
NATIVE_DIR="${THIS_DIR}/META-INF/native"
|
||||
BASE_DIR=${THIS_DIR}/..
|
||||
TMP_DIR="${BASE_DIR}/tmp"
|
||||
CONF_DIR="${BASE_DIR}/conf"
|
||||
LIB_DIR="${BASE_DIR}/lib"
|
||||
NUM_SERVER_THREADS=4
|
||||
|
||||
if [[ -e ${TMP_DIR} ]]; then
|
||||
rm -rf ${TMP_DIR}
|
||||
fi
|
||||
mkdir ${TMP_DIR}
|
||||
|
||||
if [[ -e ${NATIVE_DIR} ]]; then
|
||||
rm -rf ${NATIVE_DIR}
|
||||
fi
|
||||
mkdir -p ${NATIVE_DIR}
|
||||
|
||||
pushd ${BASE_DIR}/bin
|
||||
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative*.jar META-INF/native/libnetty_${TCNATIVE_SUFFIX}
|
||||
popd
|
||||
|
||||
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
|
||||
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}"
|
||||
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
|
||||
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}/libnetty_${TCNATIVE_SUFFIX}"
|
||||
JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
|
||||
|
||||
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer"
|
||||
$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" monitorInterval="60">
|
||||
<Appenders>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="error" includeLocation="false">
|
||||
<AppenderRef ref="Console" level="error" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Configuration status="warn" monitorInterval="60">
|
||||
<Properties>
|
||||
<Property name="PID">????</Property>
|
||||
<Property name="LOG_PATTERN">%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%wEx</Property>
|
||||
</Properties>
|
||||
<Appenders>
|
||||
<RollingRandomAccessFile name="Rolling" fileName="/srv/logs/timely/timely-balancer.log"
|
||||
filePattern="/srv/logs/timely/$${date:yyyy-MM}/timely-balancer-%d{MM-dd-yyyy}-%i.log.gz">
|
||||
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||
<Policies>
|
||||
<SizeBasedTriggeringPolicy size="1024 MB" />
|
||||
</Policies>
|
||||
<DefaultRolloverStrategy max="10" />
|
||||
</RollingRandomAccessFile>
|
||||
<Console name="Console" target="SYSTEM_OUT">
|
||||
<PatternLayout pattern="${LOG_PATTERN}"/>
|
||||
</Console>
|
||||
</Appenders>
|
||||
<Loggers>
|
||||
<Root level="trace" includeLocation="false">
|
||||
<AppenderRef ref="Console" level="warn" />
|
||||
<AppenderRef ref="Rolling" level="trace" />
|
||||
</Root>
|
||||
</Loggers>
|
||||
</Configuration>
|
||||
@@ -0,0 +1,49 @@
|
||||
<beans:beans xmlns="http://www.springframework.org/schema/security"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:util="http://www.springframework.org/schema/util"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans
|
||||
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
|
||||
http://www.springframework.org/schema/security
|
||||
http://www.springframework.org/schema/security/spring-security.xsd
|
||||
http://www.springframework.org/schema/util
|
||||
http://www.springframework.org/schema/util/spring-util.xsd">
|
||||
|
||||
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
|
||||
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
|
||||
<beans:property name="users">
|
||||
<util:map key-type="java.lang.String">
|
||||
<beans:entry key="example.com">
|
||||
<util:list>
|
||||
<beans:value>D</beans:value>
|
||||
<beans:value>E</beans:value>
|
||||
<beans:value>F</beans:value>
|
||||
</util:list>
|
||||
</beans:entry>
|
||||
</util:map>
|
||||
</beans:property>
|
||||
</beans:bean>
|
||||
|
||||
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
|
||||
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
|
||||
</beans:bean>
|
||||
|
||||
<user-service id="usernamePasswordService">
|
||||
<user name="test" password="test1" authorities="A,B,C" />
|
||||
</user-service>
|
||||
|
||||
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
|
||||
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
|
||||
</beans:bean>
|
||||
|
||||
<authentication-manager alias="authenticationManager">
|
||||
<!-- X509 PreAuthenticated authorization provider -->
|
||||
<authentication-provider ref="x509AuthorizationProvider"/>
|
||||
<!-- Basic authorization provider, validates provided username and password -->
|
||||
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
|
||||
</authentication-manager>
|
||||
|
||||
</beans:beans>
|
||||
@@ -0,0 +1,91 @@
|
||||
timely:
|
||||
balancer:
|
||||
timelyHosts:
|
||||
-
|
||||
regex: ^[a-g].*
|
||||
host: localhost
|
||||
tcpPort: 4242
|
||||
httpPort: 4243
|
||||
wsPort: 4244
|
||||
udpPort: 4245
|
||||
-
|
||||
regex: ^[h-n].*
|
||||
host: localhost
|
||||
tcpPort: 4342
|
||||
httpPort: 4343
|
||||
wsPort: 4244
|
||||
udpPort: 4245
|
||||
-
|
||||
regex: ^[o-z].*
|
||||
host: localhost
|
||||
tcpPort: 4442
|
||||
httpPort: 4443
|
||||
wsPort: 4444
|
||||
udpPort: 4445
|
||||
tcpClientPool:
|
||||
minIdlePerKey: 0
|
||||
maxIdlePerKey: 8
|
||||
maxTotalPerKey: 8
|
||||
maxTotal: -1
|
||||
lifo: true
|
||||
fairness: false
|
||||
maxWaitMillis: -1
|
||||
minEvictableIdleTimeMillis: 1800000
|
||||
evictorShutdownTimeoutMillis: 10000
|
||||
softMinEvictableIdleTimeMillis: -1
|
||||
numTestsPerEvictionRun: 3
|
||||
timeBetweenEvictionRunsMillis: -1
|
||||
blockWhenExhausted: true
|
||||
jmxEnabled: true
|
||||
httpClientPool:
|
||||
minIdlePerKey: 0
|
||||
maxIdlePerKey: 8
|
||||
maxTotalPerKey: 8
|
||||
maxTotal: -1
|
||||
lifo: true
|
||||
fairness: false
|
||||
maxWaitMillis: -1
|
||||
minEvictableIdleTimeMillis: 1800000
|
||||
evictorShutdownTimeoutMillis: 10000
|
||||
softMinEvictableIdleTimeMillis: -1
|
||||
numTestsPerEvictionRun: 3
|
||||
timeBetweenEvictionRunsMillis: -1
|
||||
blockWhenExhausted: true
|
||||
jmxEnabled: true
|
||||
wsClientPool:
|
||||
minIdlePerKey: 0
|
||||
maxIdlePerKey: 8
|
||||
maxTotalPerKey: 8
|
||||
maxTotal: -1
|
||||
lifo: true
|
||||
fairness: false
|
||||
maxWaitMillis: -1
|
||||
minEvictableIdleTimeMillis: 1800000
|
||||
evictorShutdownTimeoutMillis: 10000
|
||||
softMinEvictableIdleTimeMillis: -1
|
||||
numTestsPerEvictionRun: 3
|
||||
timeBetweenEvictionRunsMillis: -1
|
||||
blockWhenExhausted: true
|
||||
jmxEnabled: true
|
||||
udpClientPool:
|
||||
minIdlePerKey: 0
|
||||
maxIdlePerKey: 8
|
||||
maxTotalPerKey: 8
|
||||
maxTotal: -1
|
||||
lifo: true
|
||||
fairness: false
|
||||
maxWaitMillis: -1
|
||||
minEvictableIdleTimeMillis: 1800000
|
||||
evictorShutdownTimeoutMillis: 10000
|
||||
softMinEvictableIdleTimeMillis: -1
|
||||
numTestsPerEvictionRun: 3
|
||||
timeBetweenEvictionRunsMillis: -1
|
||||
blockWhenExhausted: true
|
||||
jmxEnabled: true
|
||||
clientSsl:
|
||||
keyFile:
|
||||
keyType: pkcs12
|
||||
keyPassword:
|
||||
trustStoreFile:
|
||||
trustStoreType: jks
|
||||
trustStorePassword:
|
||||
@@ -0,0 +1,54 @@
|
||||
timely:
|
||||
metrics-table: timely.metrics
|
||||
meta-table: timely.meta
|
||||
metric-age-off-days:
|
||||
default: 7
|
||||
accumulo:
|
||||
instance-name: dummy
|
||||
zookeepers: localhost
|
||||
username: dummy
|
||||
password: dummy
|
||||
write:
|
||||
latency: 5s
|
||||
threads: 3
|
||||
bufferSize: 52428800
|
||||
scan:
|
||||
threads: 4
|
||||
max-downsample-memory: 10000000
|
||||
security:
|
||||
session-max-age: 86400
|
||||
allow-anonymous-access: true
|
||||
ssl:
|
||||
certificate-file:
|
||||
key-file:
|
||||
key-password:
|
||||
use-generated-keypair: false
|
||||
trust-store-file:
|
||||
use-openssl: true
|
||||
#use-ciphers:
|
||||
server:
|
||||
ip: 127.0.0.1
|
||||
tcp-port: 5242
|
||||
udp-port: 5245
|
||||
http:
|
||||
host: localhost
|
||||
ip: 127.0.0.1
|
||||
port: 5243
|
||||
redirect-path: /secure-me
|
||||
strict-transport-max-age: 608400
|
||||
cors:
|
||||
allow-any-origin: false
|
||||
allow-null-origin: false
|
||||
allow-credentials: true
|
||||
allowed-origins:
|
||||
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
|
||||
allowed-headers: content-type
|
||||
websocket:
|
||||
ip: 127.0.0.1
|
||||
port: 5244
|
||||
timeout: 60
|
||||
subscription-lag: 120
|
||||
scanner-batch-size: 10000
|
||||
flush-interval-seconds: 30
|
||||
scanner-read-ahead: 1
|
||||
subscription-batch-size: 5000
|
||||
@@ -0,0 +1,331 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<artifactId>timely-balancer</artifactId>
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-server</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-client</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<version>${parent.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-annotations</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-core</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.datatype</groupId>
|
||||
<artifactId>jackson-datatype-jdk8</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.ben-manes.caffeine</groupId>
|
||||
<artifactId>caffeine</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.flatbuffers</groupId>
|
||||
<artifactId>flatbuffers-java</artifactId>
|
||||
<scope>system</scope>
|
||||
<systemPath>${project.basedir}/../server/lib/flatbuffers-java-1.4.0.jar</systemPath>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-beanutils</groupId>
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>2.5.0</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.lmax</groupId>
|
||||
<artifactId>disruptor</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fi.iki.yak</groupId>
|
||||
<artifactId>compression-gorilla</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.lukehutch</groupId>
|
||||
<artifactId>fast-classpath-scanner</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-all</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>${version.netty-tcnative}</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-core</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-slf4j-impl</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-core</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.accumulo</groupId>
|
||||
<artifactId>accumulo-minicluster</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>jdk.tools</groupId>
|
||||
<artifactId>jdk.tools</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-client</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-minicluster</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.javassist</groupId>
|
||||
<artifactId>javassist</artifactId>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter</artifactId>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-logging</artifactId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-log4j2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-test-autoconfigure</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-config</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-web</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-websocket</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<extensions>
|
||||
<!-- Use os-maven-plugin to initialize the "os.detected" properties -->
|
||||
<extension>
|
||||
<groupId>kr.motd.maven</groupId>
|
||||
<artifactId>os-maven-plugin</artifactId>
|
||||
</extension>
|
||||
</extensions>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
<version>${version.findbugs-maven}</version>
|
||||
<configuration>
|
||||
<effort>Max</effort>
|
||||
<threshold>Medium</threshold>
|
||||
</configuration>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-assembly-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>assemble</id>
|
||||
<goals>
|
||||
<goal>single</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<descriptors>
|
||||
<descriptor>src/main/dist/assembly.xml</descriptor>
|
||||
</descriptors>
|
||||
<tarLongFileMode>posix</tarLongFileMode>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-dependency-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>copy-dependencies</id>
|
||||
<goals>
|
||||
<goal>copy-dependencies</goal>
|
||||
</goals>
|
||||
<phase>prepare-package</phase>
|
||||
<configuration>
|
||||
<excludeArtifactIds>junit</excludeArtifactIds>
|
||||
<outputDirectory>${project.build.directory}/lib</outputDirectory>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>unpack-native</id>
|
||||
<goals>
|
||||
<goal>unpack</goal>
|
||||
</goals>
|
||||
<phase>process-test-classes</phase>
|
||||
<configuration>
|
||||
<artifactItems>
|
||||
<artifactItem>
|
||||
<groupId>io.netty</groupId>
|
||||
<artifactId>netty-tcnative</artifactId>
|
||||
<version>${version.netty-tcnative}</version>
|
||||
<classifier>${os.detected.classifier}</classifier>
|
||||
<type>jar</type>
|
||||
<overWrite>false</overWrite>
|
||||
<outputDirectory>${project.build.directory}/native</outputDirectory>
|
||||
<includes>**/*.so</includes>
|
||||
</artifactItem>
|
||||
</artifactItems>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-failsafe-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>build-helper-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>add-source</id>
|
||||
<phase>generate-sources</phase>
|
||||
<goals>
|
||||
<goal>add-source</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sources>
|
||||
<source>src/main/generated-java</source>
|
||||
</sources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>findbugs-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-surefire-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.jacoco</groupId>
|
||||
<artifactId>jacoco-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>net.revelc.code</groupId>
|
||||
<artifactId>formatter-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</project>
|
||||
Vendored
+54
@@ -0,0 +1,54 @@
|
||||
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
|
||||
<id>dist</id>
|
||||
<formats>
|
||||
<format>tar.gz</format>
|
||||
</formats>
|
||||
<fileSets>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}</directory>
|
||||
<outputDirectory></outputDirectory>
|
||||
<includes>
|
||||
<include>README*</include>
|
||||
<include>LICENSE*</include>
|
||||
<include>NOTICE*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<includes>
|
||||
<include>timely*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.build.directory}/lib</directory>
|
||||
<outputDirectory>lib</outputDirectory>
|
||||
<includes>
|
||||
<include>*.jar</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/bin</directory>
|
||||
<outputDirectory>bin</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/conf</directory>
|
||||
<outputDirectory>conf</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
<fileSet>
|
||||
<directory>${project.basedir}/src/main/resources/webapp</directory>
|
||||
<outputDirectory>bin/webapp</outputDirectory>
|
||||
<includes>
|
||||
<include>*</include>
|
||||
</includes>
|
||||
</fileSet>
|
||||
</fileSets>
|
||||
</assembly>
|
||||
@@ -0,0 +1,141 @@
|
||||
package timely.balancer;
|
||||
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class ArrivalRate {
|
||||
|
||||
private long shortTermArrivals = 0l;
|
||||
private double shortTermRate = 0;
|
||||
private long shortTermLastReset = 0l;
|
||||
|
||||
private long mediumTermArrivals = 0l;
|
||||
private double mediumTermRate = 0;
|
||||
private long mediumTermLastReset = 0l;
|
||||
|
||||
private long longTermArrivals = 0l;
|
||||
private double longTermRate = 0;
|
||||
private long longTermLastReset = 0l;
|
||||
|
||||
private double rate = 0;
|
||||
|
||||
private boolean recalculateEveryUpdate = true;
|
||||
private long created = System.currentTimeMillis();
|
||||
|
||||
private Timer timer = new Timer("ArrivalRateTimer");
|
||||
|
||||
public ArrivalRate() {
|
||||
shortTermLastReset = mediumTermLastReset = longTermLastReset = System.currentTimeMillis();
|
||||
|
||||
// 5 minute
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
resetShort();
|
||||
}
|
||||
}, 300000, 300000);
|
||||
|
||||
// 5 minute
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
resetMedium();
|
||||
}
|
||||
}, 900000, 900000);
|
||||
|
||||
// 5 minute
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
resetLong();
|
||||
}
|
||||
}, 3600000, 3600000);
|
||||
}
|
||||
|
||||
synchronized private void resetShort() {
|
||||
long now = System.currentTimeMillis();
|
||||
shortTermRate = shortTermArrivals / (((double) (now - shortTermLastReset)) / 1000);
|
||||
shortTermArrivals = 0;
|
||||
shortTermLastReset = now;
|
||||
}
|
||||
|
||||
synchronized private void resetMedium() {
|
||||
long now = System.currentTimeMillis();
|
||||
mediumTermRate = mediumTermArrivals / (((double) (now - mediumTermLastReset)) / 1000);
|
||||
mediumTermArrivals = 0;
|
||||
mediumTermLastReset = now;
|
||||
}
|
||||
|
||||
synchronized private void resetLong() {
|
||||
long now = System.currentTimeMillis();
|
||||
longTermRate = longTermArrivals / (((double) (now - longTermLastReset)) / 1000);
|
||||
longTermArrivals = 0;
|
||||
longTermLastReset = now;
|
||||
}
|
||||
|
||||
synchronized public void arrived() {
|
||||
shortTermArrivals++;
|
||||
mediumTermArrivals++;
|
||||
longTermArrivals++;
|
||||
if (recalculateEveryUpdate) {
|
||||
calculateRate();
|
||||
if ((System.currentTimeMillis() - created) > 600000) {
|
||||
recalculateEveryUpdate = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private double getShortRate(long now) {
|
||||
if (shortTermRate > 0) {
|
||||
return shortTermRate;
|
||||
} else {
|
||||
return shortTermArrivals / (((double) (now - shortTermLastReset)) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private double getMediumRate(long now) {
|
||||
if (mediumTermRate > 0) {
|
||||
return mediumTermRate;
|
||||
} else {
|
||||
return mediumTermArrivals / (((double) (now - mediumTermLastReset)) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized private double getLongRate(long now) {
|
||||
if (longTermRate > 0) {
|
||||
return longTermRate;
|
||||
} else {
|
||||
return longTermArrivals / (((double) (now - longTermLastReset)) / 1000);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void calculateRate() {
|
||||
long now = System.currentTimeMillis();
|
||||
// 1 minute
|
||||
if (now - shortTermLastReset > 60000) {
|
||||
// use live
|
||||
shortTermRate = 0;
|
||||
}
|
||||
// 3 minute
|
||||
if (now - mediumTermLastReset > 180000) {
|
||||
// use live
|
||||
mediumTermRate = 0;
|
||||
}
|
||||
// 12 minute
|
||||
if (now - longTermLastReset > 720000) {
|
||||
// use live
|
||||
longTermRate = 0;
|
||||
}
|
||||
rate = 0.50 * getLongRate(now) + 0.30 * getMediumRate(now) + 0.20 * getShortRate(now);
|
||||
}
|
||||
|
||||
synchronized public double getRate() {
|
||||
if (rate == 0) {
|
||||
calculateRate();
|
||||
}
|
||||
return rate;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,525 @@
|
||||
package timely.balancer;
|
||||
|
||||
import io.netty.bootstrap.Bootstrap;
|
||||
import io.netty.bootstrap.ServerBootstrap;
|
||||
import io.netty.buffer.PooledByteBufAllocator;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandler;
|
||||
import io.netty.channel.ChannelInitializer;
|
||||
import io.netty.channel.ChannelOption;
|
||||
import io.netty.channel.EventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollChannelOption;
|
||||
import io.netty.channel.epoll.EpollDatagramChannel;
|
||||
import io.netty.channel.epoll.EpollEventLoopGroup;
|
||||
import io.netty.channel.epoll.EpollServerSocketChannel;
|
||||
import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.DatagramChannel;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioDatagramChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.codec.DelimiterBasedFrameDecoder;
|
||||
import io.netty.handler.codec.Delimiters;
|
||||
import io.netty.handler.codec.http.HttpContentCompressor;
|
||||
import io.netty.handler.codec.http.HttpContentDecompressor;
|
||||
import io.netty.handler.codec.http.HttpObjectAggregator;
|
||||
import io.netty.handler.codec.http.HttpRequestDecoder;
|
||||
import io.netty.handler.codec.http.HttpResponseEncoder;
|
||||
import io.netty.handler.codec.http.HttpServerCodec;
|
||||
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
|
||||
import io.netty.handler.logging.LoggingHandler;
|
||||
import io.netty.handler.ssl.ClientAuth;
|
||||
import io.netty.handler.ssl.OpenSslServerContext;
|
||||
import io.netty.handler.ssl.OpenSslServerSessionContext;
|
||||
import io.netty.handler.ssl.SslContext;
|
||||
import io.netty.handler.ssl.SslContextBuilder;
|
||||
import io.netty.handler.ssl.SslProvider;
|
||||
import io.netty.handler.ssl.util.SelfSignedCertificate;
|
||||
import io.netty.handler.stream.ChunkedWriteHandler;
|
||||
import io.netty.handler.timeout.IdleStateHandler;
|
||||
import io.netty.util.NettyRuntime;
|
||||
import io.netty.util.internal.SystemPropertyUtil;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.builder.SpringApplicationBuilder;
|
||||
import org.springframework.context.ConfigurableApplicationContext;
|
||||
import timely.Configuration;
|
||||
import timely.SpringBootstrap;
|
||||
import timely.balancer.connection.http.HttpClientPool;
|
||||
import timely.balancer.connection.tcp.TcpClientPool;
|
||||
import timely.balancer.connection.udp.UdpClientPool;
|
||||
import timely.balancer.connection.ws.WsClientPool;
|
||||
import timely.balancer.healthcheck.HealthChecker;
|
||||
import timely.balancer.netty.http.HttpRelayHandler;
|
||||
import timely.balancer.netty.tcp.TcpRelayHandler;
|
||||
import timely.balancer.netty.udp.UdpRelayHandler;
|
||||
import timely.balancer.netty.ws.WsRelayHandler;
|
||||
import timely.balancer.resolver.BalancedMetricResolver;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
import timely.client.http.HttpClient;
|
||||
import timely.netty.http.HttpStaticFileServerHandler;
|
||||
import timely.netty.http.NonSslRedirectHandler;
|
||||
import timely.netty.http.TimelyExceptionHandler;
|
||||
import timely.netty.http.auth.X509LoginRequestHandler;
|
||||
import timely.netty.tcp.MetricsBufferDecoder;
|
||||
import timely.netty.tcp.TcpDecoder;
|
||||
import timely.netty.tcp.TcpVersionHandler;
|
||||
import timely.netty.udp.UdpDecoder;
|
||||
import timely.netty.udp.UdpPacketToByteBuf;
|
||||
import timely.netty.websocket.WebSocketHttpCookieHandler;
|
||||
import timely.netty.websocket.WebSocketRequestDecoder;
|
||||
import timely.netty.websocket.subscription.WSTimelyExceptionHandler;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
import java.io.File;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class Balancer {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(Balancer.class);
|
||||
private static final int EPOLL_MIN_MAJOR_VERSION = 2;
|
||||
private static final int EPOLL_MIN_MINOR_VERSION = 6;
|
||||
private static final int EPOLL_MIN_PATCH_VERSION = 32;
|
||||
private static final String OS_NAME = "os.name";
|
||||
private static final String OS_VERSION = "os.version";
|
||||
private static final String WS_PATH = "/websocket";
|
||||
protected static final CountDownLatch LATCH = new CountDownLatch(1);
|
||||
static ConfigurableApplicationContext applicationContext;
|
||||
|
||||
private final Configuration config;
|
||||
private final BalancerConfiguration balancerConfig;
|
||||
private EventLoopGroup tcpWorkerGroup = null;
|
||||
private EventLoopGroup tcpBossGroup = null;
|
||||
private EventLoopGroup httpWorkerGroup = null;
|
||||
private EventLoopGroup httpBossGroup = null;
|
||||
private EventLoopGroup wsWorkerGroup = null;
|
||||
private EventLoopGroup wsBossGroup = null;
|
||||
private EventLoopGroup udpBossGroup = null;
|
||||
private EventLoopGroup udpWorkerGroup = null;
|
||||
private MetricResolver metricResolver;
|
||||
protected Channel tcpChannelHandle = null;
|
||||
protected Channel httpChannelHandle = null;
|
||||
protected Channel wsChannelHandle = null;
|
||||
protected List<Channel> udpChannelHandleList = new ArrayList<>();
|
||||
protected volatile boolean shutdown = false;
|
||||
private static final int DEFAULT_EVENT_LOOP_THREADS;
|
||||
|
||||
static {
|
||||
DEFAULT_EVENT_LOOP_THREADS = Math.max(1,
|
||||
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
|
||||
}
|
||||
|
||||
public Balancer(Configuration conf, BalancerConfiguration balancerConf) throws Exception {
|
||||
this.config = conf;
|
||||
this.balancerConfig = balancerConf;
|
||||
}
|
||||
|
||||
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) {
|
||||
return new SpringApplicationBuilder(SpringBootstrap.class).web(false).run(args);
|
||||
}
|
||||
|
||||
private void shutdownHook() {
|
||||
|
||||
final Runnable shutdownRunner = () -> {
|
||||
if (!shutdown) {
|
||||
shutdown();
|
||||
}
|
||||
};
|
||||
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
|
||||
Runtime.getRuntime().addShutdownHook(hook);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
List<ChannelFuture> channelFutures = new ArrayList<>();
|
||||
|
||||
if (tcpChannelHandle != null) {
|
||||
LOG.info("Closing tcpChannelHandle");
|
||||
channelFutures.add(tcpChannelHandle.close());
|
||||
}
|
||||
|
||||
if (httpChannelHandle != null) {
|
||||
LOG.info("Closing httpChannelHandle");
|
||||
channelFutures.add(httpChannelHandle.close());
|
||||
}
|
||||
|
||||
if (wsChannelHandle != null) {
|
||||
LOG.info("Closing wsChannelHandle");
|
||||
channelFutures.add(wsChannelHandle.close());
|
||||
}
|
||||
|
||||
for (Channel c : udpChannelHandleList) {
|
||||
LOG.info("Closing udpChannelHandle");
|
||||
channelFutures.add(c.close());
|
||||
}
|
||||
|
||||
// wait for the channels to shutdown
|
||||
channelFutures.forEach(f -> {
|
||||
try {
|
||||
f.get();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error while shutting down channel: {}", f.channel().config());
|
||||
LOG.error("{}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
Integer quietPeriod = config.getServer().getShutdownQuietPeriod();
|
||||
List<Future<?>> groupFutures = new ArrayList<>();
|
||||
if (tcpBossGroup != null) {
|
||||
LOG.info("Shutting down tcpBossGroup");
|
||||
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (tcpWorkerGroup != null) {
|
||||
LOG.info("Shutting down tcpWorkerGroup");
|
||||
groupFutures.add(tcpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (httpBossGroup != null) {
|
||||
LOG.info("Shutting down httpBossGroup");
|
||||
groupFutures.add(httpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (httpWorkerGroup != null) {
|
||||
LOG.info("Shutting down httpWorkerGroup");
|
||||
groupFutures.add(httpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (wsBossGroup != null) {
|
||||
LOG.info("Shutting down wsBossGroup");
|
||||
groupFutures.add(wsBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (wsWorkerGroup != null) {
|
||||
LOG.info("Shutting down wsWorkerGroup");
|
||||
groupFutures.add(wsWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (udpBossGroup != null) {
|
||||
LOG.info("Shutting down udpBossGroup");
|
||||
groupFutures.add(udpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
if (udpWorkerGroup != null) {
|
||||
LOG.info("Shutting down udpWorkerGroup");
|
||||
groupFutures.add(udpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
|
||||
}
|
||||
|
||||
groupFutures.parallelStream().forEach(f -> {
|
||||
try {
|
||||
f.get();
|
||||
} catch (final Exception e) {
|
||||
LOG.error("Error while shutting down group: {}", f.toString());
|
||||
LOG.error("{}", e.getMessage());
|
||||
e.printStackTrace();
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
LOG.info("Closing WebSocketRequestDecoder");
|
||||
WebSocketRequestDecoder.close();
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
|
||||
}
|
||||
|
||||
if (applicationContext != null) {
|
||||
LOG.info("Closing applicationContext");
|
||||
applicationContext.close();
|
||||
}
|
||||
this.shutdown = true;
|
||||
LOG.info("Server shut down.");
|
||||
}
|
||||
|
||||
private static boolean useEpoll() {
|
||||
|
||||
// Should we just return true if this is Linux and if we get an error
|
||||
// during Epoll
|
||||
// setup handle it there?
|
||||
final String os = SystemPropertyUtil.get(OS_NAME).toLowerCase().trim();
|
||||
final String[] version = SystemPropertyUtil.get(OS_VERSION).toLowerCase().trim().split("\\.");
|
||||
if (os.startsWith("linux") && version.length >= 3) {
|
||||
final int major = Integer.parseInt(version[0]);
|
||||
if (major > EPOLL_MIN_MAJOR_VERSION) {
|
||||
return true;
|
||||
} else if (major == EPOLL_MIN_MAJOR_VERSION) {
|
||||
final int minor = Integer.parseInt(version[1]);
|
||||
if (minor > EPOLL_MIN_MINOR_VERSION) {
|
||||
return true;
|
||||
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
|
||||
final int patch = Integer.parseInt(version[2].substring(0, 2));
|
||||
return patch >= EPOLL_MIN_PATCH_VERSION;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected SslContext createSSLContext(Configuration config) throws Exception {
|
||||
|
||||
Configuration.Ssl sslCfg = config.getSecurity().getSsl();
|
||||
Boolean generate = sslCfg.isUseGeneratedKeypair();
|
||||
SslContextBuilder ssl;
|
||||
if (generate) {
|
||||
LOG.warn("Using generated self signed server certificate");
|
||||
Date begin = new Date();
|
||||
Date end = new Date(begin.getTime() + 86400000);
|
||||
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
|
||||
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
|
||||
} else {
|
||||
String cert = sslCfg.getCertificateFile();
|
||||
String key = sslCfg.getKeyFile();
|
||||
String keyPass = sslCfg.getKeyPassword();
|
||||
if (null == cert || null == key) {
|
||||
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
|
||||
}
|
||||
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
|
||||
}
|
||||
|
||||
ssl.ciphers(sslCfg.getUseCiphers());
|
||||
|
||||
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
|
||||
ssl.clientAuth(ClientAuth.OPTIONAL);
|
||||
|
||||
Boolean useOpenSSL = sslCfg.isUseOpenssl();
|
||||
if (useOpenSSL) {
|
||||
ssl.sslProvider(SslProvider.OPENSSL);
|
||||
} else {
|
||||
ssl.sslProvider(SslProvider.JDK);
|
||||
}
|
||||
String trustStore = sslCfg.getTrustStoreFile();
|
||||
if (null != trustStore) {
|
||||
if (!trustStore.isEmpty()) {
|
||||
ssl.trustManager(new File(trustStore));
|
||||
}
|
||||
}
|
||||
return ssl.build();
|
||||
}
|
||||
|
||||
protected SSLContext createSSLClientContext(BalancerConfiguration config) throws Exception {
|
||||
|
||||
BalancerConfiguration.ClientSsl ssl = config.getClientSsl();
|
||||
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(),
|
||||
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
|
||||
}
|
||||
|
||||
public void run() throws Exception {
|
||||
|
||||
final boolean useEpoll = useEpoll();
|
||||
Class<? extends ServerSocketChannel> channelClass;
|
||||
Class<? extends Channel> datagramChannelClass;
|
||||
if (useEpoll) {
|
||||
tcpWorkerGroup = new EpollEventLoopGroup();
|
||||
tcpBossGroup = new EpollEventLoopGroup();
|
||||
httpWorkerGroup = new EpollEventLoopGroup();
|
||||
httpBossGroup = new EpollEventLoopGroup();
|
||||
wsWorkerGroup = new EpollEventLoopGroup();
|
||||
wsBossGroup = new EpollEventLoopGroup();
|
||||
udpWorkerGroup = new EpollEventLoopGroup();
|
||||
udpBossGroup = new EpollEventLoopGroup();
|
||||
channelClass = EpollServerSocketChannel.class;
|
||||
datagramChannelClass = EpollDatagramChannel.class;
|
||||
} else {
|
||||
tcpWorkerGroup = new NioEventLoopGroup();
|
||||
tcpBossGroup = new NioEventLoopGroup();
|
||||
httpWorkerGroup = new NioEventLoopGroup();
|
||||
httpBossGroup = new NioEventLoopGroup();
|
||||
wsWorkerGroup = new NioEventLoopGroup();
|
||||
wsBossGroup = new NioEventLoopGroup();
|
||||
udpWorkerGroup = new NioEventLoopGroup();
|
||||
udpBossGroup = new NioEventLoopGroup();
|
||||
channelClass = NioServerSocketChannel.class;
|
||||
datagramChannelClass = NioDatagramChannel.class;
|
||||
}
|
||||
LOG.info("Using channel class {}", channelClass.getSimpleName());
|
||||
|
||||
TcpClientPool tcpClientPool = new TcpClientPool(this.balancerConfig);
|
||||
HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPool);
|
||||
this.metricResolver = new BalancedMetricResolver(this.balancerConfig, healthChecker);
|
||||
|
||||
final ServerBootstrap tcpServer = new ServerBootstrap();
|
||||
tcpServer.group(tcpBossGroup, tcpWorkerGroup);
|
||||
tcpServer.channel(channelClass);
|
||||
tcpServer.handler(new LoggingHandler());
|
||||
tcpServer.childHandler(setupTcpChannel(metricResolver, tcpClientPool));
|
||||
tcpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
tcpServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||
tcpServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
final int tcpPort = config.getServer().getTcpPort();
|
||||
final String tcpIp = config.getServer().getIp();
|
||||
tcpChannelHandle = tcpServer.bind(tcpIp, tcpPort).sync().channel();
|
||||
final String tcpAddress = ((InetSocketAddress) tcpChannelHandle.localAddress()).getAddress().getHostAddress();
|
||||
|
||||
final int httpPort = config.getHttp().getPort();
|
||||
final String httpIp = config.getHttp().getIp();
|
||||
SslContext sslCtx = createSSLContext(config);
|
||||
if (sslCtx instanceof OpenSslServerContext) {
|
||||
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
|
||||
String application = "Timely_" + httpPort;
|
||||
OpenSslServerSessionContext opensslCtx = openssl.sessionContext();
|
||||
opensslCtx.setSessionCacheEnabled(true);
|
||||
opensslCtx.setSessionCacheSize(128);
|
||||
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
|
||||
opensslCtx.setSessionTimeout(config.getSecurity().getSessionMaxAge());
|
||||
}
|
||||
SSLContext clientSSLContext = createSSLClientContext(balancerConfig);
|
||||
final ServerBootstrap httpServer = new ServerBootstrap();
|
||||
httpServer.group(httpBossGroup, httpWorkerGroup);
|
||||
httpServer.channel(channelClass);
|
||||
httpServer.handler(new LoggingHandler());
|
||||
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig, clientSSLContext);
|
||||
httpServer.childHandler(setupHttpChannel(config, balancerConfig, sslCtx, metricResolver, httpClientPool));
|
||||
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
httpServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||
httpServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
httpChannelHandle = httpServer.bind(httpIp, httpPort).sync().channel();
|
||||
final String httpAddress = ((InetSocketAddress) httpChannelHandle.localAddress()).getAddress().getHostAddress();
|
||||
|
||||
final int wsPort = config.getWebsocket().getPort();
|
||||
final String wsIp = config.getWebsocket().getIp();
|
||||
final ServerBootstrap wsServer = new ServerBootstrap();
|
||||
wsServer.group(wsBossGroup, wsWorkerGroup);
|
||||
wsServer.channel(channelClass);
|
||||
wsServer.handler(new LoggingHandler());
|
||||
WsClientPool wsClientPool = new WsClientPool(config, balancerConfig, clientSSLContext);
|
||||
wsServer.childHandler(setupWSChannel(config, balancerConfig, sslCtx, metricResolver, wsClientPool));
|
||||
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
wsServer.option(ChannelOption.SO_BACKLOG, 128);
|
||||
wsServer.option(ChannelOption.SO_KEEPALIVE, true);
|
||||
/* Not sure if next three lines are necessary */
|
||||
wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
|
||||
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860);
|
||||
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
|
||||
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
|
||||
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
|
||||
|
||||
final int udpPort = config.getServer().getUdpPort();
|
||||
final String udpIp = config.getServer().getIp();
|
||||
UdpClientPool udpClientPool = new UdpClientPool(balancerConfig);
|
||||
for (int n = 1; n < DEFAULT_EVENT_LOOP_THREADS; n++) {
|
||||
final Bootstrap udpServer = new Bootstrap();
|
||||
udpServer.group(udpBossGroup);
|
||||
udpServer.channel(datagramChannelClass);
|
||||
udpServer.handler(setupUdpChannel(metricResolver, udpClientPool));
|
||||
udpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
|
||||
udpServer.option(EpollChannelOption.SO_REUSEADDR, true);
|
||||
udpServer.option(EpollChannelOption.SO_REUSEPORT, true);
|
||||
udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel());
|
||||
}
|
||||
shutdownHook();
|
||||
LOG.info(
|
||||
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
|
||||
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
|
||||
Balancer.applicationContext = Balancer.initializeConfiguration(args);
|
||||
Configuration conf = applicationContext.getBean(Configuration.class);
|
||||
BalancerConfiguration balancerConf = applicationContext.getBean(BalancerConfiguration.class);
|
||||
|
||||
Balancer b = new Balancer(conf, balancerConf);
|
||||
try {
|
||||
b.run();
|
||||
LATCH.await();
|
||||
} catch (final InterruptedException e) {
|
||||
LOG.info("Server shutting down.");
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error running server.", e);
|
||||
} finally {
|
||||
try {
|
||||
b.shutdown();
|
||||
} catch (Exception e) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ChannelHandler setupHttpChannel(Configuration config, BalancerConfiguration balancerConfig,
|
||||
SslContext sslCtx, MetricResolver metricResolver, HttpClientPool httpClientPool) {
|
||||
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
|
||||
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(config, sslCtx));
|
||||
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
|
||||
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
|
||||
ch.pipeline().addLast("compressor", new HttpContentCompressor());
|
||||
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
|
||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
||||
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
|
||||
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(config));
|
||||
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
|
||||
ch.pipeline().addLast("login", new X509LoginRequestHandler(config));
|
||||
ch.pipeline()
|
||||
.addLast("httpRelay", new HttpRelayHandler(balancerConfig, metricResolver, httpClientPool));
|
||||
ch.pipeline().addLast("error", new TimelyExceptionHandler());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ChannelHandler setupUdpChannel(MetricResolver metricResolver, UdpClientPool udpClientPool) {
|
||||
return new ChannelInitializer<DatagramChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(DatagramChannel ch) throws Exception {
|
||||
ch.pipeline().addLast("logger", new LoggingHandler());
|
||||
ch.pipeline().addLast("packetDecoder", new UdpPacketToByteBuf());
|
||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
||||
ch.pipeline().addLast("putDecoder", new UdpDecoder());
|
||||
ch.pipeline().addLast("udpRelayHandler", new UdpRelayHandler(metricResolver, udpClientPool));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ChannelHandler setupTcpChannel(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast("buffer", new MetricsBufferDecoder());
|
||||
ch.pipeline().addLast("frame", new DelimiterBasedFrameDecoder(8192, true, Delimiters.lineDelimiter()));
|
||||
ch.pipeline().addLast("putDecoder", new TcpDecoder());
|
||||
ch.pipeline().addLast("tcpRelayHandler", new TcpRelayHandler(metricResolver, tcpClientPool));
|
||||
ch.pipeline().addLast("versionHandler", new TcpVersionHandler());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
protected ChannelHandler setupWSChannel(Configuration conf, BalancerConfiguration balancerConfig,
|
||||
SslContext sslCtx, MetricResolver metricResolver, WsClientPool wsClientPool) {
|
||||
return new ChannelInitializer<SocketChannel>() {
|
||||
|
||||
@Override
|
||||
protected void initChannel(SocketChannel ch) throws Exception {
|
||||
ch.pipeline().addLast("ssl", sslCtx.newHandler(ch.alloc()));
|
||||
ch.pipeline().addLast("httpServer", new HttpServerCodec());
|
||||
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(8192));
|
||||
ch.pipeline().addLast("sessionExtractor", new WebSocketHttpCookieHandler(config));
|
||||
|
||||
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
|
||||
ch.pipeline().addLast("ws-protocol",
|
||||
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true));
|
||||
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config));
|
||||
ch.pipeline().addLast("httpRelay",
|
||||
new WsRelayHandler(balancerConfig, config, metricResolver, wsClientPool));
|
||||
ch.pipeline().addLast("error", new WSTimelyExceptionHandler());
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
package timely.balancer;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
import org.springframework.boot.context.properties.NestedConfigurationProperty;
|
||||
import org.springframework.stereotype.Component;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "timely.balancer")
|
||||
public class BalancerConfiguration {
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private GenericKeyedObjectPoolConfig udpClientPool = new GenericKeyedObjectPoolConfig();
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private GenericKeyedObjectPoolConfig tcpClientPool = new GenericKeyedObjectPoolConfig();
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private GenericKeyedObjectPoolConfig httpClientPool = new GenericKeyedObjectPoolConfig();
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private GenericKeyedObjectPoolConfig wsClientPool = new GenericKeyedObjectPoolConfig();
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private ClientSsl clientSsl = new ClientSsl();
|
||||
|
||||
private boolean loginRequired = false;
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
|
||||
|
||||
private long checkServerHealthInterval = 10000;
|
||||
|
||||
private int serverFailuresBeforeDown = 3;
|
||||
|
||||
private int serverSuccessesBeforeUp = 3;
|
||||
|
||||
@Valid
|
||||
@NestedConfigurationProperty
|
||||
private List<TimelyBalancedHost> wsHosts = new ArrayList<>();
|
||||
|
||||
public void setCheckServerHealthInterval(long checkServerHealthInterval) {
|
||||
this.checkServerHealthInterval = checkServerHealthInterval;
|
||||
}
|
||||
|
||||
public long getCheckServerHealthInterval() {
|
||||
return checkServerHealthInterval;
|
||||
}
|
||||
|
||||
public int getServerFailuresBeforeDown() {
|
||||
return serverFailuresBeforeDown;
|
||||
}
|
||||
|
||||
public void setServerFailuresBeforeDown(int serverFailuresBeforeDown) {
|
||||
this.serverFailuresBeforeDown = serverFailuresBeforeDown;
|
||||
}
|
||||
|
||||
public int getServerSuccessesBeforeUp() {
|
||||
return serverSuccessesBeforeUp;
|
||||
}
|
||||
|
||||
public void setServerSuccessesBeforeUp(int serverSuccessesBeforeUp) {
|
||||
this.serverSuccessesBeforeUp = serverSuccessesBeforeUp;
|
||||
}
|
||||
|
||||
public GenericKeyedObjectPoolConfig getUdpClientPool() {
|
||||
return udpClientPool;
|
||||
}
|
||||
|
||||
public GenericKeyedObjectPoolConfig getTcpClientPool() {
|
||||
return tcpClientPool;
|
||||
}
|
||||
|
||||
public GenericKeyedObjectPoolConfig getHttpClientPool() {
|
||||
return httpClientPool;
|
||||
}
|
||||
|
||||
public GenericKeyedObjectPoolConfig getWsClientPool() {
|
||||
return wsClientPool;
|
||||
}
|
||||
|
||||
public ClientSsl getClientSsl() {
|
||||
return clientSsl;
|
||||
}
|
||||
|
||||
public void setLoginRequired(boolean loginRequired) {
|
||||
this.loginRequired = loginRequired;
|
||||
}
|
||||
|
||||
public boolean isLoginRequired() {
|
||||
return loginRequired;
|
||||
}
|
||||
|
||||
public List<TimelyBalancedHost> getTimelyHosts() {
|
||||
return timelyHosts;
|
||||
}
|
||||
|
||||
public class ClientSsl {
|
||||
|
||||
private String keyFile;
|
||||
private String keyType;
|
||||
private String keyPassword;
|
||||
private String trustStoreFile;
|
||||
private String trustStoreType;
|
||||
private String trustStorePassword;
|
||||
|
||||
public String getKeyFile() {
|
||||
return keyFile;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setKeyFile(String keyFile) {
|
||||
this.keyFile = keyFile;
|
||||
return BalancerConfiguration.this;
|
||||
}
|
||||
|
||||
public String getKeyType() {
|
||||
return keyType;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setKeyType(String keyType) {
|
||||
this.keyType = keyType;
|
||||
return BalancerConfiguration.this;
|
||||
}
|
||||
|
||||
public String getKeyPassword() {
|
||||
return keyPassword;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setKeyPassword(String keyPassword) {
|
||||
this.keyPassword = keyPassword;
|
||||
return BalancerConfiguration.this;
|
||||
}
|
||||
|
||||
public String getTrustStoreFile() {
|
||||
return trustStoreFile;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setTrustStoreFile(String trustStoreFile) {
|
||||
this.trustStoreFile = trustStoreFile;
|
||||
return BalancerConfiguration.this;
|
||||
}
|
||||
|
||||
public String getTrustStoreType() {
|
||||
return trustStoreType;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setTrustStoreType(String trustStoreType) {
|
||||
this.trustStoreType = trustStoreType;
|
||||
return BalancerConfiguration.this;
|
||||
}
|
||||
|
||||
public String getTrustStorePassword() {
|
||||
return trustStorePassword;
|
||||
}
|
||||
|
||||
public BalancerConfiguration setTrustStorePassword(String trustStorePassword) {
|
||||
this.trustStorePassword = trustStorePassword;
|
||||
return BalancerConfiguration.this;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package timely.balancer;
|
||||
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import timely.SpringBootstrap;
|
||||
|
||||
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
|
||||
public class BalancerSpringBootstrap {
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package timely.balancer.connection;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.balancer.ArrivalRate;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
|
||||
public class TimelyBalancedHost {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class);
|
||||
|
||||
private String regex;
|
||||
private String host;
|
||||
private int tcpPort;
|
||||
private int httpPort;
|
||||
private int wsPort;
|
||||
private int udpPort;
|
||||
private boolean isUp = true;
|
||||
private BalancerConfiguration config;
|
||||
private int failures = 0;
|
||||
private int successes = 0;
|
||||
private ArrivalRate arrivalRate = new ArrivalRate();
|
||||
|
||||
public TimelyBalancedHost() {
|
||||
|
||||
}
|
||||
|
||||
public TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
|
||||
this.host = host;
|
||||
this.tcpPort = tcpPort;
|
||||
this.httpPort = httpPort;
|
||||
this.wsPort = wsPort;
|
||||
this.udpPort = udpPort;
|
||||
}
|
||||
|
||||
public TimelyBalancedHost(String host, int basePort) {
|
||||
this.host = host;
|
||||
this.tcpPort = basePort;
|
||||
this.httpPort = basePort + 1;
|
||||
this.wsPort = basePort + 2;
|
||||
this.udpPort = basePort + 3;
|
||||
}
|
||||
|
||||
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort) {
|
||||
return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort);
|
||||
}
|
||||
|
||||
static public TimelyBalancedHost of(String host, int basePort) {
|
||||
return new TimelyBalancedHost(host, basePort);
|
||||
}
|
||||
|
||||
public void setConfig(BalancerConfiguration config) {
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
synchronized public boolean isUp() {
|
||||
return isUp;
|
||||
}
|
||||
|
||||
public void reportSuccess() {
|
||||
int serverSuccessesBeforeUp = config.getServerSuccessesBeforeUp();
|
||||
String h = host;
|
||||
int p = tcpPort;
|
||||
synchronized (this) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("success reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p,
|
||||
isUp, (successes + 1), serverSuccessesBeforeUp);
|
||||
}
|
||||
if (!isUp) {
|
||||
if (++successes >= serverSuccessesBeforeUp) {
|
||||
isUp = true;
|
||||
successes = 0;
|
||||
LOG.info("host up host:{} port:{}", h, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void reportFailure() {
|
||||
int serverFailuresBeforeDown = config.getServerFailuresBeforeDown();
|
||||
String h = host;
|
||||
int p = tcpPort;
|
||||
synchronized (this) {
|
||||
if (LOG.isTraceEnabled()) {
|
||||
LOG.trace("failure reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p,
|
||||
isUp, (failures + 1), serverFailuresBeforeDown);
|
||||
}
|
||||
if (isUp) {
|
||||
if (++failures >= serverFailuresBeforeDown) {
|
||||
isUp = false;
|
||||
failures = 0;
|
||||
LOG.info("host down host:{} port:{}", h, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getRegex() {
|
||||
return regex;
|
||||
}
|
||||
|
||||
public void setRegex(String regex) {
|
||||
this.regex = regex;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public int getUdpPort() {
|
||||
return udpPort;
|
||||
}
|
||||
|
||||
public void setUdpPort(int udpPort) {
|
||||
this.udpPort = udpPort;
|
||||
}
|
||||
|
||||
public int getTcpPort() {
|
||||
return tcpPort;
|
||||
}
|
||||
|
||||
public void setTcpPort(int tcpPort) {
|
||||
this.tcpPort = tcpPort;
|
||||
}
|
||||
|
||||
public int getHttpPort() {
|
||||
return httpPort;
|
||||
}
|
||||
|
||||
public void setHttpPort(int httpPort) {
|
||||
this.httpPort = httpPort;
|
||||
}
|
||||
|
||||
public int getWsPort() {
|
||||
return wsPort;
|
||||
}
|
||||
|
||||
public void setWsPort(int wsPort) {
|
||||
this.wsPort = wsPort;
|
||||
}
|
||||
|
||||
public void arrived() {
|
||||
arrivalRate.arrived();
|
||||
}
|
||||
|
||||
public double getArrivalRate() {
|
||||
return arrivalRate.getRate();
|
||||
}
|
||||
|
||||
public void calculateRate() {
|
||||
arrivalRate.calculateRate();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package timely.balancer.connection.http;
|
||||
|
||||
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
|
||||
|
||||
private final SSLContext sslContext;
|
||||
|
||||
public HttpClientFactory(SSLContext sslContext) {
|
||||
this.sslContext = sslContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||
BasicCookieStore cookieJar = new BasicCookieStore();
|
||||
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, cookieJar, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) {
|
||||
if (k.isUp()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(TimelyBalancedHost k, PooledObject<CloseableHttpClient> o) throws Exception {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package timely.balancer.connection.http;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
//import java.util.Map;
|
||||
//import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpClientPool.class);
|
||||
|
||||
public HttpClientPool(BalancerConfiguration config, SSLContext sslContext) {
|
||||
super(new HttpClientFactory(sslContext), config.getHttpClientPool());
|
||||
}
|
||||
|
||||
// private Map<TimelyBalancedHost, Integer> checkedOut = new
|
||||
// ConcurrentHashMap<>();
|
||||
|
||||
// @Override
|
||||
// public CloseableHttpClient borrowObject(TimelyBalancedHost key) throws
|
||||
// Exception {
|
||||
// CloseableHttpClient client = super.borrowObject(key);
|
||||
// Integer n;
|
||||
// synchronized (checkedOut) {
|
||||
// n = checkedOut.get(key);
|
||||
// if (n == null) {
|
||||
// n = 0;
|
||||
// }
|
||||
// n++;
|
||||
// checkedOut.put(key, n);
|
||||
// }
|
||||
// LOG.trace("Borrowing HttpClient for key " + key.getHost() + ":" +
|
||||
// key.getHttpPort() + " " + getNumActive(key)
|
||||
// + ":" + getNumIdle(key) + ":" + n);
|
||||
// return client;
|
||||
// }
|
||||
//
|
||||
// @Override
|
||||
// public void returnObject(TimelyBalancedHost key, CloseableHttpClient obj)
|
||||
// {
|
||||
// super.returnObject(key, obj);
|
||||
// Integer n;
|
||||
// synchronized (checkedOut) {
|
||||
// n = checkedOut.get(key);
|
||||
// n--;
|
||||
// checkedOut.put(key, n);
|
||||
// }
|
||||
// LOG.trace("Returning HttpClient for key " + key.getHost() + ":" +
|
||||
// key.getHttpPort() + " " + getNumActive(key)
|
||||
// + ":" + getNumIdle(key) + ":" + n);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package timely.balancer.connection.tcp;
|
||||
|
||||
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.tcp.TcpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class TcpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, TcpClient> {
|
||||
|
||||
public TcpClientFactory() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<TcpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||
return new DefaultPooledObject<>(new TcpClient(k.getHost(), k.getTcpPort()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().flush();
|
||||
o.getObject().close();
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Error closing connection to " + k.getHost() + ":" + k.getTcpPort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) {
|
||||
if (k.isUp()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().open();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Unable to connect to " + k.getHost() + ":" + k.getTcpPort(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(TimelyBalancedHost k, PooledObject<TcpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().flush();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Error flushing connection to " + k.getHost() + ":" + k.getTcpPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package timely.balancer.connection.tcp;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.tcp.TcpClient;
|
||||
|
||||
public class TcpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, TcpClient> {
|
||||
|
||||
public TcpClientPool(BalancerConfiguration config) {
|
||||
super(new TcpClientFactory(), config.getTcpClientPool());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package timely.balancer.connection.udp;
|
||||
|
||||
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.udp.UdpClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class UdpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, UdpClient> {
|
||||
|
||||
public UdpClientFactory() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<UdpClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||
return new DefaultPooledObject<>(new UdpClient(k.getHost(), k.getUdpPort()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().flush();
|
||||
o.getObject().close();
|
||||
} catch (IOException e) {
|
||||
throw new IOException("Error closing connection to " + k.getHost() + ":" + k.getUdpPort());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) {
|
||||
if (k.isUp()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().open();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Unable to connect to " + k.getHost() + ":" + k.getUdpPort(), e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(TimelyBalancedHost k, PooledObject<UdpClient> o) throws Exception {
|
||||
try {
|
||||
o.getObject().flush();
|
||||
} catch (Exception e) {
|
||||
throw new IOException("Error flushing connection to " + k.getHost() + ":" + k.getUdpPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package timely.balancer.connection.udp;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.udp.UdpClient;
|
||||
|
||||
public class UdpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, UdpClient> {
|
||||
|
||||
public UdpClientPool(BalancerConfiguration config) {
|
||||
super(new UdpClientFactory(), config.getUdpClientPool());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
package timely.balancer.connection.ws;
|
||||
|
||||
import org.apache.commons.pool2.KeyedPooledObjectFactory;
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.impl.client.BasicCookieStore;
|
||||
import timely.Configuration;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> {
|
||||
|
||||
private final SSLContext sslContext;
|
||||
private final boolean doLogin;
|
||||
private Configuration config;
|
||||
|
||||
public WsClientFactory(Configuration config, SSLContext sslContext, boolean doLogin) {
|
||||
this.sslContext = sslContext;
|
||||
this.doLogin = doLogin;
|
||||
this.config = config;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
|
||||
|
||||
int bufferSize = config.getWebsocket().getSubscriptionBatchSize() * 500;
|
||||
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
|
||||
k.getWsPort(), doLogin, "", "", false, bufferSize);
|
||||
return new DefaultPooledObject<>(client);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) {
|
||||
if (k.isUp()) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
|
||||
|
||||
// try {
|
||||
// o.getObject().open();
|
||||
// } catch (IOException e) {
|
||||
// throw new IOException("Unable to connect to " + k.getHost() + ":" +
|
||||
// k.getHttpPort());
|
||||
// }
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(TimelyBalancedHost k, PooledObject<WebSocketSubscriptionClient> o) throws Exception {
|
||||
// try {
|
||||
// o.getObject().flush();
|
||||
// } catch (Exception e) {
|
||||
// throw new IOException("Error flushing connection to " + k.getHost() +
|
||||
// ":" + k.getHttpPort());
|
||||
// }
|
||||
// try {
|
||||
// o.getObject().flush();
|
||||
// o.getObject().close();
|
||||
// } catch (IOException e) {
|
||||
// throw new IOException("Error closing connection to " + k.getHost() +
|
||||
// ":" + k.getHttpPort());
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
package timely.balancer.connection.ws;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import timely.Configuration;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
|
||||
import javax.net.ssl.SSLContext;
|
||||
|
||||
public class WsClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, WebSocketSubscriptionClient> {
|
||||
|
||||
public WsClientPool(Configuration config, BalancerConfiguration balancerConfig, SSLContext sslContext) {
|
||||
super(new WsClientFactory(config, sslContext, balancerConfig.isLoginRequired()), balancerConfig
|
||||
.getWsClientPool());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package timely.balancer.healthcheck;
|
||||
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.tcp.TcpClientPool;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
public class HealthChecker {
|
||||
|
||||
private List<TimelyBalancedHost> timelyHosts;
|
||||
private Timer timer;
|
||||
final private TCPServerHealthCheck tcpServerHealthCheck;
|
||||
private TimerTask check = new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (TimelyBalancedHost h : timelyHosts) {
|
||||
if (tcpServerHealthCheck.isServerHealthy(h)) {
|
||||
h.reportSuccess();
|
||||
} else {
|
||||
h.reportFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
|
||||
this.timelyHosts = config.getTimelyHosts();
|
||||
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
|
||||
this.timer = new Timer("HealthCheckerThread", true);
|
||||
this.timer.scheduleAtFixedRate(check, 0, config.getCheckServerHealthInterval());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package timely.balancer.healthcheck;
|
||||
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
|
||||
public interface ServerHealthCheck {
|
||||
|
||||
public boolean isServerHealthy(TimelyBalancedHost timelyBalancedHost);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package timely.balancer.healthcheck;
|
||||
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.tcp.TcpClientPool;
|
||||
import timely.client.tcp.TcpClient;
|
||||
|
||||
public class TCPServerHealthCheck implements ServerHealthCheck {
|
||||
|
||||
final private TcpClientPool tcpClientPool;
|
||||
|
||||
public TCPServerHealthCheck(TcpClientPool tcpClientPool) {
|
||||
this.tcpClientPool = tcpClientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isServerHealthy(TimelyBalancedHost timelyBalancedHost) {
|
||||
|
||||
boolean healthy = true;
|
||||
TcpClient client = null;
|
||||
try {
|
||||
client = tcpClientPool.borrowObject(timelyBalancedHost);
|
||||
client.open();
|
||||
} catch (Exception e) {
|
||||
healthy = false;
|
||||
|
||||
} finally {
|
||||
if (client != null) {
|
||||
tcpClientPool.returnObject(timelyBalancedHost, client);
|
||||
}
|
||||
}
|
||||
return healthy;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
package timely.balancer.netty.http;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpRequest;
|
||||
import io.netty.handler.codec.http.DefaultFullHttpResponse;
|
||||
import io.netty.handler.codec.http.FullHttpRequest;
|
||||
import io.netty.handler.codec.http.FullHttpResponse;
|
||||
import io.netty.handler.codec.http.HttpHeaders;
|
||||
import io.netty.handler.codec.http.HttpMethod;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.HttpVersion;
|
||||
import io.netty.util.ReferenceCountUtil;
|
||||
import org.apache.http.Header;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
import org.apache.http.client.methods.HttpGet;
|
||||
import org.apache.http.client.methods.HttpPost;
|
||||
import org.apache.http.client.methods.HttpUriRequest;
|
||||
import org.apache.http.entity.ByteArrayEntity;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.message.BasicHeader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.util.StreamUtils;
|
||||
import timely.api.request.HttpGetRequest;
|
||||
import timely.api.request.HttpPostRequest;
|
||||
import timely.api.request.MetricRequest;
|
||||
import timely.api.request.timeseries.HttpRequest;
|
||||
import timely.api.request.timeseries.QueryRequest;
|
||||
import timely.api.response.TimelyException;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.http.HttpClientPool;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
import timely.netty.http.TimelyHttpHandler;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.net.URI;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH;
|
||||
|
||||
public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(HttpRelayHandler.class);
|
||||
private static final String LOG_RECEIVED_REQUEST = "Received HTTP request {}";
|
||||
private static final String LOG_PARSED_REQUEST = "Parsed request {}";
|
||||
private static final String NO_AUTHORIZATIONS = "";
|
||||
|
||||
private final BalancerConfiguration conf;
|
||||
private final HttpClientPool httpClientPool;
|
||||
private MetricResolver metricResolver;
|
||||
|
||||
public HttpRelayHandler(BalancerConfiguration config, MetricResolver metricResolver, HttpClientPool httpClientPool) {
|
||||
this.conf = config;
|
||||
this.metricResolver = metricResolver;
|
||||
this.httpClientPool = httpClientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
|
||||
|
||||
CloseableHttpClient client = null;
|
||||
TimelyBalancedHost k = null;
|
||||
CloseableHttpResponse relayedResponse = null;
|
||||
FullHttpRequest request = null;
|
||||
try {
|
||||
try {
|
||||
request = msg.getHttpRequest();
|
||||
String originalURI = request.getUri();
|
||||
originalURI = encodeURI(originalURI);
|
||||
|
||||
String metric = null;
|
||||
if (msg instanceof QueryRequest) {
|
||||
Collection<QueryRequest.SubQuery> subqueries = ((QueryRequest) msg).getQueries();
|
||||
if (subqueries != null) {
|
||||
Iterator<QueryRequest.SubQuery> itr = subqueries.iterator();
|
||||
if (itr.hasNext()) {
|
||||
metric = itr.next().getMetric();
|
||||
}
|
||||
}
|
||||
k = metricResolver.getHostPortKey(metric);
|
||||
} else if (msg instanceof MetricRequest) {
|
||||
metric = ((MetricRequest) msg).getMetric().getName();
|
||||
k = metricResolver.getHostPortKeyIngest(metric);
|
||||
} else {
|
||||
k = metricResolver.getHostPortKey(null);
|
||||
}
|
||||
k = metricResolver.getHostPortKey(metric);
|
||||
client = httpClientPool.borrowObject(k);
|
||||
|
||||
List<Map.Entry<String, String>> headerList = request.headers().entries();
|
||||
List<Header> relayedHeaderList = new ArrayList<>();
|
||||
|
||||
for (Map.Entry<String, String> h : headerList) {
|
||||
if (!h.getKey().equals(CONTENT_LENGTH)) {
|
||||
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
|
||||
}
|
||||
}
|
||||
Header[] headerArray = new Header[relayedHeaderList.size()];
|
||||
relayedHeaderList.toArray(headerArray);
|
||||
|
||||
String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
|
||||
if (request.getMethod().equals(HttpMethod.GET)) {
|
||||
HttpUriRequest relayedRequest = new HttpGet(relayURI);
|
||||
if (headerArray.length > 0) {
|
||||
relayedRequest.setHeaders(headerArray);
|
||||
}
|
||||
relayedResponse = client.execute(relayedRequest);
|
||||
|
||||
} else if (request.getMethod().equals(HttpMethod.POST)) {
|
||||
HttpPost relayedRequest = new HttpPost(relayURI);
|
||||
|
||||
String content = request.content().toString(StandardCharsets.UTF_8);
|
||||
StringEntity entity = new StringEntity(content);
|
||||
relayedRequest.setEntity(entity);
|
||||
if (headerArray.length > 0) {
|
||||
relayedRequest.setHeaders(headerArray);
|
||||
}
|
||||
relayedResponse = client.execute(relayedRequest);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
if (message == null) {
|
||||
LOG.error("", e);
|
||||
} else {
|
||||
if (message.contains("No matching tags")) {
|
||||
LOG.trace(message);
|
||||
} else {
|
||||
LOG.error(message, e);
|
||||
}
|
||||
}
|
||||
this.sendHttpError(
|
||||
ctx,
|
||||
new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), e.getMessage(), e
|
||||
.getLocalizedMessage(), e));
|
||||
return;
|
||||
} finally {
|
||||
if (request != null && request instanceof AutoCloseable) {
|
||||
ReferenceCountUtil.release(request);
|
||||
}
|
||||
}
|
||||
FullHttpResponse response = null;
|
||||
if (relayedResponse == null) {
|
||||
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
|
||||
"Method not allowed", "Method not allowed"));
|
||||
} else {
|
||||
// buf = JsonUtil.getObjectMapper().writeValueAsBytes(msg);
|
||||
ByteArrayOutputStream baos = new MyByteArrayOutputStream();
|
||||
StreamUtils.copy(relayedResponse.getEntity().getContent(), baos);
|
||||
|
||||
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(relayedResponse
|
||||
.getStatusLine().getStatusCode()), Unpooled.copiedBuffer(baos.toByteArray()));
|
||||
for (Header h : relayedResponse.getAllHeaders()) {
|
||||
response.headers().add(h.getName(), h.getValue());
|
||||
}
|
||||
// response.headers().set(HttpHeaders.Names.CONTENT_TYPE,
|
||||
// Constants.JSON_TYPE);
|
||||
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
|
||||
sendResponse(ctx, response);
|
||||
}
|
||||
} finally {
|
||||
if (relayedResponse != null) {
|
||||
relayedResponse.close();
|
||||
}
|
||||
if (client != null && k != null) {
|
||||
httpClientPool.returnObject(k, client);
|
||||
} else {
|
||||
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static public class MyByteArrayOutputStream extends ByteArrayOutputStream {
|
||||
|
||||
public MyByteArrayOutputStream() {
|
||||
}
|
||||
|
||||
public MyByteArrayOutputStream(int size) {
|
||||
super(size);
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
|
||||
public byte[] getBuf() {
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
|
||||
static public String encodeURI(String s) {
|
||||
try {
|
||||
s = s.replaceAll("\\{", URLEncoder.encode("{", "UTF-8"));
|
||||
s = s.replaceAll("\\}", URLEncoder.encode("}", "UTF-8"));
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
String s = "https://127.0.0.1:4243/api/query?start=1356998400000&end=1528398304000&m=sum:rate{false,100,0}:sys.thermal.gauge{class=aws}";
|
||||
URI uri = URI.create(encodeURI(s));
|
||||
|
||||
System.out.println(uri.toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package timely.balancer.netty.tcp;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.api.request.MetricRequest;
|
||||
import timely.api.request.TcpRequest;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.tcp.TcpClientPool;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
import timely.client.tcp.TcpClient;
|
||||
import timely.netty.Constants;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(TcpRelayHandler.class);
|
||||
private static final String LOG_ERR_MSG = "Error storing put metric: {}";
|
||||
private static final String ERR_MSG = "Error storing put metric: ";
|
||||
|
||||
private MetricResolver metricResolver;
|
||||
private TcpClientPool tcpClientPool;
|
||||
|
||||
public TcpRelayHandler(MetricResolver metricResolver, TcpClientPool tcpClientPool) {
|
||||
this.metricResolver = metricResolver;
|
||||
this.tcpClientPool = tcpClientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, TcpRequest msg) throws Exception {
|
||||
LOG.trace("Received {}", msg);
|
||||
try {
|
||||
String line;
|
||||
Pair<TimelyBalancedHost, TcpClient> keyClientPair = null;
|
||||
try {
|
||||
if (msg instanceof MetricRequest) {
|
||||
String metricName = ((MetricRequest) msg).getMetric().getName();
|
||||
line = ((MetricRequest) msg).getLine();
|
||||
keyClientPair = getClient(metricName, true);
|
||||
} else {
|
||||
// Version request
|
||||
line = "version";
|
||||
keyClientPair = getClient(null, false);
|
||||
}
|
||||
keyClientPair.getRight().write(line + "\n");
|
||||
keyClientPair.getRight().flush();
|
||||
} finally {
|
||||
if (keyClientPair != null && keyClientPair.getLeft() != null && keyClientPair.getRight() != null) {
|
||||
tcpClientPool.returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error(LOG_ERR_MSG, msg, e);
|
||||
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n")
|
||||
.getBytes(StandardCharsets.UTF_8)));
|
||||
if (!cf.isSuccess()) {
|
||||
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<TimelyBalancedHost, TcpClient> getClient(String metric, boolean metricRequest) {
|
||||
TcpClient client = null;
|
||||
TimelyBalancedHost k = null;
|
||||
int failures = 0;
|
||||
while (client == null) {
|
||||
try {
|
||||
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver
|
||||
.getHostPortKey(metric);
|
||||
client = tcpClientPool.borrowObject(k);
|
||||
} catch (Exception e1) {
|
||||
failures++;
|
||||
client = null;
|
||||
LOG.error(e1.getMessage(), e1);
|
||||
try {
|
||||
Thread.sleep(failures < 10 ? 500 : 60000);
|
||||
} catch (InterruptedException e2) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair.of(k, client);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package timely.balancer.netty.udp;
|
||||
|
||||
import io.netty.buffer.Unpooled;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.api.request.MetricRequest;
|
||||
import timely.api.request.UdpRequest;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.udp.UdpClientPool;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
import timely.client.udp.UdpClient;
|
||||
import timely.netty.Constants;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
public class UdpRelayHandler extends SimpleChannelInboundHandler<UdpRequest> {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UdpRelayHandler.class);
|
||||
private static final String LOG_ERR_MSG = "Error storing put metric: {}";
|
||||
private static final String ERR_MSG = "Error storing put metric: ";
|
||||
|
||||
private MetricResolver metricResolver;
|
||||
private UdpClientPool udpClientPool;
|
||||
|
||||
public UdpRelayHandler(MetricResolver metricResolver, UdpClientPool udpClientPool) {
|
||||
this.metricResolver = metricResolver;
|
||||
this.udpClientPool = udpClientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, UdpRequest msg) throws Exception {
|
||||
LOG.trace("Received {}", msg);
|
||||
try {
|
||||
MetricRequest metricRequest = (MetricRequest) msg;
|
||||
String metricName = metricRequest.getMetric().getName();
|
||||
Pair<TimelyBalancedHost, UdpClient> keyClientPair = null;
|
||||
try {
|
||||
keyClientPair = getClient(metricName, true);
|
||||
keyClientPair.getRight().write(metricRequest.getLine() + "\n");
|
||||
keyClientPair.getRight().flush();
|
||||
} finally {
|
||||
if (keyClientPair != null && keyClientPair.getLeft() != null && keyClientPair.getRight() != null) {
|
||||
udpClientPool.returnObject(keyClientPair.getLeft(), keyClientPair.getRight());
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error(LOG_ERR_MSG, msg, e);
|
||||
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n")
|
||||
.getBytes(StandardCharsets.UTF_8)));
|
||||
if (!cf.isSuccess()) {
|
||||
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Pair<TimelyBalancedHost, UdpClient> getClient(String metric, boolean metricRequest) {
|
||||
UdpClient client = null;
|
||||
TimelyBalancedHost k = null;
|
||||
int failures = 0;
|
||||
while (client == null) {
|
||||
try {
|
||||
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver
|
||||
.getHostPortKey(metric);
|
||||
client = udpClientPool.borrowObject(k);
|
||||
} catch (Exception e1) {
|
||||
failures++;
|
||||
client = null;
|
||||
LOG.error(e1.getMessage(), e1);
|
||||
try {
|
||||
Thread.sleep(failures < 10 ? 500 : 60000);
|
||||
} catch (InterruptedException e2) {
|
||||
// nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair.of(k, client);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package timely.balancer.netty.ws;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import io.netty.util.concurrent.ScheduledFuture;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.api.response.TimelyException;
|
||||
import timely.client.websocket.ClientHandler;
|
||||
|
||||
import javax.websocket.CloseReason;
|
||||
import javax.websocket.EndpointConfig;
|
||||
import javax.websocket.MessageHandler;
|
||||
import javax.websocket.Session;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class WsClientHandler extends ClientHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WsClientHandler.class);
|
||||
private final ChannelHandlerContext ctx;
|
||||
private ScheduledFuture<?> ping;
|
||||
|
||||
public WsClientHandler(ChannelHandlerContext ctx, int pingRate) {
|
||||
this.ctx = ctx;
|
||||
this.ping = this.ctx.executor().scheduleAtFixedRate(() -> {
|
||||
LOG.trace("Sending ping on channel {}", ctx.channel());
|
||||
ctx.writeAndFlush(new PingWebSocketFrame());
|
||||
}, pingRate, pingRate, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onOpen(Session session, EndpointConfig config) {
|
||||
session.addMessageHandler((MessageHandler.Whole<String>) message -> {
|
||||
ctx.writeAndFlush(new TextWebSocketFrame(message));
|
||||
LOG.debug("Message received on Websocket session {}: {}", session.getId(), message);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose(Session session, CloseReason reason) {
|
||||
super.onClose(session, reason);
|
||||
if (!reason.getCloseCode().equals(CloseReason.CloseCodes.NORMAL_CLOSURE)) {
|
||||
LOG.error("Abnormal close: " + reason.getReasonPhrase());
|
||||
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
|
||||
reason.getReasonPhrase(), "");
|
||||
WsRelayHandler.sendErrorResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e);
|
||||
}
|
||||
if (!ping.isCancelled()) {
|
||||
ping.cancel(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(Session session, Throwable t) {
|
||||
super.onError(session, t);
|
||||
LOG.error(t.getMessage(), t);
|
||||
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), t.getMessage(), "");
|
||||
WsRelayHandler.sendErrorResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e);
|
||||
if (!ping.isCancelled()) {
|
||||
ping.cancel(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package timely.balancer.netty.ws;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.ws.WsClientPool;
|
||||
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
public class WsClientHolder {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WsClientHolder.class);
|
||||
private WebSocketSubscriptionClient client = null;
|
||||
private TimelyBalancedHost host = null;
|
||||
|
||||
public WsClientHolder(TimelyBalancedHost host, WebSocketSubscriptionClient client) {
|
||||
this.host = host;
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
synchronized public WebSocketSubscriptionClient getClient() {
|
||||
return client;
|
||||
}
|
||||
|
||||
synchronized public TimelyBalancedHost getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
synchronized public void close(WsClientPool wsClientPool) {
|
||||
if (client != null && host != null) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error closing web socket client", e);
|
||||
}
|
||||
wsClientPool.returnObject(host, client);
|
||||
client = null;
|
||||
host = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package timely.balancer.netty.ws;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
|
||||
import io.netty.channel.ChannelFuture;
|
||||
import io.netty.channel.ChannelFutureListener;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
|
||||
import io.netty.handler.codec.http.HttpResponseStatus;
|
||||
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
|
||||
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.Configuration;
|
||||
import timely.api.request.subscription.AddSubscription;
|
||||
import timely.api.request.subscription.CloseSubscription;
|
||||
import timely.api.request.subscription.CreateSubscription;
|
||||
import timely.api.request.subscription.RemoveSubscription;
|
||||
import timely.api.request.subscription.SubscriptionRequest;
|
||||
import timely.api.response.TimelyException;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.connection.ws.WsClientPool;
|
||||
import timely.balancer.resolver.MetricResolver;
|
||||
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
|
||||
import timely.netty.http.TimelyHttpHandler;
|
||||
|
||||
import java.net.ConnectException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequest> implements TimelyHttpHandler {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(WsRelayHandler.class);
|
||||
|
||||
private final BalancerConfiguration balancerConfig;
|
||||
private final Configuration config;
|
||||
private final WsClientPool wsClientPool;
|
||||
private MetricResolver metricResolver;
|
||||
static private ObjectMapper mapper;
|
||||
Map<String, Map<String, WsClientHolder>> wsClients = new ConcurrentHashMap<>();
|
||||
Map<String, ChannelHandlerContext> wsSubscriptions = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
mapper = new ObjectMapper();
|
||||
mapper.registerModule(new Jdk8Module());
|
||||
mapper.configure(SerializationFeature.INDENT_OUTPUT, true);
|
||||
mapper.configure(SerializationFeature.WRAP_EXCEPTIONS, true);
|
||||
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
|
||||
}
|
||||
|
||||
public WsRelayHandler(BalancerConfiguration balancerConfig, Configuration config, MetricResolver metricResolver,
|
||||
WsClientPool wsClientPool) {
|
||||
this.balancerConfig = balancerConfig;
|
||||
this.config = config;
|
||||
this.metricResolver = metricResolver;
|
||||
this.wsClientPool = wsClientPool;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void channelRead0(ChannelHandlerContext ctx, SubscriptionRequest msg) throws Exception {
|
||||
TimelyBalancedHost k;
|
||||
ChannelHandlerContext origContext = null;
|
||||
String subscriptionId;
|
||||
try {
|
||||
String metric = null;
|
||||
if (msg instanceof CreateSubscription) {
|
||||
CreateSubscription create = (CreateSubscription) msg;
|
||||
final String currentSubscriptionId = create.getSubscriptionId();
|
||||
subscriptionId = currentSubscriptionId;
|
||||
wsSubscriptions.put(subscriptionId, ctx);
|
||||
// WebSocketSubscriptionClient sends the createSubscription when
|
||||
// we call open
|
||||
// We don't know where to send it to yet anyway because that
|
||||
// depends on the metric.
|
||||
ctx.channel().closeFuture().addListener(new ChannelFutureListener() {
|
||||
|
||||
@Override
|
||||
public void operationComplete(ChannelFuture future) throws Exception {
|
||||
Map<String, WsClientHolder> metricToClientMap = wsClients.get(currentSubscriptionId);
|
||||
|
||||
if (metricToClientMap != null) {
|
||||
synchronized (metricToClientMap) {
|
||||
// close all clients for this subscriptionId
|
||||
LOG.info("Channel closed, closing subscriptions for subscriptionId:%s",
|
||||
currentSubscriptionId);
|
||||
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
|
||||
entry.getValue().close(wsClientPool);
|
||||
}
|
||||
wsClients.remove(currentSubscriptionId);
|
||||
wsSubscriptions.remove(currentSubscriptionId);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
} else if (msg instanceof AddSubscription) {
|
||||
AddSubscription add = (AddSubscription) msg;
|
||||
metric = add.getMetric();
|
||||
subscriptionId = add.getSubscriptionId();
|
||||
origContext = wsSubscriptions.get(subscriptionId);
|
||||
if (origContext == null) {
|
||||
// send error because user never sent a CreateSubscription
|
||||
// and we have nowhere to send the results
|
||||
LOG.info("ChannelHandlerContext not found for subscriptionId:%s - createSubscription not called?",
|
||||
subscriptionId);
|
||||
sendErrorResponse(ctx, HttpResponseStatus.BAD_REQUEST, new IllegalArgumentException(
|
||||
"Must call create first"));
|
||||
}
|
||||
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
|
||||
if (metricToClientMap == null) {
|
||||
metricToClientMap = new HashMap<>();
|
||||
wsClients.put(subscriptionId, metricToClientMap);
|
||||
}
|
||||
WebSocketSubscriptionClient client = null;
|
||||
synchronized (metricToClientMap) {
|
||||
WsClientHolder hostClientHolder = metricToClientMap.get(metric);
|
||||
if (hostClientHolder == null) {
|
||||
k = metricResolver.getHostPortKey(metric);
|
||||
client = wsClientPool.borrowObject(k);
|
||||
client.open(new WsClientHandler(origContext, (config.getWebsocket().getTimeout() / 2)));
|
||||
metricToClientMap.put(metric, new WsClientHolder(k, client));
|
||||
} else {
|
||||
client = hostClientHolder.getClient();
|
||||
}
|
||||
}
|
||||
Map<String, String> tags = null;
|
||||
|
||||
Long startTime = 0L;
|
||||
Long endTime = 0L;
|
||||
Long delayTime = 5000L;
|
||||
if (add.getTags().isPresent()) {
|
||||
tags = add.getTags().get();
|
||||
}
|
||||
if (add.getStartTime().isPresent()) {
|
||||
startTime = add.getStartTime().get();
|
||||
}
|
||||
if (add.getEndTime().isPresent()) {
|
||||
endTime = add.getEndTime().get();
|
||||
}
|
||||
if (add.getDelayTime().isPresent()) {
|
||||
delayTime = add.getDelayTime().get();
|
||||
}
|
||||
|
||||
client.addSubscription(add.getMetric(), tags, startTime, endTime, delayTime);
|
||||
|
||||
} else if (msg instanceof RemoveSubscription) {
|
||||
RemoveSubscription remove = (RemoveSubscription) msg;
|
||||
metric = remove.getMetric();
|
||||
subscriptionId = remove.getSubscriptionId();
|
||||
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
|
||||
if (metricToClientMap != null) {
|
||||
synchronized (metricToClientMap) {
|
||||
WsClientHolder hostClientHolder = metricToClientMap.get(metric);
|
||||
if (hostClientHolder != null) {
|
||||
hostClientHolder.getClient().removeSubscription(metric);
|
||||
} else {
|
||||
LOG.info("client not found for subscriptionId:%s metric:%s", subscriptionId, metric);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (msg instanceof CloseSubscription) {
|
||||
CloseSubscription close = (CloseSubscription) msg;
|
||||
subscriptionId = close.getSubscriptionId();
|
||||
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
|
||||
if (metricToClientMap != null) {
|
||||
synchronized (metricToClientMap) {
|
||||
// close all clients for this subscriptionId
|
||||
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
|
||||
entry.getValue().close(wsClientPool);
|
||||
}
|
||||
}
|
||||
wsClients.remove(subscriptionId);
|
||||
wsSubscriptions.remove(subscriptionId);
|
||||
}
|
||||
ctx.writeAndFlush(new CloseWebSocketFrame(1000, "Client requested close."));
|
||||
}
|
||||
} catch (ConnectException e1) {
|
||||
LOG.error(e1.getMessage(), e1);
|
||||
ChannelHandlerContext errorCtx = origContext == null ? ctx : origContext;
|
||||
sendErrorResponse(errorCtx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e1);
|
||||
} catch (Exception e2) {
|
||||
LOG.error(e2.getMessage(), e2);
|
||||
ChannelHandlerContext errorCtx = origContext == null ? ctx : origContext;
|
||||
sendErrorResponse(errorCtx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e2);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
static public void sendErrorResponse(ChannelHandlerContext ctx, HttpResponseStatus status, Exception ex) {
|
||||
|
||||
try {
|
||||
TimelyException te = new TimelyException(status.code(), ex.getMessage(), ex.getMessage());
|
||||
String json = mapper.writeValueAsString(te);
|
||||
ctx.writeAndFlush(new TextWebSocketFrame(json));
|
||||
} catch (JsonProcessingException e) {
|
||||
LOG.error("Error serializing exception");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,297 @@
|
||||
package timely.balancer.resolver;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.balancer.ArrivalRate;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.healthcheck.HealthChecker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class BalancedMetricResolver implements MetricResolver {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(BalancedMetricResolver.class);
|
||||
|
||||
private Map<String, TimelyBalancedHost> metricToHostMap = new HashMap<>();
|
||||
private Map<Integer, TimelyBalancedHost> serverMap = new HashMap<>();
|
||||
private Map<String, ArrivalRate> metricMap = new HashMap<>();
|
||||
private Random r = new Random();
|
||||
final private HealthChecker healthChecker;
|
||||
private Timer timer = new Timer("RebalanceTimer", true);
|
||||
private long balanceUntil = System.currentTimeMillis() + 1800000l;
|
||||
|
||||
public BalancedMetricResolver(BalancerConfiguration config, HealthChecker healthChecker) {
|
||||
int n = 0;
|
||||
synchronized (this) {
|
||||
for (TimelyBalancedHost h : config.getTimelyHosts()) {
|
||||
h.setConfig(config);
|
||||
serverMap.put(n++, h);
|
||||
}
|
||||
}
|
||||
this.healthChecker = healthChecker;
|
||||
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
rebalanceAllMetrics();
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}, 300000);
|
||||
|
||||
timer.schedule(new TimerTask() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
if (System.currentTimeMillis() < balanceUntil) {
|
||||
try {
|
||||
balance();
|
||||
} catch (Exception e) {
|
||||
LOG.error(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 600000, 120000);
|
||||
}
|
||||
|
||||
private TimelyBalancedHost getLeastUsedHost() {
|
||||
|
||||
Map<Double, TimelyBalancedHost> rateSortedHosts = new TreeMap<>();
|
||||
for (Map.Entry<Integer, TimelyBalancedHost> e : serverMap.entrySet()) {
|
||||
rateSortedHosts.put(e.getValue().getArrivalRate(), e.getValue());
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Double, TimelyBalancedHost>> itr = rateSortedHosts.entrySet().iterator();
|
||||
|
||||
TimelyBalancedHost tbh = null;
|
||||
|
||||
while (itr.hasNext() && tbh == null) {
|
||||
TimelyBalancedHost currentTBH = itr.next().getValue();
|
||||
if (currentTBH.isUp()) {
|
||||
tbh = currentTBH;
|
||||
}
|
||||
}
|
||||
return tbh;
|
||||
}
|
||||
|
||||
private TimelyBalancedHost getRandomHost(TimelyBalancedHost notThisOne) {
|
||||
|
||||
TimelyBalancedHost tbh = null;
|
||||
for (int x = 0; tbh == null && x < serverMap.size(); x++) {
|
||||
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
|
||||
if (!tbh.isUp()) {
|
||||
tbh = null;
|
||||
} else if (notThisOne != null && tbh.equals(notThisOne)) {
|
||||
tbh = null;
|
||||
}
|
||||
}
|
||||
return tbh;
|
||||
}
|
||||
|
||||
private TimelyBalancedHost getRoundRobinHost() {
|
||||
TimelyBalancedHost tbh;
|
||||
int x = metricToHostMap.size() % serverMap.size();
|
||||
tbh = serverMap.get(x);
|
||||
if (tbh.isUp()) {
|
||||
return tbh;
|
||||
} else {
|
||||
return getRandomHost(null);
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void rebalanceAllMetrics() {
|
||||
|
||||
Map<Double, String> rateSortedMetrics = new TreeMap<>();
|
||||
for (Map.Entry<String, ArrivalRate> e : metricMap.entrySet()) {
|
||||
rateSortedMetrics.put(e.getValue().getRate(), e.getKey());
|
||||
}
|
||||
metricToHostMap.clear();
|
||||
for (String m : rateSortedMetrics.values()) {
|
||||
metricToHostMap.put(m, getRoundRobinHost());
|
||||
}
|
||||
}
|
||||
|
||||
synchronized public void balance() {
|
||||
LOG.info("rebalancing begin");
|
||||
Map<Double, TimelyBalancedHost> ratedSortedHosts = new TreeMap<>();
|
||||
double totalArrivalRate = 0;
|
||||
for (Map.Entry<Integer, TimelyBalancedHost> e : serverMap.entrySet()) {
|
||||
ratedSortedHosts.put(e.getValue().getArrivalRate(), e.getValue());
|
||||
totalArrivalRate += e.getValue().getArrivalRate();
|
||||
}
|
||||
|
||||
Iterator<Map.Entry<Double, TimelyBalancedHost>> itr = ratedSortedHosts.entrySet().iterator();
|
||||
TimelyBalancedHost mostUsed = null;
|
||||
TimelyBalancedHost leastUsed = null;
|
||||
|
||||
while (itr.hasNext()) {
|
||||
TimelyBalancedHost currentTBH = itr.next().getValue();
|
||||
if (currentTBH.isUp()) {
|
||||
if (leastUsed == null) {
|
||||
leastUsed = currentTBH;
|
||||
mostUsed = currentTBH;
|
||||
} else {
|
||||
// should end up with the last server that is up
|
||||
mostUsed = currentTBH;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double averageArrivalRate = totalArrivalRate / serverMap.size();
|
||||
double highestArrivalRate = mostUsed.getArrivalRate();
|
||||
double lowestArrivalRate = leastUsed.getArrivalRate();
|
||||
|
||||
LOG.info("rebalancing high:{} avg:{} low:{}", highestArrivalRate, averageArrivalRate, lowestArrivalRate);
|
||||
|
||||
// 5% over average
|
||||
int numReassigned = 0;
|
||||
if (highestArrivalRate > averageArrivalRate * 1.05) {
|
||||
LOG.info("rebalancing: high > 5% higher than average");
|
||||
// sort metrics by rate
|
||||
Map<Double, String> rateSortedMetrics = new TreeMap<>();
|
||||
for (Map.Entry<String, ArrivalRate> e : metricMap.entrySet()) {
|
||||
rateSortedMetrics.put(e.getValue().getRate(), e.getKey());
|
||||
}
|
||||
|
||||
double desiredDeltaHighest = (highestArrivalRate - averageArrivalRate) * 0.1;
|
||||
double desiredDeltaLowest = (averageArrivalRate - lowestArrivalRate) * 0.1;
|
||||
Iterator<Map.Entry<Double, String>> metricItr = rateSortedMetrics.entrySet().iterator();
|
||||
boolean done = false;
|
||||
LOG.info("rebalancing: desiredDeltaHighest:{} desiredDeltaLowest:{} rateSortedMetrics.size():{}",
|
||||
desiredDeltaHighest, desiredDeltaLowest, rateSortedMetrics.size());
|
||||
// advance to halfway
|
||||
for (int numMetric = 0; metricItr.hasNext() && numMetric <= rateSortedMetrics.size() / 2; numMetric++) {
|
||||
metricItr.next();
|
||||
}
|
||||
long maxToReassign = Math.round(((double) metricMap.size() / serverMap.size()) * 0.20);
|
||||
while (!done && metricItr.hasNext() && numReassigned < maxToReassign) {
|
||||
Map.Entry<Double, String> current = metricItr.next();
|
||||
Double currentRate = current.getKey();
|
||||
String currentMetric = current.getValue();
|
||||
|
||||
if (desiredDeltaHighest > 0) {
|
||||
if (metricToHostMap.get(currentMetric).equals(mostUsed)) {
|
||||
LOG.debug("rebalancing: trying to reassign metric {} from server {}:{}", currentMetric,
|
||||
mostUsed.getHost(), mostUsed.getTcpPort());
|
||||
if (desiredDeltaHighest > 0) {
|
||||
numReassigned++;
|
||||
metricToHostMap.put(currentMetric, leastUsed);
|
||||
LOG.debug("rebalancing: reassigning metric {} from server {}:{} to {}:{}", currentMetric,
|
||||
mostUsed.getHost(), mostUsed.getTcpPort(), leastUsed.getHost(),
|
||||
leastUsed.getTcpPort());
|
||||
desiredDeltaLowest -= currentRate;
|
||||
desiredDeltaHighest -= currentRate;
|
||||
} else {
|
||||
TimelyBalancedHost tbh = getRandomHost(mostUsed);
|
||||
if (tbh != null) {
|
||||
numReassigned++;
|
||||
metricToHostMap.put(currentMetric, tbh);
|
||||
LOG.info("rebalancing: reassigning metric {} from server {}:{} to {}:{}",
|
||||
currentMetric, mostUsed.getHost(), mostUsed.getTcpPort(), tbh.getHost(),
|
||||
tbh.getTcpPort());
|
||||
desiredDeltaHighest -= currentRate;
|
||||
} else {
|
||||
LOG.debug(
|
||||
"rebalancing: unable to reassign metric {} from server {}:{} - getRandomHost returned null",
|
||||
currentMetric, mostUsed.getHost(), mostUsed.getTcpPort());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG.info("rebalancing end - reassigned {}", numReassigned);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelyBalancedHost getHostPortKeyIngest(String metric) {
|
||||
if (StringUtils.isNotBlank(metric)) {
|
||||
ArrivalRate rate;
|
||||
rate = metricMap.get(metric);
|
||||
if (rate == null) {
|
||||
rate = new ArrivalRate();
|
||||
metricMap.put(metric, rate);
|
||||
}
|
||||
rate.arrived();
|
||||
}
|
||||
|
||||
TimelyBalancedHost tbh;
|
||||
if (StringUtils.isBlank(metric)) {
|
||||
tbh = getRandomHost(null);
|
||||
} else {
|
||||
tbh = metricToHostMap.get(metric);
|
||||
if (tbh == null) {
|
||||
tbh = getRoundRobinHost();
|
||||
metricToHostMap.put(metric, tbh);
|
||||
} else if (!tbh.isUp()) {
|
||||
TimelyBalancedHost oldTbh = tbh;
|
||||
tbh = getLeastUsedHost();
|
||||
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}",
|
||||
metric, oldTbh.getHost(), oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort());
|
||||
metricToHostMap.put(metric, tbh);
|
||||
}
|
||||
}
|
||||
|
||||
// if all else fails
|
||||
if (tbh == null || !tbh.isUp()) {
|
||||
for (TimelyBalancedHost h : serverMap.values()) {
|
||||
if (h.isUp()) {
|
||||
tbh = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tbh != null && StringUtils.isNotBlank(metric)) {
|
||||
metricToHostMap.put(metric, tbh);
|
||||
}
|
||||
}
|
||||
if (tbh != null) {
|
||||
tbh.arrived();
|
||||
tbh.calculateRate();
|
||||
}
|
||||
return tbh;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelyBalancedHost getHostPortKey(String metric) {
|
||||
TimelyBalancedHost tbh = null;
|
||||
if (StringUtils.isNotBlank(metric)) {
|
||||
tbh = metricToHostMap.get(metric);
|
||||
}
|
||||
|
||||
if (tbh == null || !tbh.isUp()) {
|
||||
for (int x = 0; tbh == null && x < serverMap.size(); x++) {
|
||||
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
|
||||
if (!tbh.isUp()) {
|
||||
tbh = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if all else fails
|
||||
if (tbh == null || !tbh.isUp()) {
|
||||
for (TimelyBalancedHost h : serverMap.values()) {
|
||||
if (h.isUp()) {
|
||||
tbh = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (tbh != null && StringUtils.isNotBlank(metric)) {
|
||||
metricToHostMap.put(metric, tbh);
|
||||
}
|
||||
}
|
||||
return tbh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package timely.balancer.resolver;
|
||||
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
|
||||
public interface MetricResolver {
|
||||
|
||||
public TimelyBalancedHost getHostPortKeyIngest(String metric);
|
||||
|
||||
public TimelyBalancedHost getHostPortKey(String metric);
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package timely.balancer.resolver;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import timely.balancer.BalancerConfiguration;
|
||||
import timely.balancer.connection.TimelyBalancedHost;
|
||||
import timely.balancer.healthcheck.HealthChecker;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
|
||||
public class RegexMetricResolver implements MetricResolver {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(RegexMetricResolver.class);
|
||||
|
||||
private Map<Integer, TimelyBalancedHost> serverMap = new HashMap<>();
|
||||
private Random r = new Random();
|
||||
final private HealthChecker healthChecker;
|
||||
|
||||
public RegexMetricResolver(BalancerConfiguration config, HealthChecker healthChecker) {
|
||||
int n = 0;
|
||||
for (TimelyBalancedHost h : config.getTimelyHosts()) {
|
||||
h.setConfig(config);
|
||||
serverMap.put(n++, h);
|
||||
}
|
||||
this.healthChecker = healthChecker;
|
||||
}
|
||||
|
||||
public TimelyBalancedHost getHostPortKeyInternal(String metric) {
|
||||
|
||||
TimelyBalancedHost tbh = null;
|
||||
if (StringUtils.isNotBlank(metric)) {
|
||||
for (TimelyBalancedHost h : serverMap.values()) {
|
||||
if (metric.matches(h.getRegex()) && h.isUp()) {
|
||||
tbh = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (tbh == null) {
|
||||
tbh = getFallbackHost(metric);
|
||||
}
|
||||
|
||||
LOG.info("routing metric: " + metric + " to " + tbh.getHost() + ":" + tbh.getTcpPort());
|
||||
return tbh;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelyBalancedHost getHostPortKeyIngest(String metric) {
|
||||
return getHostPortKeyInternal(metric);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimelyBalancedHost getHostPortKey(String metric) {
|
||||
return getHostPortKeyInternal(metric);
|
||||
}
|
||||
|
||||
private TimelyBalancedHost getFallbackHost(String metric) {
|
||||
|
||||
TimelyBalancedHost tbh;
|
||||
if (StringUtils.isBlank(metric)) {
|
||||
do {
|
||||
tbh = serverMap.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverMap.size());
|
||||
} while (!tbh.isUp());
|
||||
} else {
|
||||
// reverse metric so that similar metrics go to different hosts in a
|
||||
// reproducible way
|
||||
String reverseMetric = StringUtils.reverse(metric);
|
||||
int x = 0;
|
||||
do {
|
||||
// subtract a charter off of the end until we find a server that
|
||||
// is up
|
||||
int hash = reverseMetric.substring(x++).hashCode();
|
||||
tbh = serverMap.get(Math.abs(hash & Integer.MAX_VALUE) % serverMap.size());
|
||||
} while (!tbh.isUp());
|
||||
}
|
||||
|
||||
// if all else fails
|
||||
if (!tbh.isUp()) {
|
||||
for (TimelyBalancedHost h : serverMap.values()) {
|
||||
if (h.isUp()) {
|
||||
tbh = h;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return tbh;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
cd src/main/generated-java
|
||||
flatc --java ../resources/fb/metric.fbs
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace timely.api.flatbuffer;
|
||||
|
||||
file_identifier "TMFB";
|
||||
|
||||
table Tag {
|
||||
key:string;
|
||||
value:string;
|
||||
}
|
||||
|
||||
table Metric {
|
||||
name:string;
|
||||
timestamp:long;
|
||||
value:double;
|
||||
tags:[Tag];
|
||||
}
|
||||
|
||||
table Metrics {
|
||||
metrics:[Metric];
|
||||
}
|
||||
|
||||
root_type Metrics;
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<FindBugsFilter>
|
||||
<Match>
|
||||
<Class name="timely.balancer.Balancer" />
|
||||
<Method name="shutdown" />
|
||||
<Bug pattern="UC_USELESS_OBJECT" />
|
||||
</Match>
|
||||
<Match>
|
||||
<Class name="timely.balancer.netty.http.HttpRelayHandler$MyByteArrayOutputStream" />
|
||||
<Method name="getBuf" />
|
||||
<Bug pattern="EI_EXPOSE_REP" />
|
||||
</Match>
|
||||
</FindBugsFilter>
|
||||
+68
-1
@@ -5,8 +5,12 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<properties>
|
||||
<!-- PEP 440 compliant version gets supstituted into src/main/python/template/setup.py -->
|
||||
<python.client.version>1.0.1.dev0</python.client.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
@@ -99,8 +103,71 @@
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>buildnumber-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<artifactId>maven-resources-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>filter-setup</id>
|
||||
<goals>
|
||||
<goal>copy-resources</goal>
|
||||
</goals>
|
||||
<phase>process-resources</phase>
|
||||
<configuration>
|
||||
<outputDirectory>${project.basedir}/src/main/python</outputDirectory>
|
||||
<resources>
|
||||
<resource>
|
||||
<directory>${project.basedir}/src/main/python/template</directory>
|
||||
<includes>
|
||||
<include>setup.py</include>
|
||||
</includes>
|
||||
<filtering>true</filtering>
|
||||
</resource>
|
||||
</resources>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>python</id>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.codehaus.mojo</groupId>
|
||||
<artifactId>exec-maven-plugin</artifactId>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>python-dist</id>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<executable>python</executable>
|
||||
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
|
||||
<commandlineArgs>setup.py sdist --dist-dir ${project.build.directory}</commandlineArgs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>python-clean</id>
|
||||
<goals>
|
||||
<goal>exec</goal>
|
||||
</goals>
|
||||
<phase>package</phase>
|
||||
<configuration>
|
||||
<executable>rm</executable>
|
||||
<workingDirectory>${project.basedir}/src/main/python</workingDirectory>
|
||||
<commandlineArgs>-rf timely.egg-info setup.py</commandlineArgs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
</profile>
|
||||
</profiles>
|
||||
<reporting>
|
||||
<plugins>
|
||||
<plugin>
|
||||
|
||||
@@ -63,7 +63,7 @@ public class TcpClient implements AutoCloseable {
|
||||
*/
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
LOG.info("Shutting down connection to Timely at {}:{}", host, port);
|
||||
LOG.trace("Shutting down connection to Timely at {}:{}", host, port);
|
||||
if (null != sock) {
|
||||
try {
|
||||
if (null != out) {
|
||||
@@ -86,13 +86,13 @@ public class TcpClient implements AutoCloseable {
|
||||
sock = new Socket(host, port);
|
||||
out = new PrintWriter(sock.getOutputStream(), false);
|
||||
backoff = 2000;
|
||||
LOG.info("Connected to Timely at {}:{}", host, port);
|
||||
LOG.trace("Connected to Timely at {}:{}", host, port);
|
||||
} catch (IOException e) {
|
||||
LOG.error("Error connecting to Timely at {}:" + host + ":" + port + ". Error: " + e.getMessage());
|
||||
backoff = backoff * 2;
|
||||
sock = null;
|
||||
out = null;
|
||||
LOG.warn("Will retry connection in {} ms.", backoff);
|
||||
LOG.info("Will retry connection in {} ms.", backoff);
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
package timely.client.udp;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import static java.nio.charset.StandardCharsets.UTF_8;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -9,13 +12,19 @@ import java.net.InetSocketAddress;
|
||||
|
||||
public class UdpClient implements AutoCloseable {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(UdpClient.class);
|
||||
|
||||
private final String host;
|
||||
private final int port;
|
||||
private final InetSocketAddress address;
|
||||
private final DatagramPacket packet;
|
||||
private DatagramSocket sock;
|
||||
|
||||
public UdpClient(String hostname, int port) {
|
||||
this.address = new InetSocketAddress(hostname, port);
|
||||
public UdpClient(String host, int port) {
|
||||
this.address = new InetSocketAddress(host, port);
|
||||
this.packet = new DatagramPacket("".getBytes(UTF_8), 0, 0, address.getAddress(), port);
|
||||
this.host = host;
|
||||
this.port = port;
|
||||
}
|
||||
|
||||
public void open() throws IOException {
|
||||
@@ -29,6 +38,7 @@ public class UdpClient implements AutoCloseable {
|
||||
throw new IllegalStateException("Must call open first");
|
||||
}
|
||||
this.packet.setData(metric.getBytes(UTF_8));
|
||||
LOG.info("writing '" + metric + "' to " + this.host + ":" + this.port);
|
||||
this.sock.send(packet);
|
||||
}
|
||||
|
||||
@@ -38,6 +48,7 @@ public class UdpClient implements AutoCloseable {
|
||||
public void close() throws IOException {
|
||||
if (null != sock) {
|
||||
sock.close();
|
||||
sock = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,200 @@
|
||||
package timely.model;
|
||||
|
||||
import java.lang.reflect.Array;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* A simple interface that objects can implement to return the object size.
|
||||
*/
|
||||
public interface ObjectSizeOf {
|
||||
|
||||
/**
|
||||
* The (approximate) size of the object
|
||||
*/
|
||||
long sizeInBytes();
|
||||
|
||||
public static class ObjectInstance {
|
||||
|
||||
private Object o;
|
||||
|
||||
public ObjectInstance(Object _o) {
|
||||
this.o = _o;
|
||||
}
|
||||
|
||||
public Object getObject() {
|
||||
return o;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return System.identityHashCode(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (!(obj instanceof ObjectInstance)) {
|
||||
return false;
|
||||
}
|
||||
return ((ObjectInstance) obj).o == this.o;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Sizer {
|
||||
|
||||
private static final Logger log = Logger.getLogger(Sizer.class.getCanonicalName());
|
||||
public static final short OBJECT_OVERHEAD = 8;
|
||||
public static final short ARRAY_OVERHEAD = 12;
|
||||
public static final short REFERENCE = 4;
|
||||
// The size of the basic Number constructs (and Boolean and Character)
|
||||
// is 16: roundUp(8 + primitiveSize)
|
||||
public static final short NUMBER_SIZE = 16;
|
||||
|
||||
/**
|
||||
* Get the size of an object. Note that we want something relatively
|
||||
* fast that gives us an order of magnitude here. The java
|
||||
* Instrumentation agent mechanism is a little too costly for general
|
||||
* use here. This will look for the ObjectSizeOf interface and if
|
||||
* implemented on the object will use that. Otherwise it will do a
|
||||
* simple navigation of the fields using reflection.
|
||||
*
|
||||
* @param o
|
||||
* @return an approximation of the object size
|
||||
*/
|
||||
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
|
||||
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(),
|
||||
useSizeInBytesMethod, debug);
|
||||
return estSize;
|
||||
}
|
||||
|
||||
public static long getObjectSize(Object o) {
|
||||
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(), true, false);
|
||||
return estSize;
|
||||
}
|
||||
|
||||
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack,
|
||||
boolean useSizeInBytesMethod, boolean debug) {
|
||||
long totalSize = 0;
|
||||
stack.add(new ObjectInstance(o));
|
||||
while (!stack.isEmpty()) {
|
||||
long size = 0;
|
||||
ObjectInstance oi = stack.pop();
|
||||
o = oi.getObject();
|
||||
if (o != null && !visited.contains(oi)) {
|
||||
visited.add(oi);
|
||||
try {
|
||||
if (useSizeInBytesMethod) {
|
||||
try {
|
||||
if (o instanceof ObjectSizeOf) {
|
||||
size = ((ObjectSizeOf) o).sizeInBytes();
|
||||
} else {
|
||||
Method sizeInBytes = o.getClass().getMethod("sizeInBytes", (Class<?>[]) null);
|
||||
size = (Long) sizeInBytes.invoke(o);
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
// ok, lets do this the hard way...
|
||||
}
|
||||
}
|
||||
if (size == 0) {
|
||||
// the hard way...
|
||||
// do not include Class related objects or
|
||||
// reflection objects
|
||||
if ((o instanceof Class)
|
||||
|| (o instanceof ClassLoader)
|
||||
|| (o.getClass().getPackage() != null && o.getClass().getPackage().getName()
|
||||
.startsWith("java.lang.reflect"))) {
|
||||
size = 0;
|
||||
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
|
||||
size = NUMBER_SIZE;
|
||||
} else {
|
||||
// lets do a simple sizing
|
||||
Class<?> c = o.getClass();
|
||||
if (c.isArray()) {
|
||||
size = ARRAY_OVERHEAD;
|
||||
int length = Array.getLength(o);
|
||||
if (c.getComponentType().isPrimitive()) {
|
||||
size += length * getPrimitiveObjectSize(c.getComponentType());
|
||||
} else {
|
||||
size += length * REFERENCE;
|
||||
for (int i = 0; i < length; i++) {
|
||||
Object element = Array.get(o, i);
|
||||
if (element != null) {
|
||||
stack.add(new ObjectInstance(element));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size += OBJECT_OVERHEAD;
|
||||
while (c != null) {
|
||||
for (Field field : c.getDeclaredFields()) {
|
||||
if (Modifier.isStatic(field.getModifiers())) {
|
||||
continue;
|
||||
}
|
||||
if (field.getType().isPrimitive()) {
|
||||
size += getPrimitiveObjectSize(field.getType());
|
||||
} else {
|
||||
size += REFERENCE;
|
||||
boolean accessible = field.isAccessible();
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object fieldObject = field.get(o);
|
||||
if (fieldObject != null) {
|
||||
stack.push(new ObjectInstance(fieldObject));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
// cannot get to field, so
|
||||
// ignore it in this size
|
||||
// calculation
|
||||
e.printStackTrace();
|
||||
}
|
||||
field.setAccessible(accessible);
|
||||
}
|
||||
}
|
||||
c = c.getSuperclass();
|
||||
}
|
||||
}
|
||||
size = roundUp(size);
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
log.warning("Unable to determine object size for " + o);
|
||||
}
|
||||
if (debug) {
|
||||
System.out.println(o.getClass().getCanonicalName() + "=" + size);
|
||||
}
|
||||
}
|
||||
totalSize += size;
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
public static long roundUp(long size) {
|
||||
long extra = size % 8;
|
||||
if (extra > 0) {
|
||||
size = size + 8 - extra;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
public static short getPrimitiveObjectSize(Class<?> primitiveType) {
|
||||
if (primitiveType.equals(int.class) || primitiveType.equals(float.class)) {
|
||||
return 4;
|
||||
} else if (primitiveType.equals(boolean.class) || primitiveType.equals(byte.class)) {
|
||||
return 1;
|
||||
} else if (primitiveType.equals(char.class) || primitiveType.equals(short.class)) {
|
||||
return 2;
|
||||
} else if (primitiveType.equals(long.class) || primitiveType.equals(double.class)) {
|
||||
return 8;
|
||||
} else { // if (primitiveType.equals(void.class)) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,6 @@ import com.fasterxml.jackson.annotation.JsonAnyGetter;
|
||||
import com.fasterxml.jackson.annotation.JsonAnySetter;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import com.fasterxml.jackson.annotation.JsonRootName;
|
||||
import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.apache.commons.lang3.builder.CompareToBuilder;
|
||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import com.google.common.base.Splitter;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -11,18 +13,26 @@ import java.util.Map;
|
||||
/**
|
||||
* Parses tag1=val1,tag2=val2,tag3=val3 into a {@code List} of {@code Tag}
|
||||
* Combines {@code List} of {@code Tag} into {@code String} of
|
||||
* tag1=val1,tag2=val2,tag3=val3 NOTE: the tag and the value are both URL
|
||||
* encoded and subsequently URL decoded.
|
||||
* tag1=val1,tag2=val2,tag3=val3.
|
||||
*/
|
||||
public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag>> {
|
||||
|
||||
private static final Splitter commaSplitter = Splitter.on(",").omitEmptyStrings();
|
||||
private static final TagParser tagParser = new TagParser();
|
||||
|
||||
@Override
|
||||
public List<Tag> parse(String t) {
|
||||
ArrayList<Tag> tags = new ArrayList<>();
|
||||
commaSplitter.splitToList(t).forEach(p -> tags.add(tagParser.parse(p)));
|
||||
// only split on commas that are not preceded with a backslash
|
||||
String[] parts = t.split("(?<!\\\\),");
|
||||
for (String p : parts) {
|
||||
if (!p.isEmpty()) {
|
||||
Tag tag = tagParser.parse(p);
|
||||
// unescape commas in the key and value of the tag
|
||||
tag.setKey(tag.getKey().replaceAll("\\\\,", ","));
|
||||
tag.setValue(tag.getValue().replaceAll("\\\\,", ","));
|
||||
tags.add(tag);
|
||||
}
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,20 +4,15 @@ import com.google.common.base.Joiner;
|
||||
import com.google.common.base.Splitter;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URLDecoder;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Parses tag1=val1 into a {@code Tag} Combines {@code Tag} into {@code String}
|
||||
* of tag1=val1 NOTE: the tag and the value are both URL encoded and
|
||||
* subsequently URL decoded.
|
||||
* of tag1=val1
|
||||
*/
|
||||
public class TagParser implements Parser<Tag>, Combiner<Tag> {
|
||||
|
||||
private static final Splitter equalSplitter = Splitter.on("=").trimResults();
|
||||
private static final Splitter equalSplitter = Splitter.on("=").limit(2).trimResults();
|
||||
private static final Joiner equalsJoiner = Joiner.on("=");
|
||||
|
||||
@Override
|
||||
@@ -26,28 +21,12 @@ public class TagParser implements Parser<Tag>, Combiner<Tag> {
|
||||
if (parts.size() != 2) {
|
||||
throw new IllegalArgumentException("Invalid tag format: " + t);
|
||||
}
|
||||
return new Tag(unescape(parts.get(0)), unescape(parts.get(1)));
|
||||
return new Tag(parts.get(0), parts.get(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public String combine(Tag t) {
|
||||
return equalsJoiner.join(escape(t.getKey()), escape(t.getValue()));
|
||||
}
|
||||
|
||||
private String escape(String tag) {
|
||||
try {
|
||||
return URLEncoder.encode(tag, "UTF8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private String unescape(String tag) {
|
||||
try {
|
||||
return URLDecoder.decode(tag, "UTF8");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return equalsJoiner.join(t.getKey(), t.getValue());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,128 +0,0 @@
|
||||
import time
|
||||
from datetime import datetime
|
||||
from TimeDateRange import UTC
|
||||
import pandas
|
||||
import matplotlib.dates as mdates
|
||||
import plotly.graph_objs as go
|
||||
import plotly.offline as py
|
||||
|
||||
utc = UTC()
|
||||
|
||||
def pivot(df, metric, groupByColumn=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if groupByColumn is not None:
|
||||
dataFrame = dataFrame.pivot_table(index="date", columns=groupByColumn, values=metric)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def resample(df, sample, how='mean', fill_method='ffill'):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if sample is not None:
|
||||
dataFrame = dataFrame.resample(sample, how=how, fill_method=fill_method)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def unpivot(df, metric, groupByColumn=None):
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
dataFrame = pandas.melt(dataFrame, id_vars=['date'], value_name=metric, var_name=groupByColumn)
|
||||
dataFrame = dataFrame.set_index(dataFrame['date'])
|
||||
return dataFrame
|
||||
|
||||
|
||||
def rolling_average(df, metric, rolling_average=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if rolling_average is not None:
|
||||
dataFrame[metric] = pandas.rolling_mean(dataFrame[metric], rolling_average)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def graph(analyticConfig, df, metric, seriesConfig={}, graphConfig={}, notebook=False):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['colFromIndex'] = dataFrame.index
|
||||
if analyticConfig.groupByColumn is not None:
|
||||
# sort on date and the groupByColumn so that the series legend is sorted
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex', analyticConfig.groupByColumn])
|
||||
else:
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex'])
|
||||
|
||||
title = None
|
||||
if analyticConfig.sample is None:
|
||||
title = metric
|
||||
else:
|
||||
title = metric + '\rsample ' + analyticConfig.sample
|
||||
|
||||
dataFrame['date'] = dataFrame.index
|
||||
|
||||
layoutConfig = dict(
|
||||
title=title,
|
||||
autosize=False,
|
||||
width=1000,
|
||||
height=700,
|
||||
showlegend=True,
|
||||
xaxis=dict(
|
||||
autorange=True,
|
||||
tickangle=-45
|
||||
),
|
||||
yaxis=dict(
|
||||
autorange=True,
|
||||
title=title
|
||||
)
|
||||
)
|
||||
|
||||
layoutConfig.update(graphConfig)
|
||||
|
||||
layout = go.Layout(layoutConfig)
|
||||
|
||||
data = None
|
||||
for col in df[analyticConfig.groupByColumn].unique():
|
||||
tempDataFrame = dataFrame.loc[dataFrame[analyticConfig.groupByColumn] == col]
|
||||
|
||||
addlColConfig = graphConfig.get(col, graphConfig.get('default', dict()))
|
||||
|
||||
config = dict(
|
||||
name=col,
|
||||
x=tempDataFrame.index,
|
||||
y=tempDataFrame[metric],
|
||||
hoverinfo='x+y+text',
|
||||
text=col
|
||||
)
|
||||
|
||||
config.update(addlColConfig)
|
||||
|
||||
s = go.Scatter(config)
|
||||
|
||||
if data is None:
|
||||
data = [s]
|
||||
else:
|
||||
data.append(s)
|
||||
|
||||
now = datetime.now(tz=utc)
|
||||
format = "%Y%m%d%H%M%S"
|
||||
nowStr = time.strftime(format, now.timetuple())
|
||||
|
||||
fileNameBase = analyticConfig.output_dir + "/" + metric
|
||||
if analyticConfig.sample is not None:
|
||||
fileNameBase += "-" + analyticConfig.sample
|
||||
if analyticConfig.how is not None:
|
||||
fileNameBase += "-" + analyticConfig.how
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
if notebook:
|
||||
py.iplot(fig, filename=fileNameBase+'-'+nowStr, show_link=False)
|
||||
else:
|
||||
py.plot(fig, output_type='file', filename=fileNameBase+'-'+nowStr+'.html', auto_open=False, show_link=False)
|
||||
|
||||
|
||||
def datetime2graphdays(dt):
|
||||
|
||||
return mdates.epoch2num((time.mktime(dt.timetuple()) / 86400) * mdates.SEC_PER_DAY)
|
||||
@@ -1,123 +0,0 @@
|
||||
import smtplib
|
||||
from os.path import basename
|
||||
from email.mime.application import MIMEApplication
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import COMMASPACE, formatdate
|
||||
from TimelyMetric import graph
|
||||
import syslog
|
||||
|
||||
syslog.openlog("TimelyNotifications")
|
||||
|
||||
#def __init__(self, file, send_to, subject, text, analyticConfig, notebook):
|
||||
|
||||
|
||||
class TimelyAlert():
|
||||
|
||||
def __init__(self, timelyMetric, dataFrame, seriesConfig, analyticConfig, notebook):
|
||||
self.timelyMetric = timelyMetric
|
||||
self.dataFrame = dataFrame
|
||||
self.seriesConfig = seriesConfig
|
||||
self.analyticConfig = analyticConfig
|
||||
self.notebook = notebook
|
||||
|
||||
def createFilename(self, metric, config):
|
||||
|
||||
metricTitle = metric
|
||||
if config.sample is not None:
|
||||
sampleTitle = config.sample + "-" + config.how
|
||||
|
||||
filename = metricTitle
|
||||
if sampleTitle != "":
|
||||
filename += "-" + sampleTitle
|
||||
|
||||
return filename
|
||||
|
||||
def createTitle(self, metric, config):
|
||||
|
||||
if config.orCondition:
|
||||
condition = "OR"
|
||||
else:
|
||||
condition = "AND"
|
||||
|
||||
metricTitle = metric
|
||||
sampleTitle = ""
|
||||
alertTitle = ""
|
||||
if config.sample is not None:
|
||||
sampleTitle = config.sample + " " + config.how + " samples"
|
||||
|
||||
if config.min_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y < " + str(config.min_threshold) + ")"
|
||||
|
||||
if config.max_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y > " + str(config.max_threshold) + ")"
|
||||
|
||||
if config.alert_percentage is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
if config.alert_percentage > 0:
|
||||
alertTitle += "(y > " + str(config.alert_percentage) + "% above " + str(config.rolling_average) + " sample average)"
|
||||
else:
|
||||
alertTitle += "(y < " + str(-config.alert_percentage) + "% below " + str(config.rolling_average) + " sample average)"
|
||||
|
||||
title = metricTitle
|
||||
if sampleTitle != "":
|
||||
title += "\n" + sampleTitle
|
||||
|
||||
if alertTitle != "":
|
||||
title += "\n" + alertTitle
|
||||
|
||||
return title
|
||||
|
||||
def notify(self, send_from, send_to, server="127.0.0.1"):
|
||||
|
||||
self.graph()
|
||||
|
||||
return
|
||||
|
||||
def email(self, send_from, send_to, subject, text, files=None, server="127.0.0.1"):
|
||||
assert isinstance(send_to, list)
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = send_from
|
||||
msg['To'] = COMMASPACE.join(send_to)
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
msg['Subject'] = subject
|
||||
|
||||
msg.attach(MIMEText(text))
|
||||
|
||||
for f in files or []:
|
||||
with open(f, "rb") as fil:
|
||||
part = MIMEApplication(
|
||||
fil.read(),
|
||||
Name=basename(f)
|
||||
)
|
||||
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
|
||||
msg.attach(part)
|
||||
|
||||
|
||||
smtp = smtplib.SMTP(server)
|
||||
smtp.sendmail(send_from, send_to, msg.as_string())
|
||||
smtp.close()
|
||||
|
||||
def graph(self):
|
||||
|
||||
graphConfig = {}
|
||||
graphConfig["title"] = self.createTitle(self.timelyMetric.metric, self.analyticConfig)
|
||||
graph(self.analyticConfig, self.dataFrame, self.timelyMetric.metric, seriesConfig=self.seriesConfig, graphConfig=graphConfig, notebook=self.notebook)
|
||||
|
||||
|
||||
def main():
|
||||
#syslog.syslog(syslog.LOG_ALERT, "Example message")
|
||||
a = TimelyAlert(None, None, None, None, False)
|
||||
|
||||
a.email_alert("wwoley@localhost", ["wwoley@localhost"], "Test1", "This is a sendmail test")
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,105 +0,0 @@
|
||||
import TimelyMetric
|
||||
from TimeDateRange import TimeDateRange
|
||||
from TimeDateRange import UTC
|
||||
from datetime import datetime
|
||||
import pandas
|
||||
import re
|
||||
import plotly
|
||||
from TimelyAlert import TimelyAlert
|
||||
|
||||
utc = UTC()
|
||||
plotly.offline.init_notebook_mode()
|
||||
|
||||
def logTiming(timelyMetric, startTime, stopTime):
|
||||
df = timelyMetric.getDataFrame()
|
||||
print("start: " + str(startTime))
|
||||
print("stop: " + str(stopTime))
|
||||
print("num records: " + str(len(df)))
|
||||
elapsedSec = (TimeDateRange.unix_time_millis(stopTime) - TimeDateRange.unix_time_millis(startTime)) / 1000
|
||||
print("elapsed seconds: " + str(elapsedSec))
|
||||
recPerSec = len(df) / elapsedSec
|
||||
print("records per second: " + recPerSec)
|
||||
|
||||
def addCondition(orCondition, currResult, condition):
|
||||
if orCondition:
|
||||
currResult = currResult | condition
|
||||
else:
|
||||
currResult = currResult & condition
|
||||
return currResult
|
||||
|
||||
def find_alerts(timelyMetric, analyticConfig, notebook=False):
|
||||
|
||||
if analyticConfig.groupByColumn is None:
|
||||
return
|
||||
|
||||
df = timelyMetric.getDataFrame()
|
||||
|
||||
graphDF = TimelyMetric.pivot(df, timelyMetric.metric, groupByColumn=analyticConfig.groupByColumn)
|
||||
|
||||
if analyticConfig.sample is not None:
|
||||
graphDF = TimelyMetric.resample(graphDF, analyticConfig.sample, how=analyticConfig.how)
|
||||
|
||||
if analyticConfig.excludeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : not (re.search(analyticConfig.excludeColRegex, x)), axis=1)
|
||||
if analyticConfig.includeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : re.search(analyticConfig.includeColRegex, x), axis=1)
|
||||
|
||||
graphDF_avg = pandas.DataFrame(graphDF, copy=True)
|
||||
|
||||
combined = pandas.DataFrame()
|
||||
|
||||
seriesConfig = {}
|
||||
for i in graphDF_avg.columns:
|
||||
col = str(i)
|
||||
|
||||
result = True
|
||||
if analyticConfig.orCondition:
|
||||
result = False
|
||||
|
||||
if analyticConfig.min_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) < analyticConfig.min_threshold
|
||||
result = addCondition(analyticConfig.orCondition, result, currCondition)
|
||||
|
||||
if analyticConfig.max_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) > analyticConfig.max_threshold
|
||||
result = addCondition(analyticConfig.orCondition, result, currCondition)
|
||||
|
||||
graphDF_avg = TimelyMetric.rolling_average(graphDF_avg, str(i), rolling_average=analyticConfig.rolling_average)
|
||||
if (analyticConfig.alert_percentage is not None) and (analyticConfig.rolling_average is not None):
|
||||
if analyticConfig.alert_percentage > 0:
|
||||
multiple = 1.0 + (float(abs(analyticConfig.alert_percentage)) / float(100))
|
||||
currCondition = graphDF[col].astype(float) > (graphDF_avg[col].astype(float) * multiple)
|
||||
result = addCondition(analyticConfig.orCondition, result, currCondition)
|
||||
if analyticConfig.alert_percentage < 0:
|
||||
multiple = 1.0 - (float(abs(analyticConfig.alert_percentage)) / float(100))
|
||||
if multiple > 0:
|
||||
currCondition = graphDF[col].astype(float) < (graphDF_avg[col].astype(float) * multiple)
|
||||
result = addCondition(analyticConfig.orCondition, result, currCondition)
|
||||
|
||||
exceptional = graphDF.loc[result, col]
|
||||
if (analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and exceptional.size > 0):
|
||||
combined[col] = graphDF[col]
|
||||
|
||||
if (analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and exceptional.size > 0 and analyticConfig.alert_percentage is not None and analyticConfig.rolling_average is not None):
|
||||
combined[col+'_avg'] = graphDF_avg[col]
|
||||
|
||||
if (exceptional.size > 0):
|
||||
combined[col+'_warn'] = exceptional.dropna()
|
||||
seriesConfig[col+'_warn'] = {
|
||||
"mode" : "markers",
|
||||
"marker" : {
|
||||
"symbol" : "hash-open",
|
||||
"color" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
timelyAlert = None
|
||||
if not combined.empty:
|
||||
combined = TimelyMetric.unpivot(combined, timelyMetric.metric, groupByColumn=analyticConfig.groupByColumn)
|
||||
combined = combined.sort_index()
|
||||
combined = combined.sort_values(['date', analyticConfig.groupByColumn])
|
||||
timelyAlert = TimelyAlert(timelyMetric, combined, seriesConfig, analyticConfig, notebook)
|
||||
|
||||
return timelyAlert
|
||||
@@ -1,25 +0,0 @@
|
||||
|
||||
|
||||
|
||||
class TimelyAnalyticConfiguration():
|
||||
|
||||
|
||||
def __init__(self, analyticConfig):
|
||||
|
||||
self.groupByColumn = analyticConfig.get('groupByColumn', None)
|
||||
self.includeColRegex = analyticConfig.get('includeColRegex', None)
|
||||
self.excludeColRegex = analyticConfig.get('excludeColRegex', None)
|
||||
self.sample = analyticConfig.get('sample', None)
|
||||
self.how = analyticConfig.get('how', 'mean')
|
||||
self.rolling_average = analyticConfig.get('rolling_average', None)
|
||||
self.min_threshold = analyticConfig.get('min_threshold', None)
|
||||
self.max_threshold = analyticConfig.get('max_threshold', None)
|
||||
self.alert_percentage = analyticConfig.get('alert_percentage', None)
|
||||
boolean = analyticConfig.get('boolean', 'or')
|
||||
self.orCondition = boolean == 'or' or boolean == 'OR'
|
||||
# alerts or all
|
||||
self.display = analyticConfig.get('display', 'alerts')
|
||||
self.send_alerts_to = analyticConfig.get('send_alerts_to', [])
|
||||
self.output_dir = analyticConfig.get('output_dir', '/tmp')
|
||||
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
import TimelyMetric
|
||||
import TimelyAnalytic
|
||||
|
||||
hostport = "127.0.0.1:4244"
|
||||
timelyMetric = TimelyMetric.TimelyMetric(hostport, '', None, None, None, 48, None).fetch()
|
||||
TimelyAnalytic.find_alerts(timelyMetric, TimelyAnalyticConfiguration({
|
||||
'includeColRegex' : None,
|
||||
'excludeColRegex' : None,
|
||||
'groupByColumn' : 'instance',
|
||||
'sample' : '10min',
|
||||
'how' : 'mean',
|
||||
'rolling_average' : 12,
|
||||
'min_threshold' : None,
|
||||
'max_threshold' : None,
|
||||
'alert_percentage' : 25,
|
||||
'boolean' : 'and'
|
||||
'display' : 'all',
|
||||
'output_dir' : '/path/to/output'
|
||||
}))
|
||||
|
||||
if alert is not None:
|
||||
alert.graph()
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
from setuptools import setup
|
||||
|
||||
setup(
|
||||
name='timely',
|
||||
version='${python.client.version}',
|
||||
packages=['timely'],
|
||||
install_requires=[
|
||||
'seaborn',
|
||||
'plotly'
|
||||
],
|
||||
license='Apache License, Version 2.0'
|
||||
)
|
||||
@@ -0,0 +1,328 @@
|
||||
import time
|
||||
from datetime import timedelta
|
||||
from datetime import datetime
|
||||
from TimeDateRange import UTC
|
||||
import pandas
|
||||
import matplotlib.dates as mdates
|
||||
import plotly.graph_objs as go
|
||||
import plotly.offline as py
|
||||
import seaborn
|
||||
import matplotlib.ticker as tkr
|
||||
import numpy as np
|
||||
import os
|
||||
|
||||
utc = UTC()
|
||||
|
||||
def pivot(df, metric, groupByColumn=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if groupByColumn is not None:
|
||||
dataFrame["date"] = dataFrame.index.to_pydatetime()
|
||||
dataFrame = dataFrame.pivot_table(index="date", columns=groupByColumn, values=metric)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def resample(df, sample, how='mean'):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if sample is not None:
|
||||
dataFrame = dataFrame.resample(sample, how=how).interpolate()
|
||||
return dataFrame
|
||||
|
||||
|
||||
def unpivot(df, metric, groupByColumn=None):
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
dataFrame = pandas.melt(dataFrame, id_vars=['date'], value_name=metric, var_name=groupByColumn)
|
||||
dataFrame = dataFrame.set_index('date')
|
||||
return dataFrame
|
||||
|
||||
|
||||
def rolling_average(df, metric, rolling_average=None):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
if rolling_average is not None:
|
||||
dataFrame[metric] = pandas.rolling_mean(dataFrame[metric], rolling_average)
|
||||
return dataFrame
|
||||
|
||||
|
||||
def graph(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False, type='png'):
|
||||
if type == 'png':
|
||||
return graphSeaborn(analyticConfig, df, timelyMetric, seriesConfig=seriesConfig, graphConfig=graphConfig, notebook=notebook)
|
||||
else:
|
||||
return graphPlotly(analyticConfig, df, timelyMetric, seriesConfig=seriesConfig, graphConfig=graphConfig, notebook=notebook)
|
||||
|
||||
|
||||
def graphSeaborn(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame = ensureMinSeriesLength(dataFrame, analyticConfig.groupByColumn)
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
dataFrame['dateFloat'] = dataFrame['date'].apply(lambda x: datetime2graphdays(x))
|
||||
dataFrame['index'] = dataFrame['dateFloat']
|
||||
groupByColumn = analyticConfig.groupByColumn
|
||||
|
||||
if groupByColumn is not None:
|
||||
# sort on date and the groupByColumn so that the series legend is sorted
|
||||
dataFrame = dataFrame.sort_values(['index', analyticConfig.groupByColumn])
|
||||
else:
|
||||
dataFrame = dataFrame.sort_values(['index'])
|
||||
|
||||
#dataFrame = dataFrame.set_index('index', drop=True)
|
||||
dataFrame = dataFrame.dropna()
|
||||
seaborn.set(style="darkgrid", palette="Set2")
|
||||
fig, ax = seaborn.plt.subplots()
|
||||
|
||||
colors = np.random.random((len(dataFrame), 3))
|
||||
i = 0
|
||||
for col in df[analyticConfig.groupByColumn].unique():
|
||||
tempDataFrame = dataFrame.loc[dataFrame[analyticConfig.groupByColumn] == col]
|
||||
addlColConfig = seriesConfig.get(col, seriesConfig.get('default', dict()))
|
||||
markerDict = addlColConfig.get("marker", dict())
|
||||
color = markerDict.get("color", "")
|
||||
if color == "red":
|
||||
seaborn.tsplot(tempDataFrame, time="dateFloat", unit=None, interpolate=False, color="red", marker="d",
|
||||
estimator=np.nanmean, condition=groupByColumn, value=timelyMetric.metric, ax=ax)
|
||||
else:
|
||||
seaborn.tsplot(tempDataFrame, time="dateFloat", unit=None, interpolate=True, color=colors[i],
|
||||
estimator=np.nanmean, condition=groupByColumn, value=timelyMetric.metric, ax=ax)
|
||||
i = i + 1
|
||||
|
||||
ax.xaxis_date()
|
||||
ax.xaxis.set_major_locator(mdates.AutoDateLocator())
|
||||
ax.xaxis.set_major_formatter(mdates.DateFormatter('%Y/%m/%d %H:%M:%S'))
|
||||
ax.set_xlim(xmin=dataFrame['dateFloat'].min(), xmax=dataFrame['dateFloat'].max())
|
||||
|
||||
formatter = tkr.ScalarFormatter(useMathText=False)
|
||||
formatter.set_scientific(False)
|
||||
ax.yaxis.set_major_formatter(formatter)
|
||||
|
||||
graphTitle = getTitle(timelyMetric, analyticConfig)
|
||||
seaborn.plt.title(graphTitle, fontsize='large')
|
||||
seaborn.plt.xlabel('date/time')
|
||||
seaborn.plt.ylabel(timelyMetric.metric)
|
||||
|
||||
legendTitle = "Legend"
|
||||
if groupByColumn is not None:
|
||||
legendTitle = groupByColumn
|
||||
|
||||
seaborn.plt.legend(loc='upper center', prop={'size':8}, ncol=10, title=legendTitle, bbox_to_anchor=(0.5,-0.4))
|
||||
fig.autofmt_xdate(rotation=45)
|
||||
|
||||
seaborn.plt.autoscale(tight=False)
|
||||
|
||||
if notebook == True:
|
||||
seaborn.plt.show()
|
||||
return None
|
||||
else:
|
||||
directory = analyticConfig.output_dir
|
||||
try:
|
||||
os.makedirs(directory, mode=0755)
|
||||
except OSError, e:
|
||||
# be happy if someone already created the path
|
||||
pass
|
||||
baseFilename = getFilename(timelyMetric.metric, analyticConfig)
|
||||
filename = directory+'/'+baseFilename
|
||||
fig.savefig(filename + '.png')
|
||||
return baseFilename + '.png'
|
||||
|
||||
|
||||
def graphPlotly(analyticConfig, df, timelyMetric, seriesConfig={}, graphConfig={}, notebook=False):
|
||||
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
if dataFrame is not None:
|
||||
dataFrame['colFromIndex'] = dataFrame.index
|
||||
if analyticConfig.groupByColumn is not None:
|
||||
# sort on date and the groupByColumn so that the series legend is sorted
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex', analyticConfig.groupByColumn])
|
||||
else:
|
||||
dataFrame = dataFrame.sort_values(['colFromIndex'])
|
||||
|
||||
title = getTitle(timelyMetric, analyticConfig)
|
||||
|
||||
dataFrame['date'] = dataFrame.index.values
|
||||
|
||||
layoutConfig = dict(
|
||||
title=title,
|
||||
autosize=False,
|
||||
width=1000,
|
||||
height=700,
|
||||
showlegend=True,
|
||||
xaxis=dict(
|
||||
autorange=True,
|
||||
tickangle=-45
|
||||
),
|
||||
yaxis=dict(
|
||||
autorange=True,
|
||||
title=timelyMetric.metric
|
||||
)
|
||||
)
|
||||
|
||||
layoutConfig.update(graphConfig)
|
||||
|
||||
layout = go.Layout(layoutConfig)
|
||||
|
||||
data = None
|
||||
groupByValues = sorted(dataFrame[analyticConfig.groupByColumn].unique())
|
||||
for col in groupByValues:
|
||||
tempDataFrame = dataFrame.loc[dataFrame[analyticConfig.groupByColumn] == col]
|
||||
|
||||
addlColConfig = seriesConfig.get(col, seriesConfig.get('default', dict()))
|
||||
|
||||
config = dict(
|
||||
name=col,
|
||||
x=tempDataFrame.index,
|
||||
y=tempDataFrame[timelyMetric.metric],
|
||||
hoverinfo='x+y+text',
|
||||
text=col
|
||||
)
|
||||
|
||||
config.update(addlColConfig)
|
||||
|
||||
s = go.Scatter(config)
|
||||
|
||||
if data is None:
|
||||
data = [s]
|
||||
else:
|
||||
data.append(s)
|
||||
|
||||
directory = analyticConfig.output_dir
|
||||
try:
|
||||
os.makedirs(directory, mode=0755)
|
||||
except OSError, e:
|
||||
# be happy if someone already created the path
|
||||
pass
|
||||
baseFilename = getFilename(timelyMetric.metric, analyticConfig)
|
||||
filename = directory+'/'+baseFilename
|
||||
|
||||
fig = go.Figure(data=data, layout=layout)
|
||||
if notebook:
|
||||
py.iplot(fig, filename=filename, show_link=False)
|
||||
return None
|
||||
else:
|
||||
py.plot(fig, output_type='file', filename=filename+'.html', image_filename=filename, image=None, auto_open=False, show_link=False)
|
||||
return baseFilename+'.html'
|
||||
|
||||
def ensureMinSeriesLength(df, groupByColumn):
|
||||
|
||||
dataFrame = df
|
||||
if groupByColumn is not None:
|
||||
dataFrame = pandas.DataFrame(df, copy=True)
|
||||
dataFrame = dataFrame.dropna()
|
||||
|
||||
extra = pandas.DataFrame()
|
||||
for c, (cond, df_c) in enumerate(dataFrame.groupby(groupByColumn, sort=False)):
|
||||
if len(df_c) <= 1:
|
||||
extra = extra.append(df_c)
|
||||
|
||||
extra.index = extra.index + pandas.DateOffset(seconds=1)
|
||||
dataFrame = dataFrame.append(extra)
|
||||
|
||||
return dataFrame
|
||||
|
||||
def datetime2graphdays(dt):
|
||||
|
||||
return mdates.epoch2num((time.mktime(dt.timetuple()) / 86400) * mdates.SEC_PER_DAY)
|
||||
|
||||
def graphdays2datetime(x):
|
||||
epoch = mdates.num2epoch(x)
|
||||
return datetime.fromtimestamp(int(epoch), utc)
|
||||
|
||||
|
||||
def getFilename(metric, analyticConfig):
|
||||
|
||||
now = datetime.now(tz=utc)
|
||||
format = "%Y%m%d%H%M%S"
|
||||
nowStr = time.strftime(format, now.timetuple())
|
||||
fileNameBase = metric
|
||||
if analyticConfig.sample is not None:
|
||||
fileNameBase += "-" + analyticConfig.sample
|
||||
if analyticConfig.how is not None:
|
||||
fileNameBase += "-" + analyticConfig.how
|
||||
|
||||
return fileNameBase + '-' + nowStr
|
||||
|
||||
def getDirectoryPathForDate(format='%Y/%m/%d'):
|
||||
now = datetime.now(tz=utc)
|
||||
return time.strftime(format, now.timetuple())
|
||||
|
||||
def getTitle(timelyMetric, config, separator='\n'):
|
||||
if config.orCondition:
|
||||
condition = "OR"
|
||||
else:
|
||||
condition = "AND"
|
||||
|
||||
metricTitle = timelyMetric.metric
|
||||
if timelyMetric.tags is not None and len(timelyMetric.tags) > 0:
|
||||
metricTitle += ' (' + timelyMetric.tags + ')'
|
||||
|
||||
sampleTitle = ""
|
||||
alertTitle = ""
|
||||
if config.sample_period is not None:
|
||||
sampleTitle = config.sample_period + " " + config.how + " samples"
|
||||
|
||||
if config.rolling_average_period is not None:
|
||||
sampleTitle += ', ' + config.rolling_average_period + ' rolling average'
|
||||
|
||||
if config.min_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y < " + str(config.min_threshold) + ")"
|
||||
|
||||
if config.average_min_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y-avg < " + str(config.average_min_threshold) + ")"
|
||||
|
||||
if config.max_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y > " + str(config.max_threshold) + ")"
|
||||
|
||||
if config.average_max_threshold is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
alertTitle += "(y-avg > " + str(config.average_max_threshold) + ")"
|
||||
|
||||
if config.min_threshold_percentage is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
if config.min_threshold_percentage >= 0:
|
||||
descriptivePercentage = 100 + config.min_threshold_percentage
|
||||
else:
|
||||
descriptivePercentage = abs(config.min_threshold_percentage)
|
||||
alertTitle += "(y < " + str(descriptivePercentage) + "% of " + str(
|
||||
config.rolling_average_period) + " sample average)"
|
||||
|
||||
if config.max_threshold_percentage is not None:
|
||||
if alertTitle != "":
|
||||
alertTitle += " " + condition + " "
|
||||
if config.max_threshold_percentage >= 0:
|
||||
descriptivePercentage = 100 + config.max_threshold_percentage
|
||||
else:
|
||||
descriptivePercentage = abs(config.max_threshold_percentage)
|
||||
alertTitle += "(y > " + str(descriptivePercentage) + "% of " + str(
|
||||
config.rolling_average_period) + " sample average)"
|
||||
|
||||
if config.system_name is None:
|
||||
title = metricTitle
|
||||
else:
|
||||
title = config.system_name + ' - ' + metricTitle
|
||||
|
||||
if sampleTitle != "":
|
||||
title += separator + sampleTitle
|
||||
|
||||
if alertTitle != "":
|
||||
if config.min_alert_period is not None:
|
||||
alertTitle = '(' + alertTitle + ') for at least ' + config.min_alert_period
|
||||
if config.last_alert is not None:
|
||||
alertTitle = alertTitle + ' in last ' + config.last_alert
|
||||
title += separator + alertTitle
|
||||
|
||||
|
||||
return title
|
||||
+16
-12
@@ -2,6 +2,8 @@ import time
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
from datetime import tzinfo
|
||||
from pandas.tslib import Timedelta
|
||||
import pandas
|
||||
|
||||
class TimeDateError(ValueError):
|
||||
|
||||
@@ -39,7 +41,7 @@ class TimeDateRange:
|
||||
def getBeginMs(self):
|
||||
if self.beginDt == None:
|
||||
return None
|
||||
return int(time.mktime(self.beginDt.timetuple()) * 1000)
|
||||
return TimeDateRange.unix_time_millis(self.beginDt)
|
||||
|
||||
def getBeginStr(self):
|
||||
if self.beginDt == None:
|
||||
@@ -49,24 +51,24 @@ class TimeDateRange:
|
||||
def getEndMs(self):
|
||||
if self.endDt == None:
|
||||
return None
|
||||
return int(time.mktime(self.endDt.timetuple()) * 1000)
|
||||
return TimeDateRange.unix_time_millis(self.endDt)
|
||||
|
||||
def getEndStr(self):
|
||||
if self.endDt == None:
|
||||
return None
|
||||
return time.strftime(self.format, self.endDt.timetuple())
|
||||
|
||||
def __init__(self, beginStr, endStr, hours, format="%Y%m%d-%H%M%S"):
|
||||
def __init__(self, beginStr, endStr, period, format="%Y%m%d-%H%M%S"):
|
||||
|
||||
now = datetime.now(tz=utc)
|
||||
nowMs = TimeDateRange.unix_time_millis(now)
|
||||
|
||||
if beginStr != None and endStr != None and hours != None:
|
||||
raise TimeDateError("You can not privide a begin, end, and hours")
|
||||
elif (beginStr == None and endStr == None and hours == None) or (hours == None and beginStr == None):
|
||||
if beginStr != None and endStr != None and period != None:
|
||||
raise TimeDateError("You can not provide a begin, end, and period")
|
||||
elif (beginStr == None and endStr == None and period == None) or (period == None and beginStr == None):
|
||||
raise TimeDateError("You must provide a time range")
|
||||
|
||||
if hours == None:
|
||||
if period == None:
|
||||
# calculate begin time
|
||||
beginTuple = time.strptime(beginStr, self.format)
|
||||
self.beginDt = datetime(*beginTuple[0:6], tzinfo=utc)
|
||||
@@ -81,10 +83,12 @@ class TimeDateRange:
|
||||
if self.endDt > now:
|
||||
self.endDt = now
|
||||
else:
|
||||
# hours must be specified
|
||||
rangeInSec = hours * 3600
|
||||
# period must be specified
|
||||
td = pandas.to_timedelta(period)
|
||||
rangeInSec = td.total_seconds()
|
||||
|
||||
if beginStr == None:
|
||||
# calculate begin time from end tme and hours
|
||||
# calculate begin time from end tme and period
|
||||
|
||||
# calculate end time
|
||||
# end is either specified or now
|
||||
@@ -99,7 +103,7 @@ class TimeDateRange:
|
||||
self.beginDt = self.endDt - timedelta(milliseconds=rangeInSec * 1000)
|
||||
|
||||
else:
|
||||
# calculate end time from begin tme and hours
|
||||
# calculate end time from begin tme and period
|
||||
# calculate begin time
|
||||
self.beginDt = datetime(*time.strptime(beginStr, self.format)[0:6], tzinfo=utc)
|
||||
|
||||
@@ -119,6 +123,6 @@ class TimeDateRange:
|
||||
|
||||
@classmethod
|
||||
def unix_time_millis(cls, dt):
|
||||
return (dt - TimeDateRange.epoch).total_seconds() * 1000.0
|
||||
return int((dt - TimeDateRange.epoch).total_seconds() * 1000.0)
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import smtplib
|
||||
from os.path import basename
|
||||
from email.mime.application import MIMEApplication
|
||||
from email.mime.multipart import MIMEMultipart
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import COMMASPACE, formatdate
|
||||
import TimelyMetric
|
||||
import syslog
|
||||
import DataOperations
|
||||
|
||||
syslog.openlog("TimelyNotifications")
|
||||
|
||||
|
||||
class TimelyAlert():
|
||||
|
||||
def __init__(self, timelyMetric, dataFrame, message, seriesConfig, analyticConfig, notebook):
|
||||
self.timelyMetric = timelyMetric
|
||||
self.dataFrame = dataFrame
|
||||
self.seriesConfig = seriesConfig
|
||||
self.analyticConfig = analyticConfig
|
||||
self.notebook = notebook
|
||||
self.message = message
|
||||
|
||||
def email(self, send_from, send_to, subject, text, files=None, server="127.0.0.1"):
|
||||
assert isinstance(send_to, list)
|
||||
|
||||
msg = MIMEMultipart()
|
||||
msg['From'] = send_from
|
||||
msg['To'] = COMMASPACE.join(send_to)
|
||||
msg['Date'] = formatdate(localtime=True)
|
||||
msg['Subject'] = subject
|
||||
|
||||
msg.attach(MIMEText(text))
|
||||
|
||||
for f in files or []:
|
||||
with open(f, "rb") as fil:
|
||||
part = MIMEApplication(
|
||||
fil.read(),
|
||||
Name=basename(f)
|
||||
)
|
||||
part['Content-Disposition'] = 'attachment; filename="%s"' % basename(f)
|
||||
msg.attach(part)
|
||||
|
||||
|
||||
smtp = smtplib.SMTP(server)
|
||||
smtp.sendmail(send_from, send_to, msg.as_string())
|
||||
smtp.close()
|
||||
|
||||
def log(self, message):
|
||||
|
||||
syslog.systlog(syslog.LOG_ALERT, message)
|
||||
|
||||
def graph(self, type="png"):
|
||||
|
||||
graphConfig = {}
|
||||
graphConfig["title"] = DataOperations.getTitle(self.timelyMetric, self.analyticConfig)
|
||||
return TimelyMetric.graph(self.analyticConfig, self.dataFrame, self.timelyMetric, seriesConfig=self.seriesConfig, graphConfig=graphConfig, notebook=self.notebook, type=type)
|
||||
|
||||
def getDataFrame(self):
|
||||
return self.dataFrame
|
||||
|
||||
def getAnalyticConfig(self):
|
||||
return self.analyticConfig
|
||||
|
||||
def getSeriesConfig(self):
|
||||
return self.seriesConfig
|
||||
|
||||
def getTimelyMetric(self):
|
||||
return self.timelyMetric
|
||||
|
||||
def getMessage(self):
|
||||
return self.message
|
||||
@@ -0,0 +1,224 @@
|
||||
import TimelyMetric
|
||||
from TimeDateRange import TimeDateRange
|
||||
from TimeDateRange import UTC
|
||||
from TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
|
||||
import DataOperations
|
||||
import pandas
|
||||
import re
|
||||
from datetime import datetime
|
||||
from datetime import timedelta
|
||||
|
||||
from TimelyAlert import TimelyAlert
|
||||
import numpy as np
|
||||
|
||||
utc = UTC()
|
||||
|
||||
def logTiming(timelyMetric, startTime, stopTime):
|
||||
df = timelyMetric.getDataFrame()
|
||||
print("start: " + str(startTime))
|
||||
print("stop: " + str(stopTime))
|
||||
print("num records: " + str(len(df)))
|
||||
elapsedSec = (TimeDateRange.unix_time_millis(stopTime) - TimeDateRange.unix_time_millis(startTime)) / 1000
|
||||
print("elapsed seconds: " + str(elapsedSec))
|
||||
recPerSec = len(df) / elapsedSec
|
||||
print("records per second: " + recPerSec)
|
||||
|
||||
def addCondition(orCondition, currResult, condition):
|
||||
if orCondition:
|
||||
currResult = currResult | condition
|
||||
else:
|
||||
currResult = currResult & condition
|
||||
return currResult
|
||||
|
||||
def keepConsecutiveAlerts(dataFrame, exceptions, minimumSpan):
|
||||
|
||||
if exceptions.size > 0 and minimumSpan is not None:
|
||||
|
||||
currentFirst = None
|
||||
currentLast = None
|
||||
result_span_values = pandas.DataFrame()
|
||||
result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
result_span_values = result_span_values.set_index(exceptions.index)
|
||||
current_result_span_values = pandas.DataFrame()
|
||||
current_result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
current_result_span_values = current_result_span_values.set_index(exceptions.index)
|
||||
|
||||
for index, row in dataFrame.iterrows():
|
||||
if index in exceptions.index:
|
||||
current_result_span_values.loc[index]['bool'] = True
|
||||
currentLast = index
|
||||
if currentFirst is None:
|
||||
currentFirst = index
|
||||
if int((currentLast - currentFirst).total_seconds() / 60) >= int(minimumSpan):
|
||||
result_span_values['bool'] = result_span_values['bool'] | current_result_span_values['bool']
|
||||
else:
|
||||
current_result_span_values['bool'] = np.zeros(exceptions.shape, bool)
|
||||
currentFirst = None
|
||||
currentLast = None
|
||||
|
||||
return exceptions.loc[result_span_values['bool']]
|
||||
else:
|
||||
return exceptions
|
||||
|
||||
def convertCounterToRate(timelyMetric, analyticConfig):
|
||||
# Adjust counter metric into a rate metric
|
||||
df = timelyMetric.getDataFrame()
|
||||
columnNames = df[analyticConfig.groupByColumn].unique()
|
||||
df = DataOperations.pivot(df, timelyMetric.metric, analyticConfig.groupByColumn)
|
||||
df = DataOperations.resample(df, analyticConfig.sample, how=analyticConfig.how)
|
||||
for c in columnNames:
|
||||
df[c] = df[c].diff(1) / (analyticConfig.sample_minutes * 60 * 1000)
|
||||
df = DataOperations.unpivot(df, timelyMetric.metric, analyticConfig.groupByColumn)
|
||||
df = df.dropna()
|
||||
timelyMetric.setDataFrame(df)
|
||||
|
||||
def find_alerts(timelyMetric, analyticConfig, notebook=False):
|
||||
|
||||
if (analyticConfig.counter == True):
|
||||
convertCounterToRate(timelyMetric, analyticConfig)
|
||||
|
||||
df = timelyMetric.getDataFrame()
|
||||
|
||||
graphDF = TimelyMetric.pivot(df, timelyMetric.metric, groupByColumn=analyticConfig.groupByColumn)
|
||||
|
||||
if analyticConfig.excludeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : not (re.search(analyticConfig.excludeColRegex, x)), axis=1)
|
||||
if analyticConfig.includeColRegex is not None:
|
||||
graphDF = graphDF.select(lambda x : re.search(analyticConfig.includeColRegex, x), axis=1)
|
||||
|
||||
if analyticConfig.sample is not None:
|
||||
graphDF = TimelyMetric.resample(graphDF, analyticConfig.sample, how=analyticConfig.how)
|
||||
|
||||
graphDF_avg = pandas.DataFrame(graphDF, copy=True)
|
||||
|
||||
combined = pandas.DataFrame()
|
||||
|
||||
seriesConfig = {}
|
||||
for i in graphDF_avg.columns:
|
||||
col = str(i)
|
||||
|
||||
any_conditions_values = False
|
||||
result_values = np.ones(graphDF[col].shape, bool)
|
||||
if analyticConfig.orCondition:
|
||||
result_values = np.zeros(graphDF[col].shape, bool)
|
||||
|
||||
any_conditions_average = False
|
||||
result_average = np.ones(graphDF_avg[col].shape, bool)
|
||||
if analyticConfig.orCondition:
|
||||
result_average = np.zeros(graphDF_avg[col].shape, bool)
|
||||
|
||||
if analyticConfig.min_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) < analyticConfig.min_threshold
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.max_threshold is not None:
|
||||
currCondition = graphDF[col].astype(float) > analyticConfig.max_threshold
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.rolling_average_samples is not None:
|
||||
graphDF_avg = TimelyMetric.rolling_average(graphDF_avg, col, rolling_average=analyticConfig.rolling_average_samples)
|
||||
if analyticConfig.min_threshold_percentage is not None:
|
||||
if analyticConfig.min_threshold_percentage >= 0:
|
||||
multiple = 1.0 + (float(abs(analyticConfig.min_threshold_percentage)) / float(100))
|
||||
else:
|
||||
multiple = 1.0 - (float(abs(analyticConfig.min_threshold_percentage)) / float(100))
|
||||
currCondition = graphDF[col].astype(float) < (graphDF_avg[col].astype(float) * multiple)
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.max_threshold_percentage is not None:
|
||||
if analyticConfig.max_threshold_percentage >= 0:
|
||||
multiple = 1.0 + (float(abs(analyticConfig.max_threshold_percentage)) / float(100))
|
||||
else:
|
||||
multiple = 1.0 - (float(abs(analyticConfig.max_threshold_percentage)) / float(100))
|
||||
currCondition = graphDF[col].astype(float) > (graphDF_avg[col].astype(float) * multiple)
|
||||
result_values = addCondition(analyticConfig.orCondition, result_values, currCondition)
|
||||
any_conditions_values = True
|
||||
|
||||
if analyticConfig.average_min_threshold is not None:
|
||||
currCondition = graphDF_avg[col].astype(float) < analyticConfig.average_min_threshold
|
||||
result_average = addCondition(analyticConfig.orCondition, result_average, currCondition)
|
||||
any_conditions_average = True
|
||||
if analyticConfig.average_max_threshold is not None:
|
||||
currCondition = graphDF_avg[col].astype(float) > analyticConfig.average_max_threshold
|
||||
result_average = addCondition(analyticConfig.orCondition, result_average, currCondition)
|
||||
any_conditions_average = True
|
||||
|
||||
# if orCondition is AND and no exceptional conditions have been found, then result_values will be all True
|
||||
if any_conditions_values == False:
|
||||
result_values = np.zeros(graphDF[col].shape, bool)
|
||||
exceptional_values = graphDF.loc[result_values, col]
|
||||
|
||||
# if orCondition is AND and no exceptional conditions have been found, then result_average will be all True
|
||||
if any_conditions_average == False:
|
||||
result_average = np.zeros(graphDF_avg[col].shape, bool)
|
||||
exceptional_average = graphDF_avg.loc[result_average, col]
|
||||
|
||||
# only keep alerts that are in consecutive periods of length analyticConfig.min_alert_minutes
|
||||
exceptional_values = keepConsecutiveAlerts(graphDF, exceptional_values, analyticConfig.min_alert_minutes)
|
||||
exceptional_average = keepConsecutiveAlerts(graphDF_avg, exceptional_average, analyticConfig.min_alert_minutes)
|
||||
|
||||
# only evaluate the last analyticConfig.last_alert_minutes if set
|
||||
if analyticConfig.last_alert_minutes is not None:
|
||||
end = datetime.fromtimestamp(timelyMetric.timeDateRange.getEndMs() / 1000.00, DataOperations.utc)
|
||||
recentEnoughBegin = end - timedelta(minutes=analyticConfig.last_alert_minutes)
|
||||
exceptional_values = exceptional_values.ix[recentEnoughBegin:end]
|
||||
exceptional_average = exceptional_average.ix[recentEnoughBegin:end]
|
||||
|
||||
anyValueExceptions = exceptional_values.size > 0
|
||||
anyAverageExceptions = exceptional_average.size > 0
|
||||
|
||||
if (analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyValueExceptions):
|
||||
combined[col] = graphDF[col]
|
||||
|
||||
if analyticConfig.rolling_average_samples is not None:
|
||||
if (analyticConfig.display.lower() == "all"):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
else:
|
||||
if (anyAverageExceptions):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
if (anyValueExceptions and (analyticConfig.min_threshold_percentage is not None or analyticConfig.max_threshold_percentage is not None)):
|
||||
combined[col + '_avg'] = graphDF_avg[col]
|
||||
|
||||
if ((analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyValueExceptions)):
|
||||
combined[col + '_warn'] = exceptional_values.dropna()
|
||||
|
||||
seriesConfig[col + '_warn'] = {
|
||||
"mode" : "markers",
|
||||
"marker" : {
|
||||
"symbol" : "hash-open",
|
||||
"color" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
if ((analyticConfig.display.lower() == "all") or (analyticConfig.display.lower() == "alerts" and anyAverageExceptions)):
|
||||
combined[col + '_avg_warn'] = exceptional_average.dropna()
|
||||
|
||||
seriesConfig[col + '_avg_warn'] = {
|
||||
"mode" : "markers",
|
||||
"marker" : {
|
||||
"symbol" : "hash-open",
|
||||
"color" : "red"
|
||||
}
|
||||
}
|
||||
|
||||
timelyAlert = None
|
||||
if not combined.empty:
|
||||
alertAnalyticConfig = TimelyAnalyticConfiguration(analyticConfig)
|
||||
if alertAnalyticConfig.groupByColumn is None:
|
||||
alertAnalyticConfig.groupByColumn = timelyMetric.metric + "_obs"
|
||||
combined = TimelyMetric.unpivot(combined, timelyMetric.metric, groupByColumn=alertAnalyticConfig.groupByColumn)
|
||||
combined = combined.sort_index()
|
||||
combined['date'] = combined.index.values
|
||||
combined = combined.sort_values(['date', alertAnalyticConfig.groupByColumn])
|
||||
combined = combined.drop(['date'], 1)
|
||||
combined = combined.dropna()
|
||||
combined = DataOperations.ensureMinSeriesLength(combined, alertAnalyticConfig.groupByColumn)
|
||||
|
||||
message = DataOperations.getTitle(timelyMetric, analyticConfig, separator=', ')
|
||||
|
||||
timelyAlert = TimelyAlert(timelyMetric, combined, message, seriesConfig, alertAnalyticConfig, notebook)
|
||||
|
||||
return timelyAlert
|
||||
@@ -0,0 +1,84 @@
|
||||
import pandas
|
||||
|
||||
|
||||
class TimelyAnalyticConfiguration():
|
||||
|
||||
|
||||
def __init__(self, analyticConfig):
|
||||
|
||||
if isinstance(analyticConfig, dict):
|
||||
self.groupByColumn = analyticConfig.get('groupByColumn', None)
|
||||
self.includeColRegex = analyticConfig.get('includeColRegex', None)
|
||||
self.excludeColRegex = analyticConfig.get('excludeColRegex', None)
|
||||
self.counter = analyticConfig.get('counter', False)
|
||||
self.sample_period = analyticConfig.get('sample', None)
|
||||
self.how = analyticConfig.get('how', 'mean')
|
||||
self.rolling_average_period = analyticConfig.get('rolling_average_period', None)
|
||||
self.min_threshold = analyticConfig.get('min_threshold', None)
|
||||
self.average_min_threshold = analyticConfig.get('average_min_threshold', None)
|
||||
self.max_threshold = analyticConfig.get('max_threshold', None)
|
||||
self.average_max_threshold = analyticConfig.get('average_max_threshold', None)
|
||||
self.min_threshold_percentage = analyticConfig.get('min_threshold_percentage', None)
|
||||
self.max_threshold_percentage = analyticConfig.get('max_threshold_percentage', None)
|
||||
self.min_alert_period = analyticConfig.get('min_alert_period', None)
|
||||
boolean = analyticConfig.get('boolean', 'or')
|
||||
self.orCondition = boolean == 'or' or boolean == 'OR'
|
||||
# alerts or all
|
||||
self.display = analyticConfig.get('display', 'alerts')
|
||||
self.send_alerts_to = analyticConfig.get('send_alerts_to', [])
|
||||
self.output_dir = analyticConfig.get('output_dir', '/tmp')
|
||||
self.last_alert = analyticConfig.get('last_alert', None)
|
||||
self.system_name = analyticConfig.get('system_name', None)
|
||||
|
||||
self.sample = None
|
||||
self.sample_minutes = None
|
||||
if self.sample_period is not None:
|
||||
td = pandas.to_timedelta(self.sample_period)
|
||||
self.sample_minutes = int(td.total_seconds() / 60)
|
||||
self.sample = str(self.sample_minutes) + 'min'
|
||||
|
||||
self.rolling_average_samples = None
|
||||
self.rolling_average_minutes = None
|
||||
if (self.rolling_average_period is not None) and (self.sample_minutes is not None):
|
||||
td = pandas.to_timedelta(self.rolling_average_period)
|
||||
self.rolling_average_minutes = int(td.total_seconds() / 60)
|
||||
self.rolling_average_samples = int(self.rolling_average_minutes / self.sample_minutes)
|
||||
|
||||
self.min_alert_minutes = None
|
||||
if self.min_alert_period is not None:
|
||||
td = pandas.to_timedelta(self.min_alert_period)
|
||||
self.min_alert_minutes = int(td.total_seconds() / 60)
|
||||
|
||||
self.last_alert_minutes = None
|
||||
if self.last_alert is not None:
|
||||
td = pandas.to_timedelta(self.last_alert)
|
||||
self.last_alert_minutes = int(td.total_seconds() / 60)
|
||||
|
||||
elif isinstance(analyticConfig, TimelyAnalyticConfiguration):
|
||||
self.groupByColumn = analyticConfig.groupByColumn
|
||||
self.includeColRegex = analyticConfig.includeColRegex
|
||||
self.excludeColRegex = analyticConfig.excludeColRegex
|
||||
self.counter = analyticConfig.counter
|
||||
self.sample_period = analyticConfig.sample_period
|
||||
self.sample_minutes = analyticConfig.sample_minutes
|
||||
self.sample = analyticConfig.sample
|
||||
self.how = analyticConfig.how
|
||||
self.rolling_average_period = analyticConfig.rolling_average_period
|
||||
self.rolling_average_samples = analyticConfig.rolling_average_samples
|
||||
self.rolling_average_minutes = analyticConfig.rolling_average_minutes
|
||||
self.min_threshold = analyticConfig.min_threshold
|
||||
self.average_min_threshold = analyticConfig.average_min_threshold
|
||||
self.max_threshold = analyticConfig.max_threshold
|
||||
self.average_max_threshold = analyticConfig.average_max_threshold
|
||||
self.min_threshold_percentage = analyticConfig.min_threshold_percentage
|
||||
self.max_threshold_percentage = analyticConfig.max_threshold_percentage
|
||||
self.min_alert_period = analyticConfig.min_alert_period
|
||||
self.min_alert_minutes = analyticConfig.min_alert_minutes
|
||||
self.orCondition = analyticConfig.orCondition
|
||||
# alerts or all
|
||||
self.display = analyticConfig.display
|
||||
self.send_alerts_to = analyticConfig.send_alerts_to
|
||||
self.output_dir = analyticConfig.output_dir
|
||||
self.last_alert = analyticConfig.last_alert
|
||||
self.last_alert_minutes = analyticConfig.last_alert_minutes
|
||||
self.system_name = analyticConfig.system_name
|
||||
+53
-37
@@ -23,7 +23,7 @@ class TimelyWebSocketClient(WebSocketClient):
|
||||
self.startTime = startTime
|
||||
self.endTime = endTime
|
||||
self.subscriptionId = random.randint(1000000000, 9999999999)
|
||||
WebSocketClient.__init__(self, hostport, connect_timeout, request_timeout)
|
||||
WebSocketClient.__init__(self, hostport, '/websocket', {'metric' : metric}, connect_timeout, request_timeout)
|
||||
|
||||
def _on_message(self, msg):
|
||||
# implement in subclass
|
||||
@@ -37,7 +37,6 @@ class TimelyWebSocketClient(WebSocketClient):
|
||||
"subscriptionId": self.subscriptionId,
|
||||
}
|
||||
self.send(create)
|
||||
self.send(create)
|
||||
|
||||
m1 = {
|
||||
"operation": "add",
|
||||
@@ -73,7 +72,7 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
client = None
|
||||
endtime = None
|
||||
|
||||
def __init__(self, hostport, metric, tags, beginStr, endStr, hours, sample):
|
||||
def __init__(self, hostport, metric, tags, beginStr, endStr, period, sample):
|
||||
self.series = pandas.Series()
|
||||
self.metric = metric
|
||||
self.tags = tags
|
||||
@@ -81,10 +80,9 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
self.debug = False
|
||||
self.dataFrame = None
|
||||
self.data = []
|
||||
self.timeDateRange = TimeDateRange(beginStr, endStr, period)
|
||||
|
||||
timeDateRange = TimeDateRange(beginStr, endStr, hours)
|
||||
|
||||
TimelyWebSocketClient.__init__(self, hostport, metric, tags, timeDateRange.getBeginMs(), timeDateRange.getEndMs())
|
||||
TimelyWebSocketClient.__init__(self, hostport, metric, tags, self.timeDateRange.getBeginMs(), self.timeDateRange.getEndMs())
|
||||
|
||||
def debugOn(self):
|
||||
self.debug = True
|
||||
@@ -106,37 +104,51 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
global endtime
|
||||
|
||||
responsesObj = json.loads(msg)
|
||||
|
||||
responses = responsesObj.get("responses")
|
||||
if responses is not None:
|
||||
for obj in responses:
|
||||
complete = bool(obj.get("complete"))
|
||||
if complete:
|
||||
self._on_connection_close()
|
||||
return
|
||||
else:
|
||||
date = int(obj.get("timestamp")/1000)
|
||||
dt = pandas.datetime.utcfromtimestamp(date)
|
||||
metricName = str(obj.get("metric"))
|
||||
metricValue = obj.get("value")
|
||||
|
||||
for obj in responses:
|
||||
complete = bool(obj.get("complete"))
|
||||
if complete:
|
||||
self._on_connection_close()
|
||||
return
|
||||
else:
|
||||
date = int(obj.get("timestamp")/1000)
|
||||
dt = pandas.datetime.utcfromtimestamp(date)
|
||||
metricName = str(obj.get("metric"))
|
||||
metricValue = obj.get("value")
|
||||
newData = {}
|
||||
newData["date"] = dt
|
||||
newData[metricName] = metricValue
|
||||
|
||||
newData = {}
|
||||
newData["date"] = dt
|
||||
newData[metricName] = metricValue
|
||||
|
||||
tags = obj.get("tags")
|
||||
for d in tags:
|
||||
for k,v in d.items():
|
||||
newData[k] = v
|
||||
|
||||
self.data.append(newData)
|
||||
tags = obj.get("tags")
|
||||
for d in tags:
|
||||
for k,v in d.items():
|
||||
newData[k] = v
|
||||
|
||||
self.data.append(newData)
|
||||
else:
|
||||
print("Session ended abnormally")
|
||||
exception = responsesObj.get("TimelyException")
|
||||
if exception is not None:
|
||||
code = exception.get("code")
|
||||
message = exception.get("message")
|
||||
print("Exception")
|
||||
print("code: " + str(code))
|
||||
print("message: " + message)
|
||||
self._on_connection_close()
|
||||
return
|
||||
|
||||
def _on_connection_close(self):
|
||||
global client
|
||||
|
||||
self.dataFrame = pandas.DataFrame(self.data)
|
||||
self.dataFrame = self.dataFrame.set_index(self.dataFrame['date'].apply(lambda x: pandas.DatetimeIndex([x])))
|
||||
|
||||
if len(self.dataFrame) > 0:
|
||||
self.dataFrame['date'] = pandas.DatetimeIndex(self.dataFrame['date'])
|
||||
self.dataFrame = self.dataFrame.set_index('date')
|
||||
j = pandas.to_timedelta(self.dataFrame.groupby(self.dataFrame.index).cumcount(), unit='us').values
|
||||
self.dataFrame.index = self.dataFrame.index + j
|
||||
|
||||
self.close()
|
||||
ioloop.IOLoop.current(instance=False).stop()
|
||||
@@ -146,18 +158,22 @@ class TimelyMetric(TimelyWebSocketClient):
|
||||
|
||||
return self.dataFrame;
|
||||
|
||||
def setDataFrame(self, dataFrame):
|
||||
|
||||
self.dataFrame = dataFrame
|
||||
|
||||
def print_debug(self):
|
||||
if self.debug:
|
||||
print(self.dataFrame)
|
||||
|
||||
def graph(self, groupByColumn=None):
|
||||
graph(self.dataFrame, self.metric, sample=self.sample, how=self.how, groupByColumn=groupByColumn)
|
||||
return graph(self.dataFrame, self, sample=self.sample, how=self.how, groupByColumn=groupByColumn)
|
||||
|
||||
|
||||
|
||||
def main():
|
||||
|
||||
hours = None
|
||||
period = None
|
||||
beginStr = None
|
||||
endStr = None
|
||||
|
||||
@@ -168,18 +184,18 @@ def main():
|
||||
groupByColumn = None
|
||||
try:
|
||||
argv = sys.argv
|
||||
options, remainder = getopt.getopt(argv[1:], 'h', ['metric=', 'tags=', 'hours=', 'groupByColumn=', 'begin=', 'end=', 'sample=', 'how=', 'hostport='])
|
||||
options, remainder = getopt.getopt(argv[1:], 'h', ['metric=', 'tags=', 'period=', 'groupByColumn=', 'begin=', 'end=', 'sample=', 'how=', 'hostport='])
|
||||
except getopt.GetoptError:
|
||||
print('TimelyMetric.py ---hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --hours=<hours>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
print('TimelyMetric.py ---hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --period=<period>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
sys.exit(2)
|
||||
for opt, arg in options:
|
||||
if opt == '-h':
|
||||
print('TimelyMetric.py --hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --hours=<hours>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
print('TimelyMetric.py --hostport=<host:port> --metric=<metric> --tags=<tag1=val1,tag2=val2> --period=<period>, --groupByColumn=<groupByColumn>, --sample=<sample_period> --how=<mean|min|max>')
|
||||
sys.exit()
|
||||
elif opt in ("--metric"):
|
||||
metric = arg
|
||||
elif opt in ("--hours"):
|
||||
hours = int(arg)
|
||||
elif opt in ("--period"):
|
||||
period = arg
|
||||
elif opt in ("--begin"):
|
||||
beginStr = arg
|
||||
elif opt in ("--end"):
|
||||
@@ -194,13 +210,13 @@ def main():
|
||||
hostport = arg
|
||||
try:
|
||||
|
||||
timelyMetric = TimelyMetric(hostport, metric, tags, beginStr, endStr, hours, sample).fetch()
|
||||
timelyMetric = TimelyMetric(hostport, metric, tags, beginStr, endStr, period, sample).fetch()
|
||||
dataFrame = timelyMetric.getDataFrame()
|
||||
|
||||
if dataFrame is not None:
|
||||
dataFrame = resample(dataFrame, metric, sample, how)
|
||||
dataFrame = pivot(dataFrame, metric, groupByColumn=groupByColumn)
|
||||
graph(dataFrame, metric, sample=sample, groupByColumn=groupByColumn, graphConfig={}, notebook=False)
|
||||
graph(dataFrame, timelyMetric, sample=sample, groupByColumn=groupByColumn, graphConfig={}, notebook=False)
|
||||
|
||||
except TimeDateError as e:
|
||||
print(e.message)
|
||||
+16
-2
@@ -14,12 +14,26 @@ class WebSocketClient():
|
||||
DEFAULT_REQUEST_TIMEOUT = 60
|
||||
APPLICATION_JSON = 'application/json'
|
||||
|
||||
def __init__(self, hostport, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
|
||||
def __init__(self, hostport, path, params, connect_timeout=DEFAULT_CONNECT_TIMEOUT,
|
||||
request_timeout=DEFAULT_REQUEST_TIMEOUT):
|
||||
|
||||
self.connect_timeout = connect_timeout
|
||||
self.request_timeout = request_timeout
|
||||
self.url = 'wss://' + hostport + '/websocket'
|
||||
self.url = 'wss://' + hostport
|
||||
if (path is not None and len(path) > 0):
|
||||
if (path.startswith('/')):
|
||||
self.url = self.url + path
|
||||
else:
|
||||
self.url = self.url + '/' + path
|
||||
if (any(params.values())):
|
||||
first = True
|
||||
for key, value in params.iteritems():
|
||||
if (first == True):
|
||||
self.url = self.url + '?'
|
||||
first = False
|
||||
else:
|
||||
self.url = self.url + '&'
|
||||
self.url = self.url + key + '=' + value
|
||||
|
||||
def connect(self, ioloop=ioloop.IOLoop.current(instance=False)):
|
||||
"""Connect to the server.
|
||||
Whitespace-only changes.
@@ -0,0 +1,50 @@
|
||||
import timely.TimelyMetric
|
||||
import timely.TimelyAnalytic
|
||||
import timely.DataOperations
|
||||
import os
|
||||
from timely.TimelyAnalyticConfiguration import TimelyAnalyticConfiguration
|
||||
|
||||
hostport = "127.0.0.1:5244"
|
||||
timelyMetric = timely.TimelyMetric.TimelyMetric(hostport, 'timely.keys.metric.inserted', None, None, None, '24 hours', None).fetch()
|
||||
|
||||
print(timelyMetric.getDataFrame())
|
||||
exit()
|
||||
|
||||
analyticConfig = TimelyAnalyticConfiguration({
|
||||
'system_name' : 'TestSystem',
|
||||
'counter' : False,
|
||||
'includeColRegex' : None,
|
||||
'excludeColRegex' : None,
|
||||
'groupByColumn' : 'host',
|
||||
'sample' : '1 minute',
|
||||
'how' : 'mean',
|
||||
'rolling_average_period' : '12 hours',
|
||||
'min_threshold' : None,
|
||||
'average_min_threshold' : None,
|
||||
'max_threshold' : None,
|
||||
'average_max_threshold' : None,
|
||||
'min_threshold_percentage' : None,
|
||||
'max_threshold_percentage' : None,
|
||||
'boolean' : 'and',
|
||||
'min_alert_period' : None,
|
||||
'last_alert' : None,
|
||||
'display' : 'all',
|
||||
'output_dir' : '/path/to/output'
|
||||
})
|
||||
|
||||
alert = timely.TimelyAnalytic.find_alerts(timelyMetric, analyticConfig)
|
||||
|
||||
if alert is not None:
|
||||
# write graph to file
|
||||
oldmask = os.umask(022)
|
||||
file = alert.graph(type='html')
|
||||
os.umask(oldmask)
|
||||
|
||||
text = timely.DataOperations.getTitle(timelyMetric, analyticConfig)
|
||||
|
||||
# send email with graph attached
|
||||
alert.email("", "", text, text, [file])
|
||||
|
||||
# log to syslog
|
||||
alert.log(text)
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
package timely.model.parse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import timely.model.Metric;
|
||||
import timely.model.Tag;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MetricParserTest {
|
||||
|
||||
@Test
|
||||
public void testParseWithEscapedCharacters() {
|
||||
|
||||
MetricParser parser = new MetricParser();
|
||||
Metric m = parser.parse("put mymetric 12341234 5.0 tag1=value1,value1 tag2=value2=value2");
|
||||
|
||||
Assert.assertEquals("mymetric", m.getName());
|
||||
Assert.assertEquals(12341234, (long) m.getValue().getTimestamp());
|
||||
Assert.assertEquals(5.0, (double) m.getValue().getMeasure(), 0);
|
||||
List<Tag> expected = new ArrayList<>();
|
||||
expected.add(new Tag("tag1", "value1,value1"));
|
||||
expected.add(new Tag("tag2", "value2=value2"));
|
||||
Assert.assertEquals(expected, m.getTags());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseMalformatted() {
|
||||
|
||||
MetricParser parser = new MetricParser();
|
||||
try {
|
||||
// parser should throw an exception
|
||||
parser.parse("put mymetric 12341234 5.0 tag1 tag2=value2");
|
||||
Assert.fail();
|
||||
} catch (IllegalArgumentException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
+13
-19
@@ -1,7 +1,8 @@
|
||||
package timely.model;
|
||||
package timely.model.parse;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import timely.model.Tag;
|
||||
import timely.model.parse.TagListParser;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -20,15 +21,6 @@ public class TagListParserTest {
|
||||
Assert.assertEquals(new Tag("tag2", "value2"), tags.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListParseEncoded() {
|
||||
String value = "tag%2C%3D%31=value%2C%3D%31,tag%2C%3D%32=value%2C%3D%32";
|
||||
List<Tag> tags = new TagListParser().parse(value);
|
||||
Assert.assertEquals(2, tags.size());
|
||||
Assert.assertEquals(new Tag("tag,=1", "value,=1"), tags.get(0));
|
||||
Assert.assertEquals(new Tag("tag,=2", "value,=2"), tags.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCombine() {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
@@ -38,15 +30,6 @@ public class TagListParserTest {
|
||||
Assert.assertEquals("tag1=value1,tag2=value2", combined);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCombineEncoded() {
|
||||
List<Tag> tags = new ArrayList<>();
|
||||
tags.add(new Tag("tag,=1", "value,=1"));
|
||||
tags.add(new Tag("tag,=2", "value,=2"));
|
||||
String combined = new TagListParser().combine(tags);
|
||||
Assert.assertEquals("tag%2C%3D1=value%2C%3D1,tag%2C%3D2=value%2C%3D2", combined);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testListCombineMap() {
|
||||
Map<String, String> map = new TreeMap<>();
|
||||
@@ -55,4 +38,15 @@ public class TagListParserTest {
|
||||
String combined = new TagListParser().combine(map);
|
||||
Assert.assertEquals("tag1=value1,tag2=value2", combined);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testParseTagsWithCommas() {
|
||||
|
||||
try {
|
||||
String s = "tag1=value1,tag2=3.4.3_(default\\,_Date\\,_Time)_";
|
||||
new TagListParser().parse(s);
|
||||
} catch (Exception e) {
|
||||
Assert.fail(e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-nsq-plugin</artifactId>
|
||||
<dependencies>
|
||||
@@ -16,6 +16,10 @@
|
||||
<groupId>org.apache.httpcomponents</groupId>
|
||||
<artifactId>httpclient</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
|
||||
+97
@@ -0,0 +1,97 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.apache.http.client.config.RequestConfig;
|
||||
import org.apache.http.impl.client.CloseableHttpClient;
|
||||
import org.apache.http.impl.client.HttpClientBuilder;
|
||||
import org.collectd.api.OConfigItem;
|
||||
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
|
||||
|
||||
private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds
|
||||
private int connectTimeout = 30000; // 30 seconds in milliseconds
|
||||
private int socketTimeout = 60000; // 60 seconds in milliseconds
|
||||
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "connectionRequestTimeout":
|
||||
case "ConnectionRequestTimeout":
|
||||
connectionRequestTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectTimeout":
|
||||
case "ConnectTimeout":
|
||||
connectTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "socketTimeout":
|
||||
case "SocketTimeout":
|
||||
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectionTimeToLive":
|
||||
case "ConnectionTimeToLive":
|
||||
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject makeObject() throws Exception {
|
||||
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout)
|
||||
.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
|
||||
HttpClientBuilder builder = HttpClientBuilder.create();
|
||||
builder.setDefaultRequestConfig(requestConfig);
|
||||
builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
|
||||
return new DefaultPooledObject(builder.build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject pooledObject) throws Exception {
|
||||
try {
|
||||
((CloseableHttpClient) pooledObject.getObject()).close();
|
||||
} catch (Exception e) {
|
||||
// do nothing
|
||||
} finally {
|
||||
pooledObject.invalidate();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject pooledObject) {
|
||||
return pooledObject.getObject() != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
public void setConnectionRequestTimeout(int connectionRequestTimeout) {
|
||||
this.connectionRequestTimeout = connectionRequestTimeout;
|
||||
}
|
||||
|
||||
public void setConnectTimeout(int connectTimeout) {
|
||||
this.connectTimeout = connectTimeout;
|
||||
}
|
||||
|
||||
public void setSocketTimeout(int socketTimeout) {
|
||||
this.socketTimeout = socketTimeout;
|
||||
}
|
||||
|
||||
public void setConnectionTimeToLive(long connectionTimeToLive) {
|
||||
this.connectionTimeToLive = connectionTimeToLive;
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,11 @@ package timely.collectd.plugin;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Random;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.apache.http.HttpStatus;
|
||||
import org.apache.http.client.entity.EntityBuilder;
|
||||
import org.apache.http.client.methods.CloseableHttpResponse;
|
||||
@@ -33,10 +36,11 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
private int port = 0;
|
||||
private String topic = "metrics#ephemeral";
|
||||
private String endpoint = null;
|
||||
private ByteArrayOutputStream baos = null;
|
||||
private DataOutputStream out = null;
|
||||
private GenericObjectPool<CloseableHttpClient> clientPool = null;
|
||||
|
||||
private CloseableHttpClient client = HttpClients.createDefault();
|
||||
// pool should be limited by the number of WriteThreads configured in
|
||||
// collectd
|
||||
final private int POOL_MAX_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
public WriteNSQPlugin() {
|
||||
Collectd.registerConfig(WriteNSQPlugin.class.getName(), this);
|
||||
@@ -44,16 +48,18 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
public synchronized int write(ValueList vl) {
|
||||
baos = new ByteArrayOutputStream();
|
||||
out = new DataOutputStream(baos);
|
||||
super.process(vl);
|
||||
public int write(ValueList vl) {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
DataOutputStream out = new DataOutputStream(baos);
|
||||
super.process(vl, out);
|
||||
CloseableHttpClient client = null;
|
||||
try {
|
||||
out.flush();
|
||||
HttpPost post = new HttpPost(endpoint);
|
||||
EntityBuilder request = EntityBuilder.create();
|
||||
request.setBinary(baos.toByteArray());
|
||||
post.setEntity(request.build());
|
||||
client = clientPool.borrowObject();
|
||||
CloseableHttpResponse response = client.execute(post);
|
||||
try {
|
||||
int code = response.getStatusLine().getStatusCode();
|
||||
@@ -66,13 +72,25 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
} finally {
|
||||
response.close();
|
||||
}
|
||||
} catch (IOException e) {
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error sending metrics to NSQ: " + e.getMessage());
|
||||
if (client != null) {
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e1) {
|
||||
}
|
||||
try {
|
||||
clientPool.invalidateObject(client);
|
||||
} catch (Exception e2) {
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (client != null) {
|
||||
clientPool.returnObject(client);
|
||||
}
|
||||
if (null != out) {
|
||||
try {
|
||||
out.close();
|
||||
out = null;
|
||||
} catch (IOException e) {
|
||||
}
|
||||
}
|
||||
@@ -81,9 +99,9 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(String metric) {
|
||||
public void write(String metric, OutputStream out) {
|
||||
try {
|
||||
out.writeBytes(metric);
|
||||
((DataOutputStream) out).writeBytes(metric);
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error writing metric: " + e.getMessage());
|
||||
try {
|
||||
@@ -93,21 +111,23 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
}
|
||||
}
|
||||
|
||||
public void flush() {
|
||||
}
|
||||
|
||||
public synchronized int shutdown() {
|
||||
public int shutdown() {
|
||||
Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port);
|
||||
try {
|
||||
client.close();
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error closing HttpClient: " + e.getMessage());
|
||||
}
|
||||
clientPool.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int config(OConfigItem config) {
|
||||
public int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
// use max size for maxTotal and maxIdle
|
||||
// no need to activate or passivate
|
||||
poolConfig.setMaxTotal(POOL_MAX_SIZE);
|
||||
poolConfig.setMaxIdle(POOL_MAX_SIZE);
|
||||
poolConfig.setTestOnReturn(true);
|
||||
PooledCloseableHttpClientFactory pooledCloseableHttpClientFactory = new PooledCloseableHttpClientFactory();
|
||||
pooledCloseableHttpClientFactory.config(config);
|
||||
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
@@ -133,17 +153,13 @@ public class WriteNSQPlugin extends CollectDPluginParent implements CollectdConf
|
||||
default:
|
||||
}
|
||||
}
|
||||
if (host == null || port == 0 || topic == null) {
|
||||
Collectd.logError("NSQ host, port, and topic must be configured");
|
||||
return -1;
|
||||
}
|
||||
this.clientPool = new GenericObjectPool(pooledCloseableHttpClientFactory, poolConfig);
|
||||
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("NSQ host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("NSQ port must be configured");
|
||||
}
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-plugin-parent</artifactId>
|
||||
<dependencies>
|
||||
|
||||
+17
-5
@@ -1,5 +1,6 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
@@ -17,6 +18,7 @@ public abstract class CollectDPluginParent {
|
||||
private static final Pattern HADOOP_STATSD_PATTERN = Pattern
|
||||
.compile("([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)\\.([\\w-_]+)");
|
||||
private static final String INSTANCE = " instance=";
|
||||
private static final String NAME = " name=";
|
||||
private static final String SAMPLE = " sample=";
|
||||
private static final String CODE = " code=";
|
||||
private static final String PERIOD = ".";
|
||||
@@ -53,9 +55,9 @@ public abstract class CollectDPluginParent {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public abstract void write(String metric);
|
||||
public abstract void write(String metric, OutputStream out);
|
||||
|
||||
public void process(ValueList vl) {
|
||||
public void process(ValueList vl, OutputStream out) {
|
||||
debugValueList(vl);
|
||||
|
||||
StringBuilder metric = new StringBuilder();
|
||||
@@ -170,17 +172,27 @@ public abstract class CollectDPluginParent {
|
||||
} else if (vl.getPlugin().equals("snmp")) {
|
||||
metric.append("sys.snmp.").append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getTypeInstance().replaceAll(" ", "_"));
|
||||
} else if (vl.getPlugin().equals("GenericJMX")) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
String[] pluginInstanceSplit = vl.getPluginInstance().split("-");
|
||||
if (pluginInstanceSplit.length > 0) {
|
||||
tags.append(INSTANCE).append(pluginInstanceSplit[0].replaceAll(" ", "_"));
|
||||
}
|
||||
if (pluginInstanceSplit.length > 1) {
|
||||
tags.append(NAME).append(pluginInstanceSplit[1].replaceAll(" ", "_"));
|
||||
}
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance())
|
||||
&& notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getTypeInstance()) && notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType()).append(PERIOD)
|
||||
.append(vl.getTypeInstance());
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPluginInstance()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance());
|
||||
tags.append(INSTANCE).append(vl.getPluginInstance().replaceAll(" ", "_"));
|
||||
} else if (notEmpty(vl.getType()) && notEmpty(vl.getPlugin())) {
|
||||
metric.append("sys.").append(vl.getPlugin()).append(PERIOD).append(vl.getType());
|
||||
} else {
|
||||
@@ -203,7 +215,7 @@ public abstract class CollectDPluginParent {
|
||||
String datapoint = MessageFormat.format(PUT, metricName, timestamp.toString(), value.toString(),
|
||||
tagsWithSample.toString());
|
||||
Collectd.logDebug("Writing: " + datapoint);
|
||||
write(datapoint);
|
||||
write(datapoint, out);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ package timely.collectd.plugin;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
|
||||
import net.jcip.annotations.NotThreadSafe;
|
||||
@@ -29,7 +30,7 @@ public class PluginParentTest {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(String metric) {
|
||||
public void write(String metric, OutputStream out) {
|
||||
result = metric;
|
||||
}
|
||||
|
||||
@@ -58,7 +59,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put statsd.dfs.BlocksRead 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=DataNode sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -78,7 +79,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals("put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
}
|
||||
@@ -97,7 +98,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put statsd.baz 1456156976840 1.0 host=r01n01 rack=r01 addl1=foo instance=bar sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -117,7 +118,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.hddtemp.temperature 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -137,7 +138,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.smart_badsectors 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -158,7 +159,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.raw-read-error-rate 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -179,7 +180,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.smart.Total_LBAs_Read 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo code=242 instance=sda sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -200,7 +201,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.snmp.if_octets 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo instance=Ethernet1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -222,7 +223,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.sensors.temperature.coretemp-isa-0000 1456156976840 35.0 host=r01n01 rack=r01 addl1=foo instance=1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -242,7 +243,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.haproxy.run_queue 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -263,7 +264,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.haproxy.queue_current 1456156976840 0.0 host=r01n01 rack=r01 addl1=foo proxy_name=proxy1 service_name=server1 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -284,7 +285,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ethstat.tx_comp_queue_full 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -306,7 +307,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ethstat.rx_bytes 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo queue=1 instance=eth0 sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
@@ -327,7 +328,7 @@ public class PluginParentTest {
|
||||
vl.setDataSet(ds);
|
||||
|
||||
TestPlugin test = new TestPlugin();
|
||||
test.process(vl);
|
||||
test.process(vl, null);
|
||||
assertEquals(
|
||||
"put sys.ipmi.temperature 1456156976840 6.0 host=r01n01 rack=r01 addl1=foo instance=LAN_NIC_Temp_system_board_(3.2) sample=value sampleType=GAUGE\n",
|
||||
result);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>collectd-timely-plugin</artifactId>
|
||||
<dependencies>
|
||||
@@ -12,6 +12,10 @@
|
||||
<groupId>collectd</groupId>
|
||||
<artifactId>collectd-api</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.jcip</groupId>
|
||||
<artifactId>jcip-annotations</artifactId>
|
||||
|
||||
@@ -0,0 +1,125 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import org.apache.commons.pool2.PooledObject;
|
||||
import org.apache.commons.pool2.PooledObjectFactory;
|
||||
import org.apache.commons.pool2.impl.DefaultPooledObject;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.OConfigItem;
|
||||
|
||||
import java.net.Socket;
|
||||
|
||||
public class PooledSocketFactory implements PooledObjectFactory {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private static final long initialBackoff = 2000;
|
||||
private int socketTimeout = 60000; // 60 seconds in milliseconds
|
||||
private long connectionTimeToLive = 300000; // 300 seconds in milliseconds
|
||||
|
||||
public int config(OConfigItem config) {
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
host = child.getValues().get(0).getString();
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "socketTimeout":
|
||||
case "SocketTimeout":
|
||||
socketTimeout = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
case "connectionTimeToLive":
|
||||
case "ConnectionTimeToLive":
|
||||
connectionTimeToLive = Long.parseLong(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
}
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("Timely host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("Timely port must be configured");
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private Socket connect() {
|
||||
|
||||
Socket socket = null;
|
||||
long currentBackoff = initialBackoff;
|
||||
while (socket == null) {
|
||||
try {
|
||||
socket = new Socket(host, port);
|
||||
if (socketTimeout > -1) {
|
||||
socket.setSoTimeout(socketTimeout);
|
||||
}
|
||||
Collectd.logInfo("Connected to Timely at " + host + ":" + port + " from local port:"
|
||||
+ socket.getLocalPort());
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error connecting to Timely at " + host + ":" + port + ". Error: " + e.getMessage()
|
||||
+ ". Will retry connection in " + currentBackoff + " ms.");
|
||||
try {
|
||||
Thread.sleep(currentBackoff);
|
||||
} catch (InterruptedException e1) {
|
||||
|
||||
}
|
||||
// max out reconnect period at one minute
|
||||
currentBackoff = (currentBackoff * 2 > 60000) ? 60000 : currentBackoff * 2;
|
||||
}
|
||||
}
|
||||
return socket;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PooledObject makeObject() throws Exception {
|
||||
return new DefaultPooledObject(connect());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroyObject(PooledObject pooledObject) throws Exception {
|
||||
try {
|
||||
Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port);
|
||||
Socket socket = (Socket) pooledObject.getObject();
|
||||
if (socket != null) {
|
||||
socket.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Collectd.logError("Error closing connection to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
} finally {
|
||||
pooledObject.invalidate();
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean validateObject(PooledObject pooledObject) {
|
||||
if (connectionTimeToLive > -1) {
|
||||
if (pooledObject.getCreateTime() + connectionTimeToLive > System.currentTimeMillis()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
Socket socket = (Socket) pooledObject.getObject();
|
||||
return socket != null && !socket.isClosed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void activateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void passivateObject(PooledObject pooledObject) throws Exception {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
+50
-83
@@ -1,9 +1,12 @@
|
||||
package timely.collectd.plugin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.net.Socket;
|
||||
|
||||
import org.apache.commons.pool2.impl.GenericObjectPool;
|
||||
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;
|
||||
import org.collectd.api.Collectd;
|
||||
import org.collectd.api.CollectdConfigInterface;
|
||||
import org.collectd.api.CollectdShutdownInterface;
|
||||
@@ -19,12 +22,11 @@ import org.collectd.api.ValueList;
|
||||
public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdConfigInterface,
|
||||
CollectdShutdownInterface, CollectdWriteInterface {
|
||||
|
||||
private String host = null;
|
||||
private int port = 0;
|
||||
private Socket sock = null;
|
||||
private PrintWriter out = null;
|
||||
private long connectTime = 0L;
|
||||
private long backoff = 2000;
|
||||
private GenericObjectPool<Socket> socketPool = null;
|
||||
|
||||
// pool should be limited by the number of WriteThreads configured in
|
||||
// collectd
|
||||
final private int POOL_MAX_SIZE = Integer.MAX_VALUE;
|
||||
|
||||
public WriteTimelyPlugin() {
|
||||
Collectd.registerConfig(WriteTimelyPlugin.class.getName(), this);
|
||||
@@ -32,98 +34,63 @@ public class WriteTimelyPlugin extends CollectDPluginParent implements CollectdC
|
||||
Collectd.registerWrite(WriteTimelyPlugin.class.getName(), this);
|
||||
}
|
||||
|
||||
private synchronized int connect() {
|
||||
if (null == sock || !sock.isConnected() || out.checkError()) {
|
||||
if (System.currentTimeMillis() > (connectTime + backoff)) {
|
||||
try {
|
||||
connectTime = System.currentTimeMillis();
|
||||
sock = new Socket(host, port);
|
||||
out = new PrintWriter(sock.getOutputStream(), false);
|
||||
backoff = 2000;
|
||||
Collectd.logInfo("Connected to Timely at " + host + ":" + port);
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error connecting to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
backoff = backoff * 2;
|
||||
sock = null;
|
||||
out = null;
|
||||
Collectd.logWarning("Will retry connection in " + backoff + " ms.");
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
Collectd.logWarning("Not writing to Timely, waiting to reconnect");
|
||||
public int write(ValueList vl) {
|
||||
|
||||
Socket socket = null;
|
||||
try {
|
||||
socket = socketPool.borrowObject();
|
||||
if (socket == null) {
|
||||
return -1;
|
||||
}
|
||||
super.process(vl, socket.getOutputStream());
|
||||
} catch (Exception e) {
|
||||
Collectd.logWarning(e.getMessage());
|
||||
try {
|
||||
// close socket so that the object pool will discard it and
|
||||
// reconnect
|
||||
socket.close();
|
||||
} catch (IOException e1) {
|
||||
Collectd.logError(e1.getMessage());
|
||||
}
|
||||
return -1;
|
||||
} finally {
|
||||
if (socket != null) {
|
||||
socketPool.returnObject(socket);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int write(ValueList vl) {
|
||||
|
||||
int c = connect();
|
||||
if (c != 0) {
|
||||
return c;
|
||||
}
|
||||
super.process(vl);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void write(String metric) {
|
||||
out.write(metric);
|
||||
}
|
||||
|
||||
public synchronized void flush() {
|
||||
if (null != out) {
|
||||
out.flush();
|
||||
public void write(String metric, OutputStream out) {
|
||||
PrintWriter printWriter = new PrintWriter(out, false);
|
||||
printWriter.write(metric);
|
||||
printWriter.flush();
|
||||
if (printWriter.checkError()) {
|
||||
throw new RuntimeException("Error writing to Timely");
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized int shutdown() {
|
||||
Collectd.logInfo("Shutting down connection to Timely at " + host + ":" + port);
|
||||
if (null != sock) {
|
||||
try {
|
||||
if (null != out) {
|
||||
out.close();
|
||||
}
|
||||
sock.close();
|
||||
} catch (IOException e) {
|
||||
Collectd.logError("Error closing connection to Timely at " + host + ":" + port + ". Error: "
|
||||
+ e.getMessage());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
public int shutdown() {
|
||||
socketPool.close();
|
||||
return 0;
|
||||
}
|
||||
|
||||
public synchronized int config(OConfigItem config) {
|
||||
public int config(OConfigItem config) {
|
||||
super.config(config);
|
||||
for (OConfigItem child : config.getChildren()) {
|
||||
switch (child.getKey()) {
|
||||
case "host":
|
||||
case "hostname":
|
||||
case "Host":
|
||||
case "HostName":
|
||||
host = child.getValues().get(0).getString();
|
||||
break;
|
||||
case "Port":
|
||||
case "port":
|
||||
port = Integer.parseInt(child.getValues().get(0).getString());
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
int retval = 0;
|
||||
PooledSocketFactory socketFactory = new PooledSocketFactory();
|
||||
retval = socketFactory.config(config);
|
||||
if (retval == 0) {
|
||||
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
|
||||
// use max size for maxTotal and maxIdle
|
||||
// no need to activate and passivate
|
||||
poolConfig.setMaxTotal(POOL_MAX_SIZE);
|
||||
poolConfig.setMaxIdle(POOL_MAX_SIZE);
|
||||
poolConfig.setTestOnReturn(true);
|
||||
socketPool = new GenericObjectPool(socketFactory, poolConfig);
|
||||
}
|
||||
if (null != host) {
|
||||
return 0;
|
||||
}
|
||||
if (host == null) {
|
||||
Collectd.logError("Timely host must be configured");
|
||||
}
|
||||
if (port == 0) {
|
||||
Collectd.logError("Timely port must be configured");
|
||||
}
|
||||
return -1;
|
||||
return retval;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-5
@@ -19,12 +19,14 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.powermock.api.easymock.PowerMock;
|
||||
import org.powermock.core.classloader.annotations.PowerMockIgnore;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({ Collectd.class })
|
||||
@NotThreadSafe
|
||||
@PowerMockIgnore("javax.management.*")
|
||||
public class WriteTimelyPluginTest {
|
||||
|
||||
private WriteTimelyPlugin plugin = null;
|
||||
@@ -63,7 +65,6 @@ public class WriteTimelyPluginTest {
|
||||
Assert.assertEquals(0, plugin.config(config));
|
||||
// Seed with one metric to create the connection
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
}
|
||||
@@ -77,7 +78,6 @@ public class WriteTimelyPluginTest {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
@@ -94,7 +94,6 @@ public class WriteTimelyPluginTest {
|
||||
Thread.sleep(1000);
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(100);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
server.shutdown();
|
||||
@@ -104,16 +103,18 @@ public class WriteTimelyPluginTest {
|
||||
server.create();
|
||||
Thread t2 = new Thread(server);
|
||||
t2.start();
|
||||
// Need to call this again because the server is not guaranteed to be
|
||||
// listening on the same local port as the first time that it was
|
||||
// started
|
||||
setupPlugin();
|
||||
while (!server.ready()) {
|
||||
Thread.sleep(1000);
|
||||
// Keep sending metrics to plugin to force reconnect
|
||||
int result = plugin.write(createMetric());
|
||||
System.out.println("Wrote to client, result: " + result);
|
||||
Assert.assertEquals(0, result);
|
||||
plugin.flush();
|
||||
}
|
||||
Assert.assertEquals(0, plugin.write(createMetric()));
|
||||
plugin.flush();
|
||||
Thread.sleep(1000);
|
||||
Assert.assertTrue(server.messageReceived());
|
||||
plugin.shutdown();
|
||||
@@ -139,6 +140,7 @@ public class WriteTimelyPluginTest {
|
||||
server.setReceiveBufferSize(8192);
|
||||
host = server.getInetAddress().getHostAddress();
|
||||
port = server.getLocalPort();
|
||||
System.out.println("Local port " + port);
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>timely-common</artifactId>
|
||||
<dependencies>
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@
|
||||
<parent>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
</parent>
|
||||
<build>
|
||||
<plugins>
|
||||
|
||||
+213
-163
@@ -2,6 +2,8 @@
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_TIMELY",
|
||||
"label": "Timely",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
@@ -9,10 +11,10 @@
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
@@ -21,10 +23,10 @@
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
@@ -33,18 +35,18 @@
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Accumulo Overview",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "100",
|
||||
"panels": [
|
||||
{
|
||||
@@ -71,7 +73,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 2,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -105,6 +106,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
@@ -161,7 +163,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 3,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -195,6 +196,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -251,7 +253,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 4,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -285,6 +286,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -341,7 +343,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 5,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -375,6 +376,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -408,28 +410,28 @@
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "Row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 6,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -448,6 +450,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -481,6 +484,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Minor Compactions",
|
||||
@@ -492,7 +496,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -516,18 +524,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -546,6 +550,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -579,6 +584,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Major Compactions",
|
||||
@@ -590,7 +596,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -614,18 +624,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 12,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -644,12 +650,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"counterMax": "",
|
||||
"currentTagKey": "",
|
||||
@@ -668,6 +675,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Thrift Ops",
|
||||
@@ -679,7 +687,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -703,18 +715,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 13,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -733,12 +741,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"counterMax": "",
|
||||
"currentTagKey": "",
|
||||
@@ -757,6 +766,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Thrift Avg Execution Time",
|
||||
@@ -768,7 +778,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -790,28 +804,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 8,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -830,12 +844,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -852,6 +867,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Ops",
|
||||
@@ -863,7 +879,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -887,18 +907,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 9,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -917,12 +933,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -939,6 +956,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Avg",
|
||||
@@ -950,7 +968,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -974,18 +996,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 18,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1004,12 +1022,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1026,6 +1045,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Min",
|
||||
@@ -1037,7 +1057,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1061,18 +1085,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 19,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1091,12 +1111,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1113,6 +1134,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Max",
|
||||
@@ -1124,7 +1146,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1146,28 +1172,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1186,12 +1212,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1208,6 +1235,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Num Ops",
|
||||
@@ -1219,7 +1247,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1243,18 +1275,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 10,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1273,12 +1301,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1293,6 +1322,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Avg Size",
|
||||
@@ -1304,7 +1334,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1328,18 +1362,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 17,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1358,12 +1388,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1378,6 +1409,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Max Size",
|
||||
@@ -1389,7 +1421,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1411,28 +1447,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 14,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1451,12 +1487,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1472,6 +1509,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Packets Received",
|
||||
@@ -1483,7 +1521,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1507,18 +1549,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 15,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1537,12 +1575,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1558,6 +1597,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Packets Sent",
|
||||
@@ -1569,7 +1609,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1593,18 +1637,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 16,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1623,12 +1663,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1644,6 +1685,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Average Latency",
|
||||
@@ -1655,7 +1697,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1677,9 +1723,20 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
@@ -1709,14 +1766,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"schemaVersion": 12,
|
||||
"version": 19,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
}
|
||||
"timezone": "browser",
|
||||
"title": "Accumulo Overview",
|
||||
"version": 4
|
||||
}
|
||||
+434
-353
File diff suppressed because it is too large.
Load diff
+746
-696
File diff suppressed because it is too large.
Load diff
+88
-164
@@ -11,10 +11,10 @@
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
@@ -23,10 +23,10 @@
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
@@ -35,18 +35,19 @@
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Status",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"refresh": "1m",
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "150",
|
||||
"panels": [
|
||||
{
|
||||
@@ -73,7 +74,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 8,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -107,9 +107,10 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"aggregator": "sum",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
@@ -157,7 +158,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 9,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -191,6 +191,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
@@ -198,8 +199,8 @@
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "count",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.collectd.cache_size",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
@@ -243,7 +244,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 10,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -277,6 +277,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -306,28 +307,28 @@
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -346,7 +347,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -364,6 +366,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Inserted",
|
||||
@@ -375,7 +378,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -399,18 +406,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -429,7 +432,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -447,6 +451,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Meta Inserted",
|
||||
@@ -458,7 +463,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -482,18 +491,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 5,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -512,7 +517,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -530,6 +536,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Received",
|
||||
@@ -541,7 +548,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -561,30 +572,18 @@
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -603,12 +602,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -622,6 +622,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Cache Sizes",
|
||||
@@ -633,91 +634,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Queue Length",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -739,9 +660,20 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
@@ -772,15 +704,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 12,
|
||||
"version": 8,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
"timezone": "browser",
|
||||
"title": "Timely Status",
|
||||
"version": 4
|
||||
}
|
||||
+108
-63
@@ -1,40 +1,59 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_NAME",
|
||||
"name": "DS_TIMELY-STANDALONE",
|
||||
"label": "Timely-Standalone",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource"
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Standalone Demo",
|
||||
"originalTitle": "Timely Standalone Demo",
|
||||
"tags": ["imported"],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "timely-datasource",
|
||||
"name": "Timely",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"refresh": "5s",
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)",
|
||||
"thresholdLine": false
|
||||
},
|
||||
"grid": {},
|
||||
"id": 1,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
@@ -56,6 +75,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -93,17 +113,23 @@
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Status",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": false,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -124,28 +150,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "Row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 2,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -166,12 +192,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -186,17 +213,23 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Host",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -220,18 +253,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -252,12 +281,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_rack",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -272,17 +302,23 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Rack",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -306,18 +342,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -338,12 +370,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -356,17 +389,23 @@
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Cumulative CPU",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -388,9 +427,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"imported"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
@@ -421,14 +473,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 12,
|
||||
"version": 1,
|
||||
"links": []
|
||||
}
|
||||
"timezone": "browser",
|
||||
"title": "Timely Standalone Demo",
|
||||
"version": 6
|
||||
}
|
||||
+37
-23
@@ -227,33 +227,47 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
|
||||
key: 'performTimeSeriesQuery',
|
||||
value: function performTimeSeriesQuery(queries, start, end) {
|
||||
var msResolution = true;
|
||||
var responsePromises = [];
|
||||
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: queries,
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
// execute backend datasourceRequests separately
|
||||
// for better performance and load balancing
|
||||
_.each(queries, function (query) {
|
||||
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: [query],
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query',
|
||||
data: reqBody
|
||||
};
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query?metric=' + query.metric,
|
||||
data: reqBody
|
||||
};
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
responsePromises.push(this.backendSrv.datasourceRequest(options));
|
||||
}.bind(this));
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
// wait until all promised datasourceRequests complete
|
||||
// and then return combined responses
|
||||
return Promise.all(responsePromises).then(function (queryResponses) {
|
||||
var combinedResponse = queryResponses[0];
|
||||
for (var x = 1; x < queryResponses.length; x++) {
|
||||
combinedResponse.data = combinedResponse.data.concat(queryResponses[x].data);
|
||||
}
|
||||
return combinedResponse;
|
||||
}.bind(this));
|
||||
}
|
||||
}, {
|
||||
key: '_saveTagKeys',
|
||||
@@ -408,7 +422,7 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
|
||||
var tagData = [];
|
||||
|
||||
if (!_.isEmpty(md.tags)) {
|
||||
_.each(_.pairs(md.tags), function (tag) {
|
||||
_.each(_.toPairs(md.tags), function (tag) {
|
||||
if (_.has(groupByTags, tag[0])) {
|
||||
tagData.push(tag[0] + "=" + tag[1]);
|
||||
}
|
||||
@@ -493,7 +507,7 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
return target.metric === metricData.metric;
|
||||
} else {
|
||||
return target.metric === metricData.metric && _.all(target.tags, function (tagV, tagK) {
|
||||
return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) {
|
||||
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
|
||||
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
|
||||
}.bind(this));
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","reqBody","showQuery","_addCredentialOptions","headers","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","pairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","all","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GAAxB,EAA6B;AAC3B,uBAAO,EAAEA,QAAQ,SAAV,EAAqBC,SAAS,wBAA9B,EAAwDC,OAAO,SAA/D,EAAP;AACD;AALine truncated
|
||||
{"version":3,"sources":["../../src/datasource/datasource.js"],"names":["_","angular","dateMath","TimelyDatasource","instanceSettings","$q","backendSrv","templateSrv","contextSrv","type","name","jsonData","httpsPort","timelyHost","wsPort","basicAuths","url","_makeHttpsUrl","withCredentials","basicAuth","supportMetrics","tagKeys","aggregatorsPromise","filterTypesPromise","options","start","convertToTSDBTime","rangeRaw","from","end","to","qs","each","targets","target","metric","push","convertTargetToQuery","bind","queries","compact","isEmpty","d","defer","resolve","data","promise","groupByTags","query","filters","length","val","tagk","tags","key","performTimeSeriesQuery","then","response","metricToTargetMapping","mapMetricsToTargets","result","map","metricData","index","_saveTagKeys","transformMetricData","datasourceRequest","method","status","message","title","eventList","aggregator","annotation","results","annotationObject","annotations","isGlobal","globalAnnotations","event","description","time","Math","floor","startTime","text","notes","when","interpolated","replace","err","reject","responseTransform","value","metrics_regex","tag_names_regex","tag_values_regex","tag_names_suggest_regex","tag_values_suggest_regex","metrics_query","match","_performSuggestQuery","tag_names_query","_performMetricKeyLookup","tag_values_query","_performMetricKeyValueLookup","tag_names_suggest_query","tag_values_suggest_query","msResolution","responsePromises","reqBody","showQuery","_addCredentialOptions","headers","Promise","all","queryResponses","combinedResponse","x","concat","Object","keys","aggregateTags","tag","_get","q","max","keysArray","split","trim","keysQuery","splice","join","m","limit","tagvs","r","indexOf","tagks","tagv","relativeUrl","params","isArray","sort","md","metricLabel","createMetricLabel","dps","v","k","datapoints","alias","scopedVars","clone","label","tagData","toPairs","has","hide","shouldComputeRate","rate","rateOptions","counter","isCounter","counterMax","parseInt","counterResetValue","resetValue","disableDownsampling","interval","downsampleInterval","parseFloat","downsample","downsampleAggregator","downsampleFillPolicy","copy","filter_key","filter","tag_key","metrics","interpolatedTagValue","findIndex","every","tagV","tagK","date","roundUp","parse","valueOf"],"mappings":";;;;;;;;;;;;;;;AAAOA,O;;AACAC,a;;AACKC,c;;;;;;;;;;;;;;;;;;;;;kCAECC,gB;;AAEX;AACA,kCAAYC,gBAAZ,EAA8BC,EAA9B,EAAkCC,UAAlC,EAA8CC,WAA9C,EAA2DC,UAA3D,EAAuE;AAAA;;AACrE,eAAKC,IAAL,GAAYL,iBAAiBK,IAA7B;AACA,eAAKC,IAAL,GAAYN,iBAAiBM,IAA7B;;AAEA,eAAKC,QAAL,GAAgBP,iBAAiBO,QAAjB,IAA6B,EAA7C;AACA,eAAKC,SAAL,GAAiBR,iBAAiBO,QAAjB,CAA0BC,SAA3C;AACA,eAAKC,UAAL,GAAkBT,iBAAiBO,QAAjB,CAA0BE,UAA5C;AACA,eAAKC,MAAL,GAAcV,iBAAiBO,QAAjB,CAA0BG,MAAxC;AACA,cAAI,CAACV,iBAAiBO,QAAjB,CAA0BI,UAA/B,EAA0C;AACxCX,6BAAiBO,QAAjB,CAA0BI,UAA1B,GAAuC,KAAvC;AACD;;AAED,eAAKC,GAAL,GAAW,KAAKC,aAAL,EAAX;AACA,eAAKC,eAAL,GAAuB,IAAvB;AACA,eAAKC,SAAL,GAAiB,KAAjB;;AAEA,eAAKC,cAAL,GAAsB,IAAtB;AACA,eAAKC,OAAL,GAAe,EAAf;;AAEA,eAAKhB,EAAL,GAAUA,EAAV;AACA,eAAKC,UAAL,GAAkBA,UAAlB;AACA,eAAKC,WAAL,GAAmBA,WAAnB;AACA,eAAKC,UAAL,GAAkBA,UAAlB;;AAEA,eAAKc,kBAAL,GAA0B,IAA1B;AACA,eAAKC,kBAAL,GAA0B,IAA1B;AAED;;AAED;;;;;gCACMC,O,EAAQ;AACZ,gBAAIC,QAAQ,KAAKC,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBC,IAAxC,EAA8C,KAA9C,CAAZ;AACA,gBAAIC,MAAM,KAAKH,iBAAL,CAAuBF,QAAQG,QAAR,CAAiBG,EAAxC,EAA4C,IAA5C,CAAV;AACA,gBAAIC,KAAK,EAAT;;AAEA/B,cAAEgC,IAAF,CAAOR,QAAQS,OAAf,EAAwB,UAASC,MAAT,EAAiB;AACvC,kBAAI,CAACA,OAAOC,MAAZ,EAAoB;AAAE;AAAS;AAC/BJ,iBAAGK,IAAH,CAAQ,KAAKC,oBAAL,CAA0BH,MAA1B,EAAkCV,OAAlC,CAAR;AACD,aAHuB,CAGtBc,IAHsB,CAGjB,IAHiB,CAAxB;;AAKA,gBAAIC,UAAUvC,EAAEwC,OAAF,CAAUT,EAAV,CAAd;;AAEA;AACA,gBAAI/B,EAAEyC,OAAF,CAAUF,OAAV,CAAJ,EAAwB;AACtB,kBAAIG,IAAI,KAAKrC,EAAL,CAAQsC,KAAR,EAAR;AACAD,gBAAEE,OAAF,CAAU,EAAEC,MAAM,EAAR,EAAV;AACA,qBAAOH,EAAEI,OAAT;AACD;;AAED,gBAAIC,cAAc,EAAlB;AACA/C,cAAEgC,IAAF,CAAOO,OAAP,EAAgB,UAASS,KAAT,EAAgB;AAC9B,kBAAIA,MAAMC,OAAN,IAAiBD,MAAMC,OAAN,CAAcC,MAAd,GAAuB,CAA5C,EAA+C;AAC7ClD,kBAAEgC,IAAF,CAAOgB,MAAMC,OAAb,EAAsB,UAASE,GAAT,EAAc;AAClCJ,8BAAYI,IAAIC,IAAhB,IAAwB,IAAxB;AACD,iBAFD;AAGD,eAJD,MAIO;AACLpD,kBAAEgC,IAAF,CAAOgB,MAAMK,IAAb,EAAmB,UAASF,GAAT,EAAcG,GAAd,EAAmB;AACpCP,8BAAYO,GAAZ,IAAmB,IAAnB;AACD,iBAFD;AAGD;AACF,aAVD;;AAYA,mBAAO,KAAKC,sBAAL,CAA4BhB,OAA5B,EAAqCd,KAArC,EAA4CI,GAA5C,EAAiD2B,IAAjD,CAAsD,UAASC,QAAT,EAAmB;AAC9E,kBAAIC,wBAAwB,KAAKC,mBAAL,CAAyBF,SAASZ,IAAlC,EAAwCrB,OAAxC,CAA5B;AACA,kBAAIoC,SAAS5D,EAAE6D,GAAF,CAAMJ,SAASZ,IAAf,EAAqB,UAASiB,UAAT,EAAqBC,KAArB,EAA4B;AAC5DA,wBAAQL,sBAAsBK,KAAtB,CAAR;AACA,oBAAIA,UAAU,CAAC,CAAf,EAAkB;AAChBA,0BAAQ,CAAR;AACD;AACD,qBAAKC,YAAL,CAAkBF,UAAlB;;AAEA,uBAAO,KAAKG,mBAAL,CAAyBH,UAAzB,EAAqCf,WAArC,EAAkDvB,QAAQS,OAAR,CAAgB8B,KAAhB,CAAlD,EAA0EvC,OAA1E,CAAP;AACD,eARiC,CAQhCc,IARgC,CAQ3B,IAR2B,CAArB,CAAb;AASA,qBAAO,EAAEO,MAAMe,MAAR,EAAP;AACD,aAZ4D,CAY3DtB,IAZ2D,CAYtD,IAZsD,CAAtD,CAAP;AAaD;;;2CAGe;AACd,mBAAO,KAAKhC,UAAL,CAAgB4D,iBAAhB,CAAkC;AACvClD,mBAAK,KAAKA,GAAL,GAAW,UADuB;AAEvCmD,sBAAQ;AAF+B,aAAlC,EAGJX,IAHI,CAGC,oBAAY;AAClB,kBAAIC,SAASW,MAAT,KAAoB,GLine truncated
|
||||
Vendored
+3
-3
@@ -23,8 +23,8 @@
|
||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||
],
|
||||
"version": "0.0.4-SNAPSHOT",
|
||||
"updated": "2016-10-27"
|
||||
"version": "0.0.6-SNAPSHOT",
|
||||
"updated": "2017-08-11"
|
||||
},
|
||||
|
||||
"includes": [
|
||||
@@ -34,7 +34,7 @@
|
||||
],
|
||||
|
||||
"dependencies": {
|
||||
"grafanaVersion": "3.x.x",
|
||||
"grafanaVersion": "4.x.x",
|
||||
"plugins": []
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,8 @@
|
||||
"grunt-execute": "~0.2.2",
|
||||
"grunt-mocha-test": "~0.12.7",
|
||||
"grunt-systemjs-builder": "^0.2.5",
|
||||
"load-grunt-tasks": "~3.2.0"
|
||||
"load-grunt-tasks": "~3.2.0",
|
||||
"mocha": "^5.2.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"ag-grid": "^5.0.2",
|
||||
|
||||
@@ -23,8 +23,8 @@
|
||||
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
|
||||
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
|
||||
],
|
||||
"version": "0.0.4-SNAPSHOT",
|
||||
"updated": "2016-10-27"
|
||||
"version": "0.0.6-SNAPSHOT",
|
||||
"updated": "2017-08-11"
|
||||
},
|
||||
|
||||
"includes": [
|
||||
@@ -34,7 +34,7 @@
|
||||
],
|
||||
|
||||
"dependencies": {
|
||||
"grafanaVersion": "3.x.x",
|
||||
"grafanaVersion": "4.x.x",
|
||||
"plugins": []
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_TIMELY",
|
||||
"label": "Timely",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
@@ -9,10 +11,10 @@
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
@@ -21,10 +23,10 @@
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
@@ -33,18 +35,18 @@
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Accumulo Overview",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "100",
|
||||
"panels": [
|
||||
{
|
||||
@@ -71,7 +73,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 2,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -105,6 +106,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
@@ -161,7 +163,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 3,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -195,6 +196,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -251,7 +253,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 4,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -285,6 +286,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -341,7 +343,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 5,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -375,6 +376,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -408,28 +410,28 @@
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "Row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 6,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -448,6 +450,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -481,6 +484,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Minor Compactions",
|
||||
@@ -492,7 +496,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -516,18 +524,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -546,6 +550,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -579,6 +584,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Major Compactions",
|
||||
@@ -590,7 +596,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -614,18 +624,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 12,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -644,12 +650,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"counterMax": "",
|
||||
"currentTagKey": "",
|
||||
@@ -668,6 +675,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Thrift Ops",
|
||||
@@ -679,7 +687,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -703,18 +715,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 13,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -733,12 +741,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"counterMax": "",
|
||||
"currentTagKey": "",
|
||||
@@ -757,6 +766,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Thrift Avg Execution Time",
|
||||
@@ -768,7 +778,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -790,28 +804,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 8,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -830,12 +844,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -852,6 +867,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Ops",
|
||||
@@ -863,7 +879,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -887,18 +907,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 9,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -917,12 +933,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -939,6 +956,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Avg",
|
||||
@@ -950,7 +968,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -974,18 +996,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 18,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1004,12 +1022,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1026,6 +1045,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Min",
|
||||
@@ -1037,7 +1057,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1061,18 +1085,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 19,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1091,12 +1111,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1113,6 +1134,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Write Ahead Logs Write Time Max",
|
||||
@@ -1124,7 +1146,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1146,28 +1172,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1186,12 +1212,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1208,6 +1235,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Num Ops",
|
||||
@@ -1219,7 +1247,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1243,18 +1275,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 10,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1273,12 +1301,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1293,6 +1322,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Avg Size",
|
||||
@@ -1304,7 +1334,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1328,18 +1362,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 17,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1358,12 +1388,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1378,6 +1409,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Mutation Array Max Size",
|
||||
@@ -1389,7 +1421,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1411,28 +1447,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 14,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1451,12 +1487,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1472,6 +1509,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Packets Received",
|
||||
@@ -1483,7 +1521,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1507,18 +1549,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 15,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1537,12 +1575,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1558,6 +1597,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Packets Sent",
|
||||
@@ -1569,7 +1609,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1593,18 +1637,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 16,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -1623,12 +1663,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 4,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -1644,6 +1685,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Zookeeper Average Latency",
|
||||
@@ -1655,7 +1697,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -1677,9 +1723,20 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
@@ -1709,14 +1766,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"schemaVersion": 12,
|
||||
"version": 19,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
}
|
||||
"timezone": "browser",
|
||||
"title": "Accumulo Overview",
|
||||
"version": 4
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -11,10 +11,10 @@
|
||||
],
|
||||
"__requires": [
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
@@ -23,10 +23,10 @@
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "3.1.0"
|
||||
"type": "panel",
|
||||
"id": "singlestat",
|
||||
"name": "Singlestat",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
@@ -35,18 +35,19 @@
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Status",
|
||||
"tags": [],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"refresh": "1m",
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "150",
|
||||
"panels": [
|
||||
{
|
||||
@@ -73,7 +74,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 8,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -107,9 +107,10 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
"aggregator": "sum",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
@@ -157,7 +158,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 9,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -191,6 +191,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "count",
|
||||
@@ -198,8 +199,8 @@
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "count",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"downsampleInterval": "1s",
|
||||
"metric": "sys.collectd.cache_size",
|
||||
"refId": "A",
|
||||
"tags": {}
|
||||
}
|
||||
@@ -243,7 +244,6 @@
|
||||
"hideTimeOverride": true,
|
||||
"id": 10,
|
||||
"interval": null,
|
||||
"isNew": true,
|
||||
"links": [],
|
||||
"mappingType": 1,
|
||||
"mappingTypes": [
|
||||
@@ -277,6 +277,7 @@
|
||||
"lineColor": "rgb(31, 120, 193)",
|
||||
"show": false
|
||||
},
|
||||
"tableColumn": "",
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
@@ -306,28 +307,28 @@
|
||||
"valueName": "avg"
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -346,7 +347,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -364,6 +366,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Inserted",
|
||||
@@ -375,7 +378,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -399,18 +406,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -429,7 +432,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -447,6 +451,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Meta Inserted",
|
||||
@@ -458,7 +463,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -482,18 +491,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 5,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -512,7 +517,8 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 4,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
@@ -530,6 +536,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Metrics Received",
|
||||
@@ -541,7 +548,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -561,30 +572,18 @@
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 7,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
@@ -603,12 +602,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"spaceLength": 10,
|
||||
"span": 3,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -622,6 +622,7 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Cache Sizes",
|
||||
@@ -633,91 +634,11 @@
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
},
|
||||
{
|
||||
"format": "short",
|
||||
"label": null,
|
||||
"logBase": 1,
|
||||
"max": null,
|
||||
"min": null,
|
||||
"show": true
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_TIMELY}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 0,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"id": 11,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"avg": false,
|
||||
"current": false,
|
||||
"max": false,
|
||||
"min": false,
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"total": false,
|
||||
"values": false
|
||||
},
|
||||
"lines": true,
|
||||
"linewidth": 2,
|
||||
"links": [],
|
||||
"nullPointMode": "connected",
|
||||
"percentage": false,
|
||||
"pointradius": 5,
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"span": 6,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "sum",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
"downsampleAggregator": "sum",
|
||||
"downsampleFillPolicy": "none",
|
||||
"downsampleInterval": "1m",
|
||||
"metric": "sys.collectd.queue_length",
|
||||
"refId": "A",
|
||||
"tags": {
|
||||
"host": ".*"
|
||||
}
|
||||
}
|
||||
],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CollectD Queue Length",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -739,9 +660,20 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-1h",
|
||||
"to": "now"
|
||||
@@ -772,15 +704,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "1m",
|
||||
"schemaVersion": 12,
|
||||
"version": 8,
|
||||
"links": [],
|
||||
"gnetId": null
|
||||
"timezone": "browser",
|
||||
"title": "Timely Status",
|
||||
"version": 4
|
||||
}
|
||||
@@ -1,40 +1,59 @@
|
||||
{
|
||||
"__inputs": [
|
||||
{
|
||||
"name": "DS_NAME",
|
||||
"name": "DS_TIMELY-STANDALONE",
|
||||
"label": "Timely-Standalone",
|
||||
"description": "",
|
||||
"type": "datasource",
|
||||
"pluginId": "timely-datasource"
|
||||
"pluginId": "timely-datasource",
|
||||
"pluginName": "Timely"
|
||||
}
|
||||
],
|
||||
"id": null,
|
||||
"title": "Timely Standalone Demo",
|
||||
"originalTitle": "Timely Standalone Demo",
|
||||
"tags": ["imported"],
|
||||
"style": "dark",
|
||||
"timezone": "browser",
|
||||
"__requires": [
|
||||
{
|
||||
"type": "grafana",
|
||||
"id": "grafana",
|
||||
"name": "Grafana",
|
||||
"version": "4.4.1"
|
||||
},
|
||||
{
|
||||
"type": "panel",
|
||||
"id": "graph",
|
||||
"name": "Graph",
|
||||
"version": ""
|
||||
},
|
||||
{
|
||||
"type": "datasource",
|
||||
"id": "timely-datasource",
|
||||
"name": "Timely",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
],
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"editable": true,
|
||||
"gnetId": null,
|
||||
"graphTooltip": 0,
|
||||
"hideControls": false,
|
||||
"sharedCrosshair": false,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"refresh": "5s",
|
||||
"rows": [
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)",
|
||||
"thresholdLine": false
|
||||
},
|
||||
"grid": {},
|
||||
"id": 1,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
@@ -56,6 +75,7 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
@@ -93,17 +113,23 @@
|
||||
"refId": "C"
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Timely Status",
|
||||
"tooltip": {
|
||||
"msResolution": true,
|
||||
"shared": false,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -124,28 +150,28 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "Row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "Row",
|
||||
"titleSize": "h6"
|
||||
},
|
||||
{
|
||||
"collapse": false,
|
||||
"editable": true,
|
||||
"height": "250px",
|
||||
"panels": [
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 2,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -166,12 +192,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_host",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -186,17 +213,23 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Host",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -220,18 +253,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 3,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -252,12 +281,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "$tag_rack",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -272,17 +302,23 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "CPU User by Rack",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -306,18 +342,14 @@
|
||||
{
|
||||
"aliasColors": {},
|
||||
"bars": false,
|
||||
"datasource": "${DS_NAME}",
|
||||
"dashLength": 10,
|
||||
"dashes": false,
|
||||
"datasource": "${DS_TIMELY-STANDALONE}",
|
||||
"editable": true,
|
||||
"error": false,
|
||||
"fill": 1,
|
||||
"grid": {
|
||||
"threshold1": null,
|
||||
"threshold1Color": "rgba(216, 200, 27, 0.27)",
|
||||
"threshold2": null,
|
||||
"threshold2Color": "rgba(234, 112, 112, 0.22)"
|
||||
},
|
||||
"grid": {},
|
||||
"id": 4,
|
||||
"isNew": true,
|
||||
"legend": {
|
||||
"alignAsTable": true,
|
||||
"avg": false,
|
||||
@@ -338,12 +370,13 @@
|
||||
"points": false,
|
||||
"renderer": "flot",
|
||||
"seriesOverrides": [],
|
||||
"spaceLength": 10,
|
||||
"span": 12,
|
||||
"stack": false,
|
||||
"steppedLine": false,
|
||||
"targets": [
|
||||
{
|
||||
"aggregator": "max",
|
||||
"aggregator": "none",
|
||||
"alias": "",
|
||||
"currentTagKey": "",
|
||||
"currentTagValue": "",
|
||||
@@ -356,17 +389,23 @@
|
||||
"tags": {}
|
||||
}
|
||||
],
|
||||
"thresholds": [],
|
||||
"timeFrom": null,
|
||||
"timeShift": null,
|
||||
"title": "Cumulative CPU",
|
||||
"tooltip": {
|
||||
"msResolution": false,
|
||||
"shared": true,
|
||||
"sort": 0,
|
||||
"value_type": "cumulative"
|
||||
},
|
||||
"type": "graph",
|
||||
"xaxis": {
|
||||
"show": true
|
||||
"buckets": null,
|
||||
"mode": "time",
|
||||
"name": null,
|
||||
"show": true,
|
||||
"values": []
|
||||
},
|
||||
"yaxes": [
|
||||
{
|
||||
@@ -388,9 +427,22 @@
|
||||
]
|
||||
}
|
||||
],
|
||||
"title": "New row"
|
||||
"repeat": null,
|
||||
"repeatIteration": null,
|
||||
"repeatRowId": null,
|
||||
"showTitle": false,
|
||||
"title": "New row",
|
||||
"titleSize": "h6"
|
||||
}
|
||||
],
|
||||
"schemaVersion": 14,
|
||||
"style": "dark",
|
||||
"tags": [
|
||||
"imported"
|
||||
],
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"time": {
|
||||
"from": "now-15m",
|
||||
"to": "now"
|
||||
@@ -421,14 +473,7 @@
|
||||
"30d"
|
||||
]
|
||||
},
|
||||
"templating": {
|
||||
"list": []
|
||||
},
|
||||
"annotations": {
|
||||
"list": []
|
||||
},
|
||||
"refresh": "5s",
|
||||
"schemaVersion": 12,
|
||||
"version": 1,
|
||||
"links": []
|
||||
}
|
||||
"timezone": "browser",
|
||||
"title": "Timely Standalone Demo",
|
||||
"version": 6
|
||||
}
|
||||
@@ -183,33 +183,47 @@ export class TimelyDatasource {
|
||||
|
||||
performTimeSeriesQuery(queries, start, end){
|
||||
var msResolution = true;
|
||||
var responsePromises = [];
|
||||
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: queries,
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
// execute backend datasourceRequests separately
|
||||
// for better performance and load balancing
|
||||
_.each(queries, function(query) {
|
||||
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
var reqBody = {
|
||||
start: start,
|
||||
queries: [query],
|
||||
msResolution: msResolution,
|
||||
globalAnnotations: true,
|
||||
showQuery: true
|
||||
};
|
||||
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query',
|
||||
data: reqBody
|
||||
};
|
||||
// Relative queries (e.g. last hour) don't include an end time
|
||||
if (end) {
|
||||
reqBody.end = end;
|
||||
}
|
||||
|
||||
this._addCredentialOptions(options);
|
||||
var options = {
|
||||
method: 'POST',
|
||||
url: this.url + '/api/query?metric=' + query.metric,
|
||||
data: reqBody
|
||||
};
|
||||
this._addCredentialOptions(options);
|
||||
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
// In case the backend is 3rd-party hosted and does not suport OPTIONS, urlencoded requests
|
||||
// go as POST rather than OPTIONS+POST
|
||||
options.headers = { 'Content-Type': 'application/x-www-form-urlencoded' };
|
||||
responsePromises.push(this.backendSrv.datasourceRequest(options));
|
||||
}.bind(this));
|
||||
|
||||
return this.backendSrv.datasourceRequest(options);
|
||||
// wait until all promised datasourceRequests complete
|
||||
// and then return combined responses
|
||||
return Promise.all(responsePromises).then(function(queryResponses) {
|
||||
var combinedResponse = queryResponses[0];
|
||||
for (var x=1; x < queryResponses.length; x++) {
|
||||
combinedResponse.data = combinedResponse.data.concat(queryResponses[x].data);
|
||||
}
|
||||
return combinedResponse;
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
_saveTagKeys(metricData) {
|
||||
@@ -347,7 +361,7 @@ export class TimelyDatasource {
|
||||
var tagData = [];
|
||||
|
||||
if (!_.isEmpty(md.tags)) {
|
||||
_.each(_.pairs(md.tags), function(tag) {
|
||||
_.each(_.toPairs(md.tags), function(tag) {
|
||||
if (_.has(groupByTags, tag[0])) {
|
||||
tagData.push(tag[0] + "=" + tag[1]);
|
||||
}
|
||||
@@ -430,9 +444,7 @@ export class TimelyDatasource {
|
||||
if (target.filters && target.filters.length > 0) {
|
||||
return target.metric === metricData.metric;
|
||||
} else {
|
||||
return target.metric === metricData.metric &&
|
||||
_.all(target.tags,
|
||||
function(tagV, tagK) {
|
||||
return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) {
|
||||
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
|
||||
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*";
|
||||
}.bind(this));
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>timely</groupId>
|
||||
<artifactId>timely</artifactId>
|
||||
<version>0.0.4</version>
|
||||
<version>1.0.1-SNAPSHOT</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Timely</name>
|
||||
<description>A secure and scalable time series database based on Apache Accumulo</description>
|
||||
@@ -26,11 +26,9 @@
|
||||
<module>common</module>
|
||||
<module>client</module>
|
||||
<module>server</module>
|
||||
<module>collectd-timely-plugin</module>
|
||||
<module>collectd-nsq-plugin</module>
|
||||
<module>collectd-plugin-parent</module>
|
||||
<module>grafana</module>
|
||||
<module>analytics</module>
|
||||
<module>balancer</module>
|
||||
</modules>
|
||||
<properties>
|
||||
<!-- Configure the os-maven-plugin extension to expand the classifier on -->
|
||||
@@ -46,7 +44,10 @@
|
||||
<version.caffeine>2.3.3</version.caffeine>
|
||||
<version.collectd>5.5.1</version.collectd>
|
||||
<version.commons-collections4>4.1</version.commons-collections4>
|
||||
<version.commons-configuration>1.10</version.commons-configuration>
|
||||
<version.commons-io>2.6</version.commons-io>
|
||||
<version.commons-lang3>3.5</version.commons-lang3>
|
||||
<version.commons-pool2>2.4.2</version.commons-pool2>
|
||||
<version.disruptor>3.3.6</version.disruptor>
|
||||
<version.fast-classpath-scanner>2.0.5</version.fast-classpath-scanner>
|
||||
<version.google-flatbuffers>1.4.0</version.google-flatbuffers>
|
||||
@@ -59,11 +60,12 @@
|
||||
<version.jsoup>1.9.2</version.jsoup>
|
||||
<version.junit>4.12</version.junit>
|
||||
<version.log4j>2.6.2</version.log4j>
|
||||
<version.netty>4.0.38.Final</version.netty>
|
||||
<version.netty-tcnative>1.1.33.Fork18</version.netty-tcnative>
|
||||
<version.netty>4.0.56.Final</version.netty>
|
||||
<version.netty-tcnative>2.0.7.Final</version.netty-tcnative>
|
||||
<version.powermock>1.6.4</version.powermock>
|
||||
<version.slf4j>1.7.21</version.slf4j>
|
||||
<version.spring-boot>1.4.1.RELEASE</version.spring-boot>
|
||||
<version.spring-websocket>5.0.8.RELEASE</version.spring-websocket>
|
||||
<!-- plugin versions -->
|
||||
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
|
||||
<version.findbugs-maven>3.0.3</version.findbugs-maven>
|
||||
@@ -136,6 +138,11 @@
|
||||
<artifactId>commons-beanutils</artifactId>
|
||||
<version>LATEST</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>fi.iki.yak</groupId>
|
||||
<artifactId>compression-gorilla</artifactId>
|
||||
<version>2.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>io.github.lukehutch</groupId>
|
||||
<artifactId>fast-classpath-scanner</artifactId>
|
||||
@@ -163,6 +170,16 @@
|
||||
<artifactId>commons-collections4</artifactId>
|
||||
<version>${version.commons-collections4}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-configuration</groupId>
|
||||
<artifactId>commons-configuration</artifactId>
|
||||
<version>${version.commons-configuration}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-io</groupId>
|
||||
<artifactId>commons-io</artifactId>
|
||||
<version>${version.commons-io}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
@@ -202,6 +219,11 @@
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>${version.commons-lang3}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-pool2</artifactId>
|
||||
<version>${version.commons-pool2}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.hadoop</groupId>
|
||||
<artifactId>hadoop-common</artifactId>
|
||||
@@ -509,4 +531,17 @@
|
||||
</plugins>
|
||||
</pluginManagement>
|
||||
</build>
|
||||
<profiles>
|
||||
<profile>
|
||||
<id>collectd</id>
|
||||
<activation>
|
||||
<activeByDefault>false</activeByDefault>
|
||||
</activation>
|
||||
<modules>
|
||||
<module>collectd-timely-plugin</module>
|
||||
<module>collectd-nsq-plugin</module>
|
||||
<module>collectd-plugin-parent</module>
|
||||
</modules>
|
||||
</profile>
|
||||
</profiles>
|
||||
</project>
|
||||
+1
-6
@@ -1,13 +1,8 @@
|
||||
FROM centos:centos7
|
||||
|
||||
MAINTAINER Jim Klucar "[email protected]"
|
||||
|
||||
VOLUME /timely-server-src
|
||||
VOLUME /timely
|
||||
|
||||
|
||||
|
||||
|
||||
RUN yum -y update && \
|
||||
yum -y install java-1.8.0-openjdk-devel maven openssl apr wget epel-release && \
|
||||
yum -y install bouncycastle && \
|
||||
@@ -20,5 +15,5 @@ ENV JAVA_HOME /usr/lib/jvm/java-1.8.0-openjdk
|
||||
|
||||
EXPOSE 9804 54321 54322
|
||||
|
||||
ENV TIMELY_VERSION 0.0.3-SNAPSHOT
|
||||
ENV TIMELY_VERSION 0.0.6-SNAPSHOT
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Loaded 100 of 177 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user