Kafka


基本概念

为什么要使用消息队列?

  • 应用解耦。各系统间耦合性太强,彼此直接关联在一起;若业务变更,会造成较大的维护成本。使用MQ作为消息中间件,子系统订阅MQ,使整体系统解耦,从而降低维护成本;
  • 流量削峰。一般指业务数据太大,超过数据库的写入极限,若直接写入库中,会导致系统宕机;将业务消息传递到MQ,数据库按合适的速度消费MQ,即可避免流量过大导致系统出现故障的问题;
  • 异步处理。将原本串行运算的子任务,使之同时接受MQ的消息,并做并行计算,从而加快系统运行速度;

Kafka其实就是个中间件,一些程序向Kafka的某些topic发送数据,被称之为生产者Producer;一些程序读取kafka特定topic上的数据,被称之为Consumer。从这个意义上说,其实Kafka与数据库的功能有很多相似的地方。

常用资料如下,最好的学习方式当然是在使用中学习:

Kafka中的专有名词:

  • Kfaka:由领英开源的一种可扩展、低延迟、高吞吐量的分布式消息系统,目的是作为系统解耦、缓冲、异步的工具。是大数据时代应用最为广泛的消息中间件;
  • Topic:一种存储消息的逻辑概念,可以认为是一个消息集合。物理上来说,不同topic的消息是分开存储的;按照业务逻辑,将消息存储在不同的区域,这些区域被称之为topic。例如城市topic、日期topic等;
  • Producer:生产者,将外部系统的数据发送到Kafka的指定topic上;
  • Consumer:消费者,使用Kafka的指定topic上的数据;
  • Broker:一台Kafka Server就是一个Broker节点,一个Kafka集群由多个Broker组成,一个Broker存储多个topics;
  • Partition:一种存储消息的物理概念,对于用户来说是不可见的。每个Topic可以划分为至少一个分区,每个分区中的消息是不同的;Partition存在的意义,是便于Kafka集群的水平扩展:当数据量很大时,单个broker会成为性能的瓶颈,因此将topic的数据分别存储在多个broker的多个partition中,可以做大吞吐量的增大。如果分区规则十分合理,那么消息会均匀的存储在多个分区上面,从而实现Kafka的负载均衡;1 个消费组包含 1 个 consumer,消费多个 partition 的情况,消费完一个 partition 会消费另一个 partition。offset 只是标记 partition 中数据顺序,不同 partition 间的 offset 没有关系;
  • Offset:新增消息到每个主题的partition当中时,每条消息会以append log的方式追加的写入分区,同时会增加一个long类型的数字Offset,它是消息在该分区内部的唯一编号,从而保证消息在分区内部的有序。各个Consumer控制和设置其在该partition下消费到offset位置,这样下次可以以该offset位置开始进行消费;
  • Replicas:副本,每个topic创建时都会生成副本,默认为1;集群节点大于3时,一般设置为3;其次为了保证高可用,每个分区都会有一定数量的副本replicas。这样如果有部分服务器不可用,副本所在的服务器就会接替上来,保证应用的持续性;
  • Leader:假如有N个副本。其中一个replica为leader,其他都为follower,leader处理partition的所有读写请求,于此同时,follower会被动定期的去复制leader上的数据;
  • Follower:
  • Isr:leader会追踪和维护ISR中所有follower的滞后状态。如果滞后太多(数量滞后和时间滞后两个维度,replica.lag.time.max.ms和replica.lag.max.message可配置),leader会把该replica从ISR中移除。被移除ISR的replica一直在追赶leader;
  • Group:Kafka实现单播和广播两种消息模型的手段。对于同一个topic,每个Group都可以拿到相同的全部数据。多个Consumer组成一个Consumer Group,同样也是逻辑上的概念;一个Consumer只能属于一个Consumer Group。Consumer Group保证其订阅的Topic的每个分区只被分配给此Consumer Group中的一个消费者处理。如果不同Consumer Group订阅了同一Topic,Consumer Group彼此之间不会干扰。这样,如果要实现一个消息可以被多个消费者同时消费(“广播”)的效果,则将每个消费者放人单独的一个Consumer Group;如果要实现一个消息只被一个消费者消费(“独占”)的效果,则将所有的Consumer放入一个Consumer Group中。在Kafka官网的介绍中,将Consumer Group称为“逻辑上的订阅者”( logical subscriber),从这个角度看,是有一定道理的。
  • 消息:Kafka中传递数据的最小单位;
  • Key:<k, v>对的键,可选项,可传空值;
  • Value:<k, v>对的值,可选项,可传空值;
  • Streams:从kafka topic的读取消息,并进行转换,然后发送到另一个topic上;
  • Connector:将Kafka连接到外部程序,从外部数据直接拉取数据,或将数据从kafka发送到外部系统中;
  • AdminClient:管理、监视Kafka topics/ broker / other object;
  • Kafka支持的组件:Spark/ Flink/ Storm/ Flume/ Hive/ ElasticSearch/ HBase/

