JavaEE 企业级分布式高级架构师(十二)Kafka学习笔记(1)

概述篇

为什么有消息系统

异步处理

  • 场景说明:用户注册后,需要发注册邮件和注册短信。传统的做法有两种:串行的方式和并行方式。
  • 串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信。以上三个任务全部完成后,返回给客户。

在这里插入图片描述

  • 并行方式:将注册信息写入数据库成功后,发送注册邮件的同时,发送注册短信。以上三个任务完成后,返回给客户端。与串行的差别是,并行的方式可以提高处理的时间。

在这里插入图片描述

  • 假设三个业务节点每个使用50毫秒钟,不考虑网络等其他开销,则串行方式的时间是150毫秒,并行的时间可能是100毫秒。
  • 如以上案例描述,传统的方式系统的性能(并发量,吞吐量,响应时间)会有瓶颈。如何解决这个问题呢?

在这里插入图片描述

  • 引入消息队列:用户的响应时间相当于是注册信息写入数据库的时间,也就是50毫秒。注册邮件,发送短信写入消息队列后,直接返回,因为写入消息队列的速度很快,基本可以忽略,因此用户的响应时间可能是50毫秒。因此架构改变后,系统的吞吐量提高到每秒20QPS。比串行提高了3倍,比并行提高了。

解耦

  • 场景说明:用户下单后,订单系统需要通知库存系统。传统的做法是,订单系统直接调用库存系统的接口。如下图:

在这里插入图片描述

  • 传统模式的缺点:假如库存系统无法访问,则订单减库存将失败,从而导致订单失败,订单系统与库存系统耦合。
  • 如何解决以上问题呢?引入应用消息队列后的方案,如下图:

在这里插入图片描述

  • 订单系统:用户下单后,订单系统完成持久化处理,将消息写入消息队列,返回用户订单下单成功。
  • 库存系统:订阅下单的消息,采用拉/推的方式,获取下单信息,库存系统根据下单信息,进行库存操作。
  • 假如:在下单时库存系统不能正常使用。也不影响正常下单,因为下单后,订单系统写入消息队列就不再关心其他的后续操作了。实现订单系统与库存系统的应用解耦。

流量消峰

  • 流量削锋也是消息队列中的常用场景,一般在秒杀或团抢活动中使用广泛!
  • 应用场景:秒杀活动,一般会因为流量过大,导致流量暴增,应用挂掉。为解决这个问题,一般需要在应用前端加入消息队列。
  • 可以控制活动的人数,可以缓解短时间内高流量压垮应用。用户的请求,服务器接收后,首先写入消息队列。假如消息队列⻓度超过最大数量,则直接抛弃用户请求或跳转到错误⻚面。
  • 秒杀业务根据消息队列中的请求信息,再做后续处理。

在这里插入图片描述

消息队列其它好处

  • 冗余:消息队列把数据进行持久化直到它们已经被完全处理,通过这一方式规避了数据丢失⻛险。许多消息队列所采用的"插入-获取-删除"范式中,在把一个消息从队列中删除之前,需要你的处理系统明确的指出该消息已经被处理完毕,从而确保你的数据被安全的保存直到你使用完毕。
  • 扩展性:因为消息队列解耦了你的处理过程,所以增大消息入队和处理的频率是很容易的,只要另外增加处理过程即可。
  • 灵活性 & 峰值处理能力:在访问量剧增的情况下,应用仍然需要继续发挥作用,但是这样的突发流量并不常⻅。如果为以能处理这类峰值访问为标准来投入资源随时待命无疑是巨大的浪费。使用消息队列能够使关键组件顶住突发的访问压力,而不会因为突发的超负荷的请求而完全崩溃。
  • 可恢复性:系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。
  • 顺序保证:在大多使用场景下,数据处理的顺序都很重要。大部分消息队列本来就是排序的,并且能保证数据会按照特定的顺序来处理。(Kafka 保证一个 Partition 内的消息的有序性)
  • 缓冲:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。

Kafka简介

  • Apache Kafka 是一个快速、可扩展的、高吞吐的、可容错的分布式“发布-订阅”消息系统, 使用 Scala 与 Java 语言编写,能够将消息从一个端点传递到另一个端点,较之传统的消息中 间件(例如 ActiveMQ、RabbitMQ),Kafka 具有高吞吐量、内置分区、支持消息副本和高容 错的特性,非常适合大规模消息处理应用程序。
  • Kafka 官网: http://kafka.apache.org/。

