文章目录
对于大多数人来说,mq只是听说,知晓一点,但不知道该如何使用的,以下一个简单的mq实战例子
mq的好处有:解耦和,异步处理,流量消峰
mq服务配置
@Configuration
public class MQConfig {
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
}
配置当前RabbitTemplate
@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);
}
}
设置当前RabbitMQ的Service
@Service
public class RabbitService {
@Autowired
private RabbitTemplate rabbitTemplate;
//发送消息的方法
//exchange交换机
//routingKey路由
//message消息
public boolean sendMessage(String exchange,String routingKey,Object message) {
rabbitTemplate.convertAndSend(exchange,routingKey,message);
return true;
}
}
以下一个发送者服务加上mq的配置信息
spring:
rabbitmq:
host: 192.168.159.134
port: 5672
username: guest
password: guest
publisher-confirm-type: CORRELATED #发布确认模式,消息是否被成功发送到交换机
publisher-returns: true
listener:
simple:
prefetch: 1
concurrency: 3
acknowledge-mode: manual #消费端手动确认
//商品上下架
@Override
public void publish(Long skuId, Integer status) {
if(status == 1) { //上架
SkuInfo skuInfo = baseMapper.selectById(skuId);
skuInfo.setPublishStatus(status);
baseMapper.updateById(skuInfo);
//整合mq把数据同步到es里面
rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
MqConst.ROUTING_GOODS_UPPER,
skuId);
} else { //下架
SkuInfo skuInfo = baseMapper.selectById(skuId);
skuInfo.setPublishStatus(status);
baseMapper.updateById(skuInfo);
//整合mq把数据同步到es里面
rabbitService.sendMessage(MqConst.EXCHANGE_GOODS_DIRECT,
MqConst.ROUTING_GOODS_LOWER,
skuId);
}
}
一个简单的上架下架问题
选择商品上架下架发送mq消息到其他服务中,让其他服务接收
接收端
spring:
rabbitmq:
host: 192.168.159.134
port: 5672
username: guest
password: guest
publisher-confirm-type: CORRELATED #发布确认模式,消息是否被成功发送到交换机
publisher-returns: true
listener:
simple:
prefetch: 1
concurrency: 3
acknowledge-mode: manual #消费端手动确认
同样如此配置信息
@Component
public class SkuReceiver {
@Autowired
private SkuService skuService;
//商品上架
@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 {
if(skuId != null) {
//调用方法商品上架
skuService.upperSku(skuId);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
//商品下架
@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 {
if(skuId != null) {
skuService.lowerSku(skuId);
}
channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
}
}
使用监听器监听mq对应的消息队列,路由,路由键是否匹配,如果匹配则成功接收消息
如果消息没有接收成功,则是手动确认没有收到。
设置ES配置
spring:
elasticsearch:
rest:
uris: http://192.168.159.134:9200
设置ES接口,可以使用该接口设置ES的简单增删改查
public interface SkuRepository extends ElasticsearchRepository<SkuEs,Long> {}
//上架
@Override
public void upperSku(Long skuId) {
//1 通过远程调用 ,根据skuid获取相关信息
SkuInfo skuInfo = productFeignClient.getSkuInfo(skuId);
if(skuInfo == null) {
return;
}
Category category = productFeignClient.getCategory(skuInfo.getCategoryId());
//2 获取数据封装SkuEs对象
SkuEs skuEs = new SkuEs();
//封装分类
if(category != null) {
skuEs.setCategoryId(category.getId());
skuEs.setCategoryName(category.getName());
}
//封装sku信息部分
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());
}
//3 调用方法添加ES
skuRepository.save(skuEs);
}
//下架
@Override
public void lowerSku(Long skuId) {
skuRepository.deleteById(skuId);
}
使用上架方法从Kibana查看ES数据是否加入成功
POST /skues/_search
{
"query": {
"match_all": {}
}
}
{
"took" : 11,
"timed_out" : false,
"_shards" : {
"total" : 3,
"successful" : 3,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "skues",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"_class" : "com.atguigu.ssyx.model.search.SkuEs",
"id" : 1,
"keyword" : "西红柿,新鲜蔬菜",
"skuType" : 0,
"isNewPerson" : 0,
"categoryId" : 1,
"categoryName" : "新鲜蔬菜",
"imgUrl" : "http://47.93.148.192:9000/gmall/20210817/微信图片_2021081711240720.jpg",
"title" : "西红柿",
"price" : 2.2,
"stock" : 100,
"perLimit" : 5,
"sale" : 0,
"wareId" : 1,
"hotScore" : 0
}
}
]
}
}
成功插入数据,一个简单的MQ+ES实战