pulsar源码系列(一)---搭建broker调试环境


pulsar包括三个部分:

核心组成:
    broker集群: 负责接收用户请求。这里我们仅启动一个broker
    zookeeper集群: 保存集群元数据。这里我们的集群仅配置一个zk节点。
    bookkeeper集群:保存数据。这里我们的集群配置三个bookkeeper节点。因为默认的配置是w=2,r=2,所以我们这里配置3个节点(bookkeeper是无主结构,要求w+r>n)。当然,简单起见也可以只配置1个节点,不过要改启动配置,否则会无法写入和读取

集群简介:
    1:client向一个broker发送请求,请求读取topic a,如果该topic不归broker管,则broker把该topic所属的broker ip发回给client,client再去请求这个topic所在的broker,反之broker直接处理。
    2:当broker启动的时候,因为broker是无状态的,也就是不存储任何信息,所以他就要去一个元数据存放的地方拉取集群元数据,包括有哪些topic,这个broker要申请哪些topic的管理权限(申请以后其他broker不管理这些topic)等,这个元数据中心可以用etcd、zk等,常用的是zk,我们会在broker的配置文件里把这个元数据库的类型(etcd/zk/...)以及url地址告诉broker,然后broker启动后就去这个地址拉取元数据,我们这里就配置一个zk集群来当做元数据库。!!bookkeeper节点与bookkeeper节点之间无需互相感知,对于每个bookkeeper节点,只需要知道一个zk地址就行了,在启动时无需知道其他bookkeeper节点的存在。
    3:broker是无状态的,不会保存元数据也不会存储数据,所以还需要一个数据库来存储数据,pulsar使用bookkeeper集群来存储数据。broker收到client的写入请求后,就调用相关api把数据写入bookkeeper。因为broker是通过元数据库来知道集群含有哪些bookkeeper,所以bookkeeper启动时需要把自己注册到zookeeper中。也就是相当于解耦,broker和bookkeeper通过zk来知道对方的存在。

环境搭建:

前言:
    1:我们的一期目标是调试broker,不包括bookkeeper,所以搭建的时候对于zk和bookkeeper我们是直接从网上下载可执行包,对于broker,我们则是通过idea编译的方式来debug运行。
    2:注意zk和broker、bookkeeper之间的版本兼容性。pulsar3.3+版本用到的zk是3.9.2版本,否则broker会连不上zk。
    3:pulsar broker只能在linux下编译运行,所以我们需要新建一个虚拟机,然后安装idea,然后编译运行(略),否则在windows下通过idea启动broker的时候,broker会启动失败。

搭建步骤:
1:直接搭建zk集群(1个节点)。我是用k8s来部署的,所以我这里就列出yaml文件,如果不用k8s则直接使用相应的命令即可。
2:搭建bookkeeper集群(3个节点)。一样是用k8s来部署的
3:idea编译broker。
4:通过./bin/pulsar-admin手动创建cluster。目前的pulsar必须手动创建cluster才能访问,暂时还不懂为什么。
5:通过./bin/pulsar-client或者代码的方式来检测集群部署是否成功。
  

1:k8s搭建zk集群(一个节点)

包括svc,pvc,statefulset,configmap

#configmap
apiVersion: v1
kind: ConfigMap
metadata:
  name: zk3-9-2
  namespace: test-d-mw
data:
  zoo.cfg: |
    tickTime=2000
    initLimit=10
    syncLimit=5
    dataDir=/tmp/zookeeper #这个必须配置,我们保险起见会在这个目录下挂一个pvc
    clientPort=2181
    clientPortAddress=0.0.0.0  #必须配置这个,否则默认时localhost,导致pod外会连不上zk

#statefuset
apiVersion: apps/v1
kind: StatefulSet
metadata:
  namespace: test-d-mw
  name: zk3-9-2
