<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>沐木</title><link>https://oldletter.cn/</link><description>沐木的个人微博</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Sun, 19 Jul 2026 01:00:00 +0800</lastBuildDate><atom:link href="https://oldletter.cn/index.xml" rel="self" type="application/rss+xml"/><item><title>信创实战：Nacos 在申威 3231 上的 RocksDB JNI 适配</title><link>https://oldletter.cn/posts/xinchuang-01-nacos/</link><pubDate>Sun, 01 Dec 2024 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/xinchuang-01-nacos/</guid><description><![CDATA[<blockquote>
<p>系列：信创国产化适配 | 作者：lin | 更新：2026-07-18</p></blockquote>
<hr>
<h2 id="先把适配对象和结果讲清楚">先把适配对象和结果讲清楚</h2>
<p>本文详细记录的源码修改、汇编适配、JNI 编译和 JAR 改造，全部发生在申威 3231 的 SW64 环境。下文的工具链、汇编和构建命令均以 SW64 为目标，不包含龙芯 LoongArch 的编译过程。</p>
<p><code>rocksdbjni 8.8.1</code> 适配完成后，申威 3231 和 HX8000 均已正常运行。</p>
<table>
  <thead>
      <tr>
          <th>平台</th>
          <th>原始情况</th>
          <th>最终结果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>龙芯 LoongArch</td>
          <td>当前已有可用支持</td>
          <td>只作现状对照，不套用申威编译过程</td>
      </tr>
      <tr>
          <td>申威 3231</td>
          <td>官方 8.8.1 JAR 缺少 SW64 原生库</td>
          <td>完成 SW64 汇编、编译和 JAR 改造后运行通过</td>
      </tr>
      <tr>
          <td>HX8000</td>
          <td>按现场环境单独验证</td>
          <td>运行通过</td>
      </tr>
  </tbody>
</table>
<p>原始故障仍要从 Nacos 的实际依赖看起。Nacos 经 JRaft 引入 <code>rocksdbjni 8.8.1</code>，官方通用 JAR 没有携带 SW64 原生库。申威 3231 的适配同时处理了 SWJDK、SW64 ABI、汇编实现、编译器、系统动态库和 Java 资源命名。</p>
<p>截至 2026-07-15，几个常见 Nacos 版本的依赖关系如下：</p>]]></description></item><item><title>信创实战：Dockerfile 信创基础镜像构建</title><link>https://oldletter.cn/posts/xinchuang-02-dockerfile/</link><pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/xinchuang-02-dockerfile/</guid><description><![CDATA[<blockquote>
<p>系列：信创国产化适配 | 作者：lin | 日期：2026-05-15</p></blockquote>
<hr>
<h2 id="一背景与问题">一、背景与问题</h2>
<h3 id="11-为什么信创环境需要自定义-docker-镜像">1.1 为什么信创环境需要自定义 Docker 镜像</h3>
<p>在信创国产化落地过程中，Docker 容器化部署是标配。但直接使用官方镜像会遇到以下问题：</p>
<ol>
<li><strong>架构不匹配</strong>：官方 Docker Hub 上的 OpenJDK 镜像主要提供 <code>amd64</code> 和 <code>aarch64</code>，缺少申威 <code>sw64</code>、龙芯 <code>loongarch64</code> 等国产架构</li>
<li><strong>OS 不兼容</strong>：信创环境通常要求使用国产操作系统（麒麟、统信），而官方镜像基于 Debian/Ubuntu</li>
<li><strong>安全合规</strong>：信创项目通常要求使用经过安全审计的基础镜像，不能直接拉取公网镜像</li>
<li><strong>定制需求</strong>：需要预装特定工具、配置国内镜像源、设置时区和 Locale 等</li>
</ol>
<h3 id="12-基础镜像选型对比">1.2 基础镜像选型对比</h3>
<table>
  <thead>
      <tr>
          <th>镜像</th>
          <th>体积</th>
          <th>包管理</th>
          <th>国产 CPU 支持</th>
          <th>适用场景</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>Alpine 3.20</strong></td>
          <td>~5MB</td>
          <td>apk</td>
          <td>仅 x86/arm64（申威/龙芯需基于国产OS）</td>
          <td>追求极致精简</td>
      </tr>
      <tr>
          <td><strong>Ubuntu 22.04</strong></td>
          <td>~77MB</td>
          <td>apt</td>
          <td>较好</td>
          <td>需要 glibc 环境</td>
      </tr>
      <tr>
          <td><strong>麒麟 V10</strong></td>
          <td>~200MB</td>
          <td>yum/dnf</td>
          <td>原生支持</td>
          <td>信创验收要求</td>
      </tr>
      <tr>
          <td><strong>统信 UOS</strong></td>
          <td>~200MB</td>
          <td>apt</td>
          <td>原生支持</td>
          <td>信创验收要求</td>
      </tr>
      <tr>
          <td><strong>Amazon Corretto</strong></td>
          <td>基于 Alpine</td>
          <td>apk</td>
          <td>官方多架构</td>
          <td>生产推荐</td>
      </tr>
  </tbody>
</table>
<div class="details admonition tip open">
    <div class="details-summary admonition-title">
        <i class="icon far fa-lightbulb" aria-hidden="true"></i>镜像选型建议<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content"><strong>鲲鹏 ARM64</strong>：直接使用 Amazon Corretto 官方多架构镜像。<strong>申威 SW64 / 龙芯 LoongArch</strong>：Alpine 官方不支持这两种架构，必须基于国产 OS（龙芯开源系统、统信 UOS、麒麟 V10 等）自行构建基础镜像。<strong>信创验收场景</strong>：基于麒麟/统信构建，满足安全审查要求。所有国产 CPU 环境都建议提前规划内部镜像仓库。</div>
    </div>
