本文主要介紹MapReduce編程模型的原理和基於Hadoop的MD5暴力破解思路。java
Hadoop做爲一個分佈式架構的實現方案,它的核心思想包括如下幾個方面:HDFS文件系統,MapReduce的編程模型以及RPC框架。不管是怎樣的架構,一個系統的關鍵無非是存儲結構和業務邏輯。HDFS分佈式文件系統是整個Hadoop的基礎。在HDFS文件系統之中,大文件被分割成不少的數據塊,每一塊都有可能分佈在集羣的不一樣節點中。也就是說在HDFS文件系統中,文件的狀況是這樣的:apache
文件保存在不一樣的節點上,而Hadoop是用於海量數據處理的,那麼如何把分佈在各個節點的數據進行高效的併發處理呢?Hadoop對此提供了不一樣的解決方案,好比yarn框架等。框架已經幫咱們寫好了不少的諸如任務分配,節點通訊之類的事情。而咱們要作的就是寫好本身的業務邏輯,那麼咱們就要遵照Hadoop的編程規範,而這個編程規範就是MapReduce。編程
那麼MapReduce的運行過程是怎麼樣的呢?且看下圖:架構
1.從HDFS文件系統中讀取文件,每個數據塊對應一個MapTask。併發
2.進行Map任務,逐行讀取文件,每一行調用一次Map函數,數據被封裝爲一個鍵值對也就是圖中的<k2,v2>。app
3.將Map後的鍵值對進行歸約,key值相同的value會被封裝到一塊兒。就好了圖中的<k,{v1,v2,v3}>框架
4.歸約後的鍵值對會被送到不一樣的Reduce中,執行Reduce任務,輸出<k3,v3>到輸出文件中。分佈式
弄懂了MapReduce的執行過程以後,咱們就能夠編寫本身的邏輯來進行處理了。函數
仍是先上圖:oop
1.編程生成全部的密碼明文文件。
2.將明文上傳至HDFS文件系統中,在Map函數中實現MD5的求值。而後直接存入文件系統中中。
代碼實現:
package com.test; import java.security.MessageDigest; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; /** * 目地很簡單。不須要reduce處理,直接在Map中解決問題 * @author hadoop * */ public class Test { //定義Map處理類 static class TestMapper extends Mapper<LongWritable, Text, Text, Text>{ //重寫map方法 public void map(LongWritable key, Text value, Context context)throws InterruptedException { try{ //生成MD5 String keyStr=value.toString(); String MD5=getMD5(keyStr); context.write(new Text(keyStr), new Text(MD5)); }catch (Exception e){ e.printStackTrace(); } } } /** * MD5計算 * @param str * @return */ public static String getMD5(String str) { try { // 生成一個MD5加密計算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); // 計算md5函數 md.update(str.getBytes()); // digest()最後肯定返回md5 hash值,返回值爲8爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符 // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;獲得字符串形式的hash值 byte[] encrypt = md.digest(); StringBuilder sb = new StringBuilder(); for (byte t : encrypt) { String s = Integer.toHexString(t & 0xFF); if (s.length() == 1) { s = "0" + s; } sb.append(s); } String res = sb.toString(); return res; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { //必需要傳遞的是自定的mapper和reducer的類,輸入輸出的路徑必須指定,輸出的類型<k3,v3>必須指定 //將自定義的MyMapper和MyReducer組裝在一塊兒 Configuration conf=new Configuration(); String jobName=Test.class.getSimpleName(); //首先寫job,知道須要conf和jobname在去建立便可 Job job = Job.getInstance(conf, jobName); //若是要打包運行改程序,則須要調用以下行 job.setJarByClass(Test.class); //讀取HDFS內容:設置輸入路徑 FileInputFormat.setInputPaths(job, new Path(args[0])); //指定解析<k1,v1>的類(誰來解析鍵值對) //*指定解析的類能夠省略不寫,由於設置解析類默認的就是TextInputFormat.class job.setInputFormatClass(TextInputFormat.class); //指定自定義mapper類 job.setMapperClass(TestMapper.class); //指定map輸出的key2的類型和value2的類型 <k2,v2> //下面兩步能夠省略,當<k3,v3>和<k2,v2>類型一致的時候,<k2,v2>類型能夠不指定 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(Text.class); //分區(默認1個),排序,分組,規約 採用 默認 // job.setCombinerClass(null); //接下來採用reduce步驟 //指定自定義的reduce類 // job.setReducerClass(null); //指定輸出的<k3,v3>類型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); //指定輸出<K3,V3>的類 //下面這一步能夠省 // job.setOutputFormatClass(TextOutputFormat.class); //指定輸出路徑 FileOutputFormat.setOutputPath(job, new Path(args[1])); //寫的mapreduce程序要交給resource manager運行 job.waitForCompletion(true); } }
這裏爲何不用Reduce過程?
Reduce是對歸約後的鍵值對進行處理的,可是能夠看見,咱們的明文都是惟一的,通過Map後輸出的鍵值對的Key都是不同的,歸約以後仍然如此,因此沒有必要在Reduce過程當中進行其餘操做。
另外我以前的想法是不在map中處理,而是將Map中讀取到的文件內容直接輸出到Reduce,而後在Reduce中進行MD5的計算,可是從Map中傳輸過來的數據總會多出一些行,致使計算出錯。(這個我也沒能弄懂怎麼回事,有大佬知道的能夠靠訴我)
有了上一步生成的數據,咱們就能夠作數據的查詢了。生成的文件仍然是在HDFS文件系統中,經過終端輸入參數(能夠是明文或者是密文),而後用MapReduce進行查找,結果輸出到文件中。
代碼:
package com.test; import java.security.MessageDigest; import org.apache.hadoop.mapreduce.Mapper; import org.apache.hadoop.io.LongWritable; import org.apache.hadoop.io.Text; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.fs.Path; import org.apache.hadoop.mapreduce.Job; import org.apache.hadoop.mapreduce.lib.input.FileInputFormat; import org.apache.hadoop.mapreduce.lib.input.TextInputFormat; import org.apache.hadoop.mapreduce.lib.output.FileOutputFormat; /** * 目地很簡單。不須要reduce處理,直接在Map中解決問題 * @author hadoop * */ public class Test { private static String s=null; //定義Map處理類 static class TestMapper extends Mapper<LongWritable, Text, Text, Text>{ //重寫map方法 public void map(LongWritable key, Text value, Context context)throws InterruptedException { try{ //查詢MD5的值 int index=value.find(s); if(index>=0){ System.out.println("=================="+value.toString()); context.write(new Text("result"), value); } }catch (Exception e){ e.printStackTrace(); } } } /** * MD5計算 * @param str * @return */ public static String getMD5(String str) { try { // 生成一個MD5加密計算摘要 MessageDigest md = MessageDigest.getInstance("MD5"); // 計算md5函數 md.update(str.getBytes()); // digest()最後肯定返回md5 hash值,返回值爲8爲字符串。由於md5 hash值是16位的hex值,實際上就是8位的字符 // BigInteger函數則將8位的字符串轉換成16位hex值,用字符串來表示;獲得字符串形式的hash值 byte[] encrypt = md.digest(); StringBuilder sb = new StringBuilder(); for (byte t : encrypt) { String s = Integer.toHexString(t & 0xFF); if (s.length() == 1) { s = "0" + s; } sb.append(s); } String res = sb.toString(); return res; } catch (Exception e) { e.printStackTrace(); } return null; } public static void main(String[] args) throws Exception { //必需要傳遞的是自定的mapper和reducer的類,輸入輸出的路徑必須指定,輸出的類型<k3,v3>必須指定 //將自定義的MyMapper和MyReducer組裝在一塊兒 //參數(明文或者MD5值) s=args[2]; Configuration conf=new Configuration(); String jobName=Test.class.getSimpleName(); //首先寫job,知道須要conf和jobname在去建立便可 Job job = Job.getInstance(conf, jobName); //若是要打包運行改程序,則須要調用以下行 job.setJarByClass(Test.class); //讀取HDFS內容:設置輸入路徑 FileInputFormat.setInputPaths(job, new Path(args[0])); //指定解析<k1,v1>的類(誰來解析鍵值對) //*指定解析的類能夠省略不寫,由於設置解析類默認的就是TextInputFormat.class job.setInputFormatClass(TextInputFormat.class); //指定自定義mapper類 job.setMapperClass(TestMapper.class); //指定map輸出的key2的類型和value2的類型 <k2,v2> //下面兩步能夠省略,當<k3,v3>和<k2,v2>類型一致的時候,<k2,v2>類型能夠不指定 job.setMapOutputKeyClass(Text.class); job.setMapOutputValueClass(Text.class); //分區(默認1個),排序,分組,規約 採用 默認 // job.setCombinerClass(null); //接下來採用reduce步驟 //指定自定義的reduce類 // job.setReducerClass(null); //指定輸出的<k3,v3>類型 job.setOutputKeyClass(Text.class); job.setOutputValueClass(Text.class); //指定輸出<K3,V3>的類 //下面這一步能夠省 // job.setOutputFormatClass(TextOutputFormat.class); //指定輸出路徑 FileOutputFormat.setOutputPath(job, new Path(args[1])); //寫的mapreduce程序要交給resource manager運行 job.waitForCompletion(true); } }
把文件導出成JAR包,在終端使用命令
生成密文:
bin/hadoop jar [jar包路徑] [輸入文件路徑] [輸出路徑]
查詢
bin/hadoop jar [jar包路徑] [輸入文件路徑] [輸出路徑] [密文或者明文]
生成的密文結果實例:
查詢的結果示例:
ok以上,祝君好運。