spec:
  selector:
    matchLabels:
      app: zk3-9-2
  replicas: 1 #单节点集群
  template:
    metadata:
      labels:
        app: zk3-9-2
    spec:
      containers:
        - name: container
          image: bitnami/zookeeper:3.9.2 #注意zk镜像版本
          ports:
            - containerPort: 2181
              protocol: TCP
            - containerPort: 2888
              protocol: TCP
            - containerPort: 3888
              protocol: TCP

          #command: ["/bin/bash", "-c", "cp /opt/bitnami/zookeeper/conf/zoo_sample.cfg /opt/bitnami/zookeeper/conf/zoo.cfg;/opt/bitnami/zookeeper/bin/zkServer.sh start;while true; do sleep 3600; done"]
          #我们是通过zkServer.sh这个脚本来启动zk节点,这个脚本默认使用conf/zoo.cfg的配置文件,所以需要我们手动创建一个conf/zoo.cfg文件,我们可以直接复制zoo_sample.cfg,不过这样只能本地访问,我们需要从其他机器访问,所以需要配置一下clientPortAddress这个变量,这里我们通过configmap来是实现
          #zk的conf/目录下有一个zoo_sample.cfg的文
          #zkServer.sh start这个命令启动zk后就会返回,所以我们执行zkServer.sh start命令后还需要一个while循环来保持这个pod一直处于运行状态,shell通过分号来执行多条命令
          command: ["/bin/bash", "-c", "/opt/bitnami/zookeeper/bin/zkServer.sh start;while true; do sleep 3600; done"]

          volumeMounts:
          - name: zk-conf
            mountPath: /opt/bitnami/zookeeper/conf/zoo.cfg #zkServer.sh默认使用这个文件
            subPath: zoo.cfg
          - name: zk-data
            mountPath: /tmp/zookeeper
      volumes:
      - name: zk-conf
        configMap:
          name: zk3-9-2
      - name: zk-data #挂一个pvc保证pod重启数据不会丢失,因为pulsar集群中zk是一个核心节点
        persistentVolumeClaim:
          claimName: zk3-9-2

#svc
apiVersion: v1
kind: Service
metadata:
  name: zk3-9-2
  namespace: test-d-mw
spec:
  selector:
    app: zk3-9-2
  ports:
    - protocol: TCP
      port: 2181   # Zookeeper 客户端端口
      targetPort: 2181  
      name: p1
    - protocol: TCP
      port: 2888   # Zookeeper 集群通信端口
      targetPort: 2888  
      name: p2
    - protocol: TCP
      port: 3888   # Zookeeper 选举端口
      targetPort: 3888 
      name: p3

#pvc
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: zk3-9-2
  namespace: test-d-mw
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 20Gi

2:搭建bookkeeper集群。

官方文档:https://bookkeeper.apache.org/docs/deployment/manual/

搭建bookkeeper集群需要两步,第一步是初始化集群元数据(就是去zk里面写一些元数据);第二步就是启动bookkeeper节点。我也是通过k8s来部署的,所以这里也是给出yaml。总共需要三个yaml,一个config,一个bookkeeper集群手动初始化,一个bookkeeper节点。其中config用来告诉bookkeeper集群,注册到哪里。

#configmap: 用来告诉bookie zk的地址
apiVersion: v1
kind: ConfigMap
metadata:
  name: bookieconfig
  namespace: test-d-mw
data:
  bookie.conf: |
    zkServers=zk3-9-2.test-d-mw.svc.cluster.local:2181 
  

#因为当集群初始化以后zk里面会有对应的信息,然后shell format的时候会需要交互来确认是否重置,所以需要另外开一个deployment,然后进入容器,然后手动执行shell format来交互,否则就会不停的询问是否重置,然后导致cpu使用率飙升。(可以通过限制limits来解决)

apiVersion: apps/v1
kind: Deployment
metadata:
  name: bookkeeper-format
  namespace: test-d-mw
spec:
  replicas: 1
  selector:
    matchLabels:
      app: bookkeeper-keeper
  template:
    metadata:
      labels:
        app: bookkeeper-keeper
    spec:
      containers:
      - name: metaformat      #必须先执行 shell metaformat在zk里面格式化一些东西,一个集群只需要初始化一次,后续再运行的话这个程序会卡住
        image: apache/bookkeeper

        command: ["/bin/bash", "-c", "while true; do sleep 3600; done"]
      
        #因为重置操作需要确认即需要交互,而pod有可能频繁重启,所以这里通过手动进入pod然后执行初始化命令来初始化集群
        #进入容器执行 /opt/bitnami/bookkeeper/bin/bookkeeper shell metaformat 

        volumeMounts:
        - name: bookie-config   #需要知道zk的地址,我们通过configmap来实现
          mountPath: /dcf/conf
        env:
        - name: BOOKIE_CONF
          value: "/dcf/conf/bookie.conf"
      volumes:
      - name: bookie-config
        configMap:
          name: bookieconfig


#初始化完成后,就可以启动bookie节点了
#用来启动bookie节点,一个副本可以算作一个节点
apiVersion: apps/v1
kind: Deployment
metadata:
  name: bookkeeper
  namespace: test-d-mw
