第二十三章:Spring AMQP 以及改造项目

本文介绍了如何在Spring Boot应用中整合Spring AMQP和RabbitMQ,实现商品服务的增删改操作触发搜索和静态页服务更新,并详细讲解了监听器、AmqpTemplate的使用以及各服务的交互过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

此博客用于个人学习,来源于网上,对知识点进行一个整理。

1. Spring AMQP:

Spring-amqp 是对 AMQP 协议的抽象实现,而 spring-rabbit 是对协议的具体实现,也是目前的唯一实现,底层使用的就是 RabbitMQ。

1.1 依赖和配置:

添加 AMQP 的启动器:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

在 application.yml 中添加 RabbitMQ 地址:

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

1.2 监听者:

在 SpringAmqp 中,对消息的消费者进行了封装和抽象,一个普通的 JavaBean 中的普通方法,只要通过简单的注解,就可以成为一个消费者。

@Component
public class Listener {

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "spring.test.queue", durable = "true"),
            exchange = @Exchange(
                    value = "spring.test.exchange",
                    ignoreDeclarationExceptions = "true",
                    type = ExchangeTypes.TOPIC
            ),
            key = {"#.#"}))
    public void listen(String msg){
        System.out.println("接收到消息:" + msg);
    }
}
  • @Componet :类上的注解,注册到 Spring 容器
  • @RabbitListener :方法上的注解,声明这个方法是一个消费者方法,需要指定下面的属性:
    • bindings :指定绑定关系,可以有多个。值是 @QueueBinding 的数组。 @QueueBinding 包含下面属性:
      • value :这个消费者关联的队列。值是 @Queue ,代表一个队列
      • exchange :队列所绑定的交换机,值是 @Exchange 类型
      • key :队列和交换机绑定的 RoutingKey

类似 listen 这样的方法在一个类中可以写多个,就代表多个消费者。

1.3 AmqpTemplate:

Spring 为 AMQP 提供了统一的消息处理模板:AmqpTemplate,非常方便的发送消息,其发送方法:

在这里插入图片描述
红框圈起来的是比较常用的3个方法,分别是:

  • 指定交换机、RoutingKey 和消息体
  • 指定消息
  • 指定 RoutingKey 和消息,会向默认的交换机发送消息

1.4 测试代码:

@RunWith(SpringRunner.class)
@SpringBootTest(classes = Application.class)
public class MqDemo {

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Test
    public void testSend() throws InterruptedException {
        String msg = "hello, Spring boot amqp";
        this.amqpTemplate.convertAndSend("spring.test.exchange","a.b", msg);
        // 等待10秒后再结束
        Thread.sleep(10000);
    }
}

2. 项目改造:

接下来,我们就改造项目,实现搜索服务、商品静态页的数据同步。

2.1 思路分析:

  • 发送方:商品微服务

    • 什么时候发?

      当商品服务对商品进行写操作:增、删、改的时候,需要发送一条消息,通知其它服务。

    • 发送什么内容?

      对商品的增删改时其它服务可能需要新的商品数据,但是如果消息内容中包含全部商品信息,数据量太大,而且并不是每个服务都需要全部的信息。因此我们只发送商品id,其它服务可以根据id查询自己需要的信息。

  • 接收方:搜索微服务、静态页微服务

    接收消息后如何处理?

    • 搜索微服务:
      • 增/改:添加新的数据到索引库
      • 删:删除索引库数据
    • 静态页微服务:
      • 增/改:创建新的静态页
      • 删:删除原来的静态页

2.2 商品服务发送消息:

我们先在商品微服务 leyou-item-service 中实现发送消息。

1)引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2)配置文件:

在 application.yml 中添加一些有关 RabbitMQ 的配置:

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou
    template:
      exchange: leyou.item.exchange
    publisher-confirms: true
  • template:有关 AmqpTemplate 的配置
    • exchange:缺省的交换机名称,此处配置后,发送消息如果不指定交换机就会使用这个
  • publisher-confirms:生产者确认机制,确保消息会正确发送,如果发送失败会有错误回执,从而触发重试

