【kafka消息队列】

Kafka

一kafka是什么

Kafka是由Apache软件基金会开发的一个开源流处理平台,由ScalaJava编写。Kafka是一种高吞吐量的分布式发布订阅消息系统,它可以处理消费者在网站中的所有动作流数据。 这种动作(网页浏览,搜索和其他用户的行动)是在现代网络上的许多社会功能的一个关键因素。 这些数据通常是由于吞吐量的要求而通过处理日志和日志聚合来解决。 对于像Hadoop一样的日志数据和离线分析系统,但又要求实时处理的限制,这是一个可行的解决方案。Kafka的目的是通过Hadoop的并行加载机制来统一线上和离线的消息处理,也是为了通过集群来提供实时的消息。

 

二kafka体系结构

2.1 逻辑架构

2.2 部署架构

2.3 集群架构

三kafka设计理念简介

3.1 基本概念

3.1.1 Producers

消息和数据生产者,向 Kafka 的一个 topic 发布消息的过程叫做 producers。Producer将消息发布到指定的Topic中,同时Producer也能决定将此消息归属于哪个partition;比如基于"round-robin"方式或者通过其他的一些算法等.消息和数据生产者,向 Kafka 的一个 topic 发布消息的过程叫做 producers。异步批量发送可以很有效的提高发送效率。Kafka producer的异步发送模式允许进行批量发送,先将消息缓存在内存中,然后一次请求批量发送出去

3.1.2 Consumers

消息和数据消费者,订阅 topics 并处理其发布的消息的过程叫做 consumers。本质上kafka只支持Topic.每个consumer属于一个consumer group;反过来说,每个group中可以有多个consumer.发送到Topic的消息,只会被订阅此Topic的每个group中的一个consumer消费.可以认为一个group是一个"订阅"者,一个Topic中的每个partions,只会被一个"订阅者"中的一个consumer消费,不过一个 consumer可以消费多个partitions中的消息.kaka只能保证一个partition中的消息被某个consumer消费时,消息是顺序的.事实上,从Topic角度来说,消息仍不是有序的.

3.1.3 Broker

缓存代理,Kafka 集群中的一台或多台服务器统称为 broker。为了减少磁盘写入的次数,broker会将消息暂时buffer起来,当消息的个数(或尺寸)达到一定阀值时,再flush到磁盘,这样减少了磁盘IO调用的次数。

3.1.4 Topic

特指 Kafka 处理的消息源(feeds of messages)的不同分类。一个Topic可以认为是一类消息,每个topic将被分成多partition(区),每个partition在存储层面是append log文件。任何发布到此partition的消息都会被直接追加到log文件的尾部,每条消息在文件中的位置称为offset(偏移量),partition是以文件的形式存储在文件系统中。Logs文件根据broker中的配置要求,保留一定时间后删除来释放磁盘空间。

 

 

3.1.5 Partition

Topic 物理上的分组,一个 topic 可以分为多个 partition,每个 partition 是一个有序的队列。partition 中的每条消息都会被分配一个有序的 id(offset)。kafka基于文件存储.通过分区,可以将日志内容分散到多个server上,来避免文件尺寸达到单机磁盘的上限,每个partiton都会被当前server(kafka实例)保存;

可以将一个topic切分多任意多个partitions,来消息保存/消费的效率.越多的partitions意味着可以容纳更多的consumer,有效提升并发消费的能力.

3.1.6 Message

Message消息:是通信的基本单位,每个 producer 可以向一个 topic(主题)发布一些消息。Kafka中的Message是以topic为基本单位组织的,不同的topic之间是相互独立的。每个topic又可以分成几个不同的partition(每个topic有几个partition是在创建topic时指定的),每个partition存储一部分Message。

partition中的每条Message包含了以下三个属性:

offset                 对应类型:long

MessageSize   对应类型:int32

data          是message的具体内容

 

 

   每条消息在文件中的位置称为offset(偏移量)。offset 为一个long型数字,它是唯一标记一条消息。它唯一的标记一条消息。kafka并没有提供其他额外的索引机制来存储offset,因为在kafka中几 乎不允许对消息进行“随机读写”。

    Partition中的每条Message由offset来表示它在这个partition中的偏移量,这个offset不是该Message在partition数据文件中的实际存储位置,而是逻辑上一个值,它唯一确定了partition中的一条Message。因此,可以认为offset是partition中Message的id。

 

3.1.7 Consumer group

