MapReduce实践

本文详细介绍了MapReduce的实践,包括使用Python和Java开发MapReduce任务。内容涵盖WordCount程序、白名单过滤、全局排序的两种方法、指定key和partition操作、JOIN操作、与二分算法结合的实例,以及开发总结。此外,还讨论了Java开发MapReduce的环境配置、常见问题及解决方案。
摘要由CSDN通过智能技术生成

MapReduce实践

一、用python开发MapReduce

1.WordCount程序

  • 文件名:run.sh
# 文件名:run.sh
#hadoop安装目录下的bin/hadoop是解析器。
HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
#用Python开发,必须通过hadoop-streaming的方式提交,需要引入hadoop-streaming.jar。
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"

#INPUT_FILE_PATH_1和OUTPUT_PATH都是在hdfs上,输入文件要上传至/1.data。
INPUT_FILE_PATH_1="/1.data"
OUTPUT_PATH="/output"

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH

#Step 1. 
#通过Streaming的方式来提交作业
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_1 \
    -output $OUTPUT_PATH \
    -mapper "python map_new.py" \
    # -mapper "python map_new.py mapper_func IPLIB" \
	-reducer "python red_new.py" \
#   或 -reducer "cat" \
#代码在本地提交,集群的多个节点是没有代码的。通过配置【-file 文件】把本地代码分发到各个执行MapReduce的节点上。
	-file ./map_new.py \
	#-file加不加引号都可以,但是-mapper和-reducer必须加引号。
	-file "./red_new.py"
  • 文件名:1.data,把它上传到hdfs的/1.data。
    • 目前通过非java开发mapreduce的输入数据通常是以TextFile结构,优点是可读性很好,缺点是数据不压缩,浪费空间磁盘开销大。
    • HDFS存储支持文件格式还有很多,SequenceFile格式按照<k,v>存储 ,支持把一些小的文件统一打包到大的SequenceFile,可以对小文件高效存储处理,支持压缩,既可以支持明文方式读取也可以通过压缩算法进行压缩。java开发通常是SequenceFile格式(默认支持),python开发通常是TextFile格式。
    • SequenceFile数据通常是二进制的,查看文件用命令hadoop fs -text /xxx,而cat不能看SequenceFile文件,所以使用text功能更好,既可以查看明文也可以查看加密文件。

Is not until you fly that you fall ,
when the sun come again ,
you are unstopable .
when the dreams come again ,
you fight .

  • 文件名:map_new.py
# encoding: utf-8
#文件名:map_new.py
import sys
for line in sys.stdin:
	ss=line.strip().split(' ')
	for word in ss:
		print '\t'.join([ word.strip(),'1' ])
  • 文件名:red_new.py
# encoding: utf-8
#文件名:red_new.py
import sys
cur_word=None
sum=0
for line in sys.stdin:
	ss=line.strip().split('\t')
	if len(ss)!=2:
		continue
	word,cnt=ss
	if cur_word==None:
		cur_word=word
	if cur_word!=word:
		print '\t'.join([cur_word,str(sum)])
		cur_word=word
		sum=0
	sum+=int(cnt)
print '\t'.join([cur_word,str(sum)])

运行命令:bash run.sh或sh run.sh
提前手动测试(sort -k1表示按第一列字符串排序,模拟了shuffle过程):

cat 1.data | python map_new.py | sort -k1 | python red_new.py( > result.local)

若出现” SyntaxError:Non-ASCII character ‘\xe6’ in file red_new.py on line 1, but no encoding declared; see http://www.python.org/peps/pep-0263.html for details “,
那么map_new.py 文件头加上# encoding: utf-8。此时执行bash run.sh成功。
如果还是没用,就把没用的注释删除。

2.白名单过滤进行WordCount

