流式计算--kafka2(java api操作kafka)

  本篇博客基于kafka集群搭建,在此基础上操作

  1.新建maven工程添加依赖:

     

 <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka_2.12</artifactId>
            <version>0.10.2.1</version>
            <scope>compile</scope>
            <exclusions>
                <exclusion>
                    <artifactId>jmxtools</artifactId>
                    <groupId>com.sun.jdmk</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>jmxri</artifactId>
                    <groupId>com.sun.jmx</groupId>
                </exclusion>
                <exclusion>
                    <artifactId>jms</artifactId>
                    <groupId>javax.jms</groupId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-api</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

 2.编写一个生产者:

  

package com.wx.kafka;
import kafka.javaapi.producer.Producer;
import kafka.producer.KeyedMessage;
import kafka.producer.ProducerConfig;

import java.util.Properties;
import java.util.UUID;

/**
 * 这是一个简单的Kafka producer代码
 * 包含两个功能:
 * 1、数据发送
 * 2、数据按照自定义的partition策略进行发送
 * KafkaSpout的类
 */
public class KafkaProducer  {
    public static void main(String[] args)
    {
        /**
         * 1、指定当前kafka producer生产的数据的目的地
         *  创建topic可以输入以下命令,在kafka集群的任一节点进行创建。下面表示的是一个分区一个副本
         *  bin/kafka-topics.sh --create --zookeeper zk01:2181 --replication-factor 1 --partitions 1 --topic test
         */
        String TOPIC = "kafka4Mq";
        /**
         * 2、读取配置文件
         */
        Properties props = new Properties();
        /*
         * key.serializer.class默认为serializer.class
         */
        props.put("serializer.class", "kafka.serializer.StringEncoder");
        /*
         * kafka broker对应的主机,格式为host1:port1,host2:port2
         */
        props.put("metadata.broker.list", "192.168.25.130:9092,192.168.25.131:9092,192.168.25.132: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("num.partitions", "4");
        /*
         * 可选配置,如果不配置,则使用默认的partitioner partitioner.class
         * 默认值:kafka.producer.DefaultPartitioner
         * 用来把消息分到各个partition中,默认行为是对key进行hash。
         */
         //props.put("partitioner.class", "com.wx.kafka.MyLogPartitioner");
          props.put("partitioner.class", "kafka.producer.DefaultPartitioner");

        /**
         * 3、通过配置文件,创建生产者
         */
        Producer<String, String> producer = new Producer<String, String>(new ProducerConfig(props));
        /**
         * 4、通过for循环生产数据
         */
        for (int messageNo = 1; messageNo < 100000; messageNo++) {

            String messageStr = new String(messageNo +"注意:这里需要指定 partitionKey,用来配合自定义的MyLogPartitioner进行数据分发注意:这里需要指定 partitionKey,用来配合自定义的MyLogPartitioner进行数据分发") ;
                    /**
                     * 5、调用producer的send方法发送数据
                     * 注意:这里需要指定 partitionKey,用来配合自定义的MyLogPartitioner进行数据分发
                     */
            producer.send(new KeyedMessage<String, String>(TOPIC, messageNo + " ", "appid" + UUID.randomUUID() + messageStr));
        }
    }

}

编写一个消费者:

 

package com.wx.kafka;

import kafka.consumer.Consumer;
import kafka.consumer.ConsumerConfig;
import kafka.consumer.ConsumerIterator;
import kafka.consumer.KafkaStream;
import kafka.javaapi.consumer.ConsumerConnector;
import kafka.message.MessageAndMetadata;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class KafkaConsumerSimple implements Runnable {
    public String title;
    public KafkaStream<byte[], byte[]> stream;
    public KafkaConsumerSimple(String title, KafkaStream<byte[], byte[]> stream) {
        this.title = title;
        this.stream = stream;
    }
    public void run() {
        System.out.println("开始运行 " + title);
        ConsumerIterator<byte[], byte[]> it = stream.iterator();
        /**
         * 不停地从stream读取新到来的消息,在等待新的消息时,hasNext()会阻塞
         * 如果调用 `ConsumerConnector#shutdown`,那么`hasNext`会返回false
         * */
        while (it.hasNext()) {
            MessageAndMetadata<byte[], byte[]> data = it.next();
            String topic = data.topic();
            int partition = data.partition();
            long offset = data.offset();
            String msg = new String(data.message());
            System.out.println(String.format(
                    "Consumer: [%s],  Topic: [%s],  PartitionId: [%d], Offset: [%d], msg: [%s]",
                    title, topic, partition, offset, msg));
        }
        System.out.println(String.format("Consumer: [%s] exiting ...", title));
    }
    public static void main(String[] args) throws Exception{
        Properties props = new Properties();
        props.put("group.id", "dashujujiagoushi");
        props.put("zookeeper.connect", "192.168.25.130:2181,192.168.25.131:2181,192.128.25.132:2181");
        props.put("auto.offset.reset", "largest");
        props.put("auto.commit.interval.ms", "1000");
        props.put("partition.assignment.strategy", "roundrobin");
        ConsumerConfig config = new ConsumerConfig(props);
        String topic1 = "kafka4Mq"; /制定消费的主题//
        String topic2 = "paymentMq";
        //只要ConsumerConnector还在的话,consumer会一直等待新消息,不会自己退出
        ConsumerConnector consumerConn = Consumer.createJavaConsumerConnector(config);
        //定义一个map
        Map<String, Integer> topicCountMap = new HashMap<String, Integer>();
        topicCountMap.put(topic1, 3);
        //Map<String, List<KafkaStream<byte[], byte[]>> 中String是topic, List<KafkaStream<byte[], byte[]>是对应的流
        Map<String, List<KafkaStream<byte[], byte[]>>> topicStreamsMap = consumerConn.createMessageStreams(topicCountMap);
        //取出 `kafkaTest` 对应的 streams
        List<KafkaStream<byte[], byte[]>> streams = topicStreamsMap.get(topic1);
        //创建一个容量为4的线程池
        ExecutorService executor = Executors.newFixedThreadPool(3);
        //创建20个consumer threads
        for (int i = 0; i < streams.size(); i++)
            executor.execute(new KafkaConsumerSimple("消费者" + (i + 1), streams.get(i)));
    }
}

