Compare commits

..
82 Commits
Author SHA1 Message Date
Bill Oley cb875819ff Release version 2.0.2 2024-01-31 11:49:57 -05:00
Bill Oley fcc5f0f716 Fix OneWaySSL tests by creating a PKCS12 file from SelfSignedCertificate 2024-01-29 17:33:13 -05:00
Bill Oley c87543f7d7 Code formatting via autoformat profile 2024-01-29 16:28:06 -05:00
Bill Oley d7f5a3b20b Add codestyle config, Pom changes to support code formatting, import sort, pom sort 2024-01-29 16:24:24 -05:00
Bill Oley 9601cdc927 curator version to 5.5.0 2024-01-29 16:23:27 -05:00
Bill Oley 60c2588c79 Bump version to 2.0.2-SNAPSHOT 2024-01-26 13:12:18 -05:00
Bill Oley f3275c6a34 Release version 2.0.1 2024-01-26 13:10:20 -05:00
Bill Oley 7486b6b761 Add RemoteUserDetailsService, fix MiniAccumulo test, simplify SSL configuration and allow more types of SSL keyStores and trustStores 2024-01-26 13:07:26 -05:00
Bill Oley b1a968590c Bump version to 2.0.1-SNAPSHOT 2024-01-17 10:42:09 -05:00
Bill Oley 8f86b20c02 Release version 2.0.0 2024-01-17 10:42:09 -05:00
Bill Oley a3620a3f24 Fixes for intermittent test failures, version to 2.0.0-SNAPSHOT 2024-01-17 10:42:09 -05:00
Bill Oley 9c99e74958 Use InMemoryAccumulo instead of MiniAccumulo, test fixes 2024-01-17 10:42:09 -05:00
Bill Oley 91ec36e819 Changes to support accumulo2 2024-01-17 10:42:08 -05:00
Bill Oley 9ad1b18b74 Delete grafana-auth module 2024-01-17 10:42:08 -05:00
Bill Oley 82c45d4dd4 Bump version to 1.3.7-SNAPSHOT 2023-10-13 10:28:19 -04:00
Bill Oley a0e47d0b48 Release version 1.3.6 2023-10-13 10:25:43 -04:00
dependabot[bot] d3dc324d84 Bump golang.org/x/net from 0.7.0 to 0.17.0 in /grafana/timely-app (#246)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.7.0 to 0.17.0.
- [Commits](https://github.com/golang/net/compare/v0.7.0...v0.17.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-13 10:18:12 -04:00
Bill Oley 4871b3a4a9 Update dependencies, protect against NPE in AuthCache, AuthorizationsUtil 2023-10-13 10:02:33 -04:00
dependabot[bot] 8d5d5eeca8 Bump terser from 4.8.0 to 4.8.1 in /grafana/timely-app (#235)
Bumps [terser](https://github.com/terser/terser) from 4.8.0 to 4.8.1.
- [Release notes](https://github.com/terser/terser/releases)
- [Changelog](https://github.com/terser/terser/blob/master/CHANGELOG.md)
- [Commits](https://github.com/terser/terser/commits)

---
updated-dependencies:
- dependency-name: terser
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 14:26:51 -04:00
dependabot[bot] a006a44372 Bump decode-uri-component from 0.2.0 to 0.2.2 in /grafana/timely-app (#240)
Bumps [decode-uri-component](https://github.com/SamVerschueren/decode-uri-component) from 0.2.0 to 0.2.2.
- [Release notes](https://github.com/SamVerschueren/decode-uri-component/releases)
- [Commits](https://github.com/SamVerschueren/decode-uri-component/compare/v0.2.0...v0.2.2)

---
updated-dependencies:
- dependency-name: decode-uri-component
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 14:26:07 -04:00
dependabot[bot] 6cfcdc1030 Bump golang.org/x/net in /grafana/timely-app (#241)
Bumps [golang.org/x/net](https://github.com/golang/net) from 0.0.0-20210614182718-04defd469f4e to 0.7.0.
- [Release notes](https://github.com/golang/net/releases)
- [Commits](https://github.com/golang/net/commits/v0.7.0)

---
updated-dependencies:
- dependency-name: golang.org/x/net
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 14:25:46 -04:00
dependabot[bot] 4eb7ceff8c Bump semver from 5.7.1 to 5.7.2 in /grafana/timely-app (#244)
Bumps [semver](https://github.com/npm/node-semver) from 5.7.1 to 5.7.2.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/v5.7.2/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v5.7.1...v5.7.2)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 14:25:13 -04:00
dependabot[bot] 6ad8da2b99 Bump word-wrap from 1.2.3 to 1.2.4 in /grafana/timely-app (#245)
Bumps [word-wrap](https://github.com/jonschlinkert/word-wrap) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/jonschlinkert/word-wrap/releases)
- [Commits](https://github.com/jonschlinkert/word-wrap/compare/1.2.3...1.2.4)

---
updated-dependencies:
- dependency-name: word-wrap
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-10-11 14:24:12 -04:00
dependabot[bot] 7f56a36906 Bump guava from 29.0-jre to 32.0.0-jre (#243)
Bumps [guava](https://github.com/google/guava) from 29.0-jre to 32.0.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

---
updated-dependencies:
- dependency-name: com.google.guava:guava
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2023-06-15 12:53:29 -04:00
Christopher Tubbs 1443fdceb9 Bump formatter and impsort plugins for newer Maven 2023-06-07 11:01:29 -04:00
Christopher Tubbs 3cff14eb03 Bump NPM/Yarn deps
* This avoids warnings about CVEs from GitHub in the referenced
  dependencies
* This required relaxing the node version constraint, since I didn't
  have version 12, only 16
2022-04-11 15:49:13 -04:00
dependabot[bot] 8fa5b56e62 Bump hadoop-common from 2.7.7 to 2.10.1 (#230)
Bumps hadoop-common from 2.7.7 to 2.10.1.

---
updated-dependencies:
- dependency-name: org.apache.hadoop:hadoop-common
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 14:22:59 -04:00
dependabot[bot] b2f2cef834 Bump follow-redirects from 1.14.1 to 1.14.8 in /grafana/timely-app (#232)
Bumps [follow-redirects](https://github.com/follow-redirects/follow-redirects) from 1.14.1 to 1.14.8.
- [Release notes](https://github.com/follow-redirects/follow-redirects/releases)
- [Commits](https://github.com/follow-redirects/follow-redirects/compare/v1.14.1...v1.14.8)

---
updated-dependencies:
- dependency-name: follow-redirects
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 14:22:18 -04:00
dependabot[bot] c6a2988083 Bump accumulo-core from 1.9.3 to 1.10.1 (#231)
Bumps [accumulo-core](https://github.com/apache/accumulo) from 1.9.3 to 1.10.1.
- [Release notes](https://github.com/apache/accumulo/releases)
- [Commits](https://github.com/apache/accumulo/compare/rel/1.9.3...rel/1.10.1)

---
updated-dependencies:
- dependency-name: org.apache.accumulo:accumulo-core
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-11 14:21:50 -04:00
Bill Oley d54817cc9a Bump version to 1.3.6-SNAPSHOT 2022-01-07 14:43:04 -05:00
Bill Oley c4ee035584 Release version 1.3.5 2022-01-07 14:40:06 -05:00
Chromico Rek d4bfc1a3aa Update log4j to 2.17.1 to address CVE-2021-44832 (#227) 2021-12-30 13:50:34 -05:00
Chromico Rek fb315148a1 Update log4j to 2.17 to address CVE-2021-45105. (#226) 2021-12-20 10:44:47 -05:00
dependabot[bot] e8ae575086 Bump @grafana/data from 8.2.2 to 8.2.3 in /grafana/timely-app (#222)
Bumps [@grafana/data](https://github.com/grafana/grafana/tree/HEAD/packages/grafana-data) from 8.2.2 to 8.2.3.
- [Release notes](https://github.com/grafana/grafana/releases)
- [Changelog](https://github.com/grafana/grafana/blob/main/CHANGELOG.md)
- [Commits](https://github.com/grafana/grafana/commits/v8.2.3/packages/grafana-data)

---
updated-dependencies:
- dependency-name: "@grafana/data"
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-12-15 16:59:06 -05:00
Christopher Tubbs da0146c741 Bump log4j and other cleanup
* Update log4j2 to 2.16.0 to address CVEs
* Update impsort and sortpom plugins
* Update spotbugs plugin to build on newer JDKs and suppress common
  minor warnings from that
* Fix a few minor warnings from spotbugs that are easy: remove hamcrest
  matcher, fix null checks that are always or never null
* Suppress module-info.class from shaded jars
2021-12-15 16:53:24 -05:00
Bill Oley 882b4e2587 Bump version to 1.3.5-SNAPSHOT 2021-11-01 10:17:49 -04:00
Bill Oley 8048862f2c Release version 1.3.4 2021-11-01 10:15:01 -04:00
Bill Oley c2f00c3d19 Convert line endings in pom files from dos->unix 2021-11-01 10:11:06 -04:00
Bill Oley 61b1a245e2 Bump version to 1.3.4-SNAPSHOT 2021-10-26 16:07:30 -04:00
Bill Oley a3c33115c4 Release version 1.3.3 2021-10-26 15:57:25 -04:00
Bill Oley bed1ceb9ee Remove yarn-error.log and add to .gitignore 2021-10-26 15:56:12 -04:00
Bill Oley 06e2699a44 Update to [email protected], [email protected], [email protected] 2021-10-26 14:59:25 -04:00
Bill Oley d09faaa087 Correct log message formatting 2021-10-22 14:21:49 -04:00
dependabot[bot] 088f1a5cab Bump jsoup from 1.11.3 to 1.14.2 (#216)
Bumps [jsoup](https://github.com/jhy/jsoup) from 1.11.3 to 1.14.2.
- [Release notes](https://github.com/jhy/jsoup/releases)
- [Changelog](https://github.com/jhy/jsoup/blob/master/CHANGES)
- [Commits](https://github.com/jhy/jsoup/compare/jsoup-1.11.3...jsoup-1.14.2)

---
updated-dependencies:
- dependency-name: org.jsoup:jsoup
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-22 13:47:11 -04:00
dependabot[bot] cf6d4ada3e Bump guava from 27.1-jre to 29.0-jre (#214)
Bumps [guava](https://github.com/google/guava) from 27.1-jre to 29.0-jre.
- [Release notes](https://github.com/google/guava/releases)
- [Commits](https://github.com/google/guava/commits)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-10-22 13:46:44 -04:00
Bill Oley e464a8c9e0 fixes #217 - Improve ageoff iterator remove/add logic in DataStoreImpl (#218)
* fixes #217 - Improve ageoff iterator remove/add logic in DataStoreImpl

* fixes #217 - Use curator LeaderLatch to elect a leader server which will remove/add the age off iterators
2021-10-22 13:13:00 -04:00
Bill Oley 8e4e61c373 fixes #219 Create scripts to help with running and testing multiple instances of server and balancer 2021-10-22 13:11:41 -04:00
dependabot[bot] 729dca23ba Bump httpclient from 4.5.6 to 4.5.13 (#215)
Bumps httpclient from 4.5.6 to 4.5.13.

---
updated-dependencies:
- dependency-name: org.apache.httpcomponents:httpclient
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2021-06-03 23:49:36 -04:00
Bill Oley 104d20ad1b Bump version to 1.3.3-SNAPSHOT 2020-11-06 13:30:04 -05:00
Bill Oley f0f005f8fa Release version 1.3.2 2020-11-06 13:23:36 -05:00
Bill Oley 8593218b89 Logging fixes 2020-11-06 12:24:22 -05:00
Bill Oley cbfe6a439f closes #213 Release reference counted netty objects to avoid memory errors 2020-11-06 11:50:24 -05:00
Bill Oley 455616d568 closes #213 Release reference counted netty objects to avoid memory errors 2020-11-05 14:17:01 -05:00
Bill Oley 9d3172cc75 Close objects when TcpClient has problem connecting to Timely 2020-11-04 16:06:08 -05:00
Bill Oley 7aa7b0da2a Remove entries from metricMap when metric is no longer cached on a particular server 2020-11-04 16:04:52 -05:00
Bill Oley 6c61861d83 Fix NPE in BalancedMetricResolver.rebalance where a metric exists in metricMap but not in metricToHostMap 2020-10-29 16:49:55 -04:00
Bill Oley 91882ba363 closes #212 Use writeLock for DataStoreCache.ageOffGorillaStores to avoid ConcurrentModificationException 2020-10-28 15:58:33 -04:00
Bill Oley ed61b5f305 Fix css inconsistent rendering in grafana datasource QueryEditor, remove overridden/unusd css 2020-10-27 14:10:58 -04:00
Bill Oley a083c2d632 Fix oauth issues - intersect TimelyUser auths with Accumulo auths when creating from oauthToken, allow X.509 (PEM) jwt signature check key, increase size of httpHeader max size 2020-10-23 14:41:46 -04:00
Jonathan SteinandJonathan Stein 2891794174 Fixing Nondeterministic Tests (#211)
* Fix MetricAdapterTest and QueryResponseTest flakiness

* change MetricAdapterTest and QueryResponseTest flakiness fix by defining Json key order

* Removed .swp file

* Fix SearchLookupResponse testResponse1

* Fixed flaky AuthCacheTest

Co-authored-by: Jonathan Stein <[email protected]>
2020-10-19 14:01:17 -04:00
Bill Oley c99b2471cf Bump version to 1.3.2-SNAPSHOT 2020-10-14 14:58:07 -04:00
Bill Oley bb479a58d7 Release version 1.3.1 2020-10-14 14:54:59 -04:00
Bill Oley ff37d40801 datasource logging fix, combine datasource golang code for readability 2020-10-14 14:46:09 -04:00
Bill Oley a7d980975e closes #210 Only reload cert and CA when changed
Only reload cert and CA when changed
2020-10-14 13:12:11 -04:00
dependabot[bot] 30f5a0c1b2 Bump junit from 4.12 to 4.13.1 (#209)
Bumps [junit](https://github.com/junit-team/junit4) from 4.12 to 4.13.1.
- [Release notes](https://github.com/junit-team/junit4/releases)
- [Changelog](https://github.com/junit-team/junit4/blob/main/doc/ReleaseNotes4.12.md)
- [Commits](https://github.com/junit-team/junit4/compare/r4.12...r4.13.1)

Signed-off-by: dependabot[bot] <[email protected]>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2020-10-13 00:54:58 -04:00
Bill Oley 2f9e6ca2d7 Bump version to 1.3.1-SNAPSHOT 2020-09-25 13:04:54 -04:00
Bill Oley 954528a460 Release version 1.3.0 2020-09-25 12:39:33 -04:00
Bill Oley 0f7b514b30 Update authentication configuration and error handling, Grafana depenancies to 7.2.0
Grafana depenancies to 7.2.0
2020-09-25 12:19:08 -04:00
Bill Oley a6ca46e1c1 Rewrite grafana plugin for grafana 7 using typescript, react. Use new backend plugin feature to enable alerts
More work - has errors

more work

more work

Add addOauthPassThru setting

CS fixes, OAuthPassThru setting, Alias fix, Auto Focus on new tag

Logging cleanup and make aliases work

Delete old version of plugin

remove extra files

Accept JWT authentication and test fixes

Accept JWT authentication

Fix assembly

Add JWT settings to sample configs
2020-09-15 16:26:29 -04:00
Bill Oley 487d1d4a5c Use MetaCache for suggest api and implement tagk and tagv types of suggest 2020-09-15 16:26:18 -04:00
Christopher Tubbs e3c26f815c Merge pull request #206 from NationalSecurityAgency/dependabot/maven/commons-beanutils-commons-beanutils-1.9.4
Bump commons-beanutils from 1.9.3 to 1.9.4
2020-07-02 17:16:58 -04:00
Christopher Tubbs bbc1946204 Update plugins for newer JDKs
* Bump formatter and impsort to latest to stay current
* Bump spotbugs to support up to at least JDK 14
2020-07-02 17:09:06 -04:00
dependabot[bot] 991412f99b Bump commons-beanutils from 1.9.3 to 1.9.4
Bumps commons-beanutils from 1.9.3 to 1.9.4.

Signed-off-by: dependabot[bot] <[email protected]>
2020-07-02 20:44:34 +00:00
Christopher Tubbs 5428e93cb5 Merge pull request #205 from NationalSecurityAgency/dependabot/maven/version.log4j-2.13.3
Bump version.log4j from 2.11.2 to 2.13.3
2020-07-02 16:44:06 -04:00
dependabot[bot] c18aae5a89 Bump version.log4j from 2.11.2 to 2.13.3
Bumps `version.log4j` from 2.11.2 to 2.13.3.

Updates `log4j-api` from 2.11.2 to 2.13.3

Updates `log4j-core` from 2.11.2 to 2.13.3

Updates `log4j-slf4j-impl` from 2.11.2 to 2.13.3

Signed-off-by: dependabot[bot] <[email protected]>
2020-07-01 18:20:00 +00:00
Bill Oley 6972c46a1a Merge pull request #204 from matthpeterson/issue-203
Closes issue #203 - avoid calling sizeInBytes if it's not expected to exist
2020-04-17 10:42:14 -04:00
matthpeterson 4d7bee7566 Closes issue #203 - Stop calling sizeInBytes if it's not expected to exist 2020-04-16 15:17:08 +00:00
Bill Oley 4b11b3d50f closes #201 Create balancer assignment file if it doesn't exist 2020-04-10 10:27:18 -04:00
Bill Oley 84b9856ec7 closes #199 Fix issues when using a generated (self-signed) server certificate 2020-04-10 10:20:25 -04:00
Bill Oley c9548a9b9a closes #198 Cleaner shutdown of resources for Timely server, balancer, grafana-auth 2020-04-10 10:01:47 -04:00
Bill Oley aec7bd4c38 closes #197 Fix scan range in MetaCacheImpl 2020-04-09 11:58:30 -04:00
Bill Oley e2b8b643f2 closes #200 Initialize non-cached metrics stored in zookeeper 2020-04-09 11:32:03 -04:00
452 changed files with 168600 additions and 28275 deletions

No files matched your search

+1
View File
@@ -7,4 +7,5 @@ target/
logs/
server/pki/
client/src/main/python/setup.py
grafana/timely-app/yarn-error.log
+15 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>timely-analytics</artifactId>
<!--
@@ -169,6 +169,12 @@
</excludes>
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
<filter>
<artifact>org.apache.flink:*</artifact>
<excludes>
@@ -246,6 +252,14 @@
<artifactSet>
<excludes combine.self="override" />
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
@@ -7,6 +7,7 @@ import java.util.List;
import java.util.stream.Collectors;
import org.apache.flink.streaming.util.serialization.SerializationSchema;
import timely.model.Tag;
public class MetricHistogram implements Serializable, SerializationSchema<MetricHistogram> {
@@ -1,13 +1,13 @@
package timely.analytics.flink;
import org.apache.flink.api.java.functions.KeySelector;
import timely.api.response.MetricResponse;
/**
* Uniquely defines a time series in a Flink stream. A unique time series is the
* metric name and all of its tags, to include visibility if it exists.
* Uniquely defines a time series in a Flink stream. A unique time series is the metric name and all of its tags, to include visibility if it exists.
*/
public class MetricKeySelector implements KeySelector<MetricResponse, String> {
public class MetricKeySelector implements KeySelector<MetricResponse,String> {
private static final long serialVersionUID = 1L;
@@ -9,11 +9,11 @@ import org.apache.flink.api.common.accumulators.Accumulator;
* Custom accumulator that retains a sorted map of strings and their counts
*
*/
public class SortedStringAccumulator implements Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> {
public class SortedStringAccumulator implements Accumulator<String,ConcurrentSkipListMap<String,MutableLong>> {
private static final long serialVersionUID = 1L;
private ConcurrentSkipListMap<String, MutableLong> values = new ConcurrentSkipListMap<>();
private ConcurrentSkipListMap<String,MutableLong> values = new ConcurrentSkipListMap<>();
@Override
public void add(String value) {
@@ -25,7 +25,7 @@ public class SortedStringAccumulator implements Accumulator<String, ConcurrentSk
}
@Override
public ConcurrentSkipListMap<String, MutableLong> getLocalValue() {
public ConcurrentSkipListMap<String,MutableLong> getLocalValue() {
return values;
}
@@ -35,7 +35,7 @@ public class SortedStringAccumulator implements Accumulator<String, ConcurrentSk
}
@Override
public void merge(Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> other) {
public void merge(Accumulator<String,ConcurrentSkipListMap<String,MutableLong>> other) {
other.getLocalValue().forEach((k, v) -> {
if (values.containsKey(k)) {
values.get(k).add(v.longValue());
@@ -46,7 +46,7 @@ public class SortedStringAccumulator implements Accumulator<String, ConcurrentSk
}
@Override
public Accumulator<String, ConcurrentSkipListMap<String, MutableLong>> clone() {
public Accumulator<String,ConcurrentSkipListMap<String,MutableLong>> clone() {
SortedStringAccumulator a = new SortedStringAccumulator();
a.getLocalValue().putAll(this.values);
return a;
@@ -22,6 +22,7 @@ import org.apache.flink.streaming.api.functions.source.RichSourceFunction;
import org.apache.flink.streaming.api.watermark.Watermark;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.response.MetricResponse;
import timely.api.response.MetricResponses;
import timely.client.websocket.ClientHandler;
@@ -65,10 +66,9 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
public void open(Configuration parameters) throws Exception {
LOG.info("Opening summarization job.");
super.open(parameters);
client = new WebSocketSubscriptionClient(jp.getTimelyHostname(), jp.getTimelyHttpsPort(), jp.getTimelyWssPort(),
jp.isClientAuth(), jp.isDoLogin(), jp.getTimelyUsername(), jp.getTimelyPassword(), jp.getKeyStoreFile(),
jp.getKeyStoreType(), jp.getKeyStorePass(), jp.getTrustStoreFile(), jp.getTrustStoreType(),
jp.getTrustStorePass(), jp.isHostVerificationEnabled(), jp.getBufferSize());
client = new WebSocketSubscriptionClient(jp.getTimelyHostname(), jp.getTimelyHttpsPort(), jp.getTimelyWssPort(), jp.isClientAuth(), jp.isDoLogin(),
jp.getTimelyUsername(), jp.getTimelyPassword(), jp.getKeyStoreFile(), jp.getKeyStoreType(), jp.getKeyStorePass(),
jp.getTrustStoreFile(), jp.getTrustStoreType(), jp.getTrustStorePass(), jp.isHostVerificationEnabled(), jp.getBufferSize());
}
@Override
@@ -103,12 +103,10 @@ public class SubscriptionSource extends RichSourceFunction<MetricResponse> imple
@Override
public void onMessage(final String message) {
LOG.info("Message received on Websocket session {}, length: {}", session.getId(),
message.length());
LOG.info("Message received on Websocket session {}, length: {}", session.getId(), message.length());
try {
// Deserialize in this thread
final MetricResponses responses = JsonSerializer.getObjectMapper().readValue(message,
MetricResponses.class);
final MetricResponses responses = JsonSerializer.getObjectMapper().readValue(message, MetricResponses.class);
// Process in the other
final Runnable r = () -> {
responses.getResponses().forEach(response -> {
@@ -13,6 +13,7 @@ import org.apache.flink.streaming.api.functions.sink.SocketClientSink;
import org.apache.flink.streaming.api.windowing.assigners.TumblingEventTimeWindows;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.response.MetricResponse;
public class SummarizationJob {
@@ -184,8 +184,8 @@ public class SummarizationJobParameters extends GlobalJobParameters implements S
}
@Override
public Map<String, String> toMap() {
Map<String, String> m = new HashMap<>();
public Map<String,String> toMap() {
Map<String,String> m = new HashMap<>();
m.put("timelyHostname", this.timelyHostname);
m.put("timelyHttpsPort", Integer.toString(this.timelyHttpsPort));
m.put("timelyWssPort", Integer.toString(this.timelyWssPort));
+8
View File
@@ -15,4 +15,12 @@
<Method name="getMetrics" />
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
@@ -6,6 +6,7 @@ import java.util.List;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import timely.model.Metric;
import timely.model.Tag;
import timely.model.parse.MetricParser;
@@ -96,9 +97,8 @@ public class MetricHistogramTest {
Metric metric = metricParser.parse(put);
Assert.assertEquals("sys.cpu.user_summarized", metric.getName());
metric.getTags().forEach(t -> {
Assert.assertTrue(
t.equals(t1) || t.equals(t2) || t.equals(avg) || t.equals(min) || t.equals(max) || t.equals(sum)
|| t.equals(count) || t.equals(p50) || t.equals(p75) || t.equals(p90) || t.equals(p99));
Assert.assertTrue(t.equals(t1) || t.equals(t2) || t.equals(avg) || t.equals(min) || t.equals(max) || t.equals(sum) || t.equals(count)
|| t.equals(p50) || t.equals(p75) || t.equals(p90) || t.equals(p99));
});
}
}
+47
View File
@@ -0,0 +1,47 @@
#!/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=$(cd $THIS_DIR/.. && pwd)
BIN_DIR=${BASE_DIR}/bin
CONF_DIR=${BASE_DIR}/conf
. ${BIN_DIR}/timely-balancer-env.sh
for i in $(seq 1 $NUM_PROFILES); do
if [ "$NUM_PROFILES" -eq "1" ]; then
FILENAME=timely-balancer.yml
else
FILENAME=timely-balancer-${i}.yml
fi
echo "creating configuration file ${CONF_DIR}/${FILENAME}"
cp ${CONF_DIR}/timely-balancer-template.yml ${CONF_DIR}/${FILENAME}
sed -i "s/\${ZOOKEEPERS}/${ZOOKEEPERS}/g" ${CONF_DIR}/${FILENAME}
MULTIPLE=$(($i - 1))
TCP_PORT=$(($TCP_PORT_START + $MULTIPLE * $PORT_INCREMENT))
sed -i "s/\${TCP_PORT}/${TCP_PORT}/g" ${CONF_DIR}/${FILENAME}
HTTP_PORT=$(($HTTP_PORT_START + $MULTIPLE * $PORT_INCREMENT))
sed -i "s/\${HTTP_PORT}/${HTTP_PORT}/g" ${CONF_DIR}/${FILENAME}
WEBSOCKET_PORT=$(($WEBSOCKET_PORT_START + $MULTIPLE * $PORT_INCREMENT))
sed -i "s/\${WEBSOCKET_PORT}/${WEBSOCKET_PORT}/g" ${CONF_DIR}/${FILENAME}
UDP_PORT=$(($UDP_PORT_START + $MULTIPLE * $PORT_INCREMENT))
sed -i "s/\${UDP_PORT}/${UDP_PORT}/g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${INSTANCE}/${i}/g" ${CONF_DIR}/${FILENAME}
sed -i "s#\${SERVER_CERTIFICATE_FILE}#${SERVER_CERTIFICATE_FILE}#g" ${CONF_DIR}/${FILENAME}
sed -i "s#\${SERVER_KEY_FILE}#${SERVER_KEY_FILE}#g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${SERVER_KEY_PASSWORD}/${SERVER_KEY_PASSWORD}/g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${SERVER_USE_GENERATED_KEYPAIR}/${SERVER_USE_GENERATED_KEYPAIR}/g" ${CONF_DIR}/${FILENAME}
sed -i "s#\${CLIENT_KEY_FILE}#${CLIENT_KEY_FILE}#g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${CLIENT_KEY_TYPE}/${CLIENT_KEY_TYPE}/g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${CLIENT_KEY_PASSWORD}/${CLIENT_KEY_PASSWORD}/g" ${CONF_DIR}/${FILENAME}
sed -i "s#\${TRUST_STORE_PEM}#${TRUST_STORE_PEM}#g" ${CONF_DIR}/${FILENAME}
sed -i "s#\${TRUST_STORE_JKS}#${TRUST_STORE_JKS}#g" ${CONF_DIR}/${FILENAME}
sed -i "s/\${TRUST_STORE_PASSWORD}/${TRUST_STORE_PASSWORD}/g" ${CONF_DIR}/${FILENAME}
done
+23
View File
@@ -0,0 +1,23 @@
#!/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=$(cd $THIS_DIR/.. && pwd)
BIN_DIR=${BASE_DIR}/bin
. ${BIN_DIR}/timely-balancer-env.sh
if [ "$NUM_PROFILES" -eq "1" ]; then
echo ${BIN_DIR}/timely-balancer.sh
${BIN_DIR}/timely-balancer.sh
else
for i in $(seq 1 $NUM_PROFILES); do
echo ${BIN_DIR}/timely-balancer.sh $i
${BIN_DIR}/timely-balancer.sh $i
done
fi
+3
View File
@@ -0,0 +1,3 @@
#!/bin/bash
pkill -9 -f timely.balancer.Balancer
+24
View File
@@ -0,0 +1,24 @@
#!/bin/bash
NUM_PROFILES=1
ZOOKEEPERS=localhost:2181
TCP_PORT_START=4242
HTTP_PORT_START=4243
WEBSOCKET_PORT_START=4244
UDP_PORT_START=4245
PORT_INCREMENT=100
HOST_IP=0.0.0.0
HOST=$(hostname -s)
HOST_FQDN=$(hostname -f)
GENERATE_KEYS=true
TRUST_STORE_TYPE=
TRUST_STORE_FILE=
TRUST_STORE_PASSWORD=
KEY_STORE_TYPE=
KEY_STORE_FILE=
KEY_STORE_PASSWORD=
AUTHORIZATION_URL=
AUTHORIZATION_CERTIFICATE_TYPE=
AUTHORIZATION_CERTIFICATE_FILE=
+58 -27
View File
@@ -9,34 +9,65 @@ else
fi
THIS_DIR="${THIS_SCRIPT%/*}"
NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=${THIS_DIR}/..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
if [[ -e ${TMP_DIR} ]]; then
rm -rf ${TMP_DIR}
set -a
. ${THIS_DIR}/timely-balancer-env.sh
set +a
if [ -n "$1" ]; then
export PROFILE_NUM=$1
PROFILE_ARG="--spring.profiles.active=${PROFILE_NUM}"
PID=`jps -m | grep timely.balancer.Balancer | grep "spring.profiles.active=${PROFILE_NUM}" | awk '{print $1}'`
else
export PROFILE_NUM=""
PID=`jps -m | grep timely.balancer.Balancer | awk '{print $1}'`
fi
mkdir ${TMP_DIR}
if [[ -e ${NATIVE_DIR} ]]; then
rm -rf ${NATIVE_DIR}
if [ "$PID" == "" ]; then
NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=$(cd $THIS_DIR/.. && pwd)
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
export LOG_DIR="${BASE_DIR}/logs"
NUM_SERVER_THREADS=4
if [[ -e ${TMP_DIR} ]]; then
rm -rf ${TMP_DIR}
fi
mkdir ${TMP_DIR}
if [[ -e ${NATIVE_DIR} ]]; then
rm -rf ${NATIVE_DIR}
fi
mkdir -p ${NATIVE_DIR}
if [[ ! -e ${LOG_DIR} ]]; then
mkdir ${LOG_DIR}
fi
pushd ${BASE_DIR}/bin
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
popd
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="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
JVM_ARGS="${JVM_ARGS} -Xmx2G -Xms2G -XX:NewSize=1G -XX:MaxNewSize=1G"
JVM_ARGS="${JVM_ARGS} -XX:+UseCompressedOops -XX:+UseNUMA -Djava.net.preferIPv4Stack=true"
JVM_ARGS="${JVM_ARGS} -XX:+UseG1GC -XX:MaxGCPauseMillis=4000 -XX:ParallelGCThreads=20 -XX:+UseStringDeduplication"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely-balancer ${PROFILE_ARG}"
nohup $JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely-balancer ${PROFILE_ARG} >> ${LOG_DIR}/timely-balancer${PROFILE_NUM}-startup.log 2>&1 &
else
if [ "$PROFILE_NUM" == ""]; then
echo "timely-balancer already running with pid ${PID}"
else
echo "timely-balancer ${PROFILE_NUM} already running with pid ${PID}"
fi
fi
mkdir -p ${NATIVE_DIR}
pushd ${BASE_DIR}/bin
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
popd
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="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.balancer.Balancer --spring.config.name=timely --spring.profiles.active=balancer
+5 -11
View File
@@ -5,25 +5,19 @@
<Property name="LOG_PATTERN">%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%wEx</Property>
</Properties>
<Appenders>
<RollingRandomAccessFile name="Rolling" fileName="/srv/logs/timely/timely-balancer.log"
filePattern="/srv/logs/timely/$${date:yyyy-MM}/timely-balancer-%d{MM-dd-yyyy}-%i.log.gz">
<RollingRandomAccessFile name="Rolling" fileName="${env:LOG_DIR}/timely-balancer${env:PROFILE_NUM}.log"
filePattern="${env:LOG_DIR}/$${date:yyyy-MM}/timely-balancer${env:PROFILE_NUM}-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1024 MB" />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingRandomAccessFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace" includeLocation="false">
<AppenderRef ref="Console" level="warn" />
<AppenderRef ref="Rolling" level="trace" />
<Root level="warn" includeLocation="false">
<AppenderRef ref="Rolling"/>
</Root>
<Logger name="timely" additivity="false">
<AppenderRef ref="Console" level="debug" />
</Logger>
<Logger name="timely" level="debug"/>
</Loggers>
</Configuration>
+9
View File
@@ -0,0 +1,9 @@
trust.store.type=${TRUST_STORE_TYPE}
trust.store.file=${TRUST_STORE_FILE}
trust.store.password=${TRUST_STORE_PASSWORD}
key.store.type=${KEY_STORE_TYPE}
key.store.file=${KEY_STORE_FILE}
key.store.password=${KEY_STORE_PASSWORD}
authorization.url=${AUTHORIZATION_URL}
authorization.certificate.type=${AUTHORIZATION_CERTIFICATE_TYPE}
authorization.certificate.file=${AUTHORIZATION_CERTIFICATE_FILE}
+34 -41
View File
@@ -1,51 +1,44 @@
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:security.properties"/>
<beans:bean id="clientSsl" class="timely.configuration.ClientSsl">
<beans:property name="trustStoreType" value="${trust.store.type}"/>
<beans:property name="trustStoreFile" value="${trust.store.file}"/>
<beans:property name="trustStorePassword" value="${trust.store.password}"/>
<beans:property name="keyStoreType" value="${key.store.type}"/>
<beans:property name="keyStoreFile" value="${key.store.file}"/>
<beans:property name="keyStorePassword" value="${key.store.password}"/>
</beans:bean>
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
<beans:property name="users">
<util:list>
<beans:bean class="timely.auth.TimelyUser">
<beans:constructor-arg name="subjectDn">
<beans:value>CN=example.com</beans:value>
</beans:constructor-arg>
<beans:constructor-arg name="auths">
<util:list>
<beans:value>D</beans:value>
<beans:value>E</beans:value>
<beans:value>F</beans:value>
</util:list>
</beans:constructor-arg>
<beans:constructor-arg name="roles">
<util:list>
<beans:value>G</beans:value>
<beans:value>H</beans:value>
<beans:value>I</beans:value>
</util:list>
</beans:constructor-arg>
</beans:bean>
</util:list>
</beans:property>
<beans:bean id="authorizationProperties" class="timely.configuration.AuthorizationProperties">
<beans:property name="authorizationUrl" value="${authorization.url}"/>
<beans:property name="publicCertificateType" value="${authorization.certificate.type}"/>
<beans:property name="publicCertificateFile" value="${authorization.certificate.file}"/>
</beans:bean>
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
</beans:bean>
<user-service id="usernamePasswordService">
<user name="test" password="test1" authorities="A,B,C" />
</user-service>
<beans:bean id="remoteUserDetailsService" class="timely.auth.RemoteUserDetailsService">
<beans:constructor-arg ref="clientSsl"/>
<beans:constructor-arg ref="authorizationProperties"/>
</beans:bean>
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="remoteUserDetailsService" />
</beans:bean>
<user-service id="usernamePasswordService">
<user name="test" password="test1" authorities="A,B,C" />
</user-service>
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
@@ -53,7 +46,7 @@
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<!-- X509 PreAuthenticated authorization provider -->
<authentication-provider ref="x509AuthorizationProvider"/>
+136
View File
@@ -0,0 +1,136 @@
timely-balancer:
zooKeeper:
servers: ${ZOOKEEPERS}
timeout: 30s
checkServerHealthInterval: 10000
serverFailuresBeforeDown: 6
serverSuccessesBeforeUp: 1
assignmentFile: /timely/balancer/assignments.csv
security:
session-max-age: 86400
cache-expiration-minutes: 60
cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
serverSsl:
key-store-type: ${KEY_STORE_TYPE}
key-store-file: ${KEY_STORE_FILE}
key-store-password: ${KEY_STORE_PASSWORD}
use-generated-keypair: ${GENERATE_KEYS}
ignore-ssl-handshake-errors: true
trust-store-type: ${TRUST_STORE_TYPE}
trust-store-file: ${TRUST_STORE_FILE}
trust-store-password: ${TRUST_STORE_PASSWORD}
clientSsl:
key-store-type: ${KEY_STORE_TYPE}
key-store-file: ${KEY_STORE_FILE}
key-store-password: ${KEY_STORE_PASSWORD}
trust-store-type: ${TRUST_STORE_TYPE}
trust-store-file: ${TRUST_STORE_FILE}
trust-store-password: ${TRUST_STORE_PASSWORD}
jwtSsl:
key-store-type: ${AUTHORIZATION_CERTIFICATE_TYPE}
key-store-file: ${AUTHORIZATION_CERTIFICATE_FILE}
key-store-password: ${AUTHORIZATION_CERTIFICATE_PASSWORD}
cache:
enabled: false
metric-age-off-hours:
default: 26
nonCachedMetrics:
server:
ip: ${HOST_IP}
tcpPort: ${TCP_PORT}
numTcpPools: 1
tcpBufferSize: 100
tcpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
maxTotalPerKey: 40
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
testOnBorrow: false
testOnReturn: false
udpPort: ${UDP_PORT}
udpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
maxTotalPerKey: 40
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
testOnBorrow: false
testOnReturn: false
http:
host: ${HOST}
ip: ${HOST_IP}
port: ${HTTP_PORT}
redirect-path: /secure-me
strict-transport-max-age: 608400
cors:
allow-any-origin: false
allow-null-origin: false
allow-credentials: true
allowed-origins:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type
httpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
maxTotalPerKey: 40
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
testOnBorrow: false
testOnReturn: false
websocket:
ip: ${HOST_IP}
port: ${WEBSOCKET_PORT}
timeout: 60
subscription-lag: 120
scanner-batch-size: 10000
flush-interval-seconds: 30
scanner-read-ahead: 1
subscription-batch-size: 5000
incomingBufferSize: 50000000
wsClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
maxTotalPerKey: 40
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
testOnBorrow: false
testOnReturn: false
+19 -14
View File
@@ -13,27 +13,32 @@ timely-balancer:
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
serverSsl:
certificate-file:
key-file:
#key-password:
key-store-type:
key-store-file:
key-store-password:
use-generated-keypair: true
ignore-ssl-handshake-errors: true
trust-store-type:
trust-store-file:
use-openssl: true
#use-ciphers:
trust-store-password:
clientSsl:
keyFile:
keyType: pkcs12
#keyPassword:
trustStoreFile:
trustStoreType: jks
#trustStorePassword:
key-store-type:
key-store-file:
key-store-password:
trust-store-type:
trust-store-file:
trust-store-password:
jwtSsl:
key-store-type:
key-store-file:
key-store-password:
cache:
enabled: false
metric-age-off-hours:
default: 26
nonCachedMetrics:
server:
ip: 127.0.0.1
ip: 0.0.0.0
tcpPort: 4242
numTcpPools: 1
tcpBufferSize: 100
@@ -74,7 +79,7 @@ timely-balancer:
testOnReturn: false
http:
host: localhost
ip: 127.0.0.1
ip: 0.0.0.0
port: 4243
redirect-path: /secure-me
strict-transport-max-age: 608400
@@ -103,7 +108,7 @@ timely-balancer:
testOnBorrow: false
testOnReturn: false
websocket:
ip: 127.0.0.1
ip: 0.0.0.0
port: 5244
timeout: 60
subscription-lag: 120
+1 -23
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>timely-balancer</artifactId>
<dependencies>
@@ -51,12 +51,6 @@
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
@@ -97,21 +91,11 @@
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<exclusions>
<exclusion>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus</groupId>
<artifactId>tyrus-client</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
@@ -123,12 +107,6 @@
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
@@ -1,10 +1,10 @@
package timely.balancer;
import static org.apache.accumulo.core.conf.ConfigurationTypeHelper.getTimeInMillis;
import static timely.Server.SERVICE_DISCOVERY_PATH;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS;
import static timely.store.cache.DataStoreCache.NON_CACHED_METRICS_LOCK_PATH;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
@@ -16,6 +16,18 @@ import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryForever;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import io.netty.bootstrap.Bootstrap;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
@@ -51,33 +63,21 @@ import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.OpenSslServerSessionContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.NettyRuntime;
import io.netty.util.internal.SystemPropertyUtil;
import org.apache.accumulo.core.conf.AccumuloConfiguration;
import org.apache.curator.RetryPolicy;
import org.apache.curator.framework.CuratorFramework;
import org.apache.curator.framework.CuratorFrameworkFactory;
import org.apache.curator.retry.RetryForever;
import org.apache.zookeeper.CreateMode;
import org.apache.zookeeper.data.Stat;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.auth.AuthCache;
import timely.auth.JWTTokenHandler;
import timely.auth.util.SslHelper;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.configuration.ClientSsl;
import timely.balancer.configuration.SpringBootstrap;
import timely.balancer.connection.http.HttpClientPool;
import timely.balancer.connection.tcp.TcpClientPool;
import timely.balancer.connection.udp.UdpClientPool;
import timely.balancer.connection.ws.WsClientPool;
import timely.balancer.healthcheck.TcpHealthChecker;
import timely.balancer.healthcheck.HealthChecker;
import timely.balancer.netty.http.HttpRelayHandler;
import timely.balancer.netty.tcp.TcpRelayHandler;
import timely.balancer.netty.udp.UdpRelayHandler;
@@ -85,6 +85,7 @@ import timely.balancer.netty.ws.WsRelayHandler;
import timely.balancer.resolver.BalancedMetricResolver;
import timely.balancer.resolver.MetricResolver;
import timely.client.http.HttpClient;
import timely.configuration.ClientSsl;
import timely.configuration.ServerSsl;
import timely.netty.http.HttpStaticFileServerHandler;
import timely.netty.http.NonSslRedirectHandler;
@@ -133,12 +134,11 @@ public class Balancer {
protected volatile boolean shutdown = false;
private static final int DEFAULT_EVENT_LOOP_THREADS;
private String[] zkPaths = new String[] { LEADER_LATCH_PATH, ASSIGNMENTS_LAST_UPDATED_PATH, ASSIGNMENTS_LOCK_PATH,
SERVICE_DISCOVERY_PATH, NON_CACHED_METRICS, NON_CACHED_METRICS_LOCK_PATH };
private String[] zkPaths = new String[] {LEADER_LATCH_PATH, ASSIGNMENTS_LAST_UPDATED_PATH, ASSIGNMENTS_LOCK_PATH, SERVICE_DISCOVERY_PATH,
NON_CACHED_METRICS, NON_CACHED_METRICS_LOCK_PATH};
static {
DEFAULT_EVENT_LOOP_THREADS = Math.max(1,
SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt("io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));
}
public Balancer(BalancerConfiguration balancerConf) throws Exception {
@@ -257,7 +257,7 @@ public class Balancer {
if (this.metricResolver != null) {
try {
LOG.info("Closing metricResolver");
this.metricResolver.stop();
this.metricResolver.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
@@ -322,40 +322,21 @@ public class Balancer {
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else {
String cert = sslCfg.getCertificateFile();
String key = sslCfg.getKeyFile();
String keyPass = sslCfg.getKeyPassword();
if (null == cert || null == key) {
if (null == sslCfg.getKeyStoreFile()) {
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
}
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
ssl = SslHelper.getSslContextBuilder(sslCfg);
}
ssl.ciphers(sslCfg.getUseCiphers());
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
ssl.clientAuth(ClientAuth.OPTIONAL);
Boolean useOpenSSL = sslCfg.isUseOpenssl();
if (useOpenSSL) {
ssl.sslProvider(SslProvider.OPENSSL);
} else {
ssl.sslProvider(SslProvider.JDK);
}
String trustStore = sslCfg.getTrustStoreFile();
if (null != trustStore) {
if (!trustStore.isEmpty()) {
ssl.trustManager(new File(trustStore));
}
}
return ssl.build();
}
protected SSLContext createSSLClientContext(BalancerConfiguration config) throws Exception {
ClientSsl ssl = config.getSecurity().getClientSsl();
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(),
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(), ssl.getKeyStoreFile(),
ssl.getKeyStoreType(), ssl.getKeyStorePassword());
}
private void ensureZkPaths(CuratorFramework curatorFramework, String[] paths) {
@@ -363,8 +344,7 @@ public class Balancer {
try {
Stat stat = curatorFramework.checkExists().forPath(s);
if (stat == null) {
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath(s);
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(s);
}
} catch (Exception e) {
LOG.info(e.getMessage());
@@ -377,14 +357,13 @@ public class Balancer {
// start curator framework
RetryPolicy retryPolicy = new RetryForever(1000);
int timeout = Long.valueOf(AccumuloConfiguration.getTimeInMillis(balancerConfig.getZooKeeper().getTimeout()))
.intValue();
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), timeout, 10000,
retryPolicy);
int timeout = Long.valueOf(getTimeInMillis(balancerConfig.getZooKeeper().getTimeout())).intValue();
curatorFramework = CuratorFrameworkFactory.newClient(balancerConfig.getZooKeeper().getServers(), timeout, 10000, retryPolicy);
curatorFramework.start();
ensureZkPaths(curatorFramework, zkPaths);
AuthCache.configure(balancerConfig.getSecurity());
JWTTokenHandler.init(balancerConfig.getSecurity().getJwtSsl(), null);
final boolean useEpoll = useEpoll();
Class<? extends ServerSocketChannel> channelClass;
Class<? extends Channel> datagramChannelClass;
@@ -417,9 +396,8 @@ public class Balancer {
for (int x = 0; x < balancerConfig.getServer().getNumTcpPools(); x++) {
tcpClientPools.add(new TcpClientPool(this.balancerConfig));
}
TcpHealthChecker healthChecker = new TcpHealthChecker(this.balancerConfig, tcpClientPools.get(0));
HealthChecker healthChecker = new HealthChecker(this.balancerConfig, tcpClientPools.get(0));
this.metricResolver = new BalancedMetricResolver(curatorFramework, this.balancerConfig, healthChecker);
this.metricResolver.start();
final ServerBootstrap tcpServer = new ServerBootstrap();
tcpServer.group(tcpBossGroup, tcpWorkerGroup);
@@ -451,8 +429,7 @@ public class Balancer {
httpServer.group(httpBossGroup, httpWorkerGroup);
httpServer.channel(channelClass);
httpServer.handler(new LoggingHandler());
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig.getSecurity(), balancerConfig.getHttp(),
clientSSLContext);
HttpClientPool httpClientPool = new HttpClientPool(balancerConfig.getSecurity(), balancerConfig.getHttp(), clientSSLContext);
httpServer.childHandler(setupHttpChannel(balancerConfig, sslCtx, metricResolver, httpClientPool));
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
httpServer.option(ChannelOption.SO_BACKLOG, 128);
@@ -491,9 +468,8 @@ public class Balancer {
udpChannelHandleList.add(udpServer.bind(udpIp, udpPort).sync().channel());
}
shutdownHook();
LOG.info(
"Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
LOG.info("Server started. Listening on {}:{} for TCP traffic, {}:{} for HTTP traffic, {}:{} for WebSocket traffic, and {}:{} for UDP traffic",
tcpAddress, tcpPort, httpAddress, httpPort, wsAddress, wsPort, wsAddress, udpPort);
}
public static void main(String[] args) throws Exception {
@@ -518,8 +494,8 @@ public class Balancer {
}
}
protected ChannelHandler setupHttpChannel(BalancerConfiguration balancerConfig, SslContext sslCtx,
MetricResolver metricResolver, HttpClientPool httpClientPool) {
protected ChannelHandler setupHttpChannel(BalancerConfiguration balancerConfig, SslContext sslCtx, MetricResolver metricResolver,
HttpClientPool httpClientPool) {
return new ChannelInitializer<SocketChannel>() {
@@ -528,20 +504,18 @@ public class Balancer {
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(balancerConfig.getHttp(), sslCtx));
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
ch.pipeline().addLast("decoder", new HttpRequestDecoder(4096, 32768, 8192, true, 128));
ch.pipeline().addLast("compressor", new HttpContentCompressor());
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(
balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("login",
new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("queryDecoder", new timely.netty.http.HttpRequestDecoder(balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler()
.setIgnoreSslHandshakeErrors(balancerConfig.getSecurity().getServerSsl().isIgnoreSslHandshakeErrors()));
ch.pipeline().addLast("login", new X509LoginRequestHandler(balancerConfig.getSecurity(), balancerConfig.getHttp()));
ch.pipeline().addLast("httpRelay", new HttpRelayHandler(metricResolver, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler().setIgnoreSslHandshakeErrors(
balancerConfig.getSecurity().getServerSsl().isUseGeneratedKeypair()));
ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(balancerConfig.getSecurity().getServerSsl().isIgnoreSslHandshakeErrors()));
}
};
}
@@ -575,8 +549,7 @@ public class Balancer {
};
}
protected ChannelHandler setupWSChannel(BalancerConfiguration balancerConfig, SslContext sslCtx,
MetricResolver metricResolver, WsClientPool wsClientPool) {
protected ChannelHandler setupWSChannel(BalancerConfiguration balancerConfig, SslContext sslCtx, MetricResolver metricResolver, WsClientPool wsClientPool) {
return new ChannelInitializer<SocketChannel>() {
@Override
@@ -586,10 +559,8 @@ public class Balancer {
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(65536));
ch.pipeline().addLast("sessionExtractor", new WebSocketFullRequestHandler());
ch.pipeline().addLast("idle-handler",
new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol",
new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("idle-handler", new IdleStateHandler(balancerConfig.getWebsocket().getTimeout(), 0, 0));
ch.pipeline().addLast("ws-protocol", new WebSocketServerProtocolHandler(WS_PATH, null, true, 65536, false, true));
ch.pipeline().addLast("wsDecoder", new WebSocketRequestDecoder(balancerConfig.getSecurity()));
ch.pipeline().addLast("httpRelay", new WsRelayHandler(balancerConfig, metricResolver, wsClientPool));
ch.pipeline().addLast("error", new WSTimelyExceptionHandler());
@@ -6,6 +6,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import timely.configuration.Cache;
@Validated
@@ -49,16 +50,6 @@ public class BalancerConfiguration {
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() {
return zooKeeper;
}
@@ -130,44 +121,4 @@ public class BalancerConfiguration {
public void setServerSuccessesBeforeUp(int 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;
}
}
@@ -5,6 +5,7 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.apache.http.impl.client.CloseableHttpClient;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.configuration.Http;
public class BalancerHttp extends Http {
@@ -3,6 +3,8 @@ package timely.balancer.configuration;
import javax.validation.Valid;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.configuration.ClientSsl;
import timely.configuration.Security;
public class BalancerSecurity extends Security {
@@ -4,6 +4,7 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.tcp.TcpClient;
import timely.client.udp.UdpClient;
import timely.configuration.Server;
@@ -4,6 +4,7 @@ import javax.validation.Valid;
import org.apache.commons.pool2.impl.GenericKeyedObjectPoolConfig;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
import timely.configuration.Websocket;
@@ -1,77 +0,0 @@
package timely.balancer.configuration;
public class ClientSsl {
private String keyFile;
private String keyType;
private String keyPassword;
private String trustStoreFile;
private String trustStoreType;
private String trustStorePassword;
private boolean useClientCert = true;
private boolean hostVerificationEnabled = true;
public String getKeyFile() {
return keyFile;
}
public void setKeyFile(String keyFile) {
this.keyFile = keyFile;
}
public String getKeyType() {
return keyType;
}
public void setKeyType(String keyType) {
this.keyType = keyType;
}
public String getKeyPassword() {
return keyPassword;
}
public void setKeyPassword(String keyPassword) {
this.keyPassword = keyPassword;
}
public String getTrustStoreFile() {
return trustStoreFile;
}
public void setTrustStoreFile(String trustStoreFile) {
this.trustStoreFile = trustStoreFile;
}
public String getTrustStoreType() {
return trustStoreType;
}
public void setTrustStoreType(String trustStoreType) {
this.trustStoreType = trustStoreType;
}
public String getTrustStorePassword() {
return trustStorePassword;
}
public void setTrustStorePassword(String trustStorePassword) {
this.trustStorePassword = trustStorePassword;
}
public void setUseClientCert(boolean useClientCert) {
this.useClientCert = useClientCert;
}
public boolean isUseClientCert() {
return useClientCert;
}
public void setHostVerificationEnabled(boolean hostVerificationEnabled) {
this.hostVerificationEnabled = hostVerificationEnabled;
}
public boolean isHostVerificationEnabled() {
return hostVerificationEnabled;
}
}
@@ -2,6 +2,5 @@ package timely.balancer.configuration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
public class SpringBootstrap {
}
@SpringBootApplication(scanBasePackageClasses = {SpringBootstrap.class})
public class SpringBootstrap {}
@@ -5,6 +5,7 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.ArrivalRate;
import timely.balancer.configuration.BalancerConfiguration;
@@ -27,8 +28,7 @@ public class TimelyBalancedHost {
}
private TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
private TimelyBalancedHost(String host, int tcpPort, int httpPort, int wsPort, int udpPort, ArrivalRate arrivalRate) {
this.host = host;
this.tcpPort = tcpPort;
this.httpPort = httpPort;
@@ -37,15 +37,10 @@ public class TimelyBalancedHost {
this.arrivalRate = arrivalRate;
}
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort,
ArrivalRate arrivalRate) {
static public TimelyBalancedHost of(String host, int tcpPort, int httpPort, int wsPort, int udpPort, ArrivalRate arrivalRate) {
return new TimelyBalancedHost(host, tcpPort, httpPort, wsPort, udpPort, arrivalRate);
}
static public TimelyBalancedHost of(String host, int basePort) {
return new TimelyBalancedHost(host, basePort, basePort + 1, basePort + 2, basePort + 3, null);
}
public void setBalancerConfig(BalancerConfiguration balancerConfig) {
this.balancerConfig = balancerConfig;
}
@@ -60,8 +55,8 @@ public class TimelyBalancedHost {
int p = tcpPort;
synchronized (this) {
if (LOG.isTraceEnabled()) {
LOG.trace("success reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p,
isUp, (successes + 1), serverSuccessesBeforeUp);
LOG.trace("success reported host:{} port:{} isUp:{} successes:{} serverSuccessesBeforeUp:{}", h, p, isUp, (successes + 1),
serverSuccessesBeforeUp);
}
if (!isUp) {
if (++successes >= serverSuccessesBeforeUp) {
@@ -79,8 +74,8 @@ public class TimelyBalancedHost {
int p = tcpPort;
synchronized (this) {
if (LOG.isTraceEnabled()) {
LOG.trace("failure reported host:{} port:{} isUp:{} failures:{} serverFailuresBeforeUp:{}", h, p, isUp,
(failures + 1), serverFailuresBeforeDown);
LOG.trace("failure reported host:{} port:{} isUp:{} failures:{} serverFailuresBeforeUp:{}", h, p, isUp, (failures + 1),
serverFailuresBeforeDown);
}
if (isUp) {
if (++failures >= serverFailuresBeforeDown) {
@@ -184,11 +179,4 @@ public class TimelyBalancedHost {
builder.append(isUp);
return builder.toString();
}
public static String toStringShort(TimelyBalancedHost tbh) {
if (tbh == null) {
return null;
}
return tbh.host + ":" + tbh.tcpPort;
}
}
@@ -6,10 +6,11 @@ import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import org.apache.http.impl.client.CloseableHttpClient;
import timely.balancer.configuration.BalancerSecurity;
import timely.balancer.connection.TimelyBalancedHost;
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, CloseableHttpClient> {
public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost,CloseableHttpClient> {
private final SSLContext sslContext;
private final BalancerSecurity security;
@@ -22,8 +23,8 @@ public class HttpClientFactory implements KeyedPooledObjectFactory<TimelyBalance
@Override
public PooledObject<CloseableHttpClient> makeObject(TimelyBalancedHost k) throws Exception {
// disable cookie management because we are sharing connections
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, null,
security.getClientSsl().isHostVerificationEnabled(), security.getClientSsl().isUseClientCert()));
return new DefaultPooledObject<>(timely.client.http.HttpClient.get(this.sslContext, null, security.getClientSsl().isHostVerificationEnabled(),
security.getClientSsl().isUseClientCert()));
}
@Override
@@ -4,11 +4,12 @@ import javax.net.ssl.SSLContext;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import org.apache.http.impl.client.CloseableHttpClient;
import timely.balancer.configuration.BalancerHttp;
import timely.balancer.configuration.BalancerSecurity;
import timely.balancer.connection.TimelyBalancedHost;
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, CloseableHttpClient> {
public class HttpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,CloseableHttpClient> {
public HttpClientPool(BalancerSecurity balancerSecurity, BalancerHttp http, SSLContext sslContext) {
super(new HttpClientFactory(balancerSecurity, sslContext), http.getHttpClientPool());
@@ -5,10 +5,11 @@ import java.io.IOException;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.tcp.TcpClient;
public class TcpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, TcpClient> {
public class TcpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost,TcpClient> {
private int bufferSize;
@@ -1,13 +1,14 @@
package timely.balancer.connection.tcp;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.tcp.TcpClient;
public class TcpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, TcpClient> {
public class TcpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,TcpClient> {
public TcpClientPool(BalancerConfiguration config) {
super(new TcpClientFactory(config.getServer().getTcpBufferSize()), config.getServer().getTcpClientPool());
}
}
}
@@ -5,13 +5,13 @@ import java.io.IOException;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.udp.UdpClient;
public class UdpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, UdpClient> {
public class UdpClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost,UdpClient> {
public UdpClientFactory() {
}
public UdpClientFactory() {}
@Override
public PooledObject<UdpClient> makeObject(TimelyBalancedHost k) throws Exception {
@@ -1,11 +1,12 @@
package timely.balancer.connection.udp;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.udp.UdpClient;
public class UdpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, UdpClient> {
public class UdpClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,UdpClient> {
public UdpClientPool(BalancerConfiguration config) {
super(new UdpClientFactory(), config.getServer().getUdpClientPool());
@@ -5,11 +5,12 @@ import javax.net.ssl.SSLContext;
import org.apache.commons.pool2.KeyedPooledObjectFactory;
import org.apache.commons.pool2.PooledObject;
import org.apache.commons.pool2.impl.DefaultPooledObject;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost, WebSocketSubscriptionClient> {
public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedHost,WebSocketSubscriptionClient> {
private final SSLContext sslContext;
private BalancerConfiguration balancerConfig;
@@ -23,10 +24,9 @@ public class WsClientFactory implements KeyedPooledObjectFactory<TimelyBalancedH
public PooledObject<WebSocketSubscriptionClient> makeObject(TimelyBalancedHost k) throws Exception {
int bufferSize = balancerConfig.getWebsocket().getIncomingBufferSize();
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(),
k.getWsPort(), balancerConfig.getSecurity().getClientSsl().isUseClientCert(),
balancerConfig.isLoginRequired(), "", "",
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(), bufferSize);
WebSocketSubscriptionClient client = new WebSocketSubscriptionClient(sslContext, k.getHost(), k.getHttpPort(), k.getWsPort(),
balancerConfig.getSecurity().getClientSsl().isUseClientCert(), balancerConfig.isLoginRequired(), "", "",
balancerConfig.getSecurity().getClientSsl().isHostVerificationEnabled(), bufferSize);
return new DefaultPooledObject<>(client);
}
@@ -3,11 +3,12 @@ package timely.balancer.connection.ws;
import javax.net.ssl.SSLContext;
import org.apache.commons.pool2.impl.GenericKeyedObjectPool;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
public class WsClientPool extends GenericKeyedObjectPool<TimelyBalancedHost, WebSocketSubscriptionClient> {
public class WsClientPool extends GenericKeyedObjectPool<TimelyBalancedHost,WebSocketSubscriptionClient> {
public WsClientPool(BalancerConfiguration balancerConfig, SSLContext sslContext) {
super(new WsClientFactory(balancerConfig, sslContext), balancerConfig.getWebsocket().getWsClientPool());
@@ -1,12 +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 interface HealthChecker {
public class HealthChecker {
public void setTimelyHosts(List<TimelyBalancedHost> timelyHosts);
private static final Logger LOG = LoggerFactory.getLogger(HealthChecker.class);
private List<TimelyBalancedHost> timelyHosts = new ArrayList<>();
private ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final private TCPServerHealthCheck tcpServerHealthCheck;
public void close() throws Exception;
public HealthChecker(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);
}
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,62 +0,0 @@
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();
}
}
}
}
@@ -11,17 +11,6 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Multimap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;
import org.apache.http.Header;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
@@ -33,6 +22,19 @@ import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import com.google.common.collect.Multimap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;
import timely.api.request.AuthenticatedRequest;
import timely.api.request.MetricRequest;
import timely.api.request.timeseries.HttpRequest;
@@ -88,7 +90,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
}
client = httpClientPool.borrowObject(k);
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
Multimap<String,String> headers = HttpHeaderUtils.toMultimap(request.headers());
if (msg instanceof AuthenticatedRequest) {
AuthenticatedRequest authenticatedRequest = (AuthenticatedRequest) msg;
TimelyAuthenticationToken token = authenticatedRequest.getToken();
@@ -100,7 +102,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
}
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.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
}
@@ -138,25 +140,22 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
LOG.error(message, e);
}
}
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
e.getMessage(), e.getLocalizedMessage(), e));
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), e.getMessage(), e.getLocalizedMessage(), e));
return;
} finally {
if (request != null && request instanceof AutoCloseable) {
if (request != null) {
ReferenceCountUtil.release(request);
}
}
FullHttpResponse response = null;
if (relayedResponse == null) {
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
"Method not allowed", "Method not allowed"));
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(), "Method not allowed", "Method not allowed"));
} else {
ByteArrayOutputStream baos = new MyByteArrayOutputStream();
StreamUtils.copy(relayedResponse.getEntity().getContent(), baos);
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()),
Unpooled.copiedBuffer(baos.toByteArray()));
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()),
Unpooled.copiedBuffer(baos.toByteArray()));
for (Header h : relayedResponse.getAllHeaders()) {
response.headers().add(h.getName(), h.getValue());
}
@@ -177,8 +176,7 @@ public class HttpRelayHandler extends SimpleChannelInboundHandler<HttpRequest> i
static public class MyByteArrayOutputStream extends ByteArrayOutputStream {
public MyByteArrayOutputStream() {
}
public MyByteArrayOutputStream() {}
public MyByteArrayOutputStream(int size) {
super(size);
@@ -4,13 +4,14 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.request.MetricRequest;
import timely.api.request.TcpRequest;
import timely.balancer.connection.TimelyBalancedHost;
@@ -44,7 +45,7 @@ public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
LOG.trace("Received {}", msg);
try {
String line;
Pair<TimelyBalancedHost, TcpClient> keyClientPair = null;
Pair<TimelyBalancedHost,TcpClient> keyClientPair = null;
try {
if (msg instanceof MetricRequest) {
String metricName = ((MetricRequest) msg).getMetric().getName();
@@ -64,22 +65,20 @@ public class TcpRelayHandler extends SimpleChannelInboundHandler<TcpRequest> {
} catch (IOException e) {
LOG.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(
Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
private Pair<TimelyBalancedHost, TcpClient> getClient(String metric, boolean metricRequest) {
private Pair<TimelyBalancedHost,TcpClient> getClient(String metric, boolean metricRequest) {
TcpClient client = null;
TimelyBalancedHost k = null;
int failures = 0;
while (client == null) {
try {
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric)
: metricResolver.getHostPortKey(metric);
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver.getHostPortKey(metric);
client = tcpClientPool().borrowObject(k);
} catch (Exception e1) {
failures++;
@@ -2,13 +2,14 @@ package timely.balancer.netty.udp;
import java.nio.charset.StandardCharsets;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.apache.commons.lang3.tuple.Pair;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.request.MetricRequest;
import timely.api.request.UdpRequest;
import timely.balancer.connection.TimelyBalancedHost;
@@ -37,7 +38,7 @@ public class UdpRelayHandler extends SimpleChannelInboundHandler<UdpRequest> {
try {
MetricRequest metricRequest = (MetricRequest) msg;
String metricName = metricRequest.getMetric().getName();
Pair<TimelyBalancedHost, UdpClient> keyClientPair = null;
Pair<TimelyBalancedHost,UdpClient> keyClientPair = null;
try {
keyClientPair = getClient(metricName, true);
keyClientPair.getRight().write(metricRequest.getLine() + "\n");
@@ -50,22 +51,20 @@ public class UdpRelayHandler extends SimpleChannelInboundHandler<UdpRequest> {
} catch (Exception e) {
LOG.error(LOG_ERR_MSG, msg, e);
ChannelFuture cf = ctx.writeAndFlush(
Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
ChannelFuture cf = ctx.writeAndFlush(Unpooled.copiedBuffer((ERR_MSG + e.getMessage() + "\n").getBytes(StandardCharsets.UTF_8)));
if (!cf.isSuccess()) {
LOG.error(Constants.ERR_WRITING_RESPONSE, cf.cause());
}
}
}
private Pair<TimelyBalancedHost, UdpClient> getClient(String metric, boolean metricRequest) {
private Pair<TimelyBalancedHost,UdpClient> getClient(String metric, boolean metricRequest) {
UdpClient client = null;
TimelyBalancedHost k = null;
int failures = 0;
while (client == null) {
try {
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric)
: metricResolver.getHostPortKey(metric);
k = (metricRequest == true) ? metricResolver.getHostPortKeyIngest(metric) : metricResolver.getHostPortKey(metric);
client = udpClientPool.borrowObject(k);
} catch (Exception e1) {
failures++;
@@ -9,15 +9,17 @@ import javax.websocket.CloseReason;
import javax.websocket.EndpointConfig;
import javax.websocket.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.websocketx.PingWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.concurrent.ScheduledFuture;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.response.TimelyException;
import timely.auth.util.ProxiedEntityUtils;
import timely.client.websocket.ClientHandler;
@@ -40,16 +42,16 @@ public class WsClientHandler extends ClientHandler {
}
@Override
public void beforeRequest(Map<String, List<String>> headers) {
public void beforeRequest(Map<String,List<String>> headers) {
if (token.getClientCert() != null) {
Multimap<String, String> proxyRequestHeaders = HashMultimap.create();
Multimap<String,String> proxyRequestHeaders = HashMultimap.create();
ProxiedEntityUtils.addProxyHeaders(proxyRequestHeaders, token.getClientCert());
for (String s : proxyRequestHeaders.keySet()) {
headers.put(s, new ArrayList<>(proxyRequestHeaders.get(s)));
}
}
Multimap<String, String> originalRequestHeaders = token.getHttpHeaders();
Multimap<String,String> originalRequestHeaders = token.getHttpHeaders();
for (String s : originalRequestHeaders.keySet()) {
if (!headers.containsKey(s)) {
// add pre-existing values if key does not exist in proxyRequest
@@ -71,8 +73,7 @@ public class WsClientHandler extends ClientHandler {
super.onClose(session, reason);
if (!reason.getCloseCode().equals(CloseReason.CloseCodes.NORMAL_CLOSURE)) {
LOG.error("Abnormal close: " + reason.getReasonPhrase());
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), reason.getReasonPhrase(),
"");
Exception e = new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(), reason.getReasonPhrase(), "");
WsRelayHandler.sendErrorResponse(ctx, HttpResponseStatus.INTERNAL_SERVER_ERROR, e);
}
if (!ping.isCancelled()) {
@@ -4,6 +4,7 @@ import java.io.IOException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.ws.WsClientPool;
import timely.client.websocket.subscription.WebSocketSubscriptionClient;
@@ -5,11 +5,15 @@ import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.google.common.collect.Multimap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
@@ -17,8 +21,6 @@ import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.request.subscription.AddSubscription;
import timely.api.request.subscription.CloseSubscription;
import timely.api.request.subscription.CreateSubscription;
@@ -41,8 +43,8 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
private final WsClientPool wsClientPool;
private MetricResolver metricResolver;
static private ObjectMapper mapper;
Map<String, Map<String, WsClientHolder>> wsClients = new ConcurrentHashMap<>();
Map<String, ChannelHandlerContext> wsSubscriptions = new ConcurrentHashMap<>();
Map<String,Map<String,WsClientHolder>> wsClients = new ConcurrentHashMap<>();
Map<String,ChannelHandlerContext> wsSubscriptions = new ConcurrentHashMap<>();
static {
mapper = new ObjectMapper();
@@ -52,8 +54,7 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
mapper.configure(SerializationFeature.WRAP_ROOT_VALUE, true);
}
public WsRelayHandler(BalancerConfiguration balancerConfig, MetricResolver metricResolver,
WsClientPool wsClientPool) {
public WsRelayHandler(BalancerConfiguration balancerConfig, MetricResolver metricResolver, WsClientPool wsClientPool) {
this.balancerConfig = balancerConfig;
this.metricResolver = metricResolver;
this.wsClientPool = wsClientPool;
@@ -67,7 +68,7 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
try {
String metric = null;
Multimap<String, String> headers = msg.getRequestHeaders();
Multimap<String,String> headers = msg.getRequestHeaders();
ProxiedEntityUtils.addProxyHeaders(headers, msg.getToken().getClientCert());
if (msg instanceof CreateSubscription) {
@@ -83,14 +84,13 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
@Override
public void operationComplete(ChannelFuture future) throws Exception {
Map<String, WsClientHolder> metricToClientMap = wsClients.get(currentSubscriptionId);
Map<String,WsClientHolder> metricToClientMap = wsClients.get(currentSubscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
// close all clients for this subscriptionId
LOG.info("Channel closed, closing subscriptions for subscriptionId:%s",
currentSubscriptionId);
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
LOG.debug("Channel closed, closing subscriptions for subscriptionId:{}", currentSubscriptionId);
for (Map.Entry<String,WsClientHolder> entry : metricToClientMap.entrySet()) {
entry.getValue().close(wsClientPool);
}
wsClients.remove(currentSubscriptionId);
@@ -108,12 +108,10 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
if (origContext == null) {
// send error because user never sent a CreateSubscription
// and we have nowhere to send the results
LOG.info("ChannelHandlerContext not found for subscriptionId:%s - createSubscription not called?",
subscriptionId);
sendErrorResponse(ctx, HttpResponseStatus.BAD_REQUEST,
new IllegalArgumentException("Must call create first"));
LOG.info("ChannelHandlerContext not found for subscriptionId:{} - createSubscription not called?", subscriptionId);
sendErrorResponse(ctx, HttpResponseStatus.BAD_REQUEST, new IllegalArgumentException("Must call create first"));
}
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
Map<String,WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap == null) {
metricToClientMap = new HashMap<>();
wsClients.put(subscriptionId, metricToClientMap);
@@ -124,14 +122,13 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
if (hostClientHolder == null) {
k = metricResolver.getHostPortKey(metric);
client = wsClientPool.borrowObject(k);
client.open(new WsClientHandler(origContext, add.getToken(),
(balancerConfig.getWebsocket().getTimeout() / 2)));
client.open(new WsClientHandler(origContext, add.getToken(), (balancerConfig.getWebsocket().getTimeout() / 2)));
metricToClientMap.put(metric, new WsClientHolder(k, client));
} else {
client = hostClientHolder.getClient();
}
}
Map<String, String> tags = null;
Map<String,String> tags = null;
Long startTime = 0L;
Long endTime = 0L;
@@ -155,25 +152,25 @@ public class WsRelayHandler extends SimpleChannelInboundHandler<SubscriptionRequ
RemoveSubscription remove = (RemoveSubscription) msg;
metric = remove.getMetric();
subscriptionId = remove.getSubscriptionId();
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
Map<String,WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
WsClientHolder hostClientHolder = metricToClientMap.get(metric);
if (hostClientHolder != null) {
hostClientHolder.getClient().removeSubscription(metric);
} else {
LOG.info("client not found for subscriptionId:%s metric:%s", subscriptionId, metric);
LOG.info("client not found for subscriptionId:{} metric:{}", subscriptionId, metric);
}
}
}
} else if (msg instanceof CloseSubscription) {
CloseSubscription close = (CloseSubscription) msg;
subscriptionId = close.getSubscriptionId();
Map<String, WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
Map<String,WsClientHolder> metricToClientMap = wsClients.get(subscriptionId);
if (metricToClientMap != null) {
synchronized (metricToClientMap) {
// close all clients for this subscriptionId
for (Map.Entry<String, WsClientHolder> entry : metricToClientMap.entrySet()) {
for (Map.Entry<String,WsClientHolder> entry : metricToClientMap.entrySet()) {
entry.getValue().close(wsClientPool);
}
}
@@ -4,14 +4,11 @@ import static timely.Server.SERVICE_DISCOVERY_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LAST_UPDATED_PATH;
import static timely.balancer.Balancer.ASSIGNMENTS_LOCK_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_LOCK_PATH;
import java.io.FileNotFoundException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
@@ -32,9 +29,10 @@ import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.SerializationUtils;
import org.apache.commons.lang3.StringUtils;
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.cache.TreeCache;
import org.apache.curator.framework.recipes.cache.TreeCacheEvent;
@@ -49,31 +47,33 @@ 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.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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.csvreader.CsvReader;
import com.csvreader.CsvWriter;
import timely.ServerDetails;
import timely.balancer.ArrivalRate;
import timely.balancer.configuration.BalancerConfiguration;
import timely.balancer.connection.TimelyBalancedHost;
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 {
private static final Logger LOG = LoggerFactory.getLogger(BalancedMetricResolver.class);
private Map<String, TimelyBalancedHost> metricToHostMap = new TreeMap<>();
private Set<String> unassignedMetrics = new HashSet<>();
private Map<String, ArrivalRate> metricMap = new HashMap<>();
private Map<String,TimelyBalancedHost> metricToHostMap = new TreeMap<>();
private Map<String,ArrivalRate> metricMap = new HashMap<>();
private ReentrantReadWriteLock balancerLock = new ReentrantReadWriteLock();
private List<TimelyBalancedHost> serverList = new ArrayList<>();
private Random r = new Random();
private final HealthChecker healthChecker;
final private HealthChecker healthChecker;
private ScheduledExecutorService assignmentExecutor = Executors.newScheduledThreadPool(2);
private ScheduledExecutorService arrivalRateExecutor = Executors.newScheduledThreadPool(2);
private int roundRobinCounter = 0;
@@ -81,46 +81,36 @@ public class BalancedMetricResolver implements MetricResolver {
private DistributedAtomicValue nonCachedMetricsIP;
private ReentrantReadWriteLock nonCachedMetricsLocalLock = new ReentrantReadWriteLock();
private InterProcessReadWriteLock nonCachedMetricsIPRWLock;
private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0);
private BalancerConfiguration balancerConfig;
private LeaderLatch leaderLatch;
private AtomicBoolean isLeader = new AtomicBoolean(false);
private InterProcessReadWriteLock assignmentsIPRWLock;
private MetricAssignmentPerister assignmentPerister;
private DistributedAtomicLong assignmentsLastUpdatedInHdfs;
private AtomicLong assignmentsLastUpdatedLocal = new AtomicLong(0);
final private BalancerConfiguration balancerConfig;
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;
private enum BalanceType {
HIGH_LOW, HIGH_AVG, AVG_LOW
}
public BalancedMetricResolver(CuratorFramework curatorFramework, BalancerConfiguration balancerConfig,
HealthChecker healthChecker) {
this.curatorFramework = curatorFramework;
public BalancedMetricResolver(CuratorFramework curatorFramework, BalancerConfiguration balancerConfig, HealthChecker healthChecker) {
this.balancerConfig = balancerConfig;
this.healthChecker = healthChecker;
}
public void start() {
assignmentsIPRWLock = new InterProcessReadWriteLock(curatorFramework, ASSIGNMENTS_LOCK_PATH);
testIPRWLock(curatorFramework, assignmentsIPRWLock, ASSIGNMENTS_LOCK_PATH);
this.assignmentPerister = new MetricAssignmentPerister(this, balancerConfig, metricToHostMap, nonCachedMetrics,
curatorFramework, assignmentsIPRWLock, nonCachedMetricsLocalLock, assignmentsLastUpdatedLocal,
balancerLock);
startLeaderLatch(curatorFramework);
startServiceListener(curatorFramework);
assignmentsLastUpdatedInHdfs = new DistributedAtomicLong(curatorFramework, ASSIGNMENTS_LAST_UPDATED_PATH, new RetryForever(1000));
TreeCacheListener assignmentListener = (curatorFramework1, event) -> {
LOG.debug("Handling assignments event {}. assignmentsLastUpdated:{}", event.getType().toString(),
new Date(assignmentPerister.getAssignmentsLastUpdatedByLeader()));
if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
assignmentPerister.readAssignments(true);
TreeCacheListener assignmentListener = new TreeCacheListener() {
@Override
public void childEvent(CuratorFramework curatorFramework, TreeCacheEvent event) throws Exception {
LOG.debug("Handling assignments event {}. assignmentsLastUpdatedInHdfs:{}", event.getType().toString(),
new Date(assignmentsLastUpdatedInHdfs.get().postValue()));
if (event.getType().equals(TreeCacheEvent.Type.NODE_UPDATED)) {
readAssignmentsFromHdfs(true);
}
}
};
@@ -160,7 +150,7 @@ public class BalancedMetricResolver implements MetricResolver {
}
readNonCachedMetricsIP();
assignmentPerister.readAssignments(false);
readAssignmentsFromHdfs(false);
assignmentExecutor.scheduleAtFixedRate(() -> {
if (isLeader.get()) {
@@ -170,28 +160,28 @@ public class BalancedMetricResolver implements MetricResolver {
LOG.error(e.getMessage(), e);
}
}
}, balancerConfig.getBalanceDelay(), balancerConfig.getBalancePeriod(), TimeUnit.MILLISECONDS);
}, 15, 15, TimeUnit.MINUTES);
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();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (lastLocalUpdate > lastHdfsUpdate) {
LOG.debug("Leader writing assignments to hdfs lastLocalUpdate ({}) > lastHdfsUpdate ({})", new Date(lastLocalUpdate),
new Date(lastHdfsUpdate));
writeAssignmentsToHdfs();
} else {
LOG.trace("Leader not writing assignments lastLocalUpdate ({}) <= lastUpdateByLeader ({})",
new Date(lastLocalUpdate), new Date(lastUpdateByLeader));
LOG.trace("Leader not writing assignments to hdfs lastLocalUpdate ({}) <= lastHdfsUpdate ({})", new Date(lastLocalUpdate),
new Date(lastHdfsUpdate));
}
} else {
assignmentPerister.readAssignments(true);
readAssignmentsFromHdfs(true);
}
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
}, balancerConfig.getPersistDelay(), balancerConfig.getPersistPeriod(), TimeUnit.MILLISECONDS);
}, 10, 60, TimeUnit.SECONDS);
}
private void readNonCachedMetricsIP() {
@@ -205,8 +195,7 @@ public class BalancedMetricResolver implements MetricResolver {
byte[] currentNonCachedMetricsDistributedBytes = nonCachedMetricsIP.get().postValue();
if (currentNonCachedMetricsDistributedBytes != null) {
try {
currentNonCachedMetricsDistributed = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes);
currentNonCachedMetricsDistributed = SerializationUtils.deserialize(currentNonCachedMetricsDistributedBytes);
} catch (Exception e) {
LOG.error(e.getMessage(), e);
currentNonCachedMetricsDistributed = new TreeSet<>();
@@ -249,7 +238,7 @@ public class BalancedMetricResolver implements MetricResolver {
public void isLeader() {
LOG.info("this balancer is the leader");
isLeader.set(true);
assignmentPerister.writeAssignments();
writeAssignmentsToHdfs();
}
@Override
@@ -265,8 +254,8 @@ public class BalancedMetricResolver implements MetricResolver {
private void startServiceListener(CuratorFramework curatorFramework) {
try {
ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class)
.client(curatorFramework).basePath(SERVICE_DISCOVERY_PATH).build();
ServiceDiscovery<ServerDetails> discovery = ServiceDiscoveryBuilder.builder(ServerDetails.class).client(curatorFramework)
.basePath(SERVICE_DISCOVERY_PATH).build();
discovery.start();
Collection<ServiceInstance<ServerDetails>> instances = discovery.queryForInstances("timely-server");
@@ -274,20 +263,19 @@ public class BalancedMetricResolver implements MetricResolver {
try {
for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(),
pl.getWsPort(), pl.getUdpPort(), new ArrivalRate(arrivalRateExecutor));
LOG.info("adding service {} host:{}", si.getId(), TimelyBalancedHost.toStringShort(tbh));
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
LOG.info("adding service {} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", si.getId(), pl.getHost(), pl.getTcpPort(),
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort());
tbh.setBalancerConfig(balancerConfig);
serverList.add(tbh);
metricHostEvent(tbh, MetricHostEvent.ActionType.ADDED);
}
healthChecker.setTimelyHosts(serverList);
} finally {
balancerLock.writeLock().unlock();
}
final ServiceCache<ServerDetails> serviceCache = discovery.serviceCacheBuilder().name("timely-server")
.build();
final ServiceCache<ServerDetails> serviceCache = discovery.serviceCacheBuilder().name("timely-server").build();
ServiceCacheListener listener = new ServiceCacheListener() {
@Override
@@ -299,9 +287,8 @@ public class BalancedMetricResolver implements MetricResolver {
Set<TimelyBalancedHost> availableHosts = new HashSet<>();
for (ServiceInstance<ServerDetails> si : instances) {
ServerDetails pl = (ServerDetails) si.getPayload();
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(),
pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
TimelyBalancedHost tbh = TimelyBalancedHost.of(pl.getHost(), pl.getTcpPort(), pl.getHttpPort(), pl.getWsPort(), pl.getUdpPort(),
new ArrivalRate(arrivalRateExecutor));
tbh.setBalancerConfig(balancerConfig);
availableHosts.add(tbh);
}
@@ -315,9 +302,9 @@ public class BalancedMetricResolver implements MetricResolver {
availableHosts.remove(h);
} else {
itr.remove();
metricHostEvent(h, MetricHostEvent.ActionType.REMOVED);
LOG.info("removing service {}", TimelyBalancedHost.toStringShort(h));
for (Map.Entry<String, TimelyBalancedHost> e : metricToHostMap.entrySet()) {
LOG.info("removing service {}:{} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", h.getHost(), h.getTcpPort(), h.getHost(),
h.getTcpPort(), h.getHttpPort(), h.getWsPort(), h.getUdpPort());
for (Map.Entry<String,TimelyBalancedHost> e : metricToHostMap.entrySet()) {
if (e.getValue().equals(h)) {
reassignMetrics.add(e.getKey());
}
@@ -328,22 +315,18 @@ public class BalancedMetricResolver implements MetricResolver {
// add new hosts that were not previously known
for (TimelyBalancedHost h : availableHosts) {
LOG.info("adding service {}", TimelyBalancedHost.toStringShort(h));
LOG.info("adding service {}:{} host:{} tcpPort:{} httpPort:{} wsPort:{} udpPort:{}", h.getHost(), h.getTcpPort(), h.getHost(),
h.getTcpPort(), h.getHttpPort(), h.getWsPort(), h.getUdpPort());
serverList.add(h);
metricHostEvent(h, MetricHostEvent.ActionType.ADDED);
rebalanceNeeded = true;
}
healthChecker.setTimelyHosts(serverList);
if (!availableHosts.isEmpty()) {
reassignMetrics.addAll(unassignedMetrics);
unassignedMetrics.clear();
}
if (isLeader.get()) {
for (String s : reassignMetrics) {
TimelyBalancedHost h = getRoundRobinHost(null);
assignMetric(s, h, HOST_REMOVED);
LOG.debug("Assigned server removed. Assigning {} to server {}", s,
TimelyBalancedHost.toStringShort(h));
assignMetric(s, h);
LOG.debug("Assigned server removed. Assigning {} to server {}:{}", s, h.getTcpPort());
}
}
@@ -354,7 +337,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (rebalanceNeeded) {
balance();
}
assignmentPerister.writeAssignments();
writeAssignmentsToHdfs();
}
}
@@ -376,12 +359,12 @@ public class BalancedMetricResolver implements MetricResolver {
TimelyBalancedHost tbh = null;
balancerLock.readLock().lock();
try {
Map<Double, TimelyBalancedHost> rateSortedHosts = new TreeMap<>();
Map<Double,TimelyBalancedHost> rateSortedHosts = new TreeMap<>();
for (TimelyBalancedHost s : serverList) {
rateSortedHosts.put(s.getArrivalRate(), s);
}
Iterator<Map.Entry<Double, TimelyBalancedHost>> itr = rateSortedHosts.entrySet().iterator();
Iterator<Map.Entry<Double,TimelyBalancedHost>> itr = rateSortedHosts.entrySet().iterator();
while (itr.hasNext() && tbh == null) {
TimelyBalancedHost currentTBH = itr.next().getValue();
@@ -414,7 +397,7 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh;
}
protected TimelyBalancedHost getRoundRobinHost(TimelyBalancedHost notThisOne) {
private TimelyBalancedHost getRoundRobinHost(TimelyBalancedHost notThisOne) {
TimelyBalancedHost tbh = null;
balancerLock.readLock().lock();
try {
@@ -446,11 +429,11 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh;
}
private TimelyBalancedHost chooseHost(Set<TimelyBalancedHost> potentialHosts,
Map<TimelyBalancedHost, Double> calculatedRates, double referenceRate, BalanceType balanceType) {
private TimelyBalancedHost chooseHost(Set<TimelyBalancedHost> potentialHosts, Map<TimelyBalancedHost,Double> calculatedRates, double referenceRate,
BalanceType balanceType) {
TimelyBalancedHost tbh = null;
Map<Long, TimelyBalancedHost> weightedList = new TreeMap<>();
Map<Long,TimelyBalancedHost> weightedList = new TreeMap<>();
long cumulativeWeight = 0;
for (TimelyBalancedHost h : potentialHosts) {
double currentDiff;
@@ -465,7 +448,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (cumulativeWeight > 0) {
long randomWeight = r.nextLong() % cumulativeWeight;
for (Map.Entry<Long, TimelyBalancedHost> e : weightedList.entrySet()) {
for (Map.Entry<Long,TimelyBalancedHost> e : weightedList.entrySet()) {
if (randomWeight <= e.getKey()) {
tbh = e.getValue();
break;
@@ -482,7 +465,7 @@ public class BalancedMetricResolver implements MetricResolver {
double controlBandPercentage = balancerConfig.getControlBandPercentage();
// save current rates so that we can modify
double totalArrivalRate = 0;
Map<TimelyBalancedHost, Double> calculatedRates = new HashMap<>();
Map<TimelyBalancedHost,Double> calculatedRates = new HashMap<>();
for (TimelyBalancedHost h : serverList) {
double tbhArrivalRate = h.getArrivalRate();
calculatedRates.put(h, tbhArrivalRate);
@@ -514,13 +497,11 @@ public class BalancedMetricResolver implements MetricResolver {
LOG.debug("Host's arrival rates are within {} of the average", controlBandPercentage);
} else if (!highHosts.isEmpty() && !lowHosts.isEmpty()) {
LOG.debug("begin rebalancing {}", BalanceType.HIGH_LOW);
numReassigned = rebalance(highHosts, lowHosts, calculatedRates, averageArrivalRate,
BalanceType.HIGH_LOW);
numReassigned = rebalance(highHosts, lowHosts, calculatedRates, averageArrivalRate, BalanceType.HIGH_LOW);
LOG.debug("end rebalancing {} - reassigned {}", BalanceType.HIGH_LOW, numReassigned);
} else if (lowHosts.isEmpty()) {
LOG.debug("begin rebalancing {}", BalanceType.HIGH_AVG);
numReassigned = rebalance(highHosts, avgHosts, calculatedRates, averageArrivalRate,
BalanceType.HIGH_AVG);
numReassigned = rebalance(highHosts, avgHosts, calculatedRates, averageArrivalRate, BalanceType.HIGH_AVG);
LOG.debug("end rebalancing {} - reassigned {}", BalanceType.HIGH_AVG, numReassigned);
} else {
LOG.debug("begin rebalancing {}", BalanceType.AVG_LOW);
@@ -531,16 +512,14 @@ public class BalancedMetricResolver implements MetricResolver {
return numReassigned;
}
public int rebalance(Set<TimelyBalancedHost> losingHosts, Set<TimelyBalancedHost> gainingHosts,
Map<TimelyBalancedHost, Double> calculatedRates, double targetArrivalRate, BalanceType balanceType) {
public int rebalance(Set<TimelyBalancedHost> losingHosts, Set<TimelyBalancedHost> gainingHosts, Map<TimelyBalancedHost,Double> calculatedRates,
double targetArrivalRate, BalanceType balanceType) {
int numReassigned = 0;
if (isLeader.get()) {
metricBalanceEvent(MetricBalanceEvent.ProgressType.BEGIN, balanceType, 0);
balancerLock.writeLock().lock();
try {
metricBalanceEvent(MetricBalanceEvent.ProgressType.BALANCER_LOCK_ACQUIRED, balanceType, 0);
Map<String, ArrivalRate> tempMetricMap = new HashMap<>();
Map<String,ArrivalRate> tempMetricMap = new HashMap<>();
tempMetricMap.putAll(metricMap);
Set<TimelyBalancedHost> focusedHosts;
@@ -562,21 +541,22 @@ public class BalancedMetricResolver implements MetricResolver {
}
// sort metrics by rate
Map<Double, String> rateSortedMetrics = new TreeMap<>(Collections.reverseOrder());
for (Map.Entry<String, ArrivalRate> e : tempMetricMap.entrySet()) {
if (metricToHostMap.get(e.getKey()).equals(h)) {
Map<Double,String> rateSortedMetrics = new TreeMap<>(Collections.reverseOrder());
for (Map.Entry<String,ArrivalRate> e : tempMetricMap.entrySet()) {
TimelyBalancedHost tbh = metricToHostMap.get(e.getKey());
if (tbh != null && tbh.equals(h)) {
rateSortedMetrics.put(e.getValue().getRate(), e.getKey());
}
}
LOG.trace("focusHost {} desiredChange:{} rateSortedMetrics.size():{}",
TimelyBalancedHost.toStringShort(h), desiredChange, rateSortedMetrics.size());
LOG.trace("focusHost {}:{} desiredChange:{} rateSortedMetrics.size():{}", h.getHost(), h.getTcpPort(), desiredChange,
rateSortedMetrics.size());
boolean doneWithHost = false;
while (!doneWithHost) {
Iterator<Map.Entry<Double, String>> itr = rateSortedMetrics.entrySet().iterator();
Iterator<Map.Entry<Double,String>> itr = rateSortedMetrics.entrySet().iterator();
while (!doneWithHost && itr.hasNext()) {
Map.Entry<Double, String> e = null;
Map.Entry<Double,String> e = null;
// find largest metric that does not exceed desired change
while (e == null && itr.hasNext()) {
e = itr.next();
@@ -591,15 +571,14 @@ public class BalancedMetricResolver implements MetricResolver {
doneWithHost = true;
} else {
LOG.trace("Selected:{} rate:{}", e.getValue(), e.getKey());
TimelyBalancedHost candidateHost = chooseHost(selectFromHosts, calculatedRates,
calculatedRates.get(h), balanceType);
TimelyBalancedHost candidateHost = chooseHost(selectFromHosts, calculatedRates, calculatedRates.get(h), balanceType);
if (candidateHost == null) {
LOG.trace("candidate host is null");
doneWithHost = true;
} else {
String metric = e.getValue();
Double metricRate = e.getKey();
assignMetric(metric, candidateHost, REBALANCE);
assignMetric(metric, candidateHost);
numReassigned++;
calculatedRates.put(candidateHost, calculatedRates.get(candidateHost) + metricRate);
calculatedRates.put(h, calculatedRates.get(h) - metricRate);
@@ -607,10 +586,8 @@ public class BalancedMetricResolver implements MetricResolver {
// don't move this metric again this host or balance
itr.remove();
tempMetricMap.remove(metric);
LOG.info(
"rebalancing: reassigning metric:{} rate:{} from server {} to {} remaining delta {}",
metric, metricRate, TimelyBalancedHost.toStringShort(h),
TimelyBalancedHost.toStringShort(candidateHost), desiredChange);
LOG.info("rebalancing: reassigning metric:{} rate:{} from server {}:{} to {}:{} remaining delta {}", metric, metricRate,
h.getHost(), h.getTcpPort(), candidateHost.getHost(), candidateHost.getTcpPort(), desiredChange);
}
}
}
@@ -633,7 +610,6 @@ public class BalancedMetricResolver implements MetricResolver {
}
} finally {
balancerLock.writeLock().unlock();
metricBalanceEvent(MetricBalanceEvent.ProgressType.END, balanceType, numReassigned);
}
}
return numReassigned;
@@ -660,8 +636,7 @@ public class BalancedMetricResolver implements MetricResolver {
if (currentNonCachedMetricsDistributedBytes == null) {
currentNonCachedMetricsIP = new TreeSet<>();
} else {
currentNonCachedMetricsIP = SerializationUtils
.deserialize(currentNonCachedMetricsDistributedBytes);
currentNonCachedMetricsIP = SerializationUtils.deserialize(currentNonCachedMetricsDistributedBytes);
}
if (currentNonCachedMetricsIP.containsAll(nonCachedMetricsUpdate)) {
LOG.debug("nonCachedMetricsIP already contains {}", nonCachedMetricsUpdate);
@@ -692,8 +667,7 @@ public class BalancedMetricResolver implements MetricResolver {
} catch (Exception e1) {
try {
curatorFramework.delete().deletingChildrenIfNeeded().forPath(path);
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT)
.forPath(path);
curatorFramework.create().creatingParentContainersIfNeeded().withMode(CreateMode.PERSISTENT).forPath(path);
} catch (Exception e2) {
LOG.info(e2.getMessage(), e2);
}
@@ -706,7 +680,7 @@ public class BalancedMetricResolver implements MetricResolver {
}
}
protected boolean shouldCache(String metricName) {
private boolean shouldCache(String metricName) {
if (StringUtils.isBlank(metricName)) {
return false;
@@ -752,23 +726,31 @@ public class BalancedMetricResolver implements MetricResolver {
boolean chooseMetricSpecificHost;
try {
chooseMetricSpecificHost = shouldCache(metric) ? true : false;
ArrivalRate rate = metricMap.get(metric);
if ((chooseMetricSpecificHost && rate == null) || (!chooseMetricSpecificHost && rate != null)) {
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
}
if (chooseMetricSpecificHost) {
ArrivalRate rate = metricMap.get(metric);
if (rate == null) {
rate = new ArrivalRate(arrivalRateExecutor);
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
// already write locked above
metricMap.put(metric, rate);
}
rate.arrived();
} else {
if (rate != null) {
// already write locked above
metricMap.remove(metric);
}
metric = null;
}
if (StringUtils.isBlank(metric)) {
tbh = getRoundRobinHost(null);
tbh = getRandomHost(null);
} else {
tbh = metricToHostMap.get(metric);
if (tbh == null) {
@@ -777,17 +759,17 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
assignMetric(metric, tbh, ASSIGN_ROUND_ROBIN);
assignMetric(metric, tbh);
} else if (!tbh.isUp()) {
TimelyBalancedHost oldTbh = tbh;
tbh = getRoundRobinHost(oldTbh);
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {} to {}", metric,
TimelyBalancedHost.toStringShort(oldTbh), TimelyBalancedHost.toStringShort(tbh));
tbh = getLeastUsedHost();
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {}:{} to {}:{}", metric, oldTbh.getHost(),
oldTbh.getTcpPort(), tbh.getHost(), tbh.getTcpPort());
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
assignMetric(metric, tbh, HOST_DOWN_ROUND_ROBIN);
assignMetric(metric, tbh);
}
}
@@ -804,7 +786,7 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
assignMetric(metric, tbh, ASSIGN_FALLBACK_SEQUENTIAL);
assignMetric(metric, tbh);
}
}
} finally {
@@ -833,23 +815,20 @@ public class BalancedMetricResolver implements MetricResolver {
metric = null;
}
if (tbh == null) {
tbh = getRoundRobinHost(null);
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
if (tbh == null || !tbh.isUp()) {
for (int x = 0; tbh == null && x < serverList.size(); x++) {
tbh = serverList.get(Math.abs(r.nextInt() & Integer.MAX_VALUE) % serverList.size());
if (!tbh.isUp()) {
tbh = null;
}
}
assignMetric(metric, tbh, ASSIGN_ROUND_ROBIN);
} else if (!tbh.isUp()) {
TimelyBalancedHost oldTbh = tbh;
tbh = getRoundRobinHost(oldTbh);
LOG.debug("rebalancing from host that is down: reassigning metric {} from server {} to {}", metric,
TimelyBalancedHost.toStringShort(oldTbh), TimelyBalancedHost.toStringShort(tbh));
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
if (tbh != null && StringUtils.isNotBlank(metric)) {
if (!balancerLock.isWriteLockedByCurrentThread()) {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
assignMetric(metric, tbh);
}
assignMetric(metric, tbh, HOST_DOWN_ROUND_ROBIN);
}
// if all else fails
@@ -865,7 +844,7 @@ public class BalancedMetricResolver implements MetricResolver {
balancerLock.readLock().unlock();
balancerLock.writeLock().lock();
}
assignMetric(metric, tbh, ASSIGN_FALLBACK_SEQUENTIAL);
assignMetric(metric, tbh);
}
}
} finally {
@@ -878,7 +857,7 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh;
}
protected TimelyBalancedHost findHost(String host, int tcpPort) {
private TimelyBalancedHost findHost(String host, int tcpPort) {
TimelyBalancedHost tbh = null;
for (TimelyBalancedHost h : serverList) {
if (h.getHost().equals(host) && h.getTcpPort() == tcpPort) {
@@ -889,73 +868,201 @@ public class BalancedMetricResolver implements MetricResolver {
return tbh;
}
private void assignMetric(String metric, TimelyBalancedHost tbh, MetricAssignedEvent.Reason reason) {
private void createAssignmentFile() {
try {
LOG.info("Creating assignment file: " + balancerConfig.getAssignmentFile());
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);
}
}
private void readAssignmentsFromHdfs(boolean checkIfNecessary) {
try {
long lastLocalUpdate = assignmentsLastUpdatedLocal.get();
long lastHdfsUpdate = assignmentsLastUpdatedInHdfs.get().postValue();
if (checkIfNecessary) {
if (lastHdfsUpdate <= lastLocalUpdate) {
LOG.debug("Not reading assignments from hdfs lastHdfsUpdate ({}) <= lastLocalUpdate ({})", new Date(lastHdfsUpdate),
new Date(lastLocalUpdate));
return;
}
}
LOG.debug("Reading assignments from hdfs lastHdfsUpdate ({}) > lastLocalUpdate ({})", new Date(lastHdfsUpdate), new Date(lastLocalUpdate));
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
// proceed with reading from HDFS
Map<String,TimelyBalancedHost> assignedMetricToHostMap = new TreeMap<>();
try {
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.readLock().acquire(60, TimeUnit.SECONDS);
}
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 (StringUtils.isNotBlank(e.getKey()) && e.getValue() != null) {
if (shouldCache(e.getKey())) {
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();
}
// remove metric from metricMap (ArrivalRate) if not being cached
metricMap.entrySet().removeIf(e -> !metricToHostMap.containsKey(e.getKey()));
assignmentsLastUpdatedLocal.set(assignmentsLastUpdatedInHdfs.get().postValue());
LOG.info("Read {} assignments from hdfs lastHdfsUpdate = 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);
}
}
}
private void writeAssignmentsToHdfs() {
CsvWriter writer = null;
try {
boolean acquired = false;
while (!acquired) {
acquired = assignmentsIPRWLock.writeLock().acquire(60, TimeUnit.SECONDS);
}
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()));
// remove metric from metricMap (ArrivalRate) if not being cached
metricMap.entrySet().removeIf(e -> !metricToHostMap.containsKey(e.getKey()));
}
} 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 (StringUtils.isNotBlank(metric) && tbh != null) {
balancerLock.writeLock().lock();
try {
metricAssignedEvent(metric, metricToHostMap.get(metric), tbh, reason);
metricToHostMap.put(metric, tbh);
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);
Exception e = new IllegalStateException("Bad assignment metric:" + metric + " host:" + tbh);
LOG.warn(e.getMessage(), e);
}
}
}
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() {
public void close() throws Exception {
this.assignmentExecutor.shutdown();
try {
@@ -1,320 +0,0 @@
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());
}
}
}
@@ -8,8 +8,5 @@ public interface MetricResolver {
public TimelyBalancedHost getHostPortKey(String metric);
public void start();
public void stop();
public void close() throws Exception;
}
@@ -1,7 +0,0 @@
package timely.balancer.resolver.eventing;
public abstract class MetricAssignedCallback {
public abstract void onEvent(MetricAssignedEvent event);
}
@@ -1,39 +0,0 @@
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;
}
}
@@ -1,7 +0,0 @@
package timely.balancer.resolver.eventing;
public abstract class MetricBalanceCallback {
public abstract void onEvent(MetricBalanceEvent event);
}
@@ -1,33 +0,0 @@
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;
}
}
@@ -1,7 +0,0 @@
package timely.balancer.resolver.eventing;
public abstract class MetricHostCallback {
public abstract void onEvent(MetricHostEvent event);
}
@@ -1,26 +0,0 @@
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;
}
}
+6 -3
View File
@@ -11,8 +11,11 @@
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<Class name="timely.balancer.resolver.MetricAssignmentPerister" />
<Method name="createAssignmentFile" />
<Bug pattern="RV_RETURN_VALUE_IGNORED_BAD_PRACTICE" />
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
@@ -1,76 +0,0 @@
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;
}
}
@@ -1,270 +0,0 @@
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();
}
}
@@ -1,89 +0,0 @@
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
@@ -1,18 +0,0 @@
<?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>
+2 -2
View File
@@ -4,13 +4,13 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>timely-client</artifactId>
<properties>
<!-- PEP 440 compliant version gets substituted into src/main/python/template/setup.py -->
<!-- use .dev0 instead of -SNAPSHOT -->
<python.client.version>1.2.8.dev0</python.client.version>
<python.client.version>2.0.2</python.client.version>
</properties>
<dependencies>
<dependency>
@@ -7,7 +7,7 @@ public class AddSubscription extends SubscriptionRequest {
private String operation = "add";
private String metric = null;
private Optional<Map<String, String>> tags = Optional.empty();
private Optional<Map<String,String>> tags = Optional.empty();
private Optional<Long> startTime = Optional.empty();
private Optional<Long> endTime = Optional.empty();
private Optional<Long> delayTime = Optional.empty();
@@ -20,7 +20,7 @@ public class AddSubscription extends SubscriptionRequest {
return metric;
}
public Optional<Map<String, String>> getTags() {
public Optional<Map<String,String>> getTags() {
return tags;
}
@@ -36,7 +36,7 @@ public class AddSubscription extends SubscriptionRequest {
this.metric = metric;
}
public void setTags(Optional<Map<String, String>> tags) {
public void setTags(Optional<Map<String,String>> tags) {
this.tags = tags;
}
@@ -71,9 +71,8 @@ public class AddSubscription extends SubscriptionRequest {
}
if (obj instanceof AddSubscription) {
AddSubscription other = (AddSubscription) obj;
return (this.subscriptionId.equals(other.subscriptionId) && this.metric.equals(other.metric)
&& this.tags.equals(other.tags) && this.startTime.equals(other.startTime)
&& this.endTime.equals(other.endTime) && this.delayTime.equals(other.delayTime));
return (this.subscriptionId.equals(other.subscriptionId) && this.metric.equals(other.metric) && this.tags.equals(other.tags)
&& this.startTime.equals(other.startTime) && this.endTime.equals(other.endTime) && this.delayTime.equals(other.delayTime));
} else {
return false;
}
@@ -5,9 +5,13 @@ import java.util.List;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import timely.model.Metric;
import timely.model.Tag;
@JsonPropertyOrder({"metric", "timestamp", "value", "tags", "subscriptionId", "complete"})
public class MetricResponse {
private String metric;
@@ -15,8 +15,8 @@ import org.glassfish.tyrus.client.SslContextConfigurator;
public class HttpClient {
public static SSLContext getSSLContext(String trustStoreFile, String trustStoreType, String trustStorePass,
String keyStoreFile, String keyStoreType, String keyStorePass) {
public static SSLContext getSSLContext(String trustStoreFile, String trustStoreType, String trustStorePass, String keyStoreFile, String keyStoreType,
String keyStorePass) {
SslContextConfigurator scc = new SslContextConfigurator();
scc.setTrustStoreFile(trustStoreFile);
scc.setTrustStoreType(trustStoreType);
@@ -31,12 +31,10 @@ public class HttpClient {
return scc.createSSLContext();
}
public static CloseableHttpClient get(String trustStoreFile, String trustStoreType, String trustStorePass,
String keyStoreFile, String keyStoreType, String keyStorePass, CookieStore cookieStore,
boolean hostVerificationEnabled) {
public static CloseableHttpClient get(String trustStoreFile, String trustStoreType, String trustStorePass, String keyStoreFile, String keyStoreType,
String keyStorePass, CookieStore cookieStore, boolean hostVerificationEnabled) {
SSLContext ssl = getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType,
keyStorePass);
SSLContext ssl = getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType, keyStorePass);
return get(ssl, cookieStore, hostVerificationEnabled);
}
@@ -44,8 +42,7 @@ public class HttpClient {
return get(ssl, cookieStore, hostVerificationEnabled, false);
}
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled,
boolean clientAuth) {
public static CloseableHttpClient get(SSLContext ssl, CookieStore cookieStore, boolean hostVerificationEnabled, boolean clientAuth) {
HostnameVerifier hostnameVerifier;
if (hostVerificationEnabled) {
hostnameVerifier = SSLConnectionSocketFactory.getDefaultHostnameVerifier();
@@ -55,16 +52,15 @@ public class HttpClient {
RequestConfig defaultRequestConfig = RequestConfig.custom().setCookieSpec(CookieSpecs.STANDARD).build();
HttpClientBuilder builder;
if (cookieStore == null) {
builder = HttpClients.custom().setSSLContext(ssl).disableCookieManagement()
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
builder = HttpClients.custom().setSSLContext(ssl).disableCookieManagement().setDefaultRequestConfig(defaultRequestConfig)
.setSSLHostnameVerifier(hostnameVerifier);
} else {
builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore)
.setDefaultRequestConfig(defaultRequestConfig).setSSLHostnameVerifier(hostnameVerifier);
builder = HttpClients.custom().setSSLContext(ssl).setDefaultCookieStore(cookieStore).setDefaultRequestConfig(defaultRequestConfig)
.setSSLHostnameVerifier(hostnameVerifier);
}
if (clientAuth) {
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl,
new String[] { "TLSv1.1", "TLSv1.2" }, null, hostnameVerifier);
SSLConnectionSocketFactory sslCSF = new SSLConnectionSocketFactory(ssl, new String[] {"TLSv1.1", "TLSv1.2"}, null, hostnameVerifier);
builder.setSSLSocketFactory(sslCSF);
}
return builder.build();
@@ -33,7 +33,7 @@ public class TcpClient implements AutoCloseable {
/**
* Opens a TCP connection to the specified host and port
*
*
* @throws IOException
* if an error occurs
*/
@@ -45,7 +45,7 @@ public class TcpClient implements AutoCloseable {
/**
* Write a metric to Timely
*
*
* @param metric
* newline terminated string representation of Timely metric
*/
@@ -69,7 +69,7 @@ public class TcpClient implements AutoCloseable {
/**
* Closes the tcp connection to Timely
*
*
* @throws IOException
* if an error occurs
*/
@@ -96,17 +96,42 @@ public class TcpClient implements AutoCloseable {
private synchronized int connect() {
if (null == sock || !sock.isConnected()) {
if (System.currentTimeMillis() > (connectTime + backoff)) {
OutputStreamWriter osw = null;
try {
connectTime = System.currentTimeMillis();
sock = new Socket(host, port);
out = new BufferedWriter(new OutputStreamWriter(sock.getOutputStream()));
osw = new OutputStreamWriter(sock.getOutputStream());
out = new BufferedWriter(osw);
backoff = 2000;
LOG.trace("Connected to Timely at {}:{}", host, port);
} catch (IOException e) {
LOG.error("Error connecting to Timely at {}:" + host + ":" + port + ". Error: " + e.getMessage());
} catch (Exception e) {
LOG.error("Error connecting to Timely at {}:{} - {}", host, port, e.getMessage());
if (sock != null) {
try {
sock.close();
} catch (IOException e1) {
LOG.error(e1.getMessage());
} finally {
sock = null;
}
}
if (osw != null) {
try {
osw.close();
} catch (IOException e1) {
LOG.error(e1.getMessage());
}
}
if (out != null) {
try {
out.close();
} catch (IOException e1) {
LOG.error(e1.getMessage());
} finally {
out = null;
}
}
backoff = backoff * 2;
sock = null;
out = null;
LOG.info("Will retry connection in {} ms.", backoff);
return -1;
}
@@ -42,8 +42,7 @@ public class UdpClient implements AutoCloseable {
this.sock.send(packet);
}
public void flush() throws IOException {
}
public void flush() throws IOException {}
public void close() throws IOException {
if (null != sock) {
@@ -6,7 +6,6 @@ import java.util.Map;
import javax.websocket.CloseReason;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import org.slf4j.Logger;
@@ -19,12 +18,8 @@ public abstract class ClientHandler extends Endpoint {
@Override
public void onOpen(Session session, EndpointConfig config) {
LOG.info("Websocket session {} opened.", session.getId());
session.addMessageHandler(new MessageHandler.Whole<String>() {
@Override
public void onMessage(String message) {
LOG.info("Message received on Websocket session {}: {}", session.getId(), message);
}
session.addMessageHandler(String.class, message -> {
LOG.info("Message received on Websocket session {}: {}", session.getId(), message);
});
}
@@ -38,6 +33,5 @@ public abstract class ClientHandler extends Endpoint {
LOG.error("Error occurred on Websocket session" + session.getId(), error);
}
public void beforeRequest(Map<String, List<String>> headers) {
}
public void beforeRequest(Map<String,List<String>> headers) {}
}
@@ -36,7 +36,7 @@ public class TimelyEndpointConfig implements ClientEndpointConfig {
}
@Override
public Map<String, Object> getUserProperties() {
public Map<String,Object> getUserProperties() {
return Collections.emptyMap();
}
@@ -55,7 +55,7 @@ public class TimelyEndpointConfig implements ClientEndpointConfig {
return new Configurator() {
@Override
public void beforeRequest(Map<String, List<String>> headers) {
public void beforeRequest(Map<String,List<String>> headers) {
super.beforeRequest(headers);
if (null != sessionCookie) {
headers.put("Cookie", Collections.singletonList(sessionCookie));
@@ -10,7 +10,6 @@ import javax.net.ssl.SSLContext;
import javax.websocket.DeploymentException;
import javax.websocket.Session;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
@@ -28,6 +27,9 @@ import org.glassfish.tyrus.client.ClientProperties;
import org.glassfish.tyrus.client.SslEngineConfigurator;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import timely.api.BasicAuthLogin;
import timely.client.http.HttpClient;
import timely.serialize.JsonSerializer;
@@ -51,9 +53,8 @@ public class WebSocketClient implements AutoCloseable {
protected Session session = null;
protected volatile boolean closed = true;
protected WebSocketClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword,
boolean hostVerificationEnabled, int bufferSize) {
protected WebSocketClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth, boolean doLogin,
String timelyUsername, String timelyPassword, boolean hostVerificationEnabled, int bufferSize) {
this.ssl = ssl;
this.timelyHostname = timelyHostname;
this.timelyHttpsPort = timelyHttpsPort;
@@ -71,19 +72,17 @@ public class WebSocketClient implements AutoCloseable {
if (doLogin && !clientAuth) {
if ((StringUtils.isEmpty(timelyUsername) && !StringUtils.isEmpty(timelyPassword)
|| (!StringUtils.isEmpty(timelyUsername) && StringUtils.isEmpty(timelyPassword)))) {
|| (!StringUtils.isEmpty(timelyUsername) && StringUtils.isEmpty(timelyPassword)))) {
throw new IllegalArgumentException("Both Timely username and password must be empty or non-empty");
}
}
}
protected WebSocketClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth,
boolean doLogin, String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType,
String keyStorePass, String trustStoreFile, String trustStoreType, String trustStorePass,
boolean hostVerificationEnabled, int bufferSize) {
this(HttpClient.getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType,
keyStorePass), timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername,
timelyPassword, hostVerificationEnabled, bufferSize);
protected WebSocketClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth, boolean doLogin, String timelyUsername,
String timelyPassword, String keyStoreFile, String keyStoreType, String keyStorePass, String trustStoreFile, String trustStoreType,
String trustStorePass, boolean hostVerificationEnabled, int bufferSize) {
this(HttpClient.getSSLContext(trustStoreFile, trustStoreType, trustStorePass, keyStoreFile, keyStoreType, keyStorePass), timelyHostname,
timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword, hostVerificationEnabled, bufferSize);
}
public void open(ClientHandler clientEndpoint) throws IOException, DeploymentException, URISyntaxException {
@@ -115,8 +114,7 @@ public class WebSocketClient implements AutoCloseable {
try {
response = client.execute(request);
if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK) {
throw new HttpResponseException(response.getStatusLine().getStatusCode(),
response.getStatusLine().getReasonPhrase());
throw new HttpResponseException(response.getStatusLine().getStatusCode(), response.getStatusLine().getReasonPhrase());
}
for (Cookie c : cookieJar.getCookies()) {
if (c.getName().equals("TSESSIONID")) {
@@ -125,8 +123,7 @@ public class WebSocketClient implements AutoCloseable {
}
}
if (null == sessionCookie) {
throw new IllegalStateException(
"Unable to find TSESSIONID cookie header in Timely login response");
throw new IllegalStateException("Unable to find TSESSIONID cookie header in Timely login response");
}
} finally {
HttpClientUtils.closeQuietly(response);
@@ -142,8 +139,7 @@ public class WebSocketClient implements AutoCloseable {
webSocketClient.getProperties().put(ClientProperties.SSL_ENGINE_CONFIGURATOR, sslEngine);
webSocketClient.getProperties().put(ClientProperties.INCOMING_BUFFER_SIZE, bufferSize);
String wssPath = "wss://" + timelyHostname + ":" + timelyWssPort + "/websocket";
session = webSocketClient.connectToServer(clientEndpoint,
new TimelyEndpointConfig(clientEndpoint, sessionCookie), new URI(wssPath));
session = webSocketClient.connectToServer(clientEndpoint, new TimelyEndpointConfig(clientEndpoint, sessionCookie), new URI(wssPath));
final ByteBuffer pingData = ByteBuffer.allocate(0);
webSocketClient.getScheduledExecutorService().scheduleAtFixedRate(() -> {
@@ -13,6 +13,7 @@ import javax.websocket.DeploymentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import timely.api.AddSubscription;
import timely.api.CloseSubscription;
import timely.api.CreateSubscription;
@@ -26,22 +27,18 @@ public class WebSocketSubscriptionClient extends WebSocketClient {
private static final Logger LOG = LoggerFactory.getLogger(WebSocketSubscriptionClient.class);
private final String subscriptionId;
public WebSocketSubscriptionClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort,
boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword,
boolean hostVerificationEnabled, int bufferSize) {
super(ssl, timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword,
hostVerificationEnabled, bufferSize);
public WebSocketSubscriptionClient(SSLContext ssl, String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth, boolean doLogin,
String timelyUsername, String timelyPassword, boolean hostVerificationEnabled, int bufferSize) {
super(ssl, timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword, hostVerificationEnabled, bufferSize);
subscriptionId = UUID.randomUUID().toString();
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
}
public WebSocketSubscriptionClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort,
boolean clientAuth, boolean doLogin, String timelyUsername, String timelyPassword, String keyStoreFile,
String keyStoreType, String keyStorePass, String trustStoreFile, String trustStoreType,
String trustStorePass, boolean hostVerificationEnabled, int bufferSize) {
super(timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword,
keyStoreFile, keyStoreType, keyStorePass, trustStoreFile, trustStoreType, trustStorePass,
hostVerificationEnabled, bufferSize);
public WebSocketSubscriptionClient(String timelyHostname, int timelyHttpsPort, int timelyWssPort, boolean clientAuth, boolean doLogin,
String timelyUsername, String timelyPassword, String keyStoreFile, String keyStoreType, String keyStorePass, String trustStoreFile,
String trustStoreType, String trustStorePass, boolean hostVerificationEnabled, int bufferSize) {
super(timelyHostname, timelyHttpsPort, timelyWssPort, clientAuth, doLogin, timelyUsername, timelyPassword, keyStoreFile, keyStoreType, keyStorePass,
trustStoreFile, trustStoreType, trustStorePass, hostVerificationEnabled, bufferSize);
subscriptionId = UUID.randomUUID().toString();
LOG.trace("Created WebSocketClient with subscriptionId {}", this.subscriptionId);
}
@@ -69,8 +66,7 @@ public class WebSocketSubscriptionClient extends WebSocketClient {
super.close();
}
public Future<Void> addSubscription(String metric, Map<String, String> tags, long startTime, long endTime,
long delayTime) throws IOException {
public Future<Void> addSubscription(String metric, Map<String,String> tags, long startTime, long endTime, long delayTime) throws IOException {
AddSubscription add = new AddSubscription();
add.setSubscriptionId(subscriptionId);
add.setMetric(metric);
@@ -10,18 +10,18 @@ import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.fasterxml.jackson.annotation.JsonUnwrapped;
import timely.model.parse.TagParser;
/**
* A Metric consists of a metric name, tags, and a Value
*
* Json has been flattened to reduce overhead. Example:
* {"name":"m1","timestamp":1,"measure":1.0,"tags":[{"k1":"v1"}]}
* Json has been flattened to reduce overhead. Example: {"name":"m1","timestamp":1,"measure":1.0,"tags":[{"k1":"v1"}]}
*
*/
@JsonRootName("metric")
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonPropertyOrder({ "name" })
@JsonPropertyOrder({"name"})
public class Metric implements Serializable {
private static final long serialVersionUID = 1L;
@@ -134,8 +134,7 @@ public class Metric implements Serializable {
}
/**
* Simple Builder for Metric. Note: cannot re-use a builder after build is
* called.
* Simple Builder for Metric. Note: cannot re-use a builder after build is called.
*/
public static class Builder {
@@ -2,12 +2,13 @@ package timely.model;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;
import java.util.logging.Logger;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A simple interface that objects can implement to return the object size.
@@ -47,7 +48,7 @@ public interface ObjectSizeOf {
public static class Sizer {
private static final Logger log = Logger.getLogger(Sizer.class.getCanonicalName());
private static final Logger LOG = LoggerFactory.getLogger(Sizer.class);
public static final short OBJECT_OVERHEAD = 8;
public static final short ARRAY_OVERHEAD = 12;
public static final short REFERENCE = 4;
@@ -56,17 +57,14 @@ public interface ObjectSizeOf {
public static final short NUMBER_SIZE = 16;
/**
* Get the size of an object. Note that we want something relatively fast that
* gives us an order of magnitude here. The java Instrumentation agent mechanism
* is a little too costly for general use here. This will look for the
* ObjectSizeOf interface and if implemented on the object will use that.
* Get the size of an object. Note that we want something relatively fast that gives us an order of magnitude here. The java Instrumentation agent
* mechanism is a little too costly for general use here. This will look for the ObjectSizeOf interface and if implemented on the object will use that.
* Otherwise it will do a simple navigation of the fields using reflection.
*
* @return an approximation of the object size
*/
public static long getObjectSize(Object o, boolean useSizeInBytesMethod, boolean debug) {
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(),
useSizeInBytesMethod, debug);
long estSize = getObjectSize(o, new HashSet<ObjectInstance>(), new Stack<ObjectInstance>(), useSizeInBytesMethod, debug);
return estSize;
}
@@ -75,8 +73,7 @@ public interface ObjectSizeOf {
return estSize;
}
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack,
boolean useSizeInBytesMethod, boolean debug) {
public static long getObjectSize(Object o, Set<ObjectInstance> visited, Stack<ObjectInstance> stack, boolean useSizeInBytesMethod, boolean debug) {
long totalSize = 0;
stack.add(new ObjectInstance(o));
while (!stack.isEmpty()) {
@@ -87,23 +84,16 @@ public interface ObjectSizeOf {
visited.add(oi);
try {
if (useSizeInBytesMethod) {
try {
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
} else {
Method sizeInBytes = o.getClass().getMethod("sizeInBytes", (Class<?>[]) null);
size = (Long) sizeInBytes.invoke(o);
}
} catch (Throwable t) {
// ok, lets do this the hard way...
if (o instanceof ObjectSizeOf) {
size = ((ObjectSizeOf) o).sizeInBytes();
}
}
if (size == 0) {
// the hard way...
// do not include Class related objects or
// reflection objects
if ((o instanceof Class) || (o instanceof ClassLoader) || (o.getClass().getPackage() != null
&& o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
if ((o instanceof Class) || (o instanceof ClassLoader)
|| (o.getClass().getPackage() != null && o.getClass().getPackage().getName().startsWith("java.lang.reflect"))) {
size = 0;
} else if (o instanceof Number || o instanceof Boolean || o instanceof Character) {
size = NUMBER_SIZE;
@@ -158,7 +148,7 @@ public interface ObjectSizeOf {
}
}
} catch (Throwable t) {
log.warning("Unable to determine object size for " + o);
LOG.warn("Unable to determine object size for " + o, t);
}
if (debug) {
System.out.println(o.getClass().getCanonicalName() + "=" + size);
+6 -5
View File
@@ -5,13 +5,15 @@ import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import com.fasterxml.jackson.annotation.JsonAnyGetter;
import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonRootName;
import com.google.common.base.Preconditions;
import org.apache.commons.lang3.builder.CompareToBuilder;
import org.apache.commons.lang3.builder.EqualsBuilder;
import timely.model.parse.TagParser;
/**
@@ -27,8 +29,7 @@ public class Tag implements Comparable<Tag>, Serializable {
private String key;
private String value;
public Tag() {
}
public Tag() {}
public Tag(Tag other) {
this.setKey(other.getKey());
@@ -47,7 +48,7 @@ public class Tag implements Comparable<Tag>, Serializable {
}
@JsonAnyGetter
public Map<String, String> get() {
public Map<String,String> get() {
return Stream.of(this).collect(Collectors.toMap(Tag::getKey, Tag::getValue));
}
+3 -6
View File
@@ -18,8 +18,7 @@ public class Value implements Comparator<Value>, Serializable {
private Long timestamp;
private Double measure;
public Value() {
}
public Value() {}
public Value(Value other) {
this.setTimestamp(other.getTimestamp());
@@ -74,11 +73,9 @@ public class Value implements Comparator<Value>, Serializable {
if (null == o || getClass() != o.getClass())
return false;
if (getTimestamp() != null ? !getTimestamp().equals(((Value) o).getTimestamp())
: ((Value) o).getTimestamp() != null)
if (getTimestamp() != null ? !getTimestamp().equals(((Value) o).getTimestamp()) : ((Value) o).getTimestamp() != null)
return false;
return !(getMeasure() != null ? !getMeasure().equals(((Value) o).getMeasure())
: ((Value) o).getMeasure() != null);
return !(getMeasure() != null ? !getMeasure().equals(((Value) o).getMeasure()) : ((Value) o).getMeasure() != null);
}
@Override
@@ -4,6 +4,7 @@ import java.util.List;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
import timely.model.Metric;
public class MetricParser implements Parser<Metric> {
@@ -8,8 +8,7 @@ import java.util.Map;
import timely.model.Tag;
/**
* Parses tag1=val1,tag2=val2,tag3=val3 into a {@code List} of {@code Tag}
* Combines {@code List} of {@code Tag} into {@code String} of
* Parses tag1=val1,tag2=val2,tag3=val3 into a {@code List} of {@code Tag} Combines {@code List} of {@code Tag} into {@code String} of
* tag1=val1,tag2=val2,tag3=val3.
*/
public class TagListParser implements Parser<List<Tag>>, Combiner<Collection<Tag>> {
@@ -44,12 +43,11 @@ 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
* tags
*
* An alternative combiner for a map of string to string instead of a list of tags
*
* @return string representation
*/
public String combine(Map<String, String> tags) {
public String combine(Map<String,String> tags) {
StringBuilder builder = new StringBuilder();
if (!tags.isEmpty()) {
tags.forEach((k, v) -> builder.append(tagParser.combine(new Tag(k, v))).append(','));
@@ -4,11 +4,11 @@ import java.util.List;
import com.google.common.base.Joiner;
import com.google.common.base.Splitter;
import timely.model.Tag;
/**
* Parses tag1=val1 into a {@code Tag} Combines {@code Tag} into {@code String}
* of tag1=val1
* Parses tag1=val1 into a {@code Tag} Combines {@code Tag} into {@code String} of tag1=val1
*/
public class TagParser implements Parser<Tag>, Combiner<Tag> {
+12
View File
@@ -10,4 +10,16 @@
<!-- https://github.com/spotbugs/spotbugs/issues/756 -->
<Bug pattern="RCN_REDUNDANT_NULLCHECK_WOULD_HAVE_BEEN_A_NPE" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="MS_EXPOSE_REP" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP" />
</Match>
<Match>
<!-- these are probably avoidable, but needs further triage to fix -->
<Bug pattern="EI_EXPOSE_REP2" />
</Match>
</FindBugsFilter>
@@ -1,8 +1,10 @@
package timely.api;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Assert;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import timely.serialize.JsonSerializer;
public class SerializationTest {
@@ -6,9 +6,10 @@ import static org.junit.Assert.assertTrue;
import java.io.IOException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
*
*
@@ -30,8 +31,7 @@ public class MetricTest {
Metric m4 = Metric.newBuilder().name("m4").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
assertFalse(m1.equals(m4));
Metric m5 = Metric.newBuilder().name("m1").tag("t3", "v3").tag("t2", "v2").tag("t1", "v1").value(1, 0.0)
.build();
Metric m5 = Metric.newBuilder().name("m1").tag("t3", "v3").tag("t2", "v2").tag("t1", "v1").value(1, 0.0).build();
assertFalse(m1.equals(m5));
Metric m6 = Metric.newBuilder().name("m1").tag("t2", "v2").tag("t1", "v1").value(2, 0.0).build();
@@ -5,6 +5,7 @@ import java.util.List;
import org.junit.Assert;
import org.junit.Test;
import timely.model.Metric;
import timely.model.Tag;
@@ -7,6 +7,7 @@ import java.util.TreeMap;
import org.junit.Assert;
import org.junit.Test;
import timely.model.Tag;
public class TagListParserTest {
@@ -31,7 +32,7 @@ public class TagListParserTest {
@Test
public void testListCombineMap() {
Map<String, String> map = new TreeMap<>();
Map<String,String> map = new TreeMap<>();
map.put("tag1", "value1");
map.put("tag2", "value2");
String combined = new TagListParser().combine(map);
+9 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>collectd-nsq-plugin</artifactId>
<dependencies>
@@ -34,6 +34,14 @@
<artifactSet>
<includes />
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
@@ -36,6 +36,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
private String topic = "metrics#ephemeral";
private String endpoint = null;
private GenericObjectPool<CloseableHttpClient> clientPool = null;
private Random random = new Random();
// pool should be limited by the number of WriteThreads configured in
// collectd
@@ -88,11 +89,9 @@ public class WriteNSQPlugin extends CollectDPluginParent
if (client != null) {
clientPool.returnObject(client);
}
if (null != out) {
try {
out.close();
} catch (IOException e) {
}
try {
out.close();
} catch (IOException e) {
}
}
return 0;
@@ -141,7 +140,7 @@ public class WriteNSQPlugin extends CollectDPluginParent
if (hosts.length == 1) {
host = hosts[0];
} else {
host = hosts[new Random().nextInt(hosts.length)];
host = hosts[random.nextInt(hosts.length)];
}
break;
case "Port":
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>collectd-plugin-parent</artifactId>
<dependencies>
+9 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>collectd-timely-plugin</artifactId>
<dependencies>
@@ -51,6 +51,14 @@
<artifactSet>
<includes />
</artifactSet>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>**/module-info.class</exclude>
</excludes>
</filter>
</filters>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
+1 -1
View File
@@ -4,7 +4,7 @@
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
<version>2.0.2</version>
</parent>
<artifactId>timely-common</artifactId>
<dependencies>
@@ -1,4 +1,3 @@
package timely.test;
public interface IntegrationTest {
}
public interface IntegrationTest {}
-42
View File
@@ -1,42 +0,0 @@
#!/bin/bash
if [[ `uname` == "Darwin" ]]; then
THIS_SCRIPT=`python -c 'import os,sys; print os.path.realpath(sys.argv[1])' $0`
TCNATIVE_SUFFIX="jnilib"
else
THIS_SCRIPT=`readlink -f $0`
TCNATIVE_SUFFIX="so"
fi
THIS_DIR="${THIS_SCRIPT%/*}"
NATIVE_DIR="${THIS_DIR}/META-INF/native"
BASE_DIR=${THIS_DIR}/..
TMP_DIR="${BASE_DIR}/tmp"
CONF_DIR="${BASE_DIR}/conf"
LIB_DIR="${BASE_DIR}/lib"
NUM_SERVER_THREADS=4
if [[ -e ${TMP_DIR} ]]; then
rm -rf ${TMP_DIR}
fi
mkdir ${TMP_DIR}
if [[ -e ${NATIVE_DIR} ]]; then
rm -rf ${NATIVE_DIR}
fi
mkdir -p ${NATIVE_DIR}
pushd ${BASE_DIR}/bin
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-tcnative-boringssl-static*.jar META-INF/native/libnetty_tcnative_linux_x86_64.${TCNATIVE_SUFFIX}
$JAVA_HOME/bin/jar xf ${LIB_DIR}/netty-all*.jar META-INF/native/libnetty_transport_native_epoll_x86_64.${TCNATIVE_SUFFIX}
popd
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="${JVM_ARGS} -DLog4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector"
JVM_ARGS="${JVM_ARGS} -Djava.library.path=${NATIVE_DIR}"
#JVM_ARGS="${JVM_ARGS} -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005"
echo "$JAVA_HOME/bin/java ${JVM_ARGS} timely.grafana.auth.GrafanaAuth --spring.config.name=grafana-auth"
$JAVA_HOME/bin/java ${JVM_ARGS} timely.grafana.auth.GrafanaAuth --spring.config.name=grafana-auth
-55
View File
@@ -1,55 +0,0 @@
grafana-auth:
grafana:
host: localhost
port: 3001
security:
session-max-age: 86400
cache-expiration-minutes: 60
cache-refresh-minutes: 5
allow-anonymous-http-access: true
allow-anonymous-ws-access: true
serverSsl:
certificate-file:
key-file:
#key-password:
use-generated-keypair: true
trust-store-file:
use-openssl: true
#use-ciphers:
clientSsl:
keyFile:
keyType: pkcs12
#keyPassword:
trustStoreFile:
trustStoreType: jks
#trustStorePassword:
http:
host: localhost
ip: 127.0.0.1
port: 3000
redirect-path: /secure-me
strict-transport-max-age: 608400
cors:
allow-any-origin: false
allow-null-origin: false
allow-credentials: true
allowed-origins:
allowed-methods: DELETE,GET,HEAD,OPTIONS,PUT,POST
allowed-headers: content-type
httpClientPool:
minIdlePerKey: 0
maxIdlePerKey: 40
maxTotalPerKey: 40
maxTotal: -1
lifo: true
fairness: false
maxWaitMillis: -1
minEvictableIdleTimeMillis: 1800000
evictorShutdownTimeoutMillis: 10000
softMinEvictableIdleTimeMillis: -1
numTestsPerEvictionRun: 3
timeBetweenEvictionRunsMillis: -1
blockWhenExhausted: true
jmxEnabled: true
testOnBorrow: false
testOnReturn: false
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="60">
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
</Console>
</Appenders>
<Loggers>
<Root level="error" includeLocation="false">
<AppenderRef ref="Console" level="error" />
</Root>
</Loggers>
</Configuration>
-29
View File
@@ -1,29 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn" monitorInterval="60">
<Properties>
<Property name="PID">????</Property>
<Property name="LOG_PATTERN">%clr{%d{yyyy-MM-dd HH:mm:ss.SSS}}{faint} %clr{%5p} %clr{${sys:PID}}{magenta} %clr{---}{faint} %clr{[%15.15t]}{faint} %clr{%-40.40c{1.}}{cyan} %clr{:}{faint} %m%n%wEx</Property>
</Properties>
<Appenders>
<RollingRandomAccessFile name="Rolling" fileName="/srv/logs/timely/grafana-auth.log"
filePattern="/srv/logs/timely/$${date:yyyy-MM}/grafana-auth-%d{MM-dd-yyyy}-%i.log.gz">
<PatternLayout pattern="${LOG_PATTERN}"/>
<Policies>
<SizeBasedTriggeringPolicy size="1024 MB" />
</Policies>
<DefaultRolloverStrategy max="10" />
</RollingRandomAccessFile>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="${LOG_PATTERN}"/>
</Console>
</Appenders>
<Loggers>
<Root level="trace" includeLocation="false">
<AppenderRef ref="Console" level="warn" />
<AppenderRef ref="Rolling" level="trace" />
</Root>
<Logger name="timely" additivity="false">
<AppenderRef ref="Console" level="debug" />
</Logger>
</Loggers>
</Configuration>
-71
View File
@@ -1,71 +0,0 @@
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util.xsd">
<beans:bean id="x509PrincipalExtractor" class="org.springframework.security.web.authentication.preauth.x509.SubjectDnX509PrincipalExtractor">
<beans:property name="subjectDnRegex" value="CN=(.*?)(?:,|$)"/>
</beans:bean>
<beans:bean id="timelyUserDetailsService" class="timely.auth.UserDetailsService">
<beans:property name="users">
<util:list>
<beans:bean class="timely.auth.TimelyUser">
<beans:constructor-arg name="subjectDn">
<beans:value>CN=example.com</beans:value>
</beans:constructor-arg>
<beans:constructor-arg name="auths">
<util:list>
<beans:value>D</beans:value>
<beans:value>E</beans:value>
<beans:value>F</beans:value>
</util:list>
</beans:constructor-arg>
<beans:constructor-arg name="roles">
<util:list>
<beans:value>G</beans:value>
<beans:value>H</beans:value>
<beans:value>I</beans:value>
</util:list>
</beans:constructor-arg>
</beans:bean>
</util:list>
</beans:property>
</beans:bean>
<beans:bean id="x509AuthorizationProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
<beans:property name="preAuthenticatedUserDetailsService" ref="timelyUserDetailsService" />
</beans:bean>
<user-service id="usernamePasswordService">
<user name="test" password="test1" authorities="A,B,C" />
</user-service>
<beans:bean id="noOpPasswordEncoder" class="org.springframework.security.crypto.password.NoOpPasswordEncoder"/>
<beans:bean id="UserNamePasswordAuthenticationProvider" class="org.springframework.security.authentication.dao.DaoAuthenticationProvider">
<beans:property name="userDetailsService" ref="usernamePasswordService"/>
<beans:property name="passwordEncoder" ref="noOpPasswordEncoder"/>
</beans:bean>
<authentication-manager alias="authenticationManager">
<!-- X509 PreAuthenticated authorization provider -->
<authentication-provider ref="x509AuthorizationProvider"/>
<!-- Basic authorization provider, validates provided username and password -->
<authentication-provider ref="UserNamePasswordAuthenticationProvider" />
</authentication-manager>
<util:list id="requiredRoles" value-type="java.lang.String">
<beans:value></beans:value>
</util:list>
<util:list id="requiredAuths" value-type="java.lang.String">
<beans:value></beans:value>
</util:list>
</beans:beans>
-295
View File
@@ -1,295 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>timely</groupId>
<artifactId>timely</artifactId>
<version>1.2.8-SNAPSHOT</version>
</parent>
<artifactId>grafana-auth</artifactId>
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.datatype</groupId>
<artifactId>jackson-datatype-jdk8</artifactId>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.google.flatbuffers</groupId>
<artifactId>flatbuffers-java</artifactId>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
</dependency>
<dependency>
<groupId>io.github.lukehutch</groupId>
<artifactId>fast-classpath-scanner</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
</dependency>
<dependency>
<groupId>net.sourceforge.javacsv</groupId>
<artifactId>javacsv</artifactId>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-core</artifactId>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.accumulo</groupId>
<artifactId>accumulo-minicluster</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-collections4</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-framework</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-recipes</artifactId>
</dependency>
<dependency>
<groupId>org.apache.curator</groupId>
<artifactId>curator-x-discovery</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-client</artifactId>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-common</artifactId>
<exclusions>
<exclusion>
<groupId>jdk.tools</groupId>
<artifactId>jdk.tools</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.hadoop</groupId>
<artifactId>hadoop-minicluster</artifactId>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-balancer</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-client</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-common</artifactId>
</dependency>
<dependency>
<groupId>timely</groupId>
<artifactId>timely-server</artifactId>
</dependency>
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>assemble</id>
<goals>
<goal>single</goal>
</goals>
<phase>package</phase>
<configuration>
<descriptors>
<descriptor>src/main/dist/assembly.xml</descriptor>
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<executions>
<execution>
<id>copy-dependencies</id>
<goals>
<goal>copy-dependencies</goal>
</goals>
<phase>prepare-package</phase>
<configuration>
<excludeArtifactIds>junit</excludeArtifactIds>
<outputDirectory>${project.build.directory}/lib</outputDirectory>
</configuration>
</execution>
<execution>
<id>unpack-native</id>
<goals>
<goal>unpack</goal>
</goals>
<phase>process-test-classes</phase>
<configuration>
<artifactItems>
<artifactItem>
<groupId>io.netty</groupId>
<artifactId>netty-tcnative-boringssl-static</artifactId>
<version>${version.netty-tcnative}</version>
<type>jar</type>
<overWrite>false</overWrite>
<outputDirectory>${project.build.directory}/native</outputDirectory>
<includes>**/*.so</includes>
</artifactItem>
</artifactItems>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<executions>
<execution>
<id>add-source</id>
<goals>
<goal>add-source</goal>
</goals>
<phase>generate-sources</phase>
<configuration>
<sources>
<source>src/main/generated-java</source>
</sources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
<extensions>
<!-- Use os-maven-plugin to initialize the "os.detected" properties -->
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
</extension>
</extensions>
</build>
</project>
-55
View File
@@ -1,55 +0,0 @@
<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 http://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>dist</id>
<formats>
<format>tar.gz</format>
</formats>
<fileSets>
<fileSet>
<directory>${project.basedir}</directory>
<outputDirectory></outputDirectory>
<includes>
<include>README*</include>
<include>LICENSE*</include>
<include>NOTICE*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>timely*.jar</include>
<include>grafana*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/lib</directory>
<outputDirectory>lib</outputDirectory>
<includes>
<include>*.jar</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/bin</directory>
<outputDirectory>bin</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/conf</directory>
<outputDirectory>conf</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.basedir}/src/main/resources/webapp</directory>
<outputDirectory>bin/webapp</outputDirectory>
<includes>
<include>*</include>
</includes>
</fileSet>
</fileSets>
</assembly>
@@ -1,322 +0,0 @@
package timely.grafana.auth;
import java.io.File;
import java.net.InetSocketAddress;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import javax.net.ssl.SSLContext;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpContentCompressor;
import io.netty.handler.codec.http.HttpContentDecompressor;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpRequestDecoder;
import io.netty.handler.codec.http.HttpResponseEncoder;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.ssl.ClientAuth;
import io.netty.handler.ssl.OpenSslServerContext;
import io.netty.handler.ssl.OpenSslServerSessionContext;
import io.netty.handler.ssl.SslContext;
import io.netty.handler.ssl.SslContextBuilder;
import io.netty.handler.ssl.SslProvider;
import io.netty.handler.ssl.util.SelfSignedCertificate;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.util.internal.SystemPropertyUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.WebApplicationType;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.context.ConfigurableApplicationContext;
import timely.auth.AuthCache;
import timely.balancer.configuration.ClientSsl;
import timely.balancer.connection.http.HttpClientPool;
import timely.client.http.HttpClient;
import timely.configuration.ServerSsl;
import timely.grafana.auth.configuration.GrafanaAuthConfiguration;
import timely.grafana.auth.configuration.SpringBootstrap;
import timely.grafana.auth.netty.http.GrafanaRelayHandler;
import timely.grafana.auth.netty.http.GrafanaRequestDecoder;
import timely.netty.http.HttpStaticFileServerHandler;
import timely.netty.http.NonSslRedirectHandler;
import timely.netty.http.TimelyExceptionHandler;
import timely.netty.http.auth.X509LoginRequestHandler;
public class GrafanaAuth {
private static final Logger LOG = LoggerFactory.getLogger(GrafanaAuth.class);
private static final int EPOLL_MIN_MAJOR_VERSION = 2;
private static final int EPOLL_MIN_MINOR_VERSION = 6;
private static final int EPOLL_MIN_PATCH_VERSION = 32;
private static final String OS_NAME = "os.name";
private static final String OS_VERSION = "os.version";
protected static final CountDownLatch LATCH = new CountDownLatch(1);
static ConfigurableApplicationContext applicationContext;
private final GrafanaAuthConfiguration grafanaAuthConfig;
private EventLoopGroup httpWorkerGroup = null;
private EventLoopGroup httpBossGroup = null;
protected Channel httpChannelHandle = null;
protected volatile boolean shutdown = false;
public GrafanaAuth(GrafanaAuthConfiguration grafanaAuthConfig) throws Exception {
this.grafanaAuthConfig = grafanaAuthConfig;
}
protected static ConfigurableApplicationContext initializeContext(String[] args) {
SpringApplicationBuilder builder = new SpringApplicationBuilder(SpringBootstrap.class);
builder.web(WebApplicationType.NONE);
builder.registerShutdownHook(false);
return builder.run(args);
}
private void shutdownHook() {
final Runnable shutdownRunner = () -> {
if (!shutdown) {
shutdown();
}
};
final Thread hook = new Thread(shutdownRunner, "shutdown-hook-thread");
Runtime.getRuntime().addShutdownHook(hook);
}
public void shutdown() {
List<ChannelFuture> channelFutures = new ArrayList<>();
if (httpChannelHandle != null) {
LOG.info("Closing httpChannelHandle");
channelFutures.add(httpChannelHandle.close());
}
// wait for the channels to shutdown
channelFutures.forEach(f -> {
try {
f.get();
} catch (final Exception e) {
LOG.error("Channel:" + f.channel().config() + " -> " + e.getMessage(), e);
}
});
int quietPeriod = grafanaAuthConfig.getShutdownQuietPeriod();
List<Future<?>> groupFutures = new ArrayList<>();
if (httpBossGroup != null) {
LOG.info("Shutting down httpBossGroup");
groupFutures.add(httpBossGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
if (httpWorkerGroup != null) {
LOG.info("Shutting down httpWorkerGroup");
groupFutures.add(httpWorkerGroup.shutdownGracefully(quietPeriod, 10, TimeUnit.SECONDS));
}
groupFutures.parallelStream().forEach(f -> {
try {
f.get();
} catch (final Exception e) {
LOG.error("Group:" + f.toString() + " -> " + e.getMessage(), e);
}
});
if (applicationContext != null) {
LOG.info("Closing applicationContext");
applicationContext.close();
}
this.shutdown = true;
LOG.info("Server shut down.");
}
private static boolean useEpoll() {
// Should we just return true if this is Linux and if we get an error
// during Epoll
// setup handle it there?
final String os = SystemPropertyUtil.get(OS_NAME).toLowerCase().trim();
final String[] version = SystemPropertyUtil.get(OS_VERSION).toLowerCase().trim().split("\\.");
if (os.startsWith("linux") && version.length >= 3) {
final int major = Integer.parseInt(version[0]);
if (major > EPOLL_MIN_MAJOR_VERSION) {
return true;
} else if (major == EPOLL_MIN_MAJOR_VERSION) {
final int minor = Integer.parseInt(version[1]);
if (minor > EPOLL_MIN_MINOR_VERSION) {
return true;
} else if (minor == EPOLL_MIN_MINOR_VERSION) {
final int patch = Integer.parseInt(version[2].substring(0, 2));
return patch >= EPOLL_MIN_PATCH_VERSION;
} else {
return false;
}
} else {
return false;
}
} else {
return false;
}
}
protected SslContext createSSLContext(GrafanaAuthConfiguration config) throws Exception {
ServerSsl sslCfg = config.getSecurity().getServerSsl();
Boolean generate = sslCfg.isUseGeneratedKeypair();
SslContextBuilder ssl;
if (generate) {
LOG.warn("Using generated self signed server certificate");
Date begin = new Date();
Date end = new Date(begin.getTime() + 86400000);
SelfSignedCertificate ssc = new SelfSignedCertificate("localhost", begin, end);
ssl = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey());
} else {
String cert = sslCfg.getCertificateFile();
String key = sslCfg.getKeyFile();
String keyPass = sslCfg.getKeyPassword();
if (null == cert || null == key) {
throw new IllegalArgumentException("Check your SSL properties, something is wrong.");
}
ssl = SslContextBuilder.forServer(new File(cert), new File(key), keyPass);
}
ssl.ciphers(sslCfg.getUseCiphers());
// Can't set to REQUIRE because the CORS pre-flight requests will fail.
ssl.clientAuth(ClientAuth.OPTIONAL);
Boolean useOpenSSL = sslCfg.isUseOpenssl();
if (useOpenSSL) {
ssl.sslProvider(SslProvider.OPENSSL);
} else {
ssl.sslProvider(SslProvider.JDK);
}
String trustStore = sslCfg.getTrustStoreFile();
if (null != trustStore) {
if (!trustStore.isEmpty()) {
ssl.trustManager(new File(trustStore));
}
}
return ssl.build();
}
protected SSLContext createSSLClientContext(GrafanaAuthConfiguration config) throws Exception {
ClientSsl ssl = config.getSecurity().getClientSsl();
return HttpClient.getSSLContext(ssl.getTrustStoreFile(), ssl.getTrustStoreType(), ssl.getTrustStorePassword(),
ssl.getKeyFile(), ssl.getKeyType(), ssl.getKeyPassword());
}
public void run() throws Exception {
AuthCache.configure(grafanaAuthConfig.getSecurity());
final boolean useEpoll = useEpoll();
Class<? extends ServerSocketChannel> channelClass;
if (useEpoll) {
httpWorkerGroup = new EpollEventLoopGroup();
httpBossGroup = new EpollEventLoopGroup();
channelClass = EpollServerSocketChannel.class;
} else {
httpWorkerGroup = new NioEventLoopGroup();
httpBossGroup = new NioEventLoopGroup();
channelClass = NioServerSocketChannel.class;
}
LOG.info("Using channel class {}", channelClass.getSimpleName());
final int httpPort = grafanaAuthConfig.getHttp().getPort();
final String httpIp = grafanaAuthConfig.getHttp().getIp();
SslContext sslCtx = createSSLContext(grafanaAuthConfig);
if (sslCtx instanceof OpenSslServerContext) {
OpenSslServerContext openssl = (OpenSslServerContext) sslCtx;
String application = "GrafanaAuth_" + httpPort;
OpenSslServerSessionContext opensslCtx = openssl.sessionContext();
opensslCtx.setSessionCacheEnabled(true);
opensslCtx.setSessionCacheSize(128);
opensslCtx.setSessionIdContext(application.getBytes(StandardCharsets.UTF_8));
opensslCtx.setSessionTimeout(grafanaAuthConfig.getSecurity().getSessionMaxAge());
}
SSLContext clientSSLContext = createSSLClientContext(grafanaAuthConfig);
final ServerBootstrap httpServer = new ServerBootstrap();
httpServer.group(httpBossGroup, httpWorkerGroup);
httpServer.channel(channelClass);
httpServer.handler(new LoggingHandler());
HttpClientPool httpClientPool = new HttpClientPool(grafanaAuthConfig.getSecurity(), grafanaAuthConfig.getHttp(),
clientSSLContext);
httpServer.childHandler(setupHttpChannel(grafanaAuthConfig, sslCtx, httpClientPool));
httpServer.option(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
httpServer.option(ChannelOption.SO_BACKLOG, 128);
httpServer.option(ChannelOption.SO_KEEPALIVE, true);
httpChannelHandle = httpServer.bind(httpIp, httpPort).sync().channel();
final String httpAddress = ((InetSocketAddress) httpChannelHandle.localAddress()).getAddress().getHostAddress();
shutdownHook();
LOG.info("Server started. Listening on {}:{} for HTTP traffic", httpAddress, httpPort);
}
public static void main(String[] args) throws Exception {
GrafanaAuth.applicationContext = GrafanaAuth.initializeContext(args);
GrafanaAuthConfiguration grafanaAuthConf = GrafanaAuth.applicationContext
.getBean(GrafanaAuthConfiguration.class);
GrafanaAuth grafanaAuth = new GrafanaAuth(grafanaAuthConf);
try {
grafanaAuth.run();
LATCH.await();
} catch (final InterruptedException e) {
LOG.info("Server shutting down.");
} catch (Exception e) {
LOG.error("Error running server.", e);
} finally {
try {
grafanaAuth.shutdown();
} catch (Exception e) {
System.exit(1);
}
}
}
protected ChannelHandler setupHttpChannel(GrafanaAuthConfiguration config, SslContext sslCtx,
HttpClientPool httpClientPool) {
return new ChannelInitializer<SocketChannel>() {
@Override
protected void initChannel(SocketChannel ch) throws Exception {
ch.pipeline().addLast("ssl", new NonSslRedirectHandler(config.getHttp(), sslCtx));
ch.pipeline().addLast("encoder", new HttpResponseEncoder());
ch.pipeline().addLast("decoder", new HttpRequestDecoder());
ch.pipeline().addLast("compressor", new HttpContentCompressor());
ch.pipeline().addLast("decompressor", new HttpContentDecompressor());
// high maximum contentLength so that grafana snapshots can be delivered
// might not be necessary if inbound chunking (while proxying) is handled
ch.pipeline().addLast("aggregator", new HttpObjectAggregator(2097152));
ch.pipeline().addLast("chunker", new ChunkedWriteHandler());
ch.pipeline().addLast("grafanaDecoder",
new GrafanaRequestDecoder(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("fileServer", new HttpStaticFileServerHandler());
ch.pipeline().addLast("login", new X509LoginRequestHandler(config.getSecurity(), config.getHttp()));
ch.pipeline().addLast("httpRelay", new GrafanaRelayHandler(config, httpClientPool));
ch.pipeline().addLast("error", new TimelyExceptionHandler()
.setIgnoreSslHandshakeErrors(config.getSecurity().getServerSsl().isUseGeneratedKeypair()));
}
};
}
}
@@ -1,32 +0,0 @@
package timely.grafana.auth.configuration;
public class Grafana {
private String host = "localhost";
private Integer port = 3000;
private String protocol = "http";
public void setHost(String host) {
this.host = host;
}
public String getHost() {
return host;
}
public void setPort(Integer port) {
this.port = port;
}
public Integer getPort() {
return port;
}
public void setProtocol(String protocol) {
this.protocol = protocol;
}
public String getProtocol() {
return protocol;
}
}
@@ -1,54 +0,0 @@
package timely.grafana.auth.configuration;
import javax.validation.Valid;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.NestedConfigurationProperty;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import timely.balancer.configuration.BalancerHttp;
import timely.balancer.configuration.BalancerSecurity;
@Validated
@Component
@ConfigurationProperties(prefix = "grafana-auth")
public class GrafanaAuthConfiguration {
@Valid
@NestedConfigurationProperty
private BalancerHttp http = new BalancerHttp();
@Valid
@NestedConfigurationProperty
private BalancerSecurity security = new BalancerSecurity();
@Valid
@NestedConfigurationProperty
private Grafana grafana = new Grafana();
private Integer shutdownQuietPeriod = 5;
public BalancerSecurity getSecurity() {
return security;
}
public BalancerHttp getHttp() {
return http;
}
public Grafana getGrafana() {
return grafana;
}
/**
* Time to wait (in seconds) for connections to finish and to make sure no new
* connections happen before shutting down Netty event loop groups.
*/
public int getShutdownQuietPeriod() {
return this.shutdownQuietPeriod;
}
public void setShutdownQuietPeriod(Integer quietPeriod) {
this.shutdownQuietPeriod = quietPeriod;
}
}
@@ -1,7 +0,0 @@
package timely.grafana.auth.configuration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication(scanBasePackageClasses = { SpringBootstrap.class })
public class SpringBootstrap {
}
@@ -1,234 +0,0 @@
package timely.grafana.auth.netty.http;
import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_LENGTH;
import java.io.ByteArrayOutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Multimap;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.DefaultFullHttpResponse;
import io.netty.handler.codec.http.FullHttpRequest;
import io.netty.handler.codec.http.FullHttpResponse;
import io.netty.handler.codec.http.HttpMethod;
import io.netty.handler.codec.http.HttpResponseStatus;
import io.netty.handler.codec.http.HttpVersion;
import io.netty.util.ReferenceCountUtil;
import org.apache.http.Header;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpDelete;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpHead;
import org.apache.http.client.methods.HttpOptions;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.message.BasicHeader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StreamUtils;
import timely.api.request.timeseries.HttpRequest;
import timely.api.response.TimelyException;
import timely.auth.TimelyPrincipal;
import timely.auth.TimelyUser;
import timely.auth.util.DnUtils;
import timely.auth.util.HttpHeaderUtils;
import timely.balancer.connection.TimelyBalancedHost;
import timely.balancer.connection.http.HttpClientPool;
import timely.grafana.auth.configuration.GrafanaAuthConfiguration;
import timely.grafana.request.GrafanaRequest;
import timely.netty.http.TimelyHttpHandler;
import timely.netty.http.auth.TimelyAuthenticationToken;
public class GrafanaRelayHandler extends SimpleChannelInboundHandler<HttpRequest> implements TimelyHttpHandler {
private static final Logger LOG = LoggerFactory.getLogger(GrafanaRelayHandler.class);
private final HttpClientPool httpClientPool;
private GrafanaAuthConfiguration config;
public GrafanaRelayHandler(GrafanaAuthConfiguration config, HttpClientPool httpClientPool) {
this.config = config;
this.httpClientPool = httpClientPool;
}
@Override
public void channelRead0(ChannelHandlerContext ctx, HttpRequest msg) throws Exception {
CloseableHttpClient client = null;
CloseableHttpResponse relayedResponse = null;
FullHttpRequest request = null;
GrafanaRequest grafanaRequest;
TimelyBalancedHost k = null;
String relayURI = null;
try {
try {
request = msg.getHttpRequest();
String originalURI = request.uri();
originalURI = encodeURI(originalURI);
if (msg instanceof GrafanaRequest) {
grafanaRequest = (GrafanaRequest) msg;
} else {
throw new UnsupportedOperationException("Request not supported");
}
TimelyAuthenticationToken token = grafanaRequest.getToken();
if (token == null) {
throw new IllegalStateException("No token on grafana request");
}
String protocol = config.getGrafana().getProtocol();
String host = config.getGrafana().getHost();
Integer port = config.getGrafana().getPort();
k = TimelyBalancedHost.of(host, -1, port, -1, -1, null);
client = httpClientPool.borrowObject(k);
Multimap<String, String> headers = HttpHeaderUtils.toMultimap(request.headers());
// If incoming user sets these headers, do not relay them
headers.removeAll("x-webauth-user");
headers.removeAll("x-webauth-name");
TimelyPrincipal principal = token.getTimelyPrincipal();
TimelyUser user = principal.getPrimaryUser();
String subjectDn = user.getDn().subjectDN();
String userName = DnUtils.getCommonName(subjectDn);
String loginName = DnUtils.getShortName(subjectDn);
headers.put("x-webauth-user", loginName);
headers.put("x-webauth-name", userName);
List<Header> relayedHeaderList = new ArrayList<>();
for (Map.Entry<String, String> h : headers.entries()) {
if (!h.getKey().equals(CONTENT_LENGTH.toString())) {
relayedHeaderList.add(new BasicHeader(h.getKey(), h.getValue()));
}
}
Header[] headerArray = new Header[relayedHeaderList.size()];
relayedHeaderList.toArray(headerArray);
relayURI = protocol + "://" + host + ":" + port + originalURI;
if (request.method().equals(HttpMethod.HEAD)) {
HttpHead relayedRequest = new HttpHead(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.GET)) {
HttpGet relayedRequest = new HttpGet(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.POST)) {
HttpPost relayedRequest = new HttpPost(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content);
relayedRequest.setEntity(entity);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.PUT)) {
HttpPut relayedRequest = new HttpPut(relayURI);
String content = request.content().toString(StandardCharsets.UTF_8);
StringEntity entity = new StringEntity(content);
relayedRequest.setEntity(entity);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.DELETE)) {
HttpDelete relayedRequest = new HttpDelete(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
} else if (request.method().equals(HttpMethod.OPTIONS)) {
HttpOptions relayedRequest = new HttpOptions(relayURI);
if (headerArray.length > 0) {
relayedRequest.setHeaders(headerArray);
}
relayedResponse = client.execute(relayedRequest);
}
} catch (Exception e) {
String message = e.getMessage();
if (message == null) {
LOG.error("", e);
} else {
LOG.error(message, e);
}
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.INTERNAL_SERVER_ERROR.code(),
e.getMessage(), e.getLocalizedMessage(), e));
return;
} finally {
if (request != null && request instanceof AutoCloseable) {
ReferenceCountUtil.release(request);
}
}
FullHttpResponse response = null;
if (relayedResponse == null) {
this.sendHttpError(ctx, new TimelyException(HttpResponseStatus.METHOD_NOT_ALLOWED.code(),
"Method not allowed", "Method not allowed"));
} else {
LOG.debug("RequestURL:{} ResponseCode:{}", relayURI, relayedResponse.getStatusLine().getStatusCode());
HttpEntity httpEntity = relayedResponse.getEntity();
if (httpEntity == null) {
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()));
} else {
ByteArrayOutputStream baos = new MyByteArrayOutputStream();
StreamUtils.copy(httpEntity.getContent(), baos);
response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
HttpResponseStatus.valueOf(relayedResponse.getStatusLine().getStatusCode()),
Unpooled.copiedBuffer(baos.toByteArray()));
response.headers().set(CONTENT_LENGTH, response.content().readableBytes());
}
for (Header h : relayedResponse.getAllHeaders()) {
response.headers().add(h.getName(), h.getValue());
}
sendResponse(ctx, response);
}
} finally {
if (relayedResponse != null) {
relayedResponse.close();
}
if (client != null && k != null) {
httpClientPool.returnObject(k, client);
} else {
LOG.error("NOT RETURNING CONNECTION! " + msg.getHttpRequest().uri());
}
}
}
static public class MyByteArrayOutputStream extends ByteArrayOutputStream {
public MyByteArrayOutputStream() {
}
public MyByteArrayOutputStream(int size) {
super(size);
}
public int getCount() {
return count;
}
public byte[] getBuf() {
return buf;
}
}
static public String encodeURI(String s) {
try {
s = s.replaceAll("\\{", URLEncoder.encode("{", "UTF-8"));
s = s.replaceAll("\\}", URLEncoder.encode("}", "UTF-8"));
} catch (Exception e) {
}
return s;
}
}
Loaded 100 of 452 files, more files were not shown because too many files have changed in this diff. Show more