View Javadoc

1   /**
2     *
3     * Licensed to the Apache Software Foundation (ASF) under one
4     * or more contributor license agreements.  See the NOTICE file
5     * distributed with this work for additional information
6     * regarding copyright ownership.  The ASF licenses this file
7     * to you under the Apache License, Version 2.0 (the
8     * "License"); you may not use this file except in compliance
9     * with the License.  You may obtain a copy of the License at
10    *
11    *     http://www.apache.org/licenses/LICENSE-2.0
12    *
13    * Unless required by applicable law or agreed to in writing, software
14    * distributed under the License is distributed on an "AS IS" BASIS,
15    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    * See the License for the specific language governing permissions and
17    * limitations under the License.
18    */
19  package org.apache.hadoop.hbase.ipc;
20  
21  import static org.junit.Assert.assertEquals;
22  import static org.junit.Assert.assertTrue;
23  import static org.junit.Assert.fail;
24  import static org.mockito.Matchers.anyInt;
25  import static org.mockito.Matchers.anyObject;
26  import static org.mockito.Mockito.doThrow;
27  import static org.mockito.Mockito.spy;
28  import static org.mockito.Mockito.verify;
29  import static org.mockito.internal.verification.VerificationModeFactory.times;
30  
31  import java.io.IOException;
32  import java.net.InetAddress;
33  import java.net.InetSocketAddress;
34  import java.net.Socket;
35  import java.util.ArrayList;
36  import java.util.List;
37  
38  import javax.net.SocketFactory;
39  
40  import org.apache.commons.logging.Log;
41  import org.apache.commons.logging.LogFactory;
42  import org.apache.hadoop.conf.Configuration;
43  import org.apache.hadoop.hbase.Cell;
44  import org.apache.hadoop.hbase.CellScannable;
45  import org.apache.hadoop.hbase.CellScanner;
46  import org.apache.hadoop.hbase.CellUtil;
47  import org.apache.hadoop.hbase.HBaseConfiguration;
48  import org.apache.hadoop.hbase.HConstants;
49  import org.apache.hadoop.hbase.HRegionInfo;
50  import org.apache.hadoop.hbase.KeyValue;
51  import org.apache.hadoop.hbase.KeyValueUtil;
52  import org.apache.hadoop.hbase.ServerName;
53  import org.apache.hadoop.hbase.client.MetricsConnection;
54  import org.apache.hadoop.hbase.client.Put;
55  import org.apache.hadoop.hbase.client.RowMutations;
56  import org.apache.hadoop.hbase.codec.Codec;
57  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoRequestProto;
58  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoResponseProto;
59  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyRequestProto;
60  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyResponseProto;
61  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos;
62  import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
63  import org.apache.hadoop.hbase.protobuf.RequestConverter;
64  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
65  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto;
66  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
67  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier;
68  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
69  import org.apache.hadoop.hbase.security.User;
70  import org.apache.hadoop.hbase.testclassification.SmallTests;
71  import org.apache.hadoop.hbase.util.Bytes;
72  import org.apache.hadoop.hbase.util.Pair;
73  import org.apache.hadoop.io.compress.GzipCodec;
74  import org.apache.hadoop.net.NetUtils;
75  import org.apache.hadoop.util.StringUtils;
76  import org.junit.Test;
77  import org.junit.experimental.categories.Category;
78  import org.mockito.Mockito;
79  import org.mockito.invocation.InvocationOnMock;
80  import org.mockito.stubbing.Answer;
81  
82  import com.google.common.collect.ImmutableList;
83  import com.google.common.collect.Lists;
84  import com.google.protobuf.BlockingRpcChannel;
85  import com.google.protobuf.BlockingService;
86  import com.google.protobuf.ByteString;
87  import com.google.protobuf.Descriptors.MethodDescriptor;
88  import com.google.protobuf.Message;
89  import com.google.protobuf.RpcController;
90  import com.google.protobuf.ServiceException;
91  
92  /**
93   * Some basic ipc tests.
94   */
95  @Category(SmallTests.class)
96  public class TestIPC {
97    public static final Log LOG = LogFactory.getLog(TestIPC.class);
98    static byte [] CELL_BYTES =  Bytes.toBytes("xyz");
99    static Cell CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, CELL_BYTES);
100   static byte [] BIG_CELL_BYTES = new byte [10 * 1024];
101   static Cell BIG_CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, BIG_CELL_BYTES);
102   private final static Configuration CONF = HBaseConfiguration.create();
103   // We are using the test TestRpcServiceProtos generated classes and Service because they are
104   // available and basic with methods like 'echo', and ping.  Below we make a blocking service
105   // by passing in implementation of blocking interface.  We use this service in all tests that
106   // follow.
107   private static final BlockingService SERVICE =
108    TestRpcServiceProtos.TestProtobufRpcProto.newReflectiveBlockingService(
109      new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {
110 
111     @Override
112     public EmptyResponseProto ping(RpcController controller,
113         EmptyRequestProto request) throws ServiceException {
114       // TODO Auto-generated method stub
115       return null;
116     }
117 
118     @Override
119     public EmptyResponseProto error(RpcController controller,
120         EmptyRequestProto request) throws ServiceException {
121       // TODO Auto-generated method stub
122       return null;
123     }
124 
125     @Override
126     public EchoResponseProto echo(RpcController controller, EchoRequestProto request)
127     throws ServiceException {
128       if (controller instanceof PayloadCarryingRpcController) {
129         PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController)controller;
130         // If cells, scan them to check we are able to iterate what we were given and since this is
131         // an echo, just put them back on the controller creating a new block.  Tests our block
132         // building.
133         CellScanner cellScanner = pcrc.cellScanner();
134         List<Cell> list = null;
135         if (cellScanner != null) {
136           list = new ArrayList<Cell>();
137           try {
138             while(cellScanner.advance()) {
139               list.add(cellScanner.current());
140             }
141           } catch (IOException e) {
142             throw new ServiceException(e);
143           }
144         }
145         cellScanner = CellUtil.createCellScanner(list);
146         ((PayloadCarryingRpcController)controller).setCellScanner(cellScanner);
147       }
148       return EchoResponseProto.newBuilder().setMessage(request.getMessage()).build();
149     }
150   });
151 
152   /**
153    * Instance of server.  We actually don't do anything speical in here so could just use
154    * HBaseRpcServer directly.
155    */
156   private static class TestRpcServer extends RpcServer {
157 
158     TestRpcServer() throws IOException {
159       this(new FifoRpcScheduler(CONF, 1));
160     }
161 
162     TestRpcServer(RpcScheduler scheduler) throws IOException {
163       super(null, "testRpcServer",
164           Lists.newArrayList(new BlockingServiceAndInterface(SERVICE, null)),
165         new InetSocketAddress("localhost", 0), CONF, scheduler);
166     }
167 
168     @Override
169     public Pair<Message, CellScanner> call(BlockingService service,
170         MethodDescriptor md, Message param, CellScanner cellScanner,
171         long receiveTime, MonitoredRPCHandler status) throws IOException {
172       return super.call(service, md, param, cellScanner, receiveTime, status);
173     }
174   }
175 
176   /**
177    * Ensure we do not HAVE TO HAVE a codec.
178    * @throws InterruptedException
179    * @throws IOException
180    */
181   @Test
182   public void testNoCodec() throws InterruptedException, IOException {
183     Configuration conf = HBaseConfiguration.create();
184     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT) {
185       @Override
186       Codec getCodec() {
187         return null;
188       }
189     };
190     TestRpcServer rpcServer = new TestRpcServer();
191     try {
192       rpcServer.start();
193       InetSocketAddress address = rpcServer.getListenerAddress();
194       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
195       final String message = "hello";
196       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage(message).build();
197       Pair<Message, CellScanner> r = client.call(md, param, null,
198         md.getOutputType().toProto(), User.getCurrent(), address, 0);
199       assertTrue(r.getSecond() == null);
200       // Silly assertion that the message is in the returned pb.
201       assertTrue(r.getFirst().toString().contains(message));
202     } finally {
203       client.stop();
204       rpcServer.stop();
205     }
206   }
207 
208   /**
209    * It is hard to verify the compression is actually happening under the wraps.  Hope that if
210    * unsupported, we'll get an exception out of some time (meantime, have to trace it manually
211    * to confirm that compression is happening down in the client and server).
212    * @throws IOException
213    * @throws InterruptedException
214    * @throws SecurityException
215    * @throws NoSuchMethodException
216    */
217   @Test
218   public void testCompressCellBlock()
219   throws IOException, InterruptedException, SecurityException, NoSuchMethodException {
220     Configuration conf = new Configuration(HBaseConfiguration.create());
221     conf.set("hbase.client.rpc.compressor", GzipCodec.class.getCanonicalName());
222     doSimpleTest(conf, new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT));
223   }
224 
225   private void doSimpleTest(final Configuration conf, final RpcClient client)
226   throws InterruptedException, IOException {
227     TestRpcServer rpcServer = new TestRpcServer();
228     List<Cell> cells = new ArrayList<Cell>();
229     int count = 3;
230     for (int i = 0; i < count; i++) cells.add(CELL);
231     try {
232       rpcServer.start();
233       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
234       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
235       InetSocketAddress address = rpcServer.getListenerAddress();
236       if (address == null) {
237         throw new IOException("Listener channel is closed");
238       }
239       Pair<Message, CellScanner> r = client.call(md, param, CellUtil.createCellScanner(cells),
240         md.getOutputType().toProto(), User.getCurrent(), address, 0);
241       int index = 0;
242       while (r.getSecond().advance()) {
243         assertTrue(CELL.equals(r.getSecond().current()));
244         index++;
245       }
246       assertEquals(count, index);
247     } finally {
248       client.stop();
249       rpcServer.stop();
250     }
251   }
252 
253   @Test
254   public void testRTEDuringConnectionSetup() throws Exception {
255     Configuration conf = HBaseConfiguration.create();
256     SocketFactory spyFactory = spy(NetUtils.getDefaultSocketFactory(conf));
257     Mockito.doAnswer(new Answer<Socket>() {
258       @Override
259       public Socket answer(InvocationOnMock invocation) throws Throwable {
260         Socket s = spy((Socket)invocation.callRealMethod());
261         doThrow(new RuntimeException("Injected fault")).when(s).setSoTimeout(anyInt());
262         return s;
263       }
264     }).when(spyFactory).createSocket();
265 
266     TestRpcServer rpcServer = new TestRpcServer();
267     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT, spyFactory);
268     try {
269       rpcServer.start();
270       InetSocketAddress address = rpcServer.getListenerAddress();
271       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
272       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
273       client.call(md, param, null, null, User.getCurrent(), address, 0);
274       fail("Expected an exception to have been thrown!");
275     } catch (Exception e) {
276       LOG.info("Caught expected exception: " + e.toString());
277       assertTrue(StringUtils.stringifyException(e).contains("Injected fault"));
278     } finally {
279       client.stop();
280       rpcServer.stop();
281     }
282   }
283 
284   /** Tests that the rpc scheduler is called when requests arrive. */
285   @Test
286   public void testRpcScheduler() throws IOException, InterruptedException {
287     RpcScheduler scheduler = spy(new FifoRpcScheduler(CONF, 1));
288     RpcServer rpcServer = new TestRpcServer(scheduler);
289     verify(scheduler).init((RpcScheduler.Context) anyObject());
290     RpcClient client = new RpcClient(CONF, HConstants.CLUSTER_ID_DEFAULT);
291     try {
292       rpcServer.start();
293       verify(scheduler).start();
294       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
295       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
296       for (int i = 0; i < 10; i++) {
297         client.call(md, param, CellUtil.createCellScanner(ImmutableList.of(CELL)),
298             md.getOutputType().toProto(), User.getCurrent(), rpcServer.getListenerAddress(), 0);
299       }
300       verify(scheduler, times(10)).dispatch((CallRunner) anyObject());
301     } finally {
302       rpcServer.stop();
303       verify(scheduler).stop();
304     }
305   }
306 
307   /**
308    * Instance of RpcServer that echoes client hostAddress back to client
309    */
310   static class TestRpcServer1 extends RpcServer {
311 
312     private static TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface SERVICE1 =
313         new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {
314           @Override
315           public EmptyResponseProto ping(RpcController unused, EmptyRequestProto request)
316               throws ServiceException {
317             return EmptyResponseProto.newBuilder().build();
318           }
319 
320           @Override
321           public EchoResponseProto echo(RpcController unused, EchoRequestProto request)
322               throws ServiceException {
323             final InetAddress remoteAddr = TestRpcServer1.getRemoteAddress();
324             final String message = remoteAddr == null ? "NULL" : remoteAddr.getHostAddress();
325             return EchoResponseProto.newBuilder().setMessage(message).build();
326           }
327 
328           @Override
329           public EmptyResponseProto error(RpcController unused, EmptyRequestProto request)
330               throws ServiceException {
331             throw new ServiceException("error", new IOException("error"));
332           }
333         };
334 
335     TestRpcServer1() throws IOException {
336       this(new FifoRpcScheduler(CONF, 1));
337     }
338 
339     TestRpcServer1(RpcScheduler scheduler) throws IOException {
340       super(null, "testRemoteAddressInCallObject", Lists
341           .newArrayList(new BlockingServiceAndInterface(TestRpcServiceProtos.TestProtobufRpcProto
342               .newReflectiveBlockingService(SERVICE1), null)),
343           new InetSocketAddress("localhost", 0), CONF, scheduler);
344     }
345   }
346 
347   /**
348    * Tests that the RpcServer creates & dispatches CallRunner object to scheduler with non-null
349    * remoteAddress set to its Call Object
350    * @throws ServiceException
351    */
352   @Test
353   public void testRpcServerForNotNullRemoteAddressInCallObject() throws IOException,
354       ServiceException {
355     final RpcScheduler scheduler = new FifoRpcScheduler(CONF, 1);
356     final TestRpcServer1 rpcServer = new TestRpcServer1(scheduler);
357     final InetSocketAddress localAddr = new InetSocketAddress("localhost", 0);
358     final RpcClient client =
359         new RpcClient(CONF, HConstants.CLUSTER_ID_DEFAULT, localAddr, null);
360     try {
361       rpcServer.start();
362       final InetSocketAddress isa = rpcServer.getListenerAddress();
363       if (isa == null) {
364         throw new IOException("Listener channel is closed");
365       }
366       final BlockingRpcChannel channel =
367           client.createBlockingRpcChannel(
368             ServerName.valueOf(isa.getHostName(), isa.getPort(), System.currentTimeMillis()),
369             User.getCurrent(), 0);
370       TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface stub =
371           TestRpcServiceProtos.TestProtobufRpcProto.newBlockingStub(channel);
372       final EchoRequestProto echoRequest =
373           EchoRequestProto.newBuilder().setMessage("GetRemoteAddress").build();
374       final EchoResponseProto echoResponse = stub.echo(null, echoRequest);
375       assertEquals(localAddr.getAddress().getHostAddress(), echoResponse.getMessage());
376     } finally {
377       client.stop();
378       rpcServer.stop();
379     }
380   }
381 
382   public static void main(String[] args)
383   throws IOException, SecurityException, NoSuchMethodException, InterruptedException {
384     if (args.length != 2) {
385       System.out.println("Usage: TestIPC <CYCLES> <CELLS_PER_CYCLE>");
386       return;
387     }
388     // ((Log4JLogger)HBaseServer.LOG).getLogger().setLevel(Level.INFO);
389     // ((Log4JLogger)HBaseClient.LOG).getLogger().setLevel(Level.INFO);
390     int cycles = Integer.parseInt(args[0]);
391     int cellcount = Integer.parseInt(args[1]);
392     Configuration conf = HBaseConfiguration.create();
393     TestRpcServer rpcServer = new TestRpcServer();
394     MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
395     EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
396     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT);
397     KeyValue kv = KeyValueUtil.ensureKeyValue(BIG_CELL);
398     Put p = new Put(kv.getRow());
399     for (int i = 0; i < cellcount; i++) {
400       p.add(kv);
401     }
402     RowMutations rm = new RowMutations(kv.getRow());
403     rm.add(p);
404     try {
405       rpcServer.start();
406       long startTime = System.currentTimeMillis();
407       User user = User.getCurrent();
408       InetSocketAddress address = rpcServer.getListenerAddress();
409       if (address == null) {
410         throw new IOException("Listener channel is closed");
411       }
412       for (int i = 0; i < cycles; i++) {
413         List<CellScannable> cells = new ArrayList<CellScannable>();
414         // Message param = RequestConverter.buildMultiRequest(HConstants.EMPTY_BYTE_ARRAY, rm);
415         ClientProtos.RegionAction.Builder builder = RequestConverter.buildNoDataRegionAction(
416           HConstants.EMPTY_BYTE_ARRAY, rm, cells,
417           RegionAction.newBuilder(),
418           ClientProtos.Action.newBuilder(),
419           MutationProto.newBuilder());
420         builder.setRegion(RegionSpecifier.newBuilder().setType(RegionSpecifierType.REGION_NAME).
421           setValue(ByteString.copyFrom(HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())));
422         if (i % 100000 == 0) {
423           LOG.info("" + i);
424           // Uncomment this for a thread dump every so often.
425           // ReflectionUtils.printThreadInfo(new PrintWriter(System.out),
426           //  "Thread dump " + Thread.currentThread().getName());
427         }
428         CellScanner cellScanner = CellUtil.createCellScanner(cells);
429         Pair<Message, CellScanner> response =
430           client.call(md, builder.build(), cellScanner, param, user, address, 0,
431               HConstants.NORMAL_QOS, new MetricsConnection.CallStats());
432         /*
433         int count = 0;
434         while (p.getSecond().advance()) {
435           count++;
436         }
437         assertEquals(cells.size(), count);*/
438       }
439       LOG.info("Cycled " + cycles + " time(s) with " + cellcount + " cell(s) in " +
440          (System.currentTimeMillis() - startTime) + "ms");
441     } finally {
442       client.stop();
443       rpcServer.stop();
444     }
445   }
446 }