云原生时代消息中间件Pulsar(基本命令行操作、java操作)

租户、命名空间、topic介绍

什么是多租户?

多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离
简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。

Apache Pulsar 最初诞生于雅虎,当时就是为了解决雅虎内部各个部门之间数据的协调,所以多租户特性显得至关重用,Pulsar 从诞生之日起就考虑到多租户这一特性,并在后续的实现过程中,将其不断的完善。 多租户这一特性,使得各个部门之间可以共享同一份数据,不用单独部署独立的系统来操作数据,很好的保证了各部门间数据一致性的问题,同时简化维护成本。

Pulsar 的多租户设计符合上述要求:

  1. 使用身份验证、授权和 ACL(访问控制列表)确保其安全性
  2. 为每个租户强制执行存储配额
  3. 支持在运行时更改隔离机制,从而实现操作成本低和管理简单

Pulsar的多租户性质主要体现在topic的URL中, 其结构如下:

persistent://tenant/namespace/topic
从URL中可以看出tenant(租户)是topic最基本的单元(比命名空间和topic名称更为基本)

在这里插入图片描述

Pulsar多租户的相关特性_安全性(认证和授权)

一个多租户系统需要在租户内提供系统级别的安全性,细分来讲,主要可以归类为一下两点:

  1. 租户只能访问它有权限访问的 topics
  2. 不允许访问它无法访问的 topics

在 Pulsar 中,多租户的安全性是通过身份验证和授权机制实现的。当 client 连接到 pulsar broker 时,broker 会使用身份验证插件来验证此客户端的身份,然后为其分配一个 string 类型的 role token。role token 主要有如下作用:

  1. 判断 client 是否有对 topics 进行生产或消费消息的权限
  2. 管理租户属性的配置

Pulsar 目前支持一下几种身份认证, 同时支持自定义实现自己的身份认证程序

  1. TLS 客户端身份认证

  2. 雅虎的身份认证系统: Athenz

  3. Kerberos

  4. JSON Web Token 认证

在这里插入图片描述

Pulsar多租户的相关特性_隔离性

隔离性主要分为如下两种:

  1. 软隔离: 通过磁盘配额,流量控制和限制等手段

    存储:
    Apache Pulsar 使用Bookkeeper来作为其存储层, bookie是Bookkeeper的实例, Bookkeeper本身就是具有I/O分离(读写分离)的特性,可以很多的做好IO隔离, 提升读写的效率
    同时, 不同的租户可以为不同的NameSpace配置不同的存储配额, 当租户内消息的大小达到了存储配额的限制, Pulsar会采取相应的措施, 例如: 阻止消息生成, 抛异常 或丢弃数据等
    Broker:
    每个Borker使用的内存资源都是有上限的, 当Broker达到配置的CPU或内存使用的阈值后, Pulsar会迅速的将流量转移到负载较小的Broker处理
    在生产和消费方面, Pulsar都可以进行流量控制,租户可以配置发送和接收的速率,避免出现一个客户端占用当前Broker的所有处理资源

  2. 硬隔离: 物理资源隔离

​ Pulsar 允许将某些租户或名称空间与特定 Broker 进行隔离。这可确保这些租户或命名空间可以充分利用该特定 Broker 上的资源。

什么是名称空间

namespace是Pulsar中最基本的管理单元,在namespace这一层面,可以设置权限,调整副本设置,管理跨集群的消息复制,控制消息策略和执行关键操作。一个主题topic可以继承其所对应的namespace的属性,因此我们只需对namespace的属性进行设置,就可以一次性设置该namespace中所有主题topic的属性。

namespace有两种,分别是本地的namespace和全局的namespace:

  1. 本地namespace——仅对定义它的集群可见。
  2. 全局namespace——跨集群可见,可以是同一个数据中心的集群,也可以是跨地域中心的集群,这依赖于是否在namespace中设置了跨集群拷贝数据的功能。

虽然本地namespace和全局namespace的作用域不同,但是只要对他们进行适当的设置,都可以跨团队和跨组织共享。一旦生产者获得了namespace的写入权限,那么它就可以往namespace中的所有topic主题写入数据,如果某个主题不存在,则在生产者第一次写入数据时动态创建。

什么是Topic

