
说起 PGP(Pretty Good Privacy) 的工作原理,类似早期的 TLS/HTTPS 的 RSA 密钥交换,只不过它不依赖于第三方的 CA,而是直接在两端以信任的方式直接交换了公钥和私钥。 它是基于以下几点进行工作的
- 用工具(如 PGPTool 或 GnuPG) 生成公钥和私钥对,公钥发给加密方,私钥保存在解密方
- 公钥加密的数据只能用相应的私钥解密,这是非对称加密
- 由于非对称加密解密的计算量较大,通常用一个较小随机 Key 对数据进行对称加密,而只对 Key 用公钥进行非对称加密,再把对称加密的数据与非对称加密的 Key 发给解密方,在解密方用私钥解密出 Key, 再对数据进行解密。-- 这称之为混合加密,也是目前 HTTPS 的工作方式
- 另外,在只持有公钥的一方可以对使用私钥加密的数据进行签名验证,验证数据是否是由持有私钥的一方签发的,这就是数字签名的工作原理
- PGP 也会对生成的私钥用一个口令对其进行加密,防止私钥被盗时进一步保护
- 只用私钥可以做加解密与签名验证,如加密的数据能用相同的私钥解密,私钥签名再私钥验证。但交换私钥是不安全的,所以才有了可公开的公钥,非对称加密
下面用 GnuPG 和 Java 的 Bouncy Castle 包 进行演示加解密的过程。
Read More
Java 25 是一个 LTS 版本,它的众多新特性中就数
JEP 506: Scoped Values能改变我们从前使用ThreadLocal时的编程范式。因其重要性,所以单列一篇来专门学习它。Scoped Values, 可用来替代ThreadLocal的使用,特别是在虚拟线程当中。Scoped Values用于在当前线程或子线程中共享不可变的数据,说到不可变的数据就会想到JEP 502: Stable Values (Preview).ThreadLocal, 甚至InheritableThreadLocal用于子线程中共享数据还是有些挑战性,特别碰到线程池的情况, 而且ThreadLocal不在乎数据可变还是不可变的,执行当中谁改了数据,不知道。Scoped Values的 JEP 说其目的不是用来完全替代ThreadLocal。下面来看看
Scoped Values如何在子线程中共享数据,它还能与虚拟线程的结构化并发配合使用。ThreadLocal/InteritableThreadLocal仍然是很多框架用来在线程中(间)共享数据的办法,在Spring框架中有大量的使用, 如XxxContextHolder之类的。目前的ThreadLocal存在一些问题- ThreadLocal 中的数据是可变的,多数应用场景只要不可变的数据
- 难以管理共享数据的生命周期,特别是在线程池中,线程被重用时,ThreadLocal 中的数据可能会被意外共享或泄露。由使用方主动清除数据,更是会造成潜在的 Bug
- 成本高昂,创建子线程,或线程切换时要对共享数据进行复制,
VirtualThread也是继承自Thread, 所以虚拟线程也能用ThreadLocal。 一旦虚拟线程的数量达到成千上万,十百万的级别时,ThreadLocal数据不停复制的代价就很高了