Kafka特性

  • 高吞吐、低延迟:kakfa 最大的特点就是收发消息非常快,kafka 每秒可以处理几十万条消息,它的最低延迟只有几毫秒。
  • 高伸缩性:每个主题(topic) 包含多个分区(partition),主题中的分区可以分布在不同的主机(broker)中。 持久性、可靠性方面,Kafka 能够允许数据的持久化存储,消息被持久化到磁盘,并支持数据备份防止数据丢失。
  • 容错性:允许集群中的节点失败,某个节点宕机,Kafka 集群能够正常工作。
  • 高并发:有助于控制和优化数据流经过系统的速度,解决生产消息和消费消息的处理速度不一致的情况。支持数千个客户端同时读写。

kafka系统架构

在这里插入图片描述

  • 如上图所示,一个典型的 Kafka 集群中包含若干 Producer(可以是 web 前端产生的 Page View, 或者是服务器日志,系统 CPU、Memory 等),若干 broker(Kafka 支持水平扩展,一般 broker 数量 越多,集群吞吐率越高),若干 Consumer Group,以及一个 Zookeeper 集群。Kafka 通过 Zookeeper 管理集群配置,选举 leader,以及在 Consumer Group 发生变化时进行 rebalance。 Producer 使用 push 模式将消息发布到 broker,Consumer 使用 pull 模式从 broker 订阅并消费消息。

在这里插入图片描述

应用场景

消息系统Messaging

Web站点活动追踪Website Activity Tracking

  • Kafka的起源是应用需要创建一个追踪用户行为的出入口。用户行为最终体现在网站的PV、搜索使用或者用户进行的其他操作方面,不同的用户行为数据会被发送到不同的Topic。这个出入口要能为不同的使用场景提供订阅功能,用户行为数据的使用场景包括实时计算、实时监控,或者将数据导入hadoop等类似离线数据存储系统来进行离线计算和统计报表。
  • 用户行为数据的量通常是非常大的,例如,一个用户可能就会产生许多page view的数据。
  • 用户在网站的不同活动消息发布到不同的主题中心,然后可以对这些消息进行实时监测实时处理。当然,也可加载到 Hadoop 或离线处理数据仓库,对用户进行画像。像淘宝、京东这些大型的电商平台,用户的所有活动都是要进行追踪的。

数据监控Metrics

  • Kafka通常也会用于运行数据的检测。这包括从分布式系统中搜集运行统计数据,并集中对外提供一个访问接口。

日志聚合Log Aggregation

  • 很多人把kafka当做其他日志收集方案的一个替代品。所谓日志收集,就是将不同物理机器上运行的应用所产生的日志文件放到一个公共的地方来进行处理,例如文件服务器或者HDFS。kafka 对剥离了文件的概念,并提供了一种更简洁的日志文件的抽象,即将日志文件中的数据看做是 message stream。这种方式让低延迟处理成为可能,并且更好的支持了多数据源,多消费者。通过与以日志处理为主的系统如flume、Scribe进行对比,kafka可以提供同等的性能,而备份机制可以提供更强的一致性保证,多partition提供了更低的延迟。

流处理Stream Processiong

事件源Event Sourcing

  • 事件源是一种应用设计方式,当某些状态改变时,kafka按照变化的时间顺序将这些变化记录下来。应用通过消费事件信息从而确定不同的行为方式。

提交日志Commit Log

  • Kafka可以用作分布式系统的一种外部提交日志。 该日志有助于在节点之间复制数据,并充当故障节点恢复其数据的重新同步机制。

kafka高吞吐率实现

  • kafka与其它MQ相比,其最大的特点就是高吞吐率。为了增加存储能力,kafka将所有的消息都写入到低速大容的硬盘。按理说,这将导致性能损失,但实际上,kafka仍可保持超高的吞吐率,性能并未受到影响。其主要采用了如下的方式实现高吞吐率:
  • 顺序读写:机械硬盘不需要摆动磁臂和切换磁道,读写效率提高;
  • 零拷贝:减少了用户空间和内核空间的切换;
  • 批量发送:先把消息放到内存缓存,然后批量发送(批量大小可以设置);
  • 消息压缩:producer对消息进行压缩,消耗的是消费者、生产者的CPU,对kafka主机没什么影响。
  • Page Cache(⻚缓存):为了优化读写性能,Kafka利用了操作系统本身的Page Cache,就是利用操作系统自身的内存而不是JVM空间内存。这样做的好处有:避免Object消耗,如果是使用Java堆,Java对象的内存消耗比较大,通常是所存储数据 的两倍甚至更多。避免GC问题,随着JVM中数据不断增多,垃圾回收将会变得复杂与缓慢,使用系统缓存就不会存在GC问题。

