从零放弃学习 Spring - 在 Native Image 中使用 Bouncy Castle
基于 Bouncy Castle 的 RSA 实现加解密
我的开发环境是 Oracle GraalVM 17.0.8+9.1,Linux x64。
完整代码参考 native-bc。
加密代码就不贴了,参考我的仓库代码,只需要执行:
1 | make deps keys run |
GraalVM 是 Oracle 开发的 JDK,本身就提供了更好性能的 JVM,GraalVM Native Image 可以通过 AOT 把 Java 代码直接编译成二进制可执行文件,编译后的二进制不再依赖 JVM,更快的启动速度,更小的运行内存,并且不会对性能有大影响。
Native Image 会执行静态分析,从应用入口(即 main)开始,找到所有会被访问的方法,并且只会编译找到的代码。完全按照 JVM 执行的逻辑去静态分析是不现实的,所以 Native Image 静态分析基于一个叫*封闭世界假设(closed-world assumption)*的约束:只考虑构建时的已知代码,不允许在运行时增加加载新代码。
下载安装 GraalVM 后你还需要安装必要的构建工具和 z-lib,例如 Ubuntu 下:
1 | apt-get install build-essential zlib1g-dev |
Kubernetes (以下简称 K8s) 可以直接运行 Spring Boot 应用,这部分不需要 Spring Cloud 支持。
代码参考 spring-boot-k8s。
Spring Cloud Gateway 可以用来快速开发一个网关。
通过 Spring Initializr 创建一个 gateway 项目,添加依赖:Gateway
和 Consul Discovery
。网关项目有 Spring Cloud Gateway 依赖就够了,Consul Discovery 依赖后面要用到,加上这个依赖以后就需要在项目启动前启动一个 Consul Agent,详情查看上篇文章,另外我们不需要把网关注册到 Consul,所以要给 Application 类加一行注解 @EnableDiscoveryClient(autoRegister = false)
。
直接使用 IDEA 创建一个项目,选择 Maven 构建系统,JDK 我一直用 GraalVM JDK 17。
项目要用到的依赖有
另外为了开发方便,还要在构建系统里加入自动编译 proto 文件的构建插件 protobuf-maven-plugin。
哥们最近突击学习了一下 Spring 这些个东西。
Spring 提供了一个网站 Sping Initializr 用来创建初始的 Spring 项目,这里按我需求选了 Maven 和 Java 17,然后顺手添加了 3 个依赖:Spring Data JPA
、MySQL Driver
和 Spring Web
,依赖在项目创建后随时都能修改。点击 Generate
生成项目代码。
然后使用 IDEA 加载项目,这部分最好是能够提前配置一个镜像加速 Maven 下载依赖,配置镜像可以不用修改 Maven 自带的 settings.xml,可以在 $HOME/.m2/settings.xml
直接添加一个针对当前用户的配置文件或者在 mvn 中使用 -s
选项指定配置文件路径,这里可能需要注意的是,Maven 默认阻止 http 访问(你可以在 Maven 自带的 settings.xml 里看到相关配置),尽量使用 https 镜像仓库(或者删掉阻止规则?)。
IDEA 加载项目后可以利用自带的 Maven 执行 Package
操作,完成后会在 target 目录下两个 jar 文件。一个是默认打出来(被重命名过)的 jar.original,如果没有 spring-boot-maven-plugin,那么这个是生成的目标文件,但是这个 jar 是没办法直接通过 java -jar
运行的,它只包含了项目代码中最基本的文件,不包含运行 jar 必要的元信息和依赖,你可以通过命令查看一个 jar 包中的文件列表,例如:
1 | jar tf demo-0.0.1-SNAPSHOT.jar.original |
spring-boot-maven-plugin 插件会对 jar 重新打包,重新打包后的 jar 文件里包含了必要的元信息和依赖 jar 文件。直接把 jar 文件打包进 jar 的做法叫 jar in jar,其实 Maven 有插件能够搜集所有依赖并解压,然后把所有 class 文件重新打包成一个 jar,这样打包结果会更加精简,打包过程中可以做一些适当的裁剪,但是会和一些机制产生冲突(以后会解释)。
去年本想水一篇关于 NATS JetStream 的博客,当时写了一部分,结果翻资料翻到一篇博客 淺談 NATS、STAN 和 JetStream 兩三事,把我想说的基本都提到了,所以我当时弃坑了。最近用上了 NATS 2.9 版本,所以想着把新了解到的概念和之前想聊的一些内容都整理一下。如果你对 NATS 了解不多,非常推荐先看看我前面提到的那篇文章。
在升到 NATS JetStream 之前,我一直在用 STAN(aka NATS Streaming)。STAN 是 NATS 上一代持久化方案,是一个独立的 Server,它内嵌了 NATS Server,在这之上构建了一个持久化层,并且支持集群,集群节点之间使用 Raft 算法。STAN 在使用上基本接近 NATS,但是有个别要注意的地方。
一个是客户端连接时要提供 Cluster ID 和 Client ID。可以把 NATS 抽象看作 STAN 的网络层,客户端连入的是 NATS 的网络,访问这个网络上的 STAN Server,那么连接这个网络上的 Cluster 需要说明;Client ID 必须是唯一的,STAN 用 Client ID 和 Durable Name 区分 Consumer 持久化视图。
还有一个比较重要的是断线重连问题,也是我们踩过的坑。基于前面提到的抽象理解,STAN 构建于 NATS 网络层之上,客户端实际上没有真正连接到 STAN Server,所以断线重连机制变得复杂。记得当时是阿里云网络波动,STAN 客户端集体掉线后没有恢复,造成了不小影响。后来我封装 stan.go 实现了一个支持重连的客户端,通过 SetConnectionLostHandler
在连接断开时创建新连接,并且支持在连接切换后恢复订阅。
不过这些都不重要了,STAN 已经被标记为 deprecated,只维护到 2023.6。
JetStream 是 NATS 2.2 开始内置的持久化机制,满足 At-Least-Once 语义,也提供了一些机制支持 Exactly-Once 语义。目前发展到 2.9.15 版本,已经很好用了。
我一个 Linux Mint 用户,怎么就变成 Manjaro 用户了呢?
自从上次装完 Linux Mint 20 Xfce 以后,我的体验还是不错的,我不需要很臃肿复杂的环境,很多软件对 Ubuntu 系发行版支持确实不错,用起来也算是没什么大问题。直到前段时间手贱安装了 TwistUI,想卸载时发现很难卸载干净,所以决(zhi)定(neng)重装系统,正好时间点在 7 月份中旬,Linux Mint 21 还没有发布,于是需求变成了换个发行版。开始的时候我打算试试 NixOS,在虚拟机上体验了一会,可惜 NixOS 不是 FHS,怕自己用得不太习惯,最后选择了 Manjaro。
在 K8s 更新资源操作有两种形式:Replace 和 Patch。
Replace 形式主要用于替换对象 spec
,这是一个 read-then-write 操作,即你要先从 K8s 获取对象,修改 spec
,再提交更新。这个过程中对象其他字段不需要特意维护,提交的 apiVersion
、kind
和 metadata
中提供的信息会用于确定对象,而 status
则会被 K8s 忽略(如果需要更新 status
,需要用专用的操作)。
在对象 metadata
中有一个 resourceVersion
字段,用来追踪持久化对象的变动,每次持久化对象发生变化时 resourceVersion
都会改变。Replace 隐含的竞争策略乐观锁就是依靠这个字段实现的,当操作被提交到 K8s 但 resourceVersion
不匹配时,操作会失败。
Patch 操作直接在对象上修改指定的字段,并且通过 Patch 类型确定修改的方式(合并还是覆盖)。和 Replace 不同的是,Patch 操作竞争策略是 last-write-wins,这也意味着不需要先读取对象就可以修改对象。