1. 允许consumer group(包含多个consumer,如一个集群同时消费)对一个topic进行消费,不同的consumer group之间独立订阅。

2. 为了对减小一个consumer group中不同consumer之间的分布式协调开销,指定partition为最小的并行消费单位,即一个group内的consumer只能消费不同的partition

 

kafka 以 topic 来进行消息管理,发布者发到某个 topic 的消息会被均匀的分布到多个 partition上,每个 topic 包含多个 partition,每个 part 对应一个逻辑 log,有多个 segment 组成。每个 segment 中存储多条消息,消息 id 由其逻辑位置决定,即从消息 id 可直接定位到消息的存储位置,避免 id 到位置的额外映射。每个 part 在内存中对应一个 index,记录每个 segment 中的第一条消息偏移。当某个 segment 上的消息条数达到配置值或消息发布时间超过阈值时,segment 上的消息会被 flush 到磁盘,只有 flush 到磁盘上的消息订阅者才能订阅到,segment 达到一定的大小后将不会再往该 segment 写数据,broker 会创建新的 segment。

 

四kafka安装部署

4.1 安装zookeeper

  • 下载
    • http://kafka.apache.org/downloads.html
  • 解压
    • tar -xzf kafka_2.11-0.10.0.0.tgz
  • 启动服务
    • 首先启动zookeeper服务
      • bin/zookeeper-server-start.sh config/zookeeper.properties
    • 启动Kafka
      • bin/kafka-server-start.sh config/server.properties
  • 创建topic
    • 创建一个"test"的topic,一个分区一个副本
    • bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic test
  • 查看主题
    • bin/kafka-topics.sh --list --zookeeper localhost:2181
  • 查看主题详情
    • bin/kafka-topics.sh --describe --zookeeper localhost:2181 --topic test
  • 删除主题
    • bin/kafka-topics.sh --zookeeper localhost:2181 --delete --topic test

4.2 Zookeeper 在 Kafka 中的作用

4.2.1、Broker注册

broker是分布式部署并且相互之间相互独立,但是需要有一个注册系统能够将整个集群中的Broker管理起来,此时就使用到了Zookeeper。在Zookeeper上会有一个专门用来进行Broker服务器列表记录的节点:

/brokers/ids

每个Broker在启动时,都会到Zookeeper上进行注册,即到/brokers/ids下创建属于自己的节点,如/brokers/ids/[0...N]。

Kafka使用了全局唯一的数字来指代每个Broker服务器,不同的Broker必须使用不同的Broker ID进行注册,创建完节点后,每个Broker就会将自己的IP地址和端口信息记录到该节点中去。其中,Broker创建的节点类型是临时节点,一旦Broker宕机,则对应的临时节点也会被自动删除。

4.2.2、Topic注册

在Kafka中,同一个Topic的消息会被分成多个分区并将其分布在多个Broker上,这些分区信息及与Broker的对应关系也都是由Zookeeper在维护,由专门的节点来记录,如:

/borkers/topics

Kafka中每个Topic都会以/brokers/topics/[topic]的形式被记录,如/brokers/topics/login和/brokers/topics/search等。Broker服务器启动后,会到对应Topic节点(/brokers/topics)上注册自己的Broker ID并写入针对该Topic的分区总数,如/brokers/topics/login/3->2,这个节点表示Broker ID为3的一个Broker服务器,对于"login"这个Topic的消息,提供了2个分区进行消息存储,同样,这个分区节点也是临时节点。

4.2.3、生产者负载均衡

由于同一个Topic消息会被分区并将其分布在多个Broker上,因此,生产者需要将消息合理地发送到这些分布式的Broker上那么如何实现生产者的负载均衡,Kafka支持传统的四层负载均衡,也支持Zookeeper方式实现负载均衡。

(1) 四层负载均衡,根据生产者的IP地址和端口来为其确定一个相关联的Broker。通常,一个生产者只会对应单个Broker,然后该生产者产生的消息都发往该Broker。这种方式逻辑简单,每个生产者不需要同其他系统建立额外的TCP连接,只需要和Broker维护单个TCP连接即可。但是,其无法做到真正的负载均衡,因为实际系统中的每个生产者产生的消息量及每个Broker的消息存储量都是不一样的,如果有些生产者产生的消息远多于其他生产者的话,那么会导致不同的Broker接收到的消息总数差异巨大,同时,生产者也无法实时感知到Broker的新增和删除。

