Pulsar3.0 介绍

Pulsar3.0 是 Pulsar 社区推出的第一个 LTS 长期支持版本。

如图所示,LTS 版本会最长支持到 36 个月,而 Feature 版本最多只有六个月;类似于我们使用的 JDK11,17,21 都是可以长期使用的;所以也推荐大家都升级到 LTS 版本。


作为首个 LTS 版本,3.0 自然也是自带了许多新特性,这个会在后续介绍。

升级指南

先来看看升级指南:



在官方的兼容表中会发现:不推荐跨版本升级。

也就是说如果你现在还在使用的是 2.10.x,那么推荐是先升级到 2.11.x 然后再升级到 3.0.x.

而且根据我们的使用经验来看,首个版本是不保险的,即便是 LTS 版本;

所以不推荐直接升级到 3.0.0,而是更推荐 3.0.1+,这个小版本会修复 3.0 所带来的一些 bug。

先讲一下我们的升级流程,大家可以用做参考。

升级前准备

根据我们的使用场景,为了以防万一,首先需要将我们的插件依赖升级到对应的版本。



其实简单来说就是更新下依赖,然后再重新打包,在后续的流程进行测试。

预热镜像

之后是预热镜像,我们使用 harbor 搭建了自己的 docker 镜像仓库,这样在升级重启镜像的时候可以更快的从内网拉取镜像。

毕竟一个 pulsar-all 的镜像也不小,尽量的缩短启动时间。

预热的过程也很简单:

docker pull apachepulsar/pulsar-all:3.0.1

docker tag apachepulsar/pulsar-all:3.0.1 harbor-private.xx.com/pulsar/pulsar-all:3.0.1

docker image push harbor-private.xx.com/pulsar/pulsar-all:3.0.1

之后升级的时候就可以使用私服的镜像了。

功能测试

我这边有写了一个 cli 可以帮我快速创建或升级一个集群,然后触发我所编写的功能测试。

./pulsar-upgrade-cli upgrade pulsar-test ./charts/pulsar --version x.x.x -f charts/pulsar/values.yaml -n pulsar-test

这个 cli 很简单,一共就做三件事:

  • 使用 helm 接口升级集群
  • 等待所有的 Pod 都升级成功
  • 触发功能测试

之后的效果如下:

主要就是覆盖了我们的使用场景,都跑通过之后才会走后续的流程。

运行监控

之后会启动一个 200 左右的并发生产和消费数据,模拟线上的使用情况,会一直让这个任务跑着,大概一晚上就可以了,第二天通过监控查看:

  • 应用有无异常日志
  • 流量是否正常
  • 各个组件的内存占用
  • 写入延迟等信息

升级步骤

组件的升级步骤这里参考了官方指南:

https://pulsar.apache.org/docs/3.1.x/administration-upgrade/#upgrade-zookeeper-optional

  • 升级ZK
  • 关闭auto recovery
  • 升级Bookkeeper
  • 升级Broker
  • 升级Proxy
  • 开启auto recovery

只要一步步按照这个流程走,问题不大,哪一步出现问题后需要及时回滚,回滚流程参考下面的回滚部分。

同时在升级过程中需要一直查看 broker 的 error 日志,如果有明显的不符合预期的日志一定要注意。

在升级 bookkeeper 的时候,broker 可能会出现 bk 连接失败的异常,这个可以不用在意。

线上验证

都升级完后就是线上业务验证环节了:

  • 查看监控面板,是否有明显的流量、内存、延迟的异常指标。 2023-12-24
  • topic 元数据完整性验证:这个是因为我们这次升级出了一个 topic 被删除的 bug,所以需要重点验证下;这部分会在下次详细分析。 2023-12-24
  • 查看业务消息收发有无异常 2023-12-24
  • 链路查询是否正常,我们有一个消息链路查询的页面,主要是使用 Pulsar-SQLbroker-interceptor 实现的。 2023-12-24

异常回滚

当出现异常的时候需要立即回滚,这里的异常一般就是消息收发异常,客户端掉线等。

