• 5.四种订阅模式、延时消息


    四种订阅模式

    独占Exclusive

    在独占模式下,只允许一个消费者附加到订阅上。如果多个消费者使用同一个订阅来订阅一个主题,就会发生错误。在下图中,只有消费者A-0被允许消费消息。

    此模式也是默认模式

    Exclusive subscriptions

    Demo

    subscriptionName相同,同时执行exclusive/comsumer1和exclusive/comsumer2就会报错

    package com.project.pulsar.delayMsgAndSubscriptionsModel;
    
    import com.project.pulsar.conf.PulsarConf;
    import org.apache.pulsar.client.admin.Namespaces;
    import org.apache.pulsar.client.admin.PulsarAdmin;
    import org.apache.pulsar.client.admin.Tenants;
    import org.apache.pulsar.client.api.*;
    import org.apache.pulsar.common.policies.data.DelayedDeliveryPolicies;
    import org.apache.pulsar.common.policies.data.TenantInfo;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.time.LocalDateTime;
    import java.time.ZoneOffset;
    import java.util.HashSet;
    import java.util.Set;
    
    /**
     * 独占模式
     */
    @RestController
    public class ExclusiveMessageController {
        @Autowired
        PulsarConf pulsarConf;
    
        String tenantName = "delay";//调用接口创建的租户,创建方法见com.project.pulsar.persistent.PersistentController.java
        String namespace = "mySpace";//调用接口创建的命名空间,创建方法见com.project.pulsar.persistent.PersistentController.java
    
        /**
         * 生产消息
         *
         * @param msg
         * @throws PulsarClientException
         */
        @GetMapping("/exclusive/sendMsg")
        public MessageId sendMsg(String msg) throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Producer<byte[]> producer1 = pulsarFactory.newProducer()
                    .topic(topic)
                    .create();
    
            return producer1.send(msg.getBytes());
        }
    
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/exclusive/comsumer")
        public void comsumerByArtificial() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Exclusive)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/exclusive/comsumer2")
        public void comsumerByArtificial2() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Exclusive)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    }
    

    代码下载

    代码见此delayMsgAndSubscriptionsModel包下ExclusiveMessageController.java

    灾备Failover

    在故障转移模式中,多个消费者可以附加到同一个订阅。为非分区主题或分区主题的每个分区挑选一个主消费者并接收消息。当主消费者断开连接时,所有(未确认的和后续的)消息都被传递给排在后面的消费者。对于分区主题,经纪人将按优先级和消费者名称的词汇顺序对消费者进行排序。对于非分区主题,经纪人将按照消费者订阅非分区主题的顺序挑选消费者。在下图中,消费者B-0是主消费者,而如果消费者B-0断开连接,消费者B-1将是排在后面的消费者接收消息。

    Failover subscriptions

    Demo

    package com.project.pulsar.delayMsgAndSubscriptionsModel;
    
    import com.project.pulsar.conf.PulsarConf;
    import org.apache.pulsar.client.api.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 灾备模式
     */
    @RestController
    public class FailoverMessageController {
        @Autowired
        PulsarConf pulsarConf;
    
        String tenantName = "delay";//调用接口创建的租户,创建方法见com.project.pulsar.persistent.PersistentController.java
        String namespace = "mySpace";//调用接口创建的命名空间,创建方法见com.project.pulsar.persistent.PersistentController.java
    
        /**
         * 生产消息
         *
         * @param msg
         * @throws PulsarClientException
         */
        @GetMapping("/failover/sendMsg")
        public MessageId sendMsg(String msg) throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Producer<byte[]> producer1 = pulsarFactory.newProducer()
                    .topic(topic)
                    .create();
    
            return producer1.send(msg.getBytes());
        }
    
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/failover/comsumer")
        public void comsumerByArtificial() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Failover)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/failover/comsumer2")
        public void comsumerByArtificial2() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Failover)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    }
    

    代码下载

    代码见此delayMsgAndSubscriptionsModel包下FailoverMessageController.java

    共享Shared

    在共享或循环模式下,多个使用者可以附加到同一订阅。消息在消费者之间以循环分发的方式传递,任何给定的消息只传递给一个消费者。当消费者断开连接时,发送给它但未确认的所有消息将重新安排发送给其余消费者。

    在下图中,Consumer-C-1和Consumer-C-2可以订阅该主题,但Consumer-C-3和其他人也可以订阅。

    Shared subscriptions

    Demo

    package com.project.pulsar.delayMsgAndSubscriptionsModel;
    
    import com.project.pulsar.conf.PulsarConf;
    import org.apache.pulsar.client.api.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 共享模式
     */
    @RestController
    public class ShareMessageController {
        @Autowired
        PulsarConf pulsarConf;
    
        String tenantName = "delay";//调用接口创建的租户,创建方法见com.project.pulsar.persistent.PersistentController.java
        String namespace = "mySpace";//调用接口创建的命名空间,创建方法见com.project.pulsar.persistent.PersistentController.java
    
        /**
         * 生产消息
         *
         * @param msg
         * @throws PulsarClientException
         */
        @GetMapping("/share/sendMsg")
        public MessageId sendMsg(String msg) throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Producer<byte[]> producer1 = pulsarFactory.newProducer()
                    .topic(topic)
                    .create();
    
            return producer1.send(msg.getBytes());
        }
    
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/share/comsumer")
        public void comsumerByArtificial() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Shared)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/share/comsumer2")
        public void comsumerByArtificial2() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Shared)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    }
    

    代码下载

    代码见此delayMsgAndSubscriptionsModel包下ShareMessageController.java

    按照Key共享Key_Shared

    在Key_Shared模式下,多个消费者可以附加到同一个订阅。消息在消费者之间分布传递,具有相同密钥或相同订购密钥的消息只传递给一个消费者。无论消息被重新传递多少次,它都会被传递给同一个消费者。当一个消费者连接或断开连接时,将导致所服务的消费者对消息的某些键进行改变。

    当您使用Key_Shared模式时,Key_Shared模式的局限性,应注意:您需要指定键或orderingKey的消息。你不能使用与Key_Shared模式的累积确认。你的生产者应禁用批处理或使用一个基于批处理生成器。

    Key_Shared subscriptions

    Demo

    package com.project.pulsar.delayMsgAndSubscriptionsModel;
    
    import com.project.pulsar.conf.PulsarConf;
    import org.apache.pulsar.client.api.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.Bean;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    /**
     * 共享模式
     */
    @RestController
    public class KeyShareMessageController {
        @Autowired
        PulsarConf pulsarConf;
    
        String tenantName = "delay";//调用接口创建的租户,创建方法见com.project.pulsar.persistent.PersistentController.java
        String namespace = "mySpace";//调用接口创建的命名空间,创建方法见com.project.pulsar.persistent.PersistentController.java
    
        /**
         * 生产消息
         *
         * @param msg
         * @throws PulsarClientException
         */
        @GetMapping("/keyShare/sendMsg")
        public MessageId sendMsg(String msg) throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            //持久化         租户   命名空间   主题
            Producer<byte[]> producer1 = pulsarFactory.newProducer()
                    .topic(topic)
                    .create();
    
            return producer1.send(msg.getBytes());
        }
    
    
    
        @Bean
        public void comsumerByListener1() throws PulsarClientException {
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            MessageListener myMessageListener = (consumer, msg) -> {
                try {
                    System.out.println("comsumerByListener1: " + new String(msg.getData()));
                    consumer.acknowledge(msg);
                } catch (Exception e) {
                    consumer.negativeAcknowledge(msg);
                }
            };
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscriptionByListener")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Key_Shared)
                    .messageListener(myMessageListener)
                    .subscribe();
        }
    
        @Bean
        public void comsumerByListener2() throws PulsarClientException {
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-topic";
            MessageListener myMessageListener = (consumer, msg) -> {
                try {
                    System.out.println("comsumerByListener2: " + new String(msg.getData()));
                    consumer.acknowledge(msg);
                } catch (Exception e) {
                    consumer.negativeAcknowledge(msg);
                }
            };
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscriptionByListener")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Key_Shared)
                    .messageListener(myMessageListener)
                    .subscribe();
        }
    }
    

    代码下载

    代码见此delayMsgAndSubscriptionsModel包下KeyShareMessageController.java

    延时消息

    仅对Share和KeyShare模式有效,其他模式均为立即发送

    延时消息有两种模式:

    1. 指定时长后发送

      deliverAfter(3L, TimeUnit.MINUTES)

    2. 指定时间发送

      deliverAt(LocalDateTime.of(2021, 9, 20, 16, 20).toInstant(ZoneOffset.of("+8")).toEpochMilli() )

    Demo

    package com.project.pulsar.delayMsgAndSubscriptionsModel;
    
    import com.project.pulsar.conf.PulsarConf;
    import org.apache.pulsar.client.api.*;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.web.bind.annotation.GetMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.concurrent.TimeUnit;
    
    /**
     * 延时消息
     */
    @RestController
    public class DelayMessageController {
        @Autowired
        PulsarConf pulsarConf;
    
        String tenantName = "delay";//调用接口创建的租户,创建方法见com.project.pulsar.persistent.PersistentController.java
        String namespace = "mySpace";//调用接口创建的命名空间,创建方法见com.project.pulsar.persistent.PersistentController.java
    
        /**
         * 生产消息
         *
         * @param msg
         * @throws PulsarClientException
         */
        @GetMapping("/delay/sendMsg")
        public void sendMsg(String msg) throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-delay-topic";
            //持久化         租户   命名空间   主题
            Producer<byte[]> producer = pulsarFactory.newProducer()
                    .topic(topic)
                    .create();
            producer.newMessage().deliverAfter(2L, TimeUnit.MINUTES).key("key-1").value("message-1-1".getBytes()).send();
            //        //    MessageId send = producer1.newMessage().deliverAfter(3L, TimeUnit.MINUTES).value("Hello Pulsar!".getBytes()).send();//指定消息在X时分秒后执行
    //        MessageId send =producer1.newMessage().deliverAt(
    //                LocalDateTime.of(2021, 9, 20, 16, 20).toInstant(ZoneOffset.of("+8")).toEpochMilli()
    //        ).value(msg.getBytes()).send();//指定消息在2021/09/20 15:52分发送,如发送时日期已过,会立刻发送
        }
    
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/delay/comsumer")
        public void comsumerByArtificial() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-delay-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Key_Shared)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    
        /**
         * 手动执行获取消息
         *
         * @throws PulsarClientException
         */
        @GetMapping("/delay/comsumer2")
        public void comsumerByArtificial2() throws PulsarClientException {
            PulsarClient pulsarFactory = pulsarConf.pulsarFactory();
            String topic = "persistent://" + tenantName + "/" + namespace + "/my-delay-topic";
            //持久化         租户   命名空间   主题
            Consumer<byte[]> consumer = pulsarFactory.newConsumer()
                    .topic(topic)
                    .subscriptionName("my-subscription")//必须名称一致,才可触发下一行,否则就是两个不同的消费者
                    .subscriptionType(SubscriptionType.Key_Shared)//指定模式
                    .subscribe();
            Message<byte[]> receive = consumer.receive();
            System.out.println(new String(receive.getData()));
            consumer.acknowledge(receive);//确认消息被消费
            consumer.close();
        }
    }
    

    代码下载

    代码见此delayMsgAndSubscriptionsModel包下DelayMessageController.java

    Pulsar还有很多特性,写不过来了,以后用到再总结,有问题就官网、Github、谷歌、百度

    1.所写技术都是我工作中用到的
    2.所写技术都是自己从项目中提取的
    3.所有配置搭建流程都经过2到3遍的测试
    4.因都是工作中使用的技术,所以不确定是否有转载的,如果有,请及时通知
  • 相关阅读:
    App调试的几个命令实践【转】
    解决sdk更新时候报错 http://dl-ssl.google.com/android上不去,链接拒绝
    fastjson序列化排序问题
    Java中的四种引用
    equal&==&hashcode
    ThreadPool线程池的关注点
    JVM的本地方法栈
    JVM的堆分配
    JVM的类装载子系统
    JVM的数据类型
  • 原文地址:https://www.cnblogs.com/rb2010/p/15314695.html
Copyright © 2020-2023  润新知