(2) 使用Zookeeper进行负载均衡,由于每个Broker启动时,都会完成Broker注册过程,生产者会通过该节点的变化来动态地感知到Broker服务器列表的变更,这样就可以实现动态的负载均衡机制。

4.2.4、消费者负载均衡

与生产者类似,Kafka中的消费者同样需要进行负载均衡来实现多个消费者合理地从对应的Broker服务器上接收消息,每个消费者分组包含若干消费者,每条消息都只会发送给分组中的一个消费者不同的消费者分组消费自己特定的Topic下面的消息,互不干扰。

4.2.5、分区与消费者的关系

消费组 (Consumer Group)
consumer group 下有多个 Consumer(消费者)。
对于每个消费者组 (Consumer Group),Kafka都会为其分配一个全局唯一的Group ID,Group 内部的所有消费者共享该 ID。订阅的topic下的每个分区只能分配给某个 group 下的一个consumer(当然该分区还可以被分配给其他group)。
同时,Kafka为每个消费者分配一个Consumer ID,通常采用"Hostname:UUID"形式表示。

在Kafka中,规定了每个消息分区 只能被同组的一个消费者进行消费因此,需要在 Zookeeper 上记录 消息分区 与 Consumer 之间的关系,每个消费者一旦确定了对一个消息分区的消费权力,需要将其Consumer ID 写入到 Zookeeper 对应消息分区的临时节点上,例如:

/consumers/[group_id]/owners/[topic]/[broker_id-partition_id]

其中,[broker_id-partition_id]就是一个 消息分区 的标识,节点内容就是该 消息分区 上 消费者的Consumer ID。

4.2.6、消息消费进度Offset记录

在消费者对指定消息分区进行消息消费的过程中,需要定时地将分区消息的消费进度Offset记录到Zookeeper,以便在该消费者进行重启或者其他消费者重新接管该消息分区的消息消费后,能够从之前的进度开始继续进行消息消费。Offset在Zookeeper中由一个专门节点进行记录,其节点路径为:

/consumers/[group_id]/offsets/[topic]/[broker_id-partition_id]

节点内容就是Offset的值。

4.2.7、消费者注册

消费者服务器在初始化启动时加入消费者分组的步骤如下

注册到消费者分组。每个消费者服务器启动时,都会到Zookeeper的指定节点下创建一个属于自己的消费者节点,例如/consumers/[group_id]/ids/[consumer_id],完成节点创建后,消费者就会将自己订阅的Topic信息写入该临时节点。

消费者分组 中的 消费者 的变化注册监听。每个 消费者 都需要关注所属 消费者分组 中其他消费者服务器的变化情况,即对/consumers/[group_id]/ids节点注册子节点变化的Watcher监听,一旦发现消费者新增或减少,就触发消费者的负载均衡。

Broker服务器变化注册监听。消费者需要对/broker/ids/[0-N]中的节点进行监听,如果发现Broker服务器列表发生变化,那么就根据具体情况来决定是否需要进行消费者负载均衡。

进行消费者负载均衡。为了让同一个Topic下不同分区的消息尽量均衡地被多个 消费者 消费而进行 消费者 与 消息 分区分配的过程,通常,对于一个消费者分组,如果组内的消费者服务器发生变更或Broker服务器发生变更,会发出消费者负载均衡。

 

 

4.3 kafka的安装

  • 创建生产者 producer
    • bin/kafka-console-producer.sh --broker-list localhost:9092 --topic test
  • 创建消费者 consumer
    • bin/kafka-console-consumer.sh --zookeeper localhost:2181 --topic test --from-beginning
  • 参数使用帮组信息查看:
    • 生产者参数查看:bin/kafka-console-producer.sh
    • 消费者参数查看:bin/kafka-console-consumer.sh
    •  

       多broker部署

  • 修改config/service.properties
    • broker.id=0
    • port=9020
    • log.dirs=/tmp/kafka0-logs
  • 复制service.properties生成service1.properties
    • broker.id=1            #id不能一样
    • port=9040              #port不能一样
    • log.dirs=/tmp/kafka1-logs
  • 启动多个broker
    • bin/kafka-server-start.sh config/service.properties &
    • bin/kafka-server-start.sh config/service1.properties &
  • 创建主题
    • bin/kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 3 --partitions 1 --topic test

5 kafka的使用

5.1使用场景