经过我的测试 3.0.x 的存储和之前的版本是兼容的,所以 bookkeeper 都能降级其他的组件就没啥可担心的了。

需要降级时直接将所有组件降级为上一个版本即可。

灾难恢复

因为是从 2.x 升级到 3.x 也是涉及到了跨大版本,所以也准备了灾难恢复的方案。

比如极端情况下升级失败,所有数据丢失的情况。

整个灾难恢复的主要目的就是恢复后的集群对外提供的域名不发生变化,同时所有的客户端可以自动重连上来,也就是最坏的情况下所有的数据丢了可以接受,但不能影响业务正常使用。

所以我们的流程如下:

备份 topic

@SneakyThrows
@Test
void backup(){
List<String> topicList = pulsarAdmin.topics().getPartitionedTopicList("tenant/namespace");
log.info("topic size={}",topicList.size());
// create a custom thread pool
CopyOnWriteArrayList<TopicMeta> dataList = new CopyOnWriteArrayList<>();
ExecutorService customThreadPool = Executors.newFixedThreadPool(10);
for (String topicName : topicList) {
customThreadPool.execute(()-> {
PartitionedTopicMetadata metadata;
try {
metadata = pulsarAdmin.topics().getPartitionedTopicMetadata(topicName);
TopicMeta topicMeta = new TopicMeta(); // backup topic
topicMeta.setName(topicName);
topicMeta.setPartition(metadata.partitions); // backup permission
Map<String, Set<AuthAction>> permissions = pulsarAdmin.topics().getPermissions(topicName);
topicMeta.setPermissions(permissions); // back sub
List<String> subscriptions = new ArrayList<>();
PartitionedTopicStats topicStats = pulsarAdmin.topics().getPartitionedStats(topicName, true);
topicStats.getSubscriptions().forEach((k,v)-> subscriptions.add(k));
topicMeta.setSubscriptions(subscriptions); dataList.add(topicMeta);
} catch (PulsarAdminException e) {
throw new RuntimeException(e);
} }); }
customThreadPool.shutdown();
while (!customThreadPool.isTerminated()) {
}
log.info("{}",dataList.size());
log.info("{}",JSONUtil.toJsonStr(dataList));
} // TopicMetaData
@Data
public class TopicMeta {
private String name;
private int partition;
Map<String, Set<AuthAction>> permissions;
List<String> subscriptions = new ArrayList<>();
}

第一步是备份 topic:

  • topic 主要是名称和分区数量
  • 备份权限
  • 备份 topic 的订阅者

公私钥备份

因为我们客户端使用了 JWT 验证,所有为了使得恢复的 Pulsar 集群可以让客户端无缝切换到新集群,因此必须得使用相同的公私钥。

这个其实比较简单,我们使用的是 helm 安装的集群,所以只需要备份好 Secret 即可。

apiVersion: v1
data:
PRIVATEKEY: XXX
PUBLICKEY: XXX
kind: Secret
metadata:
name: pulsar-token-asymmetric-key
namespace: pulsar
type: Opaque # 还有几个 superUser 的 Secret

数据恢复

创建新集群

首先使用 helm 重新创建一个新集群:

./scripts/pulsar/prepare_helm_release.sh -n pulsar -k pulsar

helm install \    --values charts/pulsar/values.yaml \    --set namespace=pulsar\
--set initialize=true \
pulsar ./charts/pulsar -n pulsar

恢复公私钥

直接使用刚才备份的公私钥覆盖到新集群即可。

恢复namespace

进入 toolset pod 创建需要使用的 tenant/namespace

k exec -it pulsar-toolset-0 -n pulsar bash

bin/pulsar-admin tenants create tenant

bin/pulsar-admin namespaces create tenant/namespace

元数据恢复

之后便是最重要的元数据恢复了:

@SneakyThrows
@Test
void restore() {
PulsarAdmin pulsarAdmin = PulsarAdmin.builder().serviceHttpUrl("http://url:8080")
.authentication(AuthenticationFactory.token(token))
.build();
Path filePath = Path.of("restore-ns.json");
String fileContent = Files.readString(filePath);
List<TopicMeta> topicMetaList = JSON.parseArray(fileContent, TopicMeta.class);
ExecutorService customThreadPool = Executors.newFixedThreadPool(50);
for (TopicMeta topicMeta : topicMetaList) {
customThreadPool.execute(() -> {
// Create topic
try {
pulsarAdmin.topics().createPartitionedTopic(topicMeta.getName(), topicMeta.getPartition());
} catch (PulsarAdminException e) {
log.error("Create topic error");
}
// Create sub
for (String subscription : topicMeta.getSubscriptions()) {
try {
pulsarAdmin.topics().createSubscription(topicMeta.getName(), subscription, MessageId.latest);
} catch (PulsarAdminException e) {
log.error("createSubscription error");
} }
// Grant permission
topicMeta.getPermissions().forEach((role, authActions) -> {
permission(pulsarAdmin, topicMeta.getName(), role, authActions);
});
log.info("topic:{} restore success", topicMeta.getName()); }); }
customThreadPool.shutdown();
while (!customThreadPool.isTerminated()) {
} log.info("restore success");
} private synchronized void permission(PulsarAdmin pulsarAdmin, String topic, String role, Set<AuthAction> authActions) {
try {
pulsarAdmin.topics().grantPermission(topic, role, authActions);
} catch (PulsarAdminException e) {
log.error("grantPermission error", e);
}
}

流程和备份类似:

  • 创建分区 topic
  • 创建订阅者
  • 授权角色信息

因为授权接口限制了并发调用,所有需要加锁,导致整个恢复的流程就会比较慢。

8000 topic 的 namespace 大概恢复时间为 40min 左右。

之后依次恢复其他 namespace 即可。

恢复 police

admin.namespaces().setNamespaceMessageTTL("tenant/namespace", 3600 * 6);
admin.namespaces().setBacklogQuota("tenant/namespace", BacklogQuota)

如果之前的集群有设置 TTL 或者是 backlogQuota 时都需要手动恢复。

总结

以上就是整个升级和灾难恢复的流程,当然灾难恢复希望大家不要碰到。

我会在下一篇详细介绍 Pulsar 3.0 的新功能以及所碰到的一些坑。

Pulsar3.0 升级指北的更多相关文章

  1. java 8 - java 17 升级指北

    2014年发布的java SE 8和2017年发布的java EE 8,至今还是使用最广泛的java版本,大部分java开发者对于java 8之后的升级总是敬而远之,这跟java 9以后的破坏性升级和 ...

  2. [转] iOS开发者的Weex伪最佳实践指北

    [From] http://www.cocoachina.com/ios/20170601/19404.html 引子 这篇文章是笔者近期关于Weex在iOS端的一些研究和实践心得,和大家一起分享分享 ...

  3. Python 简单入门指北(二)

    Python 简单入门指北(二) 2 函数 2.1 函数是一等公民 一等公民指的是 Python 的函数能够动态创建,能赋值给别的变量,能作为参传给函数,也能作为函数的返回值.总而言之,函数和普通变量 ...

  4. Python 简单入门指北(一)

    Python 简单入门指北(一) Python 是一门非常容易上手的语言,通过查阅资料和教程,也许一晚上就能写出一个简单的爬虫.但 Python 也是一门很难精通的语言,因为简洁的语法背后隐藏了许多黑 ...

  5. 可能比文档还详细--VueRouter完全指北

    可能比文档还详细--VueRouter完全指北 前言 关于标题,应该算不上是标题党,因为内容真的很多很长很全面.主要是在官网的基础上又详细总结,举例了很多东西.确保所有新人都能理解!所以实际上很多东西 ...

  6. Celery入门指北

    Celery入门指北 其实本文就是我看完Celery的官方文档指南的读书笔记.然后由于我的懒,只看完了那些入门指南,原文地址:First Steps with Celery,Next Steps,Us ...

  7. 后端API入门到放弃指北

    后端API入门学习指北 了解一下一下概念. RESTful API标准] 所有的API都遵循[RESTful API标准]. 建议大家都简单了解一下HTTP协议和RESTful API相关资料. 阮一 ...

  8. ThinkPHP 3.2.x 集成极光推送指北

    3.2版本已经过了维护生命周期,官方已经不再维护,请及时更新至5.0版本 -- ThinkPHP 官方仓库 以上,如果有条件,请关闭这个页面,然后升级至 ThinkPHP 5,如果由于各种各样的原因无 ...

  9. c#封装DBHelper类 c# 图片加水印 (摘)C#生成随机数的三种方法 使用LINQ、Lambda 表达式 、委托快速比较两个集合,找出需要新增、修改、删除的对象 c# 制作正方形图片 JavaScript 事件循环及异步原理(完全指北)

    c#封装DBHelper类   public enum EffentNextType { /// <summary> /// 对其他语句无任何影响 /// </summary> ...

  10. VMware Workstation 安装以及Linux虚拟机安装 指北

    最近有挺多小伙伴跟我说起虚拟机这个东西,所以,今天就给大家写一篇虚拟机安装使用指北吧. 虚拟机(英语:virtual machine),在计算机科学中的体系结构里,是指一种特殊的软件,可以在计算机平台 ...

