• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

黑马头条:kafka和异步通知文章上下架

武飞扬头像
蛋饼吧
帮助2

本笔记内容为黑马头条项目的kafka及异步通知文章上下架部分

目录

一、kafka概述

二、kafka安装配置

三、kafka入门

四、kafka高可用设计

1、集群

2、备份机制(Replication)

五、kafka生产者详解

1、发送类型

2、参数详解

六、kafka消费者详解

1、消费者组

2、消息有序性

3、提交和偏移量

七、springboot集成kafka

1、入门

2、传递消息为对象

八、自媒体文章上下架功能完成

1、需求分析

2、流程说明

3、接口定义

4、自媒体文章上下架-功能实现

5、消息通知article端文章上下架


一、kafka概述


消息中间件对比

特性 ActiveMQ RabbitMQ RocketMQ Kafka
开发语言 java erlang java scala
单机吞吐量 万级 万级 10万级 100万级
时效性 ms us ms ms级以内
可用性 高(主从) 高(主从) 非常高(分布式) 非常高(分布式)
功能特性 成熟的产品、较全的文档、各种协议支持好 并发能力强、性能好、延迟低 MQ功能比较完善,扩展性佳 只支持主要的MQ功能,主要应用于大数据领域

消息中间件对比-选择建议

消息中间件 建议
Kafka 追求高吞吐量,适合产生大量数据的互联网服务的数据收集业务
RocketMQ 可靠性要求很高的金融互联网领域,稳定性高,经历了多次阿里双11考验
RabbitMQ 性能较好,社区活跃度高,数据量没有那么大,优先选择功能比较完备的RabbitMQ

kafka介绍

Kafka 是一个分布式流媒体平台,类似于消息队列或企业消息传递系统。kafka官网:Apache Kafka 学新通 

kafka介绍-名词解释

学新通

  • producer:发布消息的对象称之为主题生产者(Kafka topic producer)

  • topic:Kafka将消息分门别类,每一类的消息称之为一个主题(Topic)

  • consumer:订阅消息并处理发布的消息的对象称之为主题消费者(consumers)

  • broker:已发布的消息保存在一组服务器中,称之为Kafka集群。集群中的每一个服务器都是一个代理(Broker)。 消费者可以订阅一个或多个主题(topic),并从Broker拉数据,从而消费这些已发布的消息。

二、kafka安装配置


Kafka对于zookeeper是强依赖,保存kafka相关的节点数据,所以安装Kafka之前必须先安装zookeeper

Docker安装zookeeper

下载镜像:

docker pull zookeeper:3.4.14

创建容器

docker run -d --name zookeeper -p 2181:2181 zookeeper:3.4.14

Docker安装kafka

下载镜像:  

docker pull wurstmeister/kafka:2.12-2.3.1

创建容器         

  1.  
    docker run -d --name kafka \
  2.  
    --env KAFKA_ADVERTISED_HOST_NAME=192.168.200.130 \
  3.  
    --env KAFKA_ZOOKEEPER_CONNECT=192.168.200.130:2181 \
  4.  
    --env KAFKA_ADVERTISED_LISTENERS=PLAINTEXT://192.168.200.130:9092 \
  5.  
    --env KAFKA_LISTENERS=PLAINTEXT://0.0.0.0:9092 \
  6.  
    --env KAFKA_HEAP_OPTS="-Xmx256M -Xms256M" \
  7.  
    --net=host wurstmeister/kafka:2.12-2.3.1

三、kafka入门


学新通

  • 生产者发送消息,多个消费者只能有一个消费者接收到消息

  • 生产者发送消息,多个消费者都可以接收到消息

(1)创建kafka-demo项目,导入依赖

  1.  
    <dependency>
  2.  
    <groupId>org.apache.kafka</groupId>
  3.  
    <artifactId>kafka-clients</artifactId>
  4.  
    </dependency>