ZooKeeper作用

  • ZooKeeper 是一种常用的分布式协调服务Kafka 用到了它的数据发布和订阅、命名服务、分布式协调/通知、集群管理、Master 选举等功能。
  • 它使用的数据模型类似于文件系统的树形结构,根目录也是以“/”开始。该结构上的每个节点被称为 znode,用来保存一些元数据协调信息。
  • ZooKeeper 赋予客户端监控 znode 变更的能力,即所谓的 Watch 通知功能。一旦 znode 节点被创建、删除,子节点数量发生变化,抑或是 znode 所存的数据本身变更,ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的方式显式通知客户端。
  • 依托于这些功能,ZooKeeper 常被用来实现集群成员管理、分布式锁、领导者选举等功能。Kafka 控制器大量使用 Watch 功能实现对集群的协调管理。

在这里插入图片描述

  • ZooKeeper 在内存中用一棵树存储数据,树中的每个节点用于存储数据,数据节点分为永久的和临时的两种,只要创建临时节点的客户端下线了临时节点就会被删除,永久节点不会
  • 客户端可以监听指定的数据节点,当特定的事件发生时,ZooKeeper 会通知客户端,比如监听节点的删除、创建或修改。

Kafka基础操作

集群搭建

在生产环境中为了防止单点问题,Kafka 都是以集群方式出现的。下面要搭建一个 Kafka 集群,包含三个 Kafka 主机,即三个 Broker。

  • 机器配置:
# 三台 kafka 主机
192.168.254.128
192.168.254.130
192.168.254.132
# zookeeper 主机
192.168.254.120

kafka的下载

https://www.apache.org/dyn/closer.cgi?path=/kafka/2.2.0/kafka_2.11-2.2.0.tgz
在这里插入图片描述

安装并配置第一台主机

  • 下载安装:
[root@centos-host tools]# wget http://mirror.bit.edu.cn/apache/kafka/2.2.0/kafka_2.11-2.2.0.tgz
# 解压安装
[root@centos-host tools]# tar -zxvf kafka_2.11-2.2.0.tgz -C /usr/apps/
# 创建软连接
[root@centos-host apps]# ln -s kafka_2.11-2.2.0/ kafka

在这里插入图片描述

  • 修改配置文件:在 kafka 安装目录下有一个 config/server.properties 文件,修改该文件。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

安装另外两台kafka

  • 按照第一台 kafka 主机的步骤安装,需要修改 server.properties 中的 broker.id、listeners、advertised.listeners 与 zookeeper.connect。
    在这里插入图片描述
    在这里插入图片描述

命令行操作

kafka的启动与停止

  • 启动 zookeeper:
[root@centos-host kafka]# zkServer.sh start
  • 启动kafka:在命令后添加-daemon 参数,可以使 kafka 以守护进程方式启动,即不占用窗口。
[root@centos-host kafka]# bin/kafka-server-start.sh -daemon config/server.properties
  • 停止kafka:
[root@centos-host kafka]# bin/kafka-server-stop.sh

topic相关操作

  • 创建topic
[root@centos-host kafka]# bin/kafka-topics.sh --create --bootstrap-server 192.168.254.128:9092 --replication-factor 1 --partitions 1 --topic test
  • 查看topic
[root@centos-host kafka]# bin/kafka-topics.sh --list --bootstrap-server 192.168.254.128:9092
  • 发送消息:该命令会创建一个生产者,然后由其生产消息。
[root@centos-host kafka]# bin/kafka-console-producer.sh --broker-list 192.168.254.128:9092 --topic test
  • 消费消息
[root@centos-host kafka]# bin/kafka-console-consumer.sh --bootstrap-server 192.168.254.128:9092 --topic test --from-beginning
  • 删除topic
[root@centos-host kafka]# bin/kafka-topics.sh --delete --bootstrap-server 192.168.254.128:9092 --topic test

日志查看

这里说的日志不是 Kafka 的启动日志,启动日志在 Kafka 安装目录下的 logs/server.log 中。消息在磁盘上都是以日志的形式保存的。我们这里说的日志是存放在/tmp/kafka_logs 目录中的消息日志,即 partition 与 segment。

