2.1 :封装rabbitmq

架构封装得思考何时调用开发者的方法,开发者的思想是如何处理

1.定义交换机的名字和事件常量

public interface EventConstact {

    /**
     * 交换机的名称
     */
    String EXCHANGE_NAME = "event-exchange";

    /**
     * 事件类型常量
     */
    String EVENT_LOGIN = "login";//用户登录事件
    String EVENT_HOTAL_INSERT = "hotal_insert";//酒店新增事件
    String EVENT_HOTAL_ROOM_INSERT = "hotal_room_insert";//酒店客房新增事件
    String EVENT_HOTAL_PRICE_UPDATE = "hotal_price_update";//酒店价格对象修改事件
    String EVENT_HOTAL_CLICK = "hotal_click";//酒店点击率事件
    String EVENT_HOTAL_ROOM_UPDATE = "hotal_room_update";//酒店房间修改事件
    //.....
}

2.创建交换机:消费者通常创建队列,提供者创建交换机 因为提供者创建交互机没有消费者顶多把消息丢失,而不会报错,消费者创建队列没有人给他发消息他也不会报错。
3.参数:把交换机的名字封装出去,设置为持久化,不要自动删除

@Configuration
public class EventPublishConfiguration {
    /**
     * 事件发布者需要创建
     * 事件接收者也需要创建
     * 创建了一个交换机
     * @return
     */
    @Bean
    public DirectExchange getExchange(){
        return new DirectExchange(EventConstact.EXCHANGE_NAME, true, false);
    }
}

4.动态事件发布定义了一个类

//事件发布者
@Component
public class EventUtil {
    @Autowired
    private RabbitTemplate rabbitTemplate;

    public void publishEvent(String eventType, Object msg){
        rabbitTemplate.convertAndSend(EventConstact.EXCHANGE_NAME, eventType, msg);
    }

}

5.事件开始发布

    /**
     * 根据id查询酒店详情信息
     * @return
     */
    @RequestMapping("/getInfo")
    public ResultData<Hotal> getInfo(Integer hid, String devId){

        //查询酒店信息
        Hotal hotal = hotalService.getById(hid);
        //TODO 发生了一次点击率 ????????????????
        //boolmfilter
//        if (!bloomUtil.isExists("djl", devId + "-" + hid)) {
            System.out.println(devId + " - " + hid + "算一次点击率!!!");
            eventUtil.publishEvent(EventConstact.EVENT_HOTAL_CLICK, hotal);
            //不存在,添加一次
//            bloomUtil.addBloom("djl", devId + "-" + hid);
//        } else {
//            System.out.println(devId + " - " + hid + "已经点击过了!!!");
//        }
        return new ResultData<Hotal>().setData(hotal);
    }
}

6.动态的监听事件类型和事件处理方法定义一个接口


/**
 * 事件监听器的接口
 */
public interface EventListener<T> {

    /**
     * 监听的事件类型
     * @return
     */
    String getEventType();

    /**
     * 事件处理方法 - 核心
     * @param msg
     */
    void eventHandler(T msg);
}

7.在消费者消费事件只需要调用事件监听器的接口就可以消费,队列,交换机,路由建全部封装到事件总线中

/**
 * 酒店事件的监听处理器
 */
@Component
public class HotalEventListener implements EventListener<Hotal> {

    @Autowired
    private ICityService cityService;

    /**
     * 处理"酒店新增"事件
     * @return
     */
    @Override
    public String getEventType() {
        return EventConstact.EVENT_HOTAL_INSERT;
    }

    /**
     * 实际的触发方法
     * @param msg
     */
    @Override
    public void eventHandler(Hotal msg) {
        System.out.println("城市服务,接收到酒店新增事件," + msg);
        cityService.updateCityHNumber(msg.getCid(), 1);
    }
}

8.上面的这个类完全没有作用,因为HotalEventListener 根本没有监听队列不会调用他,我们得做两件事
1.创建队列
2. 队列绑定到交换机
3. 有几个 但是所有的队列的消息都发布到同个交换机上面,但是事件的消费是动态的,他会消费很多不同的事件,路由建是消费者决定,所以在路由建和队列和交换机是动态的:
4. 解决:
4.1队列的名称要实现动态的,每个微服务都有独有的名称,作为队列的名称,不管什么微服务调用都区别开来
4.2获得当前的处理器需要处理的事件类型 - 路由键
4.3绑定队列和交换机,路由建也得绑定
4.4 路由建只能绑定一个with方法返回值是String,不是数组所以得动态绑定
4.5在RabbitMQListener和EventConsumerConfiguration都注入了
@Autowired
private List eventListeners;
因为发布者和消费者都依赖了封装的包但是消费者没有创建eventListeners的类,会报错@ConditionalOnBean(EventListener.class)这个注解,判断spring容器有没有EventListener类,有就会被管理起来也说明了有是消费者没有就是提供者

@Configuration
//判断spring容器有没有EventListener类有就是消费者没有就是提供者
@ConditionalOnBean(EventListener.class)
public class EventConsumerConfiguration {

    @Value("${spring.application.name}")
    private String applicatiName;

    @Autowired
    private SpringContextUtil springContextUtil;