spec:
  replicas: 3 #一个副本对应一个节点,3个副本就对应3个节点,因为bookkeeper是无主结构,无需副本与副本之间协调,直接新增副本就行了,因为有zk
  selector:
    matchLabels:
      app: bookkeeper
  template:
    metadata:
      labels:
        app: bookkeeper
    spec:
      containers:
      - name: bookkeeper-container
        image: apache/bookkeeper
         
        #启动一个bookie节点
        command: ["/bin/bash", "-c", "/opt/bookkeeper/bin/bookkeeper bookie"]
      
        volumeMounts:
        - name: bookie-config  #需要zk地址,这里就通过configmap方式实现
          mountPath: /dcf/conf
        env:
        - name: BOOKIE_CONF
          value: "/dcf/conf/bookie.conf"
      volumes:
      - name: bookie-config
        configMap:
          name: bookieconfig

3:idea编译运行broker

包括两步,第一步是成功编译,能运行起来;第二步是能成功运行,即可以正常接受和响应外部请求

1:编译:通过mvn来编译。!!要求java 17以上;mvn 要xx以上,我这里直接用最新的,目前是最新mvn版本 3.9.2,直接上最新版mvn
    1.1:mvn package -DskipTests -Dspotbugs.skip=true -Dlicense.skip=true #需要这一步,否则会报一个xx.jar相关的问题(function相关的)
    1.2:mvn install -DskipTests -Dspotbugs.skip=true -Dlicense.skip=true #因为pulsar broker依赖pulsar的其他子模块,所以编译broker之前要install

    #执行完上两步后,就通过idea来编译运行broker了,broker启动时需要一个conf文件
2:成功运行。
    2.1:编写一个mybroker.conf文件,假设放在路径x下,即x/mybroker.conf,文件内容如下(pulsar项目也有一个配置文件模板,可以参考,路径位于pulsar/conf/broker.conf):
        managedLedgerDefaultEnsembleSize=2       #默认是2,如果只有一个节点,则需要手动改为1
        managedLedgerDefaultWriteQuorum=2        #默认是2,如果只有一个节点,则需要手动改为1
        managedLedgerDefaultAckQuorum=2          #默认是2,如果只有一个节点,则需要手动改为1
        clusterName=pulsar-cluster               #必须的
        allowLoopback=true                       #必须开启,没搞懂
        metadataStoreUrl=zk:10.255.86.228:2181   #元数据存储中心,因为pulsar broker是无状态的,所以所有数据都放在zk,所以也叫metaStore更合适,因为可以有多种存储集群比如etcd、zk、rocksdb
        advertisedAddress=192.168.129.129        #客户端请求一个topic,然后请求到达broker a后,broker 会告诉client 该topic位于哪个broker,也就是把该topic对应的advertiseAddress返回给clietn,然后client再去请求这个地址,默认是localhost:6650,如果client于broker不在同一台机器上,那么client请求localhost的时候就肯定会连接失败。设置advertiedAddress后本地pulsar-client/pulsar-admin还是照样可以用
    2.2:idea图形界面运行 pulsar/pulsar-broker/PulsarBrokerStart.java里面的main函数,主要,运行之前要添加命令行参数 -c x/mybroker.conf

4:通过脚本手动创建cluster,因为pulsar好像新增了一个allowCluster的概念(暂时还不懂),然后需要手动创建一个cluster,否则创建tenant、namespace、topic的时候会报错:cluster is empty。

#官方脚本使用文档:https://pulsar.apache.org/reference/#/next/
#不会的话可以直接百度或者chatgpt


./bin/pulsar-admin clusters list #查看所有集群,刚开始是空的
./bin/pulsar-admin clusters crate dcf #创建一个叫做dcf的集群
./bin/pulsar-admin clusters list  #再次查看则有一个叫dcf的集群,可以创建多个cluster

./bin/pulsar-admin tenants create dcf #创建叫做dcf的租户
./bin/pulsar-admin namespaces create dcf/dcf #在租户dcf下创建叫做dcf的命名空间
./bin/pulsar-admin topics create dcf/dcf/dcf #在租户dcf下的dcf命名空间下创建叫做dcf的命名空间


#注意pulsar-admin是管理集群的脚本,比如cluster/tenant/namespace/topic
#pulsar-client是管理生产者消费者以及其他的脚本
#topic有persistent和non-persistent之分,这个如果创建的时候不指定,则默认是persistent的即持久化的

./bin/pulsar-client consume "persistent://dcf/dcf/dcf" -s "sub-1" -n 0 #启动一个消费者,叫做sub-1,订阅persistent://dcf/dcf/dcf,从最新的位置开始消费
                                                                       #他会一直在这里监听,所以我们需要另外开一个窗口来发送消息到persistent://dcf/dcf/dcf
                                                                       #当另一个窗口发送消息后,这个消费者窗口就会输出消费的消息