(2)生产者发送消息

  1.  
    package com.heima.kafka.sample;
  2.  
     
  3.  
    import org.apache.kafka.clients.producer.KafkaProducer;
  4.  
    import org.apache.kafka.clients.producer.ProducerConfig;
  5.  
    import org.apache.kafka.clients.producer.ProducerRecord;
  6.  
     
  7.  
    import java.util.Properties;
  8.  
     
  9.  
    /**
  10.  
    * 生产者
  11.  
    */
  12.  
    public class ProducerQuickStart {
  13.  
     
  14.  
    public static void main(String[] args) {
  15.  
    //1.kafka的配置信息
  16.  
    Properties properties = new Properties();
  17.  
    //kafka的连接地址
  18.  
    properties.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG,"192.168.200.130:9092");
  19.  
    //发送失败,失败的重试次数
  20.  
    properties.put(ProducerConfig.RETRIES_CONFIG,5);
  21.  
    //消息key的序列化器
  22.  
    properties.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
  23.  
    //消息value的序列化器
  24.  
    properties.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG,"org.apache.kafka.common.serialization.StringSerializer");
  25.  
     
  26.  
    //2.生产者对象
  27.  
    KafkaProducer<String,String> producer = new KafkaProducer<String, String>(properties);
  28.  
     
  29.  
    //封装发送的消息
  30.  
    ProducerRecord<String,String> record = new ProducerRecord<String, String>("itheima-topic","100001","hello kafka");
  31.  
     
  32.  
    //3.发送消息
  33.  
    producer.send(record);
  34.  
     
  35.  
    //4.关闭消息通道,必须关闭,否则消息发送不成功
  36.  
    producer.close();
  37.  
    }
  38.  
     
  39.  
    }
学新通

(3)消费者接收消息

  1.  
    package com.heima.kafka.sample;
  2.  
     
  3.  
    import org.apache.kafka.clients.consumer.ConsumerConfig;
  4.  
    import org.apache.kafka.clients.consumer.ConsumerRecord;
  5.  
    import org.apache.kafka.clients.consumer.ConsumerRecords;
  6.  
    import org.apache.kafka.clients.consumer.KafkaConsumer;
  7.  
     
  8.  
    import java.time.Duration;
  9.  
    import java.util.Collections;
  10.  
    import java.util.Properties;
  11.  
     
  12.  
    /**
  13.  
    * 消费者
  14.  
    */
  15.  
    public class ConsumerQuickStart {
  16.  
     
  17.  
    public static void main(String[] args) {
  18.  
    //1.添加kafka的配置信息
  19.  
    Properties properties = new Properties();
  20.  
    //kafka的连接地址
  21.  
    properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, "192.168.200.130:9092");
  22.  
    //消费者组
  23.  
    properties.put(ConsumerConfig.GROUP_ID_CONFIG, "group2");
  24.  
    //消息的反序列化器
  25.  
    properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
  26.  
    properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, "org.apache.kafka.common.serialization.StringDeserializer");
  27.  
     
  28.  
    //2.消费者对象
  29.  
    KafkaConsumer<String, String> consumer = new KafkaConsumer<String, String>(properties);
  30.  
     
  31.  
    //3.订阅主题
  32.  
    consumer.subscribe(Collections.singletonList("itheima-topic"));
  33.  
     
  34.  
    //当前线程一直处于监听状态
  35.  
    while (true) {
  36.  
    //4.获取消息
  37.  
    ConsumerRecords<String, String> consumerRecords = consumer.poll(Duration.ofMillis(1000));
  38.  
    for (ConsumerRecord<String, String> consumerRecord : consumerRecords) {
  39.  
    System.out.println(consumerRecord.key());
  40.  
    System.out.println(consumerRecord.value());
  41.  
    }
  42.  
    }
  43.  
     
  44.  
    }
  45.  
     
  46.  
    }
学新通

总结

  • 生产者发送消息,多个消费者订阅同一个主题,只能有一个消费者收到消息(一对一)

  • 生产者发送消息,多个消费者订阅同一个主题,所有消费者都能收到消息(一对多)

四、kafka高可用设计


1、集群

