超详细Netty入门,看这篇就够了

本文主要讲述Netty框架的一些特性以及重要组件,希望看完之后能对Netty框架有一个比较直观的感受,希望能帮助读者快速入门Netty,减少一些弯路。

官方的介绍:

Netty is an asynchronous event-driven network application frameworkfor rapid development of maintainable high performance protocol servers & clients.

Netty是 一个异步事件驱动的网络应用程序框架,用于快速开发可维护的高性能协议服务器和客户端

从官网上介绍,Netty是一个网络应用程序框架,开发服务器和客户端。也就是用于网络编程的一个框架。既然是网络编程,Socket就不谈了,为什么不用NIO呢?

2.1 NIO的缺点

对于这个问题,之前我写了一篇文章《NIO入门》对NIO有比较详细的介绍,NIO的主要问题是:

  • NIO的类库和API繁杂,学习成本高,你需要熟练掌握Selector、ServerSocketChannel、SocketChannel、ByteBuffer等。
  • 需要熟悉Java多线程编程。这是因为NIO编程涉及到Reactor模式,你必须对多线程和网络编程非常熟悉,才能写出高质量的NIO程序。
  • 臭名昭著的epoll bug。它会导致Selector空轮询,最终导致CPU 100%。直到JDK1.7版本依然没得到根本性的解决。

2.2 Netty的优点

相对地,Netty的优点有很多:

  • API使用简单,学习成本低。
  • 功能强大,内置了多种解码编码器,支持多种协议。
  • 性能高,对比其他主流的NIO框架,Netty的性能最优。
  • 社区活跃,发现BUG会及时修复,迭代版本周期短,不断加入新的功能。
  • Dubbo、Elasticsearch都采用了Netty,质量得到验证。

上面这张图就是在官网首页的架构图,我们从上到下分析一下。

绿色的部分Core核心模块,包括零拷贝、API库、可扩展的事件模型。

橙色部分Protocol Support协议支持,包括Http协议、webSocket、SSL(安全套接字协议)、谷歌Protobuf协议、zlib/gzip压缩与解压缩、Large File Transfer大文件传输等等。

红色的部分Transport Services传输服务,包括Socket、Datagram、Http Tunnel等等。

以上可看出Netty的功能、协议、传输方式都比较全,比较强大。

首先搭建一个HelloWord工程,先熟悉一下API,还有为后面的学习做铺垫。以下面这张图为依据:

4.1 引入Maven依赖

使用的版本是4.1.20,相对比较稳定的一个版本。

4.2 创建服务端启动类

4.3 创建服务端处理器

4.4 创建客户端启动类

4.5 创建客户端处理器

4.6 测试

先启动服务端,再启动客户端,就可以看到结果:

MyServer打印结果:

MyClient打印结果:

5.1 taskQueue任务队列

如果Handler处理器有一些长时间的业务处理,可以交给taskQueue异步处理。怎么用呢,请看代码演示:

我们打一个debug调试,是可以看到添加进去的taskQueue有一个任务。

5.2 scheduleTaskQueue延时任务队列

延时任务队列和上面介绍的任务队列非常相似,只是多了一个可延迟一定时间再执行的设置,请看代码演示:

依然打开debug进行调试查看,我们可以有一个scheduleTaskQueue任务待执行中

5.3 Future异步机制

在搭建HelloWord工程的时候,我们看到有一行这样的代码:

很多操作都返回这个ChannelFuture对象,究竟这个ChannelFuture对象是用来做什么的呢?

ChannelFuture提供操作完成时一种异步通知的方式。一般在Socket编程中,等待响应结果都是同步阻塞的,而Netty则不会造成阻塞,因为ChannelFuture是采取类似观察者模式的形式进行获取结果。请看一段代码演示:

5.4 Bootstrap与ServerBootStrap

Bootstrap和ServerBootStrap是Netty提供的一个创建客户端和服务端启动器的工厂类,使用这个工厂类非常便利地创建启动类,根据上面的一些例子,其实也看得出来能大大地减少了开发的难度。首先看一个类图:

可以看出都是继承于AbstractBootStrap抽象类,所以大致上的配置方法都相同。

一般来说,使用Bootstrap创建启动器的步骤可分为以下几步:

5.4.1 group()

在上一篇文章《Reactor模式》中,我们就讲过服务端要使用两个线程组:

  • bossGroup 用于监听客户端连接,专门负责与客户端创建连接,并把连接注册到workerGroup的Selector中。
  • workerGroup用于处理每一个连接发生的读写事件。

