Compare commits

...
Author SHA1 Message Date
Bill Oley 3f4ce0623d Refactor BalancedMetricResolver to allow HDFS and FILE backing, add callbacks for major events, begin working on tests
Refactor BalancedMetricResolver, allow FILE or HDFS persister, add callbacks for events, begin working on testing

Fixes
2020-04-16 16:32:55 -04:00
Bill Oley 2aedf9c2e5 closes #199 Fix issues when using a generated (self-signed) server certificate 2020-04-14 18:18:32 -04:00
Bill Oley 9ef8758dfb closes #198 Cleaner shutdown of resources for Timely server, balancer, grafana-auth 2020-04-14 18:18:27 -04:00
Bill Oley 489bfcc541 closes #197 Fix scan range in MetaCacheImpl 2020-04-14 17:03:14 -04:00
Bill Oley cb2679d40e closes #200 Initialize non-cached metrics stored in zookeeper 2020-04-14 17:03:14 -04:00
Bill Oley cb02950919 Refactor BalancedMetricResolver, allow FILE or HDFS persister, add callbacks for events, begin working on testing 2020-03-28 14:21:55 -04:00
Bill Oley 71cdaec2e4 Refactor BalancedMetricResolver to allow HDFS and FILE backing, add callbacks for major events, begin working on tests 2020-03-28 14:19:53 -04:00
Bill Oley c19e1d8b2f Set version to 1.2.8-SNAPSHOT 2020-02-21 10:20:48 -05:00
Bill Oley cb73e8e75c Tag version 1.2.7 2020-02-21 10:02:31 -05:00
Bill Oley 4c508c1a7d Merge pull request #195 from dtspence/TIMELY-194
Fixed #194, corrected datasource.js to look at wildcard tag value
2020-02-21 08:55:37 -05:00
dtspence c6c2c0d41e Fixed #194, corrected datasource.js to look at wildcard tag value
The alignment process to match tag query results with the metric definition
looks at the tag k/v pairs and makes a special consideration for a wildcard tag value.
The change adjusts datasource.js so that the '.*' notation will match a query result
and metric definition.
2020-02-05 19:56:03 +00:00
Christopher Tubbs a24cef78b3 Update jackson (multiple CVEs) (#193) 2019-11-07 23:05:38 -05:00
Bill Oley 08f99f6540 Set version to 1.2.7-SNAPSHOT 2019-10-30 12:33:49 -04:00
Bill Oley be11da7d15 Tag version 1.2.6 2019-10-30 12:30:33 -04:00
Bill Oley 4bc5753328 fixes #192 Thread deadlock in BalancedMetricResolver 2019-10-29 12:24:10 -04:00
Bill Oley 2ccb89a06d re #191 Correct typos 2019-10-18 09:16:03 -04:00
Bill Oley dfc3840ad4 re #191 Delete cached metrics when they are no longer being updated 2019-10-17 17:49:24 -04:00
Bill Oley 6235034d8b BalancedMetricResolver in the non-leader balancer should read assignments at intervals 2019-10-17 17:49:24 -04:00
Bill Oley d8de6ac06f Make zookeeper timeout configurable 2019-10-17 17:49:24 -04:00
Christopher Tubbs f381946abe Merge pull request #190 from NationalSecurityAgency/dependabot/maven/io.netty-netty-all-4.1.42.Final
Bump netty-all from 4.1.35.Final to 4.1.42.Final
2019-10-17 09:35:13 -07:00
dependabot[bot] 48bbe6a561 Bump netty-all from 4.1.35.Final to 4.1.42.Final
Bumps [netty-all](https://github.com/netty/netty) from 4.1.35.Final to 4.1.42.Final.
- [Release notes](https://github.com/netty/netty/releases)
- [Commits](https://github.com/netty/netty/compare/netty-4.1.35.Final...netty-4.1.42.Final)

Signed-off-by: dependabot[bot] <[email protected]>
2019-10-11 18:58:57 +00:00
Christopher Tubbs 0f6110ea59 Update jackson to 2.9.10
* Fix CVE-2019-16335
* Fix CVE-2019-14540
2019-09-26 23:40:08 -04:00
Bill Oley 461b5e7e78 Pom version to 1.2.6-SNAPSHOT 2019-08-22 12:17:45 -04:00
Bill Oley 4ec7d94c28 Tag version 1.2.5 2019-08-22 12:12:49 -04:00
Bill Oley 77e910ae06 Use BufferedOutputWriter instead of PrintWriter, pass Exceptions instead of having to check error condition 2019-08-22 10:09:27 -04:00
Bill Oley a4998af887 Move jackson-databind version to the properties section 2019-08-08 13:50:08 -04:00
Bill Oley 2858549ede Merge pull request #188 from ctubbsii/jdk11-build-support
Bump versions / permit build with JDK11
2019-08-08 13:35:54 -04:00
Christopher Tubbs 57f0e1408f Bump versions / permit build with JDK11
Support builds on newer JDKs
* Update tycho-compiler-jdt in analytics/pom.xml
* Ignore spotbugs RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE for JDK11
* Bump several plugins to latest (maven-compiler-plugin, etc.)
* Add profile for using -release flag in Eclipse and with JDK9 and later

Fix recently added warnings
* Add try-with-resources for TreeCache objects to remove recent warnings
* Suppress generics warning in MetricCompactionStrategy
* Update TabletMetadataConsole to avoid deprecated Accumulo
  ClientConfiguration API

Other
* Bump jackson-databind for CVE-2019-14439, CVE-2019-14379
* Bump hadoop 2.7 to latest in series (2.7.3->2.7.7) for build
2019-08-06 16:14:13 -04:00
Bill Oley 151adc025a Merge pull request #185 from dtspence/TIMELY-184
Fixes #184 Metric compaction strategy and summary implementation
2019-08-06 15:15:45 -04:00
Bill Oley 5abd9d7200 closes #187 Prevent deadlock in balancer distributed read/write lock 2019-08-06 12:00:57 -04:00
Bill Oley fa2e211c72 closes #186 Make balancer websocket incomingBufferSize its own setting 2019-08-06 11:47:36 -04:00
dtspence a21438976e TIMELY-184: Metric compaction strategy and summary implementation. 2019-08-01 14:05:35 +00:00
Christopher Tubbs ba37047f3a Update jackson-databind (CVE-2019-12814) 2019-07-23 20:38:46 -04:00
Bill Oley 9305470c69 re #183 Check tagKey not null or blank in Meta.toKeys 2019-06-19 09:08:20 -04:00
Bill Oley 200233542e re #183 Add timestamps to meta tags to allow proper age off 2019-06-17 15:36:51 -04:00
Bill Oley 3cd78dc624 Merge branch 'drewfarris-config-fixup' 2019-05-30 12:55:35 -04:00
Bill Oley 9027cac6e3 Merge branch 'config-fixup' of https://github.com/drewfarris/timely into drewfarris-config-fixup
Conflicts:
	server/conf/timely-docker.yml
	server/conf/timely-standalone.yml
	server/conf/timely.yml
2019-05-30 12:50:31 -04:00
Bill Oley 3663b4e6d5 Pom version to 1.2.5-SNAPSHOT 2019-05-30 12:38:42 -04:00
Bill Oley 64973d0674 Merge pull request #182 from ctubbsii/fix-jackson
Fix most compiler warnings and many other issues
2019-05-30 12:25:00 -04:00
Christopher Tubbs 5512306f1e Fix most compiler warnings and other issues
Build:
* Fix netty-tcnative-boringssl-static version specs in pom.xml
* Bump the version of accumulo to latest release (1.9.3)
* Update jackson to 2.9.9 (CVE-2019-12086)

Warnings:
* Remove all use of deprecated netty stuffs and use their replacements
  in the netty APIs
* Add missing generics parameters everywhere
* Remove unused code (unused private fields, such as loggers)
* Remove standard javadoc tags that were missing descriptions (these do
  not add value to the javadocs, since default `@param`, `@throws`, and
  `@return` tags are provided by javadoc)
* Add missing serialVersionUid fields to Serializable classes
* Fix a resource leak warning about not closing ScannerOptionsHelper
  (NOTE: 3 other resource leak warnings exist, but did not attempt to
  fix those, since they were non-trivial)
* Remove deprecated annotations from hibernate and replace with their
  non-deprecated counterparts from javax.validation.constraints
* Remove use of deprecated Accumulo ClientConfiguration constructor

Other:
* Use lowercase header names, for HTTP/2 compatibility (and to stop
  incorrectly assuming case-sensitivity for HTTP/1)
* Narrow the scope of necessary `@SuppressWarnings("unchecked")`
  occurrences to a variable instead of suppressing for an entire method or
  an entire class
* IDE added some missing overrides annotations
* (style consistency) Replace tabs with spaces
* Set log4j2 root logger level for tests to WARN instead of TRACE
2019-05-24 12:12:00 -04:00
Bill Oley d35ae08188 Tag version 1.2.4 2019-05-22 17:14:42 -04:00
Bill Oley e448657afb Correction in websocket pipeline from Netty API change 2019-05-22 16:49:11 -04:00
Bill Oley 1dcd893711 Add age off iterator for meta table
Add age off iterator for meta table
2019-05-22 16:49:08 -04:00
Bill Oley 409f5375d1 Change pom version to 1.2.4-SNAPSHOT 2019-05-22 16:48:09 -04:00
Bill Oley c5df8139ca Correct typos 2019-05-22 16:46:55 -04:00
Drew Farris e2bbe4e212 Minor fix-ups to default configuration files.
. Replace ssl with server-ssl in timely-standalone.yml and timely-docker.yml
  . Add a value for cache.enabled in timely.yml
2019-01-19 22:07:07 -05:00
167 changed files with 5446 additions and 1205 deletions

No files matched your search

+3 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-analytics</artifactId> <artifactId>timely-analytics</artifactId>
<!-- <!--
@@ -68,6 +68,7 @@
<pluginManagement> <pluginManagement>
<plugins> <plugins>
<plugin> <plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<configuration> <configuration>
<compilerId>jdt</compilerId> <compilerId>jdt</compilerId>
@@ -76,7 +77,7 @@
<dependency> <dependency>
<groupId>org.eclipse.tycho</groupId> <groupId>org.eclipse.tycho</groupId>
<artifactId>tycho-compiler-jdt</artifactId> <artifactId>tycho-compiler-jdt</artifactId>
<version>0.21.0</version> <version>1.4.0</version>
</dependency> </dependency>
</dependencies> </dependencies>
</plugin> </plugin>
@@ -67,7 +67,7 @@ public class SummarizationJob {
}) })
.name("Metric Windows") .name("Metric Windows")
.addSink( .addSink(
new SocketClientSink<MetricHistogram>(jp.getTimelyHostname(), jp.getTimelyTcpPort(), new SocketClientSink<MetricHistogram>(jp.getTimelyHostname(), jp.getTimelyTcpPort(),
new MetricHistogram(), 2, true) { new MetricHistogram(), 2, true) {
private final Logger LOG = LoggerFactory private final Logger LOG = LoggerFactory
+1 -1
View File
@@ -32,7 +32,7 @@ $JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transpo
popd popd
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}" export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}" -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector" JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}" JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
+1
View File
@@ -111,6 +111,7 @@ timely-balancer:
flush-interval-seconds: 30 flush-interval-seconds: 30
scanner-read-ahead: 1 scanner-read-ahead: 1
subscription-batch-size: 5000 subscription-batch-size: 5000
incomingBufferSize: 50000000
wsClientPool: wsClientPool:
minIdlePerKey: 0 minIdlePerKey: 0
maxIdlePerKey: 40 maxIdlePerKey: 40
+5 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-balancer</artifactId> <artifactId>timely-balancer</artifactId>
<dependencies> <dependencies>
@@ -108,6 +108,10 @@
<groupId>org.apache.hadoop</groupId> <groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId> <artifactId>hadoop-minicluster</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
@@ -162,7 +166,6 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId> <artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -1,7 +1,7 @@
package timely.balancer; package timely.balancer;
import java.util.Timer; import java.util.concurrent.ScheduledExecutorService;
import java.util.TimerTask; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import com.google.common.util.concurrent.AtomicDouble; import com.google.common.util.concurrent.AtomicDouble;
@@ -20,38 +20,26 @@ public class ArrivalRate {
private AtomicDouble longTermRate = new AtomicDouble(0); private AtomicDouble longTermRate = new AtomicDouble(0);
private AtomicLong longTermLastReset = new AtomicLong(0); private AtomicLong longTermLastReset = new AtomicLong(0);
public ArrivalRate(Timer timer) { public ArrivalRate(ScheduledExecutorService arrivalRateExecutor) {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
shortTermLastReset.set(now); shortTermLastReset.set(now);
mediumTermLastReset.set(now); mediumTermLastReset.set(now);
longTermLastReset.set(now); longTermLastReset.set(now);
// 2 minute // 2 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetShort();
@Override }, 2, 2, TimeUnit.MINUTES);
public void run() {
resetShort();
}
}, 120000, 120000);
// 5 minute // 5 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetMedium();
@Override }, 5, 5, TimeUnit.MINUTES);
public void run() {
resetMedium();
}
}, 300000, 300000);
// 15 minute // 15 minute
timer.schedule(new TimerTask() { arrivalRateExecutor.scheduleAtFixedRate(() -> {
resetLong();
@Override }, 15, 15, TimeUnit.MINUTES);
public void run() {
resetLong();
}
}, 900000, 900000);
} }
private void resetShort() { private void resetShort() {
@@ -25,6 +25,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
@@ -56,6 +57,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime; import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy; import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -75,7 +77,7 @@ import timely.balancer.connection.http.HttpClientPool;
import timely.balancer.connection.tcp.TcpClientPool; import timely.balancer.connection.tcp.TcpClientPool;
import timely.balancer.connection.udp.UdpClientPool; import timely.balancer.connection.udp.UdpClientPool;
import timely.balancer.connection.ws.WsClientPool; import timely.balancer.connection.ws.WsClientPool;
import timely.balancer.healthcheck.HealthChecker; import timely.balancer.healthcheck.TcpHealthChecker;
import timely.balancer.netty.http.HttpRelayHandler; import timely.balancer.netty.http.HttpRelayHandler;
import timely.balancer.netty.tcp.TcpRelayHandler; import timely.balancer.netty.tcp.TcpRelayHandler;
import timely.balancer.netty.udp.UdpRelayHandler; import timely.balancer.netty.udp.UdpRelayHandler;
@@ -143,8 +145,11 @@ public class Balancer {
this.balancerConfig = balancerConf; this.balancerConfig = balancerConf;
} }
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) { protected static ConfigurableApplicationContext initializeContext(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args); SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
} }
private void shutdownHook() { private void shutdownHook() {
@@ -176,8 +181,9 @@ public class Balancer {
channelFutures.add(wsChannelHandle.close()); channelFutures.add(wsChannelHandle.close());
} }
int udpChannel = 1;
for (Channel c : udpChannelHandleList) { for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle"); LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close()); channelFutures.add(c.close());
} }
@@ -186,14 +192,13 @@ public class Balancer {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config()); LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
Integer quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod(); int quietPeriod = balancerConfig.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>(); List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) { if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup"); LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -238,9 +243,7 @@ public class Balancer {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString()); LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
@@ -251,14 +254,29 @@ public class Balancer {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e); LOG.error("Error closing WebSocketRequestDecoder during shutdown", e);
} }
if (this.metricResolver != null) {
try {
LOG.info("Closing metricResolver");
this.metricResolver.stop();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (curatorFramework != null) {
try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
if (applicationContext != null) { if (applicationContext != null) {
LOG.info("Closing applicationContext"); LOG.info("Closing applicationContext");
applicationContext.close(); applicationContext.close();
} }
if (curatorFramework != null) {
curatorFramework.close();
}
this.shutdown = true; this.shutdown = true;
LOG.info("Server shut down."); LOG.info("Server shut down.");
} }
@@ -359,7 +377,9 @@ public class Balancer {
// start curator framework // start curator framework
RetryPolicy retryPolicy = new RetryForever(1000); RetryPolicy retryPolicy = new RetryForever(1000);
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), 30000, 1000, int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(balancerConfig.getZooKeeper().getTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), timeout, 10000,
retryPolicy); retryPolicy);
curatorFramework.start(); curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths); ensureZkPaths(curatorFramework, zkPaths);
@@ -397,8 +417,9 @@ public class Balancer {
for (int x = 0; x < balancerConfig.getServer().getNumTcpPools(); x++) { for (int x = 0; x < balancerConfig.getServer().getNumTcpPools(); x++) {
tcpClientPools.add(new TcpClientPool(this.balancerConfig)); tcpClientPools.add(new TcpClientPool(this.balancerConfig));
} }
HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPools.get(0)); TcpHealthChecker healthChecker = new TcpHealthChecker(this.balancerConfig, tcpClientPools.get(0));
this.metricResolver = new BalancedMetricResolver(curatorFramework, this.balancerConfig, healthChecker); this.metricResolver = new BalancedMetricResolver(curatorFramework, this.balancerConfig, healthChecker);
this.metricResolver.start();
final ServerBootstrap tcpServer = new ServerBootstrap(); final ServerBootstrap tcpServer = new ServerBootstrap();
tcpServer.group(tcpBossGroup, tcpWorkerGroup); tcpServer.group(tcpBossGroup, tcpWorkerGroup);
@@ -450,10 +471,9 @@ public class Balancer {
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
wsServer.option(ChannelOption.SO_BACKLOG, 128); wsServer.option(ChannelOption.SO_BACKLOG, 128);
wsServer.option(ChannelOption.SO_KEEPALIVE, true); wsServer.option(ChannelOption.SO_KEEPALIVE, true);
/* Not sure if next three lines are necessary */ /* Not sure if next two lines are necessary */
wsServer.option(ChannelOption.SO_SNDBUF, 1048576); wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860); wsServer.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(620145, 838860));
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel(); wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress(); final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
@@ -478,12 +498,12 @@ public class Balancer {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Balancer.applicationContext = Balancer.initializeConfiguration(args); Balancer.applicationContext = Balancer.initializeContext(args);
BalancerConfiguration balancerConf = applicationContext.getBean(BalancerConfiguration.class); BalancerConfiguration balancerConf = Balancer.applicationContext.getBean(BalancerConfiguration.class);
Balancer b = new Balancer(balancerConf); Balancer balancer = new Balancer(balancerConf);
try { try {
b.run(); balancer.run();
LATCH.await(); LATCH.await();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOG.info("Server shutting down."); LOG.info("Server shutting down.");
@@ -491,7 +511,7 @@ public class Balancer {
LOG.error("Error running server.", e); LOG.error("Error running server.", e);
} finally { } finally {
try { try {
b.shutdown(); balancer.shutdown();
} catch (Exception e) { } catch (Exception e) {
System.exit(1); System.exit(1);
} }
@@ -515,11 +535,13 @@ public class Balancer {
ch.pipeline().addLast("chunker", new ChunkedWriteHandler()); ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder( ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(
balancerConfig.getSecurity(), balancerConfig.getHttp())); balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()); ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("login", ch.pipeline().addLast("login",
new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp())); new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool)); ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler()); ch.pipeline().addLast("error", new TimelyExceptionHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
} }
}; };
} }
@@ -567,7 +589,7 @@ public class Balancer {
ch.pipeline().addLast("idle-handler", ch.pipeline().addLast("idle-handler",
new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0)); new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol", ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true)); new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(balancerConfig.getSecurity())); ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(balancerConfig.getSecurity()));
ch.pipeline().addLast("httpRelay", new WsRelayHandler(balancerConfig, metricResolver, wsClientPool)); ch.pipeline().addLast("httpRelay", new WsRelayHandler(balancerConfig, metricResolver, wsClientPool));
ch.pipeline().addLast("error", new WSTimelyExceptionHandler()); ch.pipeline().addLast("error", new WSTimelyExceptionHandler());
@@ -49,6 +49,16 @@ public class BalancerConfiguration {
private int serverSuccessesBeforeUp = 3; private int serverSuccessesBeforeUp = 3;
private String metricAssignmentPersisterType = "HDFS";
private long balanceDelay = 900000;
private long balancePeriod = 900000;
private long persistDelay = 10000;
private long persistPeriod = 60000;
public ZooKeeper getZooKeeper() { public ZooKeeper getZooKeeper() {
return zooKeeper; return zooKeeper;
} }
@@ -120,4 +130,44 @@ public class BalancerConfiguration {
public void setServerSuccessesBeforeUp(int serverSuccessesBeforeUp) { public void setServerSuccessesBeforeUp(int serverSuccessesBeforeUp) {
this.serverSuccessesBeforeUp = serverSuccessesBeforeUp; this.serverSuccessesBeforeUp = serverSuccessesBeforeUp;
} }
public String getMetricAssignmentPersisterType() {
return metricAssignmentPersisterType;
}
public void setMetricAssignmentPersisterType(String metricAssignmentPersisterType) {
this.metricAssignmentPersisterType = metricAssignmentPersisterType;
}
public void setBalanceDelay(long balanceDelay) {
this.balanceDelay = balanceDelay;
}
public long getBalanceDelay() {
return balanceDelay;
}
public void setBalancePeriod(long balancePeriod) {
this.balancePeriod = balancePeriod;
}
public long getBalancePeriod() {
return balancePeriod;
}
public void setPersistDelay(long persistDelay) {
this.persistDelay = persistDelay;
}
public long getPersistDelay() {
return persistDelay;
}
public void setPersistPeriod(long persistPeriod) {
this.persistPeriod = persistPeriod;
}
public long getPersistPeriod() {
return persistPeriod;
}
} }
@@ -3,6 +3,7 @@ package timely.balancer.configuration;
import javax.validation.Valid; import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.configuration.Http; import timely.configuration.Http;
@@ -10,9 +11,9 @@ public class BalancerHttp extends Http {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig httpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<CloseableHttpClient> httpClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfig getHttpClientPool() { public GenericKeyedObjectPoolConfig<CloseableHttpClient> getHttpClientPool() {
return httpClientPool; return httpClientPool;
} }
} }
@@ -4,27 +4,29 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.tcp.TcpClient;
import timely.client.udp.UdpClient;
import timely.configuration.Server; import timely.configuration.Server;
public class BalancerServer extends Server { public class BalancerServer extends Server {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig udpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<UdpClient> udpClientPool = new GenericKeyedObjectPoolConfig<>();
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig tcpClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<TcpClient> tcpClientPool = new GenericKeyedObjectPoolConfig<>();
private int numTcpPools = 1; private int numTcpPools = 1;
private int tcpBufferSize = -1; private int tcpBufferSize = -1;
public GenericKeyedObjectPoolConfig getUdpClientPool() { public GenericKeyedObjectPoolConfig<UdpClient> getUdpClientPool() {
return udpClientPool; return udpClientPool;
} }
public GenericKeyedObjectPoolConfig getTcpClientPool() { public GenericKeyedObjectPoolConfig<TcpClient> getTcpClientPool() {
return tcpClientPool; return tcpClientPool;
} }
@@ -4,15 +4,26 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig; import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import timely.configuration.Websocket; import timely.configuration.Websocket;
public class BalancerWebsocket extends Websocket { public class BalancerWebsocket extends Websocket {
@Valid @Valid
@NestedConfigurationProperty @NestedConfigurationProperty
private GenericKeyedObjectPoolConfig wsClientPool = new GenericKeyedObjectPoolConfig(); private GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> wsClientPool = new GenericKeyedObjectPoolConfig<>();
public GenericKeyedObjectPoolConfig getWsClientPool() { public GenericKeyedObjectPoolConfig<WebSocketSubscriptionClient> getWsClientPool() {
return wsClientPool; return wsClientPool;
} }
public int incomingBufferSize = 50000000;
public int getIncomingBufferSize() {
return incomingBufferSize;
}
public void setIncomingBufferSize(int incomingBufferSize) {
this.incomingBufferSize = incomingBufferSize;
}
} }
@@ -1,9 +1,14 @@
package timely.balancer.configuration; package timely.balancer.configuration;
import javax.validation.constraints.NotBlank;
public class ZooKeeper { public class ZooKeeper {
private String servers; private String servers;
@NotBlank
private String timeout = "120s";
public String getServers() { public String getServers() {
return servers; return servers;
} }
@@ -11,4 +16,12 @@ public class ZooKeeper {
public void setServers(String servers) { public void setServers(String servers) {
this.servers = servers; this.servers = servers;
} }
public String getTimeout() {
return timeout;
}
public void setTimeout(String timeout) {
this.timeout = timeout;
}
} }
@@ -1,7 +1,5 @@
package timely.balancer.connection; package timely.balancer.connection;
import java.util.Timer;
import org.apache.commons.lang3.builder.EqualsBuilder; import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder; import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringBuilder;
@@ -13,7 +11,6 @@ import timely.balancer.configuration.BalancerConfiguration;
public class TimelyBalancedHost { public class TimelyBalancedHost {
private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class); private static final Logger LOG = LoggerFactory.getLogger(TimelyBalancedHost.class);
static private Timer arrivalRateTimer = new Timer("ArrivalRateTimerTBH", true);
private String host; private String host;
private int tcpPort; private int tcpPort;
@@ -24,34 +21,29 @@ public class TimelyBalancedHost {
private BalancerConfiguration balancerConfig; private BalancerConfiguration balancerConfig;
private int failures = 0; private int failures = 0;
private int successes = 0; private int successes = 0;
private ArrivalRate arrivalRate = new ArrivalRate(arrivalRateTimer); private ArrivalRate arrivalRate = null;
public TimelyBalancedHost() { private TimelyBalancedHost() {
} }
public TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort) { private TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
this.host = host; this.host = host;
this.tcpPort = tcpPort; this.tcpPort = tcpPort;
this.httpPort = httpPort; this.httpPort = httpPort;
this.wsPort = wsPort; this.wsPort = wsPort;
this.udpPort = udpPort; this.udpPort = udpPort;
this.arrivalRate = arrivalRate;
} }
public TimelyBalancedHost(String host, int basePort) { static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
this.host = host; ArrivalRate arrivalRate) {
this.tcpPort = basePort; return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort, arrivalRate);
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) { static public TimelyBalancedHost of(String host, int basePort) {
return new TimelyBalancedHost(host, basePort); return new TimelyBalancedHost(host, basePort, basePort + 1, basePort + 2, basePort + 3, null);
} }
public void setBalancerConfig(BalancerConfiguration balancerConfig) { public void setBalancerConfig(BalancerConfiguration balancerConfig) {
@@ -141,11 +133,17 @@ public class TimelyBalancedHost {
} }
public void arrived() { public void arrived() {
arrivalRate.arrived(); if (this.arrivalRate != null) {
this.arrivalRate.arrived();
}
} }
public double getArrivalRate() { public double getArrivalRate() {
return arrivalRate.getRate(); if (this.arrivalRate == null) {
return 0;
} else {
return this.arrivalRate.getRate();
}
} }
@Override @Override
@@ -186,4 +184,11 @@ public class TimelyBalancedHost {
builder.append(isUp); builder.append(isUp);
return builder.toString(); return builder.toString();
} }
public static String toStringShort(TimelyBalancedHost tbh) {
if (tbh == null) {
return null;
}
return tbh.host + ":" + tbh.tcpPort;
}
} }
@@ -4,16 +4,12 @@ import javax.net.ssl.SSLContext;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool; import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.CloseableHttpClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.configuration.BalancerHttp; import timely.balancer.configuration.BalancerHttp;
import timely.balancer.configuration.BalancerSecurity; import timely.balancer.configuration.BalancerSecurity;
import timely.balancer.connection.TimelyBalancedHost; import timely.balancer.connection.TimelyBalancedHost;
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> { public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
private static final Logger LOG = LoggerFactory.getLogger(HttpClientPool.class);
public HttpClientPool(BalancerSecurity balancerSecurity, BalancerHttp http, SSLContext sslContext) { public HttpClientPool(BalancerSecurity balancerSecurity, BalancerHttp http, SSLContext sslContext) {
super(new HttpClientFactory(balancerSecurity, sslContext), http.getHttpClientPool()); super(new HttpClientFactory(balancerSecurity, sslContext), http.getHttpClientPool());
} }
@@ -22,7 +22,7 @@ public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedH
@Override @Override
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception { public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
int bufferSize = balancerConfig.getWebsocket().getSubscriptionBatchSize() * 500; int bufferSize = balancerConfig.getWebsocket().getIncomingBufferSize();
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(), WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(), k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
balancerConfig.isLoginRequired(), "", "", balancerConfig.isLoginRequired(), "", "",
@@ -1,53 +1,12 @@
package timely.balancer.healthcheck; package timely.balancer.healthcheck;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost; import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.tcp.TcpClientPool;
public class HealthChecker { public interface HealthChecker {
private static final Logger LOG = LoggerFactory.getLogger(HealthChecker.class); public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private Timer timer;
final private TCPServerHealthCheck tcpServerHealthCheck;
private TimerTask check = new TimerTask() {
@Override public void close() throws Exception;
public void run() {
synchronized (timelyHosts) {
for (TimelyBalancedHost h : timelyHosts) {
try {
if (tcpServerHealthCheck.isServerHealthy(h)) {
h.reportSuccess();
} else {
h.reportFailure();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
h.reportFailure();
}
}
}
}
};
public HealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.timer = new Timer("HealthCheckerThread", true);
this.timer.scheduleAtFixedRate(check, 0, config.getCheckServerHealthInterval());
}
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) {
synchronized (timelyHosts) {
this.timelyHosts.clear();
this.timelyHosts.addAll(timelyHosts);
}
}
} }
@@ -0,0 +1,62 @@
package timely.balancer.healthcheck;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.tcp.TcpClientPool;
public class TcpHealthChecker implements HealthChecker {
private static final Logger LOG = LoggerFactory.getLogger(TcpHealthChecker.class);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final private TCPServerHealthCheck tcpServerHealthCheck;
public TcpHealthChecker(BalancerConfiguration config, TcpClientPool tcpClientPool) {
this.tcpServerHealthCheck = new TCPServerHealthCheck(tcpClientPool);
this.executorService.scheduleAtFixedRate(() -> {
synchronized (timelyHosts) {
for (TimelyBalancedHost h : timelyHosts) {
try {
if (tcpServerHealthCheck.isServerHealthy(h)) {
h.reportSuccess();
} else {
h.reportFailure();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
h.reportFailure();
}
}
}
}, 0, config.getCheckServerHealthInterval(), TimeUnit.MILLISECONDS);
}
@Override
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) {
synchronized (timelyHosts) {
this.timelyHosts.clear();
this.timelyHosts.addAll(timelyHosts);
}
}
public void close() throws Exception {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
}
@@ -1,6 +1,6 @@
package timely.balancer.netty.http; package timely.balancer.netty.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
@@ -67,7 +67,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
try { try {
try { try {
request = msg.getHttpRequest(); request = msg.getHttpRequest();
String originalURI = request.getUri(); String originalURI = request.uri();
originalURI = encodeURI(originalURI); originalURI = encodeURI(originalURI);
String metric = null; String metric = null;
@@ -101,7 +101,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
List<Header> relayedHeaderList = new ArrayList<>(); List<Header> relayedHeaderList = new ArrayList<>();
for (Map.Entry<String, String> h : headers.entries()) { for (Map.Entry<String, String> h : headers.entries()) {
if (!h.getKey().equals(CONTENT_LENGTH)) { if (!h.getKey().equals(CONTENT_LENGTH.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue())); relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
} }
} }
@@ -109,14 +109,14 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
relayedHeaderList.toArray(headerArray); relayedHeaderList.toArray(headerArray);
String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI; String relayURI = "https://" + k.getHost() + ":" + k.getHttpPort() + originalURI;
if (request.getMethod().equals(HttpMethod.GET)) { if (request.method().equals(HttpMethod.GET)) {
HttpUriRequest relayedRequest = new HttpGet(relayURI); HttpUriRequest relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.POST)) { } else if (request.method().equals(HttpMethod.POST)) {
HttpPost relayedRequest = new HttpPost(relayURI); HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8); String content = request.content().toString(StandardCharsets.UTF_8);
@@ -170,7 +170,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
if (client != null && k != null) { if (client != null && k != null) {
httpClientPool.returnObject(k, client); httpClientPool.returnObject(k, client);
} else { } else {
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri()); LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().uri());
} }
} }
} }
@@ -45,7 +45,7 @@ public class WsClientHandler extends ClientHandler {
Multimap<String, String> proxyRequestHeaders = HashMultimap.create(); Multimap<String, String> proxyRequestHeaders = HashMultimap.create();
ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert()); ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert());
for (String s : proxyRequestHeaders.keySet()) { for (String s : proxyRequestHeaders.keySet()) {
headers.put(s, new ArrayList(proxyRequestHeaders.get(s))); headers.put(s, new ArrayList<>(proxyRequestHeaders.get(s)));
} }
} }
@@ -53,7 +53,7 @@ public class WsClientHandler extends ClientHandler {
for (String s : originalRequestHeaders.keySet()) { for (String s : originalRequestHeaders.keySet()) {
if (!headers.containsKey(s)) { if (!headers.containsKey(s)) {
// add pre-existing values if key does not exist in proxyRequest // add pre-existing values if key does not exist in proxyRequest
headers.put(s, new ArrayList(originalRequestHeaders.get(s))); headers.put(s, new ArrayList<>(originalRequestHeaders.get(s)));
} }
} }
} }
@@ -4,10 +4,14 @@ import static timely.Server.SERVICE_DISCOVERY_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH; import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LOCK_PATH; import static timely.balancer.Balancer.ASSIGNMENTS_LOCK_PATH;
import static timely.balancer.Balancer.LEADER_LATCH_PATH; import static timely.balancer.Balancer.LEADER_LATCH_PATH;
import static timely.balancer.resolver.eventing.MetricAssignedEvent.Reason.ASSIGN_FALLBACK_SEQUENTIAL;
import static timely.balancer.resolver.eventing.MetricAssignedEvent.Reason.ASSIGN_ROUND_ROBIN;
import static timely.balancer.resolver.eventing.MetricAssignedEvent.Reason.HOST_DOWN_ROUND_ROBIN;
import static timely.balancer.resolver.eventing.MetricAssignedEvent.Reason.HOST_REMOVED;
import static timely.balancer.resolver.eventing.MetricAssignedEvent.Reason.REBALANCE;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS; import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH; import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.nio.charset.Charset;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
@@ -19,21 +23,18 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Random; import java.util.Random;
import java.util.Set; import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.SerializationUtils; import org.apache.commons.lang3.SerializationUtils;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicValue; import org.apache.curator.framework.recipes.atomic.DistributedAtomicValue;
import org.apache.curator.framework.recipes.cache.TreeCache; import org.apache.curator.framework.recipes.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent; import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
@@ -48,11 +49,6 @@ import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder; import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance; import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.details.ServiceCacheListener; import org.apache.curator.x.discovery.details.ServiceCacheListener;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FSDataInputStream;
import org.apache.hadoop.fs.FSDataOutputStream;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.apache.zookeeper.CreateMode; import org.apache.zookeeper.CreateMode;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -61,69 +57,74 @@ import timely.balancer.ArrivalRate;
import timely.balancer.configuration.BalancerConfiguration; import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost; import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.healthcheck.HealthChecker; import timely.balancer.healthcheck.HealthChecker;
import timely.balancer.resolver.eventing.MetricAssignedCallback;
import timely.balancer.resolver.eventing.MetricAssignedEvent;
import timely.balancer.resolver.eventing.MetricBalanceCallback;
import timely.balancer.resolver.eventing.MetricBalanceEvent;
import timely.balancer.resolver.eventing.MetricHostCallback;
import timely.balancer.resolver.eventing.MetricHostEvent;
public class BalancedMetricResolver implements MetricResolver { public class BalancedMetricResolver implements MetricResolver {
private static final Logger LOG = LoggerFactory.getLogger(BalancedMetricResolver.class); private static final Logger LOG = LoggerFactory.getLogger(BalancedMetricResolver.class);
private Map<String, TimelyBalancedHost> metricToHostMap = new TreeMap<>(); private Map<String, TimelyBalancedHost> metricToHostMap = new TreeMap<>();
private Set<String> unassignedMetrics = new HashSet<>();
private Map<String, ArrivalRate> metricMap = new HashMap<>(); private Map<String, ArrivalRate> metricMap = new HashMap<>();
private ReentrantReadWriteLock balancerLock = new ReentrantReadWriteLock(); private ReentrantReadWriteLock balancerLock = new ReentrantReadWriteLock();
private List<TimelyBalancedHost> serverList = new ArrayList<>(); private List<TimelyBalancedHost> serverList = new ArrayList<>();
private Random r = new Random(); private Random r = new Random();
final private HealthChecker healthChecker; private final HealthChecker healthChecker;
private Timer timer = new Timer("RebalanceTimer", true); private ScheduledExecutorService assignmentExecutor = Executors.newScheduledThreadPool(2);
private Timer arrivalRateTimer = new Timer("ArrivalRateTimerResolver", true); private ScheduledExecutorService arrivalRateExecutor = Executors.newScheduledThreadPool(2);
private int roundRobinCounter = 0; private int roundRobinCounter = 0;
private Set<String> nonCachedMetrics = new HashSet<>(); private Set<String> nonCachedMetrics = new HashSet<>();
private DistributedAtomicValue nonCachedMetricsIP; private DistributedAtomicValue nonCachedMetricsIP;
private ReentrantReadWriteLock nonCachedMetricsLocalLock = new ReentrantReadWriteLock(); private ReentrantReadWriteLock nonCachedMetricsLocalLock = new ReentrantReadWriteLock();
private InterProcessReadWriteLock nonCachedMetricsIPRWLock; private InterProcessReadWriteLock nonCachedMetricsIPRWLock;
private BalancerConfiguration balancerConfig; private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0);
private LeaderLatch leaderLatch; private LeaderLatch leaderLatch;
private AtomicBoolean isLeader = new AtomicBoolean(false); private AtomicBoolean isLeader = new AtomicBoolean(false);
private InterProcessReadWriteLock assignmentsIPRWLock; private InterProcessReadWriteLock assignmentsIPRWLock;
private DistributedAtomicLong assignmentsLastUpdatedInHdfs; private MetricAssignmentPerister assignmentPerister;
private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0);
private enum BalanceType { final private BalancerConfiguration balancerConfig;
HIGH_LOW, HIGH_AVG, AVG_LOW final private CuratorFramework curatorFramework;
private List<MetricAssignedCallback> metricAssignedCallbacks = new ArrayList<>();
private List<MetricBalanceCallback> metricBalanceCallbacks = new ArrayList<>();
private List<MetricHostCallback> metricHostCallbacks = new ArrayList<>();
public enum BalanceType {
HIGH_LOW, HIGH_AVG, AVG_LOW;
} }
public BalancedMetricResolver(CuratorFramework curatorFramework, BalancerConfiguration balancerConfig, public BalancedMetricResolver(CuratorFramework curatorFramework, BalancerConfiguration balancerConfig,
HealthChecker healthChecker) { HealthChecker healthChecker) {
this.curatorFramework = curatorFramework;
this.balancerConfig = balancerConfig; this.balancerConfig = balancerConfig;
this.healthChecker = healthChecker; this.healthChecker = healthChecker;
}
public void start() {
assignmentsIPRWLock = new InterProcessReadWriteLock(curatorFramework, ASSIGNMENTS_LOCK_PATH); assignmentsIPRWLock = new InterProcessReadWriteLock(curatorFramework, ASSIGNMENTS_LOCK_PATH);
testIPRWLock(curatorFramework, assignmentsIPRWLock, ASSIGNMENTS_LOCK_PATH); testIPRWLock(curatorFramework, assignmentsIPRWLock, ASSIGNMENTS_LOCK_PATH);
this.assignmentPerister = new MetricAssignmentPerister(this, balancerConfig, metricToHostMap, nonCachedMetrics,
curatorFramework, assignmentsIPRWLock, nonCachedMetricsLocalLock, assignmentsLastUpdatedLocal,
balancerLock);
startLeaderLatch(curatorFramework); startLeaderLatch(curatorFramework);
startServiceListener(curatorFramework); startServiceListener(curatorFramework);
assignmentsLastUpdatedInHdfs = new DistributedAtomicLong(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH,
new RetryForever(1000));
TreeCacheListener assignmentListener = new TreeCacheListener() { TreeCacheListener assignmentListener = (curatorFramework1, event) -> {
LOG.debug("Handling assignments event {}. assignmentsLastUpdated:{}", event.getType().toString(),
@Override new Date(assignmentPerister.getAssignmentsLastUpdatedByLeader()));
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception { if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
LOG.debug("Handling assignments event {}. assignmentsLastUpdatedInHdfs:{}", event.getType().toString(), assignmentPerister.readAssignments(true);
new Date(assignmentsLastUpdatedInHdfs.get().postValue()));
if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastHdfsUpdate > lastLocalUpdate) {
LOG.debug("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
readAssignmentsFromHdfs();
} else {
LOG.debug("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
}
}
} }
}; };
try { try (TreeCache assignmentTreeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH)) {
TreeCache assignmentTreeCache = new TreeCache(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH);
assignmentTreeCache.getListenable().addListener(assignmentListener); assignmentTreeCache.getListenable().addListener(assignmentListener);
assignmentTreeCache.start(); assignmentTreeCache.start();
} catch (Exception e) { } catch (Exception e) {
@@ -144,8 +145,7 @@ public class BalancedMetricResolver implements MetricResolver {
} }
}; };
try { try (TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS)) {
TreeCache nonCachedMetricsTreeCache = new TreeCache(curatorFramework, NON_CACHED_METRICS);
nonCachedMetricsTreeCache.getListenable().addListener(nonCachedMetricsListener); nonCachedMetricsTreeCache.getListenable().addListener(nonCachedMetricsListener);
nonCachedMetricsTreeCache.start(); nonCachedMetricsTreeCache.start();
} catch (Exception e) { } catch (Exception e) {
@@ -160,55 +160,55 @@ public class BalancedMetricResolver implements MetricResolver {
} }
readNonCachedMetricsIP(); readNonCachedMetricsIP();
readAssignmentsFromHdfs(); assignmentPerister.readAssignments(false);
timer.schedule(new TimerTask() { assignmentExecutor.scheduleAtFixedRate(() -> {
if (isLeader.get()) {
@Override
public void run() {
if (isLeader.get()) {
try {
balance();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
}, 900000, 900000);
timer.schedule(new TimerTask() {
@Override
public void run() {
try { try {
if (isLeader.get()) { balance();
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.debug("Writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})",
new Date(lastLocalUpdate), new Date(lastHdfsUpdate));
writeAssigmentsToHdfs();
}
}
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage(), e); LOG.error(e.getMessage(), e);
} }
} }
}, 10000, 60000); }, balancerConfig.getBalanceDelay(), balancerConfig.getBalancePeriod(), TimeUnit.MILLISECONDS);
assignmentExecutor.scheduleAtFixedRate(() -> {
try {
if (isLeader.get()) {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastUpdateByLeader = assignmentPerister.getAssignmentsLastUpdatedByLeader();
if (lastLocalUpdate > lastUpdateByLeader) {
LOG.debug("Leader writing assignments lastLocalUpdate ({}) > lastUpdateByLeader ({})",
new Date(lastLocalUpdate), new Date(lastUpdateByLeader));
assignmentPerister.writeAssignments();
} else {
LOG.trace("Leader not writing assignments lastLocalUpdate ({}) <= lastUpdateByLeader ({})",
new Date(lastLocalUpdate), new Date(lastUpdateByLeader));
}
} else {
assignmentPerister.readAssignments(true);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, balancerConfig.getPersistDelay(), balancerConfig.getPersistPeriod(), TimeUnit.MILLISECONDS);
} }
private void readNonCachedMetricsIP() { private void readNonCachedMetricsIP() {
Set<String> currentNonCachedMetricsDistributed = new TreeSet<>(); Set<String> currentNonCachedMetricsDistributed = new TreeSet<>();
try { try {
nonCachedMetricsIPRWLock.readLock().acquire(); boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue(); byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
if (currentNonCachedMetricsDistributedBytes != null) { if (currentNonCachedMetricsDistributedBytes != null) {
try { try {
currentNonCachedMetricsDistributed = SerializationUtils currentNonCachedMetricsDistributed = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes); .deserialize(currentNonCachedMetricsDistributedBytes);
} catch (Exception e) { } catch (Exception e) {
LOG.error(e.getMessage()); LOG.error(e.getMessage(), e);
currentNonCachedMetricsDistributed = new TreeSet<>(); currentNonCachedMetricsDistributed = new TreeSet<>();
} }
} }
@@ -249,7 +249,7 @@ public class BalancedMetricResolver implements MetricResolver {
public void isLeader() { public void isLeader() {
LOG.info("this balancer is the leader"); LOG.info("this balancer is the leader");
isLeader.set(true); isLeader.set(true);
writeAssigmentsToHdfs(); assignmentPerister.writeAssignments();
} }
@Override @Override
@@ -265,28 +265,29 @@ public class BalancedMetricResolver implements MetricResolver {
private void startServiceListener(CuratorFramework curatorFramework) { private void startServiceListener(CuratorFramework curatorFramework) {
try { try {
ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class).client(curatorFramework) ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.basePath(SERVICE_DISCOVERY_PATH).build(); .client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start(); discovery.start();
Collection<ServiceInstance> instances = discovery.queryForInstances("timely-server"); Collection<ServiceInstance<ServerDetails>> instances = discovery.queryForInstances("timely-server");
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
try { try {
for (ServiceInstance si : instances) { for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload(); ServerDetails pl = si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(),
pl.getWsPort(), pl.getUdpPort()); pl.getWsPort(), pl.getUdpPort(), new ArrivalRate(arrivalRateExecutor));
LOG.info("adding service {} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", si.getId(), LOG.info("adding service {} host:{}", si.getId(), TimelyBalancedHost.toStringShort(tbh));
pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort());
tbh.setBalancerConfig(balancerConfig); tbh.setBalancerConfig(balancerConfig);
serverList.add(tbh); serverList.add(tbh);
metricHostEvent(tbh, MetricHostEvent.ActionType.ADDED);
} }
healthChecker.setTimelyHosts(serverList); healthChecker.setTimelyHosts(serverList);
} finally { } finally {
balancerLock.writeLock().unlock(); balancerLock.writeLock().unlock();
} }
final ServiceCache serviceCache = discovery.serviceCacheBuilder().name("timely-server").build(); final ServiceCache<ServerDetails> serviceCache = discovery.serviceCacheBuilder().name("timely-server")
.build();
ServiceCacheListener listener = new ServiceCacheListener() { ServiceCacheListener listener = new ServiceCacheListener() {
@Override @Override
@@ -294,12 +295,13 @@ public class BalancedMetricResolver implements MetricResolver {
boolean rebalanceNeeded = false; boolean rebalanceNeeded = false;
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
try { try {
List<ServiceInstance> instances = serviceCache.getInstances(); List<ServiceInstance<ServerDetails>> instances = serviceCache.getInstances();
Set<TimelyBalancedHost> availableHosts = new HashSet<>(); Set<TimelyBalancedHost> availableHosts = new HashSet<>();
for (ServiceInstance si : instances) { for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload(); ServerDetails pl = (ServerDetails) si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(),
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort()); pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
tbh.setBalancerConfig(balancerConfig); tbh.setBalancerConfig(balancerConfig);
availableHosts.add(tbh); availableHosts.add(tbh);
} }
@@ -313,9 +315,8 @@ public class BalancedMetricResolver implements MetricResolver {
availableHosts.remove(h); availableHosts.remove(h);
} else { } else {
itr.remove(); itr.remove();
LOG.info("removing service {}:{} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", metricHostEvent(h, MetricHostEvent.ActionType.REMOVED);
h.getHost(), h.getTcpPort(), h.getHost(), h.getTcpPort(), h.getHttpPort(), LOG.info("removing service {}", TimelyBalancedHost.toStringShort(h));
h.getWsPort(), h.getUdpPort());
for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) { for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) {
if (e.getValue().equals(h)) { if (e.getValue().equals(h)) {
reassignMetrics.add(e.getKey()); reassignMetrics.add(e.getKey());
@@ -327,28 +328,33 @@ public class BalancedMetricResolver implements MetricResolver {
// add new hosts that were not previously known // add new hosts that were not previously known
for (TimelyBalancedHost h : availableHosts) { for (TimelyBalancedHost h : availableHosts) {
LOG.info("adding service {}:{} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", LOG.info("adding service {}", TimelyBalancedHost.toStringShort(h));
h.getHost(), h.getTcpPort(), h.getHost(), h.getTcpPort(), h.getHttpPort(),
h.getWsPort(), h.getUdpPort());
serverList.add(h); serverList.add(h);
metricHostEvent(h, MetricHostEvent.ActionType.ADDED);
rebalanceNeeded = true; rebalanceNeeded = true;
} }
healthChecker.setTimelyHosts(serverList); healthChecker.setTimelyHosts(serverList);
if (!availableHosts.isEmpty()) {
reassignMetrics.addAll(unassignedMetrics);
unassignedMetrics.clear();
}
if (isLeader.get()) { if (isLeader.get()) {
for (String s : reassignMetrics) { for (String s : reassignMetrics) {
TimelyBalancedHost h = getRoundRobinHost(null); TimelyBalancedHost h = getRoundRobinHost(null);
assignMetric(s, h); assignMetric(s, h, HOST_REMOVED);
LOG.debug("Assigned server removed. Assigning {} to server {}:{}", s, h.getTcpPort()); LOG.debug("Assigned server removed. Assigning {} to server {}", s,
TimelyBalancedHost.toStringShort(h));
} }
writeAssigmentsToHdfs();
} }
} finally { } finally {
balancerLock.writeLock().unlock(); balancerLock.writeLock().unlock();
} }
if (isLeader.get() && rebalanceNeeded) { if (isLeader.get()) {
balance(); if (rebalanceNeeded) {
balance();
}
assignmentPerister.writeAssignments();
} }
} }
@@ -408,7 +414,7 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh; return tbh;
} }
private TimelyBalancedHost getRoundRobinHost(TimelyBalancedHost notThisOne) { protected TimelyBalancedHost getRoundRobinHost(TimelyBalancedHost notThisOne) {
TimelyBalancedHost tbh = null; TimelyBalancedHost tbh = null;
balancerLock.readLock().lock(); balancerLock.readLock().lock();
try { try {
@@ -530,8 +536,10 @@ public class BalancedMetricResolver implements MetricResolver {
int numReassigned = 0; int numReassigned = 0;
if (isLeader.get()) { if (isLeader.get()) {
metricBalanceEvent(MetricBalanceEvent.ProgressType.BEGIN, balanceType, 0);
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
try { try {
metricBalanceEvent(MetricBalanceEvent.ProgressType.BALANCER_LOCK_ACQUIRED, balanceType, 0);
Map<String, ArrivalRate> tempMetricMap = new HashMap<>(); Map<String, ArrivalRate> tempMetricMap = new HashMap<>();
tempMetricMap.putAll(metricMap); tempMetricMap.putAll(metricMap);
@@ -561,8 +569,8 @@ public class BalancedMetricResolver implements MetricResolver {
} }
} }
LOG.trace("focusHost {}:{} desiredChange:{} rateSortedMetrics.size():{}", h.getHost(), LOG.trace("focusHost {} desiredChange:{} rateSortedMetrics.size():{}",
h.getTcpPort(), desiredChange, rateSortedMetrics.size()); TimelyBalancedHost.toStringShort(h), desiredChange, rateSortedMetrics.size());
boolean doneWithHost = false; boolean doneWithHost = false;
while (!doneWithHost) { while (!doneWithHost) {
@@ -591,7 +599,7 @@ public class BalancedMetricResolver implements MetricResolver {
} else { } else {
String metric = e.getValue(); String metric = e.getValue();
Double metricRate = e.getKey(); Double metricRate = e.getKey();
assignMetric(metric, candidateHost); assignMetric(metric, candidateHost, REBALANCE);
numReassigned++; numReassigned++;
calculatedRates.put(candidateHost, calculatedRates.get(candidateHost) + metricRate); calculatedRates.put(candidateHost, calculatedRates.get(candidateHost) + metricRate);
calculatedRates.put(h, calculatedRates.get(h) - metricRate); calculatedRates.put(h, calculatedRates.get(h) - metricRate);
@@ -600,9 +608,9 @@ public class BalancedMetricResolver implements MetricResolver {
itr.remove(); itr.remove();
tempMetricMap.remove(metric); tempMetricMap.remove(metric);
LOG.info( LOG.info(
"rebalancing: reassigning metric:{} rate:{} from server {}:{} to {}:{} remaining delta {}", "rebalancing: reassigning metric:{} rate:{} from server {} to {} remaining delta {}",
metric, metricRate, h.getHost(), h.getTcpPort(), candidateHost.getHost(), metric, metricRate, TimelyBalancedHost.toStringShort(h),
candidateHost.getTcpPort(), desiredChange); TimelyBalancedHost.toStringShort(candidateHost), desiredChange);
} }
} }
} }
@@ -625,6 +633,7 @@ public class BalancedMetricResolver implements MetricResolver {
} }
} finally { } finally {
balancerLock.writeLock().unlock(); balancerLock.writeLock().unlock();
metricBalanceEvent(MetricBalanceEvent.ProgressType.END, balanceType, numReassigned);
} }
} }
return numReassigned; return numReassigned;
@@ -642,7 +651,10 @@ public class BalancedMetricResolver implements MetricResolver {
} }
try { try {
nonCachedMetricsIPRWLock.writeLock().acquire(); boolean acquired = false;
while (!acquired) {
acquired = nonCachedMetricsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue(); byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
Set<String> currentNonCachedMetricsIP; Set<String> currentNonCachedMetricsIP;
if (currentNonCachedMetricsDistributedBytes == null) { if (currentNonCachedMetricsDistributedBytes == null) {
@@ -689,12 +701,12 @@ public class BalancedMetricResolver implements MetricResolver {
try { try {
lock.writeLock().release(); lock.writeLock().release();
} catch (Exception e3) { } catch (Exception e3) {
LOG.error(e3.getMessage()); LOG.error(e3.getMessage(), e3);
} }
} }
} }
private boolean shouldCache(String metricName) { protected boolean shouldCache(String metricName) {
if (StringUtils.isBlank(metricName)) { if (StringUtils.isBlank(metricName)) {
return false; return false;
@@ -743,7 +755,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (chooseMetricSpecificHost) { if (chooseMetricSpecificHost) {
ArrivalRate rate = metricMap.get(metric); ArrivalRate rate = metricMap.get(metric);
if (rate == null) { if (rate == null) {
rate = new ArrivalRate(arrivalRateTimer); rate = new ArrivalRate(arrivalRateExecutor);
if (!balancerLock.isWriteLockedByCurrentThread()) { if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
@@ -756,7 +768,7 @@ public class BalancedMetricResolver implements MetricResolver {
} }
if (StringUtils.isBlank(metric)) { if (StringUtils.isBlank(metric)) {
tbh = getRandomHost(null); tbh = getRoundRobinHost(null);
} else { } else {
tbh = metricToHostMap.get(metric); tbh = metricToHostMap.get(metric);
if (tbh == null) { if (tbh == null) {
@@ -765,17 +777,17 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
} }
assignMetric(metric, tbh); assignMetric(metric, tbh, ASSIGN_ROUND_ROBIN);
} else if (!tbh.isUp()) { } else if (!tbh.isUp()) {
TimelyBalancedHost oldTbh = tbh; TimelyBalancedHost oldTbh = tbh;
tbh = getLeastUsedHost(); tbh = getRoundRobinHost(oldTbh);
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}", LOG.debug("rebalancing from host that is down: reassigning metric {} from server {} to {}", metric,
metric, oldTbh.getHost(), oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort()); TimelyBalancedHost.toStringShort(oldTbh), TimelyBalancedHost.toStringShort(tbh));
if (!balancerLock.isWriteLockedByCurrentThread()) { if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
} }
assignMetric(metric, tbh); assignMetric(metric, tbh, HOST_DOWN_ROUND_ROBIN);
} }
} }
@@ -792,7 +804,7 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
} }
assignMetric(metric, tbh); assignMetric(metric, tbh, ASSIGN_FALLBACK_SEQUENTIAL);
} }
} }
} finally { } finally {
@@ -821,20 +833,23 @@ public class BalancedMetricResolver implements MetricResolver {
metric = null; metric = null;
} }
if (tbh == null || !tbh.isUp()) { if (tbh == null) {
for (int x = 0; tbh == null && x < serverList.size(); x++) { tbh = getRoundRobinHost(null);
tbh = serverList.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverList.size()); if (!balancerLock.isWriteLockedByCurrentThread()) {
if (!tbh.isUp()) { balancerLock.readLock().unlock();
tbh = null; balancerLock.writeLock().lock();
}
} }
if (tbh != null && StringUtils.isNotBlank(metric)) { assignMetric(metric, tbh, ASSIGN_ROUND_ROBIN);
if (!balancerLock.isWriteLockedByCurrentThread()) { } else if (!tbh.isUp()) {
balancerLock.readLock().unlock(); TimelyBalancedHost oldTbh = tbh;
balancerLock.writeLock().lock(); tbh = getRoundRobinHost(oldTbh);
} LOG.debug("rebalancing from host that is down: reassigning metric {} from server {} to {}", metric,
assignMetric(metric, tbh); TimelyBalancedHost.toStringShort(oldTbh), TimelyBalancedHost.toStringShort(tbh));
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
} }
assignMetric(metric, tbh, HOST_DOWN_ROUND_ROBIN);
} }
// if all else fails // if all else fails
@@ -850,7 +865,7 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock(); balancerLock.readLock().unlock();
balancerLock.writeLock().lock(); balancerLock.writeLock().lock();
} }
assignMetric(metric, tbh); assignMetric(metric, tbh, ASSIGN_FALLBACK_SEQUENTIAL);
} }
} }
} finally { } finally {
@@ -863,7 +878,7 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh; return tbh;
} }
private TimelyBalancedHost findHost(String host, int tcpPort) { protected TimelyBalancedHost findHost(String host, int tcpPort) {
TimelyBalancedHost tbh = null; TimelyBalancedHost tbh = null;
for (TimelyBalancedHost h : serverList) { for (TimelyBalancedHost h : serverList) {
if (h.getHost().equals(host) && h.getTcpPort() == tcpPort) { if (h.getHost().equals(host) && h.getTcpPort() == tcpPort) {
@@ -874,144 +889,100 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh; return tbh;
} }
private void readAssignmentsFromHdfs() { private void assignMetric(String metric, TimelyBalancedHost tbh, MetricAssignedEvent.Reason reason) {
Map<String, TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>();
try {
assignmentsIPRWLock.readLock().acquire();
balancerLock.writeLock().lock();
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
FSDataInputStream iStream = fs.open(assignmentFile);
CsvReader reader = new CsvReader(iStream, ',', Charset.forName("UTF-8"));
reader.setUseTextQualifier(false);
// skip the headers
boolean success = true;
success = reader.readHeaders();
while (success) {
success = reader.readRecord();
String[] nextLine = reader.getValues();
if (nextLine.length >= 3) {
String metric = nextLine[0];
String host = nextLine[1];
int tcpPort = Integer.parseInt(nextLine[2]);
TimelyBalancedHost tbh = findHost(host, tcpPort);
if (tbh == null) {
tbh = getRoundRobinHost(null);
} else {
LOG.trace("Found assigment: {} to {}:{}", metric, host, tcpPort);
}
assignedMetricToHostMap.put(metric, tbh);
}
}
metricToHostMap.clear();
for (Map.Entry<String, TimelyBalancedHost> e : assignedMetricToHostMap.entrySet()) {
if (shouldCache(e.getKey())) {
metricToHostMap.put(e.getKey(), e.getValue());
}
}
nonCachedMetricsLocalLock.readLock().lock();
try {
Iterator<Map.Entry<String, TimelyBalancedHost>> itr = metricToHostMap.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, TimelyBalancedHost> e = itr.next();
if (nonCachedMetrics.contains(e.getKey())) {
itr.remove();
}
}
} finally {
nonCachedMetricsLocalLock.readLock().unlock();
}
assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue());
LOG.info("Read {} assignments from hdfs lastHdfsUpdate = lastLocalUpdate ({})", metricToHostMap.size(),
new Date(assignmentsLastUpdatedLocal.get()));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
balancerLock.writeLock().unlock();
try {
assignmentsIPRWLock.readLock().release();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
private void writeAssigmentsToHdfs() {
CsvWriter writer = null;
try {
assignmentsIPRWLock.writeLock().acquire();
balancerLock.readLock().lock();
nonCachedMetricsLocalLock.readLock().lock();
try {
if (!metricToHostMap.isEmpty()) {
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
if (!fs.exists(assignmentFile.getParent())) {
fs.mkdirs(assignmentFile.getParent());
}
FSDataOutputStream oStream = fs.create(assignmentFile, true);
writer = new CsvWriter(oStream, ',', Charset.forName("UTF-8"));
writer.setUseTextQualifier(false);
writer.write("metric");
writer.write("host");
writer.write("tcpPort");
writer.endRecord();
for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) {
if (!nonCachedMetrics.contains(e.getKey())) {
writer.write(e.getKey());
writer.write(e.getValue().getHost());
writer.write(Integer.toString(e.getValue().getTcpPort()));
writer.endRecord();
LOG.trace("Saving assigment: {} to {}:{}", e.getKey(), e.getValue().getHost(),
e.getValue().getTcpPort());
}
}
long now = System.currentTimeMillis();
assignmentsLastUpdatedLocal.set(now);
assignmentsLastUpdatedInHdfs.trySet(now);
if (!assignmentsLastUpdatedInHdfs.get().succeeded()) {
assignmentsLastUpdatedInHdfs.forceSet(now);
}
LOG.debug("Wrote {} assignments to hdfs lastHdfsUpdate = lastLocalUpdate ({})",
metricToHostMap.size(), new Date(assignmentsLastUpdatedLocal.get()));
}
} finally {
nonCachedMetricsLocalLock.readLock().unlock();
balancerLock.readLock().unlock();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
if (writer != null) {
writer.close();
}
try {
assignmentsIPRWLock.writeLock().release();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
private void assignMetric(String metric, TimelyBalancedHost tbh) {
if (isLeader.get()) { if (isLeader.get()) {
balancerLock.writeLock().lock(); if (StringUtils.isNotBlank(metric) && tbh != null) {
try { balancerLock.writeLock().lock();
metricToHostMap.put(metric, tbh); try {
assignmentsLastUpdatedLocal.set(System.currentTimeMillis()); metricAssignedEvent(metric, metricToHostMap.get(metric), tbh, reason);
} finally { metricToHostMap.put(metric, tbh);
balancerLock.writeLock().unlock(); assignmentsLastUpdatedLocal.set(System.currentTimeMillis());
} finally {
balancerLock.writeLock().unlock();
}
} else {
unassignedMetrics.add(metric);
// Exception e = new IllegalStateException("Bad assignment metric:" + metric + "
// host:" + tbh);
LOG.warn("Bad assignment metric:" + metric + " host:" + tbh);
} }
} }
} }
public void registerCallback(MetricAssignedCallback callback) {
this.metricAssignedCallbacks.add(callback);
}
public void registerCallback(MetricBalanceCallback callback) {
this.metricBalanceCallbacks.add(callback);
}
public void registerCallback(MetricHostCallback callback) {
this.metricHostCallbacks.add(callback);
}
protected void metricAssignedEvent(String metric, TimelyBalancedHost losing, TimelyBalancedHost gaining,
MetricAssignedEvent.Reason reason) {
if (!metricAssignedCallbacks.isEmpty()) {
MetricAssignedEvent event = new MetricAssignedEvent(metric, losing, gaining, reason);
for (MetricAssignedCallback callback : metricAssignedCallbacks) {
callback.onEvent(event);
}
}
}
protected void metricBalanceEvent(MetricBalanceEvent.ProgressType progressType,
BalancedMetricResolver.BalanceType balanceType, long numReassigned) {
if (!metricBalanceCallbacks.isEmpty()) {
MetricBalanceEvent event = new MetricBalanceEvent(progressType, balanceType, numReassigned);
for (MetricBalanceCallback callback : metricBalanceCallbacks) {
callback.onEvent(event);
}
}
}
protected void metricHostEvent(TimelyBalancedHost host, MetricHostEvent.ActionType actionType) {
if (!metricHostCallbacks.isEmpty()) {
MetricHostEvent event = new MetricHostEvent(host, actionType);
for (MetricHostCallback callback : metricHostCallbacks) {
callback.onEvent(event);
}
}
}
protected void writeAssignments(Map<String, TimelyBalancedHost> metricToHostMap) {
assignmentPerister.writeAssignments(metricToHostMap);
}
@Override
public void stop() {
this.assignmentExecutor.shutdown();
try {
this.assignmentExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.assignmentExecutor.isTerminated()) {
this.assignmentExecutor.shutdownNow();
}
}
this.arrivalRateExecutor.shutdown();
try {
this.arrivalRateExecutor.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.arrivalRateExecutor.isTerminated()) {
this.arrivalRateExecutor.shutdownNow();
}
}
try {
this.healthChecker.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
} }
@@ -0,0 +1,320 @@
package timely.balancer.resolver;
import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import org.apache.commons.lang.StringUtils;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.recipes.atomic.DistributedAtomicLong;
import org.apache.curator.framework.recipes.locks.InterProcessReadWriteLock;
import org.apache.curator.retry.RetryForever;
import org.apache.hadoop.conf.Configuration;
import org.apache.hadoop.fs.FileSystem;
import org.apache.hadoop.fs.Path;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.resolver.eventing.MetricAssignedEvent;
public class MetricAssignmentPerister {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private enum PeristerType {
HDFS, FILE
}
// passed in through constructor
BalancedMetricResolver balancedMetricResolver;
private BalancerConfiguration balancerConfig;
private Map<String, TimelyBalancedHost> metricToHostMap;
private Set<String> nonCachedMetrics;
private InterProcessReadWriteLock assignmentsIPRWLock;
private ReentrantReadWriteLock nonCachedMetricsLocalLock;
private ReentrantReadWriteLock balancerLock;
private AtomicLong assignmentsLastUpdatedLocal;
// local
private DistributedAtomicLong assignmentsLastUpdatedInHdfs;
public MetricAssignmentPerister(BalancedMetricResolver balancedMetricResolver, BalancerConfiguration balancerConfig,
Map<String, TimelyBalancedHost> metricToHostMap, Set<String> nonCachedMetrics,
CuratorFramework curatorFramework, InterProcessReadWriteLock assignmentsIPRWLock,
ReentrantReadWriteLock nonCachedMetricsLocalLock, AtomicLong assignmentsLastUpdatedLocal,
ReentrantReadWriteLock balancerLock) {
this.balancedMetricResolver = balancedMetricResolver;
this.balancerConfig = balancerConfig;
this.metricToHostMap = metricToHostMap;
this.nonCachedMetrics = nonCachedMetrics;
this.assignmentsIPRWLock = assignmentsIPRWLock;
this.nonCachedMetricsLocalLock = nonCachedMetricsLocalLock;
this.assignmentsLastUpdatedLocal = assignmentsLastUpdatedLocal;
this.balancerLock = balancerLock;
assignmentsLastUpdatedInHdfs = new DistributedAtomicLong(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH,
new RetryForever(1000));
}
public long getAssignmentsLastUpdatedByLeader() throws Exception {
return assignmentsLastUpdatedInHdfs.get().postValue();
}
public void readAssignments(boolean checkIfNecessary) {
try {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (checkIfNecessary) {
if (lastHdfsUpdate <= lastLocalUpdate) {
LOG.debug("Not reading assignments from storage lastStorageUpdate ({}) <= lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
return;
}
}
LOG.debug("Reading assignments from storage lastStorageUpdate ({}) > lastLocalUpdate ({})",
new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
// proceed with reading from storage
try (InputStream inputStream = getInputStream(balancerConfig)) {
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.writeLock().lock();
// get potential new list from implementation
Map<String, TimelyBalancedHost> assignedMetricToHostMap = readFromStream(inputStream);
metricToHostMap.clear();
for (Map.Entry<String, TimelyBalancedHost> e : assignedMetricToHostMap.entrySet()) {
if (StringUtils.isNotBlank(e.getKey()) && e.getValue() != null) {
if (balancedMetricResolver.shouldCache(e.getKey())) {
balancedMetricResolver.metricAssignedEvent(e.getKey(), null, e.getValue(),
MetricAssignedEvent.Reason.ASSIGN_FILE);
metricToHostMap.put(e.getKey(), e.getValue());
}
} else {
Exception e1 = new IllegalStateException(
"Bad assignment metric:" + e.getKey() + " host:" + e.getValue());
LOG.warn(e1.getMessage(), e1);
}
}
nonCachedMetricsLocalLock.readLock().lock();
try {
Iterator<Map.Entry<String, TimelyBalancedHost>> itr = metricToHostMap.entrySet().iterator();
while (itr.hasNext()) {
Map.Entry<String, TimelyBalancedHost> e = itr.next();
if (nonCachedMetrics.contains(e.getKey())) {
itr.remove();
}
}
} finally {
nonCachedMetricsLocalLock.readLock().unlock();
}
assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue());
LOG.info("Read {} assignments from storage lastStorageUpdate = lastLocalUpdate ({})",
metricToHostMap.size(), new Date(assignmentsLastUpdatedLocal.get()));
} catch (FileNotFoundException e) {
createAssignmentFile();
metricToHostMap.clear();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
balancerLock.writeLock().unlock();
try {
assignmentsIPRWLock.readLock().release();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
public void writeAssignments() {
writeAssignments(metricToHostMap);
}
protected void writeAssignments(Map<String, TimelyBalancedHost> metricToHostMap) {
try {
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
balancerLock.readLock().lock();
nonCachedMetricsLocalLock.readLock().lock();
try (OutputStream outputStream = getOutputStream(balancerConfig)) {
if (!metricToHostMap.isEmpty()) {
writeToStream(outputStream);
long now = System.currentTimeMillis();
assignmentsLastUpdatedLocal.set(now);
assignmentsLastUpdatedInHdfs.trySet(now);
if (!assignmentsLastUpdatedInHdfs.get().succeeded()) {
assignmentsLastUpdatedInHdfs.forceSet(now);
}
LOG.debug("Wrote {} assignments to storage lastStorageUpdate = lastLocalUpdate ({})",
metricToHostMap.size(), new Date(assignmentsLastUpdatedLocal.get()));
}
} finally {
nonCachedMetricsLocalLock.readLock().unlock();
balancerLock.readLock().unlock();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
} finally {
try {
assignmentsIPRWLock.writeLock().release();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
}
public Map<String, TimelyBalancedHost> readFromStream(InputStream inputStream) throws IOException {
Map<String, TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>();
try {
CsvReader reader = new CsvReader(inputStream, ',', Charset.forName("UTF-8"));
reader.setUseTextQualifier(false);
// skip the headers
boolean success = true;
success = reader.readHeaders();
while (success) {
success = reader.readRecord();
String[] nextLine = reader.getValues();
if (nextLine.length >= 3) {
String metric = nextLine[0];
String host = nextLine[1];
int tcpPort = Integer.parseInt(nextLine[2]);
TimelyBalancedHost tbh = balancedMetricResolver.findHost(host, tcpPort);
if (tbh == null) {
tbh = balancedMetricResolver.getRoundRobinHost(null);
} else {
LOG.trace("Found assigment: {} to {}:{}", metric, host, tcpPort);
}
assignedMetricToHostMap.put(metric, tbh);
}
}
} catch (IOException e) {
LOG.error(e.getMessage(), e);
throw e;
}
return assignedMetricToHostMap;
}
private void writeToStream(OutputStream outputStream) throws IOException {
CsvWriter writer = null;
try {
writer = new CsvWriter(outputStream, ',', Charset.forName("UTF-8"));
writer.setUseTextQualifier(false);
writer.write("metric");
writer.write("host");
writer.write("tcpPort");
writer.endRecord();
for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) {
if (!nonCachedMetrics.contains(e.getKey())) {
writer.write(e.getKey());
writer.write(e.getValue().getHost());
writer.write(Integer.toString(e.getValue().getTcpPort()));
writer.endRecord();
LOG.trace("Saving assigment: {} to {}:{}", e.getKey(), e.getValue().getHost(),
e.getValue().getTcpPort());
}
}
} finally {
if (writer != null) {
writer.close();
}
}
}
public void createAssignmentFile() {
LOG.info("Creating assignment file: " + balancerConfig.getAssignmentFile());
PeristerType peristerType = PeristerType.valueOf(balancerConfig.getMetricAssignmentPersisterType());
switch (peristerType) {
case HDFS:
try {
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfig.getAssignmentFile());
fs.create(assignmentFile);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
break;
case FILE:
try {
File assignmentFile = new File(balancerConfig.getAssignmentFile());
String parentPath = assignmentFile.getParent();
File directory = new File(parentPath);
if (!directory.exists()) {
directory.mkdirs();
}
if (!assignmentFile.exists()) {
assignmentFile.createNewFile();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
break;
}
}
public InputStream getInputStream(BalancerConfiguration balancerConfiguration) throws Exception {
PeristerType peristerType = PeristerType.valueOf(balancerConfiguration.getMetricAssignmentPersisterType());
switch (peristerType) {
case HDFS:
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfiguration.getAssignmentFile());
return fs.open(assignmentFile);
case FILE:
return new FileInputStream(new File(balancerConfiguration.getAssignmentFile()));
default:
throw new IllegalArgumentException(
"Unknown metricPersisterType " + balancerConfiguration.getMetricAssignmentPersisterType());
}
}
public OutputStream getOutputStream(BalancerConfiguration balancerConfiguration) throws IOException {
PeristerType peristerType = PeristerType.valueOf(balancerConfiguration.getMetricAssignmentPersisterType());
switch (peristerType) {
case HDFS:
Configuration configuration = new Configuration();
FileSystem fs = FileSystem.get(configuration);
Path assignmentFile = new Path(balancerConfiguration.getAssignmentFile());
if (!fs.exists(assignmentFile.getParent())) {
fs.mkdirs(assignmentFile.getParent());
}
return fs.create(assignmentFile, true);
case FILE:
return new FileOutputStream(new File(balancerConfiguration.getAssignmentFile()));
default:
throw new IllegalArgumentException(
"Unknown metricPersisterType " + balancerConfiguration.getMetricAssignmentPersisterType());
}
}
}
@@ -7,4 +7,9 @@ public interface MetricResolver {
public TimelyBalancedHost getHostPortKeyIngest(String metric); public TimelyBalancedHost getHostPortKeyIngest(String metric);
public TimelyBalancedHost getHostPortKey(String metric); public TimelyBalancedHost getHostPortKey(String metric);
public void start();
public void stop();
} }
@@ -0,0 +1,7 @@
package timely.balancer.resolver.eventing;
public abstract class MetricAssignedCallback {
public abstract void onEvent(MetricAssignedEvent event);
}
@@ -0,0 +1,39 @@
package timely.balancer.resolver.eventing;
import timely.balancer.connection.TimelyBalancedHost;
public class MetricAssignedEvent {
public enum Reason {
ASSIGN_ROUND_ROBIN, HOST_DOWN_ROUND_ROBIN, ASSIGN_FALLBACK_SEQUENTIAL, HOST_REMOVED, REBALANCE, ASSIGN_FILE
}
private final String metric;
private final TimelyBalancedHost losingHost;
private final TimelyBalancedHost gainingHost;
private final Reason reason;
public MetricAssignedEvent(String metric, TimelyBalancedHost losingHost, TimelyBalancedHost gainingHost,
Reason reason) {
this.metric = metric;
this.losingHost = losingHost;
this.gainingHost = gainingHost;
this.reason = reason;
}
public String getMetric() {
return metric;
}
public TimelyBalancedHost getLosingHost() {
return losingHost;
}
public TimelyBalancedHost getGainingHost() {
return gainingHost;
}
public Reason getReason() {
return reason;
}
}
@@ -0,0 +1,7 @@
package timely.balancer.resolver.eventing;
public abstract class MetricBalanceCallback {
public abstract void onEvent(MetricBalanceEvent event);
}
@@ -0,0 +1,33 @@
package timely.balancer.resolver.eventing;
import timely.balancer.resolver.BalancedMetricResolver;
public class MetricBalanceEvent {
public enum ProgressType {
BEGIN, BALANCER_LOCK_ACQUIRED, END
}
private final ProgressType progressType;
private final BalancedMetricResolver.BalanceType balanceType;
private final Long numRessigned;
public MetricBalanceEvent(ProgressType progressType, BalancedMetricResolver.BalanceType balanceType,
long numReassigned) {
this.progressType = progressType;
this.balanceType = balanceType;
this.numRessigned = numReassigned;
}
public BalancedMetricResolver.BalanceType getBalanceType() {
return balanceType;
}
public ProgressType getProgressType() {
return progressType;
}
public Long getNumRessigned() {
return numRessigned;
}
}
@@ -0,0 +1,7 @@
package timely.balancer.resolver.eventing;
public abstract class MetricHostCallback {
public abstract void onEvent(MetricHostEvent event);
}
@@ -0,0 +1,26 @@
package timely.balancer.resolver.eventing;
import timely.balancer.connection.TimelyBalancedHost;
public class MetricHostEvent {
public enum ActionType {
ADDED, REMOVED, SUCCESS, FAILURE
}
private final TimelyBalancedHost timelyBalancedHost;
private final ActionType actionType;
public MetricHostEvent(TimelyBalancedHost timelyBalancedHost, ActionType actionType) {
this.timelyBalancedHost = timelyBalancedHost;
this.actionType = actionType;
}
public TimelyBalancedHost getTimelyBalancedHost() {
return timelyBalancedHost;
}
public ActionType getActionType() {
return actionType;
}
}
+5
View File
@@ -10,4 +10,9 @@
<Method name="getBuf" /> <Method name="getBuf" />
<Bug pattern="EI_EXPOSE_REP" /> <Bug pattern="EI_EXPOSE_REP" />
</Match> </Match>
<Match>
<Class name="timely.balancer.resolver.MetricAssignmentPerister" />
<Method name="createAssignmentFile" />
<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
</Match>
</FindBugsFilter> </FindBugsFilter>
@@ -0,0 +1,76 @@
package timely.balancer.resolver;
import static timely.Server.SERVICE_DISCOVERY_PATH;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.x.discovery.ServiceDiscovery;
import org.apache.curator.x.discovery.ServiceDiscoveryBuilder;
import org.apache.curator.x.discovery.ServiceInstance;
import org.apache.curator.x.discovery.ServiceInstanceBuilder;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.ServerDetails;
import timely.balancer.connection.TimelyBalancedHost;
public class ServiceRegistry {
private static final Logger LOG = LoggerFactory.getLogger(ServiceRegistry.class);
public static void registerService(CuratorFramework curatorFramework, TimelyBalancedHost tbh) {
try {
try {
Stat stat = curatorFramework.checkExists().forPath(SERVICE_DISCOVERY_PATH);
if (stat == null) {
curatorFramework.create().creatingParentContainersIfNeeded().forPath(SERVICE_DISCOVERY_PATH);
}
} catch (Exception e) {
LOG.error(e.getMessage());
}
ServiceInstance<ServerDetails> serviceInstance = serviceInstance(tbh);
ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start();
discovery.registerService(serviceInstance);
} catch (Exception e) {
LOG.error(e.getMessage());
}
}
public static void unregisterService(CuratorFramework curatorFramework, TimelyBalancedHost tbh) {
try {
try {
Stat stat = curatorFramework.checkExists().forPath(SERVICE_DISCOVERY_PATH);
if (stat == null) {
curatorFramework.create().creatingParentContainersIfNeeded().forPath(SERVICE_DISCOVERY_PATH);
}
} catch (Exception e) {
LOG.error(e.getMessage());
}
ServiceInstance<ServerDetails> serviceInstance = serviceInstance(tbh);
ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start();
discovery.unregisterService(serviceInstance);
} catch (Exception e) {
LOG.error(e.getMessage());
}
}
private static ServiceInstance<ServerDetails> serviceInstance(TimelyBalancedHost config) throws Exception {
ServerDetails serverDetails = new ServerDetails();
serverDetails.setHost(config.getHost());
serverDetails.setTcpPort(config.getTcpPort());
serverDetails.setHttpPort(config.getHttpPort());
serverDetails.setWsPort(config.getWsPort());
serverDetails.setUdpPort(config.getUdpPort());
ServiceInstanceBuilder<ServerDetails> builder = ServiceInstance.builder();
String serviceName = config.getHost() + ":" + config.getTcpPort();
ServiceInstance<ServerDetails> serviceInstance = builder.id(serviceName).name("timely-server")
.address(config.getHost()).port(config.getTcpPort()).payload(serverDetails).build();
return serviceInstance;
}
}
@@ -0,0 +1,270 @@
package timely.balancer.resolver;
import java.io.File;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Random;
import java.util.UUID;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryNTimes;
import org.apache.zookeeper.server.ServerConfig;
import org.apache.zookeeper.server.ZooKeeperServerMain;
import org.apache.zookeeper.server.quorum.QuorumPeerConfig;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.SocketUtils;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.resolver.eventing.MetricAssignedCallback;
import timely.balancer.resolver.eventing.MetricAssignedEvent;
import timely.balancer.resolver.eventing.MetricBalanceCallback;
import timely.balancer.resolver.eventing.MetricBalanceEvent;
import timely.balancer.resolver.eventing.MetricHostCallback;
import timely.balancer.resolver.eventing.MetricHostEvent;
public class TestBalancedMetricResolver {
private final Logger LOG = LoggerFactory.getLogger(getClass());
private CuratorFramework curatorFramework;
private SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd HHmmss.SSS");
private Random r = new Random(System.currentTimeMillis());
private List<TimelyBalancedHost> timelyHosts;
private Map<MetricAssignedEvent, Integer> metricBalanceEventCounter = new HashMap<>();
@Before
public void startup() {
QuorumPeerConfig currentConfig = createZookeeper();
InetSocketAddress address = currentConfig.getClientPortAddress();
String zkServer = address.getHostName() + ":" + address.getPort();
curatorFramework = CuratorFrameworkFactory.newClient(zkServer, 1000, 5000, new RetryNTimes(2, 50));
curatorFramework.start();
timelyHosts = new ArrayList<>();
}
@After
public void shutdown() {
curatorFramework.close();
}
private QuorumPeerConfig createZookeeper() {
Properties startupProperties = new Properties();
File file = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID());
file.deleteOnExit();
startupProperties.setProperty("dataDir", file.getAbsolutePath());
startupProperties.setProperty("clientPort", String.valueOf(SocketUtils.findAvailableTcpPort(3000, 4000)));
QuorumPeerConfig quorumConfiguration = new QuorumPeerConfig();
try {
quorumConfiguration.parseProperties(startupProperties);
} catch (Exception e) {
throw new RuntimeException(e);
}
ZooKeeperServerMain zooKeeperServer = new ZooKeeperServerMain();
final ServerConfig configuration = new ServerConfig();
configuration.readFrom(quorumConfiguration);
new Thread() {
public void run() {
try {
zooKeeperServer.runFromConfig(configuration);
} catch (IOException e) {
LOG.error("ZooKeeper Failed", e);
}
}
}.start();
return quorumConfiguration;
}
private BalancerConfiguration getBalancerConfiguration() {
BalancerConfiguration balancerConfiguration = new BalancerConfiguration();
balancerConfiguration.setMetricAssignmentPersisterType("FILE");
File file = new File(System.getProperty("java.io.tmpdir") + File.separator + UUID.randomUUID());
file.deleteOnExit();
balancerConfiguration.setAssignmentFile(file.getAbsolutePath());
balancerConfiguration.setBalanceDelay(0);
balancerConfiguration.setBalancePeriod(1000);
balancerConfiguration.setPersistDelay(0);
balancerConfiguration.setPersistDelay(1000);
balancerConfiguration.setCheckServerHealthInterval(10);
return balancerConfiguration;
}
private void createTimelyHosts(BalancerConfiguration balancerConfiguration, int numHosts) {
for (int port = 1000, x = 0; x < numHosts; x++, port += 10) {
TimelyBalancedHost tbh = TimelyBalancedHost.of("localhost", port);
tbh.setBalancerConfig(balancerConfiguration);
timelyHosts.add(tbh);
ServiceRegistry.registerService(curatorFramework, tbh);
}
}
private void registerCallbacks(BalancedMetricResolver resolver) {
resolver.registerCallback(new MetricAssignedCallback() {
@Override
public void onEvent(MetricAssignedEvent event) {
LOG.debug(String.format("%s metric:%s lose:%s:%d gain:%s:%d reason:%s",
event.getClass().getSimpleName(), event.getMetric(),
event.getLosingHost() == null ? null : event.getLosingHost().getHost(),
event.getLosingHost() == null ? null : event.getLosingHost().getTcpPort(),
event.getGainingHost().getHost(), event.getGainingHost().getTcpPort(), event.getReason()));
}
});
resolver.registerCallback(new MetricHostCallback() {
@Override
public void onEvent(MetricHostEvent event) {
LOG.debug(String.format("%s host:%s:%d type:%s", event.getClass().getSimpleName(),
event.getTimelyBalancedHost().getHost(), event.getTimelyBalancedHost().getTcpPort(),
event.getActionType()));
}
});
resolver.registerCallback(new MetricBalanceCallback() {
@Override
public void onEvent(MetricBalanceEvent event) {
LOG.debug(String.format("%s type:%s progress:%s reassigned:%d", event.getClass().getSimpleName(),
event.getBalanceType(), event.getProgressType(), event.getNumRessigned()));
}
});
}
private TimelyBalancedHost randomServerDown(TestHealthChecker healthChecker, int numHosts) {
TimelyBalancedHost tbh = timelyHosts.get(r.nextInt(numHosts));
LOG.debug("Marking server down:" + TimelyBalancedHost.toStringShort(tbh));
healthChecker.serverDown(tbh);
return tbh;
}
private TimelyBalancedHost randomServerToggle(TestHealthChecker healthChecker, int numHosts) {
TimelyBalancedHost tbh = timelyHosts.get(r.nextInt(numHosts));
LOG.debug("Marking server " + (tbh.isUp() ? "down:" : "up:") + TimelyBalancedHost.toStringShort(tbh));
healthChecker.serverToggle(tbh);
return tbh;
}
private TimelyBalancedHost randomServerUnregister(int numHosts) {
TimelyBalancedHost tbh = timelyHosts.get(r.nextInt(numHosts));
LOG.debug("Unregistering:" + TimelyBalancedHost.toStringShort(tbh));
ServiceRegistry.unregisterService(curatorFramework, tbh);
return tbh;
}
@Test
public void test1() {
LOG.debug("Configure test");
int numHosts = 10;
BalancerConfiguration balancerConfiguration = getBalancerConfiguration();
createTimelyHosts(balancerConfiguration, numHosts);
TestHealthChecker healthChecker = new TestHealthChecker(balancerConfiguration, timelyHosts);
BalancedMetricResolver balancedMetricResolver = new BalancedMetricResolver(curatorFramework,
balancerConfiguration, healthChecker);
registerCallbacks(balancedMetricResolver);
LOG.debug("Start balancedMetricResolver");
balancedMetricResolver.start();
long now = System.currentTimeMillis();
int numMetrics = 100;
for (int x = 0; x < numMetrics; x++) {
balancedMetricResolver.getHostPortKeyIngest(String.format("metric%03d", x));
}
LOG.debug("Begin monitoring");
long cycle = 0;
List<TimelyBalancedHost> serversUnregistered = new ArrayList<>();
while (System.currentTimeMillis() < now + 15000) {
try {
Thread.sleep(100);
for (int x = 0; x < 10000; x++) {
int mNum = r.nextInt(numMetrics);
balancedMetricResolver.getHostPortKeyIngest(String.format("metric%03d", mNum));
}
// if (cycle % 100 == 0) {
// randomServerDown(healthChecker, numHosts);
// }
// if (cycle % 2 == 0) {
// randomServerToggle(healthChecker, numHosts);
// }
if (cycle % 100 == 0) {
serversUnregistered.add(randomServerUnregister(numHosts));
}
if (cycle % 500 == 0) {
serversUnregistered.stream().forEach(tbh -> ServiceRegistry.registerService(curatorFramework, tbh));
serversUnregistered.clear();
}
} catch (InterruptedException e) {
}
cycle++;
}
LOG.debug("Stop balancedMetricResolver");
balancedMetricResolver.stop();
}
@Test
public void test2() {
LOG.debug("Configure test");
int numHosts = 10;
BalancerConfiguration balancerConfiguration = getBalancerConfiguration();
createTimelyHosts(balancerConfiguration, numHosts);
TestHealthChecker healthChecker = new TestHealthChecker(balancerConfiguration, timelyHosts);
BalancedMetricResolver balancedMetricResolver = new BalancedMetricResolver(curatorFramework,
balancerConfiguration, healthChecker);
registerCallbacks(balancedMetricResolver);
LOG.debug("Start balancedMetricResolver");
balancedMetricResolver.start();
long now = System.currentTimeMillis();
int numMetrics = 100;
for (int x = 0; x < numMetrics; x++) {
balancedMetricResolver.getHostPortKeyIngest(String.format("metric%03d", x));
}
LOG.debug("Begin monitoring");
long cycle = 0;
List<TimelyBalancedHost> serversUnregistered = new ArrayList<>();
while (System.currentTimeMillis() < now + 15000) {
try {
Thread.sleep(100);
for (int x = 0; x < 10000; x++) {
int mNum = r.nextInt(numMetrics);
balancedMetricResolver.getHostPortKeyIngest(String.format("metric%03d", mNum));
}
// if (cycle % 100 == 0) {
// randomServerDown(healthChecker, numHosts);
// }
// if (cycle % 2 == 0) {
// randomServerToggle(healthChecker, numHosts);
// }
if (cycle % 100 == 0) {
serversUnregistered.add(randomServerUnregister(numHosts));
}
if (cycle % 500 == 0) {
serversUnregistered.stream().forEach(tbh -> ServiceRegistry.registerService(curatorFramework, tbh));
serversUnregistered.clear();
}
} catch (InterruptedException e) {
}
cycle++;
}
LOG.debug("Stop balancedMetricResolver");
balancedMetricResolver.stop();
}
}
@@ -0,0 +1,89 @@
package timely.balancer.resolver;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.healthcheck.HealthChecker;
import timely.balancer.healthcheck.TcpHealthChecker;
public class TestHealthChecker implements HealthChecker {
private final Logger LOG = LoggerFactory.getLogger(TcpHealthChecker.class);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private Map<TimelyBalancedHost, Boolean> serverUpMap = Collections.synchronizedMap(new HashMap<>());
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
public TestHealthChecker(BalancerConfiguration config, List<TimelyBalancedHost> timelyHosts) {
setTimelyHosts(timelyHosts);
this.executorService.scheduleAtFixedRate(() -> {
synchronized (timelyHosts) {
for (TimelyBalancedHost h : timelyHosts) {
try {
if (TestHealthChecker.this.isServerHealthy(h)) {
h.reportSuccess();
} else {
h.reportFailure();
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
h.reportFailure();
}
}
}
}, 0, config.getCheckServerHealthInterval(), TimeUnit.MILLISECONDS);
}
@Override
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts) {
synchronized (timelyHosts) {
this.timelyHosts.clear();
this.timelyHosts.addAll(timelyHosts);
for (TimelyBalancedHost h : timelyHosts) {
serverUpMap.put(h, true);
}
}
}
public boolean isServerHealthy(TimelyBalancedHost h) {
return serverUpMap.get(h);
}
public void serverUp(TimelyBalancedHost h) {
serverUpMap.put(h, true);
}
public void serverDown(TimelyBalancedHost h) {
serverUpMap.put(h, false);
}
public void serverToggle(TimelyBalancedHost h) {
synchronized (serverUpMap) {
serverUpMap.put(h, !serverUpMap.get(h));
}
}
@Override
public void close() throws Exception {
this.executorService.shutdown();
try {
this.executorService.awaitTermination(5, TimeUnit.SECONDS);
} catch (InterruptedException e) {
} finally {
if (!this.executorService.isTerminated()) {
this.executorService.shutdownNow();
}
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
<Appenders>
<Console name="CONSOLE" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyyMMdd HHmmss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Logger name="org.apache.accumulo.core.client.impl.ThriftTransportPool" level="OFF" additivity="false" />
<Logger name="org.apache.accumulo.fate.zookeeper.ZooCache" level="OFF" additivity="false" />
<Logger name="org.apache.zookeeper" level="OFF" additivity="false" />
<Logger name="timely.balancer.resolver" level="TRACE" additivity="true" />
<Logger name="timely.balancer.resolver.TestBalancedMetricResolver" level="DEBUG" additivity="true" />
<Root level="ERROR">
<AppenderRef ref="CONSOLE"/>
</Root>
</Loggers>
</Configuration>
+3 -2
View File
@@ -4,12 +4,13 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-client</artifactId> <artifactId>timely-client</artifactId>
<properties> <properties>
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py --> <!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
<python.client.version>1.2.3</python.client.version> <!-- use .dev0 instead of -SNAPSHOT -->
<python.client.version>1.2.8.dev0</python.client.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency> <dependency>
@@ -1,7 +1,8 @@
package timely.client.tcp; package timely.client.tcp;
import java.io.BufferedWriter;
import java.io.IOException; import java.io.IOException;
import java.io.PrintWriter; import java.io.OutputStreamWriter;
import java.net.Socket; import java.net.Socket;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -14,7 +15,7 @@ public class TcpClient implements AutoCloseable {
private final String host; private final String host;
private final int port; private final int port;
private Socket sock = null; private Socket sock = null;
private PrintWriter out = null; private BufferedWriter out = null;
private long connectTime = 0L; private long connectTime = 0L;
private long backoff = 2000; private long backoff = 2000;
private int bufferSize = -1; private int bufferSize = -1;
@@ -47,7 +48,6 @@ public class TcpClient implements AutoCloseable {
* *
* @param metric * @param metric
* newline terminated string representation of Timely metric * newline terminated string representation of Timely metric
* @throws IOException
*/ */
public synchronized void write(String metric) throws IOException { public synchronized void write(String metric) throws IOException {
if (connect() != 0) { if (connect() != 0) {
@@ -60,7 +60,7 @@ public class TcpClient implements AutoCloseable {
} }
} }
public synchronized void flush() { public synchronized void flush() throws IOException {
if (null != out) { if (null != out) {
out.flush(); out.flush();
writesSinceFlush = 0; writesSinceFlush = 0;
@@ -94,12 +94,12 @@ public class TcpClient implements AutoCloseable {
} }
private synchronized int connect() { private synchronized int connect() {
if (null == sock || !sock.isConnected() || out.checkError()) { if (null == sock || !sock.isConnected()) {
if (System.currentTimeMillis() > (connectTime + backoff)) { if (System.currentTimeMillis() > (connectTime + backoff)) {
try { try {
connectTime = System.currentTimeMillis(); connectTime = System.currentTimeMillis();
sock = new Socket(host, port); sock = new Socket(host, port);
out = new PrintWriter(sock.getOutputStream(), false); out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
backoff = 2000; backoff = 2000;
LOG.trace("Connected to Timely at {}:{}", host, port); LOG.trace("Connected to Timely at {}:{}", host, port);
} catch (IOException e) { } catch (IOException e) {
@@ -62,7 +62,6 @@ public interface ObjectSizeOf {
* ObjectSizeOf interface and if implemented on the object will use that. * ObjectSizeOf interface and if implemented on the object will use that.
* Otherwise it will do a simple navigation of the fields using reflection. * Otherwise it will do a simple navigation of the fields using reflection.
* *
* @param o
* @return an approximation of the object size * @return an approximation of the object size
*/ */
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) { public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
@@ -77,8 +77,6 @@ public class Tag implements Comparable<Tag>, Serializable {
/** /**
* Joins key and value into key=value * Joins key and value into key=value
*
* @return
*/ */
public String join() { public String join() {
return tagParser.combine(this); return tagParser.combine(this);
@@ -47,7 +47,6 @@ public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag
* An alternative combiner for a map of string to string instead of a list of * An alternative combiner for a map of string to string instead of a list of
* tags * tags
* *
* @param tags
* @return string representation * @return string representation
*/ */
public String combine(Map<String, String> tags) { public String combine(Map<String, String> tags) {
+5
View File
@@ -5,4 +5,9 @@
<Method name="connect" /> <Method name="connect" />
<Class name="timely.client.tcp.TcpClient"/> <Class name="timely.client.tcp.TcpClient"/>
</Match> </Match>
<Match>
<!-- Must ignore these everywhere, because of a javac byte code generation bug -->
<!-- https://github.com/spotbugs/spotbugs/issues/756 -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>
</FindBugsFilter> </FindBugsFilter>
@@ -47,9 +47,9 @@ public class SerializationTest {
testSerialization(remove); testSerialization(remove);
} }
@SuppressWarnings("unchecked")
private <T> void testSerialization(T o) throws Exception { private <T> void testSerialization(T o) throws Exception {
String ser = mapper.writeValueAsString(o); String ser = mapper.writeValueAsString(o);
@SuppressWarnings("unchecked")
T des = mapper.readValue(ser, (Class<T>) o.getClass()); T des = mapper.readValue(ser, (Class<T>) o.getClass());
Assert.assertEquals(o, des); Assert.assertEquals(o, des);
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-nsq-plugin</artifactId> <artifactId>collectd-nsq-plugin</artifactId>
<dependencies> <dependencies>
@@ -10,7 +10,7 @@ import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.impl.client.HttpClientBuilder;
import org.collectd.api.OConfigItem; import org.collectd.api.OConfigItem;
public class PooledCloseableHttpClientFactory implements PooledObjectFactory { public class PooledCloseableHttpClientFactory implements PooledObjectFactory<CloseableHttpClient> {
private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds private int connectionRequestTimeout = 10000; // 10 seconds in milliseconds
private int connectTimeout = 30000; // 30 seconds in milliseconds private int connectTimeout = 30000; // 30 seconds in milliseconds
@@ -43,19 +43,19 @@ public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
} }
@Override @Override
public PooledObject makeObject() throws Exception { public PooledObject<CloseableHttpClient> makeObject() throws Exception {
RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout) RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(connectionRequestTimeout)
.setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build(); .setConnectTimeout(connectTimeout).setSocketTimeout(socketTimeout).build();
HttpClientBuilder builder = HttpClientBuilder.create(); HttpClientBuilder builder = HttpClientBuilder.create();
builder.setDefaultRequestConfig(requestConfig); builder.setDefaultRequestConfig(requestConfig);
builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS); builder.setConnectionTimeToLive(connectionTimeToLive, TimeUnit.MILLISECONDS);
return new DefaultPooledObject(builder.build()); return new DefaultPooledObject<>(builder.build());
} }
@Override @Override
public void destroyObject(PooledObject pooledObject) throws Exception { public void destroyObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
try { try {
((CloseableHttpClient) pooledObject.getObject()).close(); pooledObject.getObject().close();
} catch (Exception e) { } catch (Exception e) {
// do nothing // do nothing
} finally { } finally {
@@ -65,17 +65,17 @@ public class PooledCloseableHttpClientFactory implements PooledObjectFactory {
} }
@Override @Override
public boolean validateObject(PooledObject pooledObject) { public boolean validateObject(PooledObject<CloseableHttpClient> pooledObject) {
return pooledObject.getObject() != null; return pooledObject.getObject() != null;
} }
@Override @Override
public void activateObject(PooledObject pooledObject) throws Exception { public void activateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
// do nothing // do nothing
} }
@Override @Override
public void passivateObject(PooledObject pooledObject) throws Exception { public void passivateObject(PooledObject<CloseableHttpClient> pooledObject) throws Exception {
// do nothing // do nothing
} }
@@ -47,6 +47,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
Collectd.registerWrite(WriteNSQPlugin.class.getName(), this); Collectd.registerWrite(WriteNSQPlugin.class.getName(), this);
} }
@Override
public int write(ValueList vl) { public int write(ValueList vl) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(); ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream out = new DataOutputStream(baos); DataOutputStream out = new DataOutputStream(baos);
@@ -110,15 +111,17 @@ public class WriteNSQPlugin extends CollectDPluginParent
} }
} }
@Override
public int shutdown() { public int shutdown() {
Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port); Collectd.logInfo("Shutting down connection to NSQ at " + host + ":" + port);
clientPool.close(); clientPool.close();
return 0; return 0;
} }
@Override
public int config(OConfigItem config) { public int config(OConfigItem config) {
super.config(config); super.config(config);
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); GenericObjectPoolConfig<CloseableHttpClient> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle // use max size for maxTotal and maxIdle
// no need to activate or passivate // no need to activate or passivate
poolConfig.setMaxTotal(POOL_MAX_SIZE); poolConfig.setMaxTotal(POOL_MAX_SIZE);
@@ -156,7 +159,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
Collectd.logError("NSQ host, port, and topic must be configured"); Collectd.logError("NSQ host, port, and topic must be configured");
return -1; return -1;
} }
this.clientPool = new GenericObjectPool(pooledCloseableHttpClientFactory, poolConfig); this.clientPool = new GenericObjectPool<>(pooledCloseableHttpClientFactory, poolConfig);
this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic; this.endpoint = "http://" + host + ":" + port + "/mpub?topic=" + topic;
return 0; return 0;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-plugin-parent</artifactId> <artifactId>collectd-plugin-parent</artifactId>
<dependencies> <dependencies>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>collectd-timely-plugin</artifactId> <artifactId>collectd-timely-plugin</artifactId>
<dependencies> <dependencies>
@@ -8,7 +8,7 @@ import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.collectd.api.Collectd; import org.collectd.api.Collectd;
import org.collectd.api.OConfigItem; import org.collectd.api.OConfigItem;
public class PooledSocketFactory implements PooledObjectFactory { public class PooledSocketFactory implements PooledObjectFactory<Socket> {
private String host = null; private String host = null;
private int port = 0; private int port = 0;
@@ -81,12 +81,12 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public PooledObject makeObject() throws Exception { public PooledObject<Socket> makeObject() throws Exception {
return new DefaultPooledObject(connect()); return new DefaultPooledObject<>(connect());
} }
@Override @Override
public void destroyObject(PooledObject pooledObject) throws Exception { public void destroyObject(PooledObject<Socket> pooledObject) throws Exception {
try { try {
Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port); Collectd.logDebug("Shutting down connection to Timely at " + host + ":" + port);
Socket socket = (Socket) pooledObject.getObject(); Socket socket = (Socket) pooledObject.getObject();
@@ -103,7 +103,7 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public boolean validateObject(PooledObject pooledObject) { public boolean validateObject(PooledObject<Socket> pooledObject) {
if (connectionTimeToLive > -1) { if (connectionTimeToLive > -1) {
if (System.currentTimeMillis() > pooledObject.getCreateTime() + connectionTimeToLive) { if (System.currentTimeMillis() > pooledObject.getCreateTime() + connectionTimeToLive) {
return false; return false;
@@ -114,12 +114,12 @@ public class PooledSocketFactory implements PooledObjectFactory {
} }
@Override @Override
public void activateObject(PooledObject pooledObject) throws Exception { public void activateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing // do nothing
} }
@Override @Override
public void passivateObject(PooledObject pooledObject) throws Exception { public void passivateObject(PooledObject<Socket> pooledObject) throws Exception {
// do nothing // do nothing
} }
} }
@@ -86,13 +86,13 @@ public class WriteTimelyPlugin extends CollectDPluginParent
PooledSocketFactory socketFactory = new PooledSocketFactory(); PooledSocketFactory socketFactory = new PooledSocketFactory();
retval = socketFactory.config(config); retval = socketFactory.config(config);
if (retval == 0) { if (retval == 0) {
GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig(); GenericObjectPoolConfig<Socket> poolConfig = new GenericObjectPoolConfig<>();
// use max size for maxTotal and maxIdle // use max size for maxTotal and maxIdle
// no need to activate and passivate // no need to activate and passivate
poolConfig.setMaxTotal(POOL_MAX_SIZE); poolConfig.setMaxTotal(POOL_MAX_SIZE);
poolConfig.setMaxIdle(POOL_MAX_SIZE); poolConfig.setMaxIdle(POOL_MAX_SIZE);
poolConfig.setTestOnReturn(true); poolConfig.setTestOnReturn(true);
socketPool = new GenericObjectPool(socketFactory, poolConfig); socketPool = new GenericObjectPool<>(socketFactory, poolConfig);
} }
return retval; return retval;
} }
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-common</artifactId> <artifactId>timely-common</artifactId>
<dependencies> <dependencies>
+1 -1
View File
@@ -32,7 +32,7 @@ $JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transpo
popd popd
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}" export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}" JVM_ARGS="-Xmx1G -Xms1G -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector" JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}" JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
+1 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>grafana-auth</artifactId> <artifactId>grafana-auth</artifactId>
<dependencies> <dependencies>
@@ -166,7 +166,6 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId> <artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -81,8 +81,11 @@ public class GrafanaAuth {
this.grafanaAuthConfig = grafanaAuthConfig; this.grafanaAuthConfig = grafanaAuthConfig;
} }
protected static ConfigurableApplicationContext initializeConfiguration(String[] args) { protected static ConfigurableApplicationContext initializeContext(String[] args) {
return new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args); SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
} }
private void shutdownHook() { private void shutdownHook() {
@@ -109,13 +112,11 @@ public class GrafanaAuth {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config()); LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
Integer quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod(); int quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>(); List<Future<?>> groupFutures = new ArrayList<>();
if (httpBossGroup != null) { if (httpBossGroup != null) {
@@ -132,9 +133,7 @@ public class GrafanaAuth {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString()); LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
@@ -272,12 +271,13 @@ public class GrafanaAuth {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
GrafanaAuth.applicationContext = GrafanaAuth.initializeConfiguration(args); GrafanaAuth.applicationContext = GrafanaAuth.initializeContext(args);
GrafanaAuthConfiguration balancerConf = applicationContext.getBean(GrafanaAuthConfiguration.class); GrafanaAuthConfiguration grafanaAuthConf = GrafanaAuth.applicationContext
.getBean(GrafanaAuthConfiguration.class);
GrafanaAuth b = new GrafanaAuth(balancerConf); GrafanaAuth grafanaAuth = new GrafanaAuth(grafanaAuthConf);
try { try {
b.run(); grafanaAuth.run();
LATCH.await(); LATCH.await();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOG.info("Server shutting down."); LOG.info("Server shutting down.");
@@ -285,7 +285,7 @@ public class GrafanaAuth {
LOG.error("Error running server.", e); LOG.error("Error running server.", e);
} finally { } finally {
try { try {
b.shutdown(); grafanaAuth.shutdown();
} catch (Exception e) { } catch (Exception e) {
System.exit(1); System.exit(1);
} }
@@ -314,7 +314,8 @@ public class GrafanaAuth {
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()); ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp())); ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("httpRelay", new GrafanaRelayHandler(config, httpClientPool)); ch.pipeline().addLast("httpRelay", new GrafanaRelayHandler(config, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler()); ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
} }
}; };
} }
@@ -43,8 +43,6 @@ public class GrafanaAuthConfiguration {
/** /**
* Time to wait (in seconds) for connections to finish and to make sure no new * Time to wait (in seconds) for connections to finish and to make sure no new
* connections happen before shutting down Netty event loop groups. * connections happen before shutting down Netty event loop groups.
*
* @return
*/ */
public int getShutdownQuietPeriod() { public int getShutdownQuietPeriod() {
return this.shutdownQuietPeriod; return this.shutdownQuietPeriod;
@@ -1,6 +1,6 @@
package timely.grafana.auth.netty.http; package timely.grafana.auth.netty.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.net.URLEncoder; import java.net.URLEncoder;
@@ -71,7 +71,7 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
try { try {
try { try {
request = msg.getHttpRequest(); request = msg.getHttpRequest();
String originalURI = request.getUri(); String originalURI = request.uri();
originalURI = encodeURI(originalURI); originalURI = encodeURI(originalURI);
if (msg instanceof GrafanaRequest) { if (msg instanceof GrafanaRequest) {
@@ -87,42 +87,42 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
String protocol = config.getGrafana().getProtocol(); String protocol = config.getGrafana().getProtocol();
String host = config.getGrafana().getHost(); String host = config.getGrafana().getHost();
Integer port = config.getGrafana().getPort(); Integer port = config.getGrafana().getPort();
k = TimelyBalancedHost.of(host, -1, port, -1, -1); k = TimelyBalancedHost.of(host, -1, port, -1, -1, null);
client = httpClientPool.borrowObject(k); client = httpClientPool.borrowObject(k);
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers()); Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
// If incoming user sets these headers, do not relay them // If incoming user sets these headers, do not relay them
headers.removeAll("X-WEBAUTH-USER"); headers.removeAll("x-webauth-user");
headers.removeAll("X-WEBAUTH-NAME"); headers.removeAll("x-webauth-name");
TimelyPrincipal principal = token.getTimelyPrincipal(); TimelyPrincipal principal = token.getTimelyPrincipal();
TimelyUser user = principal.getPrimaryUser(); TimelyUser user = principal.getPrimaryUser();
String subjectDn = user.getDn().subjectDN(); String subjectDn = user.getDn().subjectDN();
String userName = DnUtils.getCommonName(subjectDn); String userName = DnUtils.getCommonName(subjectDn);
String loginName = DnUtils.getShortName(subjectDn); String loginName = DnUtils.getShortName(subjectDn);
headers.put("X-WEBAUTH-USER", loginName); headers.put("x-webauth-user", loginName);
headers.put("X-WEBAUTH-NAME", userName); headers.put("x-webauth-name", userName);
List<Header> relayedHeaderList = new ArrayList<>(); List<Header> relayedHeaderList = new ArrayList<>();
for (Map.Entry<String, String> h : headers.entries()) { for (Map.Entry<String, String> h : headers.entries()) {
if (!h.getKey().equals(CONTENT_LENGTH)) { if (!h.getKey().equals(CONTENT_LENGTH.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue())); relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
} }
} }
Header[] headerArray = new Header[relayedHeaderList.size()]; Header[] headerArray = new Header[relayedHeaderList.size()];
relayedHeaderList.toArray(headerArray); relayedHeaderList.toArray(headerArray);
relayURI = protocol + "://" + host + ":" + port + originalURI; relayURI = protocol + "://" + host + ":" + port + originalURI;
if (request.getMethod().equals(HttpMethod.HEAD)) { if (request.method().equals(HttpMethod.HEAD)) {
HttpHead relayedRequest = new HttpHead(relayURI); HttpHead relayedRequest = new HttpHead(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.GET)) { } else if (request.method().equals(HttpMethod.GET)) {
HttpGet relayedRequest = new HttpGet(relayURI); HttpGet relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.POST)) { } else if (request.method().equals(HttpMethod.POST)) {
HttpPost relayedRequest = new HttpPost(relayURI); HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8); String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content); StringEntity entity = new StringEntity(content);
@@ -131,7 +131,7 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.PUT)) { } else if (request.method().equals(HttpMethod.PUT)) {
HttpPut relayedRequest = new HttpPut(relayURI); HttpPut relayedRequest = new HttpPut(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8); String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content); StringEntity entity = new StringEntity(content);
@@ -140,13 +140,13 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.DELETE)) { } else if (request.method().equals(HttpMethod.DELETE)) {
HttpDelete relayedRequest = new HttpDelete(relayURI); HttpDelete relayedRequest = new HttpDelete(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
} }
relayedResponse = client.execute(relayedRequest); relayedResponse = client.execute(relayedRequest);
} else if (request.getMethod().equals(HttpMethod.OPTIONS)) { } else if (request.method().equals(HttpMethod.OPTIONS)) {
HttpOptions relayedRequest = new HttpOptions(relayURI); HttpOptions relayedRequest = new HttpOptions(relayURI);
if (headerArray.length > 0) { if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray); relayedRequest.setHeaders(headerArray);
@@ -199,7 +199,7 @@ public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest
if (client != null && k != null) { if (client != null && k != null) {
httpClientPool.returnObject(k, client); httpClientPool.returnObject(k, client);
} else { } else {
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().getUri()); LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().uri());
} }
} }
} }
@@ -8,7 +8,7 @@ import com.google.common.collect.Multimap;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringDecoder;
import io.netty.handler.codec.http.cookie.ServerCookieDecoder; import io.netty.handler.codec.http.cookie.ServerCookieDecoder;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
@@ -47,7 +47,7 @@ public class GrafanaRequestDecoder extends MessageToMessageDecoder<FullHttpReque
public static String getSessionId(FullHttpRequest msg) { public static String getSessionId(FullHttpRequest msg) {
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers()); Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers());
Collection<String> cookies = headers.get(Names.COOKIE); Collection<String> cookies = headers.get(HttpHeaderNames.COOKIE.toString());
final StringBuilder buf = new StringBuilder(); final StringBuilder buf = new StringBuilder();
cookies.forEach(h -> { cookies.forEach(h -> {
ServerCookieDecoder.STRICT.decode(h).forEach(c -> { ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
@@ -70,7 +70,7 @@ public class GrafanaRequestDecoder extends MessageToMessageDecoder<FullHttpReque
LOG.trace(LOG_RECEIVED_REQUEST, msg); LOG.trace(LOG_RECEIVED_REQUEST, msg);
final String uri = msg.getUri(); final String uri = msg.uri();
final QueryStringDecoder decoder = new QueryStringDecoder(uri); final QueryStringDecoder decoder = new QueryStringDecoder(uri);
if (decoder.path().equals(nonSecureRedirectAddress)) { if (decoder.path().equals(nonSecureRedirectAddress)) {
out.add(new StrictTransportResponse()); out.add(new StrictTransportResponse());
+1 -1
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-app</artifactId> <artifactId>timely-app</artifactId>
<packaging>pom</packaging> <packaging>pom</packaging>
+1 -1
View File
@@ -509,7 +509,7 @@ System.register(['lodash', 'angular', '../../../app/core/utils/datemath'], funct
} else { } else {
return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) { return target.metric === metricData.metric && _.every(target.tags, function (tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe'); interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*"; return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this)); }.bind(this));
} }
}.bind(this)); }.bind(this));
+1 -1
View File
@@ -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","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 {"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
+2 -2
View File
@@ -23,8 +23,8 @@
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"}, {"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"} {"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
], ],
"version": "1.2.3", "version": "1.2.8-SNAPSHOT",
"updated": "2019-04-18" "updated": "2020-02-21"
}, },
"includes": [ "includes": [
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "timely-app", "name": "timely-app",
"private": true, "private": true,
"version": "1.2.3", "version": "1.2.8-SNAPSHOT",
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"scripts": { "scripts": {
+2 -2
View File
@@ -23,8 +23,8 @@
{"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"}, {"name": "Project site", "url": "https://github.com/NationalSecurityAgency/timely"},
{"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"} {"name": "License & Terms", "url": "https://raw.githubusercontent.com/NationalSecurityAgency/timely/master/LICENSE"}
], ],
"version": "1.2.3", "version": "1.2.8-SNAPSHOT",
"updated": "2019-04-18" "updated": "2020-02-21"
}, },
"includes": [ "includes": [
@@ -446,7 +446,7 @@ export class TimelyDatasource {
} else { } else {
return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) { return target.metric === metricData.metric && _.every(target.tags, function(tagV, tagK) {
interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe'); interpolatedTagValue = this.templateSrv.replace(tagV, options.scopedVars, 'pipe');
return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === "*"; return metricData.tags[tagK] === interpolatedTagValue || interpolatedTagValue === ".*";
}.bind(this)); }.bind(this));
} }
}.bind(this)); }.bind(this));
+47 -22
View File
@@ -3,7 +3,7 @@
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
<packaging>pom</packaging> <packaging>pom</packaging>
<name>Timely</name> <name>Timely</name>
<description>A secure and scalable time series database based on Apache Accumulo</description> <description>A secure and scalable time series database based on Apache Accumulo</description>
@@ -41,7 +41,7 @@
<os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes> <os.detection.classifierWithLikes>fedora</os.detection.classifierWithLikes>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<version.accumulo>1.9.2</version.accumulo> <version.accumulo>1.9.3</version.accumulo>
<version.buildnumber-maven>1.4</version.buildnumber-maven> <version.buildnumber-maven>1.4</version.buildnumber-maven>
<version.caffeine>2.6.2</version.caffeine> <version.caffeine>2.6.2</version.caffeine>
<version.collectd>5.7.1</version.collectd> <version.collectd>5.7.1</version.collectd>
@@ -50,37 +50,39 @@
<version.commons-pool2>2.6.1</version.commons-pool2> <version.commons-pool2>2.6.1</version.commons-pool2>
<version.curator>4.1.0</version.curator> <version.curator>4.1.0</version.curator>
<version.disruptor>3.4.2</version.disruptor> <version.disruptor>3.4.2</version.disruptor>
<version.easymock>4.0.2</version.easymock>
<version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle> <version.eclipse-lifecycle>1.0.0</version.eclipse-lifecycle>
<version.fast-classpath-scanner>3.1.13</version.fast-classpath-scanner> <version.fast-classpath-scanner>3.1.13</version.fast-classpath-scanner>
<version.flink>1.1.4</version.flink> <version.flink>1.1.4</version.flink>
<version.google-flatbuffers>1.10.0</version.google-flatbuffers> <version.google-flatbuffers>1.10.0</version.google-flatbuffers>
<version.guava>27.1-jre</version.guava> <version.guava>27.1-jre</version.guava>
<version.hadoop>2.7.3</version.hadoop> <version.hadoop>2.7.7</version.hadoop>
<version.httpcomponents>4.5.6</version.httpcomponents> <version.httpcomponents>4.5.6</version.httpcomponents>
<version.impsort>1.2.0</version.impsort> <version.impsort>1.3.0</version.impsort>
<version.jackson>2.9.8</version.jackson> <version.jackson>2.10.0</version.jackson>
<version.jacoco>0.7.6.201602180812</version.jacoco> <version.jackson.databind>2.10.0</version.jackson.databind>
<version.jacoco>0.8.4</version.jacoco>
<version.javacsv>2.0</version.javacsv> <version.javacsv>2.0</version.javacsv>
<version.javassist>3.24.1-GA</version.javassist> <version.javassist>3.24.1-GA</version.javassist>
<version.jcip-annotations>1.0</version.jcip-annotations> <version.jcip-annotations>1.0</version.jcip-annotations>
<version.jsoup>1.11.3</version.jsoup> <version.jsoup>1.11.3</version.jsoup>
<version.junit>4.12</version.junit> <version.junit>4.12</version.junit>
<version.log4j>2.11.2</version.log4j> <version.log4j>2.11.2</version.log4j>
<version.maven-assembly>2.6</version.maven-assembly> <version.maven-assembly>3.1.1</version.maven-assembly>
<version.maven-build-helper>1.12</version.maven-build-helper> <version.maven-build-helper>3.0.0</version.maven-build-helper>
<version.maven-compiler>3.3</version.maven-compiler> <version.maven-compiler>3.8.1</version.maven-compiler>
<version.maven-dependency>2.10</version.maven-dependency> <version.maven-dependency>3.1.1</version.maven-dependency>
<version.maven-jar>3.0.1</version.maven-jar> <version.maven-jar>3.1.2</version.maven-jar>
<version.maven-pmd>3.6</version.maven-pmd> <version.maven-pmd>3.12.0</version.maven-pmd>
<version.maven-rpm>2.1.4</version.maven-rpm> <version.maven-rpm>2.2.0</version.maven-rpm>
<version.maven-shade>2.4.2</version.maven-shade> <version.maven-shade>3.2.1</version.maven-shade>
<version.maven-surefire>2.19.1</version.maven-surefire> <version.maven-surefire>3.0.0-M3</version.maven-surefire>
<version.netty>4.1.35.Final</version.netty> <version.netty>4.1.42.Final</version.netty>
<version.netty-tcnative>2.0.25.Final</version.netty-tcnative> <version.netty-tcnative>2.0.25.Final</version.netty-tcnative>
<version.os-maven>1.5.0.Final</version.os-maven> <version.os-maven>1.5.0.Final</version.os-maven>
<version.powermock>1.6.4</version.powermock> <version.powermock>1.6.4</version.powermock>
<version.slf4j>1.7.26</version.slf4j> <version.slf4j>1.7.26</version.slf4j>
<version.spotbugs-maven>3.1.7</version.spotbugs-maven> <version.spotbugs-maven>3.1.12.1</version.spotbugs-maven>
<version.spring-boot>2.1.3.RELEASE</version.spring-boot> <version.spring-boot>2.1.3.RELEASE</version.spring-boot>
<version.tyrus>1.15</version.tyrus> <version.tyrus>1.15</version.tyrus>
</properties> </properties>
@@ -99,7 +101,7 @@
<dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId> <groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId> <artifactId>jackson-databind</artifactId>
<version>${version.jackson}</version> <version>${version.jackson.databind}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.fasterxml.jackson.datatype</groupId> <groupId>com.fasterxml.jackson.datatype</groupId>
@@ -146,6 +148,11 @@
<artifactId>netty-all</artifactId> <artifactId>netty-all</artifactId>
<version>${version.netty}</version> <version>${version.netty}</version>
</dependency> </dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
</dependency>
<dependency> <dependency>
<groupId>net.sourceforge.javacsv</groupId> <groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId> <artifactId>javacsv</artifactId>
@@ -391,6 +398,12 @@
<version>${version.jcip-annotations}</version> <version>${version.jcip-annotations}</version>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>${version.easymock}</version>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.powermock</groupId> <groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId> <artifactId>powermock-easymock-release-full</artifactId>
@@ -406,7 +419,7 @@
<plugin> <plugin>
<groupId>net.revelc.code.formatter</groupId> <groupId>net.revelc.code.formatter</groupId>
<artifactId>formatter-maven-plugin</artifactId> <artifactId>formatter-maven-plugin</artifactId>
<version>2.8.0</version> <version>2.10.0</version>
<configuration> <configuration>
<compilerCompliance>${maven.compiler.source}</compilerCompliance> <compilerCompliance>${maven.compiler.source}</compilerCompliance>
<compilerSource>${maven.compiler.source}</compilerSource> <compilerSource>${maven.compiler.source}</compilerSource>
@@ -424,7 +437,7 @@
<plugin> <plugin>
<groupId>com.github.ekryd.sortpom</groupId> <groupId>com.github.ekryd.sortpom</groupId>
<artifactId>sortpom-maven-plugin</artifactId> <artifactId>sortpom-maven-plugin</artifactId>
<version>2.8.0</version> <version>2.10.0</version>
<configuration> <configuration>
<predefinedSortOrder>recommended_2008_06</predefinedSortOrder> <predefinedSortOrder>recommended_2008_06</predefinedSortOrder>
<createBackupFile>false</createBackupFile> <createBackupFile>false</createBackupFile>
@@ -456,7 +469,7 @@
<artifactId>maven-failsafe-plugin</artifactId> <artifactId>maven-failsafe-plugin</artifactId>
<version>${version.maven-surefire}</version> <version>${version.maven-surefire}</version>
<configuration> <configuration>
<argLine>${failsafeArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty-tcnative_linux_x86_64.so -Dzookeeper.sasl.client=false</argLine> <argLine>${failsafeArgLine} -Djava.library.path=${project.build.directory}/native/META-INF/native/libnetty_tcnative_linux_x86_64.so -Dzookeeper.sasl.client=false</argLine>
<includes> <includes>
<include>**/*.java</include> <include>**/*.java</include>
</includes> </includes>
@@ -568,7 +581,7 @@
<configuration> <configuration>
<rules> <rules>
<requireMavenVersion> <requireMavenVersion>
<version>3.0.5</version> <version>3.5.0</version>
</requireMavenVersion> </requireMavenVersion>
<requireJavaVersion> <requireJavaVersion>
<version>[${maven.compiler.target},)</version> <version>[${maven.compiler.target},)</version>
@@ -739,6 +752,9 @@
<name>m2e.version</name> <name>m2e.version</name>
</property> </property>
</activation> </activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
<build> <build>
<pluginManagement> <pluginManagement>
<plugins> <plugins>
@@ -799,5 +815,14 @@
</pluginManagement> </pluginManagement>
</build> </build>
</profile> </profile>
<profile>
<id>jdk-release-flag</id>
<activation>
<jdk>[9,)</jdk>
</activation>
<properties>
<maven.compiler.release>8</maven.compiler.release>
</properties>
</profile>
</profiles> </profiles>
</project> </project>
+21
View File
@@ -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}/..
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*"
JVM_ARGS="-Xmx1024m -Xms128m"
JVM_ARGS="${JVM_ARGS} -Dlogging.config=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-error-console.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.store.compaction.util.TabletMetadataConsole --spring.config.name=timely
+1 -1
View File
@@ -32,7 +32,7 @@ $JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transpo
popd popd
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}" export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}" -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector" JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}" JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
+1 -1
View File
@@ -32,7 +32,7 @@ $JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transpo
popd popd
export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}" export CLASSPATH="${CONF_DIR}:${LIB_DIR}/*:${HADOOP_CONF_DIR}"
JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS}" -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml JVM_ARGS="-Xmx256m -Xms256m -Dio.netty.eventLoopThreads=${NUM_SERVER_THREADS} -Dlog4j.configurationFile=${CONF_DIR}/log4j2-spring.xml"
JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector" JVM_ARGS="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}" JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
# for debugging # for debugging
+1 -1
View File
@@ -22,7 +22,7 @@ timely:
cache-refresh-minutes: 5 cache-refresh-minutes: 5
allow-anonymous-http-access: true allow-anonymous-http-access: true
allow-anonymous-ws-access: true allow-anonymous-ws-access: true
ssl: server-ssl:
certificate-file: /timely-server-src/server/pki/timely.csr certificate-file: /timely-server-src/server/pki/timely.csr
key-file: /timely-server-src/server/pki/timely-pkcs8.key key-file: /timely-server-src/server/pki/timely-pkcs8.key
key-password: password key-password: password
+3 -1
View File
@@ -3,6 +3,8 @@ timely:
meta-table: timely.meta meta-table: timely.meta
metric-age-off-days: metric-age-off-days:
default: 7 default: 7
cache:
enabled: false
metrics-report-ignored-tags: metrics-report-ignored-tags:
accumulo: accumulo:
instance-name: TimelyStandalone instance-name: TimelyStandalone
@@ -22,7 +24,7 @@ timely:
cache-refresh-minutes: 5 cache-refresh-minutes: 5
allow-anonymous-http-access: true allow-anonymous-http-access: true
allow-anonymous-ws-access: true allow-anonymous-ws-access: true
ssl: server-ssl:
certificate-file: certificate-file:
key-file: key-file:
key-password: timely key-password: timely
+1 -1
View File
@@ -27,7 +27,7 @@ timely:
cache-refresh-minutes: 5 cache-refresh-minutes: 5
allow-anonymous-http-access: true allow-anonymous-http-access: true
allow-anonymous-ws-access: true allow-anonymous-ws-access: true
serverSsl: server-ssl:
certificate-file: certificate-file:
key-file: key-file:
key-password: key-password:
+16 -2
View File
@@ -4,7 +4,7 @@
<parent> <parent>
<groupId>timely</groupId> <groupId>timely</groupId>
<artifactId>timely</artifactId> <artifactId>timely</artifactId>
<version>1.2.3</version> <version>1.2.8-SNAPSHOT</version>
</parent> </parent>
<artifactId>timely-server</artifactId> <artifactId>timely-server</artifactId>
<dependencies> <dependencies>
@@ -108,6 +108,10 @@
<groupId>org.apache.hadoop</groupId> <groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId> <artifactId>hadoop-minicluster</artifactId>
</dependency> </dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>
@@ -150,7 +154,6 @@
<dependency> <dependency>
<groupId>io.netty</groupId> <groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId> <artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
<scope>runtime</scope> <scope>runtime</scope>
</dependency> </dependency>
<dependency> <dependency>
@@ -178,11 +181,22 @@
<artifactId>junit</artifactId> <artifactId>junit</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.jsoup</groupId> <groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId> <artifactId>jsoup</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.powermock</groupId>
<artifactId>powermock-easymock-release-full</artifactId>
<classifier>full</classifier>
<scope>test</scope>
</dependency>
<dependency> <dependency>
<groupId>org.springframework.boot</groupId> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId> <artifactId>spring-boot-test-autoconfigure</artifactId>
+88 -44
View File
@@ -24,6 +24,7 @@ import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption; import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup; import io.netty.channel.EventLoopGroup;
import io.netty.channel.WriteBufferWaterMark;
import io.netty.channel.epoll.EpollChannelOption; import io.netty.channel.epoll.EpollChannelOption;
import io.netty.channel.epoll.EpollDatagramChannel; import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup; import io.netty.channel.epoll.EpollEventLoopGroup;
@@ -44,6 +45,7 @@ import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.cors.CorsConfig; import io.netty.handler.codec.http.cors.CorsConfig;
import io.netty.handler.codec.http.cors.CorsConfigBuilder;
import io.netty.handler.codec.http.cors.CorsHandler; import io.netty.handler.codec.http.cors.CorsHandler;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler; import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.logging.LoggingHandler; import io.netty.handler.logging.LoggingHandler;
@@ -58,6 +60,7 @@ import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler; import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime; import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy; import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework; import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory; import org.apache.curator.framework.CuratorFrameworkFactory;
@@ -73,7 +76,6 @@ import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType; import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder; import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import timely.api.response.TimelyException;
import timely.auth.AuthCache; import timely.auth.AuthCache;
import timely.auth.VisibilityCache; import timely.auth.VisibilityCache;
import timely.configuration.Configuration; import timely.configuration.Configuration;
@@ -209,13 +211,13 @@ public class Server {
payload.setWsPort(config.getWebsocket().getPort()); payload.setWsPort(config.getWebsocket().getPort());
payload.setUdpPort(config.getServer().getUdpPort()); payload.setUdpPort(config.getServer().getUdpPort());
ServiceInstanceBuilder builder = ServiceInstance.builder(); ServiceInstanceBuilder<ServerDetails> builder = ServiceInstance.builder();
String serviceName = host + ":" + config.getServer().getTcpPort(); String serviceName = host + ":" + config.getServer().getTcpPort();
ServiceInstance serviceInstance = builder.id(serviceName).name("timely-server") ServiceInstance<ServerDetails> serviceInstance = builder.id(serviceName).name("timely-server")
.address(config.getServer().getIp()).port(config.getServer().getTcpPort()).payload(payload).build(); .address(config.getServer().getIp()).port(config.getServer().getTcpPort()).payload(payload).build();
ServiceDiscovery discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class).client(curatorFramework) ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.basePath(SERVICE_DISCOVERY_PATH).build(); .client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start(); discovery.start();
discovery.registerService(serviceInstance); discovery.registerService(serviceInstance);
@@ -231,11 +233,12 @@ public class Server {
public static void main(String[] args) throws Exception { public static void main(String[] args) throws Exception {
Configuration conf = initializeConfiguration(args); Server.applicationContext = initializeContext(args);
Configuration conf = Server.applicationContext.getBean(Configuration.class);
Server s = new Server(conf); Server server = new Server(conf);
try { try {
s.run(); server.run();
LATCH.await(); LATCH.await();
} catch (final InterruptedException e) { } catch (final InterruptedException e) {
LOG.info("Server shutting down."); LOG.info("Server shutting down.");
@@ -243,16 +246,18 @@ public class Server {
LOG.error("Error running server.", e); LOG.error("Error running server.", e);
} finally { } finally {
try { try {
s.shutdown(); server.shutdown();
} catch (Exception e) { } catch (Exception e) {
System.exit(1); System.exit(1);
} }
} }
} }
protected static Configuration initializeConfiguration(String[] args) { protected static ConfigurableApplicationContext initializeContext(String[] args) {
applicationContext = new SpringApplicationBuilder(SpringBootstrap.class).web(WebApplicationType.NONE).run(args); SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
return applicationContext.getBean(Configuration.class); builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
} }
private void shutdownHook() { private void shutdownHook() {
@@ -267,6 +272,8 @@ public class Server {
} }
public void shutdown() { public void shutdown() {
LOG.warn("Server shutdown 1");
List<ChannelFuture> channelFutures = new ArrayList<>(); List<ChannelFuture> channelFutures = new ArrayList<>();
if (tcpChannelHandle != null) { if (tcpChannelHandle != null) {
@@ -284,8 +291,9 @@ public class Server {
channelFutures.add(wsChannelHandle.close()); channelFutures.add(wsChannelHandle.close());
} }
int udpChannel = 1;
for (Channel c : udpChannelHandleList) { for (Channel c : udpChannelHandleList) {
LOG.info("Closing udpChannelHandle"); LOG.info("Closing udpChannelHandle #" + udpChannel++);
channelFutures.add(c.close()); channelFutures.add(c.close());
} }
@@ -294,14 +302,15 @@ public class Server {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down channel: {}", f.channel().config()); LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
Integer quietPeriod = config.getServer().getShutdownQuietPeriod(); LOG.warn("Server shutdown 2");
int quietPeriod = config.getServer().getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>(); List<Future<?>> groupFutures = new ArrayList<>();
if (tcpBossGroup != null) { if (tcpBossGroup != null) {
LOG.info("Shutting down tcpBossGroup"); LOG.info("Shutting down tcpBossGroup");
groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(tcpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -337,6 +346,8 @@ public class Server {
groupFutures.add(udpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(udpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
} }
LOG.warn("Server shutdown 3");
if (udpWorkerGroup != null) { if (udpWorkerGroup != null) {
LOG.info("Shutting down udpWorkerGroup"); LOG.info("Shutting down udpWorkerGroup");
groupFutures.add(udpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS)); groupFutures.add(udpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
@@ -346,41 +357,70 @@ public class Server {
try { try {
f.get(); f.get();
} catch (final Exception e) { } catch (final Exception e) {
LOG.error("Error while shutting down group: {}", f.toString()); LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
LOG.error("{}", e.getMessage());
e.printStackTrace();
} }
}); });
try { LOG.warn("Server shutdown 4");
LOG.info("Flushing datastore.");
dataStore.flush();
} catch (TimelyException e) {
LOG.error("Error flushing to server during shutdown", e);
}
try { try {
LOG.info("Closing MetaCacheFactory"); LOG.info("Closing dataStore");
dataStore.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
LOG.warn("Server shutdown 5");
if (dataStoreCache != null) {
try {
LOG.info("Closing dataStoreCache");
dataStoreCache.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}
LOG.warn("Server shutdown 6");
try {
LOG.info("Closing metaCacheFactory");
MetaCacheFactory.close(); MetaCacheFactory.close();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error closing MetaCacheFactory during shutdown", e); LOG.error(e.getMessage(), e);
} }
LOG.warn("Server shutdown 7");
try { try {
LOG.info("Closing WebSocketRequestDecoder"); LOG.info("Closing webSocketRequestDecoder subscriptions");
WebSocketRequestDecoder.close(); WebSocketRequestDecoder.close();
} catch (Exception e) { } catch (Exception e) {
LOG.error("Error closing WebSocketRequestDecoder during shutdown", e); LOG.error(e.getMessage(), e);
} }
LOG.warn("Server shutdown 8");
if (curatorFramework != null) { if (curatorFramework != null) {
curatorFramework.close(); try {
LOG.info("Closing curatorFramework");
curatorFramework.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
LOG.warn("Server shutdown 9");
if (applicationContext != null) { if (applicationContext != null) {
LOG.info("Closing applicationContext"); try {
applicationContext.close(); LOG.info("Closing applicationContext");
applicationContext.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
} }
LOG.warn("Server shutdown 10");
this.shutdown = true; this.shutdown = true;
LOG.info("Server shut down."); LOG.info("Server shut down.");
} }
@@ -415,7 +455,9 @@ public class Server {
public void run() throws Exception { public void run() throws Exception {
RetryPolicy retryPolicy = new RetryForever(1000); RetryPolicy retryPolicy = new RetryForever(1000);
curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(), 30000, 10000, int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(config.getAccumulo().getZookeeperTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(config.getAccumulo().getZookeepers(), timeout, 10000,
retryPolicy); retryPolicy);
curatorFramework.start(); curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths); ensureZkPaths(curatorFramework, zkPaths);
@@ -508,10 +550,9 @@ public class Server {
wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT); wsServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
wsServer.option(ChannelOption.SO_BACKLOG, 128); wsServer.option(ChannelOption.SO_BACKLOG, 128);
wsServer.option(ChannelOption.SO_KEEPALIVE, true); wsServer.option(ChannelOption.SO_KEEPALIVE, true);
/* Not sure if next three lines are necessary */ /* Not sure if next two lines are necessary */
wsServer.option(ChannelOption.SO_SNDBUF, 1048576); wsServer.option(ChannelOption.SO_SNDBUF, 1048576);
wsServer.option(ChannelOption.WRITE_BUFFER_HIGH_WATER_MARK, 838860); wsServer.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(620145, 838860));
wsServer.option(ChannelOption.WRITE_BUFFER_LOW_WATER_MARK, 620145);
wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel(); wsChannelHandle = wsServer.bind(wsIp, wsPort).sync().channel();
final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress(); final String wsAddress = ((InetSocketAddress) wsChannelHandle.localAddress()).getAddress().getHostAddress();
@@ -543,7 +584,7 @@ public class Server {
if (generate) { if (generate) {
LOG.warn("Using generated self signed server certificate"); LOG.warn("Using generated self signed server certificate");
Date begin = new Date(); Date begin = new Date();
Date end = new Date(begin.getTime() + 86400000); Date end = new Date(begin.getTime() + TimeUnit.DAYS.toMillis(7));
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end); SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()); ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else { } else {
@@ -591,11 +632,11 @@ public class Server {
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536)); ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("chunker", new ChunkedWriteHandler()); ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
final Cors corsCfg = config.getHttp().getCors(); final Cors corsCfg = config.getHttp().getCors();
final CorsConfig.Builder ccb; final CorsConfigBuilder ccb;
if (corsCfg.isAllowAnyOrigin()) { if (corsCfg.isAllowAnyOrigin()) {
ccb = new CorsConfig.Builder(); ccb = CorsConfigBuilder.forAnyOrigin();
} else { } else {
ccb = new CorsConfig.Builder(corsCfg.getAllowedOrigins().stream().toArray(String[]::new)); ccb = CorsConfigBuilder.forOrigins(corsCfg.getAllowedOrigins().stream().toArray(String[]::new));
} }
if (corsCfg.isAllowNullOrigin()) { if (corsCfg.isAllowNullOrigin()) {
ccb.allowNullOrigin(); ccb.allowNullOrigin();
@@ -610,7 +651,8 @@ public class Server {
ch.pipeline().addLast("cors", new CorsHandler(cors)); ch.pipeline().addLast("cors", new CorsHandler(cors));
ch.pipeline().addLast("queryDecoder", ch.pipeline().addLast("queryDecoder",
new timely.netty.http.HttpRequestDecoder(config.getSecurity(), config.getHttp())); new timely.netty.http.HttpRequestDecoder(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()); ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("strict", new StrictTransportHandler(config)); ch.pipeline().addLast("strict", new StrictTransportHandler(config));
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp())); ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("doLogin", ch.pipeline().addLast("doLogin",
@@ -623,7 +665,9 @@ public class Server {
ch.pipeline().addLast("version", new HttpVersionRequestHandler()); ch.pipeline().addLast("version", new HttpVersionRequestHandler());
ch.pipeline().addLast("cache", new HttpCacheRequestHandler(dataStoreCache)); ch.pipeline().addLast("cache", new HttpCacheRequestHandler(dataStoreCache));
ch.pipeline().addLast("put", new HttpMetricPutHandler(dataStore)); ch.pipeline().addLast("put", new HttpMetricPutHandler(dataStore));
ch.pipeline().addLast("error", new TimelyExceptionHandler()); ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
} }
}; };
} }
@@ -668,7 +712,7 @@ public class Server {
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler()); ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0)); ch.pipeline().addLast("idle-handler", new IdleStateHandler(conf.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol", ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, true)); new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config.getSecurity())); ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(config.getSecurity()));
ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler()); ch.pipeline().addLast("aggregators", new WSAggregatorsRequestHandler());
ch.pipeline().addLast("metrics", new WSMetricsRequestHandler(config)); ch.pipeline().addLast("metrics", new WSMetricsRequestHandler(config));
@@ -52,7 +52,8 @@ public class StandaloneServer extends Server {
System.exit(1); System.exit(1);
} }
Configuration conf = initializeConfiguration(args); StandaloneServer.applicationContext = initializeContext(args);
Configuration conf = StandaloneServer.applicationContext.getBean(Configuration.class);
File accumuloDir = new File(tmp, "accumulo"); File accumuloDir = new File(tmp, "accumulo");
MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret"); MiniAccumuloConfig macConfig = new MiniAccumuloConfig(accumuloDir, "secret");
@@ -0,0 +1,39 @@
package timely.adapter.accumulo;
import org.apache.accumulo.core.data.Key;
import timely.api.model.Meta;
import timely.model.Metric;
/**
* Creates Accumulo data structures from {@link Metric}
*/
public class MetaAdapter {
public static Key createMetricKey(String metricName, Long timestamp) {
return new Key(Meta.METRIC_PREFIX + metricName, "", "", timestamp);
}
public static Key createTagKey(String metricName, String tagName, Long timestamp) {
return new Key(Meta.TAG_PREFIX + metricName, tagName, "", timestamp);
}
public static Key createValueKey(String metricName, String tagName, String tagValue, Long timestamp) {
return new Key(Meta.VALUE_PREFIX + metricName, tagName, tagValue, timestamp);
}
public static Meta parse(Key key) {
String row = key.getRow().toString();
if (row.startsWith(Meta.METRIC_PREFIX)) {
return Meta.parse(key, null, Meta.METRIC_PREFIX);
} else if (row.startsWith(Meta.TAG_PREFIX)) {
return Meta.parse(key, null, Meta.TAG_PREFIX);
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
return Meta.parse(key, null, Meta.VALUE_PREFIX);
}
throw new IllegalStateException("Invalid key in meta " + key.toString());
}
public static String decodeRowKey(Key key) {
return parse(key).getMetric();
}
}
+59 -44
View File
@@ -5,6 +5,10 @@ import java.util.List;
import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Key;
import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.data.Value;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
public class Meta implements Comparable<Meta> { public class Meta implements Comparable<Meta> {
@@ -49,14 +53,44 @@ public class Meta implements Comparable<Meta> {
public List<Key> toKeys() { public List<Key> toKeys() {
List<Key> keys = new ArrayList<>(); List<Key> keys = new ArrayList<>();
keys.add(new Key(METRIC_PREFIX + metric)); if (StringUtils.isNotBlank(metric)) {
keys.add(new Key(TAG_PREFIX + metric, tagKey)); keys.add(new Key(METRIC_PREFIX + metric));
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue)); }
if (StringUtils.isNotBlank(tagKey)) {
keys.add(new Key(TAG_PREFIX + metric, tagKey));
}
if (StringUtils.isNotBlank(tagKey) && StringUtils.isNotBlank(tagValue)) {
keys.add(new Key(VALUE_PREFIX + metric, tagKey, tagValue));
}
return keys; return keys;
} }
public static Meta parse(Key k, Value v) { public static String prefix(Key key) {
return Meta.parse(k, v, METRIC_PREFIX); String row = key.getRow().toString();
if (row.startsWith(Meta.METRIC_PREFIX)) {
return Meta.METRIC_PREFIX;
} else if (row.startsWith(Meta.TAG_PREFIX)) {
return Meta.TAG_PREFIX;
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
return Meta.VALUE_PREFIX;
}
throw new IllegalStateException("Invalid key in meta " + key.toString());
}
public static Meta parse(Key key) {
return parse(key, null);
}
public static Meta parse(Key key, Value value) {
String row = key.getRow().toString();
if (row.startsWith(Meta.METRIC_PREFIX)) {
return Meta.parse(key, value, Meta.METRIC_PREFIX);
} else if (row.startsWith(Meta.TAG_PREFIX)) {
return Meta.parse(key, value, Meta.TAG_PREFIX);
} else if (row.startsWith(Meta.VALUE_PREFIX)) {
return Meta.parse(key, value, Meta.VALUE_PREFIX);
}
throw new IllegalStateException("Invalid key in meta " + key.toString());
} }
public static Meta parse(Key k, Value v, String prefix) { public static Meta parse(Key k, Value v, String prefix) {
@@ -73,39 +107,25 @@ public class Meta implements Comparable<Meta> {
@Override @Override
public int hashCode() { public int hashCode() {
final int prime = 31; HashCodeBuilder builder = new HashCodeBuilder();
int result = 1; builder.append(metric);
result = prime * result + ((metric == null) ? 0 : metric.hashCode()); builder.append(tagKey);
result = prime * result + ((tagKey == null) ? 0 : tagKey.hashCode()); builder.append(tagValue);
result = prime * result + ((tagValue == null) ? 0 : tagValue.hashCode()); return builder.toHashCode();
return result;
} }
@Override @Override
public boolean equals(Object obj) { public boolean equals(Object o) {
if (this == obj) if (o instanceof Meta) {
return true; Meta obj = (Meta) o;
if (obj == null) EqualsBuilder builder = new EqualsBuilder();
builder.append(this.metric, obj.metric);
builder.append(this.tagKey, obj.tagKey);
builder.append(this.tagValue, obj.tagValue);
return builder.isEquals();
} else {
return false; return false;
if (getClass() != obj.getClass()) }
return false;
Meta other = (Meta) obj;
if (metric == null) {
if (other.metric != null)
return false;
} else if (!metric.equals(other.metric))
return false;
if (tagKey == null) {
if (other.tagKey != null)
return false;
} else if (!tagKey.equals(other.tagKey))
return false;
if (tagValue == null) {
if (other.tagValue != null)
return false;
} else if (!tagValue.equals(other.tagValue))
return false;
return true;
} }
@Override @Override
@@ -115,15 +135,10 @@ public class Meta implements Comparable<Meta> {
@Override @Override
public int compareTo(Meta o) { public int compareTo(Meta o) {
int result = metric.compareTo(o.metric); CompareToBuilder builder = new CompareToBuilder();
if (result != 0) { builder.append(this.metric, o.metric);
return result; builder.append(this.tagKey, o.tagKey);
} builder.append(this.tagValue, o.tagValue);
result = tagKey.compareTo(o.tagKey); return builder.toComparison();
if (result != 0) {
return result;
}
return tagValue.compareTo(o.tagValue);
} }
} }
@@ -4,8 +4,6 @@ public interface TcpRequest extends Request {
/** /**
* Parse a line of text received and populate this object * Parse a line of text received and populate this object
*
* @param line
*/ */
public void parse(String line); public void parse(String line);
@@ -4,8 +4,6 @@ public interface UdpRequest {
/** /**
* Parse a line of text received and populate this object * Parse a line of text received and populate this object
*
* @param line
*/ */
public void parse(String line); public void parse(String line);
@@ -18,7 +18,7 @@ import com.fasterxml.jackson.databind.node.ObjectNode;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame; import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
@@ -129,8 +129,8 @@ public class MetricsResponse {
} }
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf)); Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, responseType); response.headers().set(HttpHeaderNames.CONTENT_TYPE, responseType);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
return response; return response;
} }
@@ -156,8 +156,10 @@ public class MetricsResponse {
b.append(TR_END); b.append(TR_END);
String prevMetric = null; String prevMetric = null;
StringBuilder tags = new StringBuilder(); StringBuilder tags = new StringBuilder();
Map<String, Long> tagMap = new HashMap<>();
for (Meta m : tree) { for (Meta m : tree) {
if (prevMetric != null && !m.getMetric().equals(prevMetric)) { if (prevMetric != null && !m.getMetric().equals(prevMetric)) {
tagMap.clear();
b.append(TR_START); b.append(TR_START);
b.append(TD_START).append(prevMetric).append(TD_END); b.append(TD_START).append(prevMetric).append(TD_END);
b.append(TD_START).append(tags.toString()).append(TD_END); b.append(TD_START).append(tags.toString()).append(TD_END);
@@ -168,8 +170,12 @@ public class MetricsResponse {
if (prevMetric == null) { if (prevMetric == null) {
prevMetric = m.getMetric(); prevMetric = m.getMetric();
} }
long numValues = tagMap.getOrDefault(m.getTagKey(), 0l);
if (!(this.ignoredTags.contains(m.getTagKey()))) { if (!(this.ignoredTags.contains(m.getTagKey()))) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" "); if (numValues <= conf.getMetaCache().getMaxTagValues()) {
tags.append(m.getTagKey()).append("=").append(m.getTagValue()).append(" ");
tagMap.put(m.getTagKey(), ++numValues);
}
} }
} }
b.append(TR_START); b.append(TR_START);
@@ -43,9 +43,13 @@ public class AuthenticationService {
try { try {
springContext = new ClassPathXmlApplicationContext("security.xml"); springContext = new ClassPathXmlApplicationContext("security.xml");
authManager = springContext.getBean("authenticationManager", AuthenticationManager.class); authManager = springContext.getBean("authenticationManager", AuthenticationManager.class);
requiredRoles = springContext.getBean("requiredRoles", List.class); @SuppressWarnings("unchecked")
List<String> uncheckedRoles = springContext.getBean("requiredRoles", List.class);
requiredRoles = uncheckedRoles;
requiredRoles.removeIf(String::isEmpty); requiredRoles.removeIf(String::isEmpty);
requiredAuths = springContext.getBean("requiredAuths", List.class); @SuppressWarnings("unchecked")
List<String> uncheckedAuths = springContext.getBean("requiredAuths", List.class);
requiredAuths = uncheckedAuths;
requiredAuths.removeIf(String::isEmpty); requiredAuths.removeIf(String::isEmpty);
} catch (BeansException e) { } catch (BeansException e) {
throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(), throw new ServiceConfigurationError("Error setting up Authentication objects: " + e.getMessage(),
@@ -16,8 +16,6 @@ import org.apache.accumulo.core.data.Value;
import org.apache.accumulo.core.data.thrift.IterInfo; import org.apache.accumulo.core.data.thrift.IterInfo;
import org.apache.accumulo.core.security.Authorizations; import org.apache.accumulo.core.security.Authorizations;
import org.apache.hadoop.io.Text; import org.apache.hadoop.io.Text;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* A simple wrapper around a {@link ScannerBase} that overrides the methods that * A simple wrapper around a {@link ScannerBase} that overrides the methods that
@@ -27,7 +25,6 @@ import org.slf4j.LoggerFactory;
*/ */
public class ScannerBaseDelegate implements ScannerBase { public class ScannerBaseDelegate implements ScannerBase {
private static final Logger log = LoggerFactory.getLogger(ScannerBaseDelegate.class);
private static final String SYSTEM_ITERATOR_NAME_PREFIX = "sys_"; private static final String SYSTEM_ITERATOR_NAME_PREFIX = "sys_";
protected final ScannerBase delegate; protected final ScannerBase delegate;
@@ -139,10 +136,11 @@ public class ScannerBaseDelegate implements ScannerBase {
@Override @Override
public void clearScanIterators() { public void clearScanIterators() {
if (delegate instanceof ScannerOptions) { if (delegate instanceof ScannerOptions) {
ScannerOptionsHelper opts = new ScannerOptionsHelper((ScannerOptions) delegate); try (ScannerOptionsHelper opts = new ScannerOptionsHelper((ScannerOptions) delegate)) {
for (IteratorSetting iteratorSetting : opts.getIterators()) { for (IteratorSetting iteratorSetting : opts.getIterators()) {
if (!iteratorSetting.getName().startsWith(SYSTEM_ITERATOR_NAME_PREFIX)) { if (!iteratorSetting.getName().startsWith(SYSTEM_ITERATOR_NAME_PREFIX)) {
delegate.removeScanIterator(iteratorSetting.getName()); delegate.removeScanIterator(iteratorSetting.getName());
}
} }
} }
} else { } else {
@@ -13,6 +13,7 @@ import timely.auth.util.ProxiedEntityUtils;
*/ */
public class SubjectIssuerDNPair implements Serializable { public class SubjectIssuerDNPair implements Serializable {
private static final long serialVersionUID = 1L;
private final String subjectDN; private final String subjectDN;
private final String issuerDN; private final String issuerDN;
@@ -29,6 +29,7 @@ import timely.auth.util.DnUtils;
@XmlAccessorType(XmlAccessType.NONE) @XmlAccessorType(XmlAccessType.NONE)
public class TimelyPrincipal implements Principal, Serializable { public class TimelyPrincipal implements Principal, Serializable {
private static final long serialVersionUID = 1L;
private final String username; private final String username;
private final TimelyUser primaryUser; private final TimelyUser primaryUser;
@XmlElement @XmlElement
@@ -103,7 +104,7 @@ public class TimelyPrincipal implements Principal, Serializable {
public Set<String> getProxyServers() { public Set<String> getProxyServers() {
// @formatter:off // @formatter:off
Set<String> proxyServers = getProxiedUsers().stream() Set<String> proxyServers = getProxiedUsers().stream()
.filter(u -> u.getUserType() == TimelyUser.UserType.SERVER) .filter(u -> u.getUserType() == TimelyUser.UserType.SERVER)
.map(TimelyUser::getDn) .map(TimelyUser::getDn)
@@ -20,6 +20,8 @@ import timely.auth.util.DnUtils;
*/ */
public class TimelyUser implements Serializable { public class TimelyUser implements Serializable {
private static final long serialVersionUID = 1L;
public enum UserType { public enum UserType {
USER, SERVER USER, SERVER
} }
@@ -13,7 +13,9 @@ public class HttpHeaderUtils {
Multimap<String, String> headerMultimap = HashMultimap.create(); Multimap<String, String> headerMultimap = HashMultimap.create();
if (headers != null) { if (headers != null) {
for (Map.Entry<String, String> e : headers.entries()) { for (Map.Entry<String, String> e : headers.entries()) {
headerMultimap.put(e.getKey(), e.getValue()); // lower case for HTTP/2 compatibility; even for HTTP/1, these were always
// case-insensitive
headerMultimap.put(e.getKey().toLowerCase(), e.getValue());
} }
} }
return headerMultimap; return headerMultimap;
@@ -22,6 +24,9 @@ public class HttpHeaderUtils {
public static String getSingleHeader(Multimap<String, String> headers, String headerName, boolean enforceOneValue) public static String getSingleHeader(Multimap<String, String> headers, String headerName, boolean enforceOneValue)
throws IllegalArgumentException { throws IllegalArgumentException {
String value = null; String value = null;
// lower case for HTTP/2 compatibility; even for HTTP/1, these were always
// case-insensitive
headerName = headerName.toLowerCase();
Collection<String> values = (headers == null) ? null : headers.get(headerName); Collection<String> values = (headers == null) ? null : headers.get(headerName);
if (values != null && !values.isEmpty()) { if (values != null && !values.isEmpty()) {
if (values.size() > 1 && enforceOneValue) { if (values.size() > 1 && enforceOneValue) {
@@ -1,8 +1,8 @@
package timely.configuration; package timely.configuration;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
public class Accumulo { public class Accumulo {
@@ -12,6 +12,8 @@ public class Accumulo {
@NotBlank @NotBlank
private String instanceName; private String instanceName;
@NotBlank @NotBlank
private String zookeeperTimeout = "120s";
@NotBlank
private String username; private String username;
@NotBlank @NotBlank
private String password; private String password;
@@ -46,6 +48,14 @@ public class Accumulo {
this.instanceName = instanceName; this.instanceName = instanceName;
} }
public String getZookeeperTimeout() {
return zookeeperTimeout;
}
public void setZookeeperTimeout(String zookeeperTimeout) {
this.zookeeperTimeout = zookeeperTimeout;
}
public String getUsername() { public String getUsername() {
return username; return username;
} }
@@ -13,6 +13,7 @@ public class Cache {
private List<String> nonCachedMetrics = new ArrayList<>(); private List<String> nonCachedMetrics = new ArrayList<>();
private long maxUniqueTagSets = 50000; private long maxUniqueTagSets = 50000;
private long flushInterval = 5000; private long flushInterval = 5000;
private long staleCacheExpiration = 1800000;
public HashMap<String, Integer> getMetricAgeOffHours() { public HashMap<String, Integer> getMetricAgeOffHours() {
return metricAgeOffHours; return metricAgeOffHours;
@@ -57,4 +58,12 @@ public class Cache {
public void setFlushInterval(long flushInterval) { public void setFlushInterval(long flushInterval) {
this.flushInterval = flushInterval; this.flushInterval = flushInterval;
} }
public long getStaleCacheExpiration() {
return staleCacheExpiration;
}
public void setStaleCacheExpiration(long staleCacheExpiration) {
this.staleCacheExpiration = staleCacheExpiration;
}
} }
@@ -1,9 +1,9 @@
package timely.configuration; package timely.configuration;
import javax.validation.Valid; import javax.validation.Valid;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
import org.springframework.boot.context.properties.NestedConfigurationProperty; import org.springframework.boot.context.properties.NestedConfigurationProperty;
public class Http { public class Http {
@@ -1,9 +1,8 @@
package timely.configuration; package timely.configuration;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
public class Server { public class Server {
@NotBlank @NotBlank
@@ -42,8 +41,6 @@ public class Server {
/** /**
* Time to wait (in seconds) for connections to finish and to make sure no new * Time to wait (in seconds) for connections to finish and to make sure no new
* connections happen before shutting down Netty event loop groups. * connections happen before shutting down Netty event loop groups.
*
* @return
*/ */
public int getShutdownQuietPeriod() { public int getShutdownQuietPeriod() {
return this.shutdownQuietPeriod; return this.shutdownQuietPeriod;
@@ -1,9 +1,8 @@
package timely.configuration; package timely.configuration;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull; import javax.validation.constraints.NotNull;
import org.hibernate.validator.constraints.NotBlank;
public class Websocket { public class Websocket {
@NotBlank @NotBlank
@@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import timely.api.request.CacheRequest; import timely.api.request.CacheRequest;
@@ -35,8 +35,8 @@ public class HttpCacheRequestHandler extends SimpleChannelInboundHandler<CacheRe
} }
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf)); Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -35,8 +35,8 @@ public class HttpMetricPutHandler extends SimpleChannelInboundHandler<MetricRequ
} }
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.EMPTY_BUFFER); Unpooled.EMPTY_BUFFER);
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -10,7 +10,7 @@ import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder; import io.netty.handler.codec.MessageToMessageDecoder;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.QueryStringDecoder; import io.netty.handler.codec.http.QueryStringDecoder;
@@ -52,7 +52,7 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
public static String getSessionId(FullHttpRequest msg) { public static String getSessionId(FullHttpRequest msg) {
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers()); Multimap<String, String> headers = HttpHeaderUtils.toMultimap(msg.headers());
Collection<String> cookies = headers.get(Names.COOKIE); Collection<String> cookies = headers.get(HttpHeaderNames.COOKIE.toString());
final StringBuilder buf = new StringBuilder(); final StringBuilder buf = new StringBuilder();
cookies.forEach(h -> { cookies.forEach(h -> {
ServerCookieDecoder.STRICT.decode(h).forEach(c -> { ServerCookieDecoder.STRICT.decode(h).forEach(c -> {
@@ -75,7 +75,7 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
LOG.trace(LOG_RECEIVED_REQUEST, msg); LOG.trace(LOG_RECEIVED_REQUEST, msg);
final String uri = msg.getUri(); final String uri = msg.uri();
final QueryStringDecoder decoder = new QueryStringDecoder(uri); final QueryStringDecoder decoder = new QueryStringDecoder(uri);
if (decoder.path().equals(nonSecureRedirectAddress)) { if (decoder.path().equals(nonSecureRedirectAddress)) {
out.add(new StrictTransportResponse()); out.add(new StrictTransportResponse());
@@ -87,10 +87,10 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
HttpRequest request; HttpRequest request;
try { try {
if (msg.getMethod().equals(HttpMethod.GET)) { if (msg.method().equals(HttpMethod.GET)) {
HttpGetRequest get = AnnotationResolver.getClassForHttpGet(decoder.path()); HttpGetRequest get = AnnotationResolver.getClassForHttpGet(decoder.path());
request = get.parseQueryParameters(decoder); request = get.parseQueryParameters(decoder);
} else if (msg.getMethod().equals(HttpMethod.POST)) { } else if (msg.method().equals(HttpMethod.POST)) {
HttpPostRequest post = AnnotationResolver.getClassForHttpPost(decoder.path()); HttpPostRequest post = AnnotationResolver.getClassForHttpPost(decoder.path());
String content = ""; String content = "";
ByteBuf body = msg.content(); ByteBuf body = msg.content();
@@ -101,8 +101,9 @@ public class HttpRequestDecoder extends MessageToMessageDecoder<FullHttpRequest>
} else { } else {
TimelyException e = new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(), TimelyException e = new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
"unhandled method type", ""); "unhandled method type", "");
e.addResponseHeader(Names.ALLOW, HttpMethod.GET.name() + "," + HttpMethod.POST.name()); e.addResponseHeader(HttpHeaderNames.ALLOW.toString(),
LOG.warn("Unhandled HTTP request type {}", msg.getMethod()); HttpMethod.GET.name() + "," + HttpMethod.POST.name());
LOG.warn("Unhandled HTTP request type {}", msg.method());
throw e; throw e;
} }
if (request instanceof AuthenticatedRequest) { if (request instanceof AuthenticatedRequest) {
@@ -15,14 +15,14 @@
*/ */
package timely.netty.http; package timely.netty.http;
import static io.netty.handler.codec.http.HttpHeaders.Names.CACHE_CONTROL; import static io.netty.handler.codec.http.HttpHeaderNames.CACHE_CONTROL;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONNECTION; import static io.netty.handler.codec.http.HttpHeaderNames.CONNECTION;
import static io.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE;
import static io.netty.handler.codec.http.HttpHeaders.Names.DATE; import static io.netty.handler.codec.http.HttpHeaderNames.DATE;
import static io.netty.handler.codec.http.HttpHeaders.Names.EXPIRES; import static io.netty.handler.codec.http.HttpHeaderNames.EXPIRES;
import static io.netty.handler.codec.http.HttpHeaders.Names.IF_MODIFIED_SINCE; import static io.netty.handler.codec.http.HttpHeaderNames.IF_MODIFIED_SINCE;
import static io.netty.handler.codec.http.HttpHeaders.Names.LAST_MODIFIED; import static io.netty.handler.codec.http.HttpHeaderNames.LAST_MODIFIED;
import static io.netty.handler.codec.http.HttpHeaders.Names.LOCATION; import static io.netty.handler.codec.http.HttpHeaderNames.LOCATION;
import static io.netty.handler.codec.http.HttpMethod.GET; import static io.netty.handler.codec.http.HttpMethod.GET;
import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST; import static io.netty.handler.codec.http.HttpResponseStatus.BAD_REQUEST;
import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN; import static io.netty.handler.codec.http.HttpResponseStatus.FORBIDDEN;
@@ -48,6 +48,7 @@ import java.util.TimeZone;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import javax.activation.MimetypesFileTypeMap; import javax.activation.MimetypesFileTypeMap;
import javax.net.ssl.SSLHandshakeException;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled; import io.netty.buffer.Unpooled;
@@ -59,9 +60,10 @@ import io.netty.handler.codec.http.DefaultHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest; import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpChunkedInput; import io.netty.handler.codec.http.HttpChunkedInput;
import io.netty.handler.codec.http.HttpHeaders; import io.netty.handler.codec.http.HttpHeaderValues;
import io.netty.handler.codec.http.HttpResponse; import io.netty.handler.codec.http.HttpResponse;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpUtil;
import io.netty.handler.stream.ChunkedFile; import io.netty.handler.stream.ChunkedFile;
import io.netty.util.CharsetUtil; import io.netty.util.CharsetUtil;
import io.netty.util.internal.SystemPropertyUtil; import io.netty.util.internal.SystemPropertyUtil;
@@ -122,20 +124,21 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz"; public static final String HTTP_DATE_FORMAT = "EEE, dd MMM yyyy HH:mm:ss zzz";
public static final String HTTP_DATE_GMT_TIMEZONE = "GMT"; public static final String HTTP_DATE_GMT_TIMEZONE = "GMT";
public static final int HTTP_CACHE_SECONDS = 60; public static final int HTTP_CACHE_SECONDS = 60;
private boolean ignoreSslHandshakeErrors = false;
@Override @Override
public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception { public void channelRead0(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {
if (!request.getDecoderResult().isSuccess()) { if (!request.decoderResult().isSuccess()) {
sendError(ctx, BAD_REQUEST); sendError(ctx, BAD_REQUEST);
return; return;
} }
if (request.getMethod() != GET) { if (request.method() != GET) {
sendError(ctx, METHOD_NOT_ALLOWED); sendError(ctx, METHOD_NOT_ALLOWED);
return; return;
} }
String uri = request.getUri(); String uri = request.uri();
if (uri.startsWith("/favicon.ico")) { if (uri.startsWith("/favicon.ico")) {
uri = uri.replaceFirst("\\/", "/webapp/"); uri = uri.replaceFirst("\\/", "/webapp/");
} }
@@ -201,12 +204,12 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
long fileLength = raf.length(); long fileLength = raf.length();
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK); HttpResponse response = new DefaultHttpResponse(HTTP_1_1, OK);
HttpHeaders.setContentLength(response, fileLength); HttpUtil.setContentLength(response, fileLength);
setContentTypeHeader(response, file); setContentTypeHeader(response, file);
setDateAndCacheHeaders(response, file); setDateAndCacheHeaders(response, file);
HttpHeaders.setTransferEncodingChunked(response); HttpUtil.setTransferEncodingChunked(response, true);
if (HttpHeaders.isKeepAlive(request)) { if (HttpUtil.isKeepAlive(request)) {
response.headers().set(CONNECTION, HttpHeaders.Values.KEEP_ALIVE); response.headers().set(CONNECTION, HttpHeaderValues.KEEP_ALIVE);
} }
// Write the initial line and the header. // Write the initial line and the header.
@@ -220,6 +223,10 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
// ignore SSLHandshakeException when using a self-signed server certificate
if (ignoreSslHandshakeErrors && cause.getCause() instanceof SSLHandshakeException) {
return;
}
LOG.error("Error serving file", cause); LOG.error("Error serving file", cause);
if (ctx.channel().isActive()) { if (ctx.channel().isActive()) {
sendError(ctx, INTERNAL_SERVER_ERROR); sendError(ctx, INTERNAL_SERVER_ERROR);
@@ -378,4 +385,9 @@ public class HttpStaticFileServerHandler extends SimpleChannelInboundHandler<Ful
MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap(); MimetypesFileTypeMap mimeTypesMap = new MimetypesFileTypeMap();
response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath())); response.headers().set(CONTENT_TYPE, mimeTypesMap.getContentType(file.getPath()));
} }
public HttpStaticFileServerHandler setIgnoreSslHandshakeErrors(boolean ignoreSslHandshakeErrors) {
this.ignoreSslHandshakeErrors = ignoreSslHandshakeErrors;
return this;
}
} }
@@ -7,7 +7,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import timely.api.request.VersionRequest; import timely.api.request.VersionRequest;
@@ -20,8 +20,8 @@ public class HttpVersionRequestHandler extends SimpleChannelInboundHandler<Versi
protected void channelRead0(ChannelHandlerContext ctx, VersionRequest v) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, VersionRequest v) throws Exception {
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(VersionRequest.VERSION.getBytes(StandardCharsets.UTF_8))); Unpooled.copiedBuffer(VersionRequest.VERSION.getBytes(StandardCharsets.UTF_8)));
response.headers().set(Names.CONTENT_TYPE, Constants.TEXT_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.TEXT_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -6,7 +6,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseEncoder; import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
@@ -51,7 +51,7 @@ public class NonSslRedirectHandler extends OptionalSslHandler implements TimelyH
LOG.trace("Received non-SSL request, returning redirect"); LOG.trace("Received non-SSL request, returning redirect");
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.MOVED_PERMANENTLY, Unpooled.EMPTY_BUFFER); HttpResponseStatus.MOVED_PERMANENTLY, Unpooled.EMPTY_BUFFER);
response.headers().set(Names.LOCATION, redirectAddress); response.headers().set(HttpHeaderNames.LOCATION, redirectAddress);
LOG.trace(Constants.LOG_RETURNING_RESPONSE, response); LOG.trace(Constants.LOG_RETURNING_RESPONSE, response);
encoder.write(ctx, response, ctx.voidPromise()); encoder.write(ctx, response, ctx.voidPromise());
ctx.flush(); ctx.flush();
@@ -1,5 +1,7 @@
package timely.netty.http; package timely.netty.http;
import javax.net.ssl.SSLHandshakeException;
import io.netty.channel.ChannelHandler.Sharable; import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
@@ -12,6 +14,7 @@ import timely.api.response.TimelyException;
public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyException> implements TimelyHttpHandler { public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyException> implements TimelyHttpHandler {
private static final Logger LOG = LoggerFactory.getLogger(TimelyExceptionHandler.class); private static final Logger LOG = LoggerFactory.getLogger(TimelyExceptionHandler.class);
private boolean ignoreSslHandshakeErrors = false;
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, TimelyException msg) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, TimelyException msg) throws Exception {
@@ -20,6 +23,10 @@ public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyEx
@Override @Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
// ignore SSLHandshakeException when using a self-signed server certificate
if (ignoreSslHandshakeErrors && cause.getCause() instanceof SSLHandshakeException) {
return;
}
LOG.error("Unhandled exception in pipeline", cause); LOG.error("Unhandled exception in pipeline", cause);
if (cause instanceof TimelyException) { if (cause instanceof TimelyException) {
this.sendHttpError(ctx, (TimelyException) cause); this.sendHttpError(ctx, (TimelyException) cause);
@@ -32,4 +39,8 @@ public class TimelyExceptionHandler extends SimpleChannelInboundHandler<TimelyEx
} }
} }
public TimelyExceptionHandler setIgnoreSslHandshakeErrors(boolean ignoreSslHandshakeErrors) {
this.ignoreSslHandshakeErrors = ignoreSslHandshakeErrors;
return this;
}
} }
@@ -6,7 +6,7 @@ import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -25,8 +25,8 @@ public interface TimelyHttpHandler {
.writeValueAsBytes("ResponseCode: " + e.getCode() + " Message: " + e.getMessage()); .writeValueAsBytes("ResponseCode: " + e.getCode() + " Message: " + e.getMessage());
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(e.getCode()), Unpooled.copiedBuffer(buf)); HttpResponseStatus.valueOf(e.getCode()), Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
e.getResponseHeaders().entrySet().forEach(entry -> response.headers().set(entry.getKey(), entry.getValue())); e.getResponseHeaders().entrySet().forEach(entry -> response.headers().set(entry.getKey(), entry.getValue()));
// Send the error response // Send the error response
sendResponse(ctx, response); sendResponse(ctx, response);
@@ -17,9 +17,10 @@ import timely.auth.util.HttpHeaderUtils;
public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationToken { public class TimelyAuthenticationToken extends PreAuthenticatedAuthenticationToken {
private static final long serialVersionUID = 1L;
private static final Logger LOG = LoggerFactory.getLogger(TimelyAuthenticationToken.class); private static final Logger LOG = LoggerFactory.getLogger(TimelyAuthenticationToken.class);
public static final String PROXIED_ENTITIES_HEADER = "X-ProxiedEntitiesChain"; public static final String PROXIED_ENTITIES_HEADER = "x-proxiedentitieschain";
public static final String PROXIED_ISSUERS_HEADER = "X-ProxiedIssuersChain"; public static final String PROXIED_ISSUERS_HEADER = "x-proxiedissuerschain";
private Multimap<String, String> httpHeaders = null; private Multimap<String, String> httpHeaders = null;
private X509Certificate clientCert = null; private X509Certificate clientCert = null;
@@ -8,7 +8,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import io.netty.handler.codec.http.cookie.DefaultCookie; import io.netty.handler.codec.http.cookie.DefaultCookie;
@@ -43,22 +43,22 @@ public abstract class TimelyLoginRequestHandler<T> extends SimpleChannelInboundH
principal.getPrimaryUser().getDn().subjectDN()); principal.getPrimaryUser().getDn().subjectDN());
String sessionIdEncoded = URLEncoder.encode(sessionId, StandardCharsets.UTF_8.name()); String sessionIdEncoded = URLEncoder.encode(sessionId, StandardCharsets.UTF_8.name());
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK); FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
DefaultCookie cookie = new DefaultCookie(Constants.COOKIE_NAME, sessionIdEncoded); DefaultCookie cookie = new DefaultCookie(Constants.COOKIE_NAME, sessionIdEncoded);
cookie.setDomain(domain); cookie.setDomain(domain);
cookie.setMaxAge(maxAge); cookie.setMaxAge(maxAge);
cookie.setPath("/"); cookie.setPath("/");
cookie.setHttpOnly(true); cookie.setHttpOnly(true);
cookie.setSecure(true); cookie.setSecure(true);
response.headers().set(Names.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie)); response.headers().set(HttpHeaderNames.SET_COOKIE, ServerCookieEncoder.STRICT.encode(cookie));
sendResponse(ctx, response); sendResponse(ctx, response);
} catch (Exception e) { } catch (Exception e) {
LOG.error("Login failure", e); LOG.error("Login failure", e);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.UNAUTHORIZED); HttpResponseStatus.UNAUTHORIZED);
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import timely.api.request.timeseries.AggregatorsRequest; import timely.api.request.timeseries.AggregatorsRequest;
@@ -22,8 +22,8 @@ public class HttpAggregatorsRequestHandler extends SimpleChannelInboundHandler<A
byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(AggregatorsResponse.RESPONSE); byte[] buf = JsonUtil.getObjectMapper().writeValueAsBytes(AggregatorsResponse.RESPONSE);
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf)); Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
@@ -2,7 +2,7 @@ package timely.netty.http.timeseries;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import timely.api.request.timeseries.MetricsRequest; import timely.api.request.timeseries.MetricsRequest;
import timely.api.response.timeseries.MetricsResponse; import timely.api.response.timeseries.MetricsResponse;
import timely.configuration.Configuration; import timely.configuration.Configuration;
@@ -20,7 +20,7 @@ public class HttpMetricsRequestHandler extends SimpleChannelInboundHandler<Metri
@Override @Override
protected void channelRead0(ChannelHandlerContext ctx, MetricsRequest msg) throws Exception { protected void channelRead0(ChannelHandlerContext ctx, MetricsRequest msg) throws Exception {
MetricsResponse r = new MetricsResponse(conf); MetricsResponse r = new MetricsResponse(conf);
String acceptHeader = msg.getRequestHeader(Names.ACCEPT); String acceptHeader = msg.getRequestHeader(HttpHeaderNames.ACCEPT.toString());
sendResponse(ctx, r.toHttpResponse(acceptHeader)); sendResponse(ctx, r.toHttpResponse(acceptHeader));
} }
@@ -5,7 +5,7 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse; import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpResponse; import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpHeaders.Names; import io.netty.handler.codec.http.HttpHeaderNames;
import io.netty.handler.codec.http.HttpResponseStatus; import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion; import io.netty.handler.codec.http.HttpVersion;
import org.slf4j.Logger; import org.slf4j.Logger;
@@ -42,8 +42,8 @@ public class HttpQueryRequestHandler extends SimpleChannelInboundHandler<QueryRe
} }
FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK, FullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK,
Unpooled.copiedBuffer(buf)); Unpooled.copiedBuffer(buf));
response.headers().set(Names.CONTENT_TYPE, Constants.JSON_TYPE); response.headers().set(HttpHeaderNames.CONTENT_TYPE, Constants.JSON_TYPE);
response.headers().set(Names.CONTENT_LENGTH, response.content().readableBytes()); response.headers().set(HttpHeaderNames.CONTENT_LENGTH, response.content().readableBytes());
sendResponse(ctx, response); sendResponse(ctx, response);
} }
Loaded 100 of 167 files, more files were not shown because too many files have changed in this diff. Show more