    @Autowired
    private List<EventListener> eventListeners;

    //每个服务都有一个独立的队列
    @Bean
    public Queue getQueue(){
        return new Queue(applicatiName + "-queue", true, false, false);
    }

    /**
     * 队列和交换机的绑定????????????  所有的队列的消息都发布到同个交换机上面,但是事件的消费是动态的,他会消费很多不同的事件,所以在路由建和队列和交换机是动态的:路由建与开发者决定
     * with的参数是string不能多次绑定所以只能多次绑定
     * @param getQueue
     * @param getExchange
     * @return
     */
    @Bean
    public Binding getBinding(Queue getQueue, DirectExchange getExchange){
        //循环所有的EventListener实现类
        eventListeners.stream().forEach(event -> {
            //获得当前的处理器需要处理的事件类型 - 路由键
            String eventType = event.getEventType();
            System.out.println("绑定的路由键:" + eventType);
            Binding binding = BindingBuilder.bind(getQueue).to(getExchange).with(eventType);
            //动态将binding对象注册到Spring容器中(万一有两个时间类型直接加hashCode)
            springContextUtil.registerBean(eventType + event.hashCode(), binding);
        });
        return null;
    }
}

@Component
public class SpringContextUtil implements BeanDefinitionRegistryPostProcessor {

    //注册bean的核心对象
    private BeanDefinitionRegistry beanDefinitionRegistry;

    /**
     * 自定义工具方法 - 注册Bean对象
     */
    public void registerBean(String beanName, Object bean){

        //将bean封装成BeanDefinition对象
        BeanDefinitionBuilder beanDefition = BeanDefinitionBuilder.genericBeanDefinition(bean.getClass(), new Supplier() {
            @Override
            public Object get() {
                return bean;
            }
        });

        //将BeanDefintion注册到Spring容器中
        beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefition.getBeanDefinition());
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        this.beanDefinitionRegistry = registry;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }
}
  1. 把rabbitmq的消息传给eventHandler处理该消息;监听message对象,message所有电子邮件的的超类,属性包括
    (1)消息地址
    (2)消息接收方
    (3)消息主题和主体等
@Component
@ConditionalOnBean(EventListener.class)
public class RabbitMQListener {

    @Autowired
    private List<EventListener> eventListeners;

    /**
     * 监听指定队列
     */
    @RabbitListener(queues = "${spring.application.name}-queue")
    public void msgHandler(Message message){
        //获得发布消息的路由键 - 事件类型
        String routingKey = message.getMessageProperties().getReceivedRoutingKey();
        //交给对应的EventListener处理
        eventListeners.stream().forEach(event -> {
            //判断事件类型和路由键是否匹配
            if (event.getEventType().equals(routingKey)) {
                //直接调用当前event处理该消息
                byte[] msgBytes = message.getBody();
                event.eventHandler(SerializationUtils.deserialize(msgBytes));
            }
        });
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
要保证RabbitMQ消息持久化,需要在以下几个方面进行配置: 1. Exchange和Queue的持久化:在RabbitMQ中,Exchange和Queue默认情况下是非持久化的,也就是说它们只存在于内存中,当RabbitMQ服务器重启时,这些非持久化的Exchange和Queue将被删除。为了实现Exchange和Queue的持久化,需要在创建Exchange和Queue时将其标记为持久化。 2. 消息的投递模式:消息持久化需要将消息先存储在磁盘上,然后再进行投递。因此,需要将消息的投递模式设置为"持久化"。 3. 消息的确认模式:为了确保消息被正确地投递并存储在磁盘上,需要启用消息的确认模式。消息确认模式有两种:Publisher Confirmation和Consumer Acknowledgement。 下面是保证RabbitMQ消息持久化的具体方法: 1. Exchange和Queue的持久化: ```java // 创建Exchange和Queue时设置为持久化 channel.exchangeDeclare("exchange_name", "direct", true); channel.queueDeclare("queue_name", true, false, false, null); // 将Queue绑定到Exchange上 channel.queueBind("queue_name", "exchange_name", "routing_key"); ``` 2. 消息的投递模式: ```java // 将消息设置为持久化 byte[] messageBytes = "Hello, RabbitMQ!".getBytes(); BasicProperties properties = new BasicProperties().builder() .deliveryMode(2) // 持久化消息 .build(); // 发送消息到持久化的Queue channel.basicPublish("exchange_name", "queue_name", properties, messageBytes); ``` 3. 消息的确认模式: ```java // 启用Publisher Confirmation模式 channel.confirmSelect(); // 发送消息到持久化的Queue,并等待确认 channel.basicPublish("exchange_name", "queue_name", properties, messageBytes); if (!channel.waitForConfirms()) { // 消息发送失败 System.out.println("Message send failed!"); } // 启用Consumer Acknowledgement模式 channel.basicConsume("queue_name", false, new DefaultConsumer(channel) { @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { // 处理消息 // 发送确认消息 channel.basicAck(envelope.getDeliveryTag(), false); } }); ``` 需要注意的是,以上三个方面都需要进行配置才能保证RabbitMQ消息的持久化,并且在消费者处理消息时,需要发送确认消息来确认消息已经被消费。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值