使用 Redis 作为消息队列 - Pub/Sub, List, SortedSet

有好长一段时间没使用 Redis 了,之前用的都是 AWS 上的 Elastic Cache 的 Redis, 那时候还是用的版本还是 4 和 5。在新的项目由于觉得 Elastic Cache Redis 太贵而未曾使用,在去年的 AWS re:Invent 2023 上发布了 Elastic Cache Serverless,对于非长时间大数据的缓存可以考虑使用。而且还可以使用 Redis 的功能对服务进行解耦合,或作为一致性的协调中心。

此文是作为正式研究使用 Redis Stream (Redis 5.0 新特性)  的一个铺垫,探索在 Redis Stream 之前,可以何种方式把 Redis 当成一个消息队列,后面将会具体讲到如何用 Redis Stream 作为一个支持消费者组,能确认消息的更为完备的消息队列。

如以下几种方式

  1. Pub/Sub 订阅模式
  2. 基于 List 的 LPUSH + BRPOP 的实现
  3. SortedSet, 使用消息 Score 排序功能进行消费

Pub/Sub 订阅模式

它就像 AWS 的 SNS 一样,只有在线的订阅者才能收到消息,每个消费者会收到相同的消息。相关的 Redis 命令是 *sub*, *pub*, 参考 Redis Command group Pub/Sub。订阅的时候可以直接指定 Channel 名称,或用模式匹配,还可关注某些 Key 的事件。 阅读全文 >>

Rust 语言学习笔记(二)

再继续快速学习一下 Rust 的以下几个知识点,就可以开始着手做点小工具了

  1. 基本数据类型
  2. 复合数据类型
  3. 基本的流程控制

Rust 设计为有效使用内存考虑的,它提供了非常细力度的数据类型,如整数分为有无符号,宽度从 8 位到 128 位,分别表示为 i8, u8, u128 等。浮点数有 f32 和 f64,以及 bool 和 range 类型。

元组

元组和 Python 的元组用法类似,Immutable, 可混合类型

数组

数组类型中的元素类型相同,表示为  [T; n], T 为类型,n 为元素个数 阅读全文 >>

Rust 语言学习笔记(一)

学了不少编程语言,多数是离不开垃圾回收的,要么像 C++ 仍然是过于复杂,对于通用,编译型编程语言 Rust 是个不错的选择, Rust 不需要垃圾回收器。Rust 是由 Mozilla 主导开发的,设计准则为 "安全,并发,实用", 支持函数式,并发式,过程式以及面向对象的编程风格。Rust 能达到与 C++ 接近的性能,它又是编译型语言,编译出来二进制文件执行时不再依赖于运行时。Rust 有自带的 Cargo 作为依赖管理与构建工具,免除了关键工具的选择综合症。AWS 在今年也推出了 Rust 的 AWS SDK, 所以学习 Rust 的过程中也打算使用它来操作 AWS 的资源。

Rust 的 Hello World

在 macOS 下的安装

$ brew install rust

$ curl https://sh.rustup.rs | sh    # 安装
$ rustc --version                              # 查看版本
$ rustup update                               # 更新
$ rustup self uninstall                     # 卸载

当前 Rust 版本为 1.74.0, 安装后有 rustc, rustdoc, rust-gdb, rust-lldb, cargo 等相关命令

创建一个并运行 hello

cargo new 生成的项目有一个标准布局,不像 C++ 的项目那么自由混乱。自然的,Cargo.toml 中可配置项目的信息及管理依赖。 阅读全文 >>

Java StackOverflowError 与递归及尾递归优化

本篇同样是阅读《100 Java Mistakes and How to Avoid Them》的一则笔记,只是火力全集中在 StackOverflowError 这个单一主题之上, 且主要与递归及尾递归优化相关。一提到 StackOverflow, 恐怕首先是想到那个代码搬运源网站 stackoverflow.com,其次才是代码执行过程中的 StackOverflowError 错误。

什么是 StackOverflow, 准确来说是指线程的栈内存不足,无法在栈中分配新的内存(或创建新的栈帧)。我们通常会把它与方法调用关联起来,因为一次方法调用会创建一个新的栈帧,分配在栈上的局部变量(包括基本类型与对象引用),和栈帧都要占用线程栈内存。而我们平时所说的方法调用栈,或出现异常时打印出的异常栈都是一个概念。

