SpringBoot实战项目整合RabbitMQ+ElaticSearch实现SKU上下架功能_尚上优选整合es+mq实现商品上下架

<dependency>
    <groupId>com.atguigu</groupId>
    <artifactId>model</artifactId>
    <version>1.0-SNAPSHOT</version>
    <scope>provided</scope>
</dependency>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    <scope>provided</scope>
</dependency>

<!-- 服务调用feign -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <scope>provided</scope>
</dependency>

4、ProductFeignClient添加定义方法



/**

  • 远程调用其他模块中的api

  • @author Eric

  • @date 2023-06-29 10:04
    */
    @FeignClient(value = “service-product”) //指定调用模块
    public interface ProductFeignClient {

    //根据分类id获取分类信息
    @GetMapping(“/api/product/inner/getCategory/{categoryId}”)
    public Category getCategory(@PathVariable(“categoryId”) Long categoryId);

    //根据skuId获取sku信息
    @GetMapping(“/api/product/inner/getSkuInfo/{skuId}”)
    public SkuInfo getSkuInfo(@PathVariable(“skuId”) Long skuId);

}


### 3.3、开发service-search 模块接口


1、controller



import com.atguigu.ssyx.common.result.Result;
import com.atguigu.ssyx.search.service.SkuService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**

  • 商品搜索列表接口

  • @author Eric

  • @date 2023-06-29 10:15
    */
    @RestController
    @RequestMapping(“api/search/sku”)
    public class SkuApiController {

    @Autowired
    private SkuService skuService;

    @ApiOperation(value = “上架商品”)
    @GetMapping(“inner/upperSku/{skuId}”)
    public Result upperGoods(@PathVariable(“skuId”) Long skuId) {
    skuService.upperSku(skuId);
    return Result.ok();
    }

    @ApiOperation(value = “下架商品”)
    @GetMapping(“inner/lowerSku/{skuId}”)
    public Result lowerGoods(@PathVariable(“skuId”) Long skuId) {
    skuService.lowerSku(skuId);
    return Result.ok();
    }
    }


2、service接口



/**

  • @author Eric

  • @date 2023-06-29 10:16
    /
    public interface SkuService {
    /
    *

    • 上架商品列表
    • @param skuId
      */
      void upperSku(Long skuId);

    /**

    • 下架商品列表
    • @param skuId
      */
      void lowerSku(Long skuId);
      }

3、impl



import com.alibaba.fastjson.JSON;
import com.atguigu.ssyx.enums.SkuType;
import com.atguigu.ssyx.model.product.Category;
import com.atguigu.ssyx.model.product.SkuInfo;
import com.atguigu.ssyx.model.search.SkuEs;
import com.atguigu.ssyx.product.client.ProductFeignClient;
import com.atguigu.ssyx.search.repository.SkuRepository;
import com.atguigu.ssyx.search.service.SkuService;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**

  • @author Eric
  • @date 2023-06-29 10:16
    */

@Slf4j
@Service
public class SkuServiceImpl implements SkuService {

@Autowired
private ProductFeignClient productFeignClient;

@Autowired
private SkuRepository skuEsRepository;

@Autowired
private RestHighLevelClient restHighLevelClient;

/**
 * 上架商品列表
 *
 * @param skuId
 */
@Override
public void upperSku(Long skuId) {
    log.info("upperSku:" + skuId);
    SkuEs skuEs = new SkuEs();

    //查询sku信息
    SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
    if (null == skuInfo) return;

    // 查询分类
    Category category = productFeignClient.getCategory(skuInfo.getCategoryId());
    if (category != null) {
        skuEs.setCategoryId(category.getId());
        skuEs.setCategoryName(category.getName());
    }
    skuEs.setId(skuInfo.getId());
    skuEs.setKeyword(skuInfo.getSkuName() + "," + skuEs.getCategoryName());
    skuEs.setWareId(skuInfo.getWareId());
    skuEs.setIsNewPerson(skuInfo.getIsNewPerson());
    skuEs.setImgUrl(skuInfo.getImgUrl());
    skuEs.setTitle(skuInfo.getSkuName());
    if (skuInfo.getSkuType() == SkuType.COMMON.getCode()) {
        skuEs.setSkuType(0);
        skuEs.setPrice(skuInfo.getPrice().doubleValue());
        skuEs.setStock(skuInfo.getStock());
        skuEs.setSale(skuInfo.getSale());
        skuEs.setPerLimit(skuInfo.getPerLimit());
    } else {
        //TODO 待完善-秒杀商品

    }
    SkuEs save = skuEsRepository.save(skuEs);//往Es中新增数据
    log.info("upperSku:" + JSON.toJSONString(save));
}

/**
 * 下架商品列表
 *
 * @param skuId
 */
@Override
public void lowerSku(Long skuId) {
    this.skuEsRepository.deleteById(skuId);//删除Es中的数据
}

}