Topic,话题主题的含义, 在一个名称空间下, 可以定义多个Topic 通过Topic进行数据的分类划分, 将不同的类别的消息放置到不同Topic, 消费者也可以从不同Topic中获取到相关的消息, 是一种更细粒度的消息划分操作, 同时在Topic下可以划分为多个分片, 进行分布式的存储操作, 每个分片下还存在有副本操作, 保证数据不丢失, 当然这些分片副本更多是由bookkeeper来提供支持
Pulsar 提供持久化与非持久化两种topic。 持久化topic是消息发布、消费的逻辑端点。 持久化topic地址的命名格式如下:

persistent://tenant/namespace/topic

非持久topic应用在仅消费实时发布消息与不需要持久化保证的应用程序。 通过这种方式,它通过删除持久消息的开销来减少消息发布延迟。 非持久化topic地址的命名格式如下:

non-persistent://tenant/namespace/topic

Pulsar基础命令

租户操作

  1. 获取租户列表

    cd /export/server/brokers/bin
    ./pulsar-admin tenants list
    
  2. 创建租户

    ./pulsar-admin tenants create my-tenant
    #在创建租户时,可以使用-r 或者 --admin-roles标志分配管理角色。可以用逗号分隔的列表指定多个角色。
    ./pulsar-admin tenants create my-tenant --admin-roles role1,role2,role3
    ./pulsar-admin tenants create my-tenant -r role1
    
  3. 删除租户 注意: 在删除的时候, 如果库下已经有名称空间, 是无法删除的,需要先删除名称空间

    ./pulsar-admin tenants delete my-tenant
    

名称空间操作

  1. 指定租户下创建名称空间(租户要有)

    cd /export/server/brokers/bin
    ./pulsar-admin namespaces create test-tenant/test-namespace
    
  2. 获取所有的名称空间列表

    ./pulsar-admin namespaces list test-tenant
    
  3. 删除名称空间

    ./pulsar-admin namespaces delete test-tenant/ns1
    

Topic 操作

注意: 不管是有分区还是没有分区, 创建topic后,如果没有任何操作, 60s后pulsar会认为此topic是不活动的, 会自动进行删除, 以避免生成垃圾数据

  1. 创建一个没有分区的topic

    cd /export/server/brokers/bin
    ./pulsar-admin topics create persistent://my-tenant/my-namespace/my-topic
    
  2. 创建一个有分区的topic

    ./pulsar-admin topics create-partitioned-topic persistent://my-tenant/my-namespace/my-topic  --partitions 4
    
  3. 列出当前某个名称空间下的所有Topic

    ./pulsar-admin topics list my-tenant/my-namespace
    #获取命名空间下的分区主题列表。
    ./pulsar-admin topics list-partitioned-topics my-tenant/my-namespace
    
  4. 更新Topic操作

    ./pulsar-admin topics update-partitioned-topic persistent://my-tenant/my-namespace/my-topic --partitions 8
    
  5. 删除Topic操作

    #删除没有分区的topic:
    ./pulsar-admin topics delete persistent://my-tenant/my-namespace/my-topic
    #删除有分区的topic
    ./pulsar-admin topics delete-partitioned-topic persistent://my-tenant/my-namespace/my-topic
    

Pulsar java操作

注意编辑hosts文件,配置master、slave1、slave2

创建maven项目导入依赖

        <dependency>
            <groupId>org.apache.pulsar</groupId>
            <artifactId>pulsar-client-all</artifactId>
            <version>2.8.1</version>
        </dependency>

租户操作

package com.example.demo_pulsar.topic;

import org.apache.pulsar.client.admin.PulsarAdmin;

import java.util.List;

//管理命名空间
public class CreateNamespaces {
    public static void main(String[] args) throws Exception {
        // 1- 创建Pulsar的Admin管理对象
        String serviceUrl = "http://master:8080";
        PulsarAdmin admin = PulsarAdmin.builder()
                .serviceHttpUrl(serviceUrl)
                .build();
        // 2- 创建名称空间
        admin.namespaces().createNamespace("itcast_pulsar_t/itcast_pulsar_n");
        // 3- 获取所有的名称空间
        System.out.println("获取当前有那些名称空间:");
        List<String> namespaces = admin.namespaces().getNamespaces("itcast_pulsar_t");
        for (String namespace : namespaces) {
            System.out.println(namespace);
        }
        // 4- 删除名称空间
        //admin.namespaces().deleteNamespace("itcast_pulsar_t/itcast_pulsar_n");
        // 6. 关闭资源
        admin.close();
    }
}