一般创建线程组直接使用以下new就完事了:

有点好奇的是,既然是线程组,那线程数默认是多少呢?深入源码:

通过源码可以看到,默认的线程数是cpu核数的两倍。假设想自定义线程数,可以使用有参构造器:

5.4.2 channel()

这个方法用于设置通道类型,当建立连接后,会根据这个设置创建对应的Channel实例。

使用debug模式可以看到

通道类型有以下:

NioSocketChannel: 异步非阻塞的客户端 TCP Socket 连接。

NioServerSocketChannel: 异步非阻塞的服务器端 TCP Socket 连接。

常用的就是这两个通道类型,因为是异步非阻塞的。所以是首选。

OioSocketChannel: 同步阻塞的客户端 TCP Socket 连接。

OioServerSocketChannel: 同步阻塞的服务器端 TCP Socket 连接。

稍微在本地调试过,用起来和Nio有一些不同,是阻塞的,所以API调用也不一样。因为是阻塞的IO,几乎没什么人会选择使用Oio,所以也很难找到例子。我稍微琢磨了一下,经过几次报错之后,总算调通了。代码如下:

NioSctpChannel: 异步的客户端 Sctp(Stream Control Transmission Protocol,流控制传输协议)连接。

NioSctpServerChannel: 异步的 Sctp 服务器端连接。

本地没启动成功,网上看了一些网友的评论,说是只能在linux环境下才可以启动。从报错信息看:SCTP not supported on this platform,不支持这个平台。因为我电脑是window系统,所以网友说的有点道理。

5.4.3 option()与childOption()

首先说一下这两个的区别。

option()设置的是服务端用于接收进来的连接,也就是boosGroup线程。

childOption()是提供给父管道接收到的连接,也就是workerGroup线程。

搞清楚了之后,我们看一下常用的一些设置有哪些:

SocketChannel参数,也就是childOption()常用的参数:

SO_RCVBUF Socket参数,TCP数据接收缓冲区大小。TCP_NODELAY TCP参数,立即发送数据,默认值为Ture。SO_KEEPALIVE Socket参数,连接保活,默认值为False。启用该功能时,TCP会主动探测空闲连接的有效性。

ServerSocketChannel参数,也就是option()常用参数:

SO_BACKLOG Socket参数,服务端接受连接的队列长度,如果队列已满,客户端连接将被拒绝。默认值,Windows为200,其他为128。

由于篇幅限制,其他就不列举了,大家可以去网上找资料看看,了解一下。

5.4.4 设置流水线(重点)

ChannelPipeline是Netty处理请求的责任链,ChannelHandler则是具体处理请求的处理器。实际上每一个channel都有一个处理器的流水线。

在Bootstrap中childHandler()方法需要初始化通道,实例化一个ChannelInitializer,这时候需要重写initChannel()初始化通道的方法,装配流水线就是在这个地方进行。代码演示如下:

处理器Handler主要分为两种:

ChannelInboundHandlerAdapter(入站处理器)、ChannelOutboundHandler(出站处理器)

入站指的是数据从底层java NIO Channel到Netty的Channel。

出站指的是通过Netty的Channel来操作底层的java NIO Channel。

ChannelInboundHandlerAdapter处理器常用的事件有

  1. 注册事件 fireChannelRegistered。
  2. 连接建立事件 fireChannelActive。
  3. 读事件和读完成事件 fireChannelRead、fireChannelReadComplete。
  4. 异常通知事件 fireExceptionCaught。
  5. 用户自定义事件 fireUserEventTriggered。
  6. Channel 可写状态变化事件 fireChannelWritabilityChanged。
  7. 连接关闭事件 fireChannelInactive。

ChannelOutboundHandler处理器常用的事件有

  1. 端口绑定 bind。
  2. 连接服务端 connect。
  3. 写事件 write。
  4. 刷新时间 flush。
  5. 读事件 read。
  6. 主动断开连接 disconnect。
  7. 关闭 channel 事件 close。

还有一个类似的handler(),主要用于装配parent通道,也就是bossGroup线程。一般情况下,都用不上这个方法。

5.4.5 bind()

提供用于服务端或者客户端绑定服务器地址和端口号,默认是异步启动。如果加上sync()方法则是同步。

有五个同名的重载方法,作用都是用于绑定地址端口号。不一一介绍了。

5.4.6 优雅地关闭EventLoopGroup

会关闭所有的child Channel。关闭之后,释放掉底层的资源。