4、创建SkuRepository(用来操作Es,这里使用的是SpringData技术)



/**

  • @author Eric
  • @date 2023-06-29 10:19
    */
    // 参数一:泛型 参数二:类型,是由泛型中的主键类型而决定的
    public interface SkuRepository extends ElasticsearchRepository<SkuEs, Long> {
    }

## 4、RabbitMQ


1、创建mq模块  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/21f11ac0e71646e080eab39b05879230.png)


因为mq的作用类似于工具类,并且多处需要使用,所以我这里选择放在common模块下


2、引入MQ依赖



org.springframework.cloud spring-cloud-starter-bus-amqp

3、添加service方法



import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**

  • @author Eric
  • @date 2023-06-30 22:57
    */

@Service
public class RabbitService {

//  引入操作rabbitmq 的模板
@Autowired
private RabbitTemplate rabbitTemplate;

/**
 * 发送消息
 *
 * @param exchange   交换机
 * @param routingKey 路由键(路由key)
 * @param message    消息
 * @return
 */
public boolean sendMessage(String exchange, String routingKey, Object message) {
    //  调用发送数据的方法
    rabbitTemplate.convertAndSend(exchange, routingKey, message);
    return true;
}

/**
 * 发送延迟消息的方法
 *
 * @param exchange   交换机
 * @param routingKey 路由键
 * @param message    消息内容
 * @param delayTime  延迟时间
 * @return
 */
public boolean sendDelayMessage(String exchange, String routingKey, Object message, int delayTime) {

    //  在发送消息的时候设置延迟时间
    rabbitTemplate.convertAndSend(exchange, routingKey, message, new MessagePostProcessor() {
        @Override
        public Message postProcessMessage(Message message) throws AmqpException {
            //  设置一个延迟时间
            message.getMessageProperties().setDelay(delayTime * 1000);
            return message;
        }
    });
    return true;
}

}


4、配置mq消息转换器



import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**

  • mq消息转换器(默认是字符串转换器)
    */

@Configuration
public class MQConfig {

@Bean
public MessageConverter messageConverter(){
    return new Jackson2JsonMessageConverter();
}

}


5、添加消息的确认配置(我这里配置的是手动确认模式)



import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;

@Component
public class MQProducerAckConfig implements RabbitTemplate.ReturnCallback,RabbitTemplate.ConfirmCallback {

//  我们发送消息使用的是 private RabbitTemplate rabbitTemplate; 对象
//  如果不做设置的话 当前的rabbitTemplate 与当前的配置类没有任何关系!
@Autowired
private RabbitTemplate rabbitTemplate;

//  设置 表示修饰一个非静态的void方法,在服务器加载Servlet的时候运行。并且只执行一次!
@PostConstruct
public void init(){
    rabbitTemplate.setReturnCallback(this);
    rabbitTemplate.setConfirmCallback(this);
}

/**
 * 表示消息是否正确发送到了交换机上
 * @param correlationData   消息的载体
 * @param ack   判断是否发送到交换机上
 * @param cause 原因
 */
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
    if(ack){
        System.out.println("消息发送成功!");
    }else {
        System.out.println("消息发送失败!"+cause);
    }
}

/**
 * 消息如果没有正确发送到队列中,则会走这个方法!如果消息被正常处理,则这个方法不会走!
 * @param message
 * @param replyCode
 * @param replyText
 * @param exchange
 * @param routingKey
 */
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
    System.out.println("消息主体: " + new String(message.getBody()));
    System.out.println("应答码: " + replyCode);
    System.out.println("描述:" + replyText);
    System.out.println("消息使用的交换器 exchange : " + exchange);
    System.out.println("消息使用的路由键 routing : " + routingKey);
}

}


6、RabbitMQ常量类