名称空间

package com.example.demo_pulsar.topic;

import org.apache.pulsar.client.admin.PulsarAdmin;

import java.util.List;

//2.名称空间
public class CreateNamespaces {
    public static void main(String[] args) throws Exception {
        // 1- 创建Pulsar的Admin管理对象
        String serviceUrl = "http://master:8080,slave1:8080,slave2:8080";
        PulsarAdmin admin = PulsarAdmin.builder()
                .serviceHttpUrl(serviceUrl)
                .build();
        // 2- 创建名称空间
        admin.namespaces().createNamespace("itcast_pulsar_t/itcast_pulsar_n");
        // 3- 获取所有的名称空间
        System.out.println("获取当前有那些名称空间:");
        List<String> namespaces = admin.namespaces().getNamespaces("itcast_pulsar_t");
        for (String namespace : namespaces) {
            System.out.println(namespace);
        }
        // 4- 删除名称空间
        //admin.namespaces().deleteNamespace("itcast_pulsar_t/itcast_pulsar_n");
        // 6. 关闭资源
        admin.close();
    }
}

Topic

package com.example.demo_pulsar.topic;

import org.apache.pulsar.client.admin.PulsarAdmin;

import java.util.List;

//3.topic
public class CreateTopic {
    public static void main(String[] args) throws Exception {
        // 1- 创建Pulsar的Admin管理对象
        String serviceUrl = "http://master:8080,slave1:8080,slave2:8080";
        PulsarAdmin admin = PulsarAdmin.builder()
                .serviceHttpUrl(serviceUrl)
                .build();
        //2. 创建Topic
        //创建一个持久化的带分区的Topic的
        // 2.1: 创建一个持久化的带分区的Topic的
        //admin.topics().createPartitionedTopic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic1", 3);
        // 2.2: 创建一个非持久化的带分区的Topic的
        //admin.topics().createPartitionedTopic("non-persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic2", 3);
        // 2.3: 创建一个持久化的不带分区的Topic的
        //admin.topics().createNonPartitionedTopic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3");
        // 2.4: 创建一个非持久化的不带分区的Topic的
        admin.topics().createNonPartitionedTopic("non-persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic4");

        // 3. 列出某个名称空间下, 所有的Topic
        // 无分区的topic
        List<String> topics = admin.topics().getList("itcast_pulsar_t/itcast_pulsar_n");
        for (String topic : topics) {
            System.out.println(topic);
        }
        // 有分区的topic
        topics = admin.topics().getPartitionedTopicList("itcast_pulsar_t/itcast_pulsar_n");
        for (String topic : topics) {
            System.out.println(topic);
        }
        // 4. 更新Topic: 增加分区数
        admin.topics().updatePartitionedTopic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic1", 5);
        int partitions = admin.topics().getPartitionedTopicMetadata("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic1").partitions;
        System.out.println("topic的分区数为:" + partitions);
        // 5. 删除Topic
        // 删除没有分区的
        //admin.topics().delete("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3");
        // 删除有分区的
        //admin.topics().deletePartitionedTopic("non-persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic2");
        // 6. 关闭资源
        admin.close();
    }
}