</div>
<hr>
<h2 id="二amazon-corretto-8-jdk-镜像构建alpine-320">二、Amazon Corretto 8 JDK 镜像构建（Alpine 3.20）</h2>
<h3 id="21-完整-dockerfile">2.1 完整 Dockerfile</h3>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-dockerfile">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-dockerfile" data-lang="dockerfile"><span class="line"><span class="cl"><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 信创基础镜像：Amazon Corretto 8 JDK (Alpine 3.20)</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 支持架构：x86_64, aarch64（鲲鹏）</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 适用场景：Java 微服务基础运行环境</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">FROM</span><span class="s"> alpine:3.20</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 镜像元信息</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">LABEL</span> <span class="nv">maintainer</span><span class="o">=</span><span class="s2">&#34;lin&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      <span class="nv">description</span><span class="o">=</span><span class="s2">&#34;Amazon Corretto 8 JDK for xinchuang&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      <span class="nv">version</span><span class="o">=</span><span class="s2">&#34;8u422&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>      architecture.multiarch<span class="o">=</span><span class="s2">&#34;true&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 环境变量</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># Corretto 版本（可通过构建参数覆盖）</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ARG</span> <span class="nv">CORRETTO_VERSION</span><span class="o">=</span><span class="m">8</span>.422.05.1<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ARG</span> <span class="nv">CORRETTO_SHA256_AMD64</span><span class="o">=</span>a7b7e2f8c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0
</span></span><span class="line"><span class="cl"><span class="k">ARG</span> <span class="nv">CORRETTO_SHA256_AARCH64</span><span class="o">=</span>b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">ENV</span> <span class="nv">JAVA_HOME</span><span class="o">=</span>/usr/lib/jvm/java-1.8.0-amazon-corretto <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">PATH</span><span class="o">=</span><span class="s2">&#34;/usr/lib/jvm/java-1.8.0-amazon-corretto/bin:</span><span class="si">${</span><span class="nv">PATH</span><span class="si">}</span><span class="s2">&#34;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">LANG</span><span class="o">=</span>en_US.UTF-8 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">LC_ALL</span><span class="o">=</span>en_US.UTF-8 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">TZ</span><span class="o">=</span>Asia/Shanghai <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="c1"># JVM 默认参数（可通过环境变量覆盖）</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>    <span class="nv">JAVA_OPTS</span><span class="o">=</span><span class="s2">&#34;-XX:+UseG1GC -XX:MaxGCPauseMillis=200 -Djava.security.egd=file:/dev/./urandom&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装基础依赖 + 配置国内镜像源</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 使用阿里云镜像源加速（信创环境通常无法直接访问 dl-cdn.alpinelinux.org）</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> sed -i <span class="s1">&#39;s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g&#39;</span> /etc/apk/repositories <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    apk add --no-cache <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        bash <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        curl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        wget <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        tzdata <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        ca-certificates <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        fontconfig <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        ttf-dejavu <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        busybox-extras <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        procps <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        gzip <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        tar <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        zip <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        unzip <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="o">&amp;&amp;</span> rm -rf /var/cache/apk/*<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 时区配置</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> ln -sf /usr/share/zoneinfo/<span class="si">${</span><span class="nv">TZ</span><span class="si">}</span> /etc/localtime <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nb">echo</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">TZ</span><span class="si">}</span><span class="s2">&#34;</span> &gt; /etc/timezone<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># Locale 配置</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">ENV</span> <span class="nv">LANG</span><span class="o">=</span>en_US.UTF-8 <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">LANGUAGE</span><span class="o">=</span>en_US:en <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="nv">LC_ALL</span><span class="o">=</span>en_US.UTF-8<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 安装 Amazon Corretto 8 JDK</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> <span class="nv">ARCH</span><span class="o">=</span><span class="k">$(</span>uname -m<span class="k">)</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="k">case</span> <span class="s2">&#34;</span><span class="si">${</span><span class="nv">ARCH</span><span class="si">}</span><span class="s2">&#34;</span> in <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        x86_64<span class="o">)</span>  <span class="nv">CORRETTO_ARCH</span><span class="o">=</span><span class="s2">&#34;x64&#34;</span> <span class="p">;;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        aarch64<span class="o">)</span> <span class="nv">CORRETTO_ARCH</span><span class="o">=</span><span class="s2">&#34;aarch64&#34;</span> <span class="p">;;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        *<span class="o">)</span>       <span class="nb">echo</span> <span class="s2">&#34;Unsupported architecture: </span><span class="si">${</span><span class="nv">ARCH</span><span class="si">}</span><span class="s2">&#34;</span> <span class="o">&amp;&amp;</span> <span class="nb">exit</span> <span class="m">1</span> <span class="p">;;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    <span class="k">esac</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    wget -q -O /tmp/corretto.tar.gz <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>        <span class="s2">&#34;https://corretto.aws/downloads/latest/amazon-corretto-</span><span class="si">${</span><span class="nv">CORRETTO_VERSION</span><span class="si">}</span><span class="s2">-linux-</span><span class="si">${</span><span class="nv">CORRETTO_ARCH</span><span class="si">}</span><span class="s2">-jdk.tar.gz&#34;</span> <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    mkdir -p /usr/lib/jvm <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    tar -xzf /tmp/corretto.tar.gz -C /usr/lib/jvm <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    ln -sf /usr/lib/jvm/amazon-corretto-<span class="si">${</span><span class="nv">CORRETTO_VERSION</span><span class="si">}</span>-linux-<span class="si">${</span><span class="nv">CORRETTO_ARCH</span><span class="si">}</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>           /usr/lib/jvm/java-1.8.0-amazon-corretto <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    rm -f /tmp/corretto.tar.gz <span class="o">&amp;&amp;</span> <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>    java -version<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># 内置健康检查脚本：java-info</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c"># ============================================================</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">RUN</span> cat &gt; /usr/local/bin/java-info &lt;&lt; <span class="s1">&#39;SCRIPT&#39;</span> <span class="o">&amp;&amp;</span> chmod +x /usr/local/bin/java-info<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="c">#!/bin/bash</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;=========================================&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;  Java Environment Info&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;=========================================&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;📦 JDK Version:&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>java -version 2&gt;<span class="p">&amp;</span><span class="m">1</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;📁 JAVA_HOME: </span><span class="si">${</span><span class="nv">JAVA_HOME</span><span class="si">}</span><span class="s2">&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;🏗️  Architecture: </span><span class="k">$(</span>uname -m<span class="k">)</span><span class="s2">&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;🐧 OS: </span><span class="k">$(</span>cat /etc/os-release 2&gt;/dev/null <span class="p">|</span> grep PRETTY_NAME <span class="p">|</span> cut -d<span class="o">=</span> -f2 <span class="p">|</span> tr -d <span class="s1">&#39;&#34;&#39;</span><span class="k">)</span><span class="s2">&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;🕐 Timezone: </span><span class="k">$(</span>cat /etc/timezone 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s1">&#39;not set&#39;</span><span class="k">)</span><span class="s2">&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;💾 Memory:&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>free -h 2&gt;/dev/null <span class="o">||</span> <span class="nb">echo</span> <span class="s2">&#34;  (free command not available)&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;🔧 JVM System Properties (key ones):&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>java -XshowSettings:all 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">|</span> grep -E <span class="s2">&#34;(java.home|os.arch|os.name|file.encoding|user.timezone)&#34;</span> <span class="p">|</span> sed <span class="s1">&#39;s/^/  /&#39;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="nb">echo</span> <span class="s2">&#34;=========================================&#34;</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span>SCRIPT<span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">WORKDIR</span><span class="s"> /app</span><span class="err">
</span></span></span><span class="line"><span class="cl"><span class="err"></span><span class="k">CMD</span> <span class="p">[</span><span class="s2">&#34;bash&#34;</span><span class="p">]</span></span></span></code></pre></div></div>
<h3 id="22-构建与使用">2.2 构建与使用</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># 构建镜像（单架构）</span>
</span></span><span class="line"><span class="cl">docker build -t xinchuang/corretto8-jdk:8u422-alpine3.20 .
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1"># 验证</span>
</span></span><span class="line"><span class="cl">docker run --rm xinchuang/corretto8-jdk:8u422-alpine3.20 java-info</span></span></code></pre></div></div>
<hr>
<h2 id="三docker-buildx-多架构构建">三、Docker Buildx 多架构构建</h2>
<h3 id="31-buildx-基础概念">3.1 Buildx 基础概念</h3>
<p>Docker Buildx 是 Docker 的扩展构建工具，支持一次构建同时生成多架构镜像。在信创场景下，因此可以用一条命令同时构建 x86_64 和 ARM64 的镜像。</p>]]></description></item><item><title>信创实战：Java 服务管理脚本（生产级）</title><link>https://oldletter.cn/posts/xinchuang-03-java-service-script/</link><pubDate>Thu, 10 Apr 2025 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/xinchuang-03-java-service-script/</guid><description><![CDATA[<blockquote>
<p>系列：信创国产化适配 | 作者：lin | 日期：2026-05-15</p></blockquote>
<hr>
<h2 id="一背景与问题">一、背景与问题</h2>
<h3 id="11-为什么需要专业的-java-服务管理脚本">1.1 为什么需要专业的 Java 服务管理脚本</h3>
<p>在很多项目中，Java 服务的启动方式还停留在：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># ❌ 野蛮启动方式</span>
</span></span><span class="line"><span class="cl">nohup java -jar app.jar &gt; app.log 2&gt;<span class="p">&amp;</span><span class="m">1</span> <span class="p">&amp;</span>
</span></span><span class="line"><span class="cl"><span class="nb">echo</span> <span class="nv">$!</span> &gt; app.pid</span></span></code></pre></div></div>
<p>这种方式存在诸多问题：</p>
<table>
  <thead>
      <tr>
          <th>问题</th>
          <th>影响</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>进程意外退出后无法自动重启</td>
          <td>服务不可用，需人工介入</td>
      </tr>
      <tr>
          <td>停止服务直接 <code>kill -9</code></td>
          <td>数据丢失、连接池未关闭</td>
      </tr>
      <tr>
          <td>没有 PID 管理</td>
          <td>重复启动、僵尸进程</td>
      </tr>
      <tr>
          <td>日志输出到 nohup.out</td>
          <td>日志无法轮转，磁盘被打满</td>
      </tr>
      <tr>
          <td>无法集成 systemd</td>
          <td>不符合 Linux 服务管理规范</td>
      </tr>
      <tr>
          <td>每次部署手动操作</td>
          <td>容易出错，效率低下</td>
      </tr>
  </tbody>