学新通

  • Kafka 的服务器端由被称为 Broker 的服务进程构成,即一个 Kafka 集群由多个 Broker 组成

  • 这样如果集群中某一台机器宕机,其他机器上的 Broker 也依然能够对外提供服务。这其实就是 Kafka 提供高可用的手段之一

2、备份机制(Replication)

学新通

Kafka 中消息的备份又叫做 副本(Replica)

Kafka 定义了两类副本:

  • 领导者副本(Leader Replica)

  • 追随者副本(Follower Replica)

同步方式

学新通

ISR(in-sync replica)需要同步复制保存的follower

如果leader失效后,需要选出新的leader,选举的原则如下:

第一:选举时优先从ISR中选定,因为这个列表中follower的数据是与leader同步的

第二:如果ISR列表中的follower都不行了,就只能从其他follower中选取

极端情况,就是所有副本都失效了,这时有两种方案

第一:等待ISR中的一个活过来,选为Leader,数据可靠,但活过来的时间不确定

第二:选择第一个活过来的Replication,不一定是ISR中的,选为leader,以最快速度恢复可用性,但数据不一定完整

五、kafka生产者详解


1、发送类型

同步发送

使用send()方法发送,它会返回一个Future对象,调用get()方法进行等待,就可以知道消息是否发送成功

  1.  
    RecordMetadata recordMetadata = producer.send(kvProducerRecord).get();
  2.  
    System.out.println(recordMetadata.offset());

异步发送

调用send()方法,并指定一个回调函数,服务器在返回响应时调用函数

  1.  
    //异步消息发送
  2.  
    producer.send(kvProducerRecord, new Callback() {
  3.  
    @Override
  4.  
    public void onCompletion(RecordMetadata recordMetadata, Exception e) {
  5.  
    if(e != null){
  6.  
    System.out.println("记录异常信息到日志表中");
  7.  
    }
  8.  
    System.out.println(recordMetadata.offset());
  9.  
    }
  10.  
    });

2、参数详解

ack

学新通

代码的配置方式:

  1.  
    //ack配置 消息确认机制
  2.  
    prop.put(ProducerConfig.ACKS_CONFIG,"all");

参数的选择说明

确认机制 说明
acks=0 生产者在成功写入消息之前不会等待任何来自服务器的响应,消息有丢失的风险,但是速度最快
acks=1(默认值) 只要集群首领节点收到消息,生产者就会收到一个来自服务器的成功响应
acks=all 只有当所有参与赋值的节点全部收到消息时,生产者才会收到一个来自服务器的成功响应

retries  

学新通

生产者从服务器收到的错误有可能是临时性错误,在这种情况下,retries参数的值决定了生产者可以重发消息的次数,如果达到这个次数,生产者会放弃重试返回错误,默认情况下,生产者会在每次重试之间等待100ms

代码中配置方式:

  1.  
    //重试次数
  2.  
    prop.put(ProducerConfig.RETRIES_CONFIG,10);

消息压缩

默认情况下, 消息发送时不会被压缩。

代码中配置方式:

  1.  
    //数据压缩
  2.  
    prop.put(ProducerConfig.COMPRESSION_TYPE_CONFIG,"lz4");
压缩算法 说明
snappy 占用较少的 CPU, 却能提供较好的性能和相当可观的压缩比, 如果看重性能和网络带宽,建议采用
lz4 占用较少的 CPU, 压缩和解压缩速度较快,压缩比也很客观
gzip 占用较多的 CPU,但会提供更高的压缩比,网络带宽有限,可以使用这种算法

使用压缩可以降低网络传输开销和存储开销,而这往往是向 Kafka 发送消息的瓶颈所在。  

六、kafka消费者详解


1、消费者组

学新通

  • 消费者组(Consumer Group) :指的就是由一个或多个消费者组成的群体

  • 一个发布在Topic上消息被分发给此消费者组中的一个消费者

    • 所有的消费者都在一个组中,那么这就变成了queue模型

    • 所有的消费者都在不同的组中,那么就完全变成了发布-订阅模型

2、消息有序性

应用场景:

  • 即时消息中的单对单聊天和群聊,保证发送方消息发送顺序与接收方的顺序一致

  • 充值转账两个渠道在同一个时间进行余额变更,短信通知必须要有顺序