/**

  • 消息队列常量类
    /
    public class MqConst {
    /
    *

    • 消息补偿
      */
      public static final String MQ_KEY_PREFIX = “ssyx.mq:list”;
      public static final int RETRY_COUNT = 3;

    /**

    • 商品上下架
      */
      public static final String EXCHANGE_GOODS_DIRECT = “ssyx.goods.direct”;
      public static final String ROUTING_GOODS_UPPER = “ssyx.goods.upper”;
      public static final String ROUTING_GOODS_LOWER = “ssyx.goods.lower”;
      //队列
      public static final String QUEUE_GOODS_UPPER = “ssyx.goods.upper”;
      public static final String QUEUE_GOODS_LOWER = “ssyx.goods.lower”;

    /**

    • 团长上下线
      */
      public static final String EXCHANGE_LEADER_DIRECT = “ssyx.leader.direct”;
      public static final String ROUTING_LEADER_UPPER = “ssyx.leader.upper”;
      public static final String ROUTING_LEADER_LOWER = “ssyx.leader.lower”;
      //队列
      public static final String QUEUE_LEADER_UPPER = “ssyx.leader.upper”;
      public static final String QUEUE_LEADER_LOWER = “ssyx.leader.lower”;

    //订单
    public static final String EXCHANGE_ORDER_DIRECT = “ssyx.order.direct”;
    public static final String ROUTING_ROLLBACK_STOCK = “ssyx.rollback.stock”;
    public static final String ROUTING_MINUS_STOCK = “ssyx.minus.stock”;

    public static final String ROUTING_DELETE_CART = “ssyx.delete.cart”;
    //解锁普通商品库存
    public static final String QUEUE_ROLLBACK_STOCK = “ssyx.rollback.stock”;
    public static final String QUEUE_SECKILL_ROLLBACK_STOCK = “ssyx.seckill.rollback.stock”;
    public static final String QUEUE_MINUS_STOCK = “ssyx.minus.stock”;
    public static final String QUEUE_DELETE_CART = “ssyx.delete.cart”;

    //支付
    public static final String EXCHANGE_PAY_DIRECT = “ssyx.pay.direct”;
    public static final String ROUTING_PAY_SUCCESS = “ssyx.pay.success”;
    public static final String QUEUE_ORDER_PAY = “ssyx.order.pay”;
    public static final String QUEUE_LEADER_BILL = “ssyx.leader.bill”;

    //取消订单
    public static final String EXCHANGE_CANCEL_ORDER_DIRECT = “ssyx.cancel.order.direct”;
    public static final String ROUTING_CANCEL_ORDER = “ssyx.cancel.order”;
    //延迟取消订单队列
    public static final String QUEUE_CANCEL_ORDER = “ssyx.cancel.order”;

    /**

    • 定时任务
      */
      public static final String EXCHANGE_DIRECT_TASK = “ssyx.exchange.direct.task”;
      public static final String ROUTING_TASK_23 = “ssyx.task.23”;
      //队列
      public static final String QUEUE_TASK_23 = “ssyx.queue.task.23”;
      }

## 5、完善SKU管理商品上下架


### 5.1、商品服务


1、找到自己的商品服务,引入rabbit-util模块



com.atguigu rabbit_util 1.0-SNAPSHOT

2、同时在配置文件添加MQ配置



rabbitmq:
host: 192.168.56.101
port: 5672
username: guest
password: guest
publisher-confirm-type: CORRELATED #发布确认模式,消息是否被成功发送到交换机
publisher-returns: true
listener:
simple:
prefetch: 1
concurrency: 3
acknowledge-mode: manual #消费端手动确认


3、修改SkuInfoServiceImpl的publish方法(上下架方法)



@Autowired
private RabbitService rabbitService;

@Transactional(rollbackFor = {Exception.class})
@Override
public void publish(Long skuId, Integer status) {
// 更改发布状态
if(status == 1) {
SkuInfo skuInfoUp = new SkuInfo();
skuInfoUp.setId(skuId);
skuInfoUp.setPublishStatus(1);
skuInfoMapper.updateById(skuInfoUp);

    //商品上架:发送mq消息同步es
    rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT, MqConst.ROUTING_GOODS_UPPER, skuId);
} else {
    SkuInfo skuInfoUp = new SkuInfo();
    skuInfoUp.setId(skuId);
    skuInfoUp.setPublishStatus(0);
    skuInfoMapper.updateById(skuInfoUp);

    //商品下架:发送mq消息同步es
    rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT, MqConst.ROUTING_GOODS_LOWER, skuId);
}

}