常用命令

安装流程

windows可安装Kafka Tool,用于kafka消息的可视化。

# 下面演示的是在单节点安装Kafka的详细教程
# 安装Java,因为Kafka的启动依赖于Java
yum install java-1.8.0-openjdk.x86_64

# 下载Kafka
wget https://mirror-hk.koddos.net/apache/kafka/2.7.0/kafka_2.12-2.7.0.tgz

# 解压
tar -zxvf /root/software/kafka_2.12-2.7.0.tgz

# 启动Zookeeper。其中,Zookeeper可以单独安装,也可以使用Kafka安装目录中默认的。这里的路径需要自己修改
nohup /root/kafka_2.12-2.7.0/bin/zookeeper-server-start.sh /root/kafka_2.12-2.7.0/config/zookeeper.properties &

# 检查Zookeeper是否正常运行,查询后台是否存在其进程
ps aux | grep zookeeper

# 启动Kafka
nohup /root/kafka_2.12-2.7.0/bin/kafka-server-start.sh /root/kafka_2.12-2.7.0/config/server.properties &

# 安装JPS,它可以显示Linux下所有的Java进程
yum install java-1.8.0-openjdk-devel.x86_64

# 查看Kafka是否启动
jps

# 添加Kafka命令到环境变量,方便快速启动
vim /etc/profile  # 打开配置文件
export PATH="/root/kafka_2.12-2.7.0/bin:$PATH"  # 增加一行
source /etc/profile  # 变量生效

常用命令

# Tip: Linux配置不同,其命令与官网也不相同
# IP地址更换为Kafka安装的IP,即下面命令中的 `localhost` 需要更换
# Kafka安装目录:[IP_address]/home/devsa_dev/kafka_2.11-1.0.0
# Kafka版本:Kafka未提供kafka version命令,可观察kafka安装文件名;例如,kafka_2.11-1.0.0,2.11是Scala版本,1.0.0是Kafka版本

# 查看所有topics;2181是zookeeper端口号,可在home/devsa_dev/zookeeper-3.4.6/conf/zoo.cfg中修改
kafka-topics.sh --list --zookeeper localhost:2181
kafka-topics.sh --list --zookeeper localhost:2181 | head -n 10  # 查看前10个topics
kafka-topics.sh --list --zookeeper localhost:2181 | tail -n 10  # 查看后10个topics
kafka-topics.sh --list --zookeeper localhost:2181 | grep "yangsong"  # 查看名称包含yangsong的topic

# 查看指定topic的信息
kafka-topics.sh --describe --zookeeper localhost:2181 --topic <topic_name>

# 创建topic
kafka-topics.sh --create --zookeeper localhost:2181 --replication-factor 1 --partitions 1 --topic <topic_name>

# 删除topic
kafka-topics.sh --delete --zookeeper localhost:2181 --topic <topic_name>

# 显示所有consumer group
kafka-consumer-groups.sh --list --bootstrap-server localhost:9092

# 查看特定consumer group的信息
kafka-consumer-groups.sh --describe --bootstrap-server localhost:9092 --group <group_name>

# 启动生产者;9092是kafka的监听端口,可在kafka/config/server.properties中修改
kafka-console-producer.sh --broker-list localhost:9092 --topic <topic_name>

# 启动消费者,2181为zookeeper的监听端口
kafka-console-consumer.sh --bootstrap-server localhost:9092 --topic <topic_name> --from-beginning  # 新版方法;9092是kafka监听的端口号