5.5 Channel

Channel是什么?不妨看一下官方文档的说明:

A nexus to a network socket or a component which is capable of I/O operations such as read, write, connect, and bind

翻译大意:一种连接到网络套接字或能进行读、写、连接和绑定等I/O操作的组件。

如果上面这段说明比较抽象,下面还有一段说明:

A channel provides a user:

the current state of the channel (e.g. is it open? is it connected?),the configuration parameters of the channel (e.g. receive buffer size),the I/O operations that the channel supports (e.g. read, write, connect, and bind), andthe ChannelPipeline which handles all I/O events and requests associated with the channel.

翻译大意:

channel为用户提供:

  1. 通道当前的状态(例如它是打开?还是已连接?)
  2. channel的配置参数(例如接收缓冲区的大小)
  3. channel支持的IO操作(例如读、写、连接和绑定),以及处理与channel相关联的所有IO事件和请求的ChannelPipeline。

5.5.1 获取channel的状态

以上就是获取channel的四种状态的方法。

5.5.2 获取channel的配置参数

获取单条配置信息,使用getOption(),代码演示:

获取多条配置信息,使用getOptions(),代码演示:

5.5.3 channel支持的IO操作

写操作,这里演示从服务端写消息发送到客户端:

客户端控制台:

连接操作,代码演示:

通过channel获取ChannelPipeline,并做相关的处理:

5.6 Selector

在NioEventLoop中,有一个成员变量selector,这是nio包的Selector,在之前《NIO入门》中,我已经讲过Selector了。

Netty中的Selector也和NIO的Selector是一样的,就是用于监听事件,管理注册到Selector中的channel,实现多路复用器。

5.7 PiPeline与ChannelPipeline

在前面介绍Channel时,我们知道可以在channel中装配ChannelHandler流水线处理器,那一个channel不可能只有一个channelHandler处理器,肯定是有很多的,既然是很多channelHandler在一个流水线工作,肯定是有顺序的。

于是pipeline就出现了,pipeline相当于处理器的容器。初始化channel时,把channelHandler按顺序装在pipeline中,就可以实现按序执行channelHandler了。

在一个Channel中,只有一个ChannelPipeline。该pipeline在Channel被创建的时候创建。ChannelPipeline包含了一个ChannelHander形成的列表,且所有ChannelHandler都会注册到ChannelPipeline中。

5.8 ChannelHandlerContext

在Netty中,Handler处理器是有我们定义的,上面讲过通过集成入站处理器或者出站处理器实现。这时如果我们想在Handler中获取pipeline对象,或者channel对象,怎么获取呢。

于是Netty设计了这个ChannelHandlerContext上下文对象,就可以拿到channel、pipeline等对象,就可以进行读写等操作。

通过类图,ChannelHandlerContext是一个接口,下面有三个实现类。

实际上ChannelHandlerContext在pipeline中是一个链表的形式。看一段源码就明白了:

下面我用一张图来表示,会更加清晰一点:

5.9 EventLoopGroup

我们先看一下EventLoopGroup的类图:

其中包括了常用的实现类NioEventLoopGroup。OioEventLoopGroup在前面的例子中也有使用过。

从Netty的架构图中,可以知道服务器是需要两个线程组进行配合工作的,而这个线程组的接口就是EventLoopGroup。

每个EventLoopGroup里包括一个或多个EventLoop,每个EventLoop中维护一个Selector实例。

5.9.1 轮询机制的实现原理

我们不妨看一段DefaultEventExecutorChooserFactory的源码:

这段代码可以确定执行的方式是轮询机制,接下来debug调试一下:

它这里还有一个判断,如果线程数不是2的N次方,则采用取模算法实现。

能力有限,如果有什么错误或者不当之处,请大家批评指正,一起学习交流!

本文为阿里云原创内容,未经允许不得转载。

JAVA 学习全攻略,从入门到精通不是梦

《JAVA 学习全攻略,从入门到精通不是梦》

在编程的世界里,JAVA 一直是备受欢迎的编程语言之一。无论是想要进入软件开发领域的小白,还是希望提升技能的程序员,都对“JAVA 到底怎么学”这个问题充满了好奇。今天,作为一名博主,我就来和大家分享一下 JAVA 的学习方法,让你在学习 JAVA 的道路上少走弯路。

一、明确学习目标

在开始学习 JAVA 之前,我们需要明确自己的学习目标。你是想成为一名 JAVA 开发工程师,还是只是想了解一下 JAVA 编程的基础知识?不同的学习目标决定了你的学习深度和广度。