</table>
<h3 id="12-设计目标">1.2 设计目标</h3>
<p>我们要实现一个生产级的 Java 服务管理脚本，满足以下要求：</p>
<ol>
<li><strong>自动检测 Java</strong>：JAVA_HOME → 常见安装路径 → PATH，三级探测</li>
<li><strong>自动识别 jar</strong>：无需硬编码 jar 文件名</li>
<li><strong>首次部署兼容</strong>：自动创建目录结构、移动配置文件</li>
<li><strong>systemd 集成</strong>：一键安装/卸载系统服务</li>
<li><strong>受控停机</strong>：发送 SIGTERM 并等待，超时后再由策略决定是否发送 SIGKILL</li>
<li><strong>PID 文件管理</strong>：防止重复启动，精确控制进程</li>
</ol>
<hr>
<h2 id="二jvm-堆内存分配公式">二、JVM 堆内存分配公式</h2>
<p>在配置 <code>JAVA_OPTS</code> 之前，理解 JVM 内存分配至关重要：</p>]]></description></item><item><title>Ubuntu 使用 Docker 部署 GitLab（自定义端口 + 中文版）</title><link>https://oldletter.cn/posts/devops-03-gitlab-docker/</link><pubDate>Sat, 15 Aug 2020 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/devops-03-gitlab-docker/</guid><description><![CDATA[<h2 id="前言">前言</h2>
<p>自建 GitLab 的理由其实很简单：代码不想放公有云，团队又不想花大价钱买私有化方案。Docker 一把梭，半小时就能跑起来一台属于自己的 GitLab 服务器。</p>
<p>本文记录了我在 Ubuntu 18.04 上用 Docker Compose 部署 GitLab 中文版的完整过程，包括自定义端口映射、初始化配置、备份策略，以及踩过的几个坑。</p>
<h2 id="环境准备">环境准备</h2>
<h3 id="安装-docker">安装 Docker</h3>
<p>如果服务器上还没有 Docker，用官方脚本一键安装：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">curl -fsSL https://get.docker.com <span class="p">|</span> bash
</span></span><span class="line"><span class="cl">sudo usermod -aG docker <span class="nv">$USER</span></span></span></code></pre></div></div>
<p>国内网络不好的话，可以配置镜像加速。编辑 <code>/etc/docker/daemon.json</code>：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-json">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;registry-mirrors&#34;</span><span class="p">:</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://mirrors.tuna.tsinghua.edu.cn&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;https://docker.mirrors.ustc.edu.cn&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<p>然后重启 Docker：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo systemctl daemon-reload
</span></span><span class="line"><span class="cl">sudo systemctl restart docker</span></span></code></pre></div></div>
<h3 id="安装-docker-compose">安装 Docker Compose</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-bash">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">sudo apt install docker-compose -y</span></span></code></pre></div></div>
<p>验证安装：</p>]]></description></item><item><title>信创实战：信创数据库全景图</title><link>https://oldletter.cn/posts/xinchuang-04-database-guide/</link><pubDate>Sun, 15 Jun 2025 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/xinchuang-04-database-guide/</guid><description><![CDATA[<blockquote>
<p>信创进入深水区，数据库选型不再是&quot;能不能用&quot;的问题，而是&quot;怎么选、选了怎么落地&quot;的问题。本文从技术路线出发，梳理主流信创数据库的产品矩阵，给出面向架构师的选型决策框架。</p></blockquote>
<hr>
<h2 id="一引言信创进入深水区数据库选型是关键决策">一、引言：信创进入深水区，数据库选型是关键决策</h2>
<p>2025-2026 年，信创产业已从&quot;试点验证&quot;迈向&quot;规模化落地&quot;。在这一阶段，数据库作为基础软件的核心组件，其选型决策直接影响：</p>
<ul>
<li><strong>系统架构的天花板</strong>：选错了路线，后续迁移成本可能是天文数字；</li>
<li><strong>国产 CPU 的适配深度</strong>：不同数据库对飞腾、鲲鹏、海光、龙芯、申威的支持程度差异显著；</li>
<li><strong>业务连续性保障</strong>：安全可靠测评等级直接关系到能否进入关键行业采购目录；</li>
<li><strong>团队的技术栈投入</strong>：一条技术路线背后是一整套人才培养和运维体系。</li>
</ul>
<p>对架构师而言，理解国产数据库的技术路线分类，比记住某个产品的版本号更重要。路线决定了基因，基因决定了上限。</p>
<hr>
<h2 id="二核心认知国产数据库的四条技术路线">二、核心认知：国产数据库的四条技术路线</h2>
<p>国产数据库的发展路径，大致可以归结为四条技术路线。每条路线有不同的技术基因、生态位和适用场景。</p>
<h3 id="21-完全自研路线">2.1 完全自研路线</h3>
<p><strong>代表产品</strong>：达梦（DM）、崖山（YashanDB）、OceanBase、TiDB、虚谷</p>
<p><strong>技术特征</strong>：</p>
<ul>
<li>内核完全自研，不依赖任何开源数据库内核</li>
<li>拥有完整的存储引擎、查询优化器、事务管理器的自主知识产权</li>
<li>SQL 方言可能与主流数据库存在差异，但近年来普遍加强了兼容性</li>
</ul>
<p><strong>优势</strong>：</p>
<ul>
<li>知识产权清晰，不存在开源协议争议</li>
<li>内核可控，针对国产硬件的深度优化空间上限</li>
<li>安全可靠测评中&quot;自主可控&quot;维度得分更高</li>
<li>长期来看不受上游开源项目路线变更的影响</li>
</ul>
<p><strong>劣势</strong>：</p>
<ul>
<li>生态工具链需要从零建设或自建适配层</li>
<li>学习曲线相对陡峭，DBA 迁移成本较高</li>
<li>部分产品的社区活跃度和第三方文档不如开源路线丰富</li>
</ul>
<p><strong>适用场景</strong>：对自主可控要求极高的场景（军工、核心政务系统），以及需要深度定制内核的场景。</p>
<hr>
<h3 id="22-postgresql-二次开发路线">2.2 PostgreSQL 二次开发路线</h3>
<p><strong>代表产品</strong>：金仓（KingbaseES）、瀚高（HighGo DB）、大云海山（中国移动）</p>
<p><strong>技术特征</strong>：</p>
<ul>
<li>基于 PostgreSQL 内核进行深度二次开发</li>
<li>保留了 PostgreSQL 的 SQL 语法、扩展机制和工具链兼容性</li>
<li>在此基础上增加国产 CPU 适配、安全增强、高可用扩展等能力</li>
</ul>
<p><strong>优势</strong>：</p>
<ul>
<li>继承了 PostgreSQL 成熟的 SQL 标准支持和扩展生态</li>
<li>迁移成本相对较低，Oracle/PG DBA 可快速上手</li>
<li>社区工具链（pg_dump、pgAdmin 等）可直接复用</li>
<li>经过多年打磨，稳定性和兼容性已有大量生产验证</li>
</ul>
<p><strong>劣势</strong>：</p>
<ul>
<li>受 PostgreSQL 上游版本演进影响，大版本升级需要同步跟进</li>
<li>深度定制可能导致与上游 PG 的兼容性逐渐偏离</li>
<li>在极端性能场景下，二次开发层可能引入额外开销</li>
</ul>
<p><strong>适用场景</strong>：已有 PostgreSQL 或 Oracle 技术栈的团队，需要平滑迁移的场景；对 SQL 标准兼容性要求较高的 OLTP 业务。</p>]]></description></item><item><title>Java Stream 实用技巧：List 与 Map 的花式转换</title><link>https://oldletter.cn/posts/java-05-stream-utils/</link><pubDate>Thu, 03 Jun 2021 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/java-05-stream-utils/</guid><description><![CDATA[<h2 id="前言">前言</h2>
<p>Java 8 引入的 Stream API 彻底改变了我们处理集合数据的方式。相比传统的 for 循环，Stream 提供了声明式、链式、可组合的数据处理风格，代码更简洁，意图更清晰。</p>
<p>在日常开发中，我们经常面对这样的场景：从数据库查出一个 <code>List&lt;Map&gt;</code>，需要转成按某个 key 分组的 <code>Map</code>；两个接口返回的列表需要按 id 合并；或者需要对字段做批量转换。这些需求用 Stream 几行代码就能搞定，而传统写法往往要写一堆临时变量和嵌套循环。</p>
<p>本文整理了 7 个实用的 Stream 技巧，每个都附带完整可运行的示例代码，希望能帮助你在实际项目中写出更优雅的集合操作。</p>
<hr>
<h2 id="一list-转-mapkey-冲突处理">一、List 转 Map（key 冲突处理）</h2>
<p>基础也常见的操作。假设有一个用户列表，需要按 id 建索引：</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-java">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.stream.*</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">List</span><span class="o">&lt;</span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">users</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrayList</span><span class="o">&lt;&gt;</span><span class="p">();</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">users</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span><span class="p">{{</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">1L</span><span class="p">);</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Alice&#34;</span><span class="p">);</span><span class="w"> </span><span class="p">}});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">users</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span><span class="p">{{</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">2L</span><span class="p">);</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Bob&#34;</span><span class="p">);</span><span class="w"> </span><span class="p">}});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">users</span><span class="p">.</span><span class="na">add</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">HashMap</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;</span><span class="p">()</span><span class="w"> </span><span class="p">{{</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">,</span><span class="w"> </span><span class="n">3L</span><span class="p">);</span><span class="w"> </span><span class="n">put</span><span class="p">(</span><span class="s">&#34;name&#34;</span><span class="p">,</span><span class="w"> </span><span class="s">&#34;Charlie&#34;</span><span class="p">);</span><span class="w"> </span><span class="p">}});</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// 基本转换</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">Map</span><span class="o">&lt;</span><span class="n">Long</span><span class="p">,</span><span class="w"> </span><span class="n">Map</span><span class="o">&lt;</span><span class="n">String</span><span class="p">,</span><span class="w"> </span><span class="n">Object</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="n">result</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">users</span><span class="p">.</span><span class="na">stream</span><span class="p">()</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">collect</span><span class="p">(</span><span class="n">Collectors</span><span class="p">.</span><span class="na">toMap</span><span class="p">(</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">m</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="p">(</span><span class="n">Long</span><span class="p">)</span><span class="w"> </span><span class="n">m</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="s">&#34;id&#34;</span><span class="p">),</span><span class="w">   </span><span class="c1">// key mapper</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="n">m</span><span class="w"> </span><span class="o">-&gt;</span><span class="w"> </span><span class="n">m</span><span class="w">                      </span><span class="c1">// value mapper</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">));</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="n">result</span><span class="p">);</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="o">//</span><span class="w"> </span><span class="p">{</span><span class="n">1</span><span class="o">=</span><span class="p">{</span><span class="n">name</span><span class="o">=</span><span class="n">Alice</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="n">1</span><span class="p">},</span><span class="w"> </span><span class="n">2</span><span class="o">=</span><span class="p">{</span><span class="n">name</span><span class="o">=</span><span class="n">Bob</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="n">2</span><span class="p">},</span><span class="w"> </span><span class="n">3</span><span class="o">=</span><span class="p">{</span><span class="n">name</span><span class="o">=</span><span class="n">Charlie</span><span class="p">,</span><span class="w"> </span><span class="n">id</span><span class="o">=</span><span class="n">3</span><span class="p">}}</span></span></span></code></pre></div></div>
<p><strong>踩坑提示</strong>：如果 key 有重复，<code>toMap</code> 会抛 <code>IllegalStateException</code>。解决方案是提供第三个参数，合并函数：</p>]]></description></item><item><title>密码学基础：Hash、对称加密、非对称加密</title><link>https://oldletter.cn/posts/crypto-01-fundamentals/</link><pubDate>Thu, 05 Jan 2023 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/crypto-01-fundamentals/</guid><description><![CDATA[<blockquote>
<p>作者：林 | 系列：密码学 | 适合读者：后端开发 / 对密码学感兴趣的工程师</p></blockquote>
<hr>
<h2 id="一为什么要懂密码学">一、为什么要懂密码学</h2>
<p>作为开发者，你可能每天都在用密码学而不自知：</p>
<ul>
<li>HTTPS 连接 → TLS 握手（非对称 + 对称加密）</li>
<li>用户密码存储 → BCrypt/Argon2（散列函数）</li>
<li>JWT Token → HMAC-SHA256（消息认证码）</li>
<li>支付接口签名 → RSA/SM2（数字签名）</li>
<li>数据库敏感字段 → AES/SM4（对称加密）</li>
</ul>
<p>密码学并非安全团队的专属，重点是每个后端工程师的基本功。本文用下限的篇幅把三大支柱讲清楚，后续每篇深入一个方向。</p>
<hr>
<h2 id="二三大分类总览">二、三大分类总览</h2>
<table>
  <thead>
      <tr>
          <th>能力</th>
          <th>散列函数</th>
          <th>对称密码</th>
          <th>公钥密码</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>主要用途</td>
          <td>完整性、派生、消息认证的组成部分</td>
          <td>数据机密性与认证加密</td>
          <td>密钥协商、签名，部分算法支持加密</td>
      </tr>
      <tr>
          <td>是否可逆</td>
          <td>不可逆</td>
          <td>持有密钥可解密</td>
          <td>取决于具体算法与用途</td>
      </tr>
      <tr>
          <td>密钥</td>
          <td>普通散列无密钥，HMAC 有密钥</td>
          <td>双方共享密钥</td>
          <td>公钥与私钥成对</td>
      </tr>
      <tr>
          <td>常见算法</td>
          <td>SHA-2、SHA-3、SM3</td>
          <td>AES、SM4、ChaCha20</td>
          <td>RSA、ECC、SM2</td>
      </tr>
  </tbody>
</table>
<p>核心区别一句话：</p>
<ul>
<li><strong>Hash</strong>：单向压缩，不能解密，用于验证完整性</li>
<li><strong>对称加密</strong>：一把钥匙，能加能解，用于加密数据</li>
<li><strong>非对称加密</strong>：两把钥匙，公私配对，用于密钥交换和签名</li>
</ul>
<hr>
<h2 id="三散列函数hash">三、散列函数（Hash）</h2>
<h3 id="31-特征">3.1 特征</h3>
<table>
  <thead>
      <tr>
          <th>特征</th>
          <th>说明</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>单向性</td>
          <td>从哈希值无法反推出原文</td>
      </tr>
      <tr>
          <td>固定长度</td>
          <td>无论输入多长，输出长度固定</td>
      </tr>
      <tr>
          <td>雪崩效应</td>
          <td>输入改一个 bit，输出变化超过 50%</td>
      </tr>
      <tr>
          <td>抗碰撞</td>
          <td>找到两个不同输入产生相同输出，计算上不可行</td>
      </tr>
  </tbody>
</table>
<h3 id="32-常见算法">3.2 常见算法</h3>
<table>
  <thead>
      <tr>
          <th>算法</th>
          <th>输出长度</th>
          <th>状态</th>
          <th>用途</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>MD5</td>
          <td>128 bit</td>
          <td>❌ 已破解</td>
          <td>仅用于文件校验（非安全场景）</td>
      </tr>
      <tr>
          <td>SHA-1</td>
          <td>160 bit</td>
          <td>❌ 已破解</td>
          <td>不应再使用</td>
      </tr>
      <tr>
          <td>SHA-256</td>
          <td>256 bit</td>
          <td>✅ 安全</td>
          <td>数字签名、证书、区块链</td>
      </tr>
      <tr>
          <td>SHA-3</td>
          <td>可变</td>
          <td>✅ 安全</td>
          <td>SHA-2 的备选方案</td>
      </tr>
      <tr>
          <td>SM3</td>
          <td>256 bit</td>
          <td>✅ 安全</td>
          <td>国密体系，对标 SHA-256</td>
      </tr>
  </tbody>
</table>
<h3 id="33-代码示例">3.3 代码示例</h3>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-java">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-java" data-lang="java"><span class="line"><span class="cl"><span class="kn">import</span><span class="w"> </span><span class="nn">java.security.MessageDigest</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// SHA-256</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">byte</span><span class="o">[]</span><span class="w"> </span><span class="n">hash</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MessageDigest</span><span class="p">.</span><span class="na">getInstance</span><span class="p">(</span><span class="s">&#34;SHA-256&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">digest</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">.</span><span class="na">getBytes</span><span class="p">());</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="c1">// SM3（需要 BouncyCastle）</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="kt">byte</span><span class="o">[]</span><span class="w"> </span><span class="n">sm3Hash</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MessageDigest</span><span class="p">.</span><span class="na">getInstance</span><span class="p">(</span><span class="s">&#34;SM3&#34;</span><span class="p">)</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span><span class="p">.</span><span class="na">digest</span><span class="p">(</span><span class="s">&#34;hello&#34;</span><span class="p">.</span><span class="na">getBytes</span><span class="p">());</span></span></span></code></pre></div></div>
<h3 id="34-典型应用">3.4 典型应用</h3>
<table>
  <thead>
      <tr>
          <th>场景</th>
          <th>做法</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>密码存储</td>
          <td>BCrypt/Argon2（带盐的慢哈希）</td>
      </tr>
      <tr>
          <td>文件完整性校验</td>
          <td>SHA-256 摘要比对</td>
      </tr>
      <tr>
          <td>消息认证（HMAC）</td>
          <td>HMAC-SHA256(key, message)</td>
      </tr>
      <tr>
          <td>数字签名</td>
          <td>先 Hash 再用私钥签 Hash 值</td>
      </tr>
      <tr>
          <td>区块链</td>
          <td>SHA-256 链式哈希</td>
      </tr>
  </tbody>
</table>
<div class="details admonition warning open">
    <div class="details-summary admonition-title">
        <i class="icon fas fa-exclamation" aria-hidden="true"></i>别用 MD5/SHA-1 做安全用途<i class="details-icon fas fa-angle-right" aria-hidden="true"></i>
    </div>
    <div class="details-content">
        <div class="admonition-content">MD5 碰撞攻击成本已低至几秒，SHA-1 也在 2017 年被 Google 实际碰撞。密码存储、签名验证等安全场景，必须用 SHA-256+ 或 SM3。</div>
    </div>
</div>
<hr>
<h2 id="四对称加密symmetric-encryption">四、对称加密（Symmetric Encryption）</h2>
<h3 id="41-原理">4.1 原理</h3>
<p>加密和解密使用<strong>同一把密钥</strong>：</p>]]></description></item><item><title>散列函数：MD5、SHA、SM3 与 HMAC</title><link>https://oldletter.cn/posts/crypto-02-hash/</link><pubDate>Fri, 10 Feb 2023 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/crypto-02-hash/</guid><description><![CDATA[<blockquote>
<p>作者：林 | 系列：密码学 | 适合读者：后端开发 / 安全工程师</p></blockquote>
<hr>
<h2 id="一什么是散列函数">一、什么是散列函数</h2>
<p>散列函数（Hash Function）将任意长度的输入映射为固定长度的输出，且这个过程<strong>不可逆</strong>。</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">输入（任意长度）       →  散列函数  →  输出（固定长度）
</span></span><span class="line"><span class="cl">&#34;hello&#34;              →  SHA-256  →  2cf24dba5fb0a30e...（64 hex = 256 bit）
</span></span><span class="line"><span class="cl">&#34;hello world&#34;        →  SHA-256  →  b94d27b9934d3e08...（64 hex = 256 bit）
</span></span><span class="line"><span class="cl">整本《红楼梦》(2MB)   →  SHA-256  →  固定 64 hex 字符</span></span></code></pre></div></div>
<h3 id="11-密码学安全散列的四个性质">1.1 密码学安全散列的四个性质</h3>
<table>
  <thead>
      <tr>
          <th>性质</th>
          <th>含义</th>
          <th>被破坏的后果</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><strong>单向性</strong>（Preimage Resistance）</td>
          <td>给定 h，找不到 m 使得 H(m)=h</td>
          <td>能从哈希值反推出原文</td>
      </tr>
      <tr>
          <td><strong>第二原像抗性</strong>（Second Preimage）</td>
          <td>给定 m1，找不到 m2≠m1 使得 H(m1)=H(m2)</td>
          <td>能伪造替换文件</td>
      </tr>
      <tr>
          <td><strong>抗碰撞性</strong>（Collision Resistance）</td>
          <td>找不到任意 m1≠m2 使得 H(m1)=H(m2)</td>
          <td>能伪造证书/签名</td>
      </tr>
      <tr>
          <td><strong>雪崩效应</strong>（Avalanche Effect）</td>
          <td>输入改 1 bit，输出至少变化 50%</td>
          <td>能通过输出推测输入特征</td>
      </tr>
  </tbody>
