1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 package org.apache.hadoop.hbase.ipc;
22
23 import java.lang.reflect.Proxy;
24 import java.lang.reflect.Method;
25 import java.lang.reflect.Array;
26 import java.lang.reflect.InvocationHandler;
27 import java.lang.reflect.InvocationTargetException;
28
29 import java.net.InetSocketAddress;
30 import java.io.*;
31 import java.util.Map;
32 import java.util.HashMap;
33
34 import javax.net.SocketFactory;
35
36 import org.apache.commons.logging.*;
37
38 import org.apache.hadoop.hbase.HRegionInfo;
39 import org.apache.hadoop.hbase.client.Operation;
40 import org.apache.hadoop.hbase.io.HbaseObjectWritable;
41 import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
42 import org.apache.hadoop.hbase.regionserver.HRegionServer;
43 import org.apache.hadoop.hbase.util.Bytes;
44 import org.apache.hadoop.hbase.util.Objects;
45 import org.apache.hadoop.io.*;
46 import org.apache.hadoop.ipc.RPC;
47 import org.apache.hadoop.hbase.ipc.VersionedProtocol;
48 import org.apache.hadoop.hbase.security.User;
49 import org.apache.hadoop.security.authorize.ServiceAuthorizationManager;
50 import org.apache.hadoop.conf.*;
51
52 import org.codehaus.jackson.map.ObjectMapper;
53
54
55 class WritableRpcEngine implements RpcEngine {
56
57
58 private static final Log LOG = LogFactory.getLog("org.apache.hadoop.ipc.RPCEngine");
59
60 private static class Invoker implements InvocationHandler {
61 private Class<? extends VersionedProtocol> protocol;
62 private InetSocketAddress address;
63 private User ticket;
64 private HBaseClient client;
65 final private int rpcTimeout;
66
67 public Invoker(HBaseClient client,
68 Class<? extends VersionedProtocol> protocol,
69 InetSocketAddress address, User ticket,
70 Configuration conf, int rpcTimeout) {
71 this.protocol = protocol;
72 this.address = address;
73 this.ticket = ticket;
74 this.client = client;
75 this.rpcTimeout = rpcTimeout;
76 }
77
78 public Object invoke(Object proxy, Method method, Object[] args)
79 throws Throwable {
80 final boolean logDebug = LOG.isDebugEnabled();
81 long startTime = 0;
82 if (logDebug) {
83 startTime = System.currentTimeMillis();
84 }
85
86 HbaseObjectWritable value = (HbaseObjectWritable)
87 client.call(new Invocation(method, protocol, args), address,
88 protocol, ticket, rpcTimeout);
89 if (logDebug) {
90
91 long callTime = System.currentTimeMillis() - startTime;
92 LOG.debug("Call: " + method.getName() + " " + callTime);
93 }
94 return value.get();
95 }
96 }
97
98 private Configuration conf;
99 private HBaseClient client;
100
101 @Override
102 public void setConf(Configuration config) {
103 this.conf = config;
104
105 if (this.client != null) {
106 this.client.stop();
107 }
108 this.client = new HBaseClient(HbaseObjectWritable.class, conf);
109 }
110
111 @Override
112 public Configuration getConf() {
113 return conf;
114 }
115
116
117
118 @Override
119 public <T extends VersionedProtocol> T getProxy(
120 Class<T> protocol, long clientVersion,
121 InetSocketAddress addr, Configuration conf, int rpcTimeout)
122 throws IOException {
123 if (this.client == null) {
124 throw new IOException("Client must be initialized by calling setConf(Configuration)");
125 }
126
127 T proxy =
128 (T) Proxy.newProxyInstance(
129 protocol.getClassLoader(), new Class[] { protocol },
130 new Invoker(client, protocol, addr, User.getCurrent(), conf,
131 HBaseRPC.getRpcTimeout(rpcTimeout)));
132
133
134
135
136
137
138 long serverVersion = ((VersionedProtocol)proxy)
139 .getProtocolVersion(protocol.getName(), clientVersion);
140 if (serverVersion != clientVersion) {
141 throw new HBaseRPC.VersionMismatch(protocol.getName(), clientVersion,
142 serverVersion);
143 }
144
145 return proxy;
146 }
147
148
149
150
151 @Override
152 public Object[] call(Method method, Object[][] params,
153 InetSocketAddress[] addrs,
154 Class<? extends VersionedProtocol> protocol,
155 User ticket, Configuration conf)
156 throws IOException, InterruptedException {
157 if (this.client == null) {
158 throw new IOException("Client must be initialized by calling setConf(Configuration)");
159 }
160
161 Invocation[] invocations = new Invocation[params.length];
162 for (int i = 0; i < params.length; i++) {
163 invocations[i] = new Invocation(method, protocol, params[i]);
164 }
165
166 Writable[] wrappedValues =
167 client.call(invocations, addrs, protocol, ticket);
168
169 if (method.getReturnType() == Void.TYPE) {
170 return null;
171 }
172
173 Object[] values =
174 (Object[])Array.newInstance(method.getReturnType(), wrappedValues.length);
175 for (int i = 0; i < values.length; i++) {
176 if (wrappedValues[i] != null) {
177 values[i] = ((HbaseObjectWritable)wrappedValues[i]).get();
178 }
179 }
180
181 return values;
182 }
183
184 @Override
185 public void close() {
186 if (this.client != null) {
187 this.client.stop();
188 }
189 }
190
191
192
193 public Server getServer(Class<? extends VersionedProtocol> protocol,
194 Object instance,
195 Class<?>[] ifaces,
196 String bindAddress, int port,
197 int numHandlers,
198 int metaHandlerCount, boolean verbose,
199 Configuration conf, int highPriorityLevel)
200 throws IOException {
201 return new Server(instance, ifaces, conf, bindAddress, port, numHandlers,
202 metaHandlerCount, verbose, highPriorityLevel);
203 }
204
205
206 public static class Server extends HBaseServer {
207 private Object instance;
208 private Class<?> implementation;
209 private Class<?>[] ifaces;
210 private boolean verbose;
211 private boolean authorize = false;
212
213
214 private static ObjectMapper mapper = new ObjectMapper();
215
216 private static final String WARN_RESPONSE_TIME =
217 "hbase.ipc.warn.response.time";
218 private static final String WARN_RESPONSE_SIZE =
219 "hbase.ipc.warn.response.size";
220
221
222 private static final int DEFAULT_WARN_RESPONSE_TIME = 10000;
223 private static final int DEFAULT_WARN_RESPONSE_SIZE = 100 * 1024 * 1024;
224
225
226 private static final String ABOVE_ONE_SEC_METRIC = ".aboveOneSec.";
227
228 private final int warnResponseTime;
229 private final int warnResponseSize;
230
231 private static String classNameBase(String className) {
232 String[] names = className.split("\\.", -1);
233 if (names == null || names.length == 0) {
234 return className;
235 }
236 return names[names.length-1];
237 }
238
239
240
241
242
243
244
245
246
247
248 public Server(Object instance, final Class<?>[] ifaces,
249 Configuration conf, String bindAddress, int port,
250 int numHandlers, int metaHandlerCount, boolean verbose,
251 int highPriorityLevel) throws IOException {
252 super(bindAddress, port, Invocation.class, numHandlers, metaHandlerCount,
253 conf, classNameBase(instance.getClass().getName()),
254 highPriorityLevel);
255 this.instance = instance;
256 this.implementation = instance.getClass();
257 this.verbose = verbose;
258
259 this.ifaces = ifaces;
260
261
262 String [] metricSuffixes = new String [] {ABOVE_ONE_SEC_METRIC};
263 this.rpcMetrics.createMetrics(this.ifaces, false, metricSuffixes);
264
265 this.authorize =
266 conf.getBoolean(
267 ServiceAuthorizationManager.SERVICE_AUTHORIZATION_CONFIG, false);
268
269 this.warnResponseTime = conf.getInt(WARN_RESPONSE_TIME,
270 DEFAULT_WARN_RESPONSE_TIME);
271 this.warnResponseSize = conf.getInt(WARN_RESPONSE_SIZE,
272 DEFAULT_WARN_RESPONSE_SIZE);
273 }
274
275 @Override
276 public Writable call(Class<? extends VersionedProtocol> protocol,
277 Writable param, long receivedTime, MonitoredRPCHandler status)
278 throws IOException {
279 try {
280 Invocation call = (Invocation)param;
281 if(call.getMethodName() == null) {
282 throw new IOException("Could not find requested method, the usual " +
283 "cause is a version mismatch between client and server.");
284 }
285 if (verbose) log("Call: " + call);
286 status.setRPC(call.getMethodName(), call.getParameters(), receivedTime);
287 status.setRPCPacket(param);
288 status.resume("Servicing call");
289
290 Method method =
291 protocol.getMethod(call.getMethodName(),
292 call.getParameterClasses());
293 method.setAccessible(true);
294
295
296
297 if (!method.getDeclaringClass().equals(VersionedProtocol.class)) {
298 long clientVersion = call.getProtocolVersion();
299 ProtocolSignature serverInfo = ((VersionedProtocol) instance)
300 .getProtocolSignature(protocol.getCanonicalName(), call
301 .getProtocolVersion(), call.getClientMethodsHash());
302 long serverVersion = serverInfo.getVersion();
303 if (serverVersion != clientVersion) {
304 LOG.warn("Version mismatch: client version=" + clientVersion
305 + ", server version=" + serverVersion);
306 throw new RPC.VersionMismatch(protocol.getName(), clientVersion,
307 serverVersion);
308 }
309 }
310 Object impl = null;
311 if (protocol.isAssignableFrom(this.implementation)) {
312 impl = this.instance;
313 }
314 else {
315 throw new HBaseRPC.UnknownProtocolException(protocol);
316 }
317
318 long startTime = System.currentTimeMillis();
319 Object[] params = call.getParameters();
320 Object value = method.invoke(impl, params);
321 int processingTime = (int) (System.currentTimeMillis() - startTime);
322 int qTime = (int) (startTime-receivedTime);
323 if (TRACELOG.isDebugEnabled()) {
324 TRACELOG.debug("Call #" + CurCall.get().id +
325 "; Served: " + protocol.getSimpleName()+"#"+call.getMethodName() +
326 " queueTime=" + qTime +
327 " processingTime=" + processingTime +
328 " contents=" + Objects.describeQuantity(params));
329 }
330 rpcMetrics.rpcQueueTime.inc(qTime);
331 rpcMetrics.rpcProcessingTime.inc(processingTime);
332 rpcMetrics.inc(call.getMethodName(), processingTime);
333 if (verbose) log("Return: "+value);
334
335 HbaseObjectWritable retVal =
336 new HbaseObjectWritable(method.getReturnType(), value);
337 long responseSize = retVal.getWritableSize();
338
339
340 boolean tooSlow = (processingTime > warnResponseTime
341 && warnResponseTime > -1);
342 boolean tooLarge = (responseSize > warnResponseSize
343 && warnResponseSize > -1);
344 if (tooSlow || tooLarge) {
345
346
347 logResponse(call, (tooLarge ? "TooLarge" : "TooSlow"),
348 status.getClient(), startTime, processingTime, qTime,
349 responseSize);
350
351 if (tooSlow) {
352 rpcMetrics.rpcSlowResponseTime.inc(processingTime);
353 }
354 }
355 if (processingTime > 1000) {
356
357
358
359 rpcMetrics.inc(call.getMethodName() + ABOVE_ONE_SEC_METRIC,
360 processingTime);
361 }
362
363 return retVal;
364 } catch (InvocationTargetException e) {
365 Throwable target = e.getTargetException();
366 if (target instanceof IOException) {
367 throw (IOException)target;
368 }
369 IOException ioe = new IOException(target.toString());
370 ioe.setStackTrace(target.getStackTrace());
371 throw ioe;
372 } catch (Throwable e) {
373 if (!(e instanceof IOException)) {
374 LOG.error("Unexpected throwable object ", e);
375 }
376 IOException ioe = new IOException(e.toString());
377 ioe.setStackTrace(e.getStackTrace());
378 throw ioe;
379 }
380 }
381
382
383
384
385
386
387
388
389
390
391
392
393
394 private void logResponse(Invocation call, String tag, String clientAddress,
395 long startTime, int processingTime, int qTime, long responseSize)
396 throws IOException {
397 Object params[] = call.getParameters();
398
399 ObjectMapper mapper = new ObjectMapper();
400
401 Map<String, Object> responseInfo = new HashMap<String, Object>();
402 responseInfo.put("starttimems", startTime);
403 responseInfo.put("processingtimems", processingTime);
404 responseInfo.put("queuetimems", qTime);
405 responseInfo.put("responsesize", responseSize);
406 responseInfo.put("client", clientAddress);
407 responseInfo.put("class", instance.getClass().getSimpleName());
408 responseInfo.put("method", call.getMethodName());
409 if (params.length == 2 && instance instanceof HRegionServer &&
410 params[0] instanceof byte[] &&
411 params[1] instanceof Operation) {
412
413
414 byte [] tableName =
415 HRegionInfo.parseRegionName((byte[]) params[0])[0];
416 responseInfo.put("table", Bytes.toStringBinary(tableName));
417
418 responseInfo.putAll(((Operation) params[1]).toMap());
419
420 LOG.warn("(operation" + tag + "): " +
421 mapper.writeValueAsString(responseInfo));
422 } else if (params.length == 1 && instance instanceof HRegionServer &&
423 params[0] instanceof Operation) {
424
425 responseInfo.putAll(((Operation) params[0]).toMap());
426
427 LOG.warn("(operation" + tag + "): " +
428 mapper.writeValueAsString(responseInfo));
429 } else {
430
431
432 responseInfo.put("call", call.toString());
433 LOG.warn("(response" + tag + "): " +
434 mapper.writeValueAsString(responseInfo));
435 }
436 }
437 }
438
439 protected static void log(String value) {
440 String v = value;
441 if (v != null && v.length() > 55)
442 v = v.substring(0, 55)+"...";
443 LOG.info(v);
444 }
445 }