Kafka
Kafka到底是什么?它在系统中充当什么角色?它主要应用哪些领域呢?
一、消息系统
Kafka 允许发布和订阅数据,从这点来看,它类似于 ActiveMQ、RabbitMQ 等框架,那有什么不同呢?
Kafka 作为一个分布式系统,是以集群的方式运行的,可以自由的伸缩。同时还提供了数据传递保证 --- 可复制、持久化等等
二、存储和持续处理大型数据流
Kafka可以存储和持续处理大型数据流,并保持持续性的低延迟。在这一点上,可以将其看成一个实时版的Hadoop。
Kafka 的低延迟特点更加适合用在核心的业务应用上,当业务事件发生时,Kakfa 能够及时对这些事件作出相响应。
三、实时流平台
Kafka其实是一个面向实时数据的流平台,也就是它不仅可以将现有的应用程序和数据系统连接起来,它还能用于加强这些触发相同数据流的应用。
这些数据流是现代数字科技公司的核心,与现金流一样。
将上述三个领域聚合在一起,将所有的数据流整合到一起,Kafa因为变得极其强大。
kafka安装
Zookeeper
在安装Kafka之前,我们首先需要安装kafka的依赖应用zookeeper(一种分布式的,开放源码的分布式应用程序协调服务)
zookeeper对Kafka集群进行管理和负载均衡。
Docker加速
打开阿里云网站
编辑
- 选择左下角的镜像加速器
- 选择linux系统(Ubuntu,CentOS)
- 拷贝图示右下区域代码
如果执行完成,没有异常则表示加速成功
Zookeeper安装
zookeeper有现成的Docker镜像,我们只需要一键安装启动即可。
在服务器上面输入如下命令
sudo docker run -d --restart=unless-stopped --name zookeeper --network host zookeeper
启动后,可以使用docker ps查看zookeeper是否启动成功
kafka安装
将私网IP和公网IP替换成如下命令对应的部分
sudo docker run -d --restart=unless-stopped --name kafka --network host \
--env KAFKA_HEAP_OPTS="-Xmx256M -Xms128M" \
--env KAFKA_ZOOKEEPER_CONNECT=127.0.0.1:2181 \
--env KAFKA_LISTENER_SECURITY_PROTOCOL_MAP=INSIDE:PLAINTEXT,OUTSIDE:PLAINTEXT \
--env KAFKA_LISTENERS=INSIDE://0.0.0.0:9092,OUTSIDE://0.0.0.0:9093 \
--env KAFKA_INTER_BROKER_LISTENER_NAME=INSIDE \
--env KAFKA_ADVERTISED_LISTENERS=INSIDE://{此处替换为私网IP}:9092,OUTSIDE://{此处替换为公网IP}:9093 \
wurstmeister/kafka
注意上面命令中提示替换的地方,左右大括号也要换。
运行以后可以执行如下命令查看运行日志
docker logs -f kafka
出现如下日志表示运行成功
[2020-08-03 08:43:45,935] INFO [GroupMetadataManager brokerId=1004] Finished loading offsets and group metadata from __consumer_offsets-45 in 0 milliseconds. (kafka.coordinator.group.GroupMetadataManager)
[2020-08-03 08:43:45,935] INFO [GroupMetadataManager brokerId=1004] Finished loading offsets and group metadata from __consumer_offsets-48 in 0 milliseconds. (kafka.coordinator.group.GroupMetadataManager)
开放阿里云端口
和之前redis逻辑一样,我们需要在安全组里面添加9092、9093、2181端口。
SpringBoot集成Kafka
Kafka环境部署,本节课我们将学习如何在 SpringBoot 中引入 Kafka。
引入依赖包
Maven 中有直接适配SpringBoot的Kafka依赖包,我们只需要在pom.xml中引入即可:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
SpringBoot 会自动匹配版本
配置 Kafka 服务器
在应用的application.properties中,加入Kafka服务器的基础配置,如下
#============== kafka ===================
# 指定kafka 代理地址,可以多个
spring.kafka.bootstrap-servers={服务器公网IP地址}:9093
#=============== 生产者配置=======================
spring.kafka.producer.retries=0
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
#===============消费者配置=======================
# 指定默认消费者group id
spring.kafka.consumer.group-id=test-consumer-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
注意,这里面大多数属性都是生产者,消费者的基础配置,我们不需要关心,下一章马上就会学习到。 暂时我们只需要替换其中{服务器公网IP地址}部分,注意大括号也要一起替换喔。
最终替换以后的结果为
spring.kafka.bootstrap-servers=xxx.xxx.xxx.xxx:9093
引入测试代码
在KafkaSender加入基础的发送消息代码,测试 Kafka 是否配置成功。
此代码大家不用特别纠结,在第 2 章我们将详细介绍,此处只是用来测试 Kafka 是否配置完成即可。
@Controller
@RequestMapping("kafka")
public class KafkaSender {
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
//发送消息方法
@RequestMapping("/send")
@ResponseBody
public String send() {
kafkaTemplate.send("topic", "youkeda");
return "success";
}
}
运行一下
代码演示
如果控制台出现如下信息则表示Kafka配置成功。
2020-08-05 10:20:59.775 INFO 368 --- [nio-8080-exec-3] o.a.k.clients.producer.ProducerConfig : ProducerConfig values:
acks = 1
batch.size = 16384
...... 此处省略N行
ssl.truststore.type = JKS
transaction.timeout.ms = 60000
transactional.id = null
value.serializer = class org.apache.kafka.common.serialization.StringSerializer
2020-08-05 10:20:59.877 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.5.0
2020-08-05 10:20:59.882 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: 66563e712b0b9f84
2020-08-05 10:20:59.882 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1596594059876
2020-08-05 10:21:00.080 INFO 368 --- [ad | producer-1] org.apache.kafka.clients.Metadata : [Producer clientId=producer-1] Cluster ID: D4w2I2tqSpiXTomr0ixBaw
基本错误判断
1. 如果出现 Timeout 超时提醒
请检查阿里云安全组端口是否已经打开
服务间的依赖耦合
Kafka的第一个应用场景,发布订阅消息系统。 在学些发布订阅系统之前,我们先来看一个服务耦合问题,回顾下 P4 学习的得物项目。
还记得当我们支付成功以后的逻辑么?
如下图所示:
编辑
我们下单调用订单服务,下单成功以后用订单号调用支付服务,支付服务唤起支付宝支付。 当支付成功以后,我们会做 3 件事情:
- 更新支付流水状态
- 更新订单状态
- 更新商品购买数
服务之间依赖耦合严重
由于上面的业务逻辑,导致支付服务对订单服务产生了依赖关系,而这个依赖关系在架构层次来说是不被允许的,为什么呢?
- 支付服务应该是一个底层服务,理论上只有订单依赖支付,而支付服务不需要关心并更新订单状态。
- 假设订单系统无法访问(升级,业务变更,故障等),将导致订单状态无法被同步,并且无法自动恢复。
学习完Dubbo后,如果我们将系统拆分成微服务架构,这种错乱的服务之间关系将给我们的系统带来灾难式的问题。
试想一下,如果我们系统继续增强,再次加入了日志服务,通知服务。
- 商品服务会产生浏览日志,上新通知服务。
- 订单会产生订单操作日志,订单状态变更通知。
- 支付会产生支付记录日志,支付通知。
那么系统服务之间关系将会是什么样呢?将产生更加复杂的依赖关系,如下图所示:
编辑
那什么办法解除服务之间的耦合关系呢?
消息系统
对,我们需要使用一个消息系统,利用消息解除服务之间的依赖关系。最终关系如下
那么问题来了,消息系统如何实现呢?请看下一节。
发布订阅消息系统
消息系统接收订阅者订阅主题
消息系统将主题发送给该主题的订阅者
编辑
kafka生产者-写入数据
发布和订阅是两个非常重要的因素。所谓的发布,Kafka中被称为生产者,顾名思义生产数据,也就是往Kafka中写入数据。
本节课,我们将从 Kafka 生产者的设计和组件讲起,学习如何在 SpringBoot 中使用 Kafka 生产者。
我们依次分析 kafka 消息和发送机制。
消息主题和内容
通过上一节的分析,我们知道每个消息都有一个明确的主题,用来筛选消息的订阅者。除了主题,更重要的当然是消息的内容。
在 Java 中,Kafka 消息用类ProducerRecord<K, V>表示。
注意这里使用了泛型,这里的 V 即是消息的内容。
有同学肯定有疑问?
这里的 K 代表什么呢?
这里的 Key 不是主题,大家可以将其当做消息的附加信息,具体作用稍后就能明白。
那么到目前为止,我们知道一个消息体的大致结果如下
内容序列化
我们知道为了网络传输,通常我们需要将内容进行序列化,同样Kafka也是如此,需要分别将Key, Value进行序列化。 回顾下之前application.properties中的配置属性。
spring.kafka.producer.retries=0
spring.kafka.producer.batch-size=16384
spring.kafka.producer.buffer-memory=33554432
//#1. key序列化
spring.kafka.producer.key-serializer=org.apache.kafka.common.serialization.StringSerializer
//#2. value序列化
spring.kafka.producer.value-serializer=org.apache.kafka.common.serialization.StringSerializer
注意#1, #2处,从配置可以看出key、value都是用的字符串序列化方式。 同样也约定我们的消息体,应该是ProducerRecord<String, String>类型。
当然 Kafka 还提供了整数和字节数组序列化器 甚至 Kafka 还提供了自定义序列化器作为扩展方案 本课程中我们暂不涉及这些复杂方案,有兴趣的同学下来自己了解下
继续在我们流程中添加序列化器
主题分区
我们继续思考的,对于消息应该用什么数据结构存储呢?
消息肯定满足是先入先出的规则,所以最好使用队列进行存储,俗称消息队列。
那么消息传递的过程如下所示:
左侧是消息入口,右侧是消息出口,越早的消息肯定越早被消费。
但是,如果仅仅是这么简单的系统,我们自己的也可以完成,人人都能造一个 Kafka 了?事实是这样么?肯定不是。
我们知道 Kafka 是为了应对大数据量,大批量消息而设计的,这种简单的系统是肯定不支持大量并发的。在此基础上,系统需要支持横向扩展的能力。
Kafka 如何实现呢?
它提出了分区(Partition)的概念,每个分区都是一个队列,每个消息会按照一定的规则放置在某个分区里面。
我们来看一个完整的流程:
当消息通过序列化到分区器时,系统首先根据Topic寻到到对应的主题区域,然后再通过规则找到对应主题下的分区。
默认情况下消息会被随机发送到主题内各个可用的分区上,并且通过算法保证分区消息量均衡。
如果消息体中有
Key,则会根据Key的哈希值找到某个固定分区,也就是如果Key相同则分区也将相同。
kafka生产者-Spring Boot中使用
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("kafka")
public class KafkaSender {
// #1. 定义Kafka消息类型
@Autowired
private KafkaTemplate<String, String> kafkaTemplate;
//发送消息方法
@RequestMapping("/send")
@ResponseBody
public String send() {
// #2. 发送Kafka消息
kafkaTemplate.send("topic", "youkeda");
return "success";
}
}
连接kafka
这里需要和application.properties中配置的序列化器相对应哈,如下所示:
发送消息
如上所示#2处的注释,我们用一句话即完成了消息的发送。
在这里我们使用的 key = null 的发送方法:
KafkaTemplate同样提供了Key != null的发送方法:
大家根据场景各取所需。
运行一下上面的代码如下:
代码演示
可以看到在调用kafka/send接口之后,控制台出现如下信息:
2020-08-05 16:46:36.253 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka version: 2.5.0
2020-08-05 16:46:36.254 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka commitId: 66563e712b0b9f84
2020-08-05 16:46:36.254 INFO 368 --- [nio-8080-exec-3] o.a.kafka.common.utils.AppInfoParser : Kafka startTimeMs: 1596617196252
2020-08-05 16:46:36.462 INFO 368 --- [ad | producer-1] org.apache.kafka.clients.Metadata : [Producer clientId=producer-1] Cluster ID: D4w2I2tqSpiXTomr0ixBaw
从日志可以看到消息已经发送成功,并且返回给我们commitId。
kafka生产者-消费数据
kafka消息消费者KafkaConsumer。
消费者 和 消费者组
从订阅发布模式来看,消费者肯定是对某个主题进行监听,一旦有消息则消费此消息,然后处理自己的业务逻辑。
试想一个场景,当消息生产的过快,消费速度跟不上生产速度会出现什么现象?这时候应该怎么办呢?
显然对于消费者,也需要支持横向扩展的能力。
就像多个生产者可以向相同的主题中写入消息一样,多个消费者也可以从同一个主题中读取消息,从而达到对消息进行分流。
那么问题来了:不同消费者如何分配同一主题的消息呢,同一主题的消息不同分区如何分流呢?
在这里我们需要引出一个新的概念 --- 消费者组。
一个消费者组里的消费者订阅同一个主题,每个消费者接受主题一部分分区的消息。
我们举几个简单例子就能明白其中的道理
案例 1:单消费者
如果一个消费者组只有一个消费者,它将消费这个主题下所有分区的消息,如下图所示:
案例 2:多消费者
如果一个消费者组有多个消费者(但不超过分区数量),它将均衡分流所有分区的消息:
如果消费者数量和分区数量刚好相同,则每个消费者接收一个分区的消息:
注意:一条消息只会被同组消费一次,也就是消息不会在同一个消费者组中重复消费,具有排他性。
已经收到消息的消费者重启计算机后也不会再次接收到同一条消息。
案例 3:超消费者
如果消费者数量超过分区数量怎么办呢?那么一部分消费者将会闲置,不会接受任何消息。
比如图中的 消费者 5 将会闲置。
案例 4: 多消费者组
在上面的例子中,如果新增消费者组 B,同样订阅了该主题,将会出现什么情况呢?
注意:多个消费者组订阅同一个主题,将分别消费这个主题的消息,也就是一个消息都会通知每个消费者组。
如下图所示:
kafka消费者-SpringBoot中使用
首先我们需要配置消费者组,消息体Key,Value的反序列化器,如application.properties中配置所示:
// 配置消费者组
spring.kafka.consumer.group-id=test-consumer-group
spring.kafka.consumer.auto-offset-reset=earliest
spring.kafka.consumer.enable-auto-commit=true
spring.kafka.consumer.auto-commit-interval=100
// 配置消息体Key反序列化器
spring.kafka.consumer.key-deserializer=org.apache.kafka.common.serialization.StringDeserializer
// 配置消息体Value反序列化器
spring.kafka.consumer.value-deserializer=org.apache.kafka.common.serialization.StringDeserializer
消费者代码实现
紧接着,我们完成消息接受代码,如下:
package com.youkeda.demo;
import java.util.Optional;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
@Component
public class KafkaReceiver {
// #1. 监听主题为topic的消息
@KafkaListener(topics = {"topic"})
public void listen(ConsumerRecord<?, ?> record) {
// #2. 如果消息存在
Optional<?> kafkaMessage = Optional.ofNullable(record.value());
if (kafkaMessage.isPresent()) {
// #3. 获取消息
Object message = kafkaMessage.get();
System.out.println("message =" + message);
}
}
}
注意上面 3 个注释
-
#1需要利用注解的方式注册我们希望监听的 Kafka 消息主题,一旦有消息,将触发这个listen方法。 -
#2判断 kafka 消息是否存在Optional是 Java8 的工具类,主要用于解决空指针异常的问题。它提供很多有用的方法,这样我们就不用显式进行空值检测。这里主要用到三个常用的方法,以判断消息是否存在,如果存在则取出消息值。 -
#3获取 Kafka 消息中的消息体
我们首先利用kafka/send接口发送一个消息,然后KafkaReceiver中将接收到刚才发出的消息。
运行结果如下所示:
在结果中可以看到
message= youkeda
接入钉钉通知
首先需要一个通知服务应用,利用kafka对接得物应用和通知应用
利用通知服务应用给钉钉系统发布消息
在这个设计中,我们通知服务应用和得物主体应用完全解除耦合,通过kafka消息机制进行通信。
项目步骤
- 接入钉钉通知系统
- 构建通知应用,确定消息模型
- 通知应用接入
kafkaConsumer - 改造订单服务接入
kafkaProducer
编辑
安全设置选择自定义关键词,这个是最简单的方式,只要通知中包括关键词即可。
编辑
选择并拷贝Webhook地址。
接入钉钉
钉钉给我们提供了maven仓库
官网地址文档钉钉开放平台
首先引入Maven依赖
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>alibaba-dingtalk-service-sdk</artifactId>
<version>1.0.1</version>
</dependency>
简单发送消息代码
@RequestMapping("/text")
@ResponseBody
public String sendText(){
// #0. 配置钉钉客户端,dingdingUrl即根据刚才拷贝的Webhook
DingTalkClient client = new DefaultDingTalkClient(this.dingdingUrl);
// #1. request表示整个消息请求
OapiRobotSendRequest request = new OapiRobotSendRequest();
// #2. 请求设置消息类别
request.setMsgtype("text");
// #3. 设置消息内容
OapiRobotSendRequest.Text text = new OapiRobotSendRequest.Text();
text.setContent("得物来新订单了");
request.setText(text);
// #4. 设置钉钉@功能
OapiRobotSendRequest.At at = new OapiRobotSendRequest.At();
at.setIsAtAll(true);
request.setAt(at);
try {
// #5. 发送消息请求
client.execute(request);
} catch (ApiException e) {
e.printStackTrace();
}
return "success";
}
最终结果为
编辑
工程解释
注意DingDingTest.java是我们提供的一个钉钉通知测试类,在测试类中
@Value("${dewu.notify.dingding}")
private String dingdingUrl;
我们利用 SpringBoot 的配置化属性配置了钉钉机器人获取到的Webhook。
这样就唯一标示发送到哪个钉钉群。
钉钉官方文档
构建通知应用
通知模型
- 我们需要支持不同的通知平台
需要一个Enum类型表示不同的通知平台
- 我们需要支持不同的消息类型
需要定义一个MsgType字段
- 通知主体内容
主题、内容、图片、跳转地址等
最终模型如下
对于NotifyType,我们选择String类型,为了方便以后扩展,企业微信不一定满足text,link,markdown三个通知类型,也可能是其他关键词,所以暂时使用字符串。
通知服务设计
1. 首先考虑对接平台的通知
我们初步将对接平台的接口,称为NotifyHelper,那么这个接口有一个发送通知的方法。
2. 完善钉钉通知和企业微信通知
紧接着我们完善钉钉通知和企业微信通知的Helper,uml 图如下所示:
企业微信我们暂时不考虑,只是留在这儿方便以后扩展
3. 接入 Kafka 消费者
我们加入接收 Kafka 消息的监听器NotifyConsumer,利用它完成对Helper的调用。
首先完成DingDingHelper类,完善其中的 4 个方法
// 根据不同的Notify的NotifyType调用不同的方法
public boolean sendNotify(Notify notify);
// 以下三个方法,将之前DingDingTest中写死的参数,替换成Notify中的属性
public boolean sendText(Notify notify);
public boolean sendLink(Notify notify);
public boolean sendMarkdown(Notify notify);
加入Kafka的配置,并且修改NotifyConsumer,将其当做通知消息的消费者。
注意 Kafka 配置如下:
customerGroupId: "dewuNotify"
topic: "notify"
再次提醒大家,需要执行如下步骤
pom.xml文件中添加对应的依赖application.properties中添加 Kafka 消费者配置信息- 完善
NotifyConsumer接入 Kafka 消费者
然后NotifyConsumer,主要完成如下两步:
NotifyConsumer完成对 Kafka 消息的反序列化- 根据平台调用不同的
Helper
如何进行反序列化呢?
可以复习一下 Java 网络编程中的序列化和反序列化
关于序列化和反序列化
实现序列化的必备要求:
只有实现了Serializable或者Externalizable接口的类的对象才能被序列化为字节序列。(不是则会抛出异常)
Fastjson 应用
添加 maven 依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>x.x.x</version>
</dependency>
Fastjson API
定义 Bean
Group.java
public class Group {
private Long id;
private String name;
private List<User> users = new ArrayList<User>();
}
User.java
public class User {
private Long id;
private String name;
}
初始化 Bean
Group group = new Group();
group.setId(0L);
group.setName("admin");
User guestUser = new User();
guestUser.setId(2L);
guestUser.setName("guest");
User rootUser = new User();
rootUser.setId(3L);
rootUser.setName("root");
group.addUser(guestUser);
group.addUser(rootUser);
序列化
String jsonString = JSON.toJSONString(group);``System.out.println(jsonString);
反序列化
Group bean = JSON.parseObject(jsonString, Group.class);
数据流
Kafka Streams到底是个什么东西,用在什么场景,处理什么问题? (本章节我们都偏向理论,当然理解了理论才能更好的实践)
总结成一句话: 它是用来支持流式处理。那到底什么是流式处理呢?
我相信大家看到流的第一反应 --- 水流。
如果脑袋里浮出这样的景象,很赞喔,我们简单将其当做对类似水流形式的处理。
河里面流动的是水,而应用里面流动的是什么呢?当然是数据,我们也可以称之为数据流。
数据流
数据流也被称为事件流,试想一下,对于淘宝网站,哪些东西是数据流?
- 我们在浏览网页时候的浏览数据
- 在搜索商品的搜索数据
- 购买商品数据
- etc...
对于淘宝而言,这些数据是无处不在,源源不断的,这就是数据流。
再抽象一层,数据流有什么特点呢?
1. 无穷的,或者说无边界的
我们之前接触的数据,大多数是有限的,比如某天的访问量,某个季度的数据
无界的表示数据是无限增长的
2. 无处不在的
从上面分析可以知道,网站上发生的一切,都是数据流
3. 有序的
数据的到来总有个先后顺序
4. 不可变的
数据一旦产生,就不能被改变,数据流表示的是某一时刻的事实,时间是无法倒流的
5. 可重播
既然数据是无法改变的,在不改变数据的情况下,结果肯定是固定的
也就是如果将数据重新跑 N 次,结果总是相同的,
流式处理
流式处理是指实时的处理一个或者多个事件流,是一种编程范式。
回顾一下之前由那些处理范式:
请求与响应
API、Service接口,都属于这种范式。由客户端发起请求,然后等待应用响应。
批处理
大多数任务程序都属于这一类比如:
统计每个学生的学习情况,我们系统会在每天凌晨将每个学生当天的学习情况做一个统计,比如学习总时长,代码量等。
为了统计网页访问量,我们系统同样在每天凌晨统计当天的访问次数PV,和当天的访问人数UV。
这些批处理的特定,对时间要求不高,但需要高吞吐量。
流式处理
批处理既高效也经济,但近几年为了及时、高效的做出决策,需要系统能在更短的时间内提供有效的数据。这种批处理范式的高延迟,明显不能满足业务的要求,因此出现了流式处理。
比如网络报警、交易报警、股票数据等。
这些业务是持续性的,他们希望系统能及时给出响应结果,这就是流式处理的应用领域
低延迟、持续性、高吞吐。
基本概念
1.事件时间
指事件或者数据产生的时间,也就是kafka消息的时间
2.处理时间
指事件或者数据流应用处理的时间
3.摄入时间
被处理以后的时间保存到kafka主题的时间。
状态
流式处理中的状态即对每个消息的处理都是相互独立的