pulsar使用注意点与消息广播

[消息持久化]: https://pulsar.apache.org/docs/next/concepts-architecture-overview#persistent-storage

Pulsar broker 负责处理通过 Pulsar的消息,包括消息的持久存储。默认情况下,对于每个topic,brokers只保留至少在一个 backlog 中的消息。backlog 是特定订阅的未确认的消息的集合。 每个主题可以有多个订阅者,所以每个主题可以有多个 backlog。
因此,在一个没有创建任何订阅的主题上不会保留任何消息(默认情况下)。
在 Pulsar 中,你有两种方式在命名空间的级别去修改这种行为:

通过设置消息保留策略持久化存储不在 backlog 内的消息
通过指定time to live(TTL) ,设置消息在指定的时间内不被确认的话,自动确认。

[消息保留策略]: https://pulsar.apache.org/docs/next/concepts-messaging#message-retention-and-expiry

当你在一个命名空间设置主题的保留策略,则必须设置两个一个大小限制和时间限制。您可以参考下表在pulsar-adminJava 中设置保留策略。

时限尺寸限制消息保留
-1-1无限保留
-1>0基于大小限制
>0-1基于时间限制
00禁用消息保留(默认)
0>0无效的
>00无效的
>0>0当时间或大小达到限制时,不会保留已确认的消息或没有活动订阅的消息。
保留设置适用于没有任何订阅的主题的所有消息,或已被所有订阅确认的消息。保留策略设置不会影响订阅主题的未确认消息。未确认的消息由积压配额控制。
当超出主题的保留限制时,最旧的消息将被标记为删除,直到保留的消息集再次落入指定的限制范围内。

默认值

可以使用以下两个参数在实例级别设置消息保留时间:defaultRetentionTimeInMinutes和defaultRetentionSizeInMB。默认情况下,这两个参数都设置为0。
这两个参数的详细信息请参考broker.conf配置文件。
要实现无限留存,将两个值都设置为 -1。

$ pulsar-admin namespaces set-retention my-tenant/my-ns \
  --size -1 \
  --time -1

要禁用消息保留配置,将值设置为 0。

$ pulsar-admin namespaces set-retention my-tenant/my-ns \
  --size 0 \
  --time 0

[生存时间 (TTL)]: https://pulsar.apache.org/docs/next/cookbooks-retention-expiry#set-the-ttl-for-a-namespace

默认情况下,Pulsar 会永久存储所有未确认的消息。 在大量消息未得到确认的情况下,可能会导致大量磁盘空间的使用。 如果需要考虑磁盘空间,可以设置生存时间(TTL),以确定未确认的消息将保留多长时间。

设置命名空间的TTL

使用 set-message-ttl 子命令并指定命名空间和TTL(以秒为单位,使用-ttl/–messageTTL参数指定)。
示例

$ pulsar-admin namespaces set-message-ttl my-tenant/my-ns \
  --messageTTL 120 # TTL of 2 minutes

从命名空间中删除消息

$ pulsar-admin namespaces remove-message-ttl my-tenant/my-ns

[偷看消息]: https://pulsar.apache.org/docs/next/admin-api-topics#peek-messages

用来查看当前消息中间件里面的消息内容,比如消费者没正常运行,不确定消息是否发送出来了还是消费者某段代码异常了

pulsar-admin topics peek-messages \
  --count 10 --subscription my-subscription \
  persistent://test-tenant/ns1/tp1 \

api

GET /admin/v2/:schema/:tenant/:namespace/:topic/subscription/:subName/position/:messagePosition

java

String topic = "persistent://my-tenant/my-namespace/my-topic";
String subName = "my-subscription";
int numMessages = 1;
admin.topics().peekMessages(topic, subName, numMessages);

produce的topic注意点

持久化topic的结构
persistent://tenant/namespace/topic
非持久化topic的结构
non-persistent://tenant/namespace/topic
代码例子

String finalTopic = "persistent://" + tenant + "/" + namespaces +"/" + topic;
Producer<byte[]> producer = client.newProducer()
    .topic(finalTopic)
    .create();

pulsar使用java对象

Producer<Demo> producer = client.newProducer(JSONSchema.of(Demo.class))
        .topic(topic)
        .batchingMaxPublishDelay(10, TimeUnit.MILLISECONDS)
        .sendTimeout(10, TimeUnit.SECONDS)
        .blockIfQueueFull(true)
        .create();
producer.newMessage()
        .key("demo") //message的
        .value(Demo.builder().name("阿三").age("123岁").build())
        .property("demo","demo-p")
        .sendAsync()
        .thenAccept(msgId -> {
            System.out.println("Message with ID " + msgId + " successfully sent");
        });