学新通topic分区中消息只能由消费者组中的唯一个一个地个消费者处理,所以消息肯定是按照先后顺序进行处理的。但是它也仅仅是保证Topic的一个分区顺序处理,不能保证跨分区的消息先后处理顺序。 所以,如果你想要顺序的处理Topic的所有消息,那就只提供一个分区。

3、提交和偏移量

kafka不会像其他JMS队列那样需要得到消费者的确认,消费者可以使用kafka来追踪消息在分区的位置(偏移量)

消费者会往一个叫做_consumer_offset的特殊主题发送消息,消息里包含了每个分区的偏移量。如果消费者发生崩溃或有新的消费者加入群组,就会触发再均衡

学新通

正常的情况

学新通

如果消费者2挂掉以后,会发生再均衡,消费者2负责的分区会被其他消费者进行消费

再均衡后不可避免会出现一些问题

问题一:

学新通

如果提交偏移量小于客户端处理的最后一个消息的偏移量,那么处于两个偏移量之间的消息就会被重复处理。

问题二:

学新通

如果提交的偏移量大于客户端的最后一个消息的偏移量,那么处于两个偏移量之间的消息将会丢失。

如果想要解决这些问题,还要知道目前kafka提交偏移量的方式:

提交偏移量的方式有两种,分别是自动提交偏移量和手动提交

  • 自动提交偏移量

当enable.auto.commit被设置为true,提交方式就是让消费者自动提交偏移量,每隔5秒消费者会自动把从poll()方法接收的最大偏移量提交上去

  • 手动提交 ,当enable.auto.commit被设置为false可以有以下三种提交方式
    • 提交当前偏移量(同步提交)

    • 异步提交

    • 同步和异步组合提交

1.提交当前偏移量(同步提交)  

enable.auto.commit设置为false,让应用程序决定何时提交偏移量。使用commitSync()提交偏移量,commitSync()将会提交poll返回的最新的偏移量,所以在处理完所有记录后要确保调用了commitSync()方法。否则还是会有消息丢失的风险。

只要没有发生不可恢复的错误,commitSync()方法会一直尝试直至提交成功,如果提交失败也可以记录到错误日志里。

  1.  
    while (true){
  2.  
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
  3.  
    for (ConsumerRecord<String, String> record : records) {
  4.  
    System.out.println(record.value());
  5.  
    System.out.println(record.key());
  6.  
    try {
  7.  
    consumer.commitSync();//同步提交当前最新的偏移量
  8.  
    }catch (CommitFailedException e){
  9.  
    System.out.println("记录提交失败的异常:" e);
  10.  
    }
  11.  
     
  12.  
    }
  13.  
    }

2.异步提交

手动提交有一个缺点,那就是当发起提交调用时应用会阻塞。当然我们可以减少手动提交的频率,但这个会增加消息重复的概率(和自动提交一样)。另外一个解决办法是,使用异步提交的API。

  1.  
    while (true){
  2.  
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
  3.  
    for (ConsumerRecord<String, String> record : records) {
  4.  
    System.out.println(record.value());
  5.  
    System.out.println(record.key());
  6.  
    }
  7.  
    consumer.commitAsync(new OffsetCommitCallback() {
  8.  
    @Override
  9.  
    public void onComplete(Map<TopicPartition, OffsetAndMetadata> map, Exception e) {
  10.  
    if(e!=null){
  11.  
    System.out.println("记录错误的提交偏移量:" map ",异常信息" e);
  12.  
    }
  13.  
    }
  14.  
    });
  15.  
    }
学新通

3.同步和异步组合提交

异步提交也有个缺点,那就是如果服务器返回提交失败,异步提交不会进行重试。相比较起来,同步提交会进行重试直到成功或者最后抛出异常给应用。异步提交没有实现重试是因为,如果同时存在多个异步提交,进行重试可能会导致位移覆盖。