# 查询topic最小的offset
kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list localhost:9092 -topic <topic_name> --time -2

# 查询topic最大的offset
kafka-run-class.sh kafka.tools.GetOffsetShell --broker-list localhost:9092 -topic <topic_name> --time -1

# 查看topic消费进度
kafka-consumer-groups.sh --describe --group <group_name> --zookeeper localhost:2181

# 在[localhost]节点上开启zookeeper;输入jps后,若出现QuorumPeerMain,说明zookeeper已开启
zkServer.sh start

# 启动kafka server;输入jps后,若出现Kafka,说明Kafka已开启
kafka-server-start.sh config/server.properties  # 单节点启动方式,当前界面启动
# 集群启动方式
kafka-server-start.sh -daemon config/server.properties  # 单节点启动方式,后台启动,推荐
# 集群启动方式

# 关闭kafka;kafka的打开和关闭都需要数十秒的时间延迟;正常开启Kafka之后,就不要关闭了,因为作业需要一直运行
kafka-server-stop.sh

Maven依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>demo0408</artifactId>
    <version>1.0</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>

    <dependencies>
        <!-- kafka常用的maven依赖 -->
        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka_2.11</artifactId>
            <version>2.0.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-clients</artifactId>
            <version>2.0.0</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.apache.kafka</groupId>
            <artifactId>kafka-streams</artifactId>
            <version>2.0.0</version>
            <scope>compile</scope>
        </dependency>

    </dependencies>

    <!-- 使打包的jar文件包含了主函数的入口 -->
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-shade-plugin</artifactId>
                <version>1.2.1</version>
                <executions>
                    <execution>
                        <phase>package</phase>
                        <goals>
                            <goal>shade</goal>
                        </goals>
                        <configuration>
                            <transformers>
                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                    <mainClass>kafka_producer_demo0407</mainClass>
                                </transformer>
                            </transformers>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

</project>

Kafka Demo: Producer简单例子

注意,idea的Java程序打包有两种方式:

  • 胖包,参考博客:https://blog.csdn.net/weixin_42089175/article/details/89113271;这种方式将程序所需的依赖全都注入到jar包里面,耗费空间、但是稳定,不易报错;
  • 瘦包,参考博客:https://jingyan.baidu.com/article/15622f24d673befdfdbea557.html;这种方式基于Maven打包,将依赖对应的链接注入jar包中,占用空间小、但是容易出现缺少第三方包的报错;

打包后,将jar包提交到集群中运行即可:java -jar <jar_name>

import java.util.Properties;

import org.apache.kafka.clients.producer.KafkaProducer;
import org.apache.kafka.clients.producer.Producer;
import org.apache.kafka.clients.producer.ProducerRecord;

public class kafka_producer_demo0407 {
    public static void  main(String[] args) {
        Properties properties = new Properties();
        properties.put("bootstrap.servers", "localhost:9092");  // Broker的IP和端口
        properties.put("ack", "all");  // 所有follower都响应了才认为消息提交成功,即"committed"
        properties.put("retries", 0);  // retries = MAX 无限重试,直到你意识到出现了问题
        properties.put("batch.size", 16384);
        properties.put("linger.ms", 1);
        properties.put("buffer.memory", 33554432);
        properties.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
        properties.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");

        Producer<String, String> producer = new KafkaProducer<String, String>(properties);
        String topic = "mesong0407";

        // 以下为业务逻辑,将业务所需数据装配为<k, v>形式,然后发送给指定的topic
        String msg_key;
        String msg_value;
        for (int i = 0; i < 3; i++) {
            msg_key = Integer.toString(i);  // Kafka的key可以指定为null
            msg_value = Integer.toString(i);
            producer.send(new ProducerRecord<String, String>(topic, msg_key, msg_value));
            System.out.println("发送消息到Kafka");
        }
        producer.close();

    }
}


Kafka Demo: Consumer简单例子

import java.util.Arrays;
import java.util.Properties;

import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.apache.kafka.clients.consumer.ConsumerRecords;
import org.apache.kafka.clients.consumer.KafkaConsumer;