如果你的目标是成为一名专业的 JAVA 开发工程师,那么你需要掌握 JAVA 的基础知识、面向对象编程思想、数据库编程、Web 开发等方面的知识。如果你的目标只是了解一下 JAVA 编程,那么你可以学习一些 JAVA 的基础知识和简单的编程实例。

二、学习基础知识

  1. 安装 JDK
  2. JDK(Java Development Kit)是 JAVA 开发工具包,它包含了 JAVA 编译器、JRE(Java Runtime Environment)等工具。在学习 JAVA 之前,我们需要先安装 JDK。
  3. 安装 JDK 非常简单,只需要按照安装向导的提示进行操作即可。安装完成后,我们需要配置环境变量,以便在命令行中使用 JAVA 命令。
  4. 学习基本语法
  5. JAVA 的基本语法包括变量、数据类型、运算符、控制语句等。这些都是 JAVA 编程的基础,我们需要认真学习并掌握。
  6. 可以通过阅读 JAVA 教程、观看视频教程等方式来学习 JAVA 的基本语法。在学习的过程中,我们可以通过编写一些简单的程序来巩固所学的知识。
  7. 掌握面向对象编程思想
  8. JAVA 是一种面向对象的编程语言,面向对象编程思想是 JAVA 编程的核心。我们需要掌握面向对象编程的基本概念,如类、对象、封装、继承、多态等。
  9. 可以通过阅读相关的书籍、教程来学习面向对象编程思想。在学习的过程中,我们可以通过编写一些面向对象的程序来加深对面向对象编程思想的理解。

三、深入学习

  1. 数据库编程
  2. 在实际的开发中,我们经常需要使用数据库来存储和管理数据。因此,学习数据库编程是非常重要的。
  3. JAVA 中可以使用 JDBC(Java Database Connectivity)来连接数据库,并进行数据库操作。我们需要学习 JDBC 的基本用法,以及如何使用 SQL 语句来操作数据库。
  4. Web 开发
  5. JAVA 在 Web 开发领域也有广泛的应用。我们可以学习 JAVA Web 开发的相关技术,如 Servlet、JSP、Spring、MyBatis 等。
  6. 学习 JAVA Web 开发需要掌握 HTML、CSS、JavaScript 等前端技术,以及数据库编程等后端技术。
  7. 框架学习
  8. 在 JAVA 开发中,框架可以大大提高开发效率。我们可以学习一些常用的 JAVA 框架,如 Spring、Spring Boot、MyBatis 等。
  9. 学习框架需要掌握框架的基本用法,以及如何使用框架来开发项目。

四、实践项目

学习 JAVA 最好的方法就是实践。我们可以通过参与实际的项目开发,来提高自己的编程能力和解决问题的能力。

  1. 参与开源项目
  2. 开源项目是一个非常好的学习资源。我们可以参与一些开源项目,了解其他开发者的编程思路和方法,同时也可以为开源项目做出自己的贡献。
  3. 自己动手开发项目
  4. 我们可以自己动手开发一些项目,如博客系统、电商系统等。在开发项目的过程中,我们可以将所学的知识应用到实际中,同时也可以发现自己的不足之处,并加以改进。

五、持续学习

JAVA 是一种不断发展和变化的编程语言,我们需要持续学习,才能跟上技术的发展步伐。

  1. 关注技术博客和论坛
  2. 我们可以关注一些技术博客和论坛,了解最新的技术动态和编程技巧。同时,我们也可以在博客和论坛上与其他开发者交流和分享经验。
  3. 参加技术培训和会议
  4. 参加技术培训和会议可以让我们接触到最新的技术和思想,同时也可以与其他开发者进行面对面的交流和学习。
  5. 阅读技术书籍和论文
  6. 阅读技术书籍和论文可以让我们深入了解 JAVA 编程的原理和技术,同时也可以提高我们的编程水平和解决问题的能力。

总之,学习 JAVA 需要有明确的学习目标、扎实的基础知识、深入的学习和实践项目,以及持续的学习态度。希望本文对大家学习 JAVA 有所帮助。

全网最通俗易懂的Kafka入门

专注于Java领域优质技术,欢迎关注

作者: Java3y 来自:Java3y

前言

只有光头才能变强。

文本已收录至我的GitHub仓库,欢迎Star:https://github.com/ZhongFuCheng3y/3y

在这篇之前已经写过两篇基础文章了,强烈建议先去阅读:

众所周知,消息队列的产品有好几种,这里我选择学习Kafka的原因,无他,公司在用。

我司使用的是Kafka和自研的消息队列(Kafka和RocketMQ)改版,于是我就想学学Kafka这款消息队列啦。本篇文章对Kafka入门,希望对大家有所帮助。

本文知识点提前预览:

提前预览

这篇文章花了我很长时间画图,目的是希望以最通俗易懂的方式带大家入门,如果觉得不错,希望能给我点个赞

首先我们得去官网看看是怎么介绍Kafka的:

  • https://kafka.apache.org/intro

在收集资料学习的时候,已经发现有不少的前辈对官网的介绍进行翻译和总结了,所以我这里就不重复了,贴下地址大家自行去学习啦:

  • https://scala.cool/2018/03/learning-kafka-1/
  • https://colobu.com/2014/08/06/kafka-quickstart/

我之前写过的文章也提到了,要做一个消息队列可能要考虑到以下的问题:

  • 使用消息队列不可能是单机的(必然是分布式or集群)
  • 数据写到消息队列,可能会存在数据丢失问题,数据在消息队列需要持久化(磁盘?数据库?Redis?分布式文件系统?)
  • 想要保证消息(数据)是有序的,怎么做?
  • 为什么在消息队列中重复消费了数据

下面我以Kafka为例对这些问题进行简单的解答,进而入门Kafka。

1.1 Kafka入门

众所周知,Kafka是一个消息队列,把消息放到队列里边的叫生产者,从队列里边消费的叫消费者

生产者和消费者

一个消息中间件,队列不单单只有一个,我们往往会有多个队列,而我们生产者和消费者就得知道:把数据丢给哪个队列,从哪个队列消息。我们需要给队列取名字,叫做topic(相当于数据库里边的概念)

给队列取名字,专业名词叫topic

现在我们给队列取了名字以后,生产者就知道往哪个队列丢数据了,消费者也知道往哪个队列拿数据了。我们可以有多个生产者往同一个队列(topic)丢数据,多个消费者往同一个队列(topic)拿数据

为了提高一个队列(topic)的吞吐量,Kafka会把topic进行分区(Partition)

Kafka分区

所以,生产者实际上是往一个topic名为Java3y中的分区(Partition)丢数据,消费者实际上是往一个topic名为Java3y的分区(Partition)取数据

生产者和消费者实际上操作的是分区

一台Kafka服务器叫做Broker,Kafka集群就是多台Kafka服务器:

Kafka集群

一个topic会分为多个partition,实际上partition会分布在不同的broker中,举个例子:

一个生产者丢数据给topic

由此得知:Kafka是天然分布式的

如果不了解分布式/集群,以及基本的分布式概念的同学,可以关注我的GitHub:https://github.com/ZhongFuCheng3y/3y

关键字:分布式、SpringCloud 保证能让你搞懂。觉得我写得不错,就给我点个赞

现在我们已经知道了往topic里边丢数据,实际上这些数据会分到不同的partition上,这些partition存在不同的broker上。分布式肯定会带来问题:“万一其中一台broker(Kafka服务器)出现网络抖动或者挂了,怎么办?”

Kafka是这样做的:我们数据存在不同的partition上,那kafka就把这些partition做备份。比如,现在我们有三个partition,分别存在三台broker上。每个partition都会备份,这些备份散落在不同的broker上。

红色代表主分区,紫色代表备份分区

红色块的partition代表的是分区,紫色的partition块代表的是备份分区。生产者往topic丢数据,是与分区交互,消费者消费topic的数据,也是与主分区交互。

备份分区仅仅用作于备份,不做读写。如果某个Broker挂了,那就会选举出其他Broker的partition来作为主分区,这就实现了高可用

另外值得一提的是:当生产者把数据丢进topic时,我们知道是写在partition上的,那partition是怎么将其持久化的呢?(不持久化如果Broker中途挂了,那肯定会丢数据嘛)。

Kafka是将partition的数据写在磁盘的(消息日志),不过Kafka只允许追加写入(顺序访问),避免缓慢的随机 I/O 操作。

  • Kafka也不是partition一有数据就立马将数据写到磁盘上,它会先缓存一部分,等到足够多数据量或等待一定的时间再批量写入(flush)。

上面balabala地都是讲生产者把数据丢进topic是怎么样的,下面来讲讲消费者是怎么消费的。既然数据是保存在partition中的,那么消费者实际上也是从partition中取数据。

从各个主分区取数据