</table>
<hr>
<h2 id="二主流算法对比">二、主流算法对比</h2>
<h3 id="21-算法一览">2.1 算法一览</h3>
<table>
  <thead>
      <tr>
          <th>算法</th>
          <th>输出长度</th>
          <th>分组长度</th>
          <th>结构</th>
          <th>安全状态</th>
          <th>性能（相对）</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>MD5</td>
          <td>128 bit</td>
          <td>512 bit</td>
          <td>Merkle-Damgård</td>
          <td>❌ 碰撞已破解</td>
          <td>速度高</td>
      </tr>
      <tr>
          <td>SHA-1</td>
          <td>160 bit</td>
          <td>512 bit</td>
          <td>Merkle-Damgård</td>
          <td>❌ 碰撞已破解</td>
          <td>快</td>
      </tr>
      <tr>
          <td>SHA-256</td>
          <td>256 bit</td>
          <td>512 bit</td>
          <td>Merkle-Damgård</td>
          <td>✅ 安全</td>
          <td>中</td>
      </tr>
      <tr>
          <td>SHA-512</td>
          <td>512 bit</td>
          <td>1024 bit</td>
          <td>Merkle-Damgård</td>
          <td>✅ 安全</td>
          <td>64位机更快</td>
      </tr>
      <tr>
          <td>SHA-3(256)</td>
          <td>256 bit</td>
          <td>1088 bit</td>
          <td>Sponge（海绵结构）</td>
          <td>✅ 安全</td>
          <td>略慢</td>
      </tr>
      <tr>
          <td>SM3</td>
          <td>256 bit</td>
          <td>512 bit</td>
          <td>Merkle-Damgård</td>
          <td>✅ 安全</td>
          <td>与 SHA-256 相当</td>
      </tr>
      <tr>
          <td>BLAKE3</td>
          <td>256 bit</td>
          <td>64 byte</td>
          <td>Merkle Tree</td>
          <td>✅ 安全</td>
          <td>极快（可并行）</td>
      </tr>
  </tbody>