public class demo {
    public static void main(String[] args)
    {
        Properties properties = new Properties();
        properties.put("bootstrap.servers", "localhost:9092");  // Broker的IP和端口
        properties.put("group.id", "yangsong");  // 指定Consumer GroupId
        properties.put("enable.auto.commit", "true");
        properties.put("auto.commit.interval.ms", "1000");
        properties.put("key.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");
        properties.put("value.deserializer", "org.apache.kafka.common.serialization.StringDeserializer");

        KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);  // 实例化一个Consumer对象
        consumer.subscribe(Arrays.asList("yangsong.test"));  // Consumer订阅该topic,可以订阅多个topic
        while (true)
        {
            ConsumerRecords<String, String> records = consumer.poll(100);
            for (ConsumerRecord<String, String> record : records) {
                System.out.printf("offset = %d, key = %s, value = %s%n"  // 输出该topic中新增消息记录的偏移量、key、value
                        , record.offset()  // 消息的偏移量
                        , record.key()  // 消息的键
                        , record.value());  // 消息的值
            }
        }

//        consumer.close();

    }
}

踩坑记录

  • Q:zkServer.sh start启动zookeeper时,报错log为Permission denied FAILED TO WRITE PID
  • A:表示此时所用账号没有写入权限,先sudo su切换root账号,然后chmod a+xwr zookeeper-3.4.6/增加写入权限,再开启zookeeper即可;

  • Q:启动kafka时,日志报错:kafka.common.KafkaException: Failed to acquire lock on file .lock in /tmp/kafka-logs. A Kafka instance in another process or thread is using this directory
  • A:先jps查看进程,kill掉已开启的kafka和zookeeper。然后重新开启zookeeper进程、kafka即可。非root用户,有时候无法通过jps命令查看所有后台进程;

  • Q:开启kafka生产者后,在会话框输入信息后报错log:WARN [Producer clientId=console-producer] Connection to node -1 could not be established. Broker may not be available
  • A:检查kafka/config/server.properties中的listeners,输入listeners指的host和端口号,或者直接输入真实IP;

经验点

  • Kafka默认端口为9092,一般最好不要修改,因为至少要修改5个配置文件,比较麻烦;
  • Kafka自带了Zookeeper,在单节点情况下,不用另外安装zookeeper即可使用;
  • 开发环境、测试环境、生产环境的Java版本尽量保持一致,否则会出现版本不同导致的报错;
apache kafka 在数据处理中特删是日志和消息的处理上会有徆多出色的表现.首先弼然推荐的是 kafka 的宏 网 http://kafka.apache.org/。在宏网最值得参考的文章就是 kafka design: http://kafka.apache.org/design.html,要特删重规返篇文章,里面有好多理念都特删好,推荐多读几遍。 在 OSC 的翻译频道有 kafka design 全中文的翻译,翻得挺好的,推荐一下: http://www.oschina.net/translate/kafka-design。kafka 的 wiki 是徆丌错的学习文档: https://cwiki.apache.org/confluence/display/KAFKA/Index 接下来就是一系列文章,文章都是循序渐迕的方式带你了览 kafka: 关亍 kafka 的基本知识,分布式的基础:《分布式消息系统 Kafka 刜步》 kafka 的分布式搭建,quick start:《kafka 分布式环境搭建》 关亍 kafka 的实现细节,返主要就是讲 design 的部分:《细节上》、《细节下》 关亍 kafka 开収环境,scala 环境的搭建:《开収环境搭建》 数据生产者,producer 的用法:《producer 的用法》、《producer 使用注意》 数据消费者,consumer 的用法:《consumer 的用法》 迓有些零碎的,关亍通信段的源码览读:《net 包源码览读》、《broker 配置》 扩展的阅读迓有下面返些: 关亍 kafka 和 jafka 的相关博客,特删好,有徆多问题也都找他览决的,大神一般的存在: http://rockybean.github.com/@rockybean kafka 的 java 化版本 jafka:https://github.com/adyliu/jafka 淘宝的 metaQ:https://github.com/killme2008/Metamorphosis 最近在写的 inforQ,刚开始写,也纯粹是为了读下源码,丌定期更新: https://github.com/ielts0909/inforq
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值