首先需要将The_Man_of_Property.txt上传到输入路径INPUT_FILE_PATH_1位置。

  • run.sh文件

    • 用-file方式
    # 文件名:run.sh
    HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
    STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
    
    
    INPUT_FILE_PATH_1="/mytest/The_Man_of_Property.txt"
    OUTPUT_PATH="/mytest/output_file_broadcast"
    
    $HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
    #如果运行失败,把如下代码的注释删除后运行。
    $HADOOP_CMD jar $STREAM_JAR_PATH \
            -input $INPUT_FILE_PATH_1 \
            -output $OUTPUT_PATH \
            -mapper "python map.py mapper_func white_list" \
            #mapper_func是map.py文件里的一个函数名字,white_list是此函数的参数,这里传入的参数white_list是下面-file的white_list文件。执行时自动定位到map.py文件的mapper_func函数位置,相当于java的反射。
            -reducer "python red.py" \
            -jobconf "mapred.reduce.tasks=3" \
            -file ./map.py \
            #-file "./map.py" \,加不加引号都可以
            -file ./red.py \
            -file ./white_list
    
    • 用-cacheFile方式
      • 或者可以用-cacheFile编写run.sh(集群上传),上传大文件,分发快。-file表示本地上传,文件一般比较小。
      • 代码:只要把 " -file ./white_list " 改成" -cacheFile “hdfs://master:9000/mytest/white_list#ABC” \ "
    # 文件名:run.sh
    HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
    STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
    
    
    INPUT_FILE_PATH_1="/mytest/The_Man_of_Property.txt"
    OUTPUT_PATH="/mytest/output_file_broadcast"
    
    $HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
    
    #-file本地分发,-cacheFile集群hdfs分发,参数#前面是路径文件white_list,#后面是名称(它代表前面路径文件别名)。注意把文件white_list上传到集群dfs的-cacheFile对应路径
    $HADOOP_CMD jar $STREAM_JAR_PATH \
        -input $INPUT_FILE_PATH_1 \
        -output $OUTPUT_PATH \
        -mapper "python map.py mapper_func ABC" \
    	-reducer "python red.py" \
    	-jobconf "mapred.reduce.tasks=2" \
    	-jobconf "mapred.job.name=cachefile_demo" \
    	-cacheFile "hdfs://master:9000/mytest/white_list#ABC" \
    	-file ./map.py \
    	-file ./red.py \
    
    • 用-cacheArchive方式
      • 当白名单目录文件很多,可以用cacheArchive,分发效率更高而且会把复杂的目录结构统一管理起来。如果用cacheFile需要把每个文件都上传,或者放在一个总的目录里,则map.py需要列出目录每个文件去读对应去配套开发。
      • 用tar cvzf w.tar.gz white_list1 white_list2命令打包,得到gzip格式的包w.tar.gz后上传。
      • 把w.tar.gz包上传到hdfs上,上传前是一个压缩文件,会自动帮忙解压成目录,这个目录本地在/usr/local/src/hadoop-1.2.1/tmp/mapred/local/taskTracker/distcache/7205497718363743885_-2016522075_437997088/master/w.tar.gz。hadoop环境会自动帮忙解压gzip这个格式成目录,所以可以ls w.tar.gz/。
      • 代码:只要把上面程序的-cacheFile改成 -cacheArchive “hdfs://master:9000/mytest/w.tar.gz#ABC” \。cacheFile和cacheArchive 两者都要注意如果是多级目录,map.py需要列出目录一个一个读文件配套对应去开发。
    # 文件名:run.sh
    HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
    STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
    
    
    INPUT_FILE_PATH_1="/mytest/The_Man_of_Property.txt"
    OUTPUT_PATH="/mytest/output_file_broadcast"
    
    $HADOOP_CMD fs -rmr -skipTrash $OUTPUT_PATH
    
    $HADOOP_CMD jar $STREAM_JAR_PATH \
        -input $INPUT_FILE_PATH_1 \
        -output $OUTPUT_PATH \
        -mapper "python map.py mapper_func ABC" \
    	-reducer "python red.py" \
    	-jobconf "mapred.reduce.tasks=2" \
    	-jobconf "mapred.job.name=cachefile_demo" \
    	-cacheArchive "hdfs://master:9000/mytest/w.tar.gz#ABC" \
    	-file ./map.py \
    	-file ./red.py \
    
  • map.py文件

# encoding: utf-8
# 文件名:map.py
#!/usr/bin/python

import os
import sys
import gzip


def get_file_handler(f):
    file_in = open(f, 'r')
    return file_in

def get_cachefile_handlers(f):
    f_handlers_list = []
    if os.path.isdir(f):
        for fd in os.listdir(f):
            f_handlers_list.append(get_file_handler(f + '/' + fd))
    return f_handlers_list


def read_local_file_func(f):
    word_set = set()
    for cachefile in get_cachefile_handlers(f):
        for line in cachefile:
            word = line.strip()
            word_set.add(word)
    return word_set