### 5.2、es服务


在service-search服务也引入消息依赖



com.atguigu rabbit_util 1.0-SNAPSHOT

2、添加SkuReceiver接收MQ消息方法



import com.atguigu.ssyx.rabbit.constant.MqConst;
import com.atguigu.ssyx.search.service.SkuService;
import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.io.IOException;

@Component
public class SkuReceiver {

@Autowired
private SkuService skuService;

/**
 * 商品上架
 * RabbitListener  自动监听
 *
 * @param skuId
 * @param message
 * @param channel
 * @throws IOException
 */
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = MqConst.QUEUE_GOODS_UPPER, durable = "true"),
        exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
        key = {MqConst.ROUTING_GOODS_UPPER}
))
public void upperSku(Long skuId, Message message, Channel channel) throws IOException {
    System.out.println("上架:消息消费成功!");
    if (null != skuId) {
        skuService.upperSku(skuId);
    }
    /**
     * 第一个参数:表示收到的消息的标号
     * 第二个参数:如果为true表示可以签收多个消息
     */
    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}

/**
 * 商品下架
 *
 * @param skuId
 */
@RabbitListener(bindings = @QueueBinding(
        value = @Queue(value = MqConst.QUEUE_GOODS_LOWER, durable = "true"),
        exchange = @Exchange(value = MqConst.EXCHANGE_GOODS_DIRECT),
        key = {MqConst.ROUTING_GOODS_LOWER}
))
public void lowerSku(Long skuId, Message message, Channel channel) throws IOException {
    System.out.println("下架:消息消费成功!");
    if (null != skuId) {
        skuService.lowerSku(skuId);
    }
    channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
}

}


## 6、最终测试


测试:需要启动如下服务:  
 1、Nginx  
 2、Naocs  
 3、ElaticSearch  
 4、kibana  
 5、RabbitMQ  
 6、相关微服务模块  
 7、前端服务


我们先使用Kibana查询Es中的数据



GET /_cat/indices?v

POST /skues/_search
{
“query”: {
“match_all”: {}
}
}


发现此时为空  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9b4cfb0d5f394816bc22f2ddb1ff8604.png)  
 此时我们任意选择一个SKU点击上架,上架成功,此时我们去到MQ中查看消息,发现消息发送成功  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/e8a6ce84fb914ffab54c4cf4b095f843.png)  
 注意,我这里能看到消息是因为我在消费前断点了,如果没有断点的话消息是会被立刻消费掉的,  
 此刻,我们再去es中查看,发现sku数据成功新增  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/9bb89b84f2f144aea69330a516263656.png)  
 此时,我们再点击下架。  
 再重新查询,发现es中的sku数据成功删除!  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/22a375c433b4429dacc5596ce08a310c.png)


## 总结


**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
![img](https://img-blog.csdnimg.cn/img_convert/1829687f68e959c093cab1a277bab294.png)
![img](https://img-blog.csdnimg.cn/img_convert/d58b180bd7988ff685a576cacdc7f779.png)
![img](https://img-blog.csdnimg.cn/img_convert/78f1c4d758725981a6103a0ed3c95faf.png)
![img](https://img-blog.csdnimg.cn/img_convert/a137ccae7b9900c57c6ea3939b959151.png)
![img](https://img-blog.csdnimg.cn/img_convert/1be49a6a5e7cd5a9f243b42fde5b67da.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)**
![img](https://img-blog.csdnimg.cn/img_convert/4e0a478ac88570fb163c74e808fb37fc.png)

0c.png)


## 总结


**自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。**

**深知大多数大数据工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!**

**因此收集整理了一份《2024年大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。**
[外链图片转存中...(img-OkjfdVxa-1712862368648)]
[外链图片转存中...(img-RRZtAIqb-1712862368648)]
[外链图片转存中...(img-pEmnMf5z-1712862368649)]
[外链图片转存中...(img-L9uc4Hlp-1712862368649)]
[外链图片转存中...(img-04plGT9P-1712862368650)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上大数据开发知识点,真正体系化!**

**由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**

**如果你觉得这些内容对你有帮助,可以添加VX:vip204888 (备注大数据获取)**
[外链图片转存中...(img-O6kDk4Qj-1712862368650)]

  • 22
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值