生产数据

  1. 同步模式

    // 模拟pulsar的生产者_同步模式
    public class PulsarProducerSyncTest {
        public static void main(String[] args) throws Exception {
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://master:6650,slave1:6650,slave2:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建生产者的对象
            Producer<byte[]> producer = client.newProducer()
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3")
                    .create();
            //3. 发送消息:
            producer.send("你好 Puslar...".getBytes());
            //4. 释放资源
            producer.close();
            client.close();
        }
    }
    
  2. 异步模式

    // 模拟pulsar的生产者_异步模式
    public class PulsarProducerAsyncTest {
        public static void main(String[] args) throws Exception {
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://master:6650,slave1:6650,slave2:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建生产者的对象
            Producer<byte[]> producer = client.newProducer()
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3")
                    .create();
            //3. 发送消息:
            producer.sendAsync("你好 Pulsar...".getBytes());
            // 如果采用异步发送数据, 由于需要先放置在缓存区中, 如果立即关闭, 会导致无法发送
            Thread.sleep(1000);
            //4. 释放资源
            producer.close();
            client.close();
        }
    }
    
  3. 基于schema

    public class User implements Serializable {
    
        private String name;
        private Integer age;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    }
    
    //3- 使用JAVA如何生产数据_基于schema的发送
    public class PulsarProducerSchemaTest {
        public static void main(String[] args) throws Exception {
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://master:6650,slave1:6650,slave2:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建生产者的对象
            AvroSchema<User> schema = AvroSchema.of(SchemaDefinition.<User>builder().withPojo(User.class).build());
            Producer<User> producer = client.newProducer(schema)
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3").create();
            //3. 发送消息:
            User user = new User();
            user.setName("张三");
            user.setAge(20);
            producer.send(user);
            Thread.sleep(10000);
            //4. 释放资源
            producer.close();
            client.close();
        }
    }
    

消费数据

  1. 同步方式

    //模拟pulsar的消费者
    public class PulsarConsumerTest {
        public static void main(String[] args) throws Exception {
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://master:6650,slave1:6650,slave2:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建消费者的对象
            Consumer<byte[]> consumer = client.newConsumer()
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3")
                    .subscriptionName("my-subscription")
                    .subscribe();
            //3. 循环获取读取
            while(true) {
                Message<byte[]> message = consumer.receive();
                try {
                    System.out.println("消息为:" + new String(message.getData()));
                    consumer.acknowledge(message);
                }catch ( Exception e) {
                    consumer.negativeAcknowledge(message);
                }
            }
        }
    }
    
  2. schema方式

    public class PulsarConsumerSchemaTest {
        public static void main(String[] args) throws Exception{
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://node1:6650,node2:6650,node3:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建消费者的对象
            Consumer<User2> consumer = client.newConsumer(AvroSchema.of(SchemaDefinition.<User2>builder().withPojo(User2.class).build()))
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3")
                    .subscriptionName("my-subscription")
                    .subscribe();
            //3. 循环获取读取
            while(true) {
                Message<User2> message = consumer.receive();
                try {
                    System.out.println("消息为:" +message.getValue());
                    consumer.acknowledge(message);
                }catch ( Exception e) {
                    consumer.negativeAcknowledge(message);
                }
            }
        }
    }
    
  3. 批量处理

    //模拟pulsar的消费者_同步模式
    public class PulsarConsumerBatchTest {
        public static void main(String[] args) throws Exception {
            //1. 获取pulsar的客户端对象
            ClientBuilder clientBuilder = PulsarClient.builder();
            clientBuilder.serviceUrl("pulsar://master:6650,slave1:6650,slave2:6650");
            PulsarClient client = clientBuilder.build();
            //2. 通过客户端创建消费者的对象
            Consumer<byte[]> consumer = client.newConsumer()
                    .topic("persistent://itcast_pulsar_t/itcast_pulsar_n/my-topic3")
                    .subscriptionName("my-subscription")
                    .batchReceivePolicy(BatchReceivePolicy.builder()
                            // 设置一次性最大获取多少条消息  默认值为 -1
                            .maxNumMessages(100)  
                           // 设置每条数据允许的最大的字节大小  默认值为: 10 * 1024 * 1024
                            .maxNumBytes(1024 * 1024) 
                           //设置等待的超时时间  默认值为 100
                            .timeout(200, TimeUnit.MILLISECONDS) 
                            .build())
                    .subscribe();
           //3. 循环获取读取
            while(true) {
                Messages<byte[]> messages = consumer.batchReceive(); // 批量读取数据
                for (Message<byte[]> message : messages) {
                    try {
                        System.out.println("消息为:" + new String(message.getData()));
    
                        consumer.acknowledge(message);
                    }catch ( Exception e) {
                        consumer.negativeAcknowledge(message);
                    }
                }
    
            }
        }
    }
    

声明

大部分来源于黑马:https://space.bilibili.com/415938397/search/video?keyword=pulsar

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值