def mapper_func(white_list_fd):
    word_set = read_local_file_func(white_list_fd)

    for line in sys.stdin:
        ss = line.strip().split(' ')#每一行通过空格的方式做split得到list数据结构数据。
        for s in ss:
            word = s.strip()
            if word != "" and (word in word_set):#有效判断和白名单过滤
            #if word != "":
                print "%s\t%s" % (s, 1)# %s表示字符串
                #print s+"\t"+"1"
                #print '\t'.join([s,"1"])


#程序入口
if __name__ == "__main__":
	
    module = sys.modules[__name__]
    #sys.modules以字典形式储存程序的所有模块,__name__当前模块。sys.modules是一个全局字典,该字典是python启动后就加载在内存中。每当程序员导入新的模块,sys.modules都将记录这些模块。字典sys.modules对于加载模块起到了缓冲的作用。当某个模块第一次导入,字典sys.modules将自动记录该模块。当第二次再导入该模块时,python会直接到字典中查找,从而加快了程序运行的速度。
    func = getattr(module, sys.argv[1])
    #sys.argv说白了就是一个从程序外部获取参数的桥梁,sys.argv[0]表示程序本身(文件路径),所以从参数1开始,表示获取的参数了。
    args = None
    if len(sys.argv) > 1:
        args = sys.argv[2:]
    func(*args)
  • red.py文件
# encoding: utf-8
#文件名:red.py,和上述实践案例1的red_new.py文件相同
import sys
cur_word=None
sum=0
for line in sys.stdin:
	ss=line.strip().split('\t')
	if len(ss)!=2:
		continue
	word,cnt=ss
	if cur_word==None:
		cur_word=word
	if cur_word!=word:
		print '\t'.join([cur_word,str(sum)])
		cur_word=word
		sum=0
	sum+=int(cnt)
print '\t'.join([cur_word,str(sum)])
  • 目录white_list下的文件(文件名可以任意)

you
fight
again
fly
fall
the

  • The_Man_of_Property.txt文件,和上述实践案例1的1.data文件相同

Is not until you fly that you fall ,
when the sun come again ,
you are unstopable .
when the dreams come again ,
you fight .

运行命令:bash run.sh或sh run.sh
提前手动测试:cat The_Man_of_Property.txt | python map.py mapper_func white_list | sort -k1 | python red.py( > result.local)
验证结果准确:

cat The_Man_of_Property.txt | grep -o 'again'  | wc -l
#注意不能用下一行代码。虽然-w表示按照单词,但是cat The_Man_of_Property.txt | grep -w ‘you' 返回的是含有“you”的完整行并且对”you“进行显示颜色,所以它返回的仍是含有“again”的行数。
cat The_Man_of_Property.txt | grep -cw 'again'

3.全局排序

(1)问题:

有多个文件a.txt和b.txt,需要使用mapreduce对文件内容进行合并且按第一列排序输出

(2)方法一(缺点:靠一个reduce很危险):

(i)理论
  • 一个reduce,配置-jobconf mapred.reduce.tasks=1。
  • 如果input参数特别多,可用通配符。
  • mapreduce默认按字符排序而非数字排序
    • 需要正序排序:所以设置base_count =10000, new_key = base_count + int(key),前提是需要排序的字段<base_count =10000。然后输出可以还原回来,print str(int(key) - base_count)+‘\t’+val。
    • 逆序:base_count =99999, new_key = base_count - int(key)。然后输出可以还原回来,print str(base_count - int(key))+‘\t’+val。

set -e 和 set -x
推荐使用这个,因为使用set -e -x时代码遇到错误会退出,而不使用会跳过出错代码继续执行下面代码。
注意使用set -e -x时,对于这行代码“ HADOOP_CMD fs -rmr -skipTrash OUTPUT_SORT_PATH ”,若没有OUTPUT_SORT_PATH目录,要暂时注释这个代码或者创建这个目录,否则会报错。
———————————————————
在你开始构思并写下具体的代码逻辑之前,先插入一行set -e和一行set -x。
———————————————————
set -x会在执行每一行 shell 脚本时,把执行的内容输出来。它可以让你看到当前执行的情况,里面涉及的变量也会被替换成实际的值。
———————————————————
set -e会在执行出错时结束程序,就像其他语言中的“抛出异常”一样。(准确说,不是所有出错的时候都会结束程序,见下面的注) set -e, 这句语句告诉bash如果任何语句的执行结果不是true则应该退出。这样的好处是防止错误像滚雪球般变大导致一个致命的错误,而这些错误本应该在之前就被处理掉。
———————————————————
使用-e帮助你检查错误。如果你忘记检查(执行语句的结果),bash会帮你执行。不幸的是,你将无法检查$?,因为如果执行的语句不是返回0,bash将无法执行到检查的代码。

