在我们的日常开发中,消息中间件已经成为了java研发工程师的一项必备技能,本文主要是基于对springboot原生组件的扩展开发,基于模板设计模式和静态代理模式,简化了队列路由的绑定,交由公共模板进行统一的绑定,并在公用模板中保证了消息的幂等性和消息的可靠性投递,将这些类似的代码抽离出来,让开发者只专注于业务逻辑的开发.
整体实现思路:
- 开发者申明路由交换机等基础元数据后,交由元数据解析器完成交换机,路由的申明及相应关系的绑定
- 开发者申明消息监听器后,通过消息侦听容器完成队列与监听器的绑定
- 开发者通过消息发射器,发布消息后,系统通过不通模板完成消息的发送
申明基础信息接口
public interface MessageMetaData {
String getQueue();
ExchangeTypeEnum getExchangeType();
default Map getQueueArgs(){
return null;
};
default Map getExchangeArgs(){
return null;
};
default MessageProperties getMessageProperties(){
return null;
};
default void setMessageProperties(MessageProperties messageProperties){
};
}
定义不同的元数据模板
public abstract class FanoutMessageMetaData implements MessageMetaData {
private MessageProperties messageProperties = null;
@Override
public ExchangeTypeEnum getExchangeType() {
return ExchangeTypeEnum.FANOUT;
}
abstract public String getExchange();
@Override
public MessageProperties getMessageProperties() {
return this.messageProperties;
}
@Override
public void setMessageProperties(MessageProperties messageProperties) {
this.messageProperties=messageProperties;
}
}
public abstract class DirectMessageMetadata implements MessageMetaData{
private MessageProperties messageProperties;
@Override
public ExchangeTypeEnum getExchangeType() {
return ExchangeTypeEnum.DIRECT;
}
abstract public String getExchange();
@Override
public MessageProperties getMessageProperties() {
return this.messageProperties;
}
@Override
public void setMessageProperties(MessageProperties messageProperties) {
this.messageProperties=messageProperties;
}
}
申明元数据解析接口,定义解析规范
public interface Support {
Boolean support(ExchangeTypeEnum exchangeTypeEnum);
}
public interface MessageMetaDataResolver extends Support {
void resolve(MessageMetaData messageMetaData);
}
定义不同模板的解析器
@RequiredArgsConstructor
@Slf4j
public abstract class AbstractMessageMetadataResolver implements MessageMetaDataResolver {
private final RabbitAdmin rabbitAdmin;
@Override
public void resolve(MessageMetaData messageMetaData) {
if (support(messageMetaData.getExchangeType())){
doResolve(messageMetaData);
}
}
abstract void doResolve(MessageMetaData messageMetaData);
public void declareQueue(Queue queue){
rabbitAdmin.declareQueue(queue);
log.info("queue [{}] declared.",queue);
}
public void declareExchange(Exchange exchange){
rabbitAdmin.declareExchange(exchange);
log.info("exchange [{}] declared.",exchange);
}
public void declareBinding(Binding binding){
rabbitAdmin.declareBinding(binding);
log.info("binding [{}] declared.",binding);
}
}
public class DirectMessageMetadataResolver extends AbstractMessageMetadataResolver{
public DirectMessageMetadataResolver(RabbitAdmin rabbitAdmin) {
super(rabbitAdmin);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return ExchangeTypeEnum.DIRECT.equals(exchangeTypeEnum);
}
@Override
void doResolve(MessageMetaData messageMetaData) {
DirectMessageMetadata directMessageMetadata = (DirectMessageMetadata) messageMetaData;
// 申明队列
Queue queue = new Queue(directMessageMetadata.getQueue(), true, false, false, directMessageMetadata.getQueueArgs());
declareQueue(queue);
// 申明交换机
DirectExchange exchange = new DirectExchange(directMessageMetadata.getExchange(), true, false, directMessageMetadata.getExchangeArgs());
declareExchange(exchange);
// 交换机绑定队列
Binding binding = BindingBuilder.bind(queue).to(exchange).withQueueName();
declareBinding(binding);
}
}
public class FanoutMessageMetaDateResolver extends AbstractMessageMetadataResolver{
public FanoutMessageMetaDateResolver(RabbitAdmin rabbitAdmin) {
super(rabbitAdmin);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return ExchangeTypeEnum.FANOUT.equals(exchangeTypeEnum);
}
@Override
void doResolve(MessageMetaData messageMetaData) {
FanoutMessageMetaData FanoutMessageMetaData = (FanoutMessageMetaData) messageMetaData;
// 申明队列
Queue queue = new Queue(FanoutMessageMetaData.getQueue(), true, false, false, FanoutMessageMetaData.getQueueArgs());
declareQueue(queue);
// 申明交换机
FanoutExchange exchange = new FanoutExchange(FanoutMessageMetaData.getExchange(), true, false, FanoutMessageMetaData.getExchangeArgs());
declareExchange(exchange);
// 队列绑定交换机
Binding binding = BindingBuilder.bind(queue).to(exchange);
declareBinding(binding);
}
}
申明静态代理器
@Slf4j
public class DelegatingMessageMetaDataResolver implements MessageMetaDataResolver {
private final Map messageMetaDataResolverMap = new ConcurrentHashMap<>();
public DelegatingMessageMetaDataResolver(Map messageMetaDataResolverMap){
this.messageMetaDataResolverMap.putAll(messageMetaDataResolverMap);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return true;
}
@Override
public void resolve(MessageMetaData messageMetaData) {
if (messageMetaData==null){
log.error("metaDate resolve must have messageMetaData but is null");
return;
}
MessageMetaDataResolver messageMetaDataResolver = messageMetaDataResolverMap.get(messageMetaData.getExchangeType());
if (messageMetaDataResolver==null){
log.error("messageMetaDataResolver is null");
return;
}
messageMetaDataResolver.resolve(messageMetaData);
}
}
元数据初始化器
@RequiredArgsConstructor
@Slf4j
public class MessageMetaDataInitalizer implements InitializingBean, Ordered {
private final XlcpMqProperties xlcpMqProperties;
private final MessageMetaDataResolver messageMetaDataResolver;
@Override
public void afterPropertiesSet() throws Exception {
log.info("autoCreateMq configured with [{}]",xlcpMqProperties.getAutoCreateMq());
if (xlcpMqProperties.getAutoCreateMq()){
Map messageMetaDataMap = SpringContextHolder.getBeansOfType(MessageMetaData.class);
messageMetaDataMap.forEach((key,messageMetaData)->{
log.info("Mq auto declared with metaDate [{}]",messageMetaData);
messageMetaDataResolver.resolve(messageMetaData);
});
}
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
将静态代理器和初始器交由spring容器管理
2.2 动态消息监听器构建申明抽象消息监听器
@Slf4j public abstract class DynamicMessageListenerextends AbstractAdaptableMessageListener { private final MessageMetaData messageMetaData; public static final String MQ_MESSAGE_ID_PREFIX = "mq:message:id"; public DynamicMessageListener(MessageMetaData messageMetaData){ this.messageMetaData=messageMetaData; } public void doExcute(Message message, Channel channel, Consumer
申明单消息监听器模板和批量消息监听模板
public abstract class SingleDynamicMessageListenerextends DynamicMessageListener { public SingleDynamicMessageListener(MessageMetaData messageMetaData) { super(messageMetaData); } public abstract void onMessage(T t); @Override public void onMessage(Message message, Channel channel) throws Exception { doExcute(message,channel,object->{ onMessage((T)object); }); } } public abstract class BatchDynamicMessageListener extends DynamicMessageListener { public BatchDynamicMessageListener(MessageMetaData messageMetaData) { super(messageMetaData); } @Override public void onMessage(Message message, Channel channel) throws Exception { MessageConverter messageConverter=getMessageConverter(); doExcute(message,channel,(obj)->{ if (message instanceof Collection){ onMessageBatch((Collection ) obj); }else { ArrayList list = new ArrayList<>(); list.add((T)message); onMessageBatch(list); } }); } abstract void onMessageBatch(Collection collection); }
申明动态容器监听器
@RequiredArgsConstructor
public class DynamicMessageListenerContainer extends SimpleRabbitListenerContainerFactory {
private final int DEFAULT_PREFETCH_COUNT = 1;
private final ConnectionFactory connectionFactory;
public static final String LISTENER_CONTAINER_SUFFIX = "SimpleMessageListenerContainer";
private int prefetchCount = DEFAULT_PREFETCH_COUNT;
public SimpleMessageListenerContainer createListenerContainer(DynamicMessageListener dynamicMessageListener, MessageMetaData messageMetaData){
SimpleMessageListenerContainer simpleMessageListenerContainer=new SimpleMessageListenerContainer();
simpleMessageListenerContainer.setQueueNames(messageMetaData.getQueue());
simpleMessageListenerContainer.setMessageListener(dynamicMessageListener);
simpleMessageListenerContainer.setConnectionFactory(connectionFactory);
simpleMessageListenerContainer.setPrefetchCount(prefetchCount);
//设置当前消息为手动签收
simpleMessageListenerContainer.setAcknowledgeMode(AcknowledgeMode.MANUAL);
return simpleMessageListenerContainer;
}
public void setPrefetchCount(int prefetchCount) {
this.prefetchCount = prefetchCount;
}
}
申明动态容器监听器初始化容器
@Slf4j
public class DynamicMessageListenerInitialize implements InitializingBean, Ordered {
@Override
public void afterPropertiesSet() throws Exception {
// 获得所有客户端配置的监听器
Map listenerMap = SpringContextHolder.getBeansOfType(DynamicMessageListener.class);
// 获取bean工厂
ConfigurableListableBeanFactory beanFactory = (ConfigurableListableBeanFactory)SpringContextHolder.getBeanFactory();
// 获得监听器容器 同于配置配置监听器
DynamicMessageListenerContainer dynamicMessageListenerContainer = SpringContextHolder.getBean(DynamicMessageListenerContainer.class);
// 初始化监听器的配置
listenerMap.forEach((listenerName,listener)->{
MessageMetaData messageMetaData = listener.getMessageMetaData();
SimpleMessageListenerContainer listenerContainer = dynamicMessageListenerContainer.createListenerContainer(listener, messageMetaData);
String beanName = String.format("%s%s",listenerName,DynamicMessageListenerContainer.LISTENER_CONTAINER_SUFFIX);
beanFactory.registerSingleton(beanName,listenerContainer);
log.info("create [{}] listener to [{}]",beanName,messageMetaData);
});
}
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
将初始化容器交给spring容器管理
2.3 消息发射器构建申明发射规范
public interface MessageDelivery extends Support {
Boolean deliver(MessageMetaData messageMetaData,T t);
}
申明不同模板的发射器
public abstract class AbstractMessageDelivery implements MessageDelivery {
public final RabbitTemplate rabbitTemplate;
public AbstractMessageDelivery(RabbitTemplate rabbitTemplate){
this.rabbitTemplate=rabbitTemplate;
}
@Override
public Boolean deliver(MessageMetaData messageMetaData, T t) {
if (support(messageMetaData.getExchangeType())){
doDeliver(messageMetaData,t);
return true;
}
return false;
}
public abstract void doDeliver(MessageMetaData messageMetaData,T t);
public Message createMessage(Object object,MessageMetaData messageMetaData){
// 消息转换 并设置当前消息全局唯一id保证消息不被重复下消费
SimpleMessageConverter converter = new SimpleMessageConverter();
final MessageProperties messageProperties;
MessageProperties customMessageProperties = messageMetaData.getMessageProperties();
if (customMessageProperties!=null){
messageProperties=customMessageProperties;
}else {
messageProperties = MessagePropertiesBuilder.newInstance()
.build();
}
Message message = converter.toMessage(object, setSharedMessageProperties(messageProperties));
return message;
}
private MessageProperties setSharedMessageProperties(MessageProperties messageProperties){
messageProperties.setMessageId(Convert.toStr(UniqueIdUtil.genId()));
return messageProperties;
}
}
public class FanoutMessageMetaDateDeliver extends AbstractMessageDelivery {
public FanoutMessageMetaDateDeliver(RabbitTemplate rabbitTemplate) {
super(rabbitTemplate);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return ExchangeTypeEnum.FANOUT.equals(exchangeTypeEnum);
}
@Override
public void doDeliver(MessageMetaData messageMetaData, T t) {
FanoutMessageMetaData FanoutMessageMetaData = (FanoutMessageMetaData) messageMetaData;
Message message = createMessage(t, messageMetaData);
rabbitTemplate.convertAndSend(FanoutMessageMetaData.getExchange(),"",message);
}
}
public class DirectMessageMetaDateDeliver extends AbstractMessageDelivery{
public DirectMessageMetaDateDeliver(RabbitTemplate rabbitTemplate) {
super(rabbitTemplate);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return ExchangeTypeEnum.DIRECT.equals(exchangeTypeEnum);
}
@Override
public void doDeliver(MessageMetaData messageMetaData, T t) {
DirectMessageMetadata directMessageMetadata = (DirectMessageMetadata) messageMetaData;
Message message = createMessage(t,messageMetaData);
rabbitTemplate.convertAndSend(directMessageMetadata.getExchange(),directMessageMetadata.getQueue(),message);
}
}
申明静态代理器
public class DelegatingMessageDelivery implements MessageDelivery {
private final Map messageDeliveryMap = new ConcurrentHashMap<>();
public DelegatingMessageDelivery(Map messageDeliveryMap){
this.messageDeliveryMap.putAll(messageDeliveryMap);
}
@Override
public Boolean support(ExchangeTypeEnum exchangeTypeEnum) {
return true;
}
@Override
public Boolean deliver(MessageMetaData messageMetaData, T t) {
Assert.notNull(messageMetaData,"message deliver must have messageMetaData but is null");
ExchangeTypeEnum exchangeType = messageMetaData.getExchangeType();
MessageDelivery delivery = messageDeliveryMap.get(messageMetaData.getExchangeType());
Assert.notNull(delivery,"message deliver not null");
return delivery.deliver(messageMetaData,t);
}
}
2.4 申明公用基础属性
@ConfigurationProperties(prefix = XlcpMqProperties.PRE_FIX)
@Data
public class XlcpMqProperties {
public final static String PRE_FIX = "config.mq";
private Boolean enabled = Boolean.TRUE;
private Boolean autoCreateMq = Boolean.TRUE;
}
2.5定义springboot基础规范
@Configuration
@EnableConfigurationProperties(XlcpMqProperties.class)
public class XlcpMqMessageAutoConfiguration {
@Bean
public RabbitAdmin rabbitAdmin(ConnectionFactory connectionFactory){
return new RabbitAdmin(connectionFactory);
}
@Bean
@ConditionalOnMissingBean
public MessageMetaDataResolver messageMetaDataResolver(RabbitAdmin rabbitAdmin){
HashMap resolverHashMap = new HashMap<>();
resolverHashMap.put(ExchangeTypeEnum.FANOUT,new FanoutMessageMetaDateResolver(rabbitAdmin));
resolverHashMap.put(ExchangeTypeEnum.DIRECT,new DirectMessageMetadataResolver(rabbitAdmin));
return new DelegatingMessageMetaDataResolver(resolverHashMap);
}
@Bean
@ConditionalOnMissingBean
public MessageMetaDataInitalizer messageMetaDataInitalizer(MessageMetaDataResolver messageMetaDataResolver,XlcpMqProperties xlcpMqProperties){
return new MessageMetaDataInitalizer(xlcpMqProperties,messageMetaDataResolver);
}
@Bean
@ConditionalOnMissingBean
public MessageDelivery messageDelivery(RabbitTemplate rabbitTemplate){
HashMap deliveryHashMap = new HashMap<>();
deliveryHashMap.put(ExchangeTypeEnum.FANOUT,new FanoutMessageMetaDateDeliver(rabbitTemplate));
deliveryHashMap.put(ExchangeTypeEnum.DIRECT,new DirectMessageMetaDateDeliver(rabbitTemplate));
return new DelegatingMessageDelivery(deliveryHashMap);
}
@Bean
public DynamicMessageListenerContainer dynamicMessageListenerContainer(ConnectionFactory connectionFactory){
return new DynamicMessageListenerContainer(connectionFactory);
}
@Bean
public DynamicMessageListenerInitialize dynamicMessageListenerInitialize(){
return new DynamicMessageListenerInitialize();
}
}
三 客户端使用
3.1 申明自定义元数据信息
@Component
public class DemoMessageMetaData extends FanoutMessageMetaData {
@Override
public String getExchange() {
return "demoExchange";
}
@Override
public String getQueue() {
return "demoQueue";
}
}
3.2 申明消息监听器
@Data
public class DemoMessage implements Serializable {
private static final long serialVersionUID = -5307128314859934844L;
private String text;
}
@Component
@Slf4j
public class DemoMessageListener extends SingleDynamicMessageListener {
public DemoMessageListener() {
super(new DemoMessageMetaData());
}
@Override
public void onMessage(DemoMessage demoMessage) {
log.info("监听到消息:{}",demoMessage);
}
}
3.3 业务代码中使用
@RestController
@RequestMapping("/mq")
@Slf4j
public class DemoMessageController {
@Autowired
private DemoMessageMetaData demoMessageMetaData;
@Autowired
private MessageDelivery messageDelivery;
@GetMapping("/message")
@Inner(value = false)
public void sendSingleMessage(){
DemoMessage demoMessage = new DemoMessage();
demoMessage.setText("hello mq");
MessageProperties messageProperties = MessagePropertiesBuilder.newInstance()
.setExpiration("1")
.setDeliveryTag(1234L)
.build();
demoMessageMetaData.setMessageProperties(messageProperties);
messageDelivery.deliver(demoMessageMetaData,demoMessage);
}
}
四 一些说明
4.1 如何保证消息的幂等性?
4.2 如何保证消息的可靠性投递
主要从三个方面入手:
开启数据的持久化,避免服务宕机后重启消息丢失
消息的投递:通过confirm回调确保消息投递成功
消费端主要是通过手动签收来确保消息被消费成功
注意:这种方式当最前面的消息没过期时后面的消息过期了也不会投递到死信交换机中,这里容易导致消息的堆积,一般用于时长固定的定时任务,可以采用rocketMq进行可变的延时消息的投递.