Java 24 也是一个过渡版本, 还是到下面两个链接中找相应的更新
IntelliJ IDEA 对 Java 24 Language level 描述是
- 24 - No new language features
- 24 (Preview) - Flexible constructor bodies, simple source files, etc.
把上面第二个链接中的特性列出来
- 404: Generational Shenandoah (Experimental)
- 450: Compact Object Headers (Experimental)
- 472: Prepare to Restrict the Use of JNI
- 475: Late Barrier Expansion for G1
- 478: Key Derivation Function API (Preview)
- 479: Remove the Windows 32-bit x86 Port
- 483: Ahead-of-Time Class Loading & Linking
- 484: Class-File API *
- 485: Stream Gatherers *
- 486: Permanently Disable the Security Manager
- 487: Scoped Values (Fourth Preview)
- 488: Primitive Types in Patterns, instanceof, and switch (Second Preview)
- 489: Vector API (Ninth Incubator)
- 490: ZGC: Remove the Non-Generational Mode
- 491: Synchronize Virtual Threads without Pinning
- 492: Flexible Constructor Bodies (Third Preview)
- 493: Linking Run-Time Images without JMODs
- 494: Module Import Declarations (Second Preview)
- 495: Simple Source Files and Instance Main Methods (Fourth Preview)
- 496: Quantum-Resistant Module-Lattice-Based Key Encapsulation Mechanism
- 497: Quantum-Resistant Module-Lattice-Based Digital Signature Algorithm
- 498: Warn upon Use of Memory-Access Methods in sun.misc.Unsafe
- 499: Structured Concurrency (Fourth Preview)
- 501: Deprecate the 32-bit x86 Port for Removal
Java 24 中列出的特性恰如其版本一样,有 24 条,因其是一个过渡版本,多数为非正式特性,也就为什么 IntelliJ IDEA 从语言特性上把 Java 标记为 No new language features. 其实也不然,个人觉得有两个正式特性值得关注,即 Class-File API 和 Stream Gathers。
Read More
人们在跨语言, 跨进程通信方面采用过不少的方案, 交换文件, CORBA, SOAP, 最后得到最广泛应用的是 RESTful API, 交换格式通常用文本格式的 JSON 和 XML. 但作为更高效的通信还是二进制格式, 在 Java 方面, 有过 Java 内置的 RMI, Spring 的 Hessian, Dubbo. 而发展到今天 gRPC 受到了更多的关注, gRPC 的通信协议是 HTTP/2, 编码格式是 Google 高效的 Protobuf.
gRPC 是由 Google 发起的一个远程调用框架, 是 gRPC Remote Procedure Calls 的缩写, 各处的解释是 g 最初是代表 Google, 现在只是像 YAML Ain't Markup Language 类似的命名, 说 g 不再代表 Google, 怎么听起来有点 既要又要的感觉, 怎么都会认为 gRPC 为 Google 的 RPC.
总之, gRPC 是一个高性能的开源统一的 RPC 框架, 能叫做统一是因为它支持多种语方, 如 Go, C++, Java, Python, Node, C#, PHP 等, 多语言支持是 由 Protobuf 格式决定的, 总之它是 Protobuf over HTTP/2, 实现上是用 Protobuf 定义数据结构与服务方法, 再映射成不同语言的代码实现. gRPC 已然成为了 RPC 的标准, 真正意识到它的不一般是在 Postman 中发现了它, 可见其在业界受不到了应用的重视.
Read More
Java 22 正式引入了 Foreign Function & Memory API, 简称 FFM API, 它源自于 Project Panama, 旨在用来替代 JNI, 提供更安全,健壮的本地动态库调用方式。之前在一文 Java 22 新特性学习 中跳过了对它的体验, 因为直接用 FFM API 来调用本地代码的话,写起来比 JNI 还更复杂,更别提和 JNA 对比,性能上 FFM API 大致与 JNI 相当,但总比 JNA 要好些, 原因是 JNA 使用了一个与系统相关的中间层动态库。像这种调用本地代码的场景,性能的差异基本体现在本地代码上,调用过程的性能差异一般可以忽略不计.
那时没深入 FFM API 的原因是它用起来过于复杂,这种复杂性应该由第三方库来屏蔽,可是自 Java 22 于 2024-03-19 发布以来目前仍未找到一款类似于 JNA 屏蔽了 JNI 那样的基于 FFM 的第三方库。所以还是事先来体验一下 FFM API 的开发过程,现在可以借助 AI 来引导学习, 并在后面与 JNA 作个性能对比, 只供参考。
Read More
本想用一篇日志记录下在 Docker 容器中使用 Java 调用 C++ 动态库时,当 C++ Crash 时如何自动生成 core dump, 不想分成了至少三篇来完成这一研究。 可以回顾一下前两篇日志
本文是基于第二篇进一步推进,继续探索如何在 Docker 容器中 Java 调用 C++ 动态库时的 core dump 如何生成。首先测试的平台依然是 AWS EC2 实例, OS 为 Amazon Linux 2023, Docker 版本为 25.0.14。为叙事方便,本文所用代码与上篇一样,但还是重复一遍,省却了连接跳转。
Read More
在 Java 应用程序通过 JNA 调用 C++ 动态库时,C++ 代码运行在与 Java 同一进程中,当 C++ 代码 Crash 的时候,将会导致整个 Java 应用程序崩溃。 对于一个 Web 应用,这不是我们期望的结果,由于某一个请求输入的数据导致 C++ 代码崩溃了当前 Java 进程,从而造成该 Web 服务已接受到的所有请求全部失败, 这是非常糟糕的用户体验。如果是 Java 代码本身的异常我们可用 try/catch 进行保护,影响只限制在当前请求。如果是 C++ 代码崩溃的话,Java 应用程序无法捕获到这个异常,以致于整个 Java 应用程序崩溃,甚至发生这种情况时连 hs_err.log 文件都来不及生成,更别说生成 HeapDump, 或 CoreDump 了。
如果是用原始 JNI 的方式来调用动态库,我们还能在 JNI 相关的 C++ 代码中捕获到异常,并抛给 Java 去处理。而用 JNA, 我们贪图了它的方便, 比如一个 Java 进程中同时加载同一接口的不同动态库版本(JNI 要同样的实现必须用自定义的 ClassLoader),但在 C++ 代码崩溃时, Java 就显得无能为力了, 只能跟随着立即死亡, 并且在控制台下找不到关于 C++ 因何失败的线索。比如 C++ 中内存被多次释放,或地址越界访问破坏了内存数据等。
Read More
本文初衷是为了解决 Java 应用程序通过 JNA 调用 C++ 动态库时,C++ 代码运行崩溃导致整个 Java 应用程序崩溃而进行的研究。从一个 C++ 调用 C++ 写的动态库起步,记录它在什么情况下产生 core dump 文件,如何分析 core dump 文件等过程。可惜篇幅无法控制,不足以再加入 Java->JNA->C++ 动态库内容了,所以不得不单列此篇,并更名为 'C++ 调用 C++ 动态库时问题诊断'. 关于 Java JNA 到 C++ 的问题诊断只能另立一篇了。
下面我们来用 JNA 的方式来调用 C++ 动态库,演示当 C++ 代码崩溃时会发生什么,并试图找到好的诊断办法。以下演示在 Linux 下进行, 并且 Linux 发行版是 Amazon Linux 2023.
Read More
Java 23 也是一个过渡版本, 还是到下面两个链接中找相应的更新
IntelliJ IDEA 对 Java 23 Language level 描述是
- 23 - Markdown document comments
- 23 (Preview) - Primitive types in patterns, implicitly declared classes, etc.
把上面第二个链接中的特性列出来
本文对上面用红点标记的特性重点关注
Read More
Java 22 是一个过渡版本, 还是到下面两个链接中找相应的更新
IntelliJ IDEA 对 Java 22 Language level 描述是
- 22 - Unnamed variables and patterns
- 22(Preview) - Statements before super(), string templates (2nd preview), etc.
把上面第二个链接中的特性列出来
本文对上面用红点标记的特性重点关注
Read More