查看分区与备份

1个分区1个备份

我们前面创建的 test 主题是 1 个分区 1 个备份。

在这里插入图片描述

3个分区1个备份

再次创建一个主题,命名为 one,创建三个分区,但仍为一个备份。 依次查看三台broker,可以看到每台 broker 中都有一个 one 主题的分区。

[root@centos-host kafka]# bin/kafka-topics.sh --create --bootstrap-server 192.168.254.128:9092 --replication-factor 1 --partitions 3 --topic one

在这里插入图片描述

3个分区3个备份
  • 再次创建一个主题,命名为 two,创建三个分区,三个备份。依次查看三台 broker,可 以看到每台 broker 中都有三份 two 主题的分区。
[root@centos-host kafka]# bin/kafka-topics.sh --create --bootstrap-server 192.168.254.128:9092 --replication-factor 3 --partitions 3 --topic two

在这里插入图片描述

查看段segment

segment文件
  • segment是一个逻辑概念,其由两类物理文件组成,分别为“.index”文件和“.log”文件。“.log”文件中存放的是消息,而“.index”文件中存放的是“.log”文件中消息的索引。
  • 如何进行消息查找:假设当前 kafka 中某 topic 共有 865235498 条消息,现在要查找第 170213 条消息的内容。使用二分法查找在文件名中定位到 0…170210.log 文件,使用 170213 - 170210 = 3。
    在这里插入图片描述
查看segment
  • 对于 segment 中的 log 文件,不能直接通过 cat 命令查找其内容,而是通过 kafka 自带的一个工具查看。
[root@centos-host kafka]# bin/kafka-run-class.sh kafka.tools.DumpLogSegments --files /tmp/kafka-logs/test-0/00000000000000000000.log --print-data-log

Kafka API

使用Kafka原生API

首先在命令行创建一个名称为 cities 的主题,并创建该主题的订阅者。

[root@centos-host kafka]# bin/kafka-topics.sh --create --bootstrap-server 192.168.254.128:9092 --replication-factor 3 --partitions 3 --topic cities
创建工程

创建一个 Maven 的 Java 工程,命名为 kafka-demo。创建时无需导入依赖。为了简单, 后面的发布者与消费者均创建在该工程中。

导入依赖
<!-- kafka依赖 -->
<dependency>
    <groupId>org.apache.kafka</groupId>
    <artifactId>kafka_2.12</artifactId>
    <version>1.1.1</version>
</dependency>
创建发布者SimpleProducer
  • 创建发布者SimpleProducer
public class SimpleProducer {
    // 第一个泛型为key的类型,第二个泛型为消息本身的类型
    private KafkaProducer<Integer, String> producer;

    public SimpleProducer(){
        Properties properties = new Properties();
        // 指定kafka集群
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
//        properties.put("bootstrap.servers",
                "192.168.254.128:9092,192.168.254.130:9092,192.168.254.132:9092");
        // 指定key-value的序列化器
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
//        properties.put("key.serializer",
                "org.apache.kafka.common.serialization.IntegerSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
//        properties.put("value.serializer",
                "org.apache.kafka.common.serialization.StringSerializer");

        this.producer = new KafkaProducer<Integer, String>(properties);
    }

    public void sendMsg(){
        // 创建记录(消息)
        // 指定主题及消息本身
        // ProducerRecord<Integer, String> record =
        //                        new ProducerRecord<>("cities", "shanghai");
        // 指定主题、key,及消息本身
        // ProducerRecord<Integer, String> record =
        //                        new ProducerRecord<>("cities", 1, "shanghai");
        // 指定主题、要写入的patition、key,及消息本身
        ProducerRecord<Integer, String> record = new ProducerRecord<>("cities", 1, 1, "shanghai");

        // 发布消息,其返回值为Future对象,表示其发送过程为异步,不过这里不使用该返回结果
        // Future<RecordMetadata> future = producer.send(record);
        producer.send(record);
    }
}
  • 创建测试类SimpleProducerTest
public class SimpleProducerTest {
    public static void main(String[] args) throws IOException {
        SimpleProducer producer = new SimpleProducer();
        producer.sendMsg();
        System.in.read();
    }
}
创建发布者CallbackProducer