5.1.1 消息队列

        比起大多数的消息系统来说,Kafka 有更好的吞吐量,内置的分区,冗余及容错性,这让 Kafka 成为了一个很好的大规模消息处理应用的解决方案。消息系统一般吞吐量相对较低,但是需要更小的端到端延时,并尝尝依赖于 Kafka 提供的强大的持久性保障。在这个领域,Kafka 足以媲美传统消息系统,如 ActiveMR 或 RabbitMQ。

 

5.1.2 行为跟踪

        Kafka 的另一个应用场景是跟踪用户浏览页面、搜索及其他行为,以发布-订阅的模式实时记录到对应的 topic 里。那么这些结果被订阅者拿到后,就可以做进一步的实时处理,或实时监控,或放到 hadoop/离线数据仓库里处理。

5.1.3. 元信息监控

 作为操作记录的监控模块来使用,即汇集记录一些操作信息,可以理解为运维性质的数据监控吧。

5.1.4. 日志收集

        日志收集方面,其实开源产品有很多,包括 Scribe、Apache Flume。很多人使用 Kafka 代替日志聚合(log aggregation)。日志聚合一般来说是从服务器上收集日志文件,然后放到一个集中的位置(文件服务器或 HDFS)进行处理。然而 Kafka 忽略掉文件的细节,将其更清晰地抽象成一个个日志或事件的消息流。这就让 Kafka 处理过程延迟更低,更容易支持多数据源和分布式数据处理。比起以日志为中心的系统比如 Scribe 或者 Flume 来说,Kafka 提供同样高效的性能和因为复制导致的更高的耐用性保证,以及更低的端到端延迟。

5.1.5. 流处理

        这个场景可能比较多,也很好理解。保存收集流数据,以提供之后对接的 Storm 或其他流式计算框架进行处理。很多用户会将那些从原始 topic 来的数据进行阶段性处理,汇总,扩充或者以其他的方式转换到新的 topic 下再继续后面的处理。例如一个文章推荐的处理流程,可能是先从 RSS 数据源中抓取文章的内容,然后将其丢入一个叫做“文章”的 topic 中;后续操作可能是需要对这个内容进行清理,比如回复正常数据或者删除重复数据,最后再将内容匹配的结果返还给用户。这就在一个独立的 topic 之外,产生了一系列的实时数据处理的流程。Strom 和 Samza 是非常著名的实现这种类型数据转换的框架。

5.1.6. 事件源

        事件源是一种应用程序设计的方式,该方式的状态转移被记录为按时间顺序排序的记录序列。Kafka 可以存储大量的日志数据,这使得它成为一个对这种方式的应用来说绝佳的后台。比如动态汇总(News feed)。

5.1.7. 持久性日志(commit log)

        Kafka 可以为一种外部的持久性日志的分布式系统提供服务。这种日志可以在节点间备份数据,并为故障节点数据回复提供一种重新同步的机制。Kafka 中日志压缩功能为这种用法提供了条件。在这种用法中,Kafka 类似于 Apache BookKeeper 项目。

5.2 springboot集成方式

5.2.1 pom

<!-- https://mvnrepository.com/artifact/org.springframework.kafka/spring-kafka -->
<dependency>
    <groupId>org.springframework.kafka</groupId>
    <artifactId>spring-kafka</artifactId>
</dependency>

<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka-clients</artifactId>
</dependency>

<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
</dependency>

 

5.2.2 producer



@Component

public class KakfaProducer {



    @Autowired

    private KafkaTemplate<String, String> kafkaTemplate;



    private Gson gson = new GsonBuilder().create();



    //发送消息方法

    public void send() {

        for(int i=0;i<5;i++){

            Message message = new Message();

            message.setId(System.currentTimeMillis());

            message.setMsg(UUID.randomUUID().toString()+ "---" +i);

            message.setSendTime(new Date());

            System.out.println("发送消息 ----->>>>>  message = {}"+gson.toJson(message));

            kafkaTemplate.send("hello",0,message.getId()+i+"", gson.toJson(message));

}

    }

}
 

5.2.3 consumer


@Component

public class KafkaConsumer {



    private static Logger logger = LoggerFactory.getLogger(KafkaConsumer.class);



    @KafkaListener(topics = {"hello"})

    public void listen(ConsumerRecord<?, ?> record) {

        Optional<?> kafkaMessage = Optional.ofNullable(record.value());

        if (kafkaMessage.isPresent()) {

            Object message = kafkaMessage.get();

            logger.info("----------------- record =" + record);

            logger.info("------------------ message =" + message);

        }

    }

}