client.newConsumer()
        .topic("public/engma/non")
        .subscriptionName("subscription")
        .messageListener((consumer, msg) -> {
            try{
                System.out.println(msg.getKey());
                System.out.println(msg.getProperty("demo"));
                System.out.println(JSON.toJSONString(msg.getValue()));
                consumer.acknowledge(msg);
            }catch (Exception e){
                log.error(e.getMessage(),e);
                consumer.negativeAcknowledge(msg);
            }
        })
        .subscribe();

完整代码例子

public static void main(String[] args) {
        try {
            PulsarClient client = PulsarClient.builder()
                    .serviceUrl("pulsar://ip:port")
                    .build();
            String topic = "persistent://tenant/namespace/topic";
            String deadLetterTopic = topic + "-subscription-DLQ";
            Producer<Demo> producer = client.newProducer(JSONSchema.of(Demo.class))
                    .topic(topic)
                    .batchingMaxPublishDelay(10, TimeUnit.MILLISECONDS)//批量发送的最大等待时间
                    .sendTimeout(10, TimeUnit.SECONDS) //发送超时时间
                    .blockIfQueueFull(true) //当达到最大缓存数时是否block客户端
                    .create();
            producer.newMessage()
                    .key("demo") //message的
                    .value(Demo.builder().name("阿三").age("12岁").build())
                    .property("demo", "demo-p")
                    .sendAsync()
                    .thenAccept(msgId -> {
                        //todo 处理发送成功的事情,比如记录msgId
                    });
            client.newConsumer()
                    .topic(topic)
                    .subscriptionName("subscription")
                    .receiverQueueSize(100) //设置队列
                    .ackTimeout(3, TimeUnit.SECONDS) //确认超时
                    .enableRetry(true) //是否重试
                    .negativeAckRedeliveryDelay(1,TimeUnit.SECONDS) //拒绝超时
                    .deadLetterPolicy(
                            DeadLetterPolicy.builder()
                                    //可以指定最大重试次数,最大重试三次后,进入到死信队列
                                    .maxRedeliverCount(3)
                                    //重试队列,如果不设置重置队列需要设置consumer的subscriptionType Shared或Key_Shared 否则死信队列不起作用
                                    .retryLetterTopic(topic)
                                    //可以指定死信队列
                                    .deadLetterTopic(deadLetterTopic)
                                    .build()
                    )
                    .messageListener((consumer, msg) -> {
                        try {
                            //todo 业务代码
                            // msg.getKey() 获取消息的key
                            // msg.getProperty("demo") 获取消息设置的属性
                            // msg.getValue() 获取消息内容
                			//consumer.reconsumeLater(msg,1,TimeUnit.SECONDS); 消息重发,会进入重试队列
                            consumer.acknowledge(msg);
                        } catch (Exception e) {
                            log.error(e.getMessage(), e);
                            consumer.negativeAcknowledge(msg); //否定消息,触发重发机制
                        }
                    })
                    .subscribe();
        } catch (Exception e) {
            log.error(e.getMessage(), e);
        }
    }

可视化注意点

使用docker部署的情况

首先保证有pulsar

docker pull apachepulsar/pulsar:latest
docker run -d -it \
    -p 6650:6650 \
    -p 8080:8080 \
    -v pulsardata:/pulsar/data \
    -v pulsarconf:/pulsar/conf \
    --name pulsar-standalone \
    apachepulsar/pulsar:latest \
    bin/pulsar standalone

这边可能会报错:

java.io.FileNotFoundException: /pulsar/conf/standalone.conf (No such file or directory)

暂时没什么比较好的解决办法,另外使用中没有需要定制化的配置所以,这里忽略了这个问题,去掉了-v pulsarconf:/pulsar/conf \
接下来部署pulsar-manager

docker pull apachepulsar/pulsar-manager:v0.2.0
docker run -it \
    -p 9527:9527 -p 7750:7750 \
    -e SPRING_CONFIGURATION_FILE=/pulsar-manager/pulsar-manager/application.properties \
    -v $PWD/bkvm.conf:/pulsar-manager/pulsar-manager/bkvm.conf \
    --link pulsar-standalone \
    apachepulsar/pulsar-manager:v0.2.0

部署后可能会无法看到配置文件,这时候可以手动复制出来:

docker cp pulsar-manager:/pulsar-manager/pulsar-manager/application.properties /opt/pulsar/manager/application.properties
docker cp pulsar-manager:/pulsar-manager/pulsar-manager/bkvm.conf /opt/pulsar/manager/bkvm.conf

然后:

1.application.properties

bookie.enable=true
pulsar.peek.message=true

2.bkvm.conf

bookie.enable=true

pulsar可视化:pulsar/pulsar:http://localhost:7750/ui/index.html
bookie可视化:admin/admin:http://localhost:7750/bkvm/

7750端口不行就用9527试试