生产者可以有多个,消费者也可以有多个。像上面图的情况,是一个消费者消费三个分区的数据。多个消费者可以组成一个消费者组

消费者组

本来是一个消费者消费三个分区的,现在我们有消费者组,就可以每个消费者去消费一个分区(也是为了提高吞吐量)

消费者组的每个消费者会去对应partition拿数据

按图上所示的情况,这里想要说明的是:

  • 如果消费者组中的某个消费者挂了,那么其中一个消费者可能就要消费两个partition了
  • 如果只有三个partition,而消费者组有4个消费者,那么一个消费者会空闲
  • 如果多加入一个消费者组,无论是新增的消费者组还是原本的消费者组,都能消费topic的全部数据。(消费者组之间从逻辑上它们是独立的)

前面讲解到了生产者往topic里丢数据是存在partition上的,而partition持久化到磁盘是IO顺序访问的,并且是先写缓存,隔一段时间或者数据量足够大的时候才批量写入磁盘的。

消费者在读的时候也很有讲究:正常的读磁盘数据是需要将内核态数据拷贝到用户态的,而Kafka 通过调用sendfile()直接从内核空间(DMA的)到内核空间(Socket的),少做了一步拷贝的操作。

Kafka 读数据 巧妙

有的同学可能会产生疑问:消费者是怎么知道自己消费到哪里的呀?Kafka不是支持回溯吗?那是怎么做的呀?

  • 比如上面也提到:如果一个消费者组中的某个消费者挂了,那挂掉的消费者所消费的分区可能就由存活的消费者消费。那存活的消费者是需要知道挂掉的消费者消费到哪了,不然怎么玩。

这里要引出offset了,Kafka就是用offset来表示消费者的消费进度到哪了,每个消费者会都有自己的offset。说白了offset就是表示消费者的消费进度

在以前版本的Kafka,这个offset是由Zookeeper来管理的,后来Kafka开发者认为Zookeeper不合适大量的删改操作,于是把offset在broker以内部topic(__consumer_offsets)的方式来保存起来。

每次消费者消费的时候,都会提交这个offset,Kafka可以让你选择是自动提交还是手动提交。

既然提到了Zookeeper,那就多说一句。Zookeeper虽然在新版的Kafka中没有用作于保存客户端的offset,但是Zookeeper是Kafka一个重要的依赖。

  • 探测broker和consumer的添加或移除。
  • 负责维护所有partition的领导者/从属者关系(主分区和备份分区),如果主分区挂了,需要选举出备份分区作为主分区。
  • 维护topic、partition等元配置信息
  • ….

这张图来源胡夕老师的《Kafka核心技术与实战》

通过这篇文章,文章开头那几个问题估计多多少少都懂一些啦。我来简要回答一下:

使用消息队列不可能是单机的(必然是分布式or集群)

Kafka天然是分布式的,往一个topic丢数据,实际上就是往多个broker的partition存储数据

数据写到消息队列,可能会存在数据丢失问题,数据在消息队列需要持久化(磁盘?数据库?Redis?分布式文件系统?)

Kafka会将partition以消息日志的方式(落磁盘)存储起来,通过 顺序访问IO和缓存(等到一定的量或时间)才真正把数据写到磁盘上,来提高速度。

想要保证消息(数据)是有序的,怎么做?

Kafka会将数据写到partition,单个partition的写入是有顺序的。如果要保证全局有序,那只能写入一个partition中。如果要消费也有序,消费者也只能有一个。

为什么在消息队列中重复消费了数据

凡是分布式就无法避免网络抖动/机器宕机等问题的发生,很有可能消费者A读取了数据,还没来得及消费,就挂掉了。Zookeeper发现消费者A挂了,让消费者B去消费原本消费者A的分区,等消费者A重连的时候,发现已经重复消费同一条数据了。(各种各样的情况,消费者超时等等都有可能…)

如果业务上不允许重复消费的问题,最好消费者那端做业务上的校验(如果已经消费过了,就不消费了)

这篇文章主要是Kafka入门,Kafka还涉及到别的概念,以及还有别的东西。在我感觉中,很多的面试题都跟配置有关,所以在解决某些问题的时候,先看看能不能通过现有配置解决掉(学多了框架,你就会发现很多官方的就已经支持解决了,你做的可能改改配置/参数就完事了)。

本文作者及来源:Renderbus瑞云渲染农场https://www.renderbus.com

点赞 0
收藏 0

文章为作者独立观点不代本网立场,未经允许不得转载。