Dubbo远程调用,属于同步调用,在调用后还在等待被调用方返回的结果。
同步:你走我不走,我走你不走
异步:你走你的,我走我的,互不打扰
redis缓存中间件
当然,我们也可以通过异步调用的方式,利用消息中间件。
例如订单服务向消息中间件中发送消息,而商品服务可以从消息中间件中获取消息。
不能说ConsumerGroupA中的consumerA消费完MsgTopicA后就把消息队列中的MsgTopicA干掉,应当给MsgTopicA做一个标记,表示ConsumerGroupA消费完了(短信发送完了)。
因为接下来可能ConsumerGroupB也会来消费消息队列中的MsgTopicA(来执行发邮件操作)。
ConsumerGroupA中的consumerB去消费消息队列中的MsgTopicA,这时他发现同组的consumerA已经消费过了,就不再重复消费了。
那消息2怎么保证自己一定会发送给broken2呢?
看RooketMQ的部署模型。
整合
- 导包
<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-client</artifactId>
<version>4.4.0</version>
</dependency>
- 消息⽣产者实现
package com.cskaoyan;
import java.nio.charset.Charset;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
public class Producer {
public static void main(String[] args) throws MQClientException, RemotingException, InterruptedException, MQBrokerException {
// 消息的发送者
DefaultMQProducer producer = new DefaultMQProducer("producer_group");
// 设置nameServer的地址
producer.setNamesrvAddr("localhost:9876");
producer.start();
// 创建一个消息对象
String bodyStr = "jiayoutiezi";
Message message = new Message("topicA", bodyStr.getBytes(Charset.forName("utf-8")));
// 设置消息的延迟级别(18个延迟级别)
// messageDelayLevel=1s 5s 10s 30s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m 20m 30m 1h 2h
message.setDelayTimeLevel(2);
// sendResult消息发送的结果
SendResult sendResult = producer.send(message);
// 消息发送的状态
// SEND_OK, 发送成功
// FLUSH_DISK_TIMEOUT, 刷盘超时
// FLUSH_SLAVE_TIMEOUT, 同步到Slave超时
// SLAVE_NOT_AVAILABLE; 从节点不可用
SendStatus sendStatus = sendResult.getSendStatus();
if (SendStatus.SEND_OK.equals(sendStatus)) {
System.out.println("send succeed!" + System.currentTimeMillis());
} else {
System.out.println("send failed!" + System.currentTimeMillis());
}
}
}
- 消息消费者实现
package com.cskaoyan;
import java.util.List;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
public class Consumer {
public static void main(String[] args) throws MQClientException {
// DefaultMQPullConsumer表示主动拉取
// DefaultMQPushConsumer表示推送
// 新建一个消费者
DefaultMQPushConsumer consumer = new DefaultMQPushConsumer("consumer_group");
// 设置注册中心的地址
consumer.setNamesrvAddr("localhost:9876");
//订阅Topic,第二个参数表示过滤条件
consumer.subscribe("topicA", "*");
// 注册消息监听器
consumer.registerMessageListener(new MessageListenerConcurrently() {
// 这个方法就是consumer收到了消息之后调用的方法
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
System.out.println("收到消息。" + System.currentTimeMillis());
// 每次从消息队列中拿出一个消息对象
MessageExt messageExt = list.get(0);
byte[] body = messageExt.getBody();
String bodyStr = new String(body);
// 做一些我们需要的业务逻辑
System.out.println("收到的消息内容是:" + bodyStr);
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
// 假如在这里消费失败的话,rocketMQ默认会重试16次
}
});
consumer.start();
}
}
消息队列的知识用在哪呢?
订单超时取消中,和以上代码的思路差不多,但是需要使用spring来管理bean。
application.yml:
mq:
addr: localhost:9876
producer_group: producer_group
topic: order_timeout
OrderProducer:
package com.mall.order.mq;
import com.alibaba.fastjson.JSON;
import org.apache.rocketmq.client.exception.MQBrokerException;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.client.producer.DefaultMQProducer;
import org.apache.rocketmq.common.message.Message;
import org.apache.rocketmq.remoting.exception.RemotingException;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;
/**
* 发送一个订单超时取消的消息
* 创建订单时就发送
*/
@Component
public class OrderProducer {
@Value("${mq.producerGroup}")
private String producerGroup;
@Value("${mq.addr}")
private String addr;
@Value("${mq.topic}")
private String topic;
DefaultMQProducer mqProducer;
// 在对OrderProducer这个bean初始化的时候,就会执行@PostConstruct注解下的方法。
@PostConstruct
public void init(){
// 实例化OrderProducer的时候执行
mqProducer = new DefaultMQProducer(producerGroup);
mqProducer.setNamesrvAddr(addr);
try {
mqProducer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
}
public void sendOrderOvertimeMsg(String orderId,Integer userId){
Map map = new HashMap<String,Object>();
map.put("orderId",orderId);
map.put("userId",userId);
String jsonString = JSON.toJSONString(map);
Message message = new Message(topic, jsonString.getBytes(Charset.forName("utf-8")));
// 延迟5分钟 开发时进行修改
message.setDelayTimeLevel(9);
try {
mqProducer.send(message);
} catch (MQClientException e) {
e.printStackTrace();
} catch (RemotingException e) {
e.printStackTrace();
} catch (MQBrokerException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
SendMessageHandler:
package com.mall.order.biz.handler;
import com.mall.order.biz.context.AbsTransHandlerContext;
import com.mall.order.biz.context.CreateOrderContext;
import com.mall.order.biz.context.TransHandlerContext;
import com.mall.order.mq.OrderProducer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
/**
* @Description: 利用mq发送延迟取消订单消息
**/
@Component
@Slf4j
public class SendMessageHandler extends AbstractTransHandler {
@Autowired
private OrderProducer orderProducer;
@Override
public boolean isAsync() {
return false;
}
@Override
public boolean handle(TransHandlerContext context) {
// 还需要修改
CreateOrderContext createOrderContext = (CreateOrderContext) context;
String userIdStr = createOrderContext.getUserId().toString();
Integer userId = Integer.valueOf(userIdStr);
String orderId = createOrderContext.getOrderId();
orderProducer.sendOrderOvertimeMsg(orderId,userId);
return true;
}
}
OrderConsumer:
package com.mall.order.mq;
import com.alibaba.fastjson.JSON;
import com.mall.order.constants.OrderConstants;
import com.mall.order.dal.entitys.Order;
import com.mall.order.dal.persistence.OrderMapper;
import org.apache.rocketmq.client.consumer.DefaultMQPushConsumer;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyContext;
import org.apache.rocketmq.client.consumer.listener.ConsumeConcurrentlyStatus;
import org.apache.rocketmq.client.consumer.listener.MessageListenerConcurrently;
import org.apache.rocketmq.client.exception.MQClientException;
import org.apache.rocketmq.common.message.MessageExt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Map;
/**
* order消费者 需要修改
*/
@Component
public class OrderConsumer {
private DefaultMQPushConsumer consumer;
@Autowired
OrderMapper orderMapper;
@Value("${mq.topic}")
private String topic;
@Value("${mq.addr}")
private String addr;
@Value("${mq.consumerGroup}")
private String consumerGroup;
@PostConstruct
public void init(){
consumer = new DefaultMQPushConsumer(consumerGroup);
consumer.setNamesrvAddr(addr);
try {
consumer.subscribe(topic,"*");
} catch (MQClientException e) {
e.printStackTrace();
}
consumer.registerMessageListener(new MessageListenerConcurrently() {
@Override
public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {
MessageExt messageExt = list.get(0);
byte[] body = messageExt.getBody();
String bodyStr = new String(body);
Map<String,Object> map = JSON.parseObject(bodyStr, Map.class);
String orderId = (String) map.get("orderId");
Integer userId = (Integer) map.get("userId");
Order order = orderMapper.selectByPrimaryKey(orderId);
// 如果订单还是一个初始化状态的话
if (order.getStatus() == OrderConstants.ORDER_STATUS_INIT){
// 就将其修改为取消状态
order.setStatus(OrderConstants.ORDER_STATUS_TRANSACTION_CANCEL);
int effectedRows = orderMapper.updateByPrimaryKey(order);
// 进行判断
if (effectedRows < 1){
// 重试
return ConsumeConcurrentlyStatus.RECONSUME_LATER;
}else {
// 消费成功
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
}
return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
}
});
try {
consumer.start();
} catch (MQClientException e) {
e.printStackTrace();
}
}
}
https://blog.csdn.net/qq_42933931/article/details/113790433