精华内容
下载资源
问答
  • 发布
    万次阅读
    2021-12-25 13:22:31


    前言

    分布式通信中的远程调用的核心是在网络服务层封装了通信协议、序列化、传输等操作,让用户调用远程服务如同进行本地调用一样。 通过网络服务层的封装实现了不同机器上不同进程之间的直接通信,因为是直接通信,所以通过线程阻塞的方式实现同步调用比较容易,因此通常被用于同步调用。比如,机器 1 上的进程 A 调用机器 2 上的进程 B,进程 A 被挂起,进程 B 开始执行,当进程 B 将值返回给 A 时,A 继续执行。

    虽然这种方式也可以用于异步通信,但因为进程之间是直接交互的,所以当进程比较多时, 会导致进程维护通信的复杂度非常高,且一个进程通信接口改变,与其通信的进程都会受到影响。

    随着业务和分布式计算规模的逐渐增大和复杂化,远程调用模型有点心有余力而不足了,为此出现了专门的异步通信模式,也就是消息发布订阅模式和消息队列模式。

    什么是发布订阅?

    发布订阅的思想在生活中随处可见。比如,学术届电子论文的订阅方式。通常各个会议方或出版社会将学术论文发布到论文网站(或平台上,比如 ACM、知网等),然后学生或老师向论文网站订阅自己感兴趣的论文。

    当会议方或出版社将论文发布到论文网站后,论文网站会根据订阅信息,将相应的论文推送给订阅者(比如通过邮件的方式)。这里的会议方或出版社就相当于生产者,负责发布论文,学生或老师就相当于消费者,而论文网站就相当于一个消息中心。

    在这里插入图片描述

    发布订阅的三要素是生产者、消费者和消息中心,生产者负责产生数据放到消息中心,消费者向消息中心订阅自己感兴趣的消息,当发布者推送数据到消息中心后,消息中心根据消费者订阅情况将相关数据推送给对应的订阅者。

    发布订阅的基本工作原理

    在分布式通信领域中,消息系统一般有两种典型的模式:

    1. 点对点模式(P2P,Point to Point)
    2. 发布订阅模式(Pub/Sub,Publish/Subscribe)。

    点对点模式

    生产者将消息发送到消息中心,然后消费者从消息中心取出对应的消息进行消费。消息被消费后,消息中心不再存储该消息,因此其他消费者无法再消费该消息。点对点模式虽然支持多个消费者,但一个消息只能被一个消费者消费,不允许重复消费。

    就好比限定了每篇论文只能被一个用户消费,比如现在有一篇分布式相关的论文,这篇论文推送给学生 A 之后,论文网站就必须将其删除或下架,其他用户无法再获取或阅读该论文了。
    在这里插入图片描述

    发布订阅模式。

    生产者可以发送消息到消息中心,而消息中心通常以主题(Topic)进行划分,每条消息都会有相应的主题,消息会被存储到自己所属的主题中,订阅该主题的所有消费者均可获得该消息进行消费。

    在这里插入图片描述

    假设生产者 1 发布一个 Topic 相关数据或消息,消费者 1~3 均订阅了该 Topic 消息,则该消息会推送消费者 1~3,同一个消息被 3 个消费者消费了。

    就好比不同的方向代表不同的主题,比如分布式领域代表一个主题,当会议方或出版社发布分布式相关的论文时,该论文会被存储到论文网站的分布式主题下,根据自己感兴趣的主题进行订阅。如果学生 A 订阅了分布式主题,那么当会议方或出版社发布分布式相关的论文后,会议网站会将这些论文推送给学生 A。

    与点对点模式相比,发布订阅模式中一个消息可以被多个消费者进行消费,这也是和点对点模式的本质区别。

    在分布式系统中,通常会为多用户服务,而多个用户通常会关注相同类型的消息,因此发布订阅模式在分布式系统中非常常见。

    Kafka 发布订阅原理及工作机制

    Kafka 是一种典型的发布订阅消息系统,其系统架构也是包括生产者、消费者和消息中心三 部分:

    • 生产者(Producer)负责发布消息到消息中心,比如电子论文的会议方或出版社;
    • 消费者(Consumer)向消息中心订阅自己感兴趣的消息,获得数据后进行数据处理, 比如订阅电子论文的老师或学生;
    • 消息中心(Broker)负责存储生产者发布的消息和管理消费者订阅信息,根据消费者订阅信息,将消息推送给消费者,比如论文网站。在 Kafka 中,消息中心本质上就是一组服务器,也可以说是 Kafka 集群。

    Kafka 的架构图,如下所示:

    在这里插入图片描述

    Kafka 中除了 Producer、Broker、Consumer 之外,还有一个 ZooKeeper 集群。Zookeeper 集群用来协调和管理 Broker 和 Consumer,实现了 Broker 和 Consumer 的解耦,并为系统提供可靠性保证。

    ZooKeeper 集群是一个提供了分布式服务协同能力的第三方组件,Consumer 和 Broker 启动时均会向 ZooKeeper 进行注册,由 ZooKeeper 进行统一管理和协调。

    ZooKeeper 中会存储一些元数据信息,比如对于 Broker,会存储主题对应哪些分区 (Partition),每个分区的存储位置等;对于 Consumer,会存储消费组(Consumer Group)中包含哪些 Consumer,每个 Consumer 会负责消费哪些分区等。

    分区和消费组的原理和作用

    Broker 负责存储消息数据,Consumer 负责消费数据, Consumer 消费数据的能力会影响 Broker 数据存储是否溢出的问题。若 Consumer 消费太慢,会导致 Broker 存储溢出,Broker 就会丢弃一部分消息。

    因此 Broker 和 Consumer 是 Kafka 的核心。如下图所示:

    在这里插入图片描述

    Broker

    在 Kafka 中,为了解决消息存储的负载均衡和系统可靠性问题,所以引入了主题和分区的概念。

    主题是一个逻辑概念,指的是消息类型或数据类型,就好比电子论文案例所讲的分布式是一个主题。

    分区是针对主题而言的,指的是一个主题的内容可以被划分成多个集合,分布在不同的 Broker 上,不同的 Broker 在不同的节点上。这里的集合就是分区,其中同一个分区只属于一个 Broker。

    分区的优点:

    • 实现负载均衡,避免单个 Broker 上的负载过高。比如,Topic 0 被分为 Partiton-0、 Partiton-1 和 Partiton-2 三个分区,分别分布在 Broker 0、Broker 1 和 Broker 2 上。 使 Topic 0 的消息可以分布在这 3 个分区中,实现负载均衡。
    • 实现消息的备份,从而保证系统的高可靠。比如,Topic 1 包含两个分区 Partiton-0、 Partiton-1,每个分区内容一致,分别存储在 Broker 0 和 Broker 1 上,借此实现了数据备份。

    Consumer

    Kafka 中的消费组,指的是多个消费者的一个集合。一个消费组中的消费者共同消费主题消息,并且主题中每个消息只可以由消费组中的某一个消费者进行消费。

    引入消费组的目的:在消息过多的情况下,单个消费者消费能力有限时,会导致消费效率过低,从而导致 Broker 存储溢出,丢弃一部分消息。

    发布订阅实践应用

    假设在电商购物平台中,用户首先在订单系统下单,下单后库存系统会进行出货,通知系统则负责通知用户,整个流程可以用发布订阅的模式进行,如下图所示:

    在这里插入图片描述

    订单系统对应发布订阅模式中的生产者,消息中心有个主题专门存放下单信息,每次用户下单后,订单系统会向该主题写入数据;库存系统和通知系统对应发布订阅模式中的消费者,它们会向消息中心订阅下单信息相关的主题;订单系统向消息中心发布订单信息后,库存系统和通知系统都会获取到相应的下单信息,然后进行各自后续的操作,即库存系统进行出货,通知系统通过短信或邮件等方式通知用户。

    发布订阅模式的关键特征

    • 实现了系统解耦,易于维护。生产者 / 发布者只负责消息的发布,不需要知道订阅者 / 消费者的数量,也不需要知道订阅者 / 消费者获取消息用来做什么,而订阅者 / 消费者也不需要知道什么时候生产者 / 发布者会发布消息。 所以生产者 / 发布者和订阅者 / 消费者互相独立,进而实现了系统解耦,每个部分可以单独维护,减少了因为生产者和消费者的耦合引入的一些相互影响。比如,如果两者耦合在 一起,当生产者逻辑更改需要修改代码时,消费者部分的代码也受影响,因此每个部分单独维护降低了维护的复杂度。

    • 实现了异步执行,避免高负载。生产者 / 发布者发布消息到消息中心,当消息超过消息中心可以存储的容量后,消息中心会丢弃掉超出的消息,这样系统就不会因为消息数量多而导致系统故障。

    知识扩展:观察者模式和发布订阅模式的区别是什么?

    观察者模式下有观察者,就有被观察者。观察者负责监控被观察者的状态变更,如果被观察者的状态发生了改变,观察者根据状态的变更执行相关操作。举个例子,现在进程 A 是被观察者,进程 B 和进程 C 是观察者, 当进程 B 观察到进程 A 中变量 X 由 3 变为 4 时,执行 X+1 的操作;当进程 C 观察到进程 A 中变量 X 由 3 变为 4 时,执行 X-1 的操作。观察者模式,定义了被观察者与观察者的直接交互或通信关系。

    发布订阅模式中存在发布者、订阅者和消息中心,订阅者需要向消息中心指定自己对哪些数据感兴趣,发布者推送的数据放入消息中心后,消息中 心根据订阅者订阅信息推送数据。发布者和订阅者之间引入了消息中心,实现的是间接通信。

    观察者模式采用了直接通信,观察者和被观察者通信时延会低一些,但它们的依赖关系比较强,不管是被观察者还是观察者逻辑或接口有更改,另外一个均会受影响。

    发布者和订阅者模式采用间接通信,引入了消息中心,相对比较厚重,且通信时延相对会高一 点,但实现了订阅者与发布者的解耦。

    总结

    发布订阅就是生产者产生消息发布到消息中心,消费者订阅自己感兴趣的消息,消息中心根据消费者的订阅情况将相关消息发给对应的消费者。

    Kafka 是一个经典的发布订阅消息系统,采用多分区实现了消息备份、负载均衡,并引入消费组提高了消费者的消费能力,防止 Broker 因为存储资源不够丢弃消息的情况,从而提高了 Kafka 系统的可靠性。

    发布订阅模式可以使系统松耦合易于维护,也可异步执行解决高负载问题,适用于系统解耦、流量削峰等场景。

    在这里插入图片描述

    发布订阅模式易于理解,与点对点模式很类似。不同的是,点对点模式中一个消息只能由一 个消费者消费,而发布者订阅者模式中一个消息可以由多个消费者消费。

    不同的通信模式适用于不同的分布式场景,其中发布订阅模式适合具备多个生产者、多个消费者且异步处理的场景,比如现在的视频 App,多个用户都可以通过同一款 App 看同一部电视剧,当然这个电视剧可以是被不同的生产者发布。点对点模式由于其局限性,一般适用于需要进行点对点通信的场景,比如近场投屏等。

    更多相关内容
  • 再单独说下发布顺序的问题,假设当前有需要发布的组件 A、B、C、D,其依赖关系如下: 在遵守 CocoaPods 发布规则的前提下,发布先后顺序应依次为 A、B-C、D ,其中 B 和 C 组件可同时发布,D 则需要等 B、C 都发布...

    一、Gitlab CI/CD 简介

    ① GitLab

    • GitLab 是一个利用 Ruby on Rails 开发的开源应用程序,实现一个自托管的 Git 项目仓库,可通过 Web 界面进行访问公开的或者私有的项目。
    • GitLab 拥有与 GitHub 类似的功能,能够浏览源代码,管理缺陷和注释。
    • GitLab 可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。

    ② GitLab CI/CD

    • Gitlab CI/CD 是一个内置在 GitLab 中的工具,用于通过持续方法进行软件开发:
      • 持续集成(Continuous Integration):频繁地(一天多次)将代码集成到主干,让产品可以快速迭代,同时还能保持高质量,它的核心措施是,代码集成到主干之前,必须通过自动化测试;
      • 持续交付(Continuous Delivery):频繁地将软件的新版本,交付给质量团队或者用户,以供评审,如果评审通过,代码就进入生产阶段,持续交付可以看作持续集成的下一步,它强调的是,不管怎么更新,软件是随时随地可以交付的;
      • 持续部署(continuous Deployment):代码通过评审以后,自动部署到生产环境,是持续部署是持续交付的下一步,持续部署的目标是,代码在任何时刻都是可部署的,可以进入生产阶段。

    ③ GitLab Runner

    • GitLab Runner 用于执行 Gitlab CI/CD 触发的一系列作业,并将结果发送回 Gitlab。
    • GitLab Runner 可以在 Docker 容器内运行或部署到 Kubernetes 集群中。

    ④ Pipeline

    • Pipeline 中文称为流水线,是分阶段执行的构建任务。如:安装依赖、运行测试、打包、部署开发服务器、部署生产服务器等流程,合起来称为 Pipeline。

    在这里插入图片描述

    • Stage表示构建阶段,可以理解为上面所说安装依赖、运行测试等环节的流程。我们可以在一次 Pipeline 中定义多个 Stage。
    • Job 表示构建的作业(或称之为任务),表示某个 Stage 里面执行的具体任务,可以在 Stages 里面定义多个 Jobs。
    • Pipeline,Stage 和 Job 的关系如下所示:

    在这里插入图片描述

    ⑤ 整体流程

    • CI/CD 是一种通过在应用开发阶段引入自动化来频繁向客户交付应用的方法,CI/CD 的核心概念是持续集成、持续交付和持续部署。
    • 整个流程将分为几个部分:
      • 首先开发人员在本地完成项目的开发之后,将代码推送到 Gitlab 仓库中;
      • 当代码提交到 Gitlab 仓库时,会触发 Pipeline,Gitlab Runner 会根据 .gitlab-ci.yml 配置文件运行 Pipeline 中各阶段的任务,总共定义 3 个阶段:compile,build,deploy;
      • 在 compile 阶段,Gitlab Runner 将项目编译成 jar 包,使用 MinIO 作为缓存,首次编译项目时会从 Maven 官网拉取依赖,之后会将依赖压缩后上传至 MinIo,在下一次编译时就可以直接从 MinIO 下载依赖文件;
      • 在 build 阶段,Gitlab Runner 使用在 compile 阶段编译生成的 jar 包构建 Docker 镜像,并将镜像推送至镜像仓库;
      • 在 deploy 阶段,Gitlab Runner 使用构建好 Docker 镜像在 Kubernetes 集群中部署应用。

    二、背景分析

    • 在实施业务组件化后,大部分没有组件化工具链支撑的团队一般都会遇到组件发布效率问题,如果遇到多个特性一起上线时,发布的组件数量可能达到几十个,手动发布这些组件的话,费时费力,非常影响开发体验。虽然可以通过 CI 简化单个组件的发布,只需要根据 Podfile 中的版本提交相应 tag 即可触发发布动作,但是 CI 并没有解决多个关联组件发布的前后顺序问题。如果下层组件还未发布就发布上层组件,此组件的 CI 很可能会因为缺少下层组件的某些接口而执行失败。
    • 基于 GitFlow 工作流进行日常项目的开发,项目在进入预发阶段时,关联的组件都需要拉取 release 分支,当某次发版的所有关联项目都预发测试完毕时,此次发版的负责人(通常是其中某个项目的负责人)会通知团队内部成员对组件进行封板,然后组件的负责人会去合并 release 分支到 master & develop,并且发布一个新版本,等所有组件都发布完成后,发版负责人再去更新主工程 Podfile。
    • 整个发版过程,组件负责人除了需要重复若干次以下操作,还需要知悉是否有下层组件还未发布:
    >>>> 查看组件应升级版本,更新 podspec 版本并 commit
        >>>> 合并 release 分支 ,gitflow finish release
            >>>> 打 tag 并 push 
                >>>> CI 执行完毕,组件发布完成,可能需要通知上层组件的负责人
    
    • 可以看到如果需要发布多个组件,其过程还是非常繁琐的。再单独说下发布顺序的问题,假设当前有需要发布的组件 A、B、C、D,其依赖关系如下:

    在这里插入图片描述

    • 在遵守 CocoaPods 发布规则的前提下,发布先后顺序应依次为 A、B-C、D ,其中 B 和 C 组件可同时发布,D 则需要等 B、C 都发布完成后才可以发布,也就是说只有当前组件的依赖没有包含未发布组件,此组件才可发布。
    • 我们以前的发布情况常常是这样的:下层组件 A 由于 lint 不通过,导致依赖 A 的 B、C 都 lint 失败,由于没有限制开发者对私有源仓库的 push 权限,B、C 组件的负责人这时候可能就会选择向私有源仓库强推 podspec,导致出现 lint 失败的连锁反应,越来越多的组件本身代码没问题,却因为下层组件而 lint 失败,只能选择强推 podspec。遇到这种情况,除了强调发布规则,从根本上还是要减少发版操作给组件负责人带来的工作量。
    • labor 就是为了能在一定程度上解决以上问题而创建的,在 labor 上执行发版操作时,组件负责人只需要关注 lint 的错误信息即可,剩余发布操作,包括上下层组件的发布顺序都由 labor 进行管理。

    三、效果演示

    • 以上述的 A、B、C、D 组件为例,在 labor 上添加发布并分析依赖后,可以看到组件发布页:

    在这里插入图片描述

    • 在发布页中,使用者可以查看依赖发布的组件,也可以修改发布组件的版本,labor 会在组件发布时同步到仓库的 podspec 文件中。执行自动发布后,labor 会和 GitLab 进行一系列交互。
    • 以组件 A 为例,labor 会先创建所有发布组件目标分支的 MR:

    在这里插入图片描述

    • 然后触发对应 release 分支的 pipeline:

    在这里插入图片描述

    • 这里省去的 code review 这一步骤,如果需要的话,可以在 web 上设置入口,组件负责人设置为 review 完成后,才触发 pipeline。如果 pipeline 执行成功,那么 GitLab 会自动合并 MR,如果冲突的话,需要负责人在此 MR 下解决:

    在这里插入图片描述

    • MR 合并成功后,组件会更新状态为发布中(假如组件还有依赖未发布,那么这里的状态为已合并,等待依赖发布完成,状态才为发布中):

    在这里插入图片描述

    • 然后 labor 会给组件打 tag,并且触发 tag 的 pipeline:

    在这里插入图片描述

    • 在 tag 的 pipeline 执行成功后,就视 A 组件发布成功。A 发布成功后,labor 会查看 B、C 对应 MR 状态执行后续操作:
      • MR pipeline 已经执行成功,并且对应的分支已经合并到 master (组件状态:已合并);
      • MR pipeline 由于 A 没发布,lint 失败,分支没有合并到 master(组件状态:等待中);
    • 如果是 1 状态,则直接创建 tag 发布,如果是 2 状态,则触发 MR 对应分支的 pipeline,假如此 MR 是因为 A 组件未发布导致合并失败的,那么在 A 发布后,重新触发的 MR pipeline 一般都能执行成功,当 MR 合并成功后,后续步骤与 1 一致。受益于 GitLab 分布式的 runner,可以通过 CI 同时发布多个组件。
    • 当所有组件发布完成后,labor 会根据使用者输入的组件版本,更新发版工程的 Podfile:

    在这里插入图片描述

    • 以上就是 labor 的主要工作步骤,可以看到,使用 labor 发版后,组件负责人只需要确保 MR 能顺利合并即可,不需要等待下层组件负责人发布完成的通知,也省去了繁琐的发布操作。
    • 截止到目前为止,labor 已经帮助团队执行了近 30 次自动发布,每次发布的组件个数平均在 15 个左右,节省了很多组员沟通与操作时间。

    在这里插入图片描述

    四、labor 结构与发布类型

    • labor 由如下几个服务构成:

    在这里插入图片描述

    • 其中 web 端主要专注于发版交互与组件发布数据的展示,后端负责发版信息采集、组件发布任务调度以及和 GitLab 进行通信,websocket server 主要负责实时更新 web 中组件的发布状态。server 中很多操作都是与 GitLab 进行交互,耗时较多,所以 server 中的大部分 service 都是交给 sidekiq(后台任务处理系统)执行的。
    • 依据发布性质,labor 把发布分为两种:
      • 主发布 (main deploy);
      • 子发布 (pod deploy)。
    • 这里主发布的主体是发版工程,子发布的主体是组件,其中主发布主要负责发版工程信息的获取和更新,比如依赖的分析、最后目标分支 Podfile 的更新等。主发布经过分析后,会创建若干子发布,子发布则负责组件发布的所有流程,包括组件 MR 的创建,组件 tag 的创建,发布 CI 的触发等。
    • 主发布和子发布涉及的所有状态如下:
    created:      '待分析',
    analyzing:    '分析中',
    preparing:    '准备中',
    pending:      '等待中',
    waiting:      '待发布',
    skipped:      '已忽略',
    merged:       '已合并',
    deploying:    '发布中',
    success:      '发布成功',
    failed:       '发布失败',
    canceled:     '已取消',
    
    • 使用 state_machines-activerecord 以状态机的形式对发布状态进行管理,状态发生变更之后,都会通过 websocket 同步到 web 端。
    • 分析目标工程的组件依赖是发版的第一步,对应主发布的 analyzing 状态,如下是 labor 分析步骤的序列图:

    在这里插入图片描述

    • 首先,web 向 server 端发起分析依赖请求,server 接收到请求后,使用 gitlab 向 GitLab 请求 Podfile 文件内容。由于 Podfile 可能在仓库的根目录或者 Example 文件夹下,使用 5 层深度的递归查询来获取文件路径。
    • 获取到 Podfile 路径后,再根据文件内容创建 Podfile 对象:
    # Labor::RemoteFile::Base
    def file_contents
      @file_contents ||= gitlab.file_contents(@project_id, @path, @ref)
    rescue Gitlab::Error::NotFound => error
      # self.class.name.demodulize
      # [2..-1]
      raise Labor::Error::NotFound.new("Can't find #{self.class.name.split('::').drop(2).join('')} with error #{error.message}")
    end
    
    # Labor::RemoteFile::Podfile
    def podfile
      @podfile ||= begin
        content = file_contents
        podfile = Pod::Podfile.from_ruby(Pathname.new(path), content)
        podfile
      end
    end
    
    • 生成 Podfile 对象后就可以分析发版信息,可以先过滤出需要发版的依赖:
    podfile.dependencies.select { |dependency| dependency.external? && dependency.external_source[:tag].nil?}
    
    • 然后通过同样的方式循环获取这些依赖的 podspec 文件,并且构建 Specification 对象:
    untagged_specs = Parallel.map(untagged_git_dependencies, in_threads: 5) do |dep|  
      git = dep.external_source[:git]
      ref = dep.external_source[:branch]
      component_project = gitlab.project(git)
      remote_file = Labor::RemoteFile::Specification.new(component_project.id, ref)
      remote_file.specification
    end
    
    • 这里可以利用多线程加快执行效率,不过线程数不要过多,不然容易造成 GitLab 返回数据失败。获取到所有需要发布的 spec 后,可以结合组件与其依赖、间接依赖创建发布结构,然后保存至数据库。
    • 同样以 A、B、C、D 组件为例,它们的 podspec 依赖如下:
    # podspec
    A
    
    B
    > A
    
    C
    > A
    
    D
    > B
    > C
    
    • 最终生成的发布结构如下:
    # 发布结构
    A
    
    B
    > A
    
    C
    > A
    
    D
    > A
    > B
    > C
    
    • 当 A 发布完成后,发布结构转变为:
    # 发布结构
    A
    
    B
    
    C
    
    D
    > B
    > C
    
    • B、C 组件会在需要发布的依赖清空之后,继续执行组件发布的后续操作。

    五、发布组件

    ① 组件发布核心过程

    • 组件的发布是整个服务的核心功能,顺利发布单个组件时的序列图如下:

    在这里插入图片描述

    • 上图中省略了部分和 websocket server 相关的逻辑,实际上其右边的状态发生变更时,都会进行 12、13 步骤的消息流动。
    • 如果是自动发布所有组件,而不是发布单个组件,labor 会对主发布分析出的所有组件执行发布操作,这样相关负责人就可以选择提前去 review MR 上需要合并的代码,而不是等依赖的组件发布完成。
    • 对于已知 lint 不通过,短时间无法解决错误的组件,labor 提供了手动标志组件发布成功的功能,使用者需要手动发布组件,再设置发布成功。添加这个功能主要是考虑到发版工程会接入其他业务线的组件,而我们并不想让这些组件影响发版进程。
    • 组件发布过程中,根据处理对象的不同,又可分为以下两个阶段:
      • 准备阶段(preparing ~ pending);
      • 正式发布阶段(merged ~ success)。

    ② 准备阶段

    • 首先,server 会给还未配置过的组件工程添加 webhook ,GitLab 很多任务都是放到 sidekiq 的,要想获取任务的执行信息,只能通过 webhook 让 GitLab 主动发送,所以这一步是组件能自动发布的重要前提。
    • 接着,server 会校验创建 MR 的必要条件:
      • 组件仓库必须要有 CI 配置文件,并且文件中包含发布 stage;
      • 组件仓库的 default 分支必须为 master。
    • 如果满足以上条件,我们会继续处理目标组件的 podspec 版本。labor 在分析依赖时,默认会使用 release 分支名中或者 podspec 中较高的版本号作为发布版本,如果实际发布时,组件发布版本比仓库中的 podspec 高,就需要更新 GitLab 仓库中 podspec 的 version 字段。
    • 更新 podspec 版本的代码:
    def update_podspec_content(podspec_content, version)
      require_variable_prefix = true
      version_var_name = 'version'
      variable_prefix = require_variable_prefix ? /\w\./ : //
      version_regex = /^(?<begin>[^#]*#{variable_prefix}#{version_var_name}\s*=\s*['"])(?<value>(?<major>[0-9]+)(\.(?<minor>[0-9]+))?(\.(?<patch>[0-9]+))?(?<appendix>(\.[0-9]+)*)?(-(?<prerelease>(.+)))?)(?<end>['"])/i
    
      version_match = version_regex.match(podspec_content)
      updated_podspec_content = podspec_content.gsub(version_regex, "#{version_match[:begin]}#{version}#{version_match[:end]}")
      updated_podspec_content
    end
    
    • 更新字符串后,同步至 GitLab:
    gitlab.edit_file(@project_id, @path, @ref, content, "更正 podspec 版本 #{version}".ci_skip)
    
    • 这里 commit 信息以 [ci skip] 作为前缀,以减少触发不必要的 pipeline。最后,会校验目标分支是否为 master 或者已经合并到 master ,如果是的话 server 会直接标记此发布为 merged ,等待正式发布,否则 server 会给组件仓库创建 MR ,发布进入 pending 状态,等依赖的组件都发布完成后,如果此 MR 还未合并,server 会重新触发 MR 关联的 pipeline,pipeline 运行成功,目标分支合入 master 后,再执行正式发布。

    ③ 正式发布阶段

    • 由于 tag 触发的 pipeline 包含二进制打包、源码及二进制版本发布的 stage,因此在这个阶段只需要管理 tag 及关联的 pipeline 就可以实现发布功能。这时候如果 tag 版本已经存在,GitLab 会返回创建失败,虽然 GitLab 提供了删除 tag 的功能,但还是不建议这么做的,我们会在 web 端提示发布失败,并且注明 tag 已存在,让组件负责人修改版本后重试发布此组件。
    • GitLab 创建 tag 操作是异步的,因此我们会在 webhook 中监听 tag 创建结果,创建成功之后,就可以处理 pipeline。上文提到过 以 [ci skip] 开头 commit 不会触发 pipeline,所以需要在这里做下判断,如果已经有 pipeline,那么只更新对应数据库条目,否则就需要创建新的 pipeline。

    六、更新依赖

    • 在所有组件发布完成后,我们会把变更的组件版本,同步到发版工程对应分支的 Podfile 中。受限于目前的工作流程和有限的工具链,在开发时,还是避免不了手动修改 Podfile,所以依旧使用 ruby 来编写 Podfile,没有使用 YAML 或者 JSON 格式 (pod ipc podfile/podfile-json 可查看)。
    • 过于灵活的编写方式,使得对其执行正则匹配需要以先推行编写规范为基础,因此不使用正则,而是让开发者提供 Podfile 模版,比如正常的 Podfile 为:
    # 一些 ruby 语法的配置
    target 'E' do
      pod 'A', git: 'xxxx', branch: 'release/0.1.0'
      pod 'J'
    end
    # 一些 ruby 语法的配置
    
    • 那么开发者可以在同级目录下,添加 PodfileTemplate :
    # 一些 ruby 语法的配置
    target 'E' do
    :TRIPLECCREPLACEME
    end
    # 一些 ruby 语法的配置
    
    • labor 会先获取原 Podfile,解析其依赖之后,再根据数据库中的组件及其发布版本号,更新这些依赖,然后将 PodfileTemplate 文件的 :TRIPLECCREPLACEME 替换成更新后的依赖,以生成新的 Podfile:
    # 一些 ruby 语法的配置
    target 'E' do
      pod 'A', '= 0.1.0'
      pod 'J'
    end
    # 一些 ruby 语法的配置
    
    • 当然,在工具链成熟的情况下,使用 JSON 或者 YAML 格式编写的 Podfile,更易于自动化处理,所以个人推荐前期最好不要在 Podfile 中添加过多 ruby 自定义代码,如果需要的话,可以以 cocoapods 插件的形式集成。

    七、总结

    • 由于 labor 只是个人在开发维护,所以制定流程和细节处理上可能不是非常合理,但就目前成果来看,labor 还是初步实现了发版时的组件自动化发布。本文更多的是展示了正常发布流程,实际上整个发布过程中,还是要处理挺多异常流程的。
    • 不过在发版本时才统一对需要发布的组件进行验证,容易出现解决组件 lint 错误时间过长影响发版问题,所以可能还是需要添加组件准入规则,将这部分验证往前移,比如约束主工程的分支权限,在预发提测前让开发者预先打 rc 版本,确保 rc 版本验证成功之后,再以 web 操作的方式接入主工程,预发阶段修复组件 bug 后,重新走接入流程,这样应该就能保证发版时,需要发布组件的正确性(关于 rc 版本的 lint ,可能还需要变更下 CocoaPods 查找版本的默认方式,毕竟 Semantic Versioning 优先采用正式版本)。
    展开全文
  • k8s 应用更新策略:灰度发布和蓝绿发布

    • 生产环境如何实现蓝绿部署?

    • 什么是蓝绿部署?

    • 蓝绿部署中,一共有两套系统:一套是正在提供服务系统,标记为“绿色”;另一套是准备发布的系统,标记为“蓝色”。两套系统都是功能完善的、正在运行的系统,只是系统版本和对外服务情况不同。

    • 开发新版本,要用新版本替换线上的旧版本,在线上的系统之外,搭建了一个使用新版本代码的全新系统。 这时候,一共有两套系统在运行,正在对外提供服务的老系统是绿色系统,新部署的系统是蓝色系统。
       

    • 蓝色系统不对外提供服务,用来做什么呢?

    • 用来做发布前测试,测试过程中发现任何问题,可以直接在蓝色系统上修改,不干扰用户正在使用的系统。
      (注意,两套系统没有耦合的时候才能百分百保证不干扰)

    • 蓝色系统经过反复的测试、修改、验证,确定达到上线标准之后,直接将用户切换到蓝色系统:

    • 切换后的一段时间内,依旧是蓝绿两套系统并存,但是用户访问的已经是蓝色系统。这段时间内观察蓝色系统(新系统)工作状态,如果出现问题,直接切换回绿色系统。
    • 当确信对外提供服务的蓝色系统工作正常,不对外提供服务的绿色系统已经不再需要的时候,蓝色系统正式成为对外提供服务系统,成为新的绿色系统。 原先的绿色系统可以销毁,将资源释放出来,用于部署下一个蓝色系统。
    • 蓝绿部署的优势和缺点

    • 优点:
      1、更新过程无需停机,风险较少
      2、回滚方便,只需要更改路由或者切换 DNS 服务器,效率较高

    • 缺点:
      1、成本较高,需要部署两套环境。如果新版本中基础服务出现问题,会瞬间影响全网用户;如果新版本有问题也会影响全网用户。
      2、需要部署两套机器,费用开销大
      3、在非隔离的机器(Docker、VM)上操作时,可能会导致蓝绿环境被摧毁风险
      4、负载均衡器/反向代理/路由/DNS 处理不当,将导致流量没有切换过来情况出现

    • 通过 k8s 实现线上业务的蓝绿部署

    • Kubernetes 不支持内置的蓝绿部署。目前最好的方式是创建新的 deployment,然后更新应用程序的 service 以指向新的 deployment 部署的应用
    • 创建绿色部署环境(旧的部署环境)

    • 下面步骤在 k8s 的控制节点操作:
      # kubectl create ns blue-green
      # vim lv.yaml

      创建前端 service
      vim service_lanlv.yaml

    • 创建蓝色环境(新的部署环境)
      vim lan.yaml

    • 在浏览器访问 http://k8s-master 节点 ip:30062 显示如下:

    • 修改 service_lanlv.yaml 配置文件,修改标签,让其匹配到蓝程序(升级之后的程序)

    • 实验完成之后,把资源先删除,以免影响后面实验:
      kubectl delete -f lan.yaml
      kubectl delete -f lv.yaml
      kubectl delete -f service_lanlv.yaml

    • 通过 k8s 实现滚动更新-滚动更新流程和策略

    • 滚动更新简介

    • 滚动更新是一种自动化程度较高的发布方式,用户体验比较平滑,是目前成熟型技术组织所采用的主流发布方式,一次滚动发布一般由若干个发布批次组成,每批的数量一般是可以配置的(可以通过发布模板定义),例如第一批 1 台,第二批 10%,第三批 50%,第四批 100%。每个批次之间留观察间隔,通过手工验证或监控反馈确保没有问题再发下一批次,所以总体上滚动式发布过程是比较缓慢

    • 在 k8s 中实现滚动更新
    • 首先看下 Deployment 资源对象的组成:
      kubectl explain deployment
      kubectl explain deployment.spec

       paused <boolean>   #暂停,当更新的时候创建 pod 先暂停,不是立即更新
       revisionHistoryLimit <integer>    #保留的历史版本数,默认是 10 个
      strategy <Object>      #更新策略,支持的滚动更新策略  Default is RollingUpdate.
       
      #支持两种更新,Recreate 和 RollingUpdate 
      #Recreate 是重建式更新,删除一个更新一个
      #RollingUpdate 滚动更新,定义滚动更新的更新方式的,也就是 pod 能多几个,少几个,控制更新力度的

      maxSurge <string>  #更新的过程当中最多允许超出的指定的目标副本数有几个;它有两种取值方式,第一种直接给定数量,第二种根据百分比,百分比表示原本是 5 个,最多可以超出 20%,那就允许多一个,最多可以超过 40%,那就允许多两个
      maxUnavailable <string> #最多允许几个不可用 ,假设有 5 个副本,最多一个不可用,就表示最少有 4 个可用
    • 自定义滚动更新策略
      maxSurge 和 maxUnavailable 用来控制滚动更新的更新策略
      取值范围
      数值
      1. maxUnavailable: [0, 副本数]
      2. maxSurge: [0, 副本数]
      注意:两者不能同时为 0。
      比例
      1. maxUnavailable: [0%, 100%] 向下取整,比如 10 个副本,5%的话==0.5 个,但计算按照 0个;
      2. maxSurge: [0%, 100%] 向上取整,比如 10 个副本,5%的话==0.5 个,但计算按照 1 个;
      注意:两者不能同时为 0。

      建议配置
      1. maxUnavailable == 0
      2. maxSurge == 1
      这是我们生产环境提供给用户的默认配置。即“一上一下,先上后下”最平滑原则:
      1 个新版本 pod ready(结合 readiness)后,才销毁旧版本 pod。此配置适用场景是平滑更新、保证服务平稳,但也有缺点,就是“太慢”了。

       

    • 总结:
      maxUnavailable:和期望的副本数比,不可用副本数最大比例(或最大值),这个值越小,越能保证服务稳定,更新越平滑;
      maxSurge:和期望的副本数比,超过期望副本数最大比例(或最大值),这个值调的越大,副本更新速度越快。

    • 查看默认的滚动更新策略
      #最多允许多 25%个 pod,25%表示不足一个,可以补一个

    • 查看控制器的滚动历史
      kubectl rollout history deployment mongodb-kubernetes-operator

      回滚操作:
      kubectl rollout undo deployment/mongodb-kubernetes-operator  --to-revision=1

      自定义策略:
      修改更新策略:maxUnavailable=1,maxSurge=1 

      kubectl patch deployments.apps mongodb-kubernetes-operator -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":1}}}}'

      显示如下:
      RollingUpdateStrategy: 1 max unavailable, 1 max surge
      上面可以看到 RollingUpdateStrategy: 1 max unavailable, 1 max surge 
      这个 rollingUpdate 更新策略变成了刚才设定的,因为我们设定的 pod 副本数是 3,1 和 1 表示最少不能少于 2 个 pod,最多不能超过 4 个 pod 
      这个就是通过控制 RollingUpdateStrategy 这个字段来设置滚动更新策略 

    • 通过 k8s 完成线上业务的金丝雀发布

    • 金丝雀发布简介

    • 金丝雀发布的由来:17 世纪,英国矿井工人发现,金丝雀对瓦斯这种气体十分敏感。空气中哪怕有极其微量的瓦斯,金丝雀也会停止歌唱;当瓦斯含量超过一定限度时,虽然人类毫无察觉,金丝雀却早已毒发身亡。当时在采矿设备相对简陋的条件下,工人们每次下井都会带上一只金丝雀作为瓦斯检测指标,以便在危险状况下紧急撤离。

    • 金丝雀发布(又称灰度发布、灰度更新):金丝雀发布一般先发 1 台,或者一个小比例,例如 2%的服务器,主要做流量验证用,也称为金丝雀 (Canary) 测试 (国内常称灰度测试)。

    • 简单的金丝雀测试一般通过手工测试验证,复杂的金丝雀测试需要比较完善的监控基础设施配合,通过监控指标反馈,观察金丝雀的健康状况,作为后续发布或回退的依据。 如果金丝测试通过,则把剩余的 V1 版本全部升级为 V2 版本。如果金丝雀测试失败,则直接回退金丝雀,发布失败。

    • 优点:灵活,策略自定义,可以按照流量或具体的内容进行灰度(比如不同账号,不同参数),出现问题不会影响全网用户
      缺点:没有覆盖到所有的用户导致出现问题不好排查

    • 在 k8s 中实现金丝雀发布
      打开一个标签 1 监测更新过程
      kubectl get pods -l app=myapp -n blue-green -w

    • 打开另一个标签 执行如下操作:
      kubectl set image deployment myapp-v1 myapp=janakiramm/myapp:v2 -n blue-green && kubectl rollout pause deployment myapp-v1 -n blue-green

      注:上面的解释说明把 myapp 这个容器的镜像更新到 janakiramm/myapp:v2 版本 ,更新镜像之后,创建一个新的 pod 就立即暂停,这就是我们说的金丝雀发布;
      如果暂停几个小时之后没有问题,那么取消暂停,就会依次执行后面步骤,把所有 pod 都升级

    • 解除暂停:
      回到标签 1 继续观察:

    • 打开标签 2 执行如下:
      kubectl rollout resume deployment myapp-v1 -n blue-green

    • kubectl get rs -n blue-green
      可以看到 replicaset 控制器有 2 个了

    • 回滚:
      如果发现刚才升级的这个版本有问题可以回滚,查看当前有哪几个版本:
      kubectl rollout history deployment myapp-v1 -n blue-green

      上面说明一共有两个版本,回滚的话默认回滚到上一版本,可以指定参数回滚:
      kubectl rollout undo deployment myapp-v1 -n blue-green --to-revision=1
      #回滚到的版本号是 1
      kubectl rollout history deployment myapp-v1 -n blue-green

      上面可以看到第一版没了,被还原成了第三版,第三版的前一版是第二版 

       以看到下面的 rs 已经用第一个了,这个就是还原之后的 rs

    • 自定义 CRD 资源

      通过 crd 部署 mongodb 集群

      部署 mongodb-aperator
      1、项目地址
      https://github.com/mongodb/mongodb-kubernetes-operator.git 找到 0.5.0,看这个版本对应使用

      2、创建名称空间 mongodb,并进入到 mongodb-kubernetes-operator 目录应用 crd 资源,
      创建自定义资源类型
      kubectl create ns mongodb
      cd mongodb-kubernetes-operator-0.5.0
      kubectl apply -f deploy/crds/mongodb.com_mongodbcommunity_crd.yaml

      #查看 mongodb 是否创建成功
      kubectl get crd/mongodbcommunity.mongodb.com


      3、安装 operator
      kubectl apply -f deploy/operator/ -n mongodb

      提示:mongodb-kubernetes-operator 这个项目是将自定义控制器和自定义资源类型分开实现的;其 operator 只负责创建和监听对应资源类型的变化,在资源有变化时,实例化为对应资源对象,并保持对应资源对象状态吻合用户期望状态;上述四个清单中主要是创建了一个 sa 账户,并对对应的 sa 用户授权;


      验证:查看 operator 是否正常运行
      kubectl get pods -n mongodb

      验证:使用自定义资源类型创建一个 mongodb 副本集集群
      kubectl apply -f deploy/crds/mongodb.com_v1_mongodbcommunity_cr.yaml -n mongodb
      kubectl get pods -n mongodb

      提示:这里可以看到对应 pod 处于 pending 状态;
       查看 pod 详细信息

      提示:这里提示没有可以用的 pvc; 4 pod has unbound immediate PersistentVolumeClaims.
      删除 mongodb 名称空间下 pvc
      kubectl get pvc -n mongodb
      kubectl delete pvc --all -n mongodb

      创建 pv 和 pvc
      vim pv-demo.yaml


      在 nfs 服务器 上创建 nfs 共享目录
      mkdir /data/p{1,2,3}

      创建 pvc 资源
      vim pvc-demo.yaml

       可以看到对应的pod已经正常跑起来了;

      验证:连接mongodbpod,看看对应副本集集群是否工作正常?

    展开全文
  • Redis进阶——发布订阅详解

    千次阅读 2021-11-23 13:12:17
    什么是发布订阅? Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。 Redis 的 subscribe 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, ...

    什么是发布订阅?

    Redis 发布订阅(pub/sub)是一种消息通信模式:发送者(pub)发送消息,订阅者(sub)接收消息。

    Redis 的 subscribe 命令可以让客户端订阅任意数量的频道, 每当有新信息发送到被订阅的频道时, 信息就会被发送给所有订阅指定频道的客户端。

    ☛ 下图展示了频道 channel1 , 以及订阅这个频道的三个客户端 —— client2 、 client5 和 client1 之间的关系:

    ☛ 当有新消息通过 publish 命令发送给频道 channel1 时, 这个消息就会被发送给订阅它的三个客户端:

    为什么要用发布订阅?

    熟悉消息中间件的同学都知道,针对消息订阅发布功能,市面上很多大厂使用的是kafkaRabbitMQActiveMQRocketMQ等这几种,redis的订阅发布功能跟这三者相比,相对轻量,针对数据准确和安全性要求没有那么高可以直接使用,适用于小公司。

    redis 的List数据类型结构提供了 blpopbrpop 命令结合 rpushlpush 命令可以实现消息队列机制,基于双端链表实现的发布与订阅功能

    这种方式存在两个局限性:

    • 不能支持一对多的消息分发。
    • 如果生产者生成的速度远远大于消费者消费的速度,易堆积大量未消费的消息

    双端队列图解如下:

    解析:双端队列模式只能有一个或多个消费者轮着去消费,却不能将消息同时发给其他消费者

    发布/订阅模式图解如下:

    解析:redis订阅发布模式,生产者生产完消息通过频道分发消息,给订阅了该频道的所有消费 

    发布/订阅如何使用?

    Redis有两种发布/订阅模式:

    • 基于频道(Channel)的发布/订阅
    • 基于模式(pattern)的发布/订阅

    操作命令如下

    序号命令与描述

    ● 基于频道
    1

    subscribe channel  [channel ... ]

    订阅给定的一个或多个频道

    2

    unsubscribe  channel  [channel ... ]

    退订给定的频道

    说明:若没有指定channel,则默认退订所有频道

    3

    publish channel message 

    将消息发送给指定频道 channel

    返回结果:接收到信息的订阅者数量,无订阅者返回0

    4

    pubsub channels [argument  [atgument ...] ]

    查看订阅与发布系统的状态

    说明:返回活跃频道列表(即至少有一个订阅者的频道,订阅模式的客户端除外)

    ● 基于模式

    5

    psubscribe pattern1  [pattern...]

    订阅一个或多个符合给定模式的频道

    说明:每个模式以 * 作为匹配符;例如 cn* 匹配所有以cn开头的频道:cn.java、cn.csdn

    6

    punsubscribe [pattern [pattern ...] ] 

    退订所有给定模式的频道

    说明:pattern 未指定,则订阅的所有模式都会被退订,否则只退订指定的订阅的模式


     

    基于频道(Channel)的发布/订阅

    "发布/订阅" 包含2种角色:发布者和订阅者。发布者可以向指定的频道(channel)发送消息;订阅者可以订阅一个或者多个频道(channel),所有订阅此频道的订阅者都会收到此消息。

    • 订阅者订阅频道   subscribe channel  [channel ...] 
    --------------------------客户端1(订阅者) :订阅频道 ---------------------
    
    # 订阅 “meihuashisan” 和 “csdn” 频道(如果不存在则会创建频道)
    127.0.0.1:6379> subscribe meihuashisan csdn 
    Reading messages... (press Ctrl-C to quit)
    
    1) "subscribe"    -- 返回值类型:表示订阅成功!
    2) "meihuashisan" -- 订阅频道的名称
    3) (integer) 1    -- 当前客户端已订阅频道的数量
    
    1) "subscribe"
    2) "csdn"
    3) (integer) 2
    
    #注意:订阅后,该客户端会一直监听消息,如果发送者有消息发给频道,这里会立刻接收到消息

    • 发布者发布消息   publish channel message
    --------------------------客户端2(发布者):发布消息给频道 -------------------
    
    
    # 给“meihuashisan”这个频道 发送一条消息:“I am meihuashisan”
    127.0.0.1:6379> publish meihuashisan "I am meihuashisan"
    (integer) 1  # 接收到信息的订阅者数量,无订阅者返回0
    
    

     客户端2 (发布者) 发布消息给频道后,此时我们再来观察 客户端1 (订阅者) 的客户端窗口变化:

    # --------------------------客户端1(订阅者) :订阅频道 -----------------
    
    127.0.0.1:6379> subscribe meihuashisan csdn 
    Reading messages... (press Ctrl-C to quit)
    
    1) "subscribe"    -- 返回值类型:表示订阅成功!
    2) "meihuashisan" -- 订阅频道的名称
    3) (integer) 1    -- 当前客户端已订阅频道的数量
    
    1) "subscribe"
    2) "csdn"
    3) (integer) 2
    
    
    
     ---------------------变化如下:(实时接收到了该频道的发布者的消息)------------
    
    1) "message"           -- 返回值类型:消息
    2) "meihuashisan"      -- 来源(从哪个频道发过来的)
    3) "I am meihuashisan" -- 消息内容
    
    

    命令操作图解如下:

    注意:如果是先发布消息,再订阅频道,不会收到订阅之前就发布到该频道的消息!

    注意:进入订阅状态的客户端,不能使用除了subscribeunsubscribepsubscribe 和 punsubscribe 这四个属于"发布/订阅"之外的命令,否则会报错!

    ——这里的客户端指的是 jedis、lettuce的客户端,redis-cli是无法退出订阅状态的!

    实现原理

    底层通过字典实现。pubsub_channels 是一个字典类型,保存订阅频道的信息:字典的key为订阅的频道, 字典的value是一个链表, 链表中保存了所有订阅该频道的客户端

    struct redisServer { 
      /* General */ 
      pid_t pid; 
    
      //省略百十行 
    
      // 将频道映射到已订阅客户端的列表(就是保存客户端和订阅的频道信息)
      dict *pubsub_channels; /* Map channels to list of subscribed clients */ 
    }


     实现图如下: 

    频道订阅:订阅频道时先检查字段内部是否存在;不存在则为当前频道创建一个字典且创建一个链表存储客户端id;否则直接将客户端id插入到链表中。

    取消频道订阅:取消时将客户端id从对应的链表中删除;如果删除之后链表已经是空链表了,则将会把这个频道从字典中删除。

    发布:首先根据 channel 定位到字典的键, 然后将信息发送给字典值链表中的所有客户端

    基于模式(pattern)的发布/订阅

    如果有某个/某些模式和该频道匹配,所有订阅这个/这些频道的客户端也同样会收到信息。

    图解

    下图展示了一个带有频道和模式的例子, 其中 com.ahead.* 频道匹配了 com.ahead.juc 频道和 com.ahead.thread 频道, 并且有不同的客户端分别订阅它们三个,如下图:

    当有信息发送到com.ahead.thread 频道时, 信息除了发送给 client 4 client 5 之外, 还会发送给订阅 com.ahead.*  频道模式的 client x client y

    解析:反之也是,如果当有消息发送给 com.ahead.juc 频道,消息发送给订阅了 juc 频道的客户端之外,还会发送给订阅了 com.ahead.*  频道的客户端: client x 、client y

    通配符中?表示1个占位符,*表示任意个占位符(包括0),?*表示1个以上占位符。

    •  订阅者订阅频道   psubscribe pattern  [pattern ...] 
    --------------------------客户端1(订阅者) :订阅频道 ---------------------
    
    
    #  1. ------------订阅 “a?” "com.*" 2种模式频道--------------
    127.0.0.1:6379> psubscribe a? com.*
    # 进入订阅状态后处于阻塞,可以按Ctrl+C键退出订阅状态
    Reading messages... (press Ctrl-C to quit) 
    
    
    
    # 2. ---------------订阅成功-------------------
    
    1) "psubscribe"  -- 返回值的类型:显示订阅成功
    2) "a?"          -- 订阅的模式
    3) (integer) 1   -- 目前已订阅的模式的数量
    
    
    1) "psubscribe"
    2) "com.*"
    3) (integer) 2
    
    
    
    # 3. ---------------接收消息 (已订阅 “a?” "com.*" 两种模式!)-----------------
    
    # ---- 发布者第1条命令: publish ahead "hello"
    结果:没有接收到消息,匹配失败,不满足 “a?” ,“?”表示一个占位符, a后面的head有4个占位符
    
    
    # ---- 发布者第2条命令:  publish aa "hello" (满足 “a?”)
    1) "pmessage" -- 返回值的类型:信息
    2) "a?"       -- 信息匹配的模式:a?
    3) "aa"       -- 信息本身的目标频道:aa
    4) "hello"    -- 信息的内容:"hello"
    
    
    # ---- 发布者第3条命令:publish com.juc "hello2"(满足 “com.*”, *表示任意个占位符)
    1) "pmessage" -- 返回值的类型:信息
    2) "com.*"    -- 匹配模式:com.*
    3) "com.juc"  -- 实际频道:com.juc
    4) "hello2"   -- 信息:"hello2"
    
    # ---- 发布者第4条命令: publish com. "hello3"(满足 “com.*”, *表示任意个占位符)
    1) "pmessage" -- 返回值的类型:信息
    2) "com.*"    -- 匹配模式:com.*
    3) "com."     -- 实际频道:com.
    4) "hello3"   -- 信息:"hello3"

    • 发布者发布消息   publish channel message
    --------------------------客户端2(发布者):发布消息给频道 -------------------
    
    注意:订阅者已订阅 “a?” "com.*" 两种模式!
    
    
    # 1. ahead 不符合“a?”模式,?表示1个占位符
    127.0.0.1:6379> publish ahead "hello"  
    (integer) 0    -- 匹配失败,0:无订阅者
    
    
    # 2. aa 符合“a?”模式,?表示1个占位符
    127.0.0.1:6379> publish aa "hello"      
    (integer) 1
    
    # 3. 符合“com.*”模式,*表示任意个占位符
    127.0.0.1:6379> publish com.juc "hello2" 
    (integer) 1
    
    # 4. 符合“com.*”模式,*表示任意个占位符
    127.0.0.1:6379> publish com. "hello3" 
    (integer) 1

    命令操作图解如下:

    实现原理

    底层是pubsubPattern节点的链表

    struct redisServer {
        //...
        list *pubsub_patterns; 
        // ...
    }
    
    // 1303行订阅模式列表结构:
    typedef struct pubsubPattern {
        client *client;  -- 订阅模式客户端
        robj *pattern;   -- 被订阅的模式
    } pubsubPattern;

    实现图如下:

    模式订阅:新增一个pubsub_pattern数据结构添加到链表的最后尾部,同时保存客户端ID

    取消模式订阅:从当前的链表pubsub_pattern结构中删除需要取消的pubsubPattern结构。

    使用小结

    订阅者(listener)负责订阅频道(channel);发送者(publisher)负责向频道发送二进制的字符串消息,然后频道收到消息时,推送给订阅者。

    使用场景

    • 电商中,用户下单成功之后向指定频道发送消息,下游业务订阅支付结果这个频道处理自己相关业务逻辑
    • 粉丝关注功能
    • 文章推送

    使用注意

    • 客户端需要及时消费和处理消息。


      客户端订阅了channel之后,如果接收消息不及时,可能导致DCS实例消息堆积,当达到消息堆积阈值(默认值为32MB),或者达到某种程度(默认8MB)一段时间(默认为1分钟)后,服务器端会自动断开该客户端连接,避免导致内部内存耗尽。

    • 客户端需要支持重连。


      当连接断开之后,客户端需要使用subscribe或者psubscribe重新进行订阅,否则无法继续接收消息。

    • 不建议用于消息可靠性要求高的场景中。


      Redis的pubsub不是一种可靠的消息系统。当出现客户端连接退出,或者极端情况下服务端发生主备切换时,未消费的消息会被丢弃。

    展开全文
  • 基于apollo实现配置灰度发布

    万次阅读 2021-10-10 10:47:44
    在上一篇,通过dubbo的版本号控制,我们实现了一个服务的简单的灰度发布过程,在真实的项目环境中,灰度发布的应用场景是很多的,服务接口存在灰度的需求,本篇再介绍另一种比较常见的灰度需求场景,即配置文件的...
  • 随着互联网技术的发展,目前业务发布已经基本形成蓝绿发布、灰度(金丝雀)发布、和滚动发布这三种发布策略。 一、蓝绿发布 蓝绿部署是一种以可预测的方式发布应用的技术,目的是减少发布过程中服务停止的时间。 ...
  • 发布订阅模式

    千次阅读 2022-01-26 17:05:53
    发布/订阅模式 什么是发布订阅模式? 此模式分为发布者和订阅者两个概念,发布者收集订阅者的需求,然后在某个时刻告知订阅者 例子: 小王去小卖部买充电器,小李去小卖部买剃须刀,但是充电器和剃须刀都卖完了,...
  • 在项目迭代的过程中,不可避免需要进行项目...1、蓝绿发布(Blue/Green Deployment) 1.1. 定义 蓝绿部署是不停老版本,部署新版本然后进行测试。 确认 OK 后将流量切到新版本,然后老版本同时也升级到新版本。 ...
  • ThinkPHP新闻发布系统(前台+后台)绝对易学易用

    千次下载 热门讨论 2012-05-17 10:12:43
    通俗易懂的ThinkPHP文章发布系统源码(前台+后台) 主要是后台可以实现文章的非常方便的发布。 ThinkPHP框架+百度UEditor编辑器实现.具体使用方法参考ruby97的CSDN博客
  • Django程序的发布

    千次阅读 2022-04-01 16:15:33
    Django程序在Linux上的部署 1.服务器搭建Python环境 ...发布上的代码应该包含requirement.txt pip freeze > requirements.txt 激活虚拟环境后执行,到包含requirement.txt目录下: pip install -r
  • HoloLens2之路-发布部署

    千次阅读 2022-03-08 21:10:44
      当MR应用开发测试完毕后就需要将其发布成可安装文件,MR应用可以直接发布到Microsoft Store应用商城,也可以发布成.msix或者.appx安装文件,我们可以使用VS发布,也可以在Unity中使用MRTK提供的Appx程序包构建...
  • 发布订阅模式与观察者模式

    万次阅读 多人点赞 2019-03-29 18:25:12
    发布者和订阅者之间存在第三个组件,称为消息代理或调度中心或中间件,它维持着发布者和订阅者之间的联系,过滤所有发布者传入的消息并相应地分发它们给订阅者。 举一个例子,你在微博上关注了A,同时其他很多人...
  • 【SpringBoot】36、SpringBoot整合Redis实现发布/订阅

    万次阅读 多人点赞 2020-10-23 11:31:12
    SUBSCRIBE, UNSUBSCRIBE 和 PUBLISH 实现了 发布/订阅消息范例,发送者 (publishers) 不用编程就可以向特定的接受者发送消息 (subscribers). Rather, 发布的消息进入通道,不需要知道有没有订阅者. 订阅者发表感兴趣...
  • npm包发布详细教程

    千次阅读 2020-11-16 11:51:05
    将我们编写的代码发布到npm仓库后就可以在多个不同的项目里引入使用,不管是公司内部使用还是分享给互联网上其他用户,都是非常方便。 一、新建项目 1、建立目录 新建一个项目文件夹wxmp-rsa, 在里面新建src文件夹...
  • html发布页,发布页入口.html

    万次阅读 2021-06-23 05:18:52
    发布页入口$axure.utils.getTransparentGifPath = function() { return 'resources/images/transparent.gif'; };$axure.utils.getOtherPath = function() { return 'resources/Other.html'; };$axure.utils....
  • 本文的思维导图整理了小米自成立以来发布的所有手机型号,同时包括发布时间和发布价格 思维导图源文件已经发布在我的资源当中,有需要的可以去 我的主页 了解更多计算机学科的精品思维导图整理 本文可以转载,但请...
  • springboot 实现接口灰度发布

    千次阅读 2022-03-14 19:18:47
    springboot 实现接口灰度发布
  • Jenkins定时发布项目

    万次阅读 2021-06-18 14:55:38
    Jenkins定时发布项目1. 配置触发器2. 设置构建环境3. Jekins调用脚本输出回显问题3.1测试脚本3.2 回显情况测试3.3 输出到指定位置 1. 配置触发器 字段 说明 范围 MINUTE 分钟 0-59 HOUR 小时 0-23 DAY ...
  • Linux下使用Nginx发布项目

    千次阅读 2022-03-14 22:40:50
    Nginx是一种服务器软件,其最主要,最基本的功能是可以与服务器硬件(电脑)结合,让程序员可以将程序发布在Nginx服务器上,让成千上万的用户可以浏览。 ​除此之外,Nginx还是一种高性能的HTTP和反向代理服务器,同时...
  • 软件产品发布基本流程

    千次阅读 2020-08-12 15:12:09
    产品发布前准备 发布之前,所有程序由测试人员进行确认测试;检查缺陷管理系统(比如:JIRA)内登记的所有bug都已关闭,或者遗留的bug不影响系统的使用,如果有严重bug未解决(级别为很严重以上)不能发布; ...
  • Ros发布图像消息

    千次阅读 2022-03-28 09:43:07
    但是image_transport同时还发布了其它的topic用来兼容image_view的功能,就是说上面的代码自动帮助发布了有压缩格式的视频流: /camera/image /camera/image/compressed /camera/image/compressed/parameter_...
  • MQTT协议理解---发布与订阅

    万次阅读 多人点赞 2019-11-28 19:59:48
    4.1 订阅与发布模型 在第一课中,我们介绍了 MQTT 基于订阅与发布的消息模型,MQTT 协议的订阅与发布是基于主题的(Topic),一个典型的 MQTT 消息发送与接收的流程如下: ClientA 连接到 Broker; ClientB 连接到 ...
  • 小程序全量发布与灰度发布

    千次阅读 2021-04-19 14:39:52
    更新了~ ...灰度发布:分不同时间段来控制用户使用最新版小程序(又称分阶段发布),即固定时间段的用户会使用最新版小程序。 用于小程序功能太多,用户太多时控制风险。 不过一般的小程序都是全量更新啦
  • 文章目录华为云讲解:Istio灰度发布与技术实现典型发布类型对比蓝绿发布金丝雀发布A/B 测试Istio流量治理技术解析规则配置DestinationRuleVirtualServiceVirtualService例子智能灰度发布介绍灰度版本的存在形式灰度...
  • 文章目录tomcat 安装配置简介下载安装优化配置修改默认内存管理员用户名和密码设置支持中文文件名称idea 配置 tomcat 并发布 web 项目eclipse 配置 tomcat 并发布 web 项目 本篇内容主要讲述如何在 idea 和 eclipse...
  • 发布页入口.html

    万次阅读 2021-06-23 05:19:45
    发布页入口$axure.utils.getTransparentGifPath = function() { return 'resources/images/transparent.gif'; };$axure.utils.getOtherPath = function() { return 'resources/Other.html'; };$axure.utils....
  • VS2022 .NET5一键发布到远程腾讯云IIS服务器

    万次阅读 多人点赞 2022-03-05 18:44:16
    VS2022 .NET5项目发布到远程IIS服务器
  • Vue项目如何打包并且发布

    千次阅读 2022-04-22 14:40:29
    如何将Vue项目打包并发布?我这边是测试发布到本地,不过步骤是一样的,步骤如下: 1、安装部署Nginx服务器。(类似Tomcat服务器) 说明:Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/...
  • 手把手教你VUE前端项目发布上线

    万次阅读 多人点赞 2020-06-25 10:00:16
    已经写好的代码,有团队的需要拉取最新的代码(今天重点分享团队前端项目的发布过程) 软件:(包括但不限于)——如果工具都OK,将大大降低发布难度 Nginx:轻量级web服务器 Jenkins:持续集成开发工具 Tomcat...
  • 微信小程序发布流程

    万次阅读 2022-03-12 16:39:11
    1.授权 登录微信公众平台微信公众平台,给相应人员授权... 在发布之前可通过点击编译、预览、真机调试等,在本地进行测试。 代码测试没问题后,点击上传。 上传完成后,找到微信公众平台的版本管理。 ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 3,119,300
精华内容 1,247,720
关键字:

发布

友情链接: Cry1602液晶显示.zip