1,pom.xml中添加依赖
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka_2.12</artifactId>
<version>2.0.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.kafka/kafka-clients -->
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
<version>2.0.0</version>
</dependency>
2,定时从存入数据库中取出未发布的消息发布
@Value("${kafka.producer.toerp.topic}")
private String topic;
@Resource
private KafkaTemplate<String, String> kafkaTemplate;
@Scheduled(cron = "0/1 * * * * ? ") // 每1秒执行一次
@Override
public void pushEventPublish() {
EventPublish eventPublish = new EventPublish();
eventPublish.setStatus((byte) 0);
Map<String, Object> entityMap = findAll(eventPublish, PageRequest.of(0, Integer.MAX_VALUE));
List<EventPublish> eventList = (List<EventPublish>) entityMap.get("data");
for (EventPublish event : eventList) {
try {
kafkaTemplate.send(topic, JsonMapper.getInstance().toJson(event)).get(10, TimeUnit.SECONDS);
event.setOffset(0);
event.setStatus((byte) 1);
save(event);
} catch (Exception e) {
log.error("{}", e);
}
}
}
3,从kafka中订阅主题消息并存入数据库中
@KafkaListener(topics = "${kafka.producer.fromerp.topic}")
@Override
public void kafkaConsumer(ConsumerRecord<String, Object> record) {
EventProcess eventProcess =
JsonMapper.getInstance().fromJson(record.value().toString(), EventProcess.class);
if (eventProcess == null || eventProcess.getPayLoad() == null) {
return;
}
log.info("payLoad长度:{}", eventProcess.getPayLoad().length());
eventProcess.setStatus((byte) 0);
eventProcess.setOffset(record.offset());
eventProcess.setId(null);
save(eventProcess);
}
4,从数据库中取消息进行处理
@Scheduled(cron = "0/1 * * * * ? ") // 每30秒执行一次
@Override
public void findAllToExecute() {
//TODO
}
5,配置文件
kafka.producer.fromerp.topic=wokuerp//订阅主题
kafka.producer.toerp.topic=wokuweb//发布主题
spring.kafka.bootstrap-servers=ip:9092
spring.kafka.consumer.group-id=web-consumer-group
spring.kafka.consumer.enable-auto-commit=true
Kafka消息保证不丢失和重复消费问题
在数据生产时避免数据丢失的方法:
使用同步模式的时候,有3种状态保证消息被安全生产,在配置为1(只保证写入leader成功)的话,如果刚好leader partition挂了,数据就会丢失。 还有一种情况可能会丢失消息,就是使用异步模式的时候,当缓冲区满了,如果配置为0(还没有收到确认的情况下,缓冲池一满,就清空缓冲池里的消息), 数据就会被立即丢弃掉。 只要能避免上述两种情况,那么就可以保证消息不会被丢失。 就是说在同步模式的时候,确认机制设置为-1,也就是让消息写入leader和所有的副本。 还有,在异步模式下,如果消息发出去了,但还没有收到确认的时候,缓冲池满了,在配置文件中设置成不限制阻塞超时的时间,也就说让生产端一直阻塞,这样也能保证数据不会丢失。 在数据消费时,避免数据丢失的方法:如果使用了storm,要开启storm的ackfail机制;如果没有使用storm,确认数据被完成处理之后,再更新offset值。低级API中需要手动控制offset值。 数据重复消费的情况,如果处理? (1)去重:将消息的唯一标识保存到外部介质中,每次消费处理时判断是否处理过; (2)不管:大数据场景中,报表系统或者日志信息丢失几条都无所谓,不会影响最终的统计分析结果。
如何保证消息队列的高可用啊?
kafka一个最基本的架构认识:多个broker组成,每个broker是一个节点;你创建一个topic,这个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition就放一部分数据。
这就是天然的分布式消息队列,就是说一个topic的数据,是分散放在多个机器上的,每个机器就放一部分数据。
实际上rabbitmq之类的,并不是分布式消息队列,他就是传统的消息队列,只不过提供了一些集群、HA的机制而已,因为无论怎么玩儿,rabbitmq一个queue的数据都是放在一个节点里的,镜像集群下,也是每个节点都放这个queue的完整数据。
kafka 0.8以前,是没有HA机制的,就是任何一个broker宕机了,那个broker上的partition就废了,没法写也没法读,没有什么高可用性可言。
kafka 0.8以后,提供了HA机制,就是replica副本机制。每个partition的数据都会同步到吉他机器上,形成自己的多个replica副本。然后所有replica会选举一个leader出来,那么生产和消费都跟这个leader打交道,然后其他replica就是follower。写的时候,leader会负责把数据同步到所有follower上去,读的时候就直接读leader上数据即可。只能读写leader?很简单,要是你可以随意读写每个follower,那么就要care数据一致性的问题,系统复杂度太高,很容易出问题。kafka会均匀的将一个partition的所有replica分布在不同的机器上,这样才可以提高容错性。