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.InetSocketAddress;
33  import java.net.Socket;
34  import java.util.ArrayList;
35  import java.util.List;
36  
37  import javax.net.SocketFactory;
38  
39  import org.apache.commons.logging.Log;
40  import org.apache.commons.logging.LogFactory;
41  import org.apache.hadoop.conf.Configuration;
42  import org.apache.hadoop.hbase.Cell;
43  import org.apache.hadoop.hbase.CellScannable;
44  import org.apache.hadoop.hbase.CellScanner;
45  import org.apache.hadoop.hbase.CellUtil;
46  import org.apache.hadoop.hbase.HBaseConfiguration;
47  import org.apache.hadoop.hbase.HConstants;
48  import org.apache.hadoop.hbase.HRegionInfo;
49  import org.apache.hadoop.hbase.KeyValue;
50  import org.apache.hadoop.hbase.KeyValueUtil;
51  import org.apache.hadoop.hbase.client.MetricsConnection;
52  import org.apache.hadoop.hbase.client.Put;
53  import org.apache.hadoop.hbase.client.RowMutations;
54  import org.apache.hadoop.hbase.codec.Codec;
55  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoRequestProto;
56  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EchoResponseProto;
57  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyRequestProto;
58  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestProtos.EmptyResponseProto;
59  import org.apache.hadoop.hbase.ipc.protobuf.generated.TestRpcServiceProtos;
60  import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
61  import org.apache.hadoop.hbase.protobuf.RequestConverter;
62  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
63  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.MutationProto;
64  import org.apache.hadoop.hbase.protobuf.generated.ClientProtos.RegionAction;
65  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier;
66  import org.apache.hadoop.hbase.protobuf.generated.HBaseProtos.RegionSpecifier.RegionSpecifierType;
67  import org.apache.hadoop.hbase.security.User;
68  import org.apache.hadoop.hbase.testclassification.SmallTests;
69  import org.apache.hadoop.hbase.util.Bytes;
70  import org.apache.hadoop.hbase.util.Pair;
71  import org.apache.hadoop.io.compress.GzipCodec;
72  import org.apache.hadoop.net.NetUtils;
73  import org.apache.hadoop.util.StringUtils;
74  import org.junit.Test;
75  import org.junit.experimental.categories.Category;
76  import org.mockito.Mockito;
77  import org.mockito.invocation.InvocationOnMock;
78  import org.mockito.stubbing.Answer;
79  
80  import com.google.common.collect.ImmutableList;
81  import com.google.common.collect.Lists;
82  import com.google.protobuf.BlockingService;
83  import com.google.protobuf.ByteString;
84  import com.google.protobuf.Descriptors.MethodDescriptor;
85  import com.google.protobuf.Message;
86  import com.google.protobuf.RpcController;
87  import com.google.protobuf.ServiceException;
88  
89  /**
90   * Some basic ipc tests.
91   */
92  @Category(SmallTests.class)
93  public class TestIPC {
94    public static final Log LOG = LogFactory.getLog(TestIPC.class);
95    static byte [] CELL_BYTES =  Bytes.toBytes("xyz");
96    static Cell CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, CELL_BYTES);
97    static byte [] BIG_CELL_BYTES = new byte [10 * 1024];
98    static Cell BIG_CELL = new KeyValue(CELL_BYTES, CELL_BYTES, CELL_BYTES, BIG_CELL_BYTES);
99    private final static Configuration CONF = HBaseConfiguration.create();
100   // We are using the test TestRpcServiceProtos generated classes and Service because they are
101   // available and basic with methods like 'echo', and ping.  Below we make a blocking service
102   // by passing in implementation of blocking interface.  We use this service in all tests that
103   // follow.
104   private static final BlockingService SERVICE =
105    TestRpcServiceProtos.TestProtobufRpcProto.newReflectiveBlockingService(
106      new TestRpcServiceProtos.TestProtobufRpcProto.BlockingInterface() {
107 
108     @Override
109     public EmptyResponseProto ping(RpcController controller,
110         EmptyRequestProto request) throws ServiceException {
111       // TODO Auto-generated method stub
112       return null;
113     }
114 
115     @Override
116     public EmptyResponseProto error(RpcController controller,
117         EmptyRequestProto request) throws ServiceException {
118       // TODO Auto-generated method stub
119       return null;
120     }
121 
122     @Override
123     public EchoResponseProto echo(RpcController controller, EchoRequestProto request)
124     throws ServiceException {
125       if (controller instanceof PayloadCarryingRpcController) {
126         PayloadCarryingRpcController pcrc = (PayloadCarryingRpcController)controller;
127         // If cells, scan them to check we are able to iterate what we were given and since this is
128         // an echo, just put them back on the controller creating a new block.  Tests our block
129         // building.
130         CellScanner cellScanner = pcrc.cellScanner();
131         List<Cell> list = null;
132         if (cellScanner != null) {
133           list = new ArrayList<Cell>();
134           try {
135             while(cellScanner.advance()) {
136               list.add(cellScanner.current());
137             }
138           } catch (IOException e) {
139             throw new ServiceException(e);
140           }
141         }
142         cellScanner = CellUtil.createCellScanner(list);
143         ((PayloadCarryingRpcController)controller).setCellScanner(cellScanner);
144       }
145       return EchoResponseProto.newBuilder().setMessage(request.getMessage()).build();
146     }
147   });
148 
149   /**
150    * Instance of server.  We actually don't do anything speical in here so could just use
151    * HBaseRpcServer directly.
152    */
153   private static class TestRpcServer extends RpcServer {
154 
155     TestRpcServer() throws IOException {
156       this(new FifoRpcScheduler(CONF, 1));
157     }
158 
159     TestRpcServer(RpcScheduler scheduler) throws IOException {
160       super(null, "testRpcServer",
161           Lists.newArrayList(new BlockingServiceAndInterface(SERVICE, null)),
162         new InetSocketAddress("localhost", 0), CONF, scheduler);
163     }
164 
165     @Override
166     public Pair<Message, CellScanner> call(BlockingService service,
167         MethodDescriptor md, Message param, CellScanner cellScanner,
168         long receiveTime, MonitoredRPCHandler status) throws IOException {
169       return super.call(service, md, param, cellScanner, receiveTime, status);
170     }
171   }
172 
173   /**
174    * Ensure we do not HAVE TO HAVE a codec.
175    * @throws InterruptedException
176    * @throws IOException
177    */
178   @Test
179   public void testNoCodec() throws InterruptedException, IOException {
180     Configuration conf = HBaseConfiguration.create();
181     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT) {
182       @Override
183       Codec getCodec() {
184         return null;
185       }
186     };
187     TestRpcServer rpcServer = new TestRpcServer();
188     try {
189       rpcServer.start();
190       InetSocketAddress address = rpcServer.getListenerAddress();
191       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
192       final String message = "hello";
193       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage(message).build();
194       Pair<Message, CellScanner> r = client.call(md, param, null,
195         md.getOutputType().toProto(), User.getCurrent(), address, 0);
196       assertTrue(r.getSecond() == null);
197       // Silly assertion that the message is in the returned pb.
198       assertTrue(r.getFirst().toString().contains(message));
199     } finally {
200       client.stop();
201       rpcServer.stop();
202     }
203   }
204 
205   /**
206    * It is hard to verify the compression is actually happening under the wraps.  Hope that if
207    * unsupported, we'll get an exception out of some time (meantime, have to trace it manually
208    * to confirm that compression is happening down in the client and server).
209    * @throws IOException
210    * @throws InterruptedException
211    * @throws SecurityException
212    * @throws NoSuchMethodException
213    */
214   @Test
215   public void testCompressCellBlock()
216   throws IOException, InterruptedException, SecurityException, NoSuchMethodException {
217     Configuration conf = new Configuration(HBaseConfiguration.create());
218     conf.set("hbase.client.rpc.compressor", GzipCodec.class.getCanonicalName());
219     doSimpleTest(conf, new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT));
220   }
221 
222   private void doSimpleTest(final Configuration conf, final RpcClient client)
223   throws InterruptedException, IOException {
224     TestRpcServer rpcServer = new TestRpcServer();
225     List<Cell> cells = new ArrayList<Cell>();
226     int count = 3;
227     for (int i = 0; i < count; i++) cells.add(CELL);
228     try {
229       rpcServer.start();
230       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
231       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
232       InetSocketAddress address = rpcServer.getListenerAddress();
233       if (address == null) {
234         throw new IOException("Listener channel is closed");
235       }
236       Pair<Message, CellScanner> r = client.call(md, param, CellUtil.createCellScanner(cells),
237         md.getOutputType().toProto(), User.getCurrent(), address, 0);
238       int index = 0;
239       while (r.getSecond().advance()) {
240         assertTrue(CELL.equals(r.getSecond().current()));
241         index++;
242       }
243       assertEquals(count, index);
244     } finally {
245       client.stop();
246       rpcServer.stop();
247     }
248   }
249 
250   @Test
251   public void testRTEDuringConnectionSetup() throws Exception {
252     Configuration conf = HBaseConfiguration.create();
253     SocketFactory spyFactory = spy(NetUtils.getDefaultSocketFactory(conf));
254     Mockito.doAnswer(new Answer<Socket>() {
255       @Override
256       public Socket answer(InvocationOnMock invocation) throws Throwable {
257         Socket s = spy((Socket)invocation.callRealMethod());
258         doThrow(new RuntimeException("Injected fault")).when(s).setSoTimeout(anyInt());
259         return s;
260       }
261     }).when(spyFactory).createSocket();
262 
263     TestRpcServer rpcServer = new TestRpcServer();
264     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT, spyFactory);
265     try {
266       rpcServer.start();
267       InetSocketAddress address = rpcServer.getListenerAddress();
268       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
269       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
270       client.call(md, param, null, null, User.getCurrent(), address, 0);
271       fail("Expected an exception to have been thrown!");
272     } catch (Exception e) {
273       LOG.info("Caught expected exception: " + e.toString());
274       assertTrue(StringUtils.stringifyException(e).contains("Injected fault"));
275     } finally {
276       client.stop();
277       rpcServer.stop();
278     }
279   }
280 
281   /** Tests that the rpc scheduler is called when requests arrive. */
282   @Test
283   public void testRpcScheduler() throws IOException, InterruptedException {
284     RpcScheduler scheduler = spy(new FifoRpcScheduler(CONF, 1));
285     RpcServer rpcServer = new TestRpcServer(scheduler);
286     verify(scheduler).init((RpcScheduler.Context) anyObject());
287     RpcClient client = new RpcClient(CONF, HConstants.CLUSTER_ID_DEFAULT);
288     try {
289       rpcServer.start();
290       verify(scheduler).start();
291       MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
292       EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
293       for (int i = 0; i < 10; i++) {
294         client.call(md, param, CellUtil.createCellScanner(ImmutableList.of(CELL)),
295             md.getOutputType().toProto(), User.getCurrent(), rpcServer.getListenerAddress(), 0);
296       }
297       verify(scheduler, times(10)).dispatch((CallRunner) anyObject());
298     } finally {
299       rpcServer.stop();
300       verify(scheduler).stop();
301     }
302   }
303 
304   public static void main(String[] args)
305   throws IOException, SecurityException, NoSuchMethodException, InterruptedException {
306     if (args.length != 2) {
307       System.out.println("Usage: TestIPC <CYCLES> <CELLS_PER_CYCLE>");
308       return;
309     }
310     // ((Log4JLogger)HBaseServer.LOG).getLogger().setLevel(Level.INFO);
311     // ((Log4JLogger)HBaseClient.LOG).getLogger().setLevel(Level.INFO);
312     int cycles = Integer.parseInt(args[0]);
313     int cellcount = Integer.parseInt(args[1]);
314     Configuration conf = HBaseConfiguration.create();
315     TestRpcServer rpcServer = new TestRpcServer();
316     MethodDescriptor md = SERVICE.getDescriptorForType().findMethodByName("echo");
317     EchoRequestProto param = EchoRequestProto.newBuilder().setMessage("hello").build();
318     RpcClient client = new RpcClient(conf, HConstants.CLUSTER_ID_DEFAULT);
319     KeyValue kv = KeyValueUtil.ensureKeyValue(BIG_CELL);
320     Put p = new Put(kv.getRow());
321     for (int i = 0; i < cellcount; i++) {
322       p.add(kv);
323     }
324     RowMutations rm = new RowMutations(kv.getRow());
325     rm.add(p);
326     try {
327       rpcServer.start();
328       long startTime = System.currentTimeMillis();
329       User user = User.getCurrent();
330       InetSocketAddress address = rpcServer.getListenerAddress();
331       if (address == null) {
332         throw new IOException("Listener channel is closed");
333       }
334       for (int i = 0; i < cycles; i++) {
335         List<CellScannable> cells = new ArrayList<CellScannable>();
336         // Message param = RequestConverter.buildMultiRequest(HConstants.EMPTY_BYTE_ARRAY, rm);
337         ClientProtos.RegionAction.Builder builder = RequestConverter.buildNoDataRegionAction(
338           HConstants.EMPTY_BYTE_ARRAY, rm, cells,
339           RegionAction.newBuilder(),
340           ClientProtos.Action.newBuilder(),
341           MutationProto.newBuilder());
342         builder.setRegion(RegionSpecifier.newBuilder().setType(RegionSpecifierType.REGION_NAME).
343           setValue(ByteString.copyFrom(HRegionInfo.FIRST_META_REGIONINFO.getEncodedNameAsBytes())));
344         if (i % 100000 == 0) {
345           LOG.info("" + i);
346           // Uncomment this for a thread dump every so often.
347           // ReflectionUtils.printThreadInfo(new PrintWriter(System.out),
348           //  "Thread dump " + Thread.currentThread().getName());
349         }
350         CellScanner cellScanner = CellUtil.createCellScanner(cells);
351         Pair<Message, CellScanner> response =
352           client.call(md, builder.build(), cellScanner, param, user, address, 0,
353               HConstants.NORMAL_QOS, new MetricsConnection.CallStats());
354         /*
355         int count = 0;
356         while (p.getSecond().advance()) {
357           count++;
358         }
359         assertEquals(cells.size(), count);*/
360       }
361       LOG.info("Cycled " + cycles + " time(s) with " + cellcount + " cell(s) in " +
362          (System.currentTimeMillis() - startTime) + "ms");
363     } finally {
364       client.stop();
365       rpcServer.stop();
366     }
367   }
368 }