/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package com.aliyun.odps.mapred; import java.io.IOException; import java.util.Iterator; import com.aliyun.odps.data.Record; /** * Reducer 对与一个键(Key)关联的一组数值集(Values)进行归约计算. * * <p> * Reducer 由{@link com.aliyun.odps.mapred.conf.JobConf#setReducerClass(Class)} * 进行指定.<br/> * Reducer 任务数由 * {@link com.aliyun.odps.mapred.conf.JobConf#setNumReduceTasks(int)} * 进行指定,用户需要根据 Reducer 要处理的数据量({@link Mapper} 的输出)进行调整,若没有指定 Reducer 任务数,默认设置为 * {@link Mapper} 数的 1/4<br/> * </p> * * <p> * 与 {@link Mapper} 类似,Reducer 也包括三个方法: * <ul> * <li>{@link #setup(TaskContext)} 在任务开始, reduce 方法之前调用,每个reduce调用且仅调用一次 * <li>{@link #cleanup(TaskContext)} 在任务结束,reduce 方法之后调用,每个reduce调用且仅调用一次 * <li>{@link #reduce(Record, Iterator, TaskContext)} reduce 方法,每次调用传入一个 Key * 和与之关联的一组 Value集合 * </ul> * 上述方法都会传入一个上下文对象,Reducer 实现时可以调用上下文对象的方法,编写代码时最好能多参考下 {@link TaskContext} 接口<br/> * * <br/> * <strong><font color="red"> 注意:传给 reduce 方法的 Key key 和 Iterator<VALUEIN> * values 里的Value对象都会被上下文对象复用。如果需要保存Record的数据,使用{@link Record#toArray} * </font></strong> <br/> * * </p> * * <p> * 在{@link Mapper} 端,每个 mapper 生成中间结果(键值对)并通过Partitioner Columns 哈希分发给对应的 * Reducer,Reducer 端,每个 Reducer 会读入所有 {@link Mapper} 输出给它的数据,这个过程分三步:<br/> * <ol> * <li>归并排序:Reducer 的输入虽然每一路都有序,但是整体并未有序,这时 Reducer 会进行归并排序,排序使用的比较列(Sort * Columns)可以由 * {@link com.aliyun.odps.mapred.conf.JobConf#setOutputKeySortColumns(String[])} * 进行指定,如果没有指定,使用 key中的所有列做排序。 * <li>按 Key 对 Values 分组:经过第一步的归并排序,所有的 Key/Value 对都已经有序,这是会使用 * {@link com.aliyun.odps.mapred.conf.JobConf#setOutputGroupingColumns(String[])} * 指定的分组列对排好序的 Key/Value 对进行分组,如果没有指定,则默认使用key中的所有列做分组。 * <li>循环调用 reduce 方法,传入 Key 和 Value迭代器进行规约计算。 * </ol> * </p> * * * <p> * 计算结果可以通过下面的方法输出到结果表: * <ul> * <li>{@link TaskContext#write(Record)} 输出计算结果到默认输出表 * <li>{@link TaskContext#write(Record, String)} 输出计算结果到指定 label 的输出表 * </ul> * </p> * * <p> * 在客户端定义的作业配置({@link com.aliyun.odps.mapred.conf.JobConf})可以通过 * {@link TaskContext#getJobConf()} 方法取得。 * </p> * * <p> * 代码示例,摘自 WordCount: * * <pre> * public static class SumReducer extends ReducerBase { * private Record result; * * @Override * public void setup(TaskContext context) throws IOException { * result = context.createOutputRecord(); * } * * @Override * public void reduce(Record key, Iterator<Record> values, TaskContext context) throws * IOException { * long count = 0; * while (values.hasNext()) { * Record val = values.next(); * count += (Long) val.get(0); * } * result.set(0, key.get(0)); * result.set(1, count); * context.write(result); * } * } * </pre> * * </p> * * @see TaskContext * @see Mapper */ public interface Reducer { /** * 传给{@link Reducer}的上下文对象. */ public interface TaskContext extends com.aliyun.odps.mapred.TaskContext { /** * 向前读取下一个Key value对象,如果还有未读取的Key value,返回true,否则返回false * * 读取的Key对象可以通过{@link #getCurrentKey()}取得 * * @return 如果还有未读取的Key,返回true,否则返回false */ public boolean nextKeyValue(); /** * 获取当前Key对象 * * 注意:此方法返回的Key对象在{@link #nextKey()}方法里会被复用 * * @return 当前Key对象 */ public Record getCurrentKey(); /** * 返回当前Key对应的所有Value的迭代器 * * 注意:迭代器里的Value对象会被复用 * * @return 当前Key对应的所有Value的迭代器 */ public Iterator<Record> getValues(); } /** * 在任务开始, reduce 方法之前调用. * * @param context * Reduce 上下文对象 * @throws IOException */ void setup(TaskContext context) throws IOException; /** * 对每个 Key 和与其关联的一组Value集合进行处理. * * * <p> * <strong><font color="red"> 注意:传给 reduce 方法的 Key key 和 Iterator<Record> * values 里的Value对象都会被上下文对象复用!</font></strong> <br/> * </p> * * @param key * 键 * @param values * 与 key 关联的 value 迭代器 * @param context * Reduce 上下文对象 * @throws IOException */ void reduce(Record key, Iterator<Record> values, TaskContext context) throws IOException; /** * 在任务结束, reduce 方法之后调用,可以重载此方法. * * @param context * Reduce 上下文对象 * @throws IOException */ void cleanup(TaskContext context) throws IOException; }