1 kafka中topic的Partition
1.1 hadoop、storm、kafka中partion介绍
hadoop:默认是hashPartition分组策略,作用是每个maptask中相同的key数据发往同一
的reduce上,避免数据统计时的唯一性;
storm:包含很多的分组策略(ShuffleGrouping、FieldGrouping...),作用是上游组件将
数据发往下游组件的分发机制;
kafka:默认是hashPartion,作用是数据平均分配到多个partition上。
1.2 Partition的简介
说明:在kafka文件存储中,同一个topic下有多个不同的partition,每个partition为一个目录,
partition的命名规则为topic名称+有序序号,第一个partition的序号从0开始,序号最大为
partition数量减1
示例: order_par3_topic-0
order_par3_topic-1
order_par3_topic-2
1.3 Partition的副本机制
说明:hdfs中副本机制,同样的kafka中每个partition分片都可以设置副本数,解决某个broker挂掉的
数据丢失问题。
问题:如果设置多个副本,生成数据和消费数据时发给那个副本?
解决:会从多个副本中选出一个leader,由leader接受数据,并由leader提供数据的消费。
1.4 Partition和副本的示例图
![](http://i.imgur.com/WP4qvFc.png)
图解:a、连接broker-list中任意一台broker服务器;
b、发送数据是需要知道topic对应的partition的个数及leader所在的节点。这些信息的由
broker提供,每一个broker都能提供一份元数据信息,该元数据信息包含:哪些broker是
存活的,topic有多少分片,每个分片的副本中那个是leader;
c、数据生产时,数据发送到那个分片的leader上是由producer的代码指定的(轮询、随机、
hash等等策略,默认是hashPartition);
d、数据通过socket连接直接发送到partition所在的broker。
1.5 自定义Partition
基本步骤:a、实现Partition类;
b、添加构造器,该构造器主要为了校验Properties;
c、将自定义的Partition加入到Properties中,
properties.put("partitioner.class","...");
d、producer.send方法中必须指定一个PartitionKey,否则自定义的Partition不生效。
示例代码:
//自定义的Partition
package com.kafka.simple;
import kafka.producer.Partitioner;
import kafka.utils.VerifiableProperties;
public class PartitionerWrapper implements Partitioner {
/**
* Properties配置文件校验器
*
* @param verifiableProperties
*/
public PartitionerWrapper(VerifiableProperties verifiableProperties) {
super();
}
@Override
public int partition(Object key, int numPartitions) {
return 2;
}
}
//消费者代码
package com.kafka.simple;
import java.util.Properties;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;
/**
* kafka Producer API
*/
public class KafkaProducerSimple {
public static void main(String[] args) {
// 指定当前kafka producer生产数据的目的地
String topic = "self-part-topic";
// 读取配置文件
Properties props = new Properties();
// 指定序列化编码(key.serializer.class默认为serializer.class)
props.put("serializer.class", "kafka.serializer.StringEncoder");
// kafka broker对应的主机,格式为:host:port,host:port,host:port
props.put("metadata.broker.list", "hdp01:9092,hdp02:9092,hdp03:9092");
// request.required.acks,设置发送数据是否需要服务端的反馈,有三个值0,1,-1
// 0:意味着producer永远不会等待一个来自broker的ack,这就是0.7版本的行为。
// 这个选项提供了最低的延迟,但是持久化的保证是最弱的,当server挂掉的时候会丢失一些数据。
// 1:意味着在leader replica已经接收到数据后,producer会得到一个ack。
// 这个选项提供了更好的持久性,因为在server确认请求成功处理后,client才会返回。
// 如果刚写到leader上,还没来得及复制leader就挂了,那么消息才可能会丢失。
// -1:意味着在所有的ISR都接收到数据后,producer才得到一个ack。
// 这个选项提供了最好的持久性,只要还有一个replica存活,那么数据就不会丢失
props.put("request.required.acks", "1");
props.put("partitioner.class", "com.kafka.simple.PartitionerWrapper");
// 通过配置文件创建生产者
Producer<String, String> producer = new Producer<String, String>(new ProducerConfig(props));
// 通过for循环生成数据
int msgNo = 0;
while (true) {
String msg = "self-part-topic:"+System.currentTimeMillis();
// 5、调用producer的send方法发送数据
// 注意:这需要指定partitionKey,用来配合自定义的的partition进行数据分发
producer.send(new KeyedMessage<String, String>(topic, ++msgNo + "", msg));
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
2 Kafka消息的分发
2.1 Producer客户端负责消息的分发
说明:a、kafka集群中任何一个broker都可以向producer提供metadata信息,这些metadata包
含"集群中存活的servers列表、partition leader列表等信息";
b、当producer获取到metadata信息之后,producer将会和Topic下所有partition
leader保持Socket连接;
c、消息由producer直接通过socket发送到broker,中间不经过任何"路由层",消息被路由
到哪个partition上由producer客户端决定的,如果一个topic中有多个partitions,
那么producer端实现"消息均衡分发"是必要的;
d、在producer端的配置文件中,开发者可以指定partition的路由方式。
3 Consumer与Topic的关系
说明:a、每个group中可以有多个consumer,每个consumer属于一个consumer group,通常情
况下,一个group会包含多个consumer,这样不仅仅可以提高topic中消息的并发消费
能力,而且还能提高"故障容错"性,如果group中某个consumer失效那么其消费的
partitions将会由其他consumer自动接管;
b、对于topic中一条特定的消息,只会被订阅此topic的每个group中的其中一个consumer
消费,此消息不会发送给一个group的多个consumer。由此知道一个group中所有的
consumer将会交错的消费整个topic,每个group中consumer消息消费相互独立的,
可以认为一个group是一个"订阅者";
c、在kafka中,一个partition中的消息只会被group中的一个consumer消费(同一时刻)
,一个topic中的每个partitions,只会被一个"订阅者"中的一个consumer消费,不过
一个consumer可以同时消费多个partitions的消息;
d、kafka设计原理决定,对于一个topic,同一个group中不能有多于partitions的个数的
consumers同时消费,否则将意味着某些consumer将无法得到消息。
注:kafka只能保证一个partition中的消息被某个consumer消息是顺序的,事实上,从topic角
度来说,当有多个partitions时,消息仍不是全局有序的。
4 Consumer的负载均衡图
![](http://i.imgur.com/w1LOfxW.png)
当一个group中,有consumer加入或者离开时,会触发partitions均衡.均衡的最终目的,是提升topic的并发消费能力,步骤如下:
1、假如order_topic,具有如下partitions: P0,P1,P2,P3
2、加入group中,有如下consumer: C0,C1
3、首先根据partition索引号对partitions排序: P0,P1,P2,P3
4、根据consumer.id排序: C0,C1
5、计算倍数: M = [P0,P1,P2,P3].size / [C0,C1].size,本例值M=2(向上取整)
然后依次分配partitions: C0 = [P0,P1],C1=[P2,P3],即Ci = [P(i * M),P((i + 1) * M -1)]
4.1 Consumer的总结
说明:1、consumerGroup之间消费的数据互不干扰;
2、每个consumer对应一个或多个分片,一个分片对应一个consumer;
3、多于分片数的consumer,是没有分片的数据可以被消费的,多于的consumer等到其中一
个或多个consumer挂掉的时候才有机会消费到数据。这个过程叫做rebalance。