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.ipc; 019 020import java.lang.reflect.Method; 021import java.util.HashMap; 022import java.util.Map; 023import org.apache.commons.lang3.StringUtils; 024import org.apache.hadoop.hbase.HBaseRpcServicesBase; 025import org.apache.hadoop.hbase.HConstants; 026import org.apache.hadoop.hbase.security.User; 027import org.apache.yetus.audience.InterfaceAudience; 028 029import org.apache.hbase.thirdparty.com.google.protobuf.Message; 030 031import org.apache.hadoop.hbase.shaded.protobuf.generated.RPCProtos.RequestHeader; 032 033/** 034 * Reads special method annotations and table names to figure a priority for use by QoS facility in 035 * ipc; e.g: rpcs to hbase:meta get priority. 036 */ 037// TODO: Remove. This is doing way too much work just to figure a priority. Do as Elliott 038// suggests and just have the client specify a priority. 039 040// The logic for figuring out high priority RPCs is as follows: 041// 1. if the method is annotated with a QosPriority of QOS_HIGH, 042// that is honored 043// 2. parse out the protobuf message and see if the request is for meta 044// region, and if so, treat it as a high priority RPC 045// Some optimizations for (2) are done in the sub classes - 046// Clients send the argument classname as part of making the RPC. The server 047// decides whether to deserialize the proto argument message based on the 048// pre-established set of argument classes (knownArgumentClasses below). 049// This prevents the server from having to deserialize all proto argument 050// messages prematurely. 051// All the argument classes declare a 'getRegion' method that returns a 052// RegionSpecifier object. Methods can be invoked on the returned object 053// to figure out whether it is a meta region or not. 054@InterfaceAudience.Private 055public abstract class AnnotationReadingPriorityFunction<T extends HBaseRpcServicesBase<?>> 056 implements PriorityFunction { 057 058 protected final Map<String, Integer> annotatedQos; 059 // We need to mock the regionserver instance for some unit tests (set via 060 // setRegionServer method. 061 protected final T rpcServices; 062 063 /** 064 * Constructs the priority function given the RPC server implementation and the annotations on the 065 * methods. 066 * @param rpcServices The RPC server implementation 067 */ 068 public AnnotationReadingPriorityFunction(final T rpcServices) { 069 Map<String, Integer> qosMap = new HashMap<>(); 070 for (Method m : rpcServices.getClass().getMethods()) { 071 QosPriority p = m.getAnnotation(QosPriority.class); 072 if (p != null) { 073 // Since we protobuf'd, and then subsequently, when we went with pb style, method names 074 // are capitalized. This meant that this brittle compare of method names gotten by 075 // reflection no longer matched the method names coming in over pb. 076 // TODO: Get rid of this check. For now, workaround is to capitalize the names we got from 077 // reflection so they have chance of matching the pb ones. 078 String capitalizedMethodName = StringUtils.capitalize(m.getName()); 079 qosMap.put(capitalizedMethodName, p.priority()); 080 } 081 } 082 this.rpcServices = rpcServices; 083 this.annotatedQos = qosMap; 084 } 085 086 /** 087 * Returns a 'priority' based on the request type. 088 * <p/> 089 * Currently the returned priority is used for queue selection. 090 * <p/> 091 * See the {@code SimpleRpcScheduler} as example. It maintains a queue per 'priority type': 092 * <ul> 093 * <li>HIGH_QOS (meta requests)</li> 094 * <li>REPLICATION_QOS (replication requests)</li> 095 * <li>NORMAL_QOS (user requests).</li> 096 * </ul> 097 */ 098 @Override 099 public int getPriority(RequestHeader header, Message param, User user) { 100 int priorityByAnnotation = getAnnotatedPriority(header); 101 102 if (priorityByAnnotation >= 0) { 103 return priorityByAnnotation; 104 } 105 if (param == null) { 106 return HConstants.NORMAL_QOS; 107 } 108 return getBasePriority(header, param); 109 } 110 111 /** 112 * See if the method has an annotation. 113 * @return Return the priority from the annotation. If there isn't an annotation, this returns 114 * something below zero. 115 */ 116 protected int getAnnotatedPriority(RequestHeader header) { 117 String methodName = header.getMethodName(); 118 Integer priorityByAnnotation = annotatedQos.get(methodName); 119 if (priorityByAnnotation != null) { 120 return normalizePriority(priorityByAnnotation); 121 } 122 return -1; 123 } 124 125 protected abstract int normalizePriority(int priority); 126 127 /** 128 * Get the priority for a given request from the header and the param. 129 * <p/> 130 * This doesn't consider which user is sending the request at all. 131 * <p/> 132 * This doesn't consider annotations 133 */ 134 protected abstract int getBasePriority(RequestHeader header, Message param); 135}