spring cloud stream +rabbitmq 构建消息推送中间件

消息推送

最近在做消息相关的工作,先说下为何考虑使用消息,在我们的服务,当任务节点完成,或者任务创建,要发送一条提醒给相关人员,在旧的版本中使用的异步调用,但是这个会有个问题,就是有时异步方法不执行。今天我们先不看这个异步调用的的,我们直接使用消息队列进行处理消息。这样能够进行解耦,同时可以处理接口响应速度,因为feign调用是阻塞的,所以我们有必要将消息推送服务这样的耗时服务,给抽取处理,加快接口响应速度。

旧版本使用处理的的形式

在旧版本中我们使用的是java8的异步调用,今天我们先不进行深入探讨这个java8的CompltableFuture这个类的。这里在使用CompletableFuture的时候,并没有自定义线程池,默认那么就是ForkJoinPool。在生产环境中,有时这个RunAsync()异步调用是不起作用,也就是说有是是没有执行的。这里也没有详细的去探讨这个问题
spring cloud stream +rabbitmq 构建消息推送中间件_第1张图片
spring cloud stream +rabbitmq 构建消息推送中间件_第2张图片

技术选型

Spring Cloud Stream+ RabbitMQ

spring cloud stream +rabbitmq 构建消息推送中间件_第3张图片

为何采用stream,因为项目本身就是微服务,Spring Cloud Stream是一个用来为微服务应用构建消息驱动能力的框架。它可以基于Spring Boot来创建独立的、可用于生产的Spring应用程序。它通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动的微服务应用。使用Stream可以降低中间件与我们系统之间的 耦合性,我们可以让rabbitMq切换为Kafka,可以不用修改代码。只需要修改一些配置即可。

基本实现

为了将发送消息和业务进行解耦,我们只需将发送消息的服务和调用服务加入一层中间件,让消息服务通过中间间的的调用达到与业务进行解耦。

  1. 将要发送通知或者消息封装到一个对象中通过 实现(通道配置,以及通道与exchange绑定进行发送消息进行绑定),通过自定义通道进行发送消息。这样直接丢到rabbitMq中后续rangrabbitMq进行做操作
  2. 在不同的服务中可以绑定不同的信道,所以我们可以做后续处理的时候将rabbitMq的监听队列应用在不同的服务中(监听不同的信道),只需要进行监听,监听后将数据进行解析,如果需要调用推送服务直接调用推送服务进行推送,如果需要调用消息进行信息的情况我们进行更新消息数据表即可。
public interface MessageSoure{
	@Output("messageoutput")
    MessageChangel messageOutput();
}
// 信道与交换加绑定

@EnableBinding(MessageSource.class)
@Service
public class MessageProduce{
	// 注入自定义通道
    @Autowired
    private MessageSource messageSource;
    
    public void send(String message){
    	messageSource.output().send(MessageBuilder.withPayload(message).build())
    }
}

// 使用时直接注入Service 进行调用send方法
private MessageProduce messageProduce;


// sink
public interface PlanMessageSink {
    String PLAN_INPUT = "planInput";

    @Input(PlanMessageSink.PLAN_INPUT)
    SubscribableChannel planMessageInput();
}

@EnableBinding(PlanMessageSink.class)
@Slf4j
@Service
public class PlanMessageConsumer {
    @Autowired
    private GtMessageService messageFeignService;

    @StreamListener(PlanMessageSink.PLAN_INPUT)
    public void receive(String message){
    	// 1.进行json解析
        // 2.调用消息发现服务
    }
}

可扩展项:

  1. 消息消费的保证
  2. 消息持久化
  3. 消息记录
  4. 消息重试

需要解决的问题

消息消费的保证

  1. 如果消息未成功消费(消费失败)可通过以下解决。
  • 将消息重新塞入队列 。通过配置spring.cloud.stream.bindings.example-topic-input.consumer.requeue-rejected=true 这个配置会将消费失败的消息进行重新执行。直到消费成功为止。
  • 自动重试。设置重试次数 spring cloud stream默认的会设置重试次数为3次 同时我们可以进行修改重试次数的默认值 spring.cloud.stream.bindings.example-topic-input.consumer.max-attempts=1。如果超过重试次数则进入死信对列进入DLQ进行处理
  • 进行降级处理。通过在消费端进行配置 @ServiceActivator(inputchannel=“test-topic.stream-exception-handler.errors”) 注解的方法进行执行。test-topic->destination . handler-> group 相应的降级的。这一种也不能真正保证 也只能通过记录和后续处理来解决
  • 使用DLQ队列。开启DLQ spring.cloud.stream.rabbit.bindings.example-topic-input.consumer.auto-bind-dlq=true (这个配置+设置计数器 能够解决消息堆积问题)。 需要装DLQ插件 可以控制消息在DLQ中存活时间。还可以配置消息加入死信队列的时候的错误。

如何保证消息不会被重复消费

  1. 将消息进行分组。当我们指定了某个绑定所指向的消费组之后,往当前主题发送的消息在每个订阅消费组中,只会有一个订阅者接收和消费,从而实现了对消息的负载均衡。只所以之前会出现重复消费的问题,是由于默认情况下,任何订阅都会产生一个匿名消费组,所以每个订阅实例都会有自己的消费组,从而当有消息发送的时候,就形成了广播的模式.只需要在生产者和消费者加入group属性即可。

经过以上的流程分析,再将系统中的需要发送消息的位置进行埋点,实现解耦,将业务耗时程序进行分离,加快了接口响应的速度,暂时在系统中使用的有这些,下面如再有改动,再将stream进行改动,达到想要的效果。

​这样构建了生产消费模型,进行解耦出消息发送服务,独立于其他服务。完美解耦。

你可能感兴趣的:(Spring,Cloud,从入门到源码,rabbitmq,分布式,java)