 自定义分区函数:
 

package com.wx.kafka;

import kafka.producer.Partitioner;
import kafka.utils.VerifiableProperties;
import org.apache.log4j.Logger;

public class MyLogPartitioner implements Partitioner {
    private static Logger logger = Logger.getLogger(MyLogPartitioner.class);

    public MyLogPartitioner(VerifiableProperties props) {
    }

    public int partition(Object obj, int numPartitions) {

      //return Integer.parseInt(obj.toString())%numPartitions;  //使用自定分区就要出现尝试三次失败的错误
      return 1;
    }
}

这里但会一个分区:返回的写死的分区就去看分区1日志,ok  有数据,其他分区没有

问题:

1.如果使用自定义分区则会出现:Failed to send requests for topics paymoneyMq with correlation ids in [0,8] 这样的错误

最后用的生产者默认的分区,将生产者的问题解决,可以参考这篇博客:https://blog.csdn.net/high2011/article/details/53705737

2.消费者出现:org/apache/zookeeper/Watcher这个错误,在pom依赖中将zookeeper 的以来去除即可:

3.Failed to send messages after 3 tries.

    遇到该错误主要有以下几种原因

       1.服务器的zookeeper和kafka未开启

       2.配置文件中,没有配置advertised.host.name和advertised.port

       3.服务器防火墙未关闭,或者无法连接9092端口

   解决

       1.运行producer java API时,首先开启服务器的zookeeper和kafka

       2.找到$KAFKA_HOME/config/中的配置文件,配置advertised.host.name="ip",advertised.port="port".具体解释可以参考配置文件中的注释

        3.在本地telnet ip 9092,观察是否可以连接,如不能连接,关闭服务器防火墙,或者打开9092端口。

       

            更改服务器下启动kafka的配置文件,默认server.properties,添加下面内容

              host.name=192.168.195.132

              advertised.host.name=192.168.195.132

              advertised.port=9092

              问题这样就解决了

          结果发现是代码中的topic和集群中的不一致,改成集群中之前设置的orderMq就行了,

  实现自定义分区:

   首先分区:

   bin/kafka-topics.sh --create --zookeeper 192.168.31.130:2181 --replication-factor 2 --partitions 4 --topic orderMq

  查看分区:

   

  重写分区类的分区函数,上面的一个有误:

package com.wx.kafka;

import kafka.producer.Partitioner;
import kafka.utils.VerifiableProperties;
import org.apache.log4j.Logger;

public class MyLogPartitioner implements Partitioner {
  
    public MyLogPartitioner() {
        this(new VerifiableProperties());
    }


    public MyLogPartitioner(VerifiableProperties properties) {
        // nothings
    }
    public int partition(Object key, int numPartitions) {

        int num = Integer.valueOf(((String) key).replaceAll("key_", "").trim());
        return num % numPartitions;
    }
}

  运行:可以看到消费多个分区的消息:


 

下一篇博客将介绍kafka整合flume....

 

 

 

 

 

 

 

 

 

 


   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时空恋旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值