如上代码是kafka的简单使用方式,但是在实际的工作中,可能我们碰到的场景并不是只监听一个kafka实例。比如我这里有个需求我要监听A团队的TopicA,和B团队的TopicB,那么我要怎么做呢,首页如上步奏所以集成对应的kafka maven包,其次添加两个不同的kafka实例,例如
 
spring:
  kafka:
    bootstrap-servers: dmq-test.yy.com:9099
    producer:
      batch-size: 16384
      key-serializer: org.apache.kafka.common.serialization.ByteArraySerializer
      value-serializer: org.apache.kafka.common.serialization.ByteArraySerializer
    consumer:
      group-id: zk-app
      auto-offset-reset: earliest
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
      value-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
    listener:
      missing-topics-fatal: false
      ack-mode: manual
  skafka:
    bootstrap-servers: dmq-shenzhen.yy.com:9099,dmq-shanghai.yy.com:9099
    producer:
      batch-size: 16384
      key-serializer: org.apache.kafka.common.serialization.ByteArraySerializer
      value-serializer: org.apache.kafka.common.serialization.ByteArraySerializer
    consumer:
      group-id: zk-app
      auto-offset-reset: earliest
      enable-auto-commit: false
      key-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
      value-deserializer: org.apache.kafka.common.serialization.ByteArrayDeserializer
    listener:
      missing-topics-fatal: false
      ack-mode: manual

topic-name:
  sub:
    zk-UserLogin: sub_zkUserLogin
    zk-NotifyYy: sub_LivingNotify
    zk-NotifyFriend: sub_jyCompereOnline

如上配置所示,我们应该配置两个kafka的实例,分别接入两个团队给的不同的域名,topic在同一个地方配置,然后添加kafka工程的配置类,例如

package com.yy.lite.zk.app.config;

import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.ByteArrayDeserializer;
import org.apache.kafka.common.serialization.ByteArraySerializer;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.kafka.annotation.EnableKafka;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.kafka.listener.ContainerProperties;

import java.util.HashMap;
import java.util.Map;

@Configuration
@EnableKafka
public class KafkaConfig {
    @Value("${spring.kafka.bootstrap-servers}")
    private String sbserver;

    @Value("${spring.kafka.consumer.group-id}")
    private String sbgroupid;

    @Value("${spring.skafka.bootstrap-servers}")
    private String saservers;

    @Value("${spring.skafka.consumer.group-id}")
    private String sagroupid;

    @Bean
    @Primary
//理解为默认优先选择当前容器下的消费者工厂
    KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>> kafkaListenerContainerFactory() {
        ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactory());
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
        factory.setConcurrency(3);
        factory.getContainerProperties().setPollTimeout(3000);
        return factory;
    }

    @Bean//第一个消费者工厂的bean
    public ConsumerFactory<Integer, String> consumerFactory() {
        return new DefaultKafkaConsumerFactory<>(consumerConfigs());
    }

    @Bean
    public Map<String, Object> consumerConfigs() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, sbserver);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, sbgroupid);
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);
        props.put("max.poll.records", 10);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
        return props;
    }

    /**
     * 连接第二个kafka集群的配置
     */

    @Bean
    KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<Integer, String>> kafkaListenerContainerFactoryTwoSchedule() {
        ConcurrentKafkaListenerContainerFactory<Integer, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
        factory.setConsumerFactory(consumerFactoryTwoSchedule());
        factory.getContainerProperties().setAckMode(ContainerProperties.AckMode.MANUAL);
        factory.setConcurrency(3);
        factory.getContainerProperties().setPollTimeout(3000);
        return factory;
    }

    @Bean
    public ConsumerFactory<Integer, String> consumerFactoryTwoSchedule() {
        return new DefaultKafkaConsumerFactory<>(consumerConfigsTwoSchedule());
    }

    /**
     *
     *连接第二个集群的消费者配置
     */
    @Bean
    public Map<String, Object> consumerConfigsTwoSchedule() {
        Map<String, Object> props = new HashMap<>();
        props.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, saservers);
        props.put(ConsumerConfig.GROUP_ID_CONFIG, sagroupid);
        props.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG,false);
        props.put("max.poll.records", 10);
        props.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG,"earliest");
        props.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
        props.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, ByteArrayDeserializer.class);
        return props;
    }
}

