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 020package org.apache.hadoop.hbase.coprocessor; 021 022import java.io.IOException; 023import java.nio.charset.StandardCharsets; 024import java.util.HashSet; 025import java.util.List; 026import java.util.Optional; 027import java.util.Set; 028 029import org.apache.hadoop.hbase.Cell; 030import org.apache.hadoop.hbase.CoprocessorEnvironment; 031import org.apache.hadoop.hbase.TableName; 032import org.apache.hadoop.hbase.client.Delete; 033import org.apache.hadoop.hbase.client.Durability; 034import org.apache.hadoop.hbase.client.Get; 035import org.apache.hadoop.hbase.client.Put; 036import org.apache.hadoop.hbase.client.Row; 037import org.apache.hadoop.hbase.ipc.RpcServer; 038import org.apache.hadoop.hbase.metrics.MetricRegistry; 039import org.apache.hadoop.hbase.util.LossyCounting; 040import org.apache.hadoop.hbase.wal.WALEdit; 041import org.apache.yetus.audience.InterfaceAudience; 042 043import org.apache.hbase.thirdparty.com.google.common.collect.ImmutableMap; 044 045 046/** 047 * A coprocessor that collects metrics from meta table. 048 * <p> 049 * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb, 050 * etc) as well as JMX output. 051 * </p> 052 * @see MetaTableMetrics 053 */ 054 055@InterfaceAudience.Private 056public class MetaTableMetrics implements RegionCoprocessor { 057 058 private ExampleRegionObserverMeta observer; 059 private MetricRegistry registry; 060 private LossyCounting clientMetricsLossyCounting, regionMetricsLossyCounting; 061 private boolean active = false; 062 private Set<String> metrics = new HashSet<String>(); 063 064 enum MetaTableOps { 065 GET, PUT, DELETE; 066 } 067 068 private ImmutableMap<Class, MetaTableOps> opsNameMap = 069 ImmutableMap.<Class, MetaTableOps>builder() 070 .put(Put.class, MetaTableOps.PUT) 071 .put(Get.class, MetaTableOps.GET) 072 .put(Delete.class, MetaTableOps.DELETE) 073 .build(); 074 075 class ExampleRegionObserverMeta implements RegionCoprocessor, RegionObserver { 076 077 @Override 078 public Optional<RegionObserver> getRegionObserver() { 079 return Optional.of(this); 080 } 081 082 @Override 083 public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get, 084 List<Cell> results) throws IOException { 085 registerAndMarkMetrics(e, get); 086 } 087 088 @Override 089 public void prePut(ObserverContext<RegionCoprocessorEnvironment> e, Put put, WALEdit edit, 090 Durability durability) throws IOException { 091 registerAndMarkMetrics(e, put); 092 } 093 094 @Override 095 public void preDelete(ObserverContext<RegionCoprocessorEnvironment> e, Delete delete, 096 WALEdit edit, Durability durability) throws IOException { 097 registerAndMarkMetrics(e, delete); 098 } 099 100 private void registerAndMarkMetrics(ObserverContext<RegionCoprocessorEnvironment> e, Row row){ 101 if (!active || !isMetaTableOp(e)) { 102 return; 103 } 104 tableMetricRegisterAndMark(row); 105 clientMetricRegisterAndMark(); 106 regionMetricRegisterAndMark(row); 107 opMetricRegisterAndMark(row); 108 opWithClientMetricRegisterAndMark(row); 109 } 110 111 /** 112 * Get table name from Ops such as: get, put, delete. 113 * @param op such as get, put or delete. 114 */ 115 private String getTableNameFromOp(Row op) { 116 String tableName = null; 117 String tableRowKey = new String(((Row) op).getRow(), StandardCharsets.UTF_8); 118 if (tableRowKey.isEmpty()) { 119 return null; 120 } 121 tableName = tableRowKey.split(",").length > 0 ? tableRowKey.split(",")[0] : null; 122 return tableName; 123 } 124 125 /** 126 * Get regionId from Ops such as: get, put, delete. 127 * @param op such as get, put or delete. 128 */ 129 private String getRegionIdFromOp(Row op) { 130 String regionId = null; 131 String tableRowKey = new String(((Row) op).getRow(), StandardCharsets.UTF_8); 132 if (tableRowKey.isEmpty()) { 133 return null; 134 } 135 regionId = tableRowKey.split(",").length > 2 ? tableRowKey.split(",")[2] : null; 136 return regionId; 137 } 138 139 private boolean isMetaTableOp(ObserverContext<RegionCoprocessorEnvironment> e) { 140 return TableName.META_TABLE_NAME 141 .equals(e.getEnvironment().getRegionInfo().getTable()); 142 } 143 144 private void clientMetricRegisterAndMark() { 145 // Mark client metric 146 String clientIP = RpcServer.getRemoteIp() != null ? RpcServer.getRemoteIp().toString() : null; 147 if (clientIP == null || clientIP.isEmpty()) { 148 return; 149 } 150 String clientRequestMeter = clientRequestMeterName(clientIP); 151 clientMetricsLossyCounting.add(clientRequestMeter); 152 registerAndMarkMeter(clientRequestMeter); 153 } 154 155 private void tableMetricRegisterAndMark(Row op) { 156 // Mark table metric 157 String tableName = getTableNameFromOp(op); 158 if (tableName == null || tableName.isEmpty()) { 159 return; 160 } 161 String tableRequestMeter = tableMeterName(tableName); 162 registerAndMarkMeter(tableRequestMeter); 163 } 164 165 private void regionMetricRegisterAndMark(Row op) { 166 // Mark region metric 167 String regionId = getRegionIdFromOp(op); 168 if (regionId == null || regionId.isEmpty()) { 169 return; 170 } 171 String regionRequestMeter = regionMeterName(regionId); 172 regionMetricsLossyCounting.add(regionRequestMeter); 173 registerAndMarkMeter(regionRequestMeter); 174 } 175 176 private void opMetricRegisterAndMark(Row op) { 177 // Mark access type ["get", "put", "delete"] metric 178 String opMeterName = opMeterName(op); 179 if (opMeterName == null || opMeterName.isEmpty()) { 180 return; 181 } 182 registerAndMarkMeter(opMeterName); 183 } 184 185 private void opWithClientMetricRegisterAndMark(Object op) { 186 // // Mark client + access type metric 187 String opWithClientMeterName = opWithClientMeterName(op); 188 if (opWithClientMeterName == null || opWithClientMeterName.isEmpty()) { 189 return; 190 } 191 registerAndMarkMeter(opWithClientMeterName); 192 } 193 194 // Helper function to register and mark meter if not present 195 private void registerAndMarkMeter(String requestMeter) { 196 if (requestMeter.isEmpty()) { 197 return; 198 } 199 if(!registry.get(requestMeter).isPresent()){ 200 metrics.add(requestMeter); 201 } 202 registry.meter(requestMeter).mark(); 203 } 204 205 private String opWithClientMeterName(Object op) { 206 // Extract meter name containing the client IP 207 String clientIP = RpcServer.getRemoteIp() != null ? RpcServer.getRemoteIp().toString() : ""; 208 if (clientIP.isEmpty()) { 209 return ""; 210 } 211 MetaTableOps ops = opsNameMap.get(op.getClass()); 212 String opWithClientMeterName = ""; 213 switch (ops) { 214 case GET: 215 opWithClientMeterName = String.format("MetaTable_client_%s_get_request", clientIP); 216 break; 217 case PUT: 218 opWithClientMeterName = String.format("MetaTable_client_%s_put_request", clientIP); 219 break; 220 case DELETE: 221 opWithClientMeterName = String.format("MetaTable_client_%s_delete_request", clientIP); 222 break; 223 default: 224 break; 225 } 226 return opWithClientMeterName; 227 } 228 229 private String opMeterName(Object op) { 230 // Extract meter name containing the access type 231 MetaTableOps ops = opsNameMap.get(op.getClass()); 232 String opMeterName = ""; 233 switch (ops) { 234 case GET: 235 opMeterName = "MetaTable_get_request"; 236 break; 237 case PUT: 238 opMeterName = "MetaTable_put_request"; 239 break; 240 case DELETE: 241 opMeterName = "MetaTable_delete_request"; 242 break; 243 default: 244 break; 245 } 246 return opMeterName; 247 } 248 249 private String tableMeterName(String tableName) { 250 // Extract meter name containing the table name 251 return String.format("MetaTable_table_%s_request", tableName); 252 } 253 254 private String clientRequestMeterName(String clientIP) { 255 // Extract meter name containing the client IP 256 if (clientIP.isEmpty()) { 257 return ""; 258 } 259 return String.format("MetaTable_client_%s_lossy_request", clientIP); 260 } 261 262 private String regionMeterName(String regionId) { 263 // Extract meter name containing the region ID 264 return String.format("MetaTable_region_%s_lossy_request", regionId); 265 } 266 } 267 268 @Override 269 public Optional<RegionObserver> getRegionObserver() { 270 return Optional.of(observer); 271 } 272 273 @Override 274 public void start(CoprocessorEnvironment env) throws IOException { 275 observer = new ExampleRegionObserverMeta(); 276 if (env instanceof RegionCoprocessorEnvironment 277 && ((RegionCoprocessorEnvironment) env).getRegionInfo().getTable() != null 278 && ((RegionCoprocessorEnvironment) env).getRegionInfo().getTable() 279 .equals(TableName.META_TABLE_NAME)) { 280 RegionCoprocessorEnvironment regionCoprocessorEnv = (RegionCoprocessorEnvironment) env; 281 registry = regionCoprocessorEnv.getMetricRegistryForRegionServer(); 282 LossyCounting.LossyCountingListener listener = new LossyCounting.LossyCountingListener(){ 283 @Override public void sweep(String key) { 284 registry.remove(key); 285 metrics.remove(key); 286 } 287 }; 288 clientMetricsLossyCounting = new LossyCounting("clientMetaMetrics",listener); 289 regionMetricsLossyCounting = new LossyCounting("regionMetaMetrics",listener); 290 // only be active mode when this region holds meta table. 291 active = true; 292 } 293 } 294 295 @Override 296 public void stop(CoprocessorEnvironment env) throws IOException { 297 // since meta region can move around, clear stale metrics when stop. 298 for(String metric:metrics){ 299 registry.remove(metric); 300 } 301 } 302}