1.メッセージの損失の理由
1. 送信者がメッセージを送信すると、メッセージが失われます. メッセージが送信されたように見えます
. 2. MQ はメッセージを受信しましたが、消費時にコンシューマーはそれを受信していません.
p->b e->q および q->c の 3 つの場所で、確実に到達できるようにします (Shang Silicon Valley からの写真)。
2.本番側のコールバック機能と設定について
#开启发送端确认
spring.rabbitmq.publisher-confirm-type=correlated
#开启发送端消息抵达队列的确认
spring.rabbitmq.publisher-returns=true
#只要抵达队列 以异步方式优先回调returnconfirm
spring.rabbitmq.template.mandatory=true
@Slf4j
@Configuration
public class MyRabbitConfig {
@Autowired
RabbitTemplate rabbitTemplate;
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter();
}
/**
*定制RabbitTemplate
* 1.服务收到消息就回调
* 1)spring.rabbitmq.publisher-confirms=true
* 2)设置确认回调confirmCallback
* 2.消息正确抵达队列进行回调
* 1)spring.rabbitmq.publisher-returns=true
* 2) spring.rabbitmq.template.mandatory=true
* 设置确认回调ReturnCallback
* 3.消费端确认(保证每个消费被正确消费,此时才可以broker删除这个消息)
* 1)默认是自动确认的,只要消息接收到,客户端会自动确认,服务端就会移除这个消息
* 问题:
* 我们收到很多消息,自动回复服务器ack,只有一个消息处理成功,宕机了,就会发生消息丢失。
* 消费者手动确认模式,只要我们没有明确告诉MQ,货物被签收,没有ACK
* 消息就一直是unacked状态,即使Consumer宕机。消息不会丢失,会重新变成ready
* 2)如何签收:
* channel.basicAck(deliveryTag,false);签收获取
* channel.basicNack(deliveryTag,false,true);拒签
*
*/
@PostConstruct //再配置类对象创建完成以后,执行这个方法
public void initRabbitTemplate(){
RabbitTemplate.ConfirmCallback confirmCallback = new RabbitTemplate.ConfirmCallback() {
@Override
public void confirm(CorrelationData correlationData, boolean ack, String cause) {
if (ack){
System.out.println("发送成功");
}else {
System.out.println("发送失败");
}
}
};
rabbitTemplate.setConfirmCallback(confirmCallback);
rabbitTemplate.setReturnCallback(new RabbitTemplate.ReturnCallback() {
@Override
public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
System.out.println(message);
System.out.println(replyCode);
System.out.println(replyText);
}
});
}
}
(上記のコードは Shang Silicon Valley のものです)
3. コンシューマー側の ack モードとチャット関連のコード
springboot-rabbit は 3 つのメッセージ確認モードを提供します:
AcknowledgeMode.NONE: 確認なしモード (プログラムが異常であるかどうかに関係なく、監視メソッドが実行されている限り、メッセージは消費されます。rabbitmq での自動確認に相当します。 、およびこのメソッドは推奨されません)
AcknowledgeMode .AUTO: 自動確認モード (デフォルトでは、消費者は例外がない場合は自動的に確認し、例外がある場合は確認せず、無限に再試行し、その結果、ウサギの自動確認と混同しないでください) AcknowledgeMode.MANUAL: 手動確認モード (
手動で channel.basicAck を呼び出して確認する必要があります。例外をキャッチして再試行回数を制御し、失敗メッセージの処理を制御することもできます)
通常は、手動確認モードを使用することをお勧めします。例は次
のとおりです。
確認モード: なし、自動、手動
なし: 自動確認; 自動: 状況に応じて; 手動: 手動確認
spring:
rabbitmq:
host: localhost
username: guest
password: guest
virtual-host: /
listener:
simple:
acknowledge-mode: manual // 开启手动确认
retry:
enabled: true // 开启重试
max-attempts: 3 //最大重试次数
initial-interval: 2000ms //重试间隔时间
@RestController
@RequestMapping("/mq")
//@Api(tags = "mq相关接口" , description = "MqController | 消息模块")
public class MqController {
private static final Logger logger = LoggerFactory.getLogger(MqController.class);
@Autowired
private ProdServer prodServer;
@Autowired
WebSocketServer webSocketServer;
@RabbitListener(queues = {
FanoutRabbitConfig.DEFAULT_BOOK_QUEUE})
public void listenerAutoAck(String text, Message message, Channel channel) {
// TODO 如果手动ACK,消息会被监听消费,但是消息在队列中依旧存在,如果 未配置 acknowledge-mode 默认是会在消费完毕后自动ACK掉
final long deliveryTag = message.getMessageProperties().getDeliveryTag();
try {
logger.info("[消费者一监听的消息] - [{}]", text);
ChatDto.MqChat chatDto = JSONObject.parseObject(text, ChatDto.MqChat.class);
Integer rs = WebSocketServer.sendInfo(chatDto.getMessage(), chatDto.getToUserId());
// yanUserChatService.saveChat(chatDto.getOpenid(),chatDto,rs);
// webSocketServer.sendMqMessage(text);
// TODO 通知 MQ 消息已被成功消费,可以ACK了
channel.basicAck(deliveryTag, false);
} catch (IOException e) {
try {
// TODO 处理失败,重新压入MQ
channel.basicRecover();
} catch (IOException e1) {
e1.printStackTrace();
}
}
}
}
@ApiOperation("聊天核心方法 ")
public void defaultMessage(@RequestBody ChatDto chatDto,
HttpServletRequest request) throws IOException{
String message = chatDto.getContent();
String toUserId = chatDto.getEmail();
String openid = Util.fromRequestToOpenid(request);
Map<String, String> map = new HashMap<String, String>();
map.put("message",message);
map.put("toUserId",toUserId);
map.put("openid",openid);
rabbitTemplate.convertAndSend("fanoutExchange", "", JSONObject.toJSONString(map));
}
関連する websocket のコード
public static Integer sendInfo(String message,@PathParam("userId") String userId) throws IOException {
log.info("发送消息到:"+userId+",报文:"+message);
if(StringUtils.isNotBlank(userId)&&webSocketMap.containsKey(userId)){
//这个地方最好封装一下报文请求 要不然前端不好处理
JSONObject jsonObject =new JSONObject();
//追加发送人(防止串改)
jsonObject.put("type","receive");
jsonObject.put("content",message);
webSocketMap.get(userId).sendMessage(jsonObject.toJSONString());
log.info("发送消息到成功");
return 1;
}else{
log.error("用户"+userId+",不在线!");
return 0;
}
}
チャット状況下での rabbitmq のアプリケーション; メッセージの送信後にビジネス ロジックを切り離し、チャット プロセス中のパフォーマンスを向上させます。
4. メッセージの信頼性を確保する方法
1. メッセージの紛失
メッセージが送信されると、ネットワークの問題によりサーバーに到達しない
. フォールト トレラントな方法 (try-catch) を作成する. メッセージの送信時にネットワーク障害が発生する可能性がある. 障害後に再試行メカニズムが必要である. , これはデータベースに記録できます.
定期的なスキャンと再送信によって行う必要があります.,定期的にデータベースに移動して、失敗したメッセージをスキャンして再送信します. メッセージがブローカーに到達すると、ブローカーはメッセージをディスクに書き込みます (持続性) 成功と見なされます. この時点で、Broker はまだ永続化を完了しておらず、ダウンしています。パブリッシャーは、成功したメッセージを確認し、データベース メッセージのステータスを変更するための確認コールバック メカニズムも追加する必要があります。自動ACKの状態。コンシューマーはメッセージを受信しますが、メッセージを受信する時間がなく、シャットダウンします. 手動 ACK を有効にする必要があります. コンシューマーは、コンシュームが成功した場合にのみ削除されます. 失敗したか、処理する時間がなかった場合、noAck になり、キューに再入力します。
2. 繰り返されるメッセージ
メッセージの消費が成功し、トランザクションが送信され、ACK が発生するとマシンがダウンします。その結果、ACK の成功はなく、ブローカーのメッセージは unack から ready に変わり、他のコンシューマーに送信されます.
メッセージの消費は失敗します. 再試行メカニズムにより、メッセージは自動的に送信され、正常に消費されます
. ack がダウンし、メッセージが unack から ready に変わり、ブローカーが
コンシューマーを再送信する ビジネス消費インターフェースはべき等になるように設計する必要があります。例えば、インベントリーに作業指示書のステータスフラグがある場合
、重複防止テーブル (redis/mysql) を使用し、送信された各メッセージにはビジネス固有の識別子があり、処理後、処理する必要はありません。 rabbitMQ の各
メッセージ. 各メッセージには、配信されたかどうかを取得するために使用できる redelivered フィールドがあります. 再投稿, 初回ではありません
3. 未処理のニュース
コンシューマーのダウンタイムのバックログコンシューマー
の消費容量が不十分
送信者がトラフィックを送信しすぎている
通常の消費のためにオンラインになるコンシューマーが増える 専用のキュー消費サービスでオンラインになり、最初
にバッチでメッセージを取得し、データベースに記録して処理しますゆっくりオフライン
—— ——————————————
元のテキストリンクの第 4 部: https://blog.csdn.net/weixin_40566934/article/details/119643740
5. rabbitmq デッドレターキューと遅延キューの使用
———————————————
元のリンク: https://blog.csdn.net/fu_huo_1993/article/details/88350188