1、pom文件引入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> |
2、在application.properties定义队列名和交换机名
spring.rabbitmq.host=192.168.19.23 spring.rabbitmq.username=guest spring.rabbitmq.password=guest spring.rabbitmq.port=5672 spring.rabbitmq.virtual-host=/ # 开启confirms回调 P -> Exchange 发送确认 spring.rabbitmq.publisher-confirms=true # 开启returnedMessage回调 Exchange -> Queue 路由失败回调 spring.rabbitmq.publisher-returns=true spring.rabbitmq.template.mandatory=true # 签收模式为手动签收-那么需要在代码中手动ACK spring.rabbitmq.listener.simple.acknowledge-mode=manual # 每个队列最大的消费者数量 spring.rabbitmq.listener.simple.max-concurrency=1 # 每个队列启动的消费者数量 spring.rabbitmq.listener.simple.concurrency=1 # 每次从RabbitMQ获取的消息数量 spring.rabbitmq.listener.simple.prefetch=1 #定义消息队列 app.rabbitmq.queue.wx.oa=wx-oa-queue #定义死信消息队列 app.rabbitmq.queue.wx.oa.dead.letter=wx-oa-dead-letter-queue #交换机 app.rabbitmq.exchange.wx.oa=wx-oa-exchange #死信交换机 app.rabbitmq.exchange.common.dead.letter=common-dead-letter-exchange |
3、配置队列和交换机绑定
@Slf4j @Configuration public class RabbitConfig { @Autowired private CachingConnectionFactory connectionFactory; //队列名 @Value("${app.rabbitmq.queue.wx.oa}") private String wxOAQueueName; //通用死信交换机名 @Value("${app.rabbitmq.exchange.common.dead.letter}") private String commonDeadLetterExchange; //交换机名 @Value("${app.rabbitmq.exchange.wx.oa}") private String wxOAExchangeName; @Value("${app.rabbitmq.queue.wx.oa.dead.letter}") private String wxOADeadLetterQueue; /** * 微信公众号消息队列 * * * @return */ @Bean public Queue wxOAQueue( ) { return QueueBuilder .durable(wxOAQueueName) //声明该队列的死信消息发送到的 交换机 (队列添加了这个参数之后会自动与该交换机绑定,并设置路由键,不需要开发者手动设置) .withArgument("x-dead-letter-exchange", commonDeadLetterExchange) //声明该队列死信消息在交换机的 路由键 .withArgument("x-dead-letter-routing-key", "wx-oa-dead-letter-routing-key") .build(); } /** * 交换机 * * * @return */ @Bean public Exchange wxOAExchange() { return ExchangeBuilder .topicExchange(wxOAExchangeName) .durable(true) .build(); } /** * 队列与交换机绑定 * * * * @return */ @Bean public Binding wxOABinding() { return BindingBuilder .bind(wxOAQueue()) .to(wxOAExchange()) .with("wx.oa.routing.key") .noargs(); } /** * 死信交换机 * * * @return */ @Bean public Exchange commonDeadLetterExchange() { return ExchangeBuilder .topicExchange(commonDeadLetterExchange) .durable(true) .build(); } /** * 队列的死信消息 路由的队列 * 队列wx-oa-queue的死信投递到死信交换机`common-dead-letter-exchange`后再投递到该队列 * 用这个队列来接收wx-oa-queue的死信消息 * * @return */ @Bean public Queue wxOADeadLetterQueue() { return QueueBuilder .durable(wxOADeadLetterQueue) .build(); } /** * 死信队列绑定死信交换机 * * * * @return */ @Bean public Binding wxOADeadLetterBinding() { return BindingBuilder .bind(wxOADeadLetterQueue()) .to(commonDeadLetterExchange()) .with("wx-oa-dead-letter-routing-key") .noargs(); } @Bean public RabbitTemplate rabbitTemplate() { RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory); rabbitTemplate.setMessageConverter(converter()); // 消息是否成功发送到Exchange rabbitTemplate.setConfirmCallback((correlationData, ack, cause) -> { if (ack) { String msgId = correlationData.getId(); log.info("消息成功发送到Exchange",msgId); } else { log.info("消息发送到Exchange失败, {}, cause: {}", correlationData, cause); } }); // 触发setReturnCallback回调必须设置mandatory=true, 否则Exchange没有找到Queue就会丢弃掉消息, 而不会触发回调 rabbitTemplate.setMandatory(true); // 消息是否从Exchange路由到Queue, 注意: 这是一个失败回调, 只有消息从Exchange路由到Queue失败才会回调这个方法 rabbitTemplate.setReturnCallback((message, replyCode, replyText, exchange, routingKey) -> { log.info("消息从Exchange路由到Queue失败: exchange: {}, route: {}, replyCode: {}, replyText: {}, message: {}", exchange, routingKey, replyCode, replyText, message); }); return rabbitTemplate; } @Bean public Jackson2JsonMessageConverter converter() { return new Jackson2JsonMessageConverter(); } } |
4、消息发送测试
@Component public class MsgSender { @Autowired private RabbitTemplate rabbitTemplate; //routingkey 路由键 @Value("${wx.oa.msg.mq.config.queue.routing.key}") private String routingkey; @Value("${app.rabbitmq.exchange.wx.oa}") String wxOAExchangeName; /* * 发送消息的方法 */ public void send(WxOfficialAccountsMqMsg msg){ /** * convertAndSend - 转换并发送消息的template方法。 * 是将传入的普通java对象,转换为rabbitmq中需要的message类型对象,并发送消息到rabbitmq中。 * 参数一:交换器名称。 类型是String * 参数二:路由键。 类型是String * 参数三:消息,是要发送的消息内容对象。类型是Object */ String id = UUID.randomUUID().toString(); CorrelationData correlationData = new CorrelationData(id); msg.setBusinessMsgId(id); rabbitTemplate.convertAndSend(wxOAExchangeName, this.routingkey, msg,correlationData); } |
5、消息接收
@Slf4j public class MessageListener { @RabbitListener(queues = "${app.rabbitmq.queue.wx.oa}") MessageProperties properties = message.getMessageProperties(); } else { } public class MessageHelper { public static Message objToMsg(Object obj) { if (null == obj) { return null; } Message message = MessageBuilder.withBody(JsonUtil.objToStr(obj).getBytes()).build(); message.getMessageProperties().setDeliveryMode(MessageDeliveryMode.PERSISTENT);// 消息持久化 message.getMessageProperties().setContentType(MessageProperties.CONTENT_TYPE_JSON); return message; } public static <T> T msgToObj(Message message, Class<T> clazz) { if (null == message || null == clazz) { return null; } String str = new String(message.getBody()); T obj = JsonUtil.strToObj(str, clazz); return obj; } } @Slf4j public class JsonUtil { private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class); private static ObjectMapper objectMapper = new ObjectMapper(); private static final String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss"; /** * 把Java对象转换成json字符串 * * @param object 待转化为JSON字符串的Java对象 * @return json 串 or null */ public static String parseObjToJson(Object object) { String string = null; try { string = JSONObject.toJSONString(object); } catch (Exception e) { LOGGER.error(e.getMessage()); } return string; } /** * 将Json字符串信息转换成对应的Java对象 * * @param json json字符串对象 * @param c 对应的类型 */ public static <T> T parseJsonToObj(String json, Class<T> c) { try { JSONObject jsonObject = JSON.parseObject(json); return JSON.toJavaObject(jsonObject, c); } catch (Exception e) { LOGGER.error(e.getMessage()); } return null; } static { // 对象的所有字段全部列入 objectMapper.setSerializationInclusion(JsonInclude.Include.ALWAYS); // 取消默认转换timestamps形式 objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); // 忽略空bean转json的错误 objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false); // 统一日期格式 objectMapper.setDateFormat(new SimpleDateFormat(DATE_FORMAT)); // 忽略在json字符串中存在, 但在java对象中不存在对应属性的情况, 防止错误 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); } public static <T> String objToStr(T obj) { if (null == obj) { return null; } try { return obj instanceof String ? (String) obj : objectMapper.writeValueAsString(obj); } catch (Exception e) { log.warn("objToStr error: ", e); return null; } } public static <T> T strToObj(String str, Class<T> clazz) { if (StringUtils.isBlank(str) || null == clazz) { return null; } try { return clazz.equals(String.class) ? (T) str : objectMapper.readValue(str, clazz); } catch (Exception e) { log.warn("strToObj error: ", e); return null; } } public static <T> T strToObj(String str, TypeReference<T> typeReference) { if (StringUtils.isBlank(str) || null == typeReference) { return null; } try { return (T) (typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); } catch (Exception e) { log.error("strToObj error", e); return null; } } } |