举个例子,假如我们发起了一个异步提交commitA,此时的提交位移为2000,随后又发起了一个异步提交commitB且位移为3000;commitA提交失败但commitB提交成功,此时commitA进行重试并成功的话,会将实际上将已经提交的位移从3000回滚到2000,导致消息重复消费。

  1.  
    try {
  2.  
    while (true){
  3.  
    ConsumerRecords<String, String> records = consumer.poll(Duration.ofMillis(1000));
  4.  
    for (ConsumerRecord<String, String> record : records) {
  5.  
    System.out.println(record.value());
  6.  
    System.out.println(record.key());
  7.  
    }
  8.  
    consumer.commitAsync();
  9.  
    }
  10.  
    }catch (Exception e){
  11.  
    e.printStackTrace();
  12.  
    System.out.println("记录错误信息:" e);
  13.  
    }finally {
  14.  
    try {
  15.  
    consumer.commitSync();
  16.  
    }finally {
  17.  
    consumer.close();
  18.  
    }
  19.  
    }
学新通

七、springboot集成kafka


1、入门

1.导入spring-kafka依赖信息

  1.  
    <dependencies>
  2.  
    <dependency>
  3.  
    <groupId>org.springframework.boot</groupId>
  4.  
    <artifactId>spring-boot-starter-web</artifactId>
  5.  
    </dependency>
  6.  
    <!-- kafkfa -->
  7.  
    <dependency>
  8.  
    <groupId>org.springframework.kafka</groupId>
  9.  
    <artifactId>spring-kafka</artifactId>
  10.  
    <exclusions>
  11.  
    <exclusion>
  12.  
    <groupId>org.apache.kafka</groupId>
  13.  
    <artifactId>kafka-clients</artifactId>
  14.  
    </exclusion>
  15.  
    </exclusions>
  16.  
    </dependency>
  17.  
    <dependency>
  18.  
    <groupId>org.apache.kafka</groupId>
  19.  
    <artifactId>kafka-clients</artifactId>
  20.  
    </dependency>
  21.  
    <dependency>
  22.  
    <groupId>com.alibaba</groupId>
  23.  
    <artifactId>fastjson</artifactId>
  24.  
    </dependency>
  25.  
    </dependencies>
学新通

2.在resources下创建文件application.yml

  1.  
    server:
  2.  
    port: 9991
  3.  
    spring:
  4.  
    application:
  5.  
    name: kafka-demo
  6.  
    kafka:
  7.  
    bootstrap-servers: 192.168.200.130:9092
  8.  
    producer:
  9.  
    retries: 10
  10.  
    key-serializer: org.apache.kafka.common.serialization.StringSerializer
  11.  
    value-serializer: org.apache.kafka.common.serialization.StringSerializer
  12.  
    consumer:
  13.  
    group-id: ${spring.application.name}-test
  14.  
    key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
  15.  
    value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
学新通

3.消息生产者

  1.  
    package com.heima.kafka.controller;
  2.  
     
  3.  
    import org.springframework.beans.factory.annotation.Autowired;
  4.  
    import org.springframework.kafka.core.KafkaTemplate;
  5.  
    import org.springframework.web.bind.annotation.GetMapping;
  6.  
    import org.springframework.web.bind.annotation.RestController;
  7.  
     
  8.  
    @RestController
  9.  
    public class HelloController {
  10.  
     
  11.  
    @Autowired
  12.  
    private KafkaTemplate<String,String> kafkaTemplate;
  13.  
     
  14.  
    @GetMapping("/hello")
  15.  
    public String hello(){
  16.  
    kafkaTemplate.send("itcast-topic","黑马程序员");
  17.  
    return "ok";
  18.  
    }
  19.  
    }
学新通

4.消息消费者

  1.  
    package com.heima.kafka.listener;
  2.  
     
  3.  
    import org.springframework.kafka.annotation.KafkaListener;
  4.  
    import org.springframework.stereotype.Component;
  5.  
    import org.springframework.util.StringUtils;
  6.  
     
  7.  
    @Component
  8.  
    public class HelloListener {
  9.  
     
  10.  
    @KafkaListener(topics = "itcast-topic")
  11.  
    public void onMessage(String message){
  12.  
    if(!StringUtils.isEmpty(message)){
  13.  
    System.out.println(message);
  14.  
    }
  15.  
     
  16.  
    }
  17.  
    }