重启后如果提示账户密码错误,那么就手动添加一个账户

CSRF_TOKEN=$(curl http://backend-service:7750/pulsar-manager/csrf-token)
curl \
    -H "X-XSRF-TOKEN: $CSRF_TOKEN" \
    -H "Cookie: XSRF-TOKEN=$CSRF_TOKEN;" \
    -H 'Content-Type: application/json' \
    -X PUT http://backend-service:7750/pulsar-manager/users/superuser \
    -d '{"name": "admin", "password": "apachepulsar", "description": "test", "email": "username@test.org"}'

进入bookie可视化如果什么都看不到,那么就是地址错了,具体配置如下:
在这里插入图片描述

这里为什么使用pulsar-standalone:2181是因为启动的pulsar-manager容器,有这么一个配置:--link pulsar-standalone
目的是为了让pulsar-manager可以访问到pulsar容器

消息广播

pulsar的四种消息模式

在这里插入图片描述

Exclusive 独占模式

在这里插入图片描述

Exclusive 独占模式(默认模式):一个 Subscription 只能与一个 Consumer 关联,只有这个 Consumer 可以接收到 Topic 的全部消息,如果该 Consumer 出现故障了就会停止消费。 Exclusive 订阅模式下,同一个 Subscription 里只有一个 Consumer 能消费 Topic,如果多个 Consumer 订阅则会报错,适用于全局有序消费的场景。

// 构建消费者
Consumer<byte[]> consumer = pulsarClient.newConsumer()
// topic完整路径,格式为persistent://集群(租户)ID/命名空间/Topic名称,从【Topic管理】处复制 
.topic("persistent://pulsar-xxx/sdk_java/topic1")
// 需要在控制台Topic详情页创建好一个订阅,此处填写订阅名
.subscriptionName("sub_topic1")
// 声明消费模式为exclusive(独占)模式
.subscriptionType(SubscriptionType.Exclusive)
.subscribe();

在这里插入图片描述

共享模式(Shared)

在这里插入图片描述
消息通过 round robin 轮询机制(也可以自定义)分发给不同的消费者,并且每个消息仅会被分发给一个消费者。 当消费者断开连接,所有被发送给他,但没有被确认的消息将被重新安排,分发给其它存活的消费者。

// 构建消费者
Consumer<byte[]> consumer = pulsarClient.newConsumer()
// topic完整路径,格式为persistent://集群(租户)ID/命名空间/Topic名称,从【Topic管理】处复制 
.topic("persistent://pulsar-xxx/sdk_java/topic1")
// 需要在控制台Topic详情页创建好一个订阅,此处填写订阅名
.subscriptionName("sub_topic1")
// 声明消费模式为 Shared(共享)模式
.subscriptionType(SubscriptionType.Shared)
.subscribe();

多个 Shared 模式消费者。
在这里插入图片描述

灾备模式(Failover)

在这里插入图片描述
当存在多个 consumer 时,将会按字典顺序排序,第一个 consumer 被初始化为唯一接受消息的消费者。当第 一个 consumer 断开时,所有的消息(未被确认和后续进入的)将会被分发给队列中的下一个 consumer。

// 构建消费者
Consumer<byte[]> consumer = pulsarClient.newConsumer()
// topic完整路径,格式为persistent://集群(租户)ID/命名空间/Topic名称,从【Topic管理】处复制 
.topic("persistent://pulsar-xxx/sdk_java/topic1")
// 需要在控制台Topic详情页创建好一个订阅,此处填写订阅名
.subscriptionName("sub_topic1")
// 声明消费模式为灾备模式
.subscriptionType(SubscriptionType.Failover)
.subscribe();

多个 Failover 模式消费者。
在这里插入图片描述

KEY 共享模式(Key_Shared)

在这里插入图片描述

当存在多个 consumer 时,将根据消息的 key 进行分发,key 相同的消息只会被分发到同一个消费者。

// 构建消费者
Consumer<byte[]> consumer = pulsarClient.newConsumer()
// topic完整路径,格式为persistent://集群(租户)ID/命名空间/Topic名称,从【Topic管理】处复制 
.topic("persistent://pulsar-xxx/sdk_java/topic1")
// 需要在控制台Topic详情页创建好一个订阅,此处填写订阅名
.subscriptionName("sub_topic1")
// 声明消费模式为 Key_Shared(Key 共享)模式
.subscriptionType(SubscriptionType.Key_Shared) 
.subscribe();

多个 Key_Shared 模式消费者。
在这里插入图片描述

如何实现广播功能

Subscription

pulsar ⾥将 consumer 接收消息的过程称之为:subscription(订阅)
在一个topic里如果有多个不一样的订阅就可以实现广播的效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
[java参数的官方地址]:https://pulsar.apache.org/docs/next/client-libraries-java

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值