正如上代码所示,监听两个kafka实例,那么如何定义消费者呢。接下来我们看一下消费者的定义

    @KafkaListener(topics = "${topic-name.sub.zk-NotifyFriend}", groupId = "zk-app" ,containerFactory = "kafkaListenerContainerFactory")
    public void handleBroadCastStart(ConsumerRecord<String, byte[]> consumerRecord,Acknowledgment ack){
        byte[] value = consumerRecord.value();
        StringDeserializer stringDeserializer=new StringDeserializer();
        String deserialize = stringDeserializer.deserialize(null, value);
        log.info("handleBroadCastStart an deserialize video zk-NotifyFriend: {}",deserialize);
        try {
            PStartLiveNotify pStartLiveNotify = broasdCastNotifyService.bulidPStartLiveNotify(deserialize);
            if(null==pStartLiveNotify){
                ack.acknowledge();
                return ;
            }
            Uint uid = pStartLiveNotify.getUid();
            if(UidUtils.isAnonymous(uid.longValue())){
                ack.acknowledge();
                return ;
            }
            streamerInfoCacheService.refreshStreamerInfo(pStartLiveNotify);
            broasdCastNotifyService.nofitys(pStartLiveNotify);
            ReportUtil.reportMetrics(URL.valueOf("LivingNotifyFriendListener/handleBroadCastStart"), ProtocolType.HTTP,
                    "zk-app",
                    "handleBroadCastStart",
                    "1.0.1",
                    0,"200",true,"success"
            );
            ack.acknowledge();
        }catch (Exception e){
            log.error("handleBroadCastStart zk-NotifyFriend e={}",e);
            ReportUtil.reportMetrics(URL.valueOf("LivingNotifyFriendListener/handleBroadCastStart"), ProtocolType.HTTP,
                    "zk-app",
                    "handleBroadCastStart",
                    "1.0.1",
                    0,"500",true,"failed"
            );
        }

    }

}
    @KafkaListener(topics = "${topic-name.sub.zk-NotifyYy}", groupId = "zk-app",containerFactory = "kafkaListenerContainerFactoryTwoSchedule")
    public void handleBroadCastStart(ConsumerRecord<String, byte[]> consumerRecord, Acknowledgment ack){
        byte[] value = consumerRecord.value();
        ByteBufferDeserializer byteBufferDeserializer=new ByteBufferDeserializer();
        ByteBuffer deserialize = byteBufferDeserializer.deserialize(null, value);
        try {
            Unpack unpack = new Unpack(deserialize);
            long longVal = unpack.popUlong().longValue();
            int intVal = unpack.popUInt().intValue();
            if(intVal!=Constant.STARTURI){
                log.info("handleBroadCastStart intVal!=Constant.STARTURI intVal={}",intVal);
                ack.acknowledge();
                return ;
            }
            Unpack unpac = new Unpack(unpack.popVarbin());
            PStartLiveNotify pStartLiveNotify=new PStartLiveNotify();
            unpac.popMarshallable(pStartLiveNotify);
            log.info("handleBroadCastStart zk-NotifyYy pStartLiveNotify={} ",pStartLiveNotify.toString());
            Uint uid = pStartLiveNotify.getUid();
            if(UidUtils.isAnonymous(uid.longValue())){
                ack.acknowledge();
                return ;
            }
            broasdCastNotifyService.nofitys(pStartLiveNotify);
            ack.acknowledge();
            ReportUtil.reportMetrics(URL.valueOf("LivingNotifyListener/handleBroadCastStart"), ProtocolType.HTTP,
                    "zk-app",
                    "handleBroadCastStart",
                    "1.0.1",
                    0,"200",true,"success"
            );
        }catch (Exception e){
            log.error("handleBroadCastStart zk-NotifyYy e={}",e);
            ReportUtil.reportMetrics(URL.valueOf("LivingNotifyListener/handleBroadCastStart"), ProtocolType.HTTP,
                    "zk-app",
                    "handleBroadCastStart",
                    "1.0.1",
                    0,"500",true,"failed"
            );
        }
    }


}

如上两个监听者所示,可以看到它通过 containerFactory配置来决定,应该使用哪个kafka实例的哪个topic,这样如果是对接不同的团队消费kafka消息的话,就可以用如上所示的工厂配置。还有就是可以配置kafka消息是否需要手动确认。有的时候不配置手动确认机制,就容易导致消息丢失。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值