</table>
<h3 id="22-md5为什么不能再用">2.2 MD5：为什么不能再用</h3>
<p>MD5 的碰撞攻击已经非常成熟：</p>]]></description></item><item><title>对称加密：AES 与 SM4 模式选择指南</title><link>https://oldletter.cn/posts/crypto-03-symmetric/</link><pubDate>Wed, 15 Mar 2023 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/crypto-03-symmetric/</guid><description><![CDATA[<blockquote>
<p>作者：林 | 系列：密码学 | 适合读者：后端开发 / 安全工程师</p></blockquote>
<hr>
<h2 id="一对称加密基础">一、对称加密基础</h2>
<p>对称加密使用同一把密钥进行加密和解密。当前主流的两个分组密码算法：</p>
<table>
  <thead>
      <tr>
          <th>属性</th>
          <th>AES</th>
          <th>SM4</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>全称</td>
          <td>Advanced Encryption Standard</td>
          <td>国密分组密码算法</td>
      </tr>
      <tr>
          <td>标准</td>
          <td>NIST FIPS 197 (2001)</td>
          <td>GM/T 0002 (2012)</td>
      </tr>
      <tr>
          <td>分组长度</td>
          <td>128 bit</td>
          <td>128 bit</td>
      </tr>
      <tr>
          <td>密钥长度</td>
          <td>128 / 192 / 256 bit</td>
          <td>128 bit</td>
      </tr>
      <tr>
          <td>轮数</td>
          <td>10 / 12 / 14</td>
          <td>32</td>
      </tr>
      <tr>
          <td>结构</td>
          <td>SPN (代换-置换网络)</td>
          <td>非平衡 Feistel</td>
      </tr>
      <tr>
          <td>安全性</td>
          <td>无已知实际攻击</td>
          <td>无已知实际攻击</td>
      </tr>
  </tbody>