(ii)实践
  • run.sh文件
# 文件名:run.sh
set -e -x

HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"
INPUT_FILE_PATH_A="/mytest/mapreduce/mr_allsort_1reduce_python/input/a.txt"
INPUT_FILE_PATH_B="/mytest/mapreduce/mr_allsort_1reduce_python/input/b.txt"

OUTPUT_SORT_PATH="/mytest/mapreduce/mr_allsort_1reduce_python/output_sort"

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH

# Step 3.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B\
    -output $OUTPUT_SORT_PATH \
    -mapper "python map_sort.py" \
    -reducer "python red_sort.py" \
    -jobconf "mapred.reduce.tasks=1" \
    -file ./map_sort.py \
    -file ./red_sort.py \
  • map_sort.py文件
# encoding: utf-8
# 文件名:map_sort.py
#!/usr/local/bin/python

import sys

#正序,base_count = 10000

base_count = 99999

for line in sys.stdin:
    ss = line.strip().split('\t')
    key = ss[0]
    val = ss[1]

    new_key = base_count - int(key) 
    #正序,new_key = base_count + int(key)
    
    print "%s\t%s" % (new_key, val)
  • red_sort.py文件
# encoding: utf-8
# 文件名:red_sort.py
#!/usr/local/bin/python

import sys

#base_value = 10000
base_value = 99999

for line in sys.stdin:
    key, val = line.strip().split('\t')
    #正序,print str(int(key) - base_value) + "\t" + val
    print str(base_value - int(key)) + "\t" + val
  • a.txt文件
    在这里插入图片描述
  • b.txt文件
    在这里插入图片描述

手动测试:cat a.txt b.txt | python map_sort.py | sort -k1 | python red_sort.py
执行:

hadoop fs -put a.txt b.txt /mytest/mapreduce/mr_allsort_1reduce_python/input/
bash run.sh

结果:
在这里插入图片描述

(3)方法二(多个reduce并发):

(i)理论
  • 配置的理解

    • -jobconf mapred.reduce.tasks=2 \
      • 设置两个桶,reduce个数设置为2.
    • -jobconf stream.num.map.output.key.fields=2 \
      • 指定前两个字段做key。
        在这里插入图片描述
    • -jobconf num.key.fields.for.partition=1 \
      • 指定哪一个做partition。
      • 数据:aaa bbb ccc。通过设置这个参数可以指定哪一个做partition,这里可以指定aaa或bbb或ccc做partition。
    • -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
  • 配置代码

    -jobconf mapred.reduce.tasks=2 \
    -jobconf stream.num.map.output.key.fields=2 \
    -jobconf num.key.fields.for.partition=1 \
    -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner
    
  • 假如设置reduce个数=2

    • 那么map_sort.py要开发对应的代码来支持,首先划分key的两个范围1-50,50-100。然后设置key的范围是1-50的桶号为0,设置50-100的key桶号为1。

    思路
    reduce1: 1-50
    reduce2: 51-110
    要想全局排序,不仅桶内部需要有序(框架自带),而且桶与桶之间也需要排序(与partition有关,这里根据范围创建分区id)。

(ii)实践
  • run.sh文件
# 文件名:run.sh
set -e -x

HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"

INPUT_FILE_PATH_A="/mytest/mapreduce/allsort_python/input/a.txt"
INPUT_FILE_PATH_B="/mytest/mapreduce/allsort_python/input/b.txt"

OUTPUT_SORT_PATH="/mytest/mapreduce/allsort_python/output_sort"

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH

# Step 3.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_A,$INPUT_FILE_PATH_B\
    -output $OUTPUT_SORT_PATH \
    -mapper "python map_sort.py" \
    -reducer "python red_sort.py" \
    -file ./map_sort.py \
    -file ./red_sort.py \
    -jobconf mapred.reduce.tasks=2 \
    -jobconf stream.num.map.output.key.fields=2 \
    -jobconf num.key.fields.for.partition=1 \
    -partitioner org.apache.hadoop.mapred.lib.KeyFieldBasedPartitioner


    #-jobconf stream.map.output.field.separator='	' \
  • map_sort.py文件
# encoding: utf-8
# 文件名:map_sort.py
#!/usr/local/bin/python

import sys

base_count = 10000