前面的方式在消息发送成功后,代码中没有任何提示,这里可以使用回调方式,即发送成功后,会触发回调方法的执行。

  • 创建发布者类CallbackProducer:复制 SimpleProducer 类,仅修改 sendMsg()方法。
    在这里插入图片描述
  • 创建测试类 CallbackProducerTest:和 SimpleProducerTest 类似
批量发送消息
  • 复制前面的发布者类,在其基础上进行修改发布者类
public class BatchProducer {
    // 第一个泛型为key的类型,第二个泛型为消息本身的类型
    private KafkaProducer<Integer, String> producer;

    public BatchProducer(){
        Properties properties = new Properties();
        // 指定kafka集群
        properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,
//        properties.put("bootstrap.servers",
                "192.168.254.128:9092,192.168.254.130:9092,192.168.254.132:9092");
        // 指定key-value的序列化器
        properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,
//        properties.put("key.serializer",
                "org.apache.kafka.common.serialization.IntegerSerializer");
        properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,
//        properties.put("value.serializer",
                "org.apache.kafka.common.serialization.StringSerializer");
        // 指定要批量发送的消息个数,默认16k
        properties.put(ProducerConfig.BATCH_SIZE_CONFIG,
//        properties.put("batch.size",
                16384);     // 16K
        // 指定积攒消息的时长,默认值为0ms
        properties.put(ProducerConfig.LINGER_MS_CONFIG,
//        properties.put("linger.ms", 50);        // 50ms
        this.producer = new KafkaProducer<Integer, String>(properties);
    }

    public void sendMsg(){
        for(int i=0; i<50; i++){
            ProducerRecord<Integer, String> record =
                    new ProducerRecord<>("cities", 0, i*10, "city-" + i*100);
            producer.send(record, new Callback() {
                // RecordMetadata,消息元数据,即主题、消息的key、消息本身等的封装对象
                @Override
                public void onCompletion(RecordMetadata metadata, Exception e) {
                    System.out.print("partition = " + metadata.partition());
                    System.out.print(",topic = " + metadata.topic());
                    System.out.println(",offset = " + metadata.offset());
                }
            });
        }
    }
}
  • 创建测试类 BatchProducerTest:和 SimpleProducerTest 类似
消费者组
  • 创建消费者类SimpleConsumer
public class SimpleConsumer extends ShutdownableThread {
    private KafkaConsumer<Integer, String> consumer;

    public SimpleConsumer(){
        // 第一个参数:为当前消费者指定一个名称,随意
        // 第二个参数:消费过程是否会被中断
        super("KafkaConsumerTest", false);

        Properties properties = new Properties();
        String brokers = "192.168.254.128:9092,192.168.254.130:9092,192.168.254.132:9092";
        // 指定集群
        properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokers);
        // 指定消费者组名称
        properties.put(ConsumerConfig.GROUP_ID_CONFIG, "cityGroup1");
        // 指定自动提交
        properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true");
        // 设置自动提交的最晚时间间隔
        properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, "1000");
        // 指定消费者被认为已经挂了的时限
        properties.put(ConsumerConfig.SESSION_TIMEOUT_MS_CONFIG, "30000");
        // 指定消费者发送心跳的时间间隔
        properties.put(ConsumerConfig.HEARTBEAT_INTERVAL_MS_CONFIG, "10000");
        // 若消费者没有指定 offset 的初始值或指定的 offset 不存在,则从这里获取 offset
        // earliest:从第一条消息开始消费
        // lastest:从最后一条消息开始消费
        properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest");
        // 指定key与value的反序列化器
        properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.IntegerDeserializer");
        properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG,
                "org.apache.kafka.common.serialization.StringDeserializer");
        this.consumer = new KafkaConsumer<Integer, String>(properties);
    }
    @Override
    public void doWork() {
        // 指定要消费的主题,可以指定多个主题
        consumer.subscribe(Collections.singletonList("cities"));
        // poll()的参数是当前消费者等待消费的最长时间,在指定时间内没有等到消费,则返回空
        ConsumerRecords<Integer, String> records = consumer.poll(1000);
        for(ConsumerRecord record : records){
            System.out.print("topic = " + record.topic());
            System.out.print(" partition = " + record.partition());
            System.out.print(" key = " + record.key());
            System.out.println(" value = " + record.value());
        }
    }
}
  • 创建测试类SimpleConsumerTest