</table>
<p>两者安全性等价（AES-128 ≈ SM4），区别在于合规要求：信创项目用 SM4，其他场景用 AES。</p>
<hr>
<h2 id="二加密模式比算法本身更重要">二、加密模式：比算法本身更重要</h2>
<p>分组密码一次只能处理一个固定长度的数据块（16 字节）。当明文超过 16 字节时，<strong>加密模式</strong>决定了如何处理多个分组。</p>
<p>模式选错，算法再强也白搭。</p>
<h3 id="21-ecb千万别用">2.1 ECB：千万别用</h3>
<p>ECB（Electronic Codebook）每个分组独立加密：</p>]]></description></item><item><title>非对称加密与签名：RSA、ECC、SM2</title><link>https://oldletter.cn/posts/crypto-04-asymmetric/</link><pubDate>Thu, 20 Apr 2023 00:00:00 +0000</pubDate><author>mumu</author><guid>https://oldletter.cn/posts/crypto-04-asymmetric/</guid><description><![CDATA[<blockquote>
<p>作者：林 | 系列：密码学 | 适合读者：后端开发 / 安全工程师</p></blockquote>
<hr>
<h2 id="一非对称加密原理">一、非对称加密原理</h2>
<p>对称加密用一把钥匙，非对称加密用一对钥匙：</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">公钥（Public Key）：可以公开，任何人都能拿到
</span></span><span class="line"><span class="cl">私钥（Private Key）：必须保密，只有自己持有
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">加密：用对方的公钥加密 → 只有对方的私钥能解
</span></span><span class="line"><span class="cl">签名：用自己的私钥签名 → 任何人用公钥都能验证</span></span></code></pre></div></div>
<p>非对称加密的数学基础是&quot;单向陷门函数&quot;，正向计算容易，反向计算极难，但持有特定信息（私钥）可以轻松反向。</p>
<hr>
<h2 id="二三大主流算法">二、三大主流算法</h2>
<h3 id="21-rsa">2.1 RSA</h3>
<p>基于<strong>大整数分解难题</strong>：给定两个大素数 p、q，计算 n = p × q 容易；但给定 n，分解出 p 和 q 极难。</p>
<div class="code-block code-line-numbers open" style="counter-reset: code-block 0">
    <div class="code-header language-text">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title=""><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-text" data-lang="text"><span class="line"><span class="cl">密钥生成：
</span></span><span class="line"><span class="cl">  1. 选两个大素数 p, q（各 1024 bit）
</span></span><span class="line"><span class="cl">  2. 计算 n = p × q（2048 bit）
</span></span><span class="line"><span class="cl">  3. 计算 φ(n) = (p-1)(q-1)
</span></span><span class="line"><span class="cl">  4. 选择 e（通常 65537），满足 gcd(e, φ(n)) = 1
</span></span><span class="line"><span class="cl">  5. 计算 d = e⁻¹ mod φ(n)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  公钥 = (n, e)
</span></span><span class="line"><span class="cl">  私钥 = (n, d)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">加密：C = M^e mod n
</span></span><span class="line"><span class="cl">解密：M = C^d mod n</span></span></code></pre></div></div>
<p>RSA 的问题：密钥太长。2048 bit 才提供 112 bit 安全强度，4096 bit 才提供 128 bit。</p>]]></description></item></channel></rss>