for line in sys.stdin:
    ss = line.strip().split('\t')
    key = ss[0]
    val = ss[1]

    new_key = base_count + int(key)

    red_idx = 1
    #原来的key>50时桶号idx=1,key<50时idx=0.
    if new_key < (10100 + 10000) / 2:
        red_idx = 0

    print "%s\t%s\t%s" % (red_idx, new_key, val)
  • red_sort.py文件
# encoding: utf-8
# 文件名:red_sort.py
#!/usr/local/bin/python

import sys

base_count = 10000

for line in sys.stdin:
    idx_id, key, val = line.strip().split('\t')

    new_key = int(key) - base_count
    print '\t'.join([str(new_key), val])
  • a.txt文件
1	hadoop
31	hadoop
51	hadoop
71	hadoop
91	hadoop
  • b.txt文件
10	java
30	java
50	java
70	java
90	java
100	java

手动测试:cat a.txt b.txt | python map_sort.py | sort -k1,2 | python red_sort.py
在这里插入图片描述
在这里插入图片描述

执行:bash run.sh
在这里插入图片描述

4.指定key和partition操作实践

  • aaa.txt文件
    d.1.5.23
    e.9.4.5
    e.5.9.22
    e.5.1.45
    e.5.1.23
    a.7.2.6
    f.8.3.3
    
  • run.sh文件
    • -jobconf stream.num.map.output.key.fields=3 \
      • 取前三个字段作为key,比如一行数据a b c d,key=a b c,value=d。
    • -jobconf stream.map.output.field.separator=. \
      • 指输入的数据怎样进行分割,指定特殊分割符为" . ",默认情况是制表符\t。
    • -jobconf map.output.key.field.separator=. \
      • 指对key的内部怎样分割,这里可以删除这个配置效果也是一样的。
    • -jobconf mapred.text.key.partitioner.options=-k2,3 \
      • partition按照第二、三列分,比如一行数据a b c d,partition=b c。
    • -jobconf mapred.reduce.tasks=3
      • 3个reduce
# 文件名:run.sh
set -e -x

HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"

INPUT_FILE_PATH_A="/mytest/mapreduce/mr_partiotion_key/input/aaa.txt"

OUTPUT_SORT_PATH="/mytest/mapreduce/mr_partiotion_key/output_sort"

$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_SORT_PATH

# Step 3.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_A \
    -output $OUTPUT_SORT_PATH \
    -mapper "cat" \
    -reducer "cat" \
    -jobconf stream.num.map.output.key.fields=3 \
    -jobconf stream.map.output.field.separator=. \
    -jobconf mapred.text.key.partitioner.options=-k2,3 \
    -jobconf mapred.reduce.tasks=3
  • 手动测试:cat aaa.txt | sort -t ‘.’ -k2,3 | sort -t ‘.’ -k1

    • map和reduce都是cat,代码只是根据第2、3列分区且对key(前三列)排序,相当于sort -t ‘.’ -k2,3 | sort -t ‘.’ -k1。
      在这里插入图片描述
  • 执行:bash run.sh

    • partition相同的每一行数据会分到同一个reduce中,同一个reduce中的数据按key排序。
      在这里插入图片描述

5.JOIN

  • a.txt文件
aaa1	100
aaa100	111
bbb1	122
bbb100	133
ccc1	144
ccc100	155
  • b.txt文件
aaa1	hadoop
aaa100	spark
bbb1	spark
bbb100	hadoop
ccc1	hive
ccc100	hadoop
  • map_a.py文件
    • 获得中间文件如下图
      在这里插入图片描述
# encoding: utf-8
# 文件名:map_a.py
#!/usr/local/bin/python

import sys

for line in sys.stdin:
    ss = line.strip().split('	')

    key = ss[0]
    val = ss[1]

    print "%s\t1\t%s" % (key, val)
  • map_b.py文件
    • 获得中间文件如下图
      在这里插入图片描述
# encoding: utf-8
# 文件名:map_b.py
#!/usr/local/bin/python

import sys

for line in sys.stdin:
    ss = line.strip().split('	')

    key = ss[0]
    val = ss[1]

    print "%s\t2\t%s" % (key, val)
  • red_join.py文件
    • 相同的key会放到一起,可以用
      在这里插入图片描述
# encoding: utf-8
# 文件名:red_join.py
#!/usr/local/bin/python

import sys

val_1 = ""