随机推荐

  1. 深入理解Linux内核——内存管理(4)——伙伴系统(1)

    提要:本系列文章主要参考MIT 6.828课程以及两本书籍<深入理解Linux内核> <深入Linux内核架构>对Linux内核内容进行总结. 内存管理的实现覆盖了多个领域: ...

  2. Hadoop环境安装与配置

    1.基础操作系统环境安装(略) 2.JDK的安装与配置 当前各大数据软件如Hadoop等,仍然停留在Java 8上,在本实验选用的是Java 8.在自己的Linux系统中,jdk可以使用如下命令进行一 ...

  3. 好好回答下 TCP 和 UDP 的区别!

    写了这么多篇关于 TCP 和 UDP 的文章,还没有好好聊过这两个协议的区别,这篇文章我们就来开诚布公的谈一谈. 关于 TCP 和 UDP ,想必大家都看过一张这样的图. 有一个小姑娘在对着瓶口慢慢的 ...

  4. 分拣平台API安全治理实战 | 京东物流技术团队

    导读 本文主要基于京东物流的分拣业务平台在生产环境遇到的一些安全类问题,进行定位并采取合适的解决方案进行安全治理,引出对行业内不同业务领域.不同类型系统的安全治理方案的探究,最后笔者也基于自己在金融领 ...

  5. Teamcenter RAC 开发之《新建Item》

    private TCComponentItem createOperation(String itemName,String itemType) { //obejct_name itemType tr ...

  6. 前端三件套系例之CSS——CSS是什么、CSS3语法、css代码书写位置(引入方式)、css选择器

    文章目录 1.CSS是什么 2.CSS3语法 2.1 CSS实例 2.2 CSS注释 3.css代码书写位置(引入方式) 3-1 行间式 3-2 内联式 3-3 外联式 总结 3 css选择器 1.基 ...

  7. Ubuntu 14.04解决登录界面无限循环的方法

    在Ubuntu下配置Android的环境时,想像在Windows中那样在终端中直接启动adb,以为Linux和Windows一样,将adb的路径添加到环境变量中,于是将adb的路径也export到/e ...

  8. Vue项目打包为桌面应用

    vue项目首先使用 npm run build 打包为dist文件后,进入dist目录得到如下文件:就是打包后的html+css+js+static 新建一个deskapp文件夹,里面在新建一个App ...

  9. JS个人总结(2)

    1.null被认为是一个空的对象引用..如果定义的变量准备将来用保存对象,最好将该变量初始化null.即 var x=null;这样只有检查null值就可以知道这个变量是否已经保存了一个对象.. 2. ...

  10. JavaScript:判断数据类型的四种方法

    JavaScript目前有两种数据类型:基本数据类型和引用数据类型. 基本数据类型:Undefined.Null.Boolean.String.Number.Symbol(ES6) 引用数据类型:Ob ...