001/** 002 * 003 * Licensed to the Apache Software Foundation (ASF) under one 004 * or more contributor license agreements. See the NOTICE file 005 * distributed with this work for additional information 006 * regarding copyright ownership. The ASF licenses this file 007 * to you under the Apache License, Version 2.0 (the 008 * "License"); you may not use this file except in compliance 009 * with the License. You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 */ 019 020 021package org.apache.hadoop.hbase.coprocessor.example; 022 023import java.io.IOException; 024import java.util.List; 025import java.util.Optional; 026import java.util.concurrent.ThreadLocalRandom; 027import org.apache.hadoop.hbase.Cell; 028import org.apache.hadoop.hbase.CoprocessorEnvironment; 029import org.apache.hadoop.hbase.client.Get; 030import org.apache.hadoop.hbase.coprocessor.ObserverContext; 031import org.apache.hadoop.hbase.coprocessor.RegionCoprocessor; 032import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment; 033import org.apache.hadoop.hbase.coprocessor.RegionObserver; 034import org.apache.hadoop.hbase.metrics.Counter; 035import org.apache.hadoop.hbase.metrics.MetricRegistry; 036import org.apache.hadoop.hbase.metrics.Timer; 037import org.apache.hadoop.hbase.regionserver.FlushLifeCycleTracker; 038import org.apache.hadoop.hbase.regionserver.Store; 039import org.apache.hadoop.hbase.regionserver.StoreFile; 040import org.apache.hadoop.hbase.regionserver.compactions.CompactionLifeCycleTracker; 041import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest; 042import org.apache.yetus.audience.InterfaceAudience; 043 044/** 045 * An example coprocessor that collects some metrics to demonstrate the usage of exporting custom 046 * metrics from the coprocessor. 047 * <p> 048 * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb, 049 * etc) as well as JMX output. You can view a snapshot of the metrics by going to the http web UI 050 * of the regionserver page, something like http://myregionserverhost:16030/jmx 051 * </p> 052 * 053 * @see ExampleMasterObserverWithMetrics 054 */ 055@InterfaceAudience.Private 056public class ExampleRegionObserverWithMetrics implements RegionCoprocessor { 057 058 private Counter preGetCounter; 059 private Counter flushCounter; 060 private Counter filesCompactedCounter; 061 private Timer costlyOperationTimer; 062 private ExampleRegionObserver observer; 063 064 class ExampleRegionObserver implements RegionCoprocessor, RegionObserver { 065 @Override 066 public Optional<RegionObserver> getRegionObserver() { 067 return Optional.of(this); 068 } 069 070 @Override 071 public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, 072 List<Cell> results) throws IOException { 073 // Increment the Counter whenever the coprocessor is called 074 preGetCounter.increment(); 075 } 076 077 @Override 078 public void postGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, 079 List<Cell> results) throws IOException { 080 // do a costly (high latency) operation which we want to measure how long it takes by 081 // using a Timer (which is a Meter and a Histogram). 082 long start = System.nanoTime(); 083 try { 084 performCostlyOperation(); 085 } finally { 086 costlyOperationTimer.updateNanos(System.nanoTime() - start); 087 } 088 } 089 090 @Override 091 public void postFlush( 092 ObserverContext<RegionCoprocessorEnvironment> c, 093 FlushLifeCycleTracker tracker) throws IOException { 094 flushCounter.increment(); 095 } 096 097 @Override 098 public void postFlush( 099 ObserverContext<RegionCoprocessorEnvironment> c, Store store, StoreFile resultFile, 100 FlushLifeCycleTracker tracker) throws IOException { 101 flushCounter.increment(); 102 } 103 104 @Override 105 public void postCompactSelection( 106 ObserverContext<RegionCoprocessorEnvironment> c, Store store, 107 List<? extends StoreFile> selected, CompactionLifeCycleTracker tracker, 108 CompactionRequest request) { 109 if (selected != null) { 110 filesCompactedCounter.increment(selected.size()); 111 } 112 } 113 114 private void performCostlyOperation() { 115 try { 116 // simulate the operation by sleeping. 117 Thread.sleep(ThreadLocalRandom.current().nextLong(100)); 118 } catch (InterruptedException ignore) { 119 } 120 } 121 } 122 123 @Override public Optional<RegionObserver> getRegionObserver() { 124 return Optional.of(observer); 125 } 126 127 @Override 128 public void start(CoprocessorEnvironment env) throws IOException { 129 // start for the RegionServerObserver will be called only once in the lifetime of the 130 // server. We will construct and register all metrics that we will track across method 131 // invocations. 132 133 if (env instanceof RegionCoprocessorEnvironment) { 134 // Obtain the MetricRegistry for the RegionServer. Metrics from this registry will be reported 135 // at the region server level per-regionserver. 136 MetricRegistry registry = 137 ((RegionCoprocessorEnvironment) env).getMetricRegistryForRegionServer(); 138 observer = new ExampleRegionObserver(); 139 140 if (preGetCounter == null) { 141 // Create a new Counter, or get the already registered counter. 142 // It is much better to only call this once and save the Counter as a class field instead 143 // of creating the counter every time a coprocessor method is invoked. This will negate 144 // any performance bottleneck coming from map lookups tracking metrics in the registry. 145 // Returned counter instance is shared by all coprocessors of the same class in the same 146 // region server. 147 preGetCounter = registry.counter("preGetRequests"); 148 } 149 150 if (costlyOperationTimer == null) { 151 // Create a Timer to track execution times for the costly operation. 152 costlyOperationTimer = registry.timer("costlyOperation"); 153 } 154 155 if (flushCounter == null) { 156 // Track the number of flushes that have completed 157 flushCounter = registry.counter("flushesCompleted"); 158 } 159 160 if (filesCompactedCounter == null) { 161 // Track the number of files that were compacted (many files may be rewritten in a single 162 // compaction). 163 filesCompactedCounter = registry.counter("filesCompacted"); 164 } 165 } 166 } 167 168 @Override 169 public void stop(CoprocessorEnvironment e) throws IOException { 170 // we should NOT remove / deregister the metrics in stop(). The whole registry will be 171 // removed when the last region of the table is closed. 172 } 173}