学新通

2、传递消息为对象

目前springboot整合后的kafka,因为序列化器是StringSerializer,这个时候如果需要传递对象可以有两种方式

方式一:可以自定义序列化器,对象类型众多,这种方式通用性不强,本章节不介绍

方式二:可以把要传递的对象进行转json字符串,接收消息后再转为对象即可,本项目采用这种方式

发送消息

  1.  
    @GetMapping("/hello")
  2.  
    public String hello(){
  3.  
    User user = new User();
  4.  
    user.setUsername("xiaowang");
  5.  
    user.setAge(18);
  6.  
     
  7.  
    kafkaTemplate.send("user-topic", JSON.toJSONString(user));
  8.  
     
  9.  
    return "ok";
  10.  
    }

接收消息

  1.  
    package com.heima.kafka.listener;
  2.  
     
  3.  
    import com.alibaba.fastjson.JSON;
  4.  
    import com.heima.kafka.pojo.User;
  5.  
    import org.springframework.kafka.annotation.KafkaListener;
  6.  
    import org.springframework.stereotype.Component;
  7.  
    import org.springframework.util.StringUtils;
  8.  
     
  9.  
    @Component
  10.  
    public class HelloListener {
  11.  
     
  12.  
    @KafkaListener(topics = "user-topic")
  13.  
    public void onMessage(String message){
  14.  
    if(!StringUtils.isEmpty(message)){
  15.  
    User user = JSON.parseObject(message, User.class);
  16.  
    System.out.println(user);
  17.  
    }
  18.  
     
  19.  
    }
  20.  
    }
学新通

八、自媒体文章上下架功能完成


1、需求分析

学新通

  • 已发表且已上架的文章可以下架

  • 已发表且已下架的文章可以上架

2、流程说明

学新通

3、接口定义

说明
接口路径 /api/v1/news/down_or_up
请求方式 POST
参数 DTO
响应结果 ResponseResult

DTO

  1.  
    @Data
  2.  
    public class WmNewsDto {
  3.  
     
  4.  
    private Integer id;
  5.  
    /**
  6.  
    * 是否上架 0 下架 1 上架
  7.  
    */
  8.  
    private Short enable;
  9.  
     
  10.  
    }

ResponseResult

学新通

4、自媒体文章上下架-功能实现

1.接口定义

在heima-leadnews-wemedia工程下的WmNewsController新增方法

  1.  
    @PostMapping("/down_or_up")
  2.  
    public ResponseResult downOrUp(@RequestBody WmNewsDto dto){
  3.  
    return null;
  4.  
    }

在WmNewsDto中新增enable属性 ,完整的代码如下:

  1.  
    package com.heima.model.wemedia.dtos;
  2.  
     
  3.  
    import lombok.Data;
  4.  
     
  5.  
    import java.util.Date;
  6.  
    import java.util.List;
  7.  
     
  8.  
    @Data
  9.  
    public class WmNewsDto {
  10.  
     
  11.  
    private Integer id;
  12.  
    /**
  13.  
    * 标题
  14.  
    */
  15.  
    private String title;
  16.  
    /**
  17.  
    * 频道id
  18.  
    */
  19.  
    private Integer channelId;
  20.  
    /**
  21.  
    * 标签
  22.  
    */
  23.  
    private String labels;
  24.  
    /**
  25.  
    * 发布时间
  26.  
    */
  27.  
    private Date publishTime;
  28.  
    /**
  29.  
    * 文章内容
  30.  
    */
  31.  
    private String content;
  32.  
    /**
  33.  
    * 文章封面类型 0 无图 1 单图 3 多图 -1 自动
  34.  
    */
  35.  
    private Short type;
  36.  
    /**
  37.  
    * 提交时间
  38.  
    */
  39.  
    private Date submitedTime;
  40.  
    /**
  41.  
    * 状态 提交为1 草稿为0
  42.  
    */
  43.  
    private Short status;
  44.  
     
  45.  
    /**
  46.  
    * 封面图片列表 多张图以逗号隔开
  47.  
    */
  48.  
    private List<String> images;
  49.  
     
  50.  
    /**
  51.  
    * 上下架 0 下架 1 上架
  52.  
    */
  53.  
    private Short enable;
  54.  
    }