StackOverflowError 一般出现在方法调用太深(方法调方法),手动编写的方法调用一般不容易达到这个限制,所以它与递归调用关系最为密切,递归调用次数太多或甚至没有出口条件无限递归; 也可能是经过一番递归调用后,再正常调用后续方法时出现 StackOverflowError(因为前面的递归调用资源消耗的差不多)

何谓递归调用,简单的说就是方法自己调用自己,从而实现循环的效果,或使代码更精练,例如经典排序中的快速和归并排序就要用到递归。但循环与递归又有本质上的区别,循环不增加调用深度;递归却不同,递归分递进与回归两个过程,递进调用时每次调用前都需通过压栈来保留现场,逆向回归时再逐级恢复现场,保留现场的过程就要从线程栈中分配内存空间; 死循环可能不会造成程序异常,但死(无限)递归必定出现 StackOverflowError。 阅读全文 >>

《100 Java Mistakes and How to Avoid Them》笔记 3

本书的阅读又搁置了许久,虽然感觉 Manning 出版社的这一 100 Mistakes 系列从书的质量不是那么的高,但开了头还是继续从本书 40% 的位置往下。

开始要讲述到异常了,异常还是有必要认真对待的,比如

  1. Java 中很容易被 CheckedException 弄得代码不整洁
  2. 缺少必要的参数检查,不舍得抛出异常,视异常为 Bug
  3. 不明确出现异常时后续如何处理,
  4. 或者是捕获而隐藏了异常致使定位错误变得更难。

Java 的主要异常大分类是

Throwable
├── Error
└── Exception
          └── RuntimeException

NullPointerException, 这恐怕是一个最常见的异常,Java 对一个对象是否能为 null 值没什么约束,甚至用 null 来表示业务上的空。比如说方法的参数与返回值,Java 都可以是 null 值,而在 Kotlin 中非明确可为 null 的时不能为 null 阅读全文 >>

本地和 ECS 容器(EC2/Fargate) 如何处理 ENTRYPOINT

不觉一晃还是在五年前记录过一篇 Dockerfile 中命令的两种书写方式的区别,其中提到过 Dockerfile 中可选择用 ENTRYPOINT 或 CMD 来启动进程,并且在 ENTRYPOINT 和 CMD 都支持  exec, shell 和增强型 shell 方式。如果同时有 ENTRYPOINT 和  CMD(或 docker 运行时的 CMD), 则 CMD 将为 ENTRYPOINT 提供参数。

在原来那篇文中认为 shell 无法接收到 docker stop 或  docker -s SIGTERM 发来的信号,也许是随着 Docker 版本的变迁,Docker 变得越发聪明了起来,无论何种格式的 ENTRYPOINT, 都能够收到 SIGTERM 信号,比如在 Java 的 ShutdownHook 能捕捉到该信号,得以在进程停止之前作必要的清理工作。

进行本文相关研究的主因是部署在 ECS(Fargate) 中的 Java Web 服务,Task 总是因为 OutOfMemoryError 被杀掉,而在应用程序日志中却见不着半点线索说 JVM 的 OutOfMemoryError,即使后来给 Fargate 配上了 EFS, 加了 +XX:+HeapDumpOnOutOfMemoryError XX:HeapDumpPath=/efs JVM参数,在任务被 kill 时在 /efs 上从来就都没生成过内存映像文件。最后发现是因为 JVM 的 -Xmx 配置太高,留给 Fargate 容器的太少的缘故。 阅读全文 >>

Vagrant 没了 VirtualBox 的话可以配 Qemu

开源虚拟机软件 VirtualBox 从当初不可一世的 Sun 易手到 Oracle 之间,变得不那么被许多公司信任了。之前一直是用 Vagrant 搭配 VirtualBox 在 Mac 下使用 Linux 虚拟机,因为不需要用到 Linux 桌面,用 Vagrant 操作虚拟机非常方便。但现在不得不要考虑别的方式,那就是 Vagrant + Qemu。Vagrant 的默认 Provider 是 VirtualBox, 我们搜索 Vagrant Box https://app.vagrantup.com/boxes/search 的时候,注意到  Vagrant 其实支持的 Provider 是一个很长的列表:

aws, cloudstack, digitalocean, docker, google, hyperv, libvirt, lxc, openstack, parallels, qemu, rackspace, softlayer, veertu, virtubalbox, vmware, vmware_desktop, vmware_fusion, vmware_ovf, vmware_workstation, vsphere, xenserver

有些尚未听说过,还有一些虽说支持,但别人提供的相应的 box 不多,Qemu 还算不错的 virtualbox 的替代品。Qemu 也是一款开源的虚拟机软件,它支持 Linux, macOS 和 Windows  平台,但它本身未提供友好的 UI 工具,所以创建一个 Qemu 虚拟机的命令会让人畏而却步。在 macOS 下有一个 Qemu 的 UI 工具 UTM 阅读全文 >>

Docker 容器中使用 Docker - DinD 和 DooD

突然间研究这个来的缘由是正在从 Jenkins 往 Harness 的过度, 而完全用命令来构建 Docker 镜像变得不一样了。在 Jenkins 中 Agent 本身也是一个 Docker Daemon, 所以 Docker 命令执行无障碍,而 Harness 的所谓的 Agent 就是一个个的运行在 Kubernetes 中的 Docker Container (Pod) 了,这其中没有 Docker Daemon, 又不能连接到 Kubernetes 本身的 Docker Daemon。另外 CloudBees CI/CD 的运行环境与 Harness 类似,也是运行在 Kubernetes 中的 Pod。

因此可能要使用某个 Docker 容器来作为 Docker Daemon, 所以牵连出对此的研究,相应的方案有 Docker in Docker(DinD) 和  Docker outside of Docker(DooD)。

对容器中启动 Docker Daemon 的探索

在知晓 DinD 和 DooD 这两个概念本人还试图构建过一个 Docker 镜像,试图用一个 Docker 容器既作 Docker Daemon 又作为 Docker 客户端。在容器中 Docker 安装成功,但无法在容器中启动 Docker Daemon。比如用下面的 Dockerfile 阅读全文 >>

理解 JUnit, JaCoCo 到 SonarQube 的过程及 Maven 配置

Java 项目需要产生单元测试及代码覆盖率的话一直都是走的 JUnit 单元测试,JaCoCo 基于测试产生测试覆盖率,然后送到 SonarQube 去展示这条路子。当然 SonarQube 还可以帮我们进行代码的静态分析。但对其中的具体使用及过程知晓的并不深,基本就是在 pom.xml 中依葫芦画瓢。本文稍加深入的理解每一步的功效与配置,以 Maven 管理的 Java 项目为例,JUnit 采用是众多旧项目仍然无法摆脱的 JUnit 4。

示例项目名称为 JaCoCoSonar, 创建一个 Calc 类,其中有 int add(int op1, int op2) 方法,为其写一个单元测试 CalcTest

单元测试实际是被 maven-surefire-plugin 插件执行的

现在开始第一步,执行 mvn test 看会发生什么,执行过程中控制台显示 阅读全文 >>

《100 Java Mistakes and How to Avoid Them》笔记 2

继续阅读本书,编程语言处理数值都有可能出现问题,如溢出,整数的最大最小值不对称,Double.NaN 等。

由于 Java 学了 C,也用 0 开始的数字来表示 8 进制数,如 037, 010 分别是十进制的 31 和 8,这与现实不相符。因为如果你在纸上写下 037, 010, 几乎所有人(除了某些程序员)都会认为它们就是十进制的 37 和 10。但是 Java 表示 2 进制, 16 进制的方式没有问题的,如 0b10, 0x37。IntelliJ IDEA 看到使用 0 开头的 8 进制数会不建议那么使用. 8 进制数字的范围是 0~8, 所以 09 是错误的, 但是 Java 编译器似乎对此很陌生.

int a = 09;

IntelliJ IDEA 会提示 Integer number too large, 编译器提示说 java: ';' expected, 有点驴唇不对马嘴.

现在几乎没有必要使用 0 开始的 8 进制数的方式, 或许还有用的就是表示 Unix 下文件权限, 如

int fileMode = 0644

所以任何时候看到 0 开头的数字都必须仔细检视, 基本可以禁止使用这种方式 阅读全文 >>