HBase
 sql >> Base de données >  >> NoSQL >> HBase

Comment :tester les applications HBase à l'aide d'outils populaires

Bien que l'adoption d'Apache HBase pour la création d'applications pour les utilisateurs finaux ait explosé, bon nombre de ces applications (et de nombreuses applications en général) n'ont pas été bien testées. Dans cet article, vous apprendrez certaines des façons dont ces tests peuvent être facilement effectués.

Nous allons commencer par les tests unitaires via JUnit, puis passer à l'utilisation de Mockito et Apache MRUnit, puis à l'utilisation d'un mini-cluster HBase pour les tests d'intégration. (La base de code HBase elle-même est testée via un mini-cluster, alors pourquoi ne pas en profiter également pour les applications en amont ?)

Comme base de discussion, supposons que vous ayez un objet d'accès aux données HBase (DAO) qui effectue l'insertion suivante dans HBase. La logique pourrait être plus compliquée bien sûr, mais à titre d'exemple, cela fait l'affaire.

public class MyHBaseDAO { public static void insertRecord(HTableInterface table, HBaseTestObj obj) throws Exception { Put put =createPut(obj); table.put(put); } private static Put createPut(HBaseTestObj obj) { Put put =new Put(Bytes.toBytes(obj.getRowKey())); put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1"), Bytes.toBytes(obj.getData1())); put.add(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2"), Bytes.toBytes(obj.getData2())); retour mettre; }}

HBaseTestObj est un objet de données de base avec des getters et des setters pour rowkey, data1 et data2.

L'insertRecord effectue une insertion dans la table HBase par rapport à la famille de colonnes de CF, avec CQ-1 et CQ-2 comme qualificatifs. La méthode createPut remplit simplement un Put et le renvoie à la méthode appelante.

Utiliser JUnit

JUnit, bien connu de la plupart des développeurs Java à ce stade, s'applique facilement à de nombreuses applications HBase. Tout d'abord, ajoutez la dépendance à votre pom :

 junit   junit   4.11   test

Maintenant, dans la classe de test :

public class TestMyHbaseDAOData { @Test public void testCreatePut() lance une exception {HBaseTestObj obj =new HBaseTestObj(); obj.setRowKey("ROWKEY-1"); obj.setData1("DONNÉES-1"); obj.setData2("DONNEES-2"); Put put =MyHBaseDAO.createPut(obj); assertEquals(obj.getRowKey(), Bytes.toString(put.getRow())); assertEquals(obj.getData1(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1")).get(0).getValue())); assertEquals(obj.getData2(), Bytes.toString(put.get(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2")).get(0).getValue())); } }

Ce que vous avez fait ici était de vous assurer que votre méthode createPut crée, remplit et renvoie un objet Put avec les valeurs attendues.

Utiliser Mockito

Alors, comment procédez-vous pour tester unitairement la méthode insertRecord ci-dessus ? Une approche très efficace consiste à le faire avec Mockito.

Tout d'abord, ajoutez Mockito en tant que dépendance à votre pom :

   org.mockito   mockito-all   1.9.5 test 

Puis, en classe de test :