public class SimpleConsumerTest {
    public static void main(String[] args) {
        SimpleConsumer consumer = new SimpleConsumer();
        consumer.start();
    }
}
手动提交
  • 自动提交的问题:前面的消费者都是以自动提交 offset 的方式对 broker 中的消息进行消费的,但自动提交 可能会出现消息重复消费的情况。所以在生产环境下,很多时候需要对 offset 进行手动提交, 以解决重复消费的问题。
  • 手动提交分类:手动提交又可以划分为同步提交异步提交同异步联合提交。这些提交方式仅仅是 doWork()方法不相同,其构造器是相同的。所以下面首先在前面消费者类的基础上进行构造 器的修改,然后再分别实现三种不同的提交方式。
消费者同步手动提交
  • 原理:同步提交方式是,消费者向 broker 提交 offset 后等待 broker 成功响应。若没有收到响 应,则会重新提交,直到获取到响应。而在这个等待过程中,消费者是阻塞的。其严重影响 了消费者的吞吐量。
  • 创建消费者类SyncManualConsumer:直接复制前面的 SimpleConsumer,在其基础上进行修改。
  • 修改构造器:
    在这里插入图片描述
  • 修改doWork()方法:
    在这里插入图片描述
  • 创建测试类SyncManualConsumerTest:和 SimpleConsumerTest 类似
消费者异步手动提交
  • 原理:手动同步提交方式需要等待 broker 的成功响应,效率太低,影响消费者的吞吐量。异步提交方式是,消费者向 broker 提交 offset 后不用等待成功响应,所以其增加了消费者的吞吐量。
  • 创建消费者类 AsyncManualConsumer:复制前面的 SyncManualConsumer 类,在其基础上进行修改。
    在这里插入图片描述
  • 创建测试类AsyncManualConsumerTest:和 SimpleConsumerTest 类似
消费者同异步手动提交
  • 原理:同异步提交,即同步提交与异步提交组合使用。一般情况下,若偶尔出现提交失败,其 也不会影响消费者的消费。因为后续提交最终会将这次提交失败的 offset 给提交了。但异步提交会产生重复消费,为了防止重复消费,可以将同步提交与异常提交联合使用。
  • 创建消费者类 SyncAsyncManualConsumer:复制前面的 AsyncManualConsumer 类,在其基础上进行修改。
    在这里插入图片描述
  • 创建测试类SyncAsyncManualConsumerTest:和 SimpleConsumerTest 类似

Spring Boot kafka

为了简单,以下代码是将消息发布者与订阅者定义到了一个工程中的。

创建工程

创建一个 Spring Boot 工程,添加 Web、Kafka 依赖。

定义发布者
  • 修改配置文件
# 自定义属性
kafka:
  topic: cities

# 配置Kafka
spring:
  kafka:
    bootstrap-servers: 192.168.254.128:9092,192.168.254.130:9092,192.168.254.132:9092
    producer:   # 配置生产者
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
  • 定义发布者处理器:Spring Kafka 通过 KafkaTemplate 完成消息的发布。
@RestController("/msg")
public class SimpleProducer {
    @Autowired
    private KafkaTemplate<String, String> template;

    // 从配置文件读取自定义属性
    @Value("${kafka.topic}")
    private String topic;

    // 由于是提交数据,所以使用Post方式
    @PostMapping("send")
    public String sendMsg(@RequestParam("message")String message){
        template.send(topic, message);
        return "send success";
    }
}
定义消费者
  • 修改配置文件:在配置文件中添加如下内容。注意,Spring 中要求必须为消费者指定组。
# 自定义属性
kafka:
  topic: cities

# 配置Kafka
spring:
  kafka:
    bootstrap-servers: 192.168.254.128:9092,192.168.254.130:9092,192.168.254.132:9092
    producer:   # 配置生产者
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer

    consumer:   # 配置消费者
      group-id: group0  # 消费者组
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
  • 定义消费者:**Spring Kafka 是通过 KafkaListener 监听方式来完成消息订阅与接收的。**当监听到有指定
    主题的消息时,就会触发@KafkaListener 注解所标注的方法的执行。
@Component
public class SimpleConsumer {
    @KafkaListener(topics = "${kafka.topic}")
    public void recvMsg(String message){
        System.out.println("Kafka消费者接受到消息:" + message);
    }
}

监控工具

zookeeper-dev-ZooInspector

java -jar zookeeper-dev-ZooInspector.jar

在这里插入图片描述

Kafka Tool

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

讲文明的喜羊羊拒绝pua

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

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

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

打赏作者

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

抵扣说明:

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

余额充值