From a307d8f595893815dd3bca241e206c2d21956356 Mon Sep 17 00:00:00 2001 From: Michael Nitschinger Date: Wed, 29 May 2013 10:13:24 +0200 Subject: [PATCH] Adding client and cluster JMX stats. --- pom.xml | 5 ++ .../spring/monitor/AbstractMonitor.java | 17 ++++ .../couchbase/spring/monitor/ClientInfo.java | 64 +++++++++++++++ .../couchbase/spring/monitor/ClusterInfo.java | 77 ++++++++++++------- .../spring/monitor/ClientInfoTest.java | 36 +++++++++ .../spring/monitor/ClusterInfoTest.java | 40 ++++++++++ 6 files changed, 213 insertions(+), 26 deletions(-) create mode 100644 src/main/java/com/couchbase/spring/monitor/ClientInfo.java create mode 100644 src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java create mode 100644 src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java diff --git a/pom.xml b/pom.xml index 1058b52d..20701336 100644 --- a/pom.xml +++ b/pom.xml @@ -49,6 +49,11 @@ spring-context ${spring} + + org.springframework + spring-web + ${spring} + cglib cglib diff --git a/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java b/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java index 653844c4..afc47c1d 100644 --- a/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java +++ b/src/main/java/com/couchbase/spring/monitor/AbstractMonitor.java @@ -1,8 +1,13 @@ package com.couchbase.spring.monitor; import com.couchbase.client.CouchbaseClient; +import org.springframework.web.client.RestTemplate; +import java.net.InetSocketAddress; import java.net.SocketAddress; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; import java.util.Map; /** @@ -12,6 +17,8 @@ public abstract class AbstractMonitor { private CouchbaseClient client; + private RestTemplate template = new RestTemplate(); + protected AbstractMonitor(final CouchbaseClient client) { this.client = client; } @@ -20,6 +27,16 @@ public abstract class AbstractMonitor { return client; } + protected RestTemplate getTemplate() { + return template; + } + + protected String randomAvailableHostname() { + List available = (ArrayList) client.getAvailableServers(); + Collections.shuffle(available); + return ((InetSocketAddress) available.get(0)).getHostName(); + } + /** * Fetches stats for all nodes. * diff --git a/src/main/java/com/couchbase/spring/monitor/ClientInfo.java b/src/main/java/com/couchbase/spring/monitor/ClientInfo.java new file mode 100644 index 00000000..85f29aef --- /dev/null +++ b/src/main/java/com/couchbase/spring/monitor/ClientInfo.java @@ -0,0 +1,64 @@ +/** + * Copyright (C) 2009-2012 Couchbase, Inc. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALING + * IN THE SOFTWARE. + */ +package com.couchbase.spring.monitor; + +import com.couchbase.client.CouchbaseClient; +import org.springframework.jmx.export.annotation.ManagedAttribute; +import org.springframework.jmx.export.annotation.ManagedResource; + +import java.net.SocketAddress; + +/** + * Exposes basic client information. + */ +@ManagedResource(description = "Client Information") +public class ClientInfo extends AbstractMonitor { + + public ClientInfo(final CouchbaseClient client) { + super(client); + } + + @ManagedAttribute(description = "Hostnames of connected nodes") + public String getHostNames() { + StringBuilder result = new StringBuilder(); + for (SocketAddress node : getStats().keySet()) { + result.append(node.toString()).append(","); + } + return result.toString(); + } + + @ManagedAttribute(description = "Number of connected nodes") + public int getNumberOfNodes() { + return getStats().keySet().size(); + } + + @ManagedAttribute(description = "Number of connected active nodes") + public int getNumberOfActiveNodes() { + return getClient().getAvailableServers().size(); + } + + @ManagedAttribute(description = "Number of connected inactive nodes") + public int getNumberOfInactiveNodes() { + return getClient().getUnavailableServers().size(); + } + +} diff --git a/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java b/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java index 7cdc0bc9..0c0a4f3d 100644 --- a/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java +++ b/src/main/java/com/couchbase/spring/monitor/ClusterInfo.java @@ -1,13 +1,15 @@ package com.couchbase.spring.monitor; import com.couchbase.client.CouchbaseClient; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.MappingJacksonHttpMessageConverter; import org.springframework.jmx.export.annotation.ManagedAttribute; -import org.springframework.jmx.export.annotation.ManagedOperation; +import org.springframework.jmx.export.annotation.ManagedMetric; import org.springframework.jmx.export.annotation.ManagedResource; -import java.net.SocketAddress; -import java.util.List; -import java.util.Map; -import java.util.Set; +import org.springframework.web.client.RestTemplate; + +import java.util.Arrays; +import java.util.HashMap; /** * Exposes basic cluster information. @@ -19,31 +21,54 @@ public class ClusterInfo extends AbstractMonitor { super(client); } - @ManagedAttribute(description = "All hostnames of nodes in the cluster") - public String getHostNames() { - StringBuilder result = new StringBuilder(); - for (SocketAddress node : getStats().keySet()) { - result.append(node.toString()).append(","); - } - return result.toString(); + @ManagedMetric(description = "Total RAM assigned") + public long getTotalRAMAssigned() { + return (Long) parseStorageTotals().get("ram").get("total"); } - @ManagedAttribute(description = "Number of nodes currently in the cluster") - public int getNumberOfNodes() { - return getStats().keySet().size(); + @ManagedMetric(description = "Total RAM used") + public long getTotalRAMUsed() { + return (Long) parseStorageTotals().get("ram").get("used"); } - @ManagedAttribute(description = "Couchbase Server versions of nodes in the cluster") - public String getNodeVersions() { - StringBuilder result = new StringBuilder(); - for(Map.Entry> entry : getStats().entrySet()) { - for(Map.Entry stat : entry.getValue().entrySet()) { - if (stat.getKey().equals("ep_version")) { - result.append(stat.getValue()).append(","); - } - } - } - return result.toString(); + @ManagedMetric(description = "Total Disk Space assigned") + public long getTotalDiskAssigned() { + return (Long) parseStorageTotals().get("hdd").get("total"); + } + + @ManagedMetric(description = "Total Disk Space used") + public long getTotalDiskUsed() { + return (Long) parseStorageTotals().get("hdd").get("used"); + } + + @ManagedMetric(description = "Total Disk Space free") + public long getTotalDiskFree() { + return (Long) parseStorageTotals().get("hdd").get("free"); + } + + @ManagedAttribute(description = "Cluster is Balanced") + public boolean getIsBalanced() { + return (Boolean) fetchPoolInfo().get("balanced"); + } + + @ManagedAttribute(description = "Rebalance Status") + public String getRebalanceStatus() { + return (String) fetchPoolInfo().get("rebalanceStatus"); + } + + @ManagedAttribute(description = "Maximum Available Buckets") + public int getMaxBuckets() { + return (Integer) fetchPoolInfo().get("maxBucketCount"); + } + + private HashMap fetchPoolInfo() { + return getTemplate().getForObject("http://" + + randomAvailableHostname() + ":8091/pools/default", HashMap.class); + } + + private HashMap parseStorageTotals() { + HashMap stats = fetchPoolInfo(); + return (HashMap) stats.get("storageTotals"); } } diff --git a/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java b/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java new file mode 100644 index 00000000..fbd98f11 --- /dev/null +++ b/src/test/java/com/couchbase/spring/monitor/ClientInfoTest.java @@ -0,0 +1,36 @@ +package com.couchbase.spring.monitor; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.spring.TestApplicationConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static junit.framework.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestApplicationConfig.class) +public class ClientInfoTest { + + @Autowired + private CouchbaseClient client; + + private ClientInfo ci; + + @Before + public void setup() { + ci = new ClientInfo(client); + } + + @Test + public void hostNames() { + String hostnames = ci.getHostNames(); + assertNotNull(hostnames); + assertFalse(hostnames.isEmpty()); + } + +} diff --git a/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java b/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java new file mode 100644 index 00000000..3fc41317 --- /dev/null +++ b/src/test/java/com/couchbase/spring/monitor/ClusterInfoTest.java @@ -0,0 +1,40 @@ +package com.couchbase.spring.monitor; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.spring.TestApplicationConfig; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.test.context.ContextConfiguration; +import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; + +import static junit.framework.Assert.assertNotNull; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertFalse; + +@RunWith(SpringJUnit4ClassRunner.class) +@ContextConfiguration(classes = TestApplicationConfig.class) +public class ClusterInfoTest { + + @Autowired + private CouchbaseClient client; + + private ClusterInfo ci; + + @Before + public void setup() { + ci = new ClusterInfo(client); + } + + @Test + public void totalRAMAssigned() { + assertTrue(ci.getTotalRAMAssigned() > 0); + } + + @Test + public void totalRAMUsed() { + assertTrue(ci.getTotalRAMUsed() > 0); + } + +}