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