@RunWith(MockitoJUnitRunner.class)classe publique TestMyHBaseDAO{  @Mock  table HTableInterface privée ; @Mock HTablePool privé hTablePool ; @Captor privé ArgumentCaptor putCaptor ; @Test  public void testInsertRecord() throws Exception {    //return mock table when getTable is called    when(hTablePool.getTable("tablename")).thenReturn(table); //créer un objet de test et appeler le DAO qui doit être testé    HBaseTestObj obj =new HBaseTestObj(); obj.setRowKey("ROWKEY-1"); obj.setData1("DONNÉES-1"); obj.setData2("DONNEES-2"); MyHBaseDAO.insertRecord(table, obj); vérifier(table).put(putCaptor.capture()); Put put =putCaptor.getValue(); assertEquals(Bytes.toString(put.getRow()), obj.getRowKey()); assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-1"))); assert(put.has(Bytes.toBytes("CF"), Bytes.toBytes("CQ-2"))); assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-1")).get(0).getValue()), "DATA-1"); assertEquals(Bytes.toString(put.get(Bytes.toBytes("CF"),Bytes.toBytes("CQ-2")).get(0).getValue()), "DATA-2"); }}

Ici, vous avez rempli HBaseTestObj avec "ROWKEY-1", "DATA-1", "DATA-2" comme valeurs. Vous avez ensuite utilisé la table simulée et le DAO pour insérer l'enregistrement. Vous avez capturé le Put que le DAO aurait inséré et vérifié que la rowkey, data1 et data2 sont ce que vous attendez d'elles.

La clé ici est de gérer la création du pool htable et de l'instance htable en dehors du DAO. Cela vous permet de les simuler proprement et de tester les puts comme indiqué ci-dessus. De même, vous pouvez maintenant développer toutes les autres opérations telles que Get, Scan, Delete, etc.

Utiliser MRUnit

Une fois les tests unitaires d'accès aux données réguliers couverts, tournons-nous vers les tâches MapReduce qui vont à l'encontre des tables HBase.

Tester les travaux MR qui vont à l'encontre de HBase est aussi simple que tester les travaux MapReduce réguliers. MRUnit facilite grandement le test des tâches MapReduce, y compris celles de HBase.

Imaginez que vous ayez un travail MR qui écrit dans une table HBase, "MyTest", qui a une famille de colonnes, "CF". Le réducteur d'un tel travail pourrait ressembler à :

public class MyReducer extend TableReducer { public static final byte[] CF ="CF".getBytes(); public static final byte[] QUALIFIER ="CQ-1".getBytes(); public void reduce(Text key, Iterable values, Context context) throws IOException, InterruptedException { //groupe de traitements pour extraire les données à insérer, dans notre cas, disons que nous ajoutons simplement //tous les enregistrements que nous recevons à partir du mappeur pour cette // clé particulière et insérez un enregistrement dans HBase StringBuffer data =new StringBuffer(); Put put =new Put(Bytes.toBytes(key.toString())); for (Text val :values) { data =data.append(val); } put.add(CF, QUALIFICATEUR, Bytes.toBytes(data.toString())); //écrire dans HBase context.write(new ImmutableBytesWritable(Bytes.toBytes(key.toString())), put); } }

Maintenant, comment procédez-vous pour tester unitairement le réducteur ci-dessus dans MRUnit ? Tout d'abord, ajoutez MRUnit en tant que dépendance à votre pom.

 org.apache.mrunit mrunit 1.0.0  test 

Ensuite, dans la classe de test, utilisez le ReduceDriver fourni par MRUnit comme ci-dessous :

public class MyReducerTest {    ReduceDriver reduceDriver ; octet[] CF ="CF".getBytes(); byte[] QUALIFICATEUR ="CQ-1".getBytes(); @Avant    public void setUp() {      MyReducer reducer =new MyReducer(); reduceDriver =ReduceDriver.newReduceDriver (réducteur); }     @Test   public void testHBaseInsert() lance IOException {      Chaîne strKey ="RowKey-1", strValue ="DATA", strValue1 ="DATA1", strValue2 ="DATA2" ; Liste liste =new ArrayList(); list.add(nouveau texte(strValue)); list.add(nouveau texte(strValue1)); list.add(nouveau texte(strValue2)); // puisque dans notre cas, tout ce que le réducteur fait est d'ajouter les enregistrements que le mappeur         // lui envoie, nous devrions obtenir la chaîne suivante :     String ExpectOutput =strValue + strValue1 + strValue2 ; //Configurer l'entrée, imiter ce que le mappeur aurait passé      //au réducteur et exécuter le test      reduceDriver.withInput(new Text(strKey), list); //exécuter le réducteur et obtenir sa sortie      List> result =reduceDriver.run(); //extrait la clé du résultat et vérifie      assertEquals(Bytes.toString(result.get(0).getFirst().get()), strKey); //extrait la valeur pour CF/QUALIFIER et vérifie      Put a =(Put)result.get(0).getSecond(); Chaîne c =Bytes.toString(a.get(CF, QUALIFIER).get(0).getValue()); assertEquals(expectedOutput,c ); }}

Fondamentalement, après un tas de traitements dans MyReducer, vous avez vérifié que :

  • Le résultat correspond à ce que vous attendez.
  • Le put inséré dans HBase a "RowKey-1" comme clé de ligne.
  • "DATADATA1DATA2" est la valeur de la famille de colonnes CF et du qualificatif de colonne CQ.

Vous pouvez également tester les mappeurs qui obtiennent des données de HBase de la même manière à l'aide de MapperDriver, ou tester les tâches MR qui lisent à partir de HBase, traitent les données et écrivent sur HDFS.

Utiliser un mini-cluster HBase

Nous allons maintenant voir comment procéder pour les tests d'intégration. HBase est livré avec HBaseTestingUtility, ce qui facilite l'écriture de tests d'intégration avec un mini-cluster HBase. Afin d'extraire les bonnes bibliothèques, les dépendances suivantes sont requises dans votre pom :

   org.apache.hadoop   hadoop-common   2.0.0-cdh4.2.0   test-jar   test org.apache.hbase   hbase   0.94.2-cdh4.2.0   test-jar   test         org.apache.hadoop   hadoop-hdfs   2.0.0-cdh4.2.0   test-jar   test   org.apache.hadoop   hadoop-hdfs   2.0.0-cdh4.2.0   test

Voyons maintenant comment exécuter un test d'intégration pour l'insert MyDAO décrit dans l'introduction :

public class MyHBaseIntegrationTest {utilitaire privé statique HBaseTestingUtility;byte[] CF ="CF".getBytes();byte[] QUALIFIER ="CQ-1".getBytes();@Beforepublic void setup() lance une exception { utilitaire =new HBaseTestingUtility(); utility.startMiniCluster();}@Test public void testInsert() lance une exception { HTableInterface table =utility.createTable(Bytes.toBytes("MyTest"), Bytes.toBytes("CF")); HBaseTestObj obj =new HBaseTestObj(); obj.setRowKey("ROWKEY-1"); obj.setData1("DONNÉES-1"); obj.setData2("DONNEES-2"); MyHBaseDAO.insertRecord(table, obj); Get get1 =new Get(Bytes.toBytes(obj.getRowKey())); get1.addColumn(CF, CQ1); Résultat result1 =table.get(get1); assertEquals(Bytes.toString(result1.getRow()), obj.getRowKey()); assertEquals(Bytes.toString(result1.value()), obj.getData1()); Get get2 =new Get(Bytes.toBytes(obj.getRowKey())); get2.addColumn(CF, CQ2); Résultat result2 =table.get(get2); assertEquals(Bytes.toString(result2.getRow()), obj.getRowKey()); assertEquals(Bytes.toString(result2.value()), obj.getData2()); }}

Ici, vous avez créé un mini-cluster HBase et l'avez démarré. Vous avez ensuite créé une table appelée "MyTest" avec une famille de colonnes, "CF". Vous avez inséré un enregistrement à l'aide du DAO que vous deviez tester, effectué un Get à partir de la même table et vérifié que le DAO avait correctement inséré les enregistrements.

La même chose pourrait être faite pour des cas d'utilisation beaucoup plus compliqués avec les tâches MR comme celles présentées ci-dessus. Vous pouvez également accéder aux mini-clusters HDFS et ZooKeeper créés lors de la création de celui de HBase, exécuter une tâche MR, la transmettre à HBase et vérifier les enregistrements insérés.

Juste une petite mise en garde :le démarrage d'un mini-cluster prend 20 à 30 secondes et ne peut se faire sous Windows sans Cygwin. Cependant, comme ils ne doivent être exécutés que périodiquement, un temps d'exécution plus long devrait être acceptable.

Vous pouvez trouver un exemple de code pour les exemples ci-dessus sur https://github.com/sitaula/HBaseTest. Bon test !