学新通

2.业务层编写

在WmNewsService新增方法

  1.  
    /**
  2.  
    * 文章的上下架
  3.  
    * @param dto
  4.  
    * @return
  5.  
    */
  6.  
    public ResponseResult downOrUp(WmNewsDto dto);

实现方法

  1.  
    /**
  2.  
    * 文章的上下架
  3.  
    * @param dto
  4.  
    * @return
  5.  
    */
  6.  
    @Override
  7.  
    public ResponseResult downOrUp(WmNewsDto dto) {
  8.  
    //1.检查参数
  9.  
    if(dto.getId() == null){
  10.  
    return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
  11.  
    }
  12.  
     
  13.  
    //2.查询文章
  14.  
    WmNews wmNews = getById(dto.getId());
  15.  
    if(wmNews == null){
  16.  
    return ResponseResult.errorResult(AppHttpCodeEnum.DATA_NOT_EXIST,"文章不存在");
  17.  
    }
  18.  
     
  19.  
    //3.判断文章是否已发布
  20.  
    if(!wmNews.getStatus().equals(WmNews.Status.PUBLISHED.getCode())){
  21.  
    return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID,"当前文章不是发布状态,不能上下架");
  22.  
    }
  23.  
     
  24.  
    //4.修改文章enable
  25.  
    if(dto.getEnable() != null && dto.getEnable() > -1 && dto.getEnable() < 2){
  26.  
    update(Wrappers.<WmNews>lambdaUpdate().set(WmNews::getEnable,dto.getEnable())
  27.  
    .eq(WmNews::getId,wmNews.getId()));
  28.  
    }
  29.  
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
  30.  
    }
学新通

3.控制器

  1.  
    @PostMapping("/down_or_up")
  2.  
    public ResponseResult downOrUp(@RequestBody WmNewsDto dto){
  3.  
    return wmNewsService.downOrUp(dto);
  4.  
    }

测试

5、消息通知article端文章上下架

1.在heima-leadnews-common模块下导入kafka依赖

  1.  
    <!-- kafkfa -->
  2.  
    <dependency>
  3.  
    <groupId>org.springframework.kafka</groupId>
  4.  
    <artifactId>spring-kafka</artifactId>
  5.  
    </dependency>
  6.  
    <dependency>
  7.  
    <groupId>org.apache.kafka</groupId>
  8.  
    <artifactId>kafka-clients</artifactId>
  9.  
    </dependency>

2.在自媒体端的nacos配置中心配置kafka的生产者

  1.  
    spring:
  2.  
    kafka:
  3.  
    bootstrap-servers: 192.168.200.130:9092
  4.  
    producer:
  5.  
    retries: 10
  6.  
    key-serializer: org.apache.kafka.common.serialization.StringSerializer
  7.  
    value-serializer: org.apache.kafka.common.serialization.StringSerializer

3.在自媒体端文章上下架后发送消息

  1.  
    //发送消息,通知article端修改文章配置
  2.  
    if(wmNews.getArticleId() != null){
  3.  
    Map<String,Object> map = new HashMap<>();
  4.  
    map.put("articleId",wmNews.getArticleId());
  5.  
    map.put("enable",dto.getEnable());
  6.  
    kafkaTemplate.send(WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC,JSON.toJSONString(map));
  7.  
    }

常量类:

  1.  
    public class WmNewsMessageConstants {
  2.  
     
  3.  
    public static final String WM_NEWS_UP_OR_DOWN_TOPIC="wm.news.up.or.down.topic";
  4.  
    }

4.在article端的nacos配置中心配置kafka的消费者

  1.  
    spring:
  2.  
    kafka:
  3.  
    bootstrap-servers: 192.168.200.130:9092
  4.  
    consumer:
  5.  
    group-id: ${spring.application.name}
  6.  
    key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
  7.  
    value-deserializer: org.apache.kafka.common.serialization.StringDeserializer

5.在article端编写监听,接收数据

  1.  
    package com.heima.article.listener;
  2.  
     
  3.  
    import com.alibaba.fastjson.JSON;
  4.  
    import com.heima.article.service.ApArticleConfigService;
  5.  
    import lombok.extern.slf4j.Slf4j;
  6.  
    import org.apache.commons.lang3.StringUtils;
  7.  
    import org.springframework.beans.factory.annotation.Autowired;
  8.  
    import org.springframework.kafka.annotation.KafkaListener;
  9.  
    import org.springframework.stereotype.Component;
  10.  
     
  11.  
    import java.util.Map;
  12.  
     
  13.  
    @Component
  14.  
    @Slf4j
  15.  
    public class ArtilceIsDownListener {
  16.  
     
  17.  
    @Autowired
  18.  
    private ApArticleConfigService apArticleConfigService;
  19.  
     
  20.  
    @KafkaListener(topics = WmNewsMessageConstants.WM_NEWS_UP_OR_DOWN_TOPIC)
  21.  
    public void onMessage(String message){
  22.  
    if(StringUtils.isNotBlank(message)){
  23.  
    Map map = JSON.parseObject(message, Map.class);
  24.  
    apArticleConfigService.updateByMap(map);
  25.  
    log.info("article端文章配置修改,articleId={}",map.get("articleId"));
  26.  
    }
  27.  
    }
  28.  
    }
学新通

6.修改ap_article_config表的数据

新建ApArticleConfigService

  1.  
    package com.heima.article.service;
  2.  
     
  3.  
    import com.baomidou.mybatisplus.extension.service.IService;
  4.  
    import com.heima.model.article.pojos.ApArticleConfig;
  5.  
     
  6.  
    import java.util.Map;
  7.  
     
  8.  
    public interface ApArticleConfigService extends IService<ApArticleConfig> {
  9.  
     
  10.  
    /**
  11.  
    * 修改文章配置
  12.  
    * @param map
  13.  
    */
  14.  
    public void updateByMap(Map map);
  15.  
    }
学新通

实现类:

  1.  
    package com.heima.article.service.impl;
  2.  
     
  3.  
    import com.baomidou.mybatisplus.core.toolkit.Wrappers;
  4.  
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
  5.  
    import com.heima.article.mapper.ApArticleConfigMapper;
  6.  
    import com.heima.article.service.ApArticleConfigService;
  7.  
    import com.heima.model.article.pojos.ApArticleConfig;
  8.  
    import lombok.extern.slf4j.Slf4j;
  9.  
    import org.springframework.stereotype.Service;
  10.  
    import org.springframework.transaction.annotation.Transactional;
  11.  
     
  12.  
    import java.util.Map;
  13.  
     
  14.  
    @Service
  15.  
    @Slf4j
  16.  
    @Transactional
  17.  
    public class ApArticleConfigServiceImpl extends ServiceImpl<ApArticleConfigMapper, ApArticleConfig> implements ApArticleConfigService {
  18.  
     
  19.  
     
  20.  
    /**
  21.  
    * 修改文章配置
  22.  
    * @param map
  23.  
    */
  24.  
    @Override
  25.  
    public void updateByMap(Map map) {
  26.  
    //0 下架 1 上架
  27.  
    Object enable = map.get("enable");
  28.  
    boolean isDown = true;
  29.  
    if(enable.equals(1)){
  30.  
    isDown = false;
  31.  
    }
  32.  
    //修改文章配置
  33.  
    update(Wrappers.<ApArticleConfig>lambdaUpdate().eq(ApArticleConfig::getArticleId,map.get("articleId")).set(ApArticleConfig::getIsDown,isDown));
  34.  
     
  35.  
    }
  36.  
    }
学新通

结束!

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhiaagbe
系列文章
更多 icon
同类精品
更多 icon
继续加载