./bin/pulsar-client produce persistent://dcf/dcf/dcf  --messages "Hello, World!" #发送消息到指定topic

5:通过go代码来访问pulsar集群。

官方文档:https://pulsar.apache.org/docs/next/client-libraries-go/ 注意,有多个pulsar包,不同的用途使用不同的pulsar包,比如admin包和client包。这里就不再重复了,官方示例很详细了。值得备注一下的如果你的go程序和broker不再同一台机器,那么必须在broker的配置文件mybroker.conf里配置advertisedAddress=broker的地址(默认是6650端口)。因为前面说过,client向一个broker发送请求,请求读取topic a,如果该topic不归broker管,则broker把该topic所属的broker ip发回给client,client再去请求这个topic所在的broker,而该broker的地址默认是localhost,如果client和broker不在同一台机器上,那么client访问localhost肯定就会报错了,所以需要配置broker的对外ip地址

package main

import (
	"context"
	"fmt"
	pulsaradmin "github.com/streamnative/pulsar-admin-go"  //注意一个是pulsar-admin
	"time" 
	"github.com/apache/pulsar-client-go/pulsar"            //一个是pulsar-client
)

func testAdmin() {
	cfg := &pulsaradmin.Config{
		WebServiceURL:              "http://192.168.129.129:8080", #管理pulsar走的是http协议,默认8080端口
		TLSAllowInsecureConnection: true,
	}
	admin, err := pulsaradmin.NewClient(cfg)
	if err != nil {
		fmt.Println("create adminClient failed")
		return
	}

	tenants, err := admin.Tenants().List()
	if err != nil {
		fmt.Println("get tenants failed:", err.Error())
	} else {
		fmt.Println("tenants:", tenants)
	}
}
func testProduce() {
	client, err := pulsar.NewClient(pulsar.ClientOptions{
		TLSAllowInsecureConnection: true,
		URL:                        "pulsar://192.168.129.129:6650",  #生产者消费者走的是pulsar协议,默认6650端口
		OperationTimeout:           30 * time.Second,
		ConnectionTimeout:          30 * time.Second,
	})
	client.Close()
	if err != nil {
		fmt.Print("error:", err.Error())
	}
	producer, err := createProduct(client)
	if err != nil {
		fmt.Println("createProduct   failed:", err.Error())
		return
	}
	_, err = producer.Send(context.Background(), &pulsar.ProducerMessage{
		Payload: []byte("hello world"),
	})
	producer.Close()
	if err != nil {
		fmt.Println("send msg failed:", err.Error())
	} else {
		fmt.Print("send msg ok: hello world")
	}
}
func createProduct(client pulsar.Client) (pulsar.Producer, error) {
	producer, err := client.CreateProducer(pulsar.ProducerOptions{
		Topic: "persistent://dcf/dcf/dcf",
	})

	if err != nil {
		return nil, err
	}
	return producer, nil

}
func main() {
	testAdmin()
	testProduce()
}
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
pulsar-java-spring-boot-starter是一个用于在Spring Boot应用程序中集成Apache Pulsar消息队列的开源库。Apache Pulsar是一个可扩展的、低延迟的分布式消息传递平台,它具有高吞吐量和高可靠性的特点。 pulsar-java-spring-boot-starter允许开发人员在Spring Boot应用程序中轻松地发送和接收Pulsar消息。它提供了一组容易使用的注解和工具类,简化了与Pulsar集群的交互。 使用pulsar-java-spring-boot-starter,开发人员可以通过添加依赖和配置一些属性来快速集成Pulsar到他们的Spring Boot应用程序中。一旦集成完成,开发人员可以使用注解来定义消息的生产者和消费者。通过生产者注解,开发人员可以将消息发送到Pulsar集群,并指定消息的主题和内容。通过消费者注解,开发人员可以订阅Pulsar主题,并定义接收和处理消息的方法。 除了基本的生产者和消费者功能,pulsar-java-spring-boot-starter还提供了一些其他特性。例如,它支持失败重试机制,当消息发送或接收出现问题时,可以自动重试。它还支持消息过滤器,可以按条件过滤接收的消息。而且,它还提供了一些监控和管理功能,可以方便地监控消息的生产和消费情况。 总之,pulsar-java-spring-boot-starter为Spring Boot开发人员提供了一种方便、快捷地集成Apache Pulsar消息队列的方法。它简化了与Pulsar集群的交互,提供了易于使用的注解和工具类,让开发人员可以更专注于业务逻辑的实现。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值