3)改造 GoodsService:

在 GoodsService 中封装一个发送消息到mq的方法:(需要注入 AmqpTemplate 模板)

private void sendMsg(String type,Long id) {
    try {
        this.amqpTemplate.convertAndSend("item."+type,id);
    } catch (AmqpException e) {
        e.printStackTrace();
    }
}

这里没有指定交换机,因此默认发送到了配置中的: leyou.item.exchange。

注意:这里要把所有异常都try起来,不能让消息的发送影响到正常的业务逻辑

在新增方法和修改方法中进行调用。

2.3 搜索服务接收消息:

搜索服务接收到消息后要做的事情:

  • 增:添加新的数据到索引库
  • 删:删除索引库数据
  • 改:修改索引库数据

因为索引库的新增和修改方法是合二为一的,因此我们可以将这两类消息一同处理,删除另外处理。

1)引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2)添加配置:

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

这里只是接收消息而不发送,所以不用配置 template 相关内容。

3)编写监听器:

@Component
public class GoodsListener {

    @Autowired
    private SearchService searchService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "LEYOU.SEARCH.SAVE.QUEUE",durable = "true"),
            exchange = @Exchange(value = "LEYOU.ITEM.EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
            key = {"item.insert","item.update"}
    ))
    public void save(Long id) throws IOException {
        if (id == null){
            return;
        }
        this.searchService.save(id);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "LEYOU.SEARCH.DELETE.QUEUE",durable = "true"),
            exchange = @Exchange(value = "LEYOU.ITEM.EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
            key = {"item.delete"}
    ))
    public void delete(Long id) throws IOException {
        if (id == null){
            return;
        }
        this.searchService.delete(id);
    }
}

4)编写创建和删除索引方法:

需要在 SearchService 中拓展两个方法,创建和删除索引:

public void save(Long id) throws IOException{
    Spu spu = this.goodsClient.querySpuById(id);
    Goods goods = this.buildGoods(spu);
    this.goodsRepository.save(goods);
}

public void delete(Long id) {
    this.goodsRepository.deleteById(id);
}

2.4 静态页服务接收消息:

商品静态页服务接收到消息后的处理:

  • 增:创建新的静态页
  • 删:删除原来的静态页
  • 改:创建新的静态页并覆盖原来的

不过,我们编写的创建静态页的方法也具备覆盖以前页面的功能,因此:增和改的消息可以放在一个方法中处理,删除消息放在另一个方法处理。

1)引入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-amqp</artifactId>
</dependency>

2)添加配置:

spring:
  rabbitmq:
    host: 192.168.56.101
    username: leyou
    password: leyou
    virtual-host: /leyou

这里只是接收消息而不发送,所以不用配置 template 相关内容。

3)编写监听器:

@Component
public class GoodsListener {

    @Autowired
    private GoodsHtmlService goodsHtmlService;

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "LEYOU.ITEM.SAVE.QUEUE",durable = "true"),
            exchange = @Exchange(value = "LEYOU.ITEM.EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
            key = {"item.insert","item.update"}
    ))
    public void save(Long id){
        if (id == null){
            return;
        }
        this.goodsHtmlService.createHtml(id);
    }

    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = "LEYOU.ITEM.DETELE.QUEUE",durable = "true"),
            exchange = @Exchange(value = "LEYOU.ITEM.EXCHANGE",ignoreDeclarationExceptions = "true",type = ExchangeTypes.TOPIC),
            key = {"item.delete"}
    ))
    public void delete(Long id){
        if (id == null){
            return;
        }
        this.goodsHtmlService.deleteHtml(id);
    }
}

4)添加删除页面方法:

public void deleteHtml(Long id) {
    File file = new File("D:\\JavaTools\\nginx-1.14.0\\html\\item\\" + id + ".html");
    file.deleteOnExit();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值