for line in sys.stdin:
    key, flag, val = line.strip().split('\t')
	#实现简单,还有很多情况
    if flag == '1':
        val_1 = val
    elif flag == '2' and val_1 != "":
        val_2 = val
        print "%s\t%s\t%s" % (key, val_1, val_2)
        val_1 = ""
  • run.sh文件
# 文件名:run.sh
set -e -x

HADOOP_CMD="/usr/local/src/hadoop-2.6.1/bin/hadoop"
STREAM_JAR_PATH="/usr/local/src/hadoop-2.6.1/share/hadoop/tools/lib/hadoop-streaming-2.6.1.jar"

INPUT_FILE_PATH_A="/mytest/mapreduce/mrjoin_python/input/a.txt"
INPUT_FILE_PATH_B="/mytest/mapreduce/mrjoin_python/input/b.txt"

OUTPUT_A_PATH="/mytest/mapreduce/mrjoin_python/output_a"
OUTPUT_B_PATH="/mytest/mapreduce/mrjoin_python/output_b"

OUTPUT_JOIN_PATH="/mytest/mapreduce/mrjoin_python/output_join"

#$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_A_PATH $OUTPUT_B_PATH $OUTPUT_JOIN_PATH
#$HADOOP_CMD fs -rmr -skipTrash $OUTPUT_JOIN_PATH

# Step 1.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_A \
    -output $OUTPUT_A_PATH \
    -mapper "python map_a.py" \
    -file ./map_a.py \

# Step 2.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $INPUT_FILE_PATH_B \
    -output $OUTPUT_B_PATH \
    -mapper "python map_b.py" \
    -file ./map_b.py \

# Step 3.
$HADOOP_CMD jar $STREAM_JAR_PATH \
    -input $OUTPUT_A_PATH,$OUTPUT_B_PATH \
    -output $OUTPUT_JOIN_PATH \
    -mapper "cat" \
    -reducer "python red_join.py" \
    -file ./map_join.py \
    -file ./red_join.py \
    -jobconf stream.num.map.output.key.fields=2 \
    -jobconf num.key.fields.for.partition=1
  • 执行:bash run.sh
    在这里插入图片描述

6.与二分算法结合

  • ip.lib.txt文件,这里只显示前10条数据。
    • 已存在IP地址和物理地址信息的表
0.0.0.0 0.255.255.255 NULL IANA保留地址 NULL
1.0.0.0 1.0.0.255 亚洲 亚太地区 NULL
1.0.1.0 1.0.1.255 亚洲 中国 福建
1.0.2.0 1.0.3.255 亚洲 中国 福建
1.0.4.0 1.0.7.255 大洋洲 澳大利亚 NULL
1.0.8.0 1.0.15.255 亚洲 中国 广东
1.0.16.0 1.0.31.255 亚洲 日本 NULL
1.0.32.0 1.0.63.255 亚洲 中国 广东
1.0.64.0 1.0.127.255 亚洲 日本 NULL
1.0.128.0 1.0.255.255 亚洲 泰国 NULL
  • cookie_ip.txt文件
    • 第一个字段代表一个用户,第二个字段是ip地址。
    • 是要处理的输入文件,每处理一行信息,需要获得其实际物理地址。
ECEE8FBBBB	113.224.76.226
ED38780B1D	106.36.217.145
120BB4FB44	113.109.42.83
9D4EC87B4B	219.153.212.31
AF0E43C785	111.77.229.40
4AAAEB560B	60.13.190.132
  • map.py文件
    • 使用二分算法查找实际物理地址
# encoding: utf-8
# 文件名:map.py
#!/usr/bin/python

import sys

#ch2 = lambda x: '.'.join([str(x/(256**i)%256) for i in range(3,-1,-1)])
ip_convert = lambda x:sum([256**j*int(i) for j,i in enumerate(x.split('.')[::-1])])

def load_ip_lib_func(ip_lib_fd):
    ip_lib_list = []
    file_in = open(ip_lib_fd, 'r')
    for line in file_in:
        ss = line.strip().split(' ')
        if len(ss) != 5:
            continue
        start_ip = ss[0].strip()
        end_ip = ss[1].strip()
        area = ss[2].strip()
        country = ss[3].strip()
        province = ss[4].strip()

        ip_lib_list.append((ip_convert(start_ip), ip_convert(end_ip), area, country, province))

    return ip_lib_list

# 从ip_lib_list列表中根据ip地址二分查找对应的实际物理地址
def get_addr(ip_lib_list, ip_str):
    ip_num = ip_convert(ip_str)

    low_index = 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值