<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:atom="http://www.w3.org/2005/Atom"
>
<channel>
<title><![CDATA[青火]]></title> 
<atom:link href="https://blog.xuhaobo.cn/rss.php" rel="self" type="application/rss+xml" />
<description><![CDATA[盘龙界大圆满主神]]></description>
<link>https://blog.xuhaobo.cn/</link>
<language>zh-cn</language>
<generator>www.emlog.net</generator>
<item>
    <title>构建 OpenClaw Docker 镜像的完整方案</title>
    <link>https://blog.xuhaobo.cn/传统节气/87.html</link>
    <description><![CDATA[<p>好的,我来为你制定一个构建 OpenClaw Docker 镜像的完整方案。</p>
<p>## 📦 OpenClaw Docker 镜像构建方案</p>
<p>根据官方文档和代码分析,我给你提供一个从零开始的构建方案:</p>
<p>### 方案一:使用官方脚本快速构建(推荐)</p>
<p>OpenClaw 提供了官方的 `docker-setup.sh` 脚本,自动化程度高,适合大多数场景:</p>
<p>```bash<br># 1. 克隆仓库<br>git clone https://github.com/openclaw/openclaw.git<br>cd openclaw</p>
<p># 2. 运行官方设置脚本(自动化构建镜像)<br>./docker-setup.sh<br>```</p>
<p>这个脚本会自动:<br>- 构建或拉取 Docker 镜像<br>- 运行 onboarding 向导<br>- 生成 gateway token<br>- 启动 Docker Compose 服务</p>
<p>### 方案二:手动构建 Docker 镜像(完全可控)</p>
<p>如果你需要完全自定义构建过程,可以手动构建:</p>
<p>#### 步骤 1: 创建 Dockerfile</p>
<p>在项目根目录创建 `Dockerfile`:</p>
<p>```dockerfile<br># 基础镜像使用 Node.js 22 Bookworm<br>FROM node:22-bookworm</p>
<p># 安装 Bun(构建脚本需要)<br>RUN curl -fsSL https://bun.sh/install | bash<br>ENV PATH="/root/.bun/bin:${PATH}"</p>
<p># 启用 corepack<br>RUN corepack enable</p>
<p># 设置工作目录<br>WORKDIR /app</p>
<p># 优先复制依赖相关文件(利用 Docker 缓存层)<br>COPY package.json pnpm-lock.yaml pnpm-workspace.yaml .npmrc ./<br>COPY ui/package.json ./ui/package.json<br>COPY scripts ./scripts</p>
<p># 安装依赖<br>RUN pnpm install --frozen-lockfile</p>
<p># 复制源代码<br>COPY . .</p>
<p># 构建<br>RUN pnpm build<br>RUN pnpm ui:install<br>RUN pnpm ui:build</p>
<p># 设置生产环境<br>ENV NODE_ENV=production</p>
<p># 启动命令<br>CMD ["node","dist/index.js"]<br>```</p>
<p>#### 步骤 2: 构建 Docker 镜像</p>
<p>```bash<br># 基础构建<br>docker build -t openclaw:local -f Dockerfile .</p>
<p># 或者使用官方 Dockerfile(如果存在)<br>docker build -t openclaw:local .<br>```</p>
<p>#### 步骤 3: 创建 docker-compose.yml</p>
<p>```yaml<br>version: '3.8'</p>
<p>services:<br>&nbsp; openclaw-gateway:<br>&nbsp; &nbsp; image: openclaw:local<br>&nbsp; &nbsp; container_name: openclaw-gateway<br>&nbsp; &nbsp; ports:<br>&nbsp; &nbsp; &nbsp; - "18789:18789"<br>&nbsp; &nbsp; volumes:<br>&nbsp; &nbsp; &nbsp; - ~/.openclaw:/home/node/.openclaw<br>&nbsp; &nbsp; &nbsp; - ~/.openclaw/workspace:/home/node/.openclaw/workspace<br>&nbsp; &nbsp; environment:<br>&nbsp; &nbsp; &nbsp; - OPENCLAW_GATEWAY_BIND=lan<br>&nbsp; &nbsp; &nbsp; - OPENCLAW_GATEWAY_MODE=local<br>&nbsp; &nbsp; restart: unless-stopped</p>
<p>&nbsp; openclaw-cli:<br>&nbsp; &nbsp; image: openclaw:local<br>&nbsp; &nbsp; container_name: openclaw-cli<br>&nbsp; &nbsp; network_mode: "service:openclaw-gateway"<br>&nbsp; &nbsp; volumes:<br>&nbsp; &nbsp; &nbsp; - ~/.openclaw:/home/node/.openclaw<br>&nbsp; &nbsp; &nbsp; - ~/.openclaw/workspace:/home/node/.openclaw/workspace<br>&nbsp; &nbsp; environment:<br>&nbsp; &nbsp; &nbsp; - OPENCLAW_GATEWAY_URL=ws://127.0.0.1:18789<br>&nbsp; &nbsp; profiles:<br>&nbsp; &nbsp; &nbsp; - cli<br>```</p>
<p>#### 步骤 4: 运行容器</p>
<p>```bash<br># 启动服务<br>docker compose up -d openclaw-gateway</p>
<p># 运行 CLI 命令<br>docker compose run --rm openclaw-cli onboard</p>
<p># 查看日志<br>docker compose logs -f openclaw-gateway</p>
<p># 停止服务<br>docker compose down<br>```</p>
<p>### 方案三:使用官方预构建镜像</p>
<p>OpenClaw 在 GitHub Container Registry 提供了预构建镜像:</p>
<p>```bash<br># 拉取官方镜像<br>docker pull ghcr.io/openclaw/openclaw:latest</p>
<p># 或者指定版本<br>docker pull ghcr.io/openclaw/openclaw:2026.3.8</p>
<p># 创建 docker-compose.yml(将镜像改为 ghcr.io/openclaw/openclaw:latest)<br># 然后启动<br>docker compose up -d<br>```</p>
<p>### 高级自定义选项</p>
<p>#### 1. 添加额外的系统包</p>
<p>```bash<br>export OPENCLAW_DOCKER_APT_PACKAGES="ffmpeg build-essential git curl jq"<br>./docker-setup.sh<br>```</p>
<p>或者在 Dockerfile 中添加:</p>
<p>```dockerfile<br>RUN apt-get update &amp;&amp; apt-get install -y \<br>&nbsp; &nbsp; ffmpeg \<br>&nbsp; &nbsp; build-essential \<br>&nbsp; &nbsp; git \<br>&nbsp; &nbsp; curl \<br>&nbsp; &nbsp; jq \<br>&nbsp; &nbsp; &amp;&amp; rm -rf /var/lib/apt/lists/*<br>```</p>
<p>#### 2. 启用 Agent Sandbox</p>
<p>```bash<br>export OPENCLAW_SANDBOX=1<br>./docker-setup.sh<br>```</p>
<p>这会构建 sandbox 镜像并配置环境隔离。</p>
<p>#### 3. 持久化容器数据</p>
<p>```bash<br>export OPENCLAW_HOME_VOLUME="openclaw_home"<br>./docker-setup.sh<br>```</p>
<p>#### 4. 额外挂载目录</p>
<p>```bash<br>export OPENCLAW_EXTRA_MOUNTS="$HOME/.codex:/home/node/.codex:ro,$HOME/github:/home/node/github:rw"<br>./docker-setup.sh<br>```</p>
<p>### 安全配置建议</p>
<p>1. **非 root 用户运行**: 容器默认以 `node` 用户(UID 1000)运行<br>2. **文件权限**: 确保宿主机挂载目录的权限正确<br>&nbsp; &nbsp;```bash<br>&nbsp; &nbsp;sudo chown -R 1000:1000 ~/.openclaw<br>&nbsp; &nbsp;```<br>3. **网络绑定**: 使用 `lan` 或 `loopback` 模式,避免暴露到公网</p>
<p>### 验证安装</p>
<p>```bash<br># 检查健康状态<br>curl -fsS http://127.0.0.1:18789/healthz<br>curl -fsS http://127.0.0.1:18789/readyz</p>
<p># 打开控制台<br>http://127.0.0.1:18789/<br>```</p>
<p>### 故障排查</p>
<p>1. **权限错误**: 调整文件所有权<br>2. **端口占用**: 检查 18789 端口是否被占用<br>3. **构建失败**: 确保至少 2GB RAM 可用<br>4. **网络问题**: 检查 `gateway.bind` 配置</p>
<p>---</p>
<p>**推荐方案**: 如果你是第一次使用,直接用**方案一**(官方脚本)。如果需要深度定制,用**方案二**(手动构建)。如果只是快速部署,用**方案三**(官方镜像)。</p>
<p>你比较倾向哪个方案?我可以针对你选择的方案提供更详细的步骤。</p>]]></description>
    <pubDate>Fri, 20 Mar 2026 14:50:00 +0800</pubDate>
    <dc:creator>admin</dc:creator>
    <guid>https://blog.xuhaobo.cn/传统节气/87.html</guid>
</item>
<item>
    <title>1001-计算机原理-进制的转换</title>
    <link>https://blog.xuhaobo.cn/rankao/86.html</link>
    <description><![CDATA[<p>进制转化可以在chorme的console里面很方便的计算。</p>
<p>比如10进制转任意进制</p>
<pre class="language-javascript"><code>//(十进制数字)转成N进制 如下142转2进制，结果为'10001110'
(142).toString(2)
</code></pre>
<p>任意进制转10进制。</p>
<pre class="language-javascript"><code>//任意进制转10进制，第一个参数写原数，第二个参数写原数的进制。执行后得到10进制 142
parseInt('10001110',2)</code></pre>
<p>任意进制转任意进制</p>
<pre class="language-javascript"><code>//先转10进制，然后在转任意进制,以下示例为2进制转16进制，最终结果为8E
parseInt('10001110',2)
(142).toString(16)
</code></pre>
<hr>
<p>软考时候的计算方法</p>
<p>1.任意进制转10进制;&nbsp; &nbsp; 按权展开法</p>
<p><a href="/content/uploadfile/202603/d2b51773643168.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202603/d2b51773643168.png" alt="image.png"></a></p>
<p>2.10进制转任意进制&nbsp; &nbsp; 短除法</p>
<p><a href="/content/uploadfile/202603/d2b51773643320.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202603/d2b51773643320.png" alt="image.png"></a></p>
<p>3.特殊情况的&nbsp; 如第一个二进制转八进制，把每三位截断，不足的补齐。之后把三位的二进制转成10进制，最终得到的就是8进制。</p>
<p><a href="/content/uploadfile/202603/d2b51773643346.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202603/d2b51773643346.png" alt="image.png"></a></p>
<p>&nbsp;</p>]]></description>
    <pubDate>Mon, 16 Mar 2026 14:24:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/rankao/86.html</guid>
</item>
<item>
    <title>Java ASM系列：（001）ASM介绍</title>
    <link>https://blog.xuhaobo.cn/private/85.html</link>
    <description><![CDATA[<p>转载自：https://blog.51cto.com/lsieun/2924433</p>
<p>简单来说， ASM是一个操作Java字节码的类库。</p>
<p>为了能够更好的理解ASM是什么，我们需要来搞清楚两个问题：</p>
<p>第一个问题，ASM的操作对象是什么呢？<br>第二个问题，ASM是如何处理字节码（ByteCode）数据的？<br>首先，我们来看第一个问题：ASM的操作对象是什么呢？ 回答：ASM所操作的对象是字节码（ByteCode）数据。</p>
<p>我们都知道，一个.java文件经过Java编译器（javac）编译之后会生成一个.class文件。<br>在.class文件中，存储的是字节码（ByteCode）数据，如下图所示。ASM所的操作对象是是字节码（ByteCode），而在许多情况下，字节码（ByteCode）的具体表现形式是.class文件。</p>
<p><a href="/content/uploadfile/202601/d2b51769497700.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202601/d2b51769497700.png" alt="image.png"></a></p>
<p>接着，我们来看第二个问题：ASM是如何处理字节码（ByteCode）数据的？ 回答：ASM处理字节码（ByteCode）的方式是&ldquo;拆分－修改－合并&rdquo;。</p>
<p>ASM处理字节码（ByteCode）数据的思路是这样的：第一步，将.class文件拆分成多个部分；第二步，对某一个部分的信息进行修改；第三步，将多个部分重新组织成一个新的.class文件。</p>
<p>在 Wikipedia上，对ASM进行了如下描述：</p>
<p>ASM provides a simple API for decomposing(将一个整体拆分成多个部分), modifying(修改某一部分的信息), and recomposing(将多个部分重新组织成一个整体) binary Java classes (i.e. ByteCode).</p>
<p>2. ASM的过去和现在<br>2.1 ASM的过去<br>对于ASM的过去，主要说明三个问题：</p>
<p>第一个问题，ASM从什么时候开始出现的？<br>第二个问题，ASM的作者是谁？<br>第三个问题，ASM的名字有什么含义？<br>在2002年的时候，Eric Bruneton、Romain Lenglet和Thierry Coupaye发表了一篇文章，名为《 ASM: a code manipulation tool to implement adaptable systems》。在这篇文章当中，他们提出了ASM的设计思路。</p>
<p>一般来说，大写字母的组合，可能是多个单词的缩写形式，例如，JVM表示&ldquo;Java Virtual Machine&rdquo;。但是，ASM并不是多个单词的首字母缩写形式。在上面的文章中，记录了下面的话：</p>
<p>The ASM name does not mean anything: it is just a reference to the __asm__ keyword in C,<br>which allows some functions to be implemented in assembly language.</p>
<p>2.2 ASM的现在<br>对于ASM的现在，主要说明两个问题：</p>
<p>第一个问题，ASM属于哪一个机构？<br>第二个问题，ASM的Logo是什么样的？<br>The ASM library is a project of the &nbsp;OW2 Consortium. OW2 is an independent, global, open-source software community.</p>
<p>作为一个小故事，我们来说一下OW2组织是如何形成的。OW2组织的形成，与中国的一些大学和公司也有很大的关系（原文内容来自 这里）：</p>
<p>2002年，ObjectWeb项目启动，它是由INRIA、Bull和France Telecom共同开发的项目，并形成了一个成熟的、开源软件社区。<br>2004年，Orientware项目启动，由中国的北京大学、北航、国防科技大学、中创软件和中国科学院软件研究所共同研发。<br>2005年，ObjectWeb和Orientware签署了一份协议，决定共享代码库，一起开发中间件软件。<br>2006年，ObjectWeb和Orientware两个社区组织融合，形成了OW2组织。OW2组织名字，可能是由ObjectWeb和Orientware名称当中的两组O和W组合而来。<br>ASM的Logo设计很有特点，它在旋转的过程中，会分别呈现出&ldquo;A&rdquo;、&ldquo;S&rdquo;和&ldquo;M&rdquo;这三个字母，如下图所示：</p>
<p><a href="/content/uploadfile/202601/d2b51769497725.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202601/d2b51769497725.png" alt="image.png"></a></p>
<p>2.3 ASM的版本发展<br>对于ASM版本的发展，我们要说明两点：</p>
<p>第一点，Java语言在不断发展，那么，ASM版本也要不断发展来跟得上Java的发展。<br>第二点，在选择ASM版本的时候，要注意它支持的Java版本，来确保兼容性。<br>比如说，我们常用的Java版本是Java 8和Java 11。针对Java 8版本，我们需要使用ASM 5.0版本，就能正常工作。对于Java 11版本，我们需要使用ASM 7.0版本，就能正常工作。当然，我们可以尽量使用较高的ASM版本。</p>
<p>ASM Release &nbsp; &nbsp;Release Date &nbsp; &nbsp;Java Support<br>2.0 &nbsp; &nbsp;2005-05-17 &nbsp; &nbsp;Java 5 language support<br>3.2 &nbsp; &nbsp;2009-06-11 &nbsp; &nbsp;support for the new invokedynamic code.<br>4.0 &nbsp; &nbsp;2011-10-29 &nbsp; &nbsp;Java 7 language support<br>5.0 &nbsp; &nbsp;2014-03-16 &nbsp; &nbsp;Java 8 language support<br>6.0 &nbsp; &nbsp;2017-09-23 &nbsp; &nbsp;Java 9 language support<br>6.1 &nbsp; &nbsp;2018-03-11 &nbsp; &nbsp;Java 10 language support<br>7.0 &nbsp; &nbsp;2018-10-27 &nbsp; &nbsp;Java 11 language support<br>7.1 &nbsp; &nbsp;2019-03-03 &nbsp; &nbsp;Java 13 language support<br>8.0 &nbsp; &nbsp;2020-03-28 &nbsp; &nbsp;Java 14 language support<br>9.0 &nbsp; &nbsp;2020-09-22 &nbsp; &nbsp;Java 16 language support<br>9.1 &nbsp; &nbsp;2021-02-06 &nbsp; &nbsp;Java 17 language support<br>3. ASM能够做什么<br>3.1 通俗的理解<br>父类：修改成一个新的父类<br>接口：添加一个新的接口、删除已有的接口<br>字段：添加一个新的字段、删除已有的字段<br>方法：添加一个新的方法、删除已有的方法、修改已有的方法<br>&hellip;&hellip;（省略）<br><code>public class HelloWorld extends Object implements Cloneable {</code><br><code>&nbsp; &nbsp; public int intValue;</code><br><code>&nbsp; &nbsp; public String strValue;</code></p>
<p><code>&nbsp; &nbsp; public int add(int a, int b) {</code><br><code>&nbsp; &nbsp; &nbsp; &nbsp; return a + b;</code><br><code>&nbsp; &nbsp; }</code></p>
<p><code>&nbsp; &nbsp; public int sub(int a, int b) {</code><br><code>&nbsp; &nbsp; &nbsp; &nbsp; return a - b;</code><br><code>&nbsp; &nbsp; }</code></p>
<p><code>&nbsp; &nbsp; @Override</code><br><code>&nbsp; &nbsp; public Object clone() throws CloneNotSupportedException {</code><br><code>&nbsp; &nbsp; &nbsp; &nbsp; return super.clone();</code><br><code>&nbsp; &nbsp; }</code><br><code>}</code><br><br>3.2 专业的描述<br>ASM is an all-purpose(多用途的；通用的) Java ByteCode manipulation and analysis framework.<br>It can be used to modify existing classes or to dynamically generate classes, directly in binary form.</p>
<p>The goal of the ASM library is to generate, transform and analyze compiled Java classes,<br>represented as byte arrays (as they are stored on disk and loaded in the Java Virtual Machine).</p>
<p><a href="/content/uploadfile/202601/d2b51769497777.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202601/d2b51769497777.png" alt="image.png"></a></p>
<p>Program analysis, which can range from a simple syntactic parsing to a full semantic analysis, can be used to find potential bugs in applications, to detect unused code, to reverse engineer code, etc.<br>Program generation is used in compilers. This includes traditional compilers, but also stub or skeleton compilers used for distributed programming, Just in Time compilers, etc.<br>Program transformation can be used to optimize or obfuscate programs, to insert debugging or performance monitoring code into applications, for aspect oriented programming, etc.<br>4. 为什么要学习ASM？<br>平常，我们使用Java语言进行开发，能够解决很多的问题。我们可以把Java语言解决问题的范围称之为&ldquo;Java语言的世界&rdquo;。那么，ASM起什么作用呢？**ASM就是一处位于&ldquo;Java语言的世界&rdquo;边界上的一扇大门，通过这扇大门，我们可以前往&ldquo;字节码的世界&rdquo;。**在&ldquo;字节码的世界&rdquo;里，我们会看到不一样的&ldquo;风景&rdquo;，能够解决不一样的&ldquo;问题&rdquo;。</p>
<p><a href="/content/uploadfile/202601/d2b51769497798.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202601/d2b51769497798.png" alt="image.png"></a></p>
<p>ASM往往在一些框架的底层起着重要的作用。接下来，我们介绍两个关于ASM的应用场景：Spring和JDK。这两个应用场景例子的目的，就是希望大家了解到ASM的重要性。</p>
<p>4.1 Spring当中的ASM<br>第一个应用场景，是Spring框架当中的AOP。 在很多Java项目中，都会使用到Spring框架，而Spring框架当中的AOP（Aspect Oriented Programming）是依赖于ASM的。具体来说，Spring的AOP，可以通过JDK的动态代理来实现，也可以通过CGLIB实现。其中，CGLib (Code Generation Library)是在ASM的基础上构建起来的，所以，Spring AOP是间接的使用了ASM。（参考自 Spring Framework Reference Documentation的 8.6 Proxying mechanisms）。</p>
<p>4.2 JDK当中的ASM<br>第二个应用场景，是JDK当中的Lambda表达式。 在Java 8中引入了一个非常重要的特性，就是支持Lambda表达式。Lambda表达式，允许把方法作为参数进行传递，它能够使代码变的更加简洁紧凑。但是，我们可能没有注意到，其实，在现阶段（Java 8版本），Lambda表达式的调用是通过ASM来实现的。</p>
<p>在rt.jar文件的jdk.internal.org.objectweb.asm包当中，就包含了JDK内置的ASM代码。在JDK 8版本当中，它所使用的ASM 5.0版本。</p>
<p>如果我们跟踪Lambda表达式的编码实现，就会找到InnerClassLambdaMetafactory.spinInnerClass()方法。在这个方法当中，我们就会看到：JDK会使用jdk.internal.org.objectweb.asm.ClassWriter来生成一个类，将lambda表达式的代码包装起来。</p>
<p>LambdaMetafactory.metafactory() 第一步，找到这个方法<br>InnerClassLambdaMetafactory.buildCallSite() 第二步，找到这个方法<br>InnerClassLambdaMetafactory.spinInnerClass() 第三步，找到这个方法</p>
<p>5. 总结<br>本文主要是对ASM进行简单的介绍，希望大家能够对ASM有基本的了解，内容总结如下：</p>
<p>第一点，ASM所处理对象是字节码数据，也可以直观的理解成.class文件，不是.java文件。<br>第二点，ASM能够对字节码数据进行哪些操作呢？回答：analyze、generate、transform。<br>第三点，ASM可以形象的理解为&ldquo;Java语言世界&rdquo;的边缘上一扇大门，通过这扇大门，可以帮助我们进入到&ldquo;字节码的世界&rdquo;。<br>同时，本文当中并没有涉及到&ldquo;技术性&rdquo;的知识点，因此也不需要&ldquo;记忆&rdquo;任何内容，了解一下就够了。</p>]]></description>
    <pubDate>Tue, 27 Jan 2026 15:06:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/85.html</guid>
</item>
<item>
    <title>apache echarts-examples 离线部署攻略，离线开发的童鞋有福了</title>
    <link>https://blog.xuhaobo.cn/private/84.html</link>
    <description><![CDATA[<p>echarts官网demo界面，可以很方便的查询各种图表的制作方式和代码。但是某些离线场景下，就没那么方便了。</p>
<p>demo界面官网地址</p>
<p>&nbsp;https://echarts.apache.org/examples/zh/index.html</p>
<p>离线部署的帖子很多，<span style="background-color: #e03e2d;">不是很老，就是执行不了</span>。本文使用官方开源代码，全新编译，nginx部署，方便后面有需要的兄弟。</p>
<h4>1.序目</h4>
<p>阅读本文者，默认你本地已安转好nodejs、git、nginx等环境，并有一定的操作基础。</p>
<h4>2.准备工作</h4>
<p>&nbsp; &nbsp; &nbsp; 2.1 下载源码，在官方demo页面右上角，有githup图标，请注意，点击打开的不是echarts-examples的界面，需要在apache下重新搜索echarts，找到echarts-examples，链接为https://github.com/apache/echarts-examples，打不开的时候可以试试https://bgithub.xyz/apache/echarts-examples</p>
<p>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</p>
<pre class="language-ruby"><code>#git clone 代码
git clone https://github.com/apache/echarts-examples</code></pre>
<p>&nbsp; &nbsp; &nbsp;2.2 阅读文档</p>
<p><a href="/content/uploadfile/202511/d2b51763541982.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51763541982.png" alt="image.png"></a></p>
<p>2.3 编译代码并验证</p>
<p>按文档执行相关操作,如果顺利，在联网环境下你访问http://127.0.0.1/ 就已经能看见示例主页面了，如果提示403，可以尝试访问http://127.0.0.1/zh/index.html.示例页面查看也无问题。</p>
<pre class="language-markup"><code>#进入目录
cd echarts-examples
#安装依赖，必须--force
npm i --force
#编译代码
npm run compile:example
#将编译好的程序复制到nginx的html目录
cp ./public/*  ${nginx_root}/html
</code></pre>
<p><a href="/content/uploadfile/202511/d2b51763542339.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51763542339.png" alt="image.png"></a></p>
<p>&nbsp; &nbsp;</p>
<h4>3.离线适配</h4>
<p>3.1 下载引用的cdn资源。</p>
<p>因为官方示例引用了10几个cdn资源，当处于离线环境的时候，无法正常访问刚才部署的<span class="s1">echarts-examples。（模拟可以尝试拔网线|开发人员工具断网等方式）。</span></p>
<p><a href="/content/uploadfile/202511/d2b51763542666.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51763542666.png" alt="image.png"></a></p>
<p>我们需要找到项目引用的cdn资源文件，分两部分</p>
<p>3.1.1 html文件的直接引用资源</p>
<p>参考/html/zh/index.html，可以看到一共引用了7个资源文件，复制链接，将其下载到/html/wai/目录.最终文件大概如图，此图已包含js引用的资源</p>
<p><a href="/content/uploadfile/202511/d2b51763543077.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51763543077.png" alt="image.png"></a></p>
<p>3.1.2 js文件加载的引用资源</p>
<p>需要浏览器的开发者工具，打开任意一个官方示例，开始录制网络日志。之后搜索https://fastly.jsdelivr.net</p>
<p><a href="/content/uploadfile/202511/d2b51763543153.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51763543153.png" alt="image.png"></a></p>
<p>需要将这些资源文件分别下载下来，本文为方便也将其下载在/html/wai目录下，注意有些资源需要多级文件夹，比如</p>
<div class="row ">
<div class="header-value ">https://fastly.jsdelivr.net/npm/echarts/dist/echarts.min.js</div>
<div class="header-value ">&nbsp;</div>
<div class="header-value ">需要将其保存在/html/wai/echarts/dist/下。其他类似。</div>
<div class="header-value ">&nbsp;</div>
<div class="header-value ">tip:官方示例加载引用的三方js大部分相似，但是也有例外的，所以总共需要多少外部资源文件没去统计，按照3.1.1章节的图去下载，基本上大部分示例都可以运行了。也可以直接去文末下载整理好的buding包。</div>
</div>
<div class="row ">&nbsp;</div>
<p>3.2 调整已编译好的程序。</p>
<p>总计需要调整七个文件，两类。</p>
<p>3.2.1 html类 ：分别为/html/zh/和/html/en/目录下的六个html文件，使用以下内容，替换原html文件中的head内容。</p>
<pre class="language-markup"><code>&lt;head&gt;
    &lt;meta charset="UTF-8" /&gt;
    &lt;meta name="viewport" content="width=device-width, initial-scale=1.0" /&gt;
    &lt;link
      rel="shortcut icon"
      href="../wai/favicon.png"
    /&gt;
    &lt;title&gt;ECharts Examples&lt;/title&gt;
    &lt;style&gt;
      body {
        margin: 0;
      }
      ul,
      li {
        margin: 0;
        padding: 0;
      }
    &lt;/style&gt;
    &lt;link
      rel="stylesheet"
      href="../wai/bootstrap.min.css"
    /&gt;
    &lt;link
      rel="stylesheet"
      href="../wai/index.min.css"
    /&gt;
    &lt;script src="../wai/vue.min.js"&gt;&lt;/script&gt;
    &lt;script src="../wai/jquery.min.js"&gt;&lt;/script&gt;
    &lt;script src="../wai/bootstrap.min.js"&gt;&lt;/script&gt;
    &lt;script src="../wai/index.min.js"&gt;&lt;/script&gt;
  &lt;/head&gt;</code></pre>
<p>3.2.2 js文件类 /html/js/example-bundle.js文件，调整代码2687行，用以下代码替换。</p>
<pre class="language-markup"><code>var JSDELIVR_ROOT = '../wai/';</code></pre>
<p>3.3离线验证</p>
<p><span class="s1">可以尝试拔网线|开发人员工具断网等方式进行验证，打开本地连接http://127.0.0.1/zh/index.html 对各种示例依次验证，如有示例打不开，重复上述步骤，直到补充完资源文件即可。</span></p>
<p><span class="s1">3.4现成的文件下载</span></p>
<p><span class="s1">以下文件可直接下载后覆盖到nginx的html目录，<a href="https://chars.xuhaobo.cn/public/echars.zip" target="_blank" rel="noopener">点击下载非离线版本程序</a>。<a href="https://chars.xuhaobo.cn/public/buding.zip" target="_blank" rel="noopener">点击下载离线运行补丁文件</a></span></p>
<p><span class="s1">下载后直接将内容释放到启动好的nginx html目录下访问即可。</span></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span class="s1">祝离线开发的同学工作顺利。</span></p>]]></description>
    <pubDate>Wed, 19 Nov 2025 16:36:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/84.html</guid>
</item>
<item>
    <title>html实现打印小票效果代码</title>
    <link>https://blog.xuhaobo.cn/private/82.html</link>
    <description><![CDATA[<p>先上图</p>
<p><a href="/content/uploadfile/202511/d2b51762503016.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202511/d2b51762503016.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>代码如下</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<pre class="language-markup"><code>&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
    &lt;title&gt;选择文件夹&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
    &lt;input type="file" id="myFolderInput" webkitdirectory directory&gt;
    &lt;div id="fileList"&gt;&lt;/div&gt;

    &lt;script&gt;
        var folderInput = document.getElementById('myFolderInput');
        var fileListDiv = document.getElementById('fileList');

        // 当选择了文件夹后会触发change事件
        folderInput.addEventListener('change', function() {
            var files = this.files;
            var fileList = '';

            for (var i = 0; i &lt; files.length; i++) {
                if(!files[i].name.startsWith(".")){
                    var fileItem = displayFile(files[i], ''); // 添加第二个参数 'indent'，初始化为空字符串
                    fileList += fileItem.html;
                }
            }

            fileListDiv.innerHTML = fileList;
        });

        function displayFile(file, indent) {
            var rootpath="/Volumes/data/"
            var html = '&lt;a href="' +rootpath+ file.webkitRelativePath + '"&gt;' + file.name + '&lt;/a&gt; &lt;br&gt;';
            if (file.webkitDirectory) {
                html += '&lt;ul&gt;';
                var reader = new FileReader();
                reader.onload = function(event) {
                    var entries = JSON.parse(event.target.result);
                    for (var i = 0; i &lt; entries.length; i++) {
                        var entry = entries[i];
                        var item = displayFile(entry, indent + '---'); // 递归调用，增加第二个参数的缩进
                        html += item.html;
                    }
                };
                reader.readAsText(file);
                html += '&lt;/ul&gt;';
            }
            return { html: indent + html }; // 返回包含HTML字符串的对象，其中包含了目录结构的缩进和链接元素。
        }
    &lt;/script&gt;
    &lt;table style="margin-right:auto; margin-left:auto; -aw-border-insidev:0.25pt single #000000; border-collapse:collapse"&gt;
&lt;tbody&gt;
&lt;tr style="height:28.35pt"&gt;
&lt;td style="width:115.48pt; border-top:1.5pt solid #000000; border-right:0.75pt solid #000000; border-bottom:0.75pt solid #000000; vertical-align:middle; -aw-border-bottom:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'; -aw-import:ignore"&gt;&ensp;&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-top:1.5pt solid #000000; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; border-bottom:0.75pt solid #000000; vertical-align:middle; -aw-border-bottom:0.25pt single; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;播&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;种&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;面&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;积&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;（千公顷）&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-top:1.5pt solid #000000; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; border-bottom:0.75pt solid #000000; vertical-align:middle; -aw-border-bottom:0.25pt single; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;总　产　量&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;（万吨）&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-top:1.5pt solid #000000; border-left:0.75pt solid #000000; border-bottom:0.75pt solid #000000; vertical-align:middle; -aw-border-bottom:0.25pt single; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;单位面积产量&lt;/span&gt;&lt;/p&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:宋体"&gt;（公斤&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;/&lt;/span&gt;&lt;span style="font-family:宋体"&gt;公顷）&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-top:0.75pt solid #000000; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single; -aw-border-top:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'; font-weight:bold"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体; font-weight:bold"&gt;全国总计&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-top:0.75pt solid #000000; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single; -aw-border-top:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; font-weight:bold; vertical-align:middle"&gt;119319.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-top:0.75pt solid #000000; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single; -aw-border-top:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; font-weight:bold; vertical-align:middle"&gt;70649.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-top:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-top:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; font-weight:bold; vertical-align:middle"&gt;5921.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;北　　京&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;94.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;57.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6115.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;天　　津&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;399.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;270.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; background-color:#ffffff; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6780.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;河　　北&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6460.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3908.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; background-color:#ffffff; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6050.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;山　　西&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3151.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1468.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4660.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;内 蒙&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;古&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;7011.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4100.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5847.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;辽　　宁&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3577.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2500.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6989.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;吉　　林&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5853.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4266.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;7287.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;黑 龙&lt;/span&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;江&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;14754.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;8001.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5423.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;上　　海&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;130.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;98.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;7542.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;江　　苏&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5475.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3810.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6958.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;浙　　江&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1047.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;650.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6210.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;安　　徽&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;7344.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4184.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5696.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;福　　建&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;844.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;514.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6091.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;江　　西&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3774.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2196.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5818.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;山　　东&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;8412.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5710.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6787.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;河　　南&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;10777.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6719.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6234.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;湖　　北&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4722.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2785.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5897.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;湖　　南&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4773.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3078.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6448.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;广　　东&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2236.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1313.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5872.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;广　　西&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2841.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1403.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4939.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;海　　南&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;272.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;142.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5222.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;重　　庆&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2031.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1100.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5417.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;四　　川&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;6405.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3633.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5672.6&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;贵　　州&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2771.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1146.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4135.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;云　　南&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4246.8&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1993.5&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4694.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;西　　藏&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;199.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;112.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5664.7&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;陕　　西&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3031.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1352.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4460.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;甘　　肃&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2715.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;1296.1&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;4772.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;青　　海&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;305.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;118.3&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;3866.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;宁　　夏&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;694.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;385.9&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;5559.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td style="width:115.48pt; border-right:0.75pt solid #000000; border-bottom:1.5pt solid #000000; vertical-align:middle; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:center; line-height:12pt"&gt;&lt;span style="font-family:'Times New Roman'"&gt;&ensp;&lt;/span&gt;&lt;span style="font-family:宋体"&gt;新　　疆&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:123.75pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; border-bottom:1.5pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2960.0&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:119.05pt; border-right:0.75pt solid #000000; border-left:0.75pt solid #000000; border-bottom:1.5pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single; -aw-border-right:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;2330.2&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;
&lt;td style="width:121.38pt; border-left:0.75pt solid #000000; border-bottom:1.5pt solid #000000; vertical-align:middle; -aw-border-left:0.25pt single"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:right; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:'Times New Roman'; vertical-align:middle"&gt;7872.4&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr style="height:14.15pt"&gt;
&lt;td colspan="4" style="width:481.9pt; border-top:1.5pt solid #000000; vertical-align:middle"&gt;
&lt;p style="margin:0pt 2.85pt; text-align:left; line-height:12pt; widows:2; orphans:2; vertical-align:middle"&gt;&lt;span style="font-family:楷体; vertical-align:middle"&gt;注：此表中部分数据因四舍五入，分省合计数与全国数略有差异。&lt;/span&gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;/body&gt;
&lt;/html&gt;</code></pre>]]></description>
    <pubDate>Fri, 07 Nov 2025 16:09:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/82.html</guid>
</item>
<item>
    <title>使用 SSH 动态端口转发（SOCKS5 代理）绕过实现微信登录</title>
    <link>https://blog.xuhaobo.cn/private/81.html</link>
    <description><![CDATA[<p><span style="background-color: #fbeeb8;">问题:我公司封禁了微信相关的网络，但是我可以访问我的阿里云服务器。我想使用阿里云服务器做一个代理，让我的微信使用阿里云代理进行登录。请问我应该怎么做？</span></p>
<p>正文：<strong>使用 SSH 动态端口转发（SOCKS5 代理）实现微信登录</strong></p>
<p>你可以利用你拥有的<strong>阿里云服务器</strong>搭建一个代理（如 SOCKS5 或 HTTP 代理），然后在本地微信客户端中配置该代理，从而绕过公司网络对微信的限制。这期间理论上不需要安装任何三方软件。以下是详细操作步骤：</p>
<h2 data-spm-anchor-id="5176.28103460.0.i33.64592e31jnYW29">🧩 前提条件</h2>
<ul>
<li>你已拥有一台&nbsp;<strong>公网 IP 可访问的阿里云 ECS 实例</strong>（建议为 Linux 系统，如 CentOS 或 Ubuntu）。</li>
<li>你本地电脑可以访问这台服务器（SSH 可连通）。</li>
<li>你了解基本的 Linux 命令操作。</li>
<li>公司网络<strong>未封禁你阿里云服务器的 IP 或端口</strong>（否则代理也无法使用）。</li>
</ul>
<h3>🔧 原理</h3>
<p data-spm-anchor-id="5176.28103460.0.i34.64592e31jnYW29">通过 SSH 连接到你的阿里云服务器，并开启本地的 SOCKS5 代理端口（如 1080），所有流量都会加密转发到服务器，再由服务器访问微信服务器。</p>
<h2 data-spm-anchor-id="5176.28103460.0.i35.64592e31jnYW29">🖥️ 步骤一：在本地电脑建立 SSH 动态代理</h2>
<h3 data-spm-anchor-id="5176.28103460.0.i37.64592e31jnYW29">Windows 用户（推荐使用 PowerShell 或 WSL）</h3>
<h4 data-spm-anchor-id="5176.28103460.0.i36.64592e31jnYW29">使用 PowerShell（Windows 10/11 自带 OpenSSH 客户端）</h4>
<ol>
<li>打开&nbsp;<strong>PowerShell</strong>（以普通用户运行即可）</li>
<li>执行以下命令（替换为你自己的服务器信息）：</li>
</ol>
<pre class="language-markup"><code>ssh -D 1080 -f -N -C root@你的阿里云服务器公网IP
</code></pre>
<p data-spm-anchor-id="5176.28103460.0.i40.64592e31jnYW29">参数说明：</p>
<ul>
<li><code>-D 1080</code>：在本地开启 SOCKS5 代理，监听 127.0.0.1:1080</li>
<li><code>-f</code>：后台运行</li>
<li><code>-N</code>：不执行远程命令（仅用于端口转发）</li>
<li data-spm-anchor-id="5176.28103460.0.i39.64592e31jnYW29"><code>-C</code>：启用压缩（可选，提升速度）</li>
<li><code>root@...</code>：你的服务器登录用户和 IP</li>
</ul>
<blockquote>
<p>⚠️ 首次连接会提示确认指纹，输入 <code>yes</code> 回车。</p>
</blockquote>
<ol start="3">
<li>输入服务器密码（或使用密钥登录更安全）</li>
<li>如果阿里云服务器端的ssh端口不是默认的22,需要在命令行内添加-p [port]参数，例如</li>
</ol>
<pre class="language-markup"><code>ssh -D 1080 -f -N -C -p ssh端口 root@你的阿里云服务器公网IP
</code></pre>
<p>✅ 此时，你的本地 <code>127.0.0.1:1080</code> 就是一个 SOCKS5 代理</p>
<h3 data-spm-anchor-id="5176.28103460.0.i37.64592e31jnYW29">&nbsp;</h3>
<h2><span style="font-size: 16px;">MAC用户在终端开启 SSH 动态代理</span></h2>
<ol>
<li>打开&nbsp;<strong>终端（Terminal）</strong></li>
<li data-spm-anchor-id="5176.28103460.0.i71.64592e31jnYW29">执行以下命令（替换为你的服务器信息）</li>
</ol>
<pre class="language-markup"><code>ssh -D 1080 -f -N -C root@你的阿里云服务器公网IP
</code></pre>
<h2 data-spm-anchor-id="5176.28103460.0.i35.64592e31jnYW29">🖥️ 步骤二：代理验证</h2>
<p><span style="font-size: 18px;">1.检查本地端口是否监听</span></p>
<p data-spm-anchor-id="5176.28103460.0.i78.64592e31jnYW29">运行以下命令，查看 1080 端口是否被 SSH 占用：</p>
<pre class="language-markup"><code>lsof -i :1080</code></pre>
<p>正常输出类似</p>
<pre class="language-markup"><code>COMMAND   PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ssh      12345  yourname   4u  IPv4 0xabcdef123456789      0t0  TCP localhost:socks (LISTEN)</code></pre>
<p>✅ 有输出 &rarr; 本地 SOCKS5 代理已启动 <br>❌ 无输出 &rarr; SSH 代理未运行<br><span style="font-size: 18px;">2.使用 <code>curl</code> 通过 SOCKS5 代理查询公网 IP</span></p>
<pre class="language-markup"><code>curl --socks5-hostname 127.0.0.1:1080 https://ip.xuhaobo.cn</code></pre>
<div>
<ul>
<li>🔍 输出应为你的&nbsp;<strong>阿里云服务器公网 IP</strong>，而不是你本地公司网络的 IP。</li>
</ul>
</div>
<div>
<div>
<pre class="language-markup"><code>curl --socks5-hostname 127.0.0.1:1080 https://ip.xuhaobo.cn

{
    "request_info": {
        "ip": "18.147.33.84",
        "user_agent": "curl/7.61.0",
        "referer": "none",
        "method": "GET",
        "url": "https://ip.xuhaobo.cn/",
        "timestamp": "2025-10-14 07:45:12"
    },
    "ip_geo_info": {
        "ip": "18.147.33.84",
        "country": "CN",
        "region": "Beijing",
        "city": "Beijing",
        "org": "AS31963 Hangzhou Alibaba Advertising Co.,Ltd.",
        "hostname": "unknown"
    }
}</code></pre>
</div>
</div>
<p data-spm-anchor-id="5176.28103460.0.i75.64592e31jnYW29">✅ 如果返回的是阿里云 IP &rarr;&nbsp;<strong>代理生效！</strong><br>❌ 如果报错或返回本地 IP &rarr; 代理未生效。</p>
<blockquote>
<p>💡 注意：必须用 <code>--socks5-hostname</code>（不是 <code>--socks5</code>），否则 DNS 会在本地解析，可能被污染或拦截。</p>
</blockquote>
<h2 data-spm-anchor-id="5176.28103460.0.i35.64592e31jnYW29">🖥️ 步骤三：微信配置代理</h2>
<p>打开微信，未登录状态下，点击设置-&gt;代理设置，在弹出的窗口填写服务器地址为本机，端口为上述配置时的端口。点击【测试并保存】，当软件提示测试通过时，你就可以当前主机使用代理网络登录微信了。&nbsp;</p>
<p><a href="/content/uploadfile/202510/d2b51760428204.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202510/d2b51760428204.png" alt="image.png"></a></p>
<h2 data-spm-anchor-id="5176.28103460.0.i80.64592e31jnYW29">🛠️ 常见问题排查</h2>
<table>
<thead>
<tr>
<th>问题</th>
<th>可能原因</th>
<th>解决方案</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>curl</code>&nbsp;返回本地 IP</td>
<td>用了&nbsp;<code>--socks5</code>&nbsp;而非&nbsp;<code>--socks5-hostname</code></td>
<td>改用&nbsp;<code>--socks5-hostname</code></td>
</tr>
<tr>
<td><code>curl</code>&nbsp;超时或拒绝连接</td>
<td>SSH 代理未运行 / 端口被占用</td>
<td>用&nbsp;<code>lsof -i :1080</code>&nbsp;检查</td>
</tr>
<tr>
<td>有进程，但是验证失败</td>
<td>因为网络策略、防火墙或服务器配置问题，<strong>-D 转发被禁用</strong></td>
<td>调整阿里云服务器的sshd_config，确保
<pre><code data-spm-anchor-id="5176.28103460.0.i83.64592e31jnYW29"><span data-spm-anchor-id="5176.28103460.0.i82.64592e31jnYW29">AllowTcpForwarding yes</span></code></pre>
</td>
</tr>
<tr>
<td>微信仍无法登录</td>
<td>尝试使用三方软件如ProxyCap进行流量绑定</td>
<td>确认 WeChat.app 路径正确，重启 ProxyCap</td>
</tr>
<tr>
<td>SSH 连接断开</td>
<td>网络不稳定</td>
<td>加&nbsp;<code>-o ServerAliveInterval=30</code> 参数保持</td>
</tr>
</tbody>
</table>]]></description>
    <pubDate>Tue, 14 Oct 2025 10:22:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/81.html</guid>
</item>
<item>
    <title>基于 Web Speech API 实现网页上的语音合成和语音识别功能</title>
    <link>https://blog.xuhaobo.cn/private/80.html</link>
    <description><![CDATA[<pre class="language-markup"><code>&lt;html lang="zh-CN"&gt;
&lt;head&gt;
    &lt;meta charset="utf-8"&gt;
    &lt;meta http-equiv="content-type" content="text/html; charset=utf-8"&gt;
    &lt;meta name="renderer" content="webkit"/&gt;
    &lt;meta name="force-rendering" content="webkit"/&gt;
    &lt;title&gt;基于 Web Speech API 实现网页上的语音合成和语音识别功能&lt;/title&gt;

&lt;script&gt;

function firstDemo(){
// 获取语音合成对象
const synth = window.speechSynthesis;
// 创建一个语音合成实例，并设置要朗读的文本
var demoStr ='这是一个简单的语音合成测试。';
var book1 =document.getElementById('book1').value;
if(book1!=''){
demoStr=book1;
}
const utterance = new SpeechSynthesisUtterance(demoStr);
utterance.lang = "zh-CN";

// 设置语速，取值范围通常在 0.1 到 10 之间，这里设置为 0.8 倍速
utterance.rate = 0.9;

// 设置语调，取值范围一般在 0 到 2 之间，这里设为 1.5
utterance.pitch = 1.2;

// 设置音量，取值范围从 0 到 1，这里设为 0.5
utterance.volume = 1;
// 语音开始播放事件监听
utterance.onstart = function() {
  console.log('语音开始播放');
};
// 语音暂停事件监听
utterance.onpause = function() {
  console.log('语音暂停');
};
// 语音结束事件监听
utterance.onend = function() {
  console.log('语音结束');
};

// 触发语音合成并播放
synth.speak(utterance);

}
&lt;/script&gt;
&lt;/head&gt;
&lt;input type="button" value = "简单测试" onclick="firstDemo()"&gt;
&lt;br&gt;
&lt;hr&gt;
&lt;br&gt;
&lt;textarea id="book1" style="width=1000px;height: 200px;"&gt;&lt;/textarea&gt;
&lt;input type="button" value = "输入阅读" onclick="firstDemo()"&gt;</code></pre>]]></description>
    <pubDate>Fri, 10 Oct 2025 15:27:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/80.html</guid>
</item>
<item>
    <title>nginx日志查询top10</title>
    <link>https://blog.xuhaobo.cn/private/79.html</link>
    <description><![CDATA[<h2><span class="words-blog hl-git-1" data-tit="nginx" data-pretit="nginx">nginx</span>日志大流量和大并发的查询</h2>
<p>当并发高的时候，先统计某一时间段访问最多的ipTOP10都有哪些，然后查看这些ip都访问了什么？&nbsp;</p>
<p>(1)统计访问最多的ip top10</p>
<pre class="language-markup"><code>sed -n '/17:45/,/18:00/p' access_2018-06-30.log|awk '{print $1}'|sort -nr |uniq -c |sort -nr|head -10 
    909 112.14.31.121
    472 123.139.16.179
    423 23.91.100.204
    416 121.30.192.10
    302 101.69.132.78
    281 101.69.132.82
    281 101.69.132.79
    274 101.69.132.81
    269 101.69.132.77
    252 101.69.132.76</code></pre>
<p>(2)查看这些ip访问了些什么</p>
<pre class="language-markup"><code>cat access_2018-06-30.log |grep "123.139.16.179"|sort -nr|uniq -c |sort -nr|more</code></pre>
<h2>其他的nginx日志各种查询</h2>
<p>日志如下：<br><code>116.211.124.29 - - [08/Feb/2018:04:58:45 +0800] video.yingyou360.cn "GET /do_not_delete/noc.gif HTTP/1.1" 200 3166 "-" "ChinaCache" "118.118.215.18"</code></p>
<p>（1）访问量大于1000的ip<br><code>awk '{print $1}' access.log | sort -n |uniq -c |awk '{if($1 &gt;1000) print $0}'|sort -rn</code><br><br>（2）当天IP访问量<br><code>awk '{print $1}' /alidata/nginx/logs/access.log | sort -n | uniq | wc -l</code><br><br>（3）查询某个 IP 的详细访问情况，按访问频率排序（这里可以使用$1传参）<br><code>grep '123.12.11.20' /alidata/nginx/logs/access.log |awk '{print $8}'|sort |uniq -c |sort -rn |head -n 100</code><br><br>（4）查看访问最频的页面(TOP100)<br><code>awk '{print $8}' access.log | sort |uniq -c | sort -rn | head -n 100</code><br><br>（5）统计某个网站每分钟的请求数，top100 的时间点(精确到分钟)<br><code>awk &nbsp;'{print $4,$6}' access_2018-09-30.log |cut -c 1-18,22-100|grep "img1.yingyou360.cn"|sort -nr|uniq -c|sort -nr|head -10</code><br><br>（6）查看某个网站每秒的请求数，top100(把访问时间排第一列然后统计在同一时刻该网站被访问了了多少次)<br><code>awk '{print $4,$6}' access.log |grep "img1.ying360.cn"|sort|uniq -c|sort -nr|head -n 100</code><br><br>（7）查询当天某个时间段的IP访问量,4-5点<br><code>grep "07/Apr/2017:0[4-5]" access.log | awk '{print $1}' | sort | uniq -c| sort -nr | wc -l</code><br><br>（8）查询某个ip每秒钟访问某个网址的并发数（把总数加起来除以10求平均值）<br><code>cat access_2018-09-30.log|awk '{print $4,$1,$6}'|grep "112.49.26.41"|grep "img1.yingyou360.cn"|sort -nr|uniq -c|sort -nr|head -10</code></p>
<p>（9）查看某个时间段流量访问最大的日志信息</p>
<pre class="language-markup"><code>116.211.124.29 - - [08/Feb/2018:04:58:45 +0800] video.yingyou360.cn "GET /do_not_delete/noc.gif HTTP/1.1" 200 3166 "-" "ChinaCache" "118.118.215.18"
122.228.115.136 - - [08/Feb/2018:08:42:19 +0800] mobile.yingyou360.cn "GET /blr2/blr2_6.0.7.apk?__=1518050443.777 HTTP/1.1" 200 529909274 "-" "Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0; NetworkBench/8.0.1.323-6674224-2856942) like Gecko" "1.83.205.162, 60.165.55.10"

#把文件大小那一行放第一列，然后对第一列进行排序（$11是文件大小，单位是字节，除以2个1024=M）,我这里根具访问的文件大小来推算，文件越大消耗的流量也就越大
# sed -n '/8:40/,/8:45/p' access.log |awk '{print $11,$4,$8,$10,$1,$NF}'|sort -nr|uniq -c|more 
</code></pre>
<h2>其他其他的nginx日志各种查询</h2>
<pre class="language-markup"><code>[root@jxq-c2-16-1 logs]# cat log.sh 
#!/bin/bash
#统计某个时间段的ip总量 
cat /alidata/nginx/logs/access.log |sed -n '/11\/May\/2017:08:32:00/,/11\/May\/2017:08:35:00/p'| awk '{print $1}' | sort | uniq -c | sort -rn | head
#查看某个时间段针对.apk是否有回源  
#sed -n '/10\/Nov\/2017:17:44:*/,/10\/Nov\/2017:17:58:*/p' access.log|grep -i  "apk" |head -1
sed -n '/17:35/,/18:00/p' access.log|grep -i  "apk" |awk '{print $1,$4,$7,$8,$10,$11,$16}'|more
#查看某个时间段流量访问最大的日志
sed -n '/8:40/,/8:45/p' access.log |awk '{print $11,$4,$8,$10,$1,$NF}'|sort -nr|uniq -c|more
#统计某每分钟ip并发数
cat access_2017-07-26.log |egrep  -v "GET|ChinaCache"|sed -n '/26\/Jul\/2017:16:01:00/,/26\/Jul\/2017:16:59:00/p'|grep "api.mobile.playyx.com" |awk -F "[:| ]"  '{print $1,$4}'|sort |uniq -c | wc -l
#统计某天的总流量
# cat access_2018-10-30.log |awk '{sum=sum+$11} END{print sum/1024^3}'
16.3368
$11是流量列，单位是字节B，除以三个1024得到单位16.3368GB
#统计某天的使用流量峰值
[root@localhost ~]# cat access_2018-10-30.log |awk '{print $11}'|sort -nr|head
683243757
683243757
568808672
370556180
244179717
31216982
27669127
25782956
24267351
21754202
[root@localhost ~]# cat access_2018-10-30.log |awk '{print $11}'|sort -nr|head &gt; ff.txt
[root@localhost ~]# cat access_2018-10-30.log |awk '{print $11/1024^2}'|sort -nr|head
651.592
651.592
542.458
353.39    #去三峰值是353.39M
232.868
29.7708
26.3873
24.5885
23.1431
20.7464
#统计访问量最多的前十个ip，每一个ip一共使用了多少流量
(1)我先列出访问量最多的前十个ip并写入
[root@localhost ~]# cat access_2018-10-30.log |awk '{print $1}'|sort -nr|uniq -c |sort -nr|head|awk '{print $2}' &gt; ip2.txt
[root@localhost ~]# cat ip2.txt   ip2.txt 
23.91.100.204
101.69.132.78
101.69.132.77
101.69.132.82
101.69.132.76
101.69.132.83
101.69.132.80
101.69.132.84
101.69.132.81
101.69.132.79
(2)然后列出ip列和流量列，并筛选出访问量最大的前十个ip对应的ip列和流量列，最后计算每个ip使用了多少流量单位是GB。
[root@localhost ~]# cat ip2.sh   
#!/bin/bash
for i in `cat ip2.txt`
do
    cat access_2018-10-30.log |awk '{print $1,$11}'|grep "$i"|awk '{sum=sum+$2} END{print $1,sum/1024^3}'
done
[root@localhost ~]# sh ip2.sh
23.91.100.204 1.20969
101.69.132.78 0.0809936
101.69.132.77 0.0759132
</code></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></description>
    <pubDate>Fri, 10 Oct 2025 14:03:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/79.html</guid>
</item>
<item>
    <title>SpringBoot：记录一次在centos上启动jar包慢的问题及解决方法</title>
    <link>https://blog.xuhaobo.cn/private/78.html</link>
    <description><![CDATA[<p>问题</p><p>项目的springboot的jar在本地开发十几秒就可以正常启动完毕，但是发布到linux<a class="rno-markdown__textlink-new" href="https://cloud.tencent.com/product/cvm?from_column=20065&amp;from=20065" target="_blank" rel="noopener" data-mce-href="https://cloud.tencent.com/product/cvm?from_column=20065&amp;from=20065">服务器</a>上几十秒甚至几百秒才会启动完成。刚启动的时候没有日志没有任何打印，连<span class="mod-overview__keyword">日志文件</span>都没有生成。</p><h4 id="dtklp">解决</h4><p>很多文章都是说修改jdk中的java.security这个文文件中的内容：</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"></div><div class="rno-markdown-code-toolbar-opt"></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript" contenteditable="false" data-mce-highlighted="true" data-mce-tabindex="0">securerandom<span class="token punctuation">.</span>source<span class="token operator">=</span>file<span class="token operator">:</span><span class="token operator">/</span>dev<span class="token operator">/</span>random</pre></div></div><p>替换成<span class="is-m-hidden"></span></p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript" contenteditable="false" data-mce-highlighted="true" data-mce-tabindex="0">securerandom<span class="token punctuation">.</span>source<span class="token operator">=</span>file<span class="token operator">:</span><span class="token operator">/</span>dev<span class="token operator">/</span>urandom</pre></div></div><p>但是对于我的项目没有任何改变。那就不是这个问题了。</p><p>执行 hostname命令 发现名称是 host-62-9-48-9 但是在/etc/hosts中只有默认的配置</p><figure class=""><div class="rno-markdown-img-url"><div class="rno-markdown-img-url-inner"><p><a href="https://blog.xuhaobo.cn/content/uploadfile/202509/d2b51759134999.png" target="_blank" rel="noopener" data-mce-href="/content/uploadfile/202509/d2b51759134999.png"><img src="https://blog.xuhaobo.cn/content/uploadfile/202509/d2b51759134999.png" alt="image.png" data-mce-src="/content/uploadfile/202509/d2b51759134999.png"></a></p></div></div></figure><p>在/etc/hosts中 添加</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"></div><div class="rno-markdown-code-toolbar-opt"></div></div><div class="developer-code-block"><div class="rno-markdown-code-opt-float"><div class="rno-markdown-code-icon-btn qa-r-editor-btn"><span class="is-m-hidden"><br data-mce-bogus="1"></span></div></div><pre class="prism-token token line-numbers language-javascript" contenteditable="false" data-mce-highlighted="true" data-mce-tabindex="0"><span class="token number">62.9</span><span class="token number">.48</span><span class="token number">.9</span> host<span class="token operator">-</span><span class="token number">62</span><span class="token operator">-</span><span class="token number">9</span><span class="token operator">-</span><span class="token number">48</span><span class="token operator">-</span><span class="token number">9</span></pre></div></div><p>再试一次重启项目 ，嗖…由原来的80多秒变成10秒启动了。</p><h4 id="bf33g">小结</h4><p>遇到jar包在centos环境启动慢的情况，建议查看 hostname 与 /etc/hosts 文件中的hostname，查看hostname 是否存在，名称是否一致，确保俩边hostname 一致，问题可能就解决了，当然导致jar启动慢的情况也有很多种，还是要具体情况，具体分析了。</p>]]></description>
    <pubDate>Mon, 29 Sep 2025 16:35:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/78.html</guid>
</item>
<item>
    <title>后端开发学习路线图：从基础原理开始</title>
    <link>https://blog.xuhaobo.cn/private/77.html</link>
    <description><![CDATA[<h1>后端开发学习路线图：从基础原理开始</h1>
<p>架构,后端,教程,Web 开发</p>
<h2>目录</h2>
<ul>
<li>整体认知</li>
<li>HTTP 协议</li>
<li>路由</li>
<li>序列化与反序列化</li>
<li>身份验证与授权</li>
<li>验证与转换</li>
<li>中间件</li>
<li>请求内容</li>
<li>处理器与控制器</li>
<li>CRUD 深入解析</li>
<li>REST 最佳实践</li>
<li>数据库</li>
<li>业务逻辑层</li>
<li>缓存</li>
<li>事务性电子邮件</li>
<li>任务队列与调度</li>
<li>Elasticsearch</li>
<li>错误处理</li>
<li>配置管理</li>
<li>日志、监控与可观测性</li>
<li>优雅关闭</li>
<li>安全性</li>
<li>扩展与性能</li>
<li>并发与并行</li>
<li>对象存储与大文件处理</li>
<li>实时系统</li>
<li>测试与代码质量</li>
<li>12 因素应用原则</li>
<li>OpenAPI 标准</li>
<li>后端工程师必备的 DevOps 知识</li>
</ul>
<h2>整体认知</h2>
<p>后端开发是 Web 开发的服务器端部分，主要关注数据库、脚本编写和网站架构。它是用户界面与数据库之间的桥梁，负责处理业务逻辑、数据处理和系统集成。</p>
<h3>核心职责</h3>
<p>后端系统必须处理数据存储与检索、执行业务逻辑、管理用户身份验证、确保安全性、处理并发请求并维护系统可靠性。它作为基础支撑，通过提供 API、管理数据流和协调各类服务，使前端应用能够正常运行。</p>
<h3>架构模式</h3>
<p>现代后端系统通常采用分层架构模式，将关注点分离到表示层、业务逻辑层和数据访问层。这种分离有助于提高可维护性、可测试性和可扩展性。后端作为服务提供者，暴露可供客户端调用的端点，以执行操作和检索数据。</p>
<h3>系统组件</h3>
<p>一个完整的后端系统由以下组件构成：处理 HTTP 请求的 Web 服务器、执行业务逻辑的应用服务器、用于数据持久化的数据库、提升性能的缓存层、用于异步处理的消息队列，以及各类外部服务集成。</p>
<h2>HTTP 协议</h2>
<p>HTTP（超文本传输协议）是万维网数据通信的基础。理解 HTTP 对后端开发至关重要，因为它定义了客户端与服务器之间消息的格式和传输方式。</p>
<h3>请求 - 响应周期</h3>
<p>每一次 HTTP 交互都遵循请求 - 响应模式。客户端向服务器发送请求，服务器处理请求后返回响应。这种无状态协议意味着每个请求都是独立的，且包含服务器完成请求所需的全部信息。</p>
<h3>HTTP 方法</h3>
<p>HTTP 定义了多种表示期望操作的方法：</p>
<ul>
<li>
<p>GET：检索数据</p>
</li>
<li>
<p>POST：提交数据以创建资源</p>
</li>
<li>
<p>PUT：更新整个资源</p>
</li>
<li>
<p>PATCH：部分更新资源</p>
</li>
<li>
<p>DELETE：删除资源</p>
</li>
<li>
<p>HEAD：仅检索响应头</p>
</li>
<li>
<p>OPTIONS：返回资源支持的方法</p>
</li>
</ul>
<h3>状态码</h3>
<p>HTTP 状态码用于告知请求的处理结果，主要分为以下几类：</p>
<ul>
<li>
<p>1xx：信息性响应</p>
</li>
<li>
<p>2xx：成功响应</p>
</li>
<li>
<p>3xx：重定向响应</p>
</li>
<li>
<p>4xx：客户端错误</p>
</li>
<li>
<p>5xx：服务器错误</p>
<p>理解这些状态码对于正确处理错误和与客户端通信至关重要。</p>
</li>
</ul>
<h3>头信息与消息体</h3>
<p>HTTP 头信息提供请求和响应的元数据，包括内容类型、身份验证信息、缓存指令和自定义应用数据。消息体包含实际传输的数据，其格式由头信息中指定的内容类型决定。</p>
<h3>连接管理</h3>
<p>现代 HTTP 实现支持持久连接，允许在单个连接上传输多个请求。HTTP/2 引入了多路复用技术，可在无队头阻塞的情况下处理并发请求。理解连接管理对性能优化至关重要。</p>
<h2>路由</h2>
<p>路由是确定应用如何响应客户端对特定端点（由 URL 路径和 HTTP 方法定义）请求的机制，相当于后端应用的流量控制系统。</p>
<h3>路由定义</h3>
<p>路由将 URL 模式映射到处理器函数，可包含静态路径、动态参数、查询字符串和通配符。设计良好的路由应直观、一致且符合 REST 规范，使 API 对使用者具有可预测性。</p>
<h3>路由匹配</h3>
<p>路由系统使用模式匹配算法，将传入的请求与已定义的路由进行匹配。当多个模式可能匹配一个请求时，优先级和特异性规则会决定由哪个路由处理请求。理解路由优先级可避免冲突，确保行为可预测。</p>
<h3>路由参数</h3>
<p>动态路由支持在 URL 路径中嵌入参数，实现灵活的端点定义。参数可分为必填或可选，且可设置类型约束和验证规则。正确处理参数有助于实现对特定资源的 CRUD 操作。</p>
<h3>路由组与命名空间</h3>
<p>将路由组织成逻辑组有助于优化代码结构和中间件应用。路由组可共享通用前缀、中间件或配置，减少代码重复并提高可维护性。</p>
<h3>高级路由特性</h3>
<p>现代路由系统支持多种高级特性，如路由模型绑定、用于性能优化的路由缓存、子域名路由以及路由专用中间件。这些特性支持复杂的 URL 方案和高效的请求处理。</p>
<h2>序列化与反序列化</h2>
<p>序列化是将对象或数据结构转换为适合存储或传输的格式的过程，反序列化则是其逆过程。这一过程是系统间数据交换和存储机制的基础。</p>
<h3>数据格式</h3>
<p>常见的序列化格式包括：</p>
<ul>
<li>
<p>JSON：因简洁性和广泛支持，常用于 Web API</p>
</li>
<li>
<p>XML：适用于结构化文档和遗留系统</p>
</li>
<li>
<p>Protocol Buffers：用于高性能二进制序列化</p>
</li>
<li>
<p>MessagePack：高效的类 JSON 二进制格式</p>
</li>
<li>
<p>YAML：用于人类可读的配置文件</p>
</li>
</ul>
<h3>序列化过程</h3>
<p>在序列化过程中，复杂数据结构会被扁平化为可传输或存储的线性格式。这一过程需要处理嵌套对象、数组、基本类型以及空值（null）、未定义值（undefined）或无穷大（infinity）等特殊值，且必须保证数据完整性和类型信息不丢失。</p>
<h3>反序列化挑战</h3>
<p>反序列化需从序列化数据中重构对象，面临多种挑战，如类型转换、处理缺失字段、验证数据完整性以及管理版本兼容性。健壮的反序列化过程应包含错误处理和数据验证机制。</p>
<h3>性能考量</h3>
<p>序列化性能会影响应用的吞吐量和响应时间。二进制格式通常比文本格式更快、更紧凑，但文本格式在调试和互操作性方面更具优势。应根据性能需求和生态系统兼容性选择合适的格式。</p>
<h3>安全隐患</h3>
<p>序列化可能通过反序列化攻击引入安全漏洞，即恶意数据利用反序列化过程进行攻击。因此，务必验证和清洗传入数据，避免将不可信数据反序列化为可执行对象，并使用安全的序列化库。</p>
<h2>身份验证与授权</h2>
<p>身份验证用于验证用户身份，授权则确定已验证用户可访问的资源。这些安全机制是保护资源和维护系统完整性的基础。</p>
<h3>身份验证方法</h3>
<ul>
<li>
<p>基于密码的身份验证：常见但易受多种攻击</p>
</li>
<li>
<p>多因素身份验证：通过 &ldquo;你知道的信息&rdquo;&ldquo;你拥有的物品&rdquo; 或 &ldquo;你的生物特征&rdquo; 增加安全层级</p>
</li>
<li>
<p>基于令牌的身份验证：使用 JWT（JSON Web Token）等令牌实现无状态验证</p>
</li>
<li>
<p>生物特征和基于证书的身份验证：为高价值系统提供更强安全性</p>
</li>
</ul>
<h3>会话管理</h3>
<ul>
<li>
<p>传统会话管理：在服务器上存储用户状态，需会话存储和清理机制</p>
</li>
<li>
<p>基于令牌的无状态身份验证：无需服务器端会话存储，但需妥善管理令牌（包括刷新令牌策略和安全存储）</p>
</li>
</ul>
<h3>授权模型</h3>
<ul>
<li>
<p>基于角色的访问控制（RBAC）：将权限分配给角色，再将角色分配给用户</p>
</li>
<li>
<p>基于属性的访问控制（ABAC）：根据用户、资源和环境属性做出授权决策</p>
</li>
<li>
<p>访问控制列表（ACL）：为单个资源指定权限</p>
<p>应根据复杂度和灵活性需求选择合适的授权模型。</p>
</li>
</ul>
<h3>OAuth 与 OpenID Connect</h3>
<ul>
<li>
<p>OAuth：提供授权委托功能，允许应用代表用户访问资源而无需暴露凭证</p>
</li>
<li>
<p>OpenID Connect：在 OAuth 基础上增加身份验证功能，提供身份验证服务</p>
<p>理解这些标准对现代应用集成至关重要。</p>
</li>
</ul>
<h3>安全最佳实践</h3>
<ul>
<li>
<p>实施安全的密码策略</p>
</li>
<li>
<p>所有身份验证流量使用 HTTPS</p>
</li>
<li>
<p>使用强哈希算法存储密码</p>
</li>
<li>
<p>实施速率限制以防止暴力攻击</p>
</li>
<li>
<p>定期审计访问模式</p>
</li>
<li>
<p>绝不要以明文形式存储敏感凭证</p>
</li>
</ul>
<h2>验证与转换</h2>
<p>数据验证确保传入数据符合应用需求，转换则将数据转换为适合处理或存储的格式。这些过程是维护数据质量和系统可靠性的关键。</p>
<h3>输入验证</h3>
<p>需验证所有传入数据的类型、格式、长度和业务规则。客户端验证可提升用户体验，但绝不能依赖它保障安全；服务器端验证是必需的，且应全面检查 SQL 注入、XSS（跨站脚本）攻击和数据一致性问题。</p>
<h3>验证策略</h3>
<ul>
<li>
<p>基于模式的验证：使用预定义模式验证数据结构和类型</p>
</li>
<li>
<p>基于规则的验证：对数据值应用业务逻辑</p>
</li>
<li>
<p>基于上下文的验证：考虑当前系统状态和用户权限</p>
<p>应在多个层级实施验证，以实现健壮的保护。</p>
</li>
</ul>
<h3>数据转换</h3>
<p>转换传入数据以匹配内部格式，包括标准化值、处理不同日期格式、单位转换和字符串清洗。无论输入源如何变化，转换都能确保数据处理和存储的一致性。</p>
<h3>错误处理</h3>
<p>验证失败时，应提供清晰、可操作的错误信息，且不暴露系统内部细节。在响应前收集所有验证错误，以提升用户体验。同时，记录验证失败情况，用于安全监控和系统改进。</p>
<h3>性能优化</h3>
<p>验证可能影响性能（尤其是处理大型数据集时）。可通过以下方式优化：</p>
<ul>
<li>
<p>实施早期验证，快速失败</p>
</li>
<li>
<p>使用高效的验证库</p>
</li>
<li>
<p>缓存验证模式</p>
</li>
<li>
<p>对非关键检查考虑异步验证</p>
</li>
</ul>
<h2>中间件</h2>
<p>中间件是在请求 - 响应周期中执行的函数，可访问请求和响应对象。它们是实现横切关注点和模块化请求处理的强大机制。</p>
<h3>中间件概念</h3>
<p>中间件函数可在将控制权传递给下一个中间件或路由处理器之前执行操作，能修改请求或响应对象、终止请求 - 响应周期，或调用栈中的下一个中间件。这种责任链模式支持灵活的请求处理。</p>
<h3>常见中间件类型</h3>
<ul>
<li>
<p>身份验证中间件：验证用户凭证</p>
</li>
<li>
<p>日志中间件：记录请求详情</p>
</li>
<li>
<p>压缩中间件：减小响应大小</p>
</li>
<li>
<p>CORS（跨域资源共享）中间件：处理跨域请求</p>
</li>
<li>
<p>速率限制中间件：防止滥用</p>
<p>每种中间件针对特定的横切关注点。</p>
</li>
</ul>
<h3>中间件顺序</h3>
<p>中间件的执行顺序至关重要：</p>
<ul>
<li>
<p>身份验证通常在授权之前</p>
</li>
<li>
<p>日志通常在早期执行，以捕获所有请求</p>
</li>
<li>
<p>错误处理中间件通常在最后，以捕获其他中间件的错误</p>
</li>
<li>
<p>压缩应在内容生成后执行</p>
</li>
</ul>
<h3>自定义中间件开发</h3>
<p>自定义中间件应遵循单一职责原则，妥善处理错误，并正确调用下一个函数。需考虑性能影响（因中间件会对每个请求执行），设计可复用、可配置的中间件。</p>
<h3>全局中间件与路由专用中间件</h3>
<ul>
<li>
<p>全局中间件：应用于所有路由</p>
</li>
<li>
<p>路由专用中间件：仅影响特定端点</p>
<p>全局中间件适用于日志、安全等通用关注点，路由专用中间件适用于特定身份验证需求等专项功能。</p>
</li>
</ul>
<h2>请求内容</h2>
<p>理解并正确处理不同类型的请求内容，是构建能接受多种数据格式和文件上传的健壮 API 的关键。</p>
<h3>内容类型</h3>
<p>应用通常处理以下内容类型：</p>
<ul>
<li>
<p>JSON：用于结构化数据交换</p>
</li>
<li>
<p>表单数据：用于传统 Web 表单</p>
</li>
<li>
<p>多部分数据（multipart）：用于文件上传</p>
</li>
<li>
<p>XML：用于遗留系统集成</p>
</li>
<li>
<p>纯文本：用于简单数据传输</p>
<p>每种内容类型需特定的解析和验证方法。</p>
</li>
</ul>
<h3>请求体解析</h3>
<p>需根据内容类型解析请求体，并设置大小限制以防止内存耗尽。妥善处理解析错误，验证内容结构，清洗数据以防止注入攻击。对于大型负载，可考虑流式处理以管理内存使用。</p>
<h3>文件上传处理</h3>
<p>文件上传需在安全、存储和性能方面特殊考虑：</p>
<ul>
<li>
<p>验证文件类型和大小</p>
</li>
<li>
<p>扫描恶意软件</p>
</li>
<li>
<p>生成唯一文件名以避免冲突</p>
</li>
<li>
<p>安全存储文件</p>
<p>可考虑使用云存储服务实现可扩展性。</p>
</li>
</ul>
<h3>内容协商</h3>
<p>根据客户端在 Accept 头中指定的偏好，支持多种响应格式。实施内容协商以返回 JSON、XML 或其他请求格式；当客户端偏好不明确时，默认使用通用格式。</p>
<h3>压缩与编码</h3>
<p>支持压缩请求体以减少带宽使用（尤其对大型负载）。正确处理不同字符编码（文本内容默认使用 UTF-8），并根据需要实施相应的解压缩和编码转换。</p>
<h2>处理器与控制器</h2>
<p>处理器和控制器是处理传入请求并生成响应的组件，包含将输入根据业务需求转换为输出的应用逻辑。</p>
<h3>处理器职责</h3>
<p>处理器接收解析后的请求、提取必要数据、验证输入、调用业务逻辑服务、格式化响应并处理错误。它们作为 HTTP 协议与应用逻辑之间的接口，实现外部契约与内部表示之间的转换。</p>
<h3>控制器组织</h3>
<p>控制器将处理相似资源或功能的处理器分组，应遵循单一职责原则（处理一种资源类型或一组相关操作）。按领域概念而非技术层级组织控制器，可提高可维护性。</p>
<h3>请求处理流程</h3>
<p>典型的处理器流程如下：</p>
<ol>
<li>
<p>提取参数和体数据</p>
</li>
<li>
<p>验证输入</p>
</li>
<li>
<p>将处理后的数据传入业务服务</p>
</li>
<li>
<p>处理服务响应和错误</p>
</li>
<li>
<p>根据内容协商格式化输出</p>
</li>
<li>
<p>设置适当的 HTTP 状态码和头信息</p>
</li>
</ol>
<h3>处理器中的错误处理</h3>
<p>处理器必须妥善处理各类错误情况，包括验证失败、服务错误、数据库连接问题和意外异常。实施一致的错误响应格式，并进行适当日志记录以方便调试和监控。</p>
<h3>处理器测试</h3>
<p>测试处理器时，需：</p>
<ul>
<li>
<p>模拟依赖项</p>
</li>
<li>
<p>验证参数提取的正确性</p>
</li>
<li>
<p>验证错误处理路径</p>
</li>
<li>
<p>检查响应格式和状态码</p>
</li>
<li>
<p>确保与中间件的正确集成</p>
<p>测试应聚焦于处理器的职责，而非底层服务。</p>
</li>
</ul>
<h2>CRUD 深入解析</h2>
<p>CRUD（创建、读取、更新、删除）操作是大多数应用中数据操作的基础。理解 CRUD 原则和最佳实践，是构建可靠数据管理系统的关键。</p>
<h3>创建操作（Create）</h3>
<p>创建操作向系统中添加新资源，需：</p>
<ul>
<li>
<p>验证所有输入数据</p>
</li>
<li>
<p>必要时检查重复资源</p>
</li>
<li>
<p>执行业务规则和约束</p>
</li>
<li>
<p>处理并发创建尝试</p>
</li>
<li>
<p>返回包含资源标识符的成功 / 失败响应</p>
</li>
</ul>
<h3>读取操作（Read）</h3>
<p>读取操作检索现有资源而不修改它们，需：</p>
<ul>
<li>
<p>支持大型数据集的过滤、排序和分页</p>
</li>
<li>
<p>实施高效查询策略</p>
</li>
<li>
<p>对敏感数据进行授权处理</p>
</li>
<li>
<p>无论数据量大小，提供一致的响应格式</p>
</li>
</ul>
<h3>更新操作（Update）</h3>
<p>更新操作修改现有资源，需：</p>
<ul>
<li>
<p>区分全量更新（PUT）和部分更新（PATCH）</p>
</li>
<li>
<p>处理并发修改冲突</p>
</li>
<li>
<p>验证更新是否保持数据一致性</p>
</li>
<li>
<p>实施原子操作以防止部分失败</p>
</li>
</ul>
<h3>删除操作（Delete）</h3>
<p>删除操作从系统中移除资源，需：</p>
<ul>
<li>
<p>删除前验证资源是否存在</p>
</li>
<li>
<p>谨慎处理级联删除</p>
</li>
<li>
<p>考虑用于审计跟踪的软删除策略</p>
</li>
<li>
<p>返回表示删除成功或资源不存在的适当状态码</p>
</li>
</ul>
<h3>CRUD 最佳实践</h3>
<ul>
<li>
<p>在所有层级实施适当验证</p>
</li>
<li>
<p>使用数据库事务确保一致性</p>
</li>
<li>
<p>提供有意义的错误信息</p>
</li>
<li>
<p>记录所有操作以用于审计</p>
</li>
<li>
<p>设计通过 HTTP 方法和 URL 清晰传达预期操作的 API</p>
</li>
</ul>
<h2>REST 最佳实践</h2>
<p>REST（表述性状态转移）是设计网络应用的架构风格。遵循 REST 原则可创建可预测、可扩展且可维护的 API。</p>
<h3>基于资源的 URL</h3>
<p>围绕资源而非操作设计 URL，资源使用名词，操作通过 HTTP 方法表示。例如，使用<code>GET /users/123</code>而非<code>GET /getUser/123</code>，这样可创建直观且一致的 API 接口。</p>
<h3>HTTP 方法使用</h3>
<ul>
<li>
<p>GET：无副作用地检索数据</p>
</li>
<li>
<p>POST：创建新资源</p>
</li>
<li>
<p>PUT：完全替换资源</p>
</li>
<li>
<p>PATCH：部分更新资源</p>
</li>
<li>
<p>DELETE：删除资源</p>
<p>选择能准确反映预期操作语义的方法。</p>
</li>
</ul>
<h3>状态码一致性</h3>
<p>始终返回适当的 HTTP 状态码：</p>
<ul>
<li>
<p>GET/PUT/PATCH 成功：200</p>
</li>
<li>
<p>POST 创建资源成功：201</p>
</li>
<li>
<p>DELETE 成功：204</p>
</li>
<li>
<p>客户端错误：400</p>
</li>
<li>
<p>身份验证失败：401</p>
</li>
<li>
<p>授权失败：403</p>
</li>
<li>
<p>服务器错误：500</p>
</li>
</ul>
<h3>响应格式标准</h3>
<p>所有端点保持一致的响应格式：</p>
<ul>
<li>
<p>包含分页信息等元数据</p>
</li>
<li>
<p>使用标准字段名</p>
</li>
<li>
<p>以一致结构提供错误详情</p>
</li>
<li>
<p>必要时通过内容协商支持多种响应格式</p>
</li>
</ul>
<h3>版本控制策略</h3>
<p>实施 API 版本控制以管理随时间的变更，常见策略包括：</p>
<ul>
<li>
<p>URL 版本控制：<code>/v1/users</code></p>
</li>
<li>
<p>头信息版本控制：<code>Accept: application/vnd.api+json;version=1</code></p>
</li>
<li>
<p>查询参数版本控制：<code>?version=1</code></p>
<p>选择一种策略并始终如一地应用。</p>
</li>
</ul>
<h3>超媒体与可发现性</h3>
<ul>
<li>
<p>在响应中包含指向相关资源的链接</p>
</li>
<li>
<p>提供 API 内的导航路径</p>
</li>
<li>
<p>文档化资源的可用操作</p>
</li>
<li>
<p>尽可能使 API 具备自描述性</p>
<p>这可提高 API 可用性，减少客户端与服务器之间的耦合。</p>
</li>
</ul>
<h2>数据库</h2>
<p>数据库是后端系统中数据持久化的基础。理解数据库概念、类型和最佳实践，对构建可靠且高性能的应用至关重要。</p>
<h3>数据库类型</h3>
<ul>
<li>
<p>关系型数据库：使用结构化模式和 SQL，适用于复杂查询和事务</p>
</li>
<li>
<p>NoSQL 数据库：提供灵活模式和水平扩展，包括文档存储、键值存储、列族数据库和图数据库</p>
<p>需根据数据结构和可扩展性需求选择。</p>
</li>
</ul>
<h3>数据库设计原则</h3>
<ul>
<li>
<p>设计数据库时，通过规范化减少冗余，但可考虑反规范化以提升性能</p>
</li>
<li>
<p>定义实体间的清晰关系</p>
</li>
<li>
<p>为查询性能建立适当索引</p>
</li>
<li>
<p>设计支持应用需求和未来增长的模式</p>
</li>
</ul>
<h3>查询优化</h3>
<ul>
<li>
<p>通过理解执行计划编写高效查询</p>
</li>
<li>
<p>使用适当索引</p>
</li>
<li>
<p>避免 N+1 查询问题</p>
</li>
<li>
<p>考虑查询复杂度</p>
</li>
<li>
<p>监控查询性能并优化瓶颈</p>
</li>
<li>
<p>使用数据库分析工具识别慢查询</p>
</li>
</ul>
<h3>事务管理</h3>
<p>要理解可靠数据操作所需的 ACID 属性（原子性 Atomicity、一致性 Consistency、隔离性 Isolation、持久性 Durability）。采用合适的事务隔离级别，妥善处理死锁问题，并尽量缩短事务时长，以减少锁定竞争。</p>
<h3>连接管理</h3>
<p>实现连接池以高效管理数据库连接。根据应用负载和数据库容量配置合适的池大小。妥善处理连接失败问题，并为临时性故障实现重试机制。</p>
<h3>数据迁移与版本控制</h3>
<p>通过迁移脚本管理数据库模式变更。为所有模式变更添加版本标识，对迁移过程进行全面测试，并制定回滚策略。使用迁移工具实现自动化操作，跟踪不同环境下的模式演进过程。</p>
<h2>业务逻辑层</h2>
<p>业务逻辑层包含定义数据创建、存储和修改方式的核心规则与流程。该层集中体现了业务领域的特定需求和规则。</p>
<h3>领域建模</h3>
<p>将业务概念建模为实体，明确其职责与关联关系。采用领域驱动设计原则构建能反映业务认知的模型，将领域逻辑与基础设施相关关注点分离。</p>
<h3>服务组织</h3>
<p>将业务逻辑组织成服务，每个服务封装一组相关操作。服务应具备清晰的接口，遵循单一职责原则，且可独立进行测试。围绕业务能力而非技术层级设计服务。</p>
<h3>业务规则实现</h3>
<p>在整个应用中统一实现业务规则。将规则逻辑集中管理以避免重复，在合适场景下将规则设计为可配置形式，并对复杂业务逻辑进行详细文档记录。对于复杂场景，可考虑使用规则引擎。</p>
<h3>数据验证与不变性</h3>
<p>通过验证和约束机制确保业务不变性。在适当的边界处对数据进行验证，维护相关实体间的一致性，并对验证失败情况进行妥善处理，返回具有明确意义的错误信息。</p>
<h3>事务边界</h3>
<p>围绕业务操作而非技术操作定义事务边界。确保事务能维护业务一致性，处理分布式事务的补偿机制，并在合适场景下考虑最终一致性模式。</p>
<h2>缓存</h2>
<p>缓存通过将频繁访问的数据存储在高速存储介质中，可提升应用性能。实施有效的缓存策略能显著缩短响应时间，降低数据库负载。</p>
<h3>缓存类型</h3>
<ul>
<li>
<p>内存缓存：将数据存储在内存（RAM）中，实现最快访问速度</p>
</li>
<li>
<p>分布式缓存：在多台服务器间共享数据</p>
</li>
<li>
<p>浏览器缓存：在客户端设备上存储资源</p>
</li>
<li>
<p>CDN 缓存：通过地理分布式节点分发内容</p>
</li>
<li>
<p>数据库查询缓存：存储数据库查询结果</p>
</li>
</ul>
<h3>缓存策略</h3>
<ul>
<li>
<p>缓存回退（Cache-aside）：当缓存未命中时，从数据源加载数据并写入缓存</p>
</li>
<li>
<p>写透（Write-through）：数据更新时同时写入缓存和数据源</p>
</li>
<li>
<p>写回（Write-behind）：延迟将数据写入数据源，先更新缓存</p>
</li>
<li>
<p>预刷新（Refresh-ahead）：在缓存过期前主动更新缓存数据</p>
<p>需根据数据访问模式和一致性要求选择合适的策略。</p>
</li>
</ul>
<h3>缓存失效</h3>
<p>实施缓存失效机制以维护数据一致性：</p>
<ul>
<li>
<p>使用 TTL（生存时间）实现缓存自动过期</p>
</li>
<li>
<p>基于事件触发即时更新缓存（事件驱动失效）</p>
</li>
<li>
<p>对分组数据采用基于标签的失效策略</p>
<p>缓存失效是计算机科学领域中最具挑战性的问题之一。</p>
</li>
</ul>
<h3>缓存性能</h3>
<ul>
<li>
<p>监控缓存命中率以衡量缓存有效性</p>
</li>
<li>
<p>根据内存限制和访问模式调整缓存大小</p>
</li>
<li>
<p>实施合适的缓存淘汰策略</p>
</li>
<li>
<p>对关键数据考虑采用缓存预热策略</p>
</li>
</ul>
<h3>分布式缓存</h3>
<ul>
<li>
<p>使用分布式缓存实现多服务器间的可扩展性</p>
</li>
<li>
<p>妥善处理缓存节点故障</p>
</li>
<li>
<p>采用一致性哈希算法实现数据分布</p>
</li>
<li>
<p>考虑数据分片策略</p>
</li>
<li>
<p>监控缓存与应用服务器间的网络延迟</p>
</li>
</ul>
<h2>事务性电子邮件</h2>
<p>事务性电子邮件是由用户操作或系统事件触发的自动化消息，对用户沟通、通知传递和业务流程至关重要。</p>
<h3>邮件类型</h3>
<ul>
<li>
<p>欢迎邮件：向新用户发送问候</p>
</li>
<li>
<p>确认邮件：验证用户操作（如注册、订单提交）</p>
</li>
<li>
<p>通知邮件：告知系统事件（如账户变动、订单状态更新）</p>
</li>
<li>
<p>密码重置邮件：支持账户找回</p>
</li>
<li>
<p>收据邮件：确认交易完成</p>
<p>每种类型都满足特定的沟通需求。</p>
</li>
</ul>
<h3>邮件服务集成</h3>
<ul>
<li>
<p>使用可靠的邮件服务提供商处理邮件发送、身份验证和发件人信誉管理</p>
</li>
<li>
<p>配置 SPF、DKIM 和 DMARC 记录以实现邮件身份验证</p>
</li>
<li>
<p>监控邮件送达率，妥善处理退信问题</p>
</li>
</ul>
<h3>模板管理</h3>
<ul>
<li>
<p>创建可复用的邮件模板，包含动态内容占位符</p>
</li>
<li>
<p>支持多语言和多种格式（HTML / 纯文本）</p>
</li>
<li>
<p>保持品牌形象一致性</p>
</li>
<li>
<p>在不同邮件客户端中测试模板显示效果</p>
</li>
<li>
<p>对模板进行版本控制，确保更新一致性</p>
</li>
</ul>
<h3>队列管理</h3>
<ul>
<li>
<p>将邮件加入队列以确保可靠发送</p>
</li>
<li>
<p>为发送失败的邮件实现重试机制</p>
</li>
<li>
<p>对紧急邮件设置优先级</p>
</li>
<li>
<p>处理高并发邮件发送场景</p>
</li>
<li>
<p>监控队列深度和处理时长，确保邮件及时送达</p>
</li>
</ul>
<h3>合规性与隐私</h3>
<ul>
<li>
<p>遵循 CAN-SPAM、GDPR 等邮件相关法规</p>
</li>
<li>
<p>提供退订机制</p>
</li>
<li>
<p>尊重用户偏好设置</p>
</li>
<li>
<p>妥善维护订阅者列表</p>
</li>
<li>
<p>包含法定要求的必要信息</p>
</li>
<li>
<p>及时处理退订请求</p>
</li>
</ul>
<h2>任务队列与调度</h2>
<p>任务队列支持对工作项的异步处理，调度则允许在特定时间执行任务。这些模式对构建响应迅速的应用和处理后台操作至关重要。</p>
<h3>队列概念</h3>
<p>队列实现了生产者与消费者的解耦，支持异步处理：</p>
<ul>
<li>
<p>消息包含任务相关信息，由工作节点（Worker）处理</p>
</li>
<li>
<p>根据实现方式，队列可提供数据持久性、顺序保证和消息投递语义</p>
</li>
</ul>
<h3>队列类型</h3>
<ul>
<li>
<p>先进先出（FIFO）队列：按消息接收顺序处理</p>
</li>
<li>
<p>优先级队列：优先处理高优先级消息</p>
</li>
<li>
<p>延迟队列：将消息延迟至指定时间后处理</p>
</li>
<li>
<p>主题型队列：根据内容或路由键路由消息</p>
</li>
</ul>
<h3>工作节点管理</h3>
<ul>
<li>
<p>工作节点从队列中获取消息并执行关联任务</p>
</li>
<li>
<p>实施完善的错误处理机制</p>
</li>
<li>
<p>为临时性故障实现重试机制</p>
</li>
<li>
<p>为无法处理的消息设置死信队列</p>
</li>
<li>
<p>根据队列深度和处理需求调整工作节点数量（弹性伸缩）</p>
</li>
</ul>
<h3>调度模式</h3>
<ul>
<li>
<p>类 Cron 调度：按固定时间间隔执行任务（如每天凌晨 3 点）</p>
</li>
<li>
<p>一次性调度：在特定时间点执行单次任务</p>
</li>
<li>
<p>周期性调度：按特定模式重复执行任务（如每周一、三、五）</p>
<p>需考虑时区处理和夏令时变更问题。</p>
</li>
</ul>
<h3>可靠性与监控</h3>
<ul>
<li>
<p>通过持久化存储确保消息不丢失</p>
</li>
<li>
<p>实施消息确认机制以保证可靠处理</p>
</li>
<li>
<p>监控队列深度和任务处理时长</p>
</li>
<li>
<p>对故障或性能问题设置告警</p>
</li>
<li>
<p>制定灾难恢复方案</p>
</li>
</ul>
<h2>Elasticsearch</h2>
<p>Elasticsearch 是基于 Apache Lucene 构建的分布式搜索与分析引擎，提供全文检索、实时分析和可扩展数据存储能力。</p>
<h3>搜索基础</h3>
<ul>
<li>
<p>Elasticsearch 使用倒排索引实现快速文本搜索</p>
</li>
<li>
<p>支持结构化和非结构化数据</p>
</li>
<li>
<p>为搜索结果提供相关性评分</p>
</li>
<li>
<p>支持多种查询类型，包括精确词查询（term）、匹配查询（match）、范围查询（range）和布尔查询（bool）</p>
</li>
</ul>
<h3>索引管理</h3>
<ul>
<li>
<p>根据数据访问模式设计索引结构</p>
</li>
<li>
<p>为不同数据类型配置合适的字段映射（Mapping）</p>
</li>
<li>
<p>使用索引模板确保配置一致性</p>
</li>
<li>
<p>对时间序列数据考虑实施索引生命周期管理</p>
</li>
</ul>
<h3>查询优化</h3>
<ul>
<li>
<p>理解查询执行过程和性能特征</p>
</li>
<li>
<p>精确匹配场景使用过滤器（Filter），相关性评分场景使用查询（Query）</p>
</li>
<li>
<p>实施合适的缓存策略</p>
</li>
<li>
<p>监控慢查询，寻找优化机会</p>
</li>
</ul>
<h3>聚合与分析</h3>
<ul>
<li>
<p>使用聚合功能进行数据分析、指标计算和报表生成</p>
</li>
<li>
<p>桶聚合（Bucket Aggregation）：对数据进行分组</p>
</li>
<li>
<p>指标聚合（Metric Aggregation）：计算统计指标（如平均值、求和）</p>
</li>
<li>
<p>管道聚合（Pipeline Aggregation）：对聚合结果进行二次处理</p>
</li>
</ul>
<h3>集群管理</h3>
<ul>
<li>
<p>配置集群以实现高可用性和高性能</p>
</li>
<li>
<p>实施合理的分片（Shard）和副本（Replica）策略</p>
</li>
<li>
<p>监控集群健康状态和资源使用情况</p>
</li>
<li>
<p>规划集群容量和扩展需求</p>
</li>
</ul>
<h2>错误处理</h2>
<p>完善的错误处理能确保应用妥善应对故障，为用户和系统提供有意义的反馈，对系统可靠性和可维护性至关重要。</p>
<h3>错误分类</h3>
<p>区分不同类型的错误并采用差异化处理策略：</p>
<ul>
<li>
<p>用户错误：如无效输入、身份验证失败</p>
</li>
<li>
<p>系统错误：如数据库连接异常、服务不可用</p>
</li>
<li>
<p>编程错误：如代码漏洞、逻辑错误</p>
</li>
</ul>
<h3>错误响应标准</h3>
<ul>
<li>
<p>提供一致的错误响应格式，包含错误码、人类可读的错误信息，必要时附加上下文信息</p>
</li>
<li>
<p>包含关联 ID（Correlation ID），便于跨系统追踪错误</p>
</li>
<li>
<p>避免暴露敏感系统信息（如数据库结构、代码路径）</p>
</li>
</ul>
<h3>异常处理策略</h3>
<ul>
<li>
<p>在可能出错的操作周围实现 try-catch 代码块</p>
</li>
<li>
<p>为不同错误场景使用特定的异常类型</p>
</li>
<li>
<p>在适当层级处理异常，避免过度捕获</p>
</li>
<li>
<p>允许严重错误向上传递，以便上层处理</p>
</li>
</ul>
<h3>重试与熔断器模式</h3>
<ul>
<li>
<p>为临时性故障实现带指数退避（Exponential Backoff）的重试机制</p>
</li>
<li>
<p>使用熔断器（Circuit Breaker）防止故障级联传播</p>
</li>
<li>
<p>在可能的情况下提供降级方案（Fallback）</p>
</li>
<li>
<p>监控故障发生率，动态调整策略</p>
</li>
</ul>
<h3>错误日志与监控</h3>
<ul>
<li>
<p>记录错误时包含足够的调试上下文</p>
</li>
<li>
<p>使用结构化日志便于分析</p>
</li>
<li>
<p>为严重问题设置错误告警</p>
</li>
<li>
<p>跟踪错误模式，寻找系统改进机会</p>
</li>
</ul>
<h2>配置管理</h2>
<p>配置管理将应用设置与代码分离，无需修改代码即可实现部署灵活性和环境特定定制。</p>
<h3>配置来源</h3>
<p>支持多种配置来源，包括：</p>
<ul>
<li>
<p>环境变量</p>
</li>
<li>
<p>配置文件（如 JSON、YAML）</p>
</li>
<li>
<p>命令行参数</p>
</li>
<li>
<p>外部配置服务（如配置中心）</p>
<p>需定义配置优先级规则，处理配置冲突。</p>
</li>
</ul>
<h3>环境特定配置</h3>
<ul>
<li>
<p>为开发、测试（Staging）和生产环境维护独立配置</p>
</li>
<li>
<p>避免在代码中硬编码环境特定值</p>
</li>
<li>
<p>使用配置模板或生成工具确保一致性</p>
</li>
</ul>
<h3>密钥管理</h3>
<ul>
<li>
<p>使用专用密钥管理系统安全存储敏感配置（如数据库密码、API 密钥）</p>
</li>
<li>
<p>绝不要将密钥提交到版本控制系统</p>
</li>
<li>
<p>定期轮换密钥</p>
</li>
<li>
<p>对敏感配置数据进行加密</p>
</li>
</ul>
<h3>配置验证</h3>
<ul>
<li>
<p>在应用启动时验证配置有效性</p>
</li>
<li>
<p>对无效配置提供清晰的错误提示</p>
</li>
<li>
<p>对配置值实施类型检查</p>
</li>
<li>
<p>对复杂配置考虑采用基于 schema 的验证</p>
</li>
</ul>
<h3>动态配置</h3>
<ul>
<li>
<p>在可能的情况下支持无需重启应用即可更新配置</p>
</li>
<li>
<p>实现配置监听机制，自动应用配置变更</p>
</li>
<li>
<p>妥善处理可能影响运行中操作的配置变更</p>
</li>
</ul>
<h2>日志、监控与可观测性</h2>
<p>可观测性通过日志（Logging）、指标（Metrics）和追踪（Tracing）实现对系统行为和性能的理解，对维护可靠的生产系统至关重要。</p>
<h3>日志最佳实践</h3>
<ul>
<li>
<p>使用结构化日志并保持格式一致</p>
</li>
<li>
<p>在日志消息中包含相关上下文信息</p>
</li>
<li>
<p>实施合适的日志级别（DEBUG、INFO、WARN、ERROR）</p>
</li>
<li>
<p>避免记录敏感信息（如密码、个人身份数据）</p>
</li>
<li>
<p>使用关联 ID（Correlation ID）追踪跨服务请求</p>
</li>
</ul>
<h3>指标与监控</h3>
<ul>
<li>
<p>收集应用指标，包括请求率、响应时间、错误率和业务指标</p>
</li>
<li>
<p>监控基础设施指标，如 CPU 使用率、内存占用、磁盘空间和网络流量</p>
</li>
<li>
<p>为关键阈值和异常情况设置告警</p>
</li>
<li>
<p>建立性能基准，跟踪性能变化趋势</p>
</li>
</ul>
<h3>分布式追踪</h3>
<ul>
<li>
<p>实施分布式追踪以跟踪跨多服务的请求流转</p>
</li>
<li>
<p>识别性能瓶颈</p>
</li>
<li>
<p>理解服务间依赖关系</p>
</li>
<li>
<p>使用追踪采样（Sampling）平衡性能开销与可见性</p>
</li>
</ul>
<h3>健康检查</h3>
<ul>
<li>
<p>实现健康检查端点，用于监控系统状态</p>
</li>
<li>
<p>包含对数据库、外部服务等依赖的检查</p>
</li>
<li>
<p>提供详细的健康信息，便于问题排查</p>
</li>
<li>
<p>结合负载均衡器配置，实现故障自动切换</p>
</li>
</ul>
<h3>告警策略</h3>
<ul>
<li>
<p>基于用户影响而非技术指标设计告警规则</p>
</li>
<li>
<p>为严重告警制定升级流程</p>
</li>
<li>
<p>通过合理设置阈值和告警管理，避免告警疲劳</p>
</li>
<li>
<p>确保告警信息包含足够的上下文，便于快速响应</p>
</li>
</ul>
<h2>优雅关闭</h2>
<p>优雅关闭确保应用能干净地停止运行：完成正在处理的请求、正确释放资源，并实现无数据丢失、无服务中断的平滑部署。</p>
<h3>关闭信号处理</h3>
<ul>
<li>
<p>监听来自操作系统或容器编排工具的关闭信号（如 SIGTERM、SIGINT）</p>
</li>
<li>
<p>实现信号处理器，触发优雅关闭流程</p>
</li>
<li>
<p>提供可配置的关闭超时时间</p>
</li>
</ul>
<h3>正在处理请求的处理</h3>
<ul>
<li>
<p>停止接收新请求，同时允许正在处理的请求完成</p>
</li>
<li>
<p>实现请求排空（Request Draining）机制，并设置合理超时</p>
</li>
<li>
<p>提供状态端点，向负载均衡器指示应用正处于关闭状态</p>
</li>
</ul>
<h3>资源清理</h3>
<ul>
<li>
<p>干净地关闭数据库连接</p>
</li>
<li>
<p>刷新待写入数据和缓存</p>
</li>
<li>
<p>停止后台任务和定时任务</p>
</li>
<li>
<p>释放文件句柄和网络连接</p>
</li>
<li>
<p>为所有托管资源实现清理流程</p>
</li>
</ul>
<h3>依赖服务关闭</h3>
<ul>
<li>
<p>与依赖服务协调关闭流程</p>
</li>
<li>
<p>妥善处理外部服务依赖</p>
</li>
<li>
<p>实现熔断器，防止关闭过程中因外部服务不可用导致的挂起</p>
</li>
</ul>
<h3>容器与编排集成</h3>
<ul>
<li>
<p>在容器编排工具中配置合适的终止宽限期</p>
</li>
<li>
<p>在关闭过程中返回正确的健康检查响应</p>
</li>
<li>
<p>确保关闭流程能在配置的超时时间内完成</p>
</li>
</ul>
<h2>安全性</h2>
<p>安全性是贯穿后端开发各层级的核心关注点，需保护数据安全、防止未授权访问、维护系统完整性。</p>
<h3>输入安全</h3>
<ul>
<li>
<p>验证并清洗所有输入数据，防止注入攻击</p>
</li>
<li>
<p>采用正确的参数绑定方式，防止 SQL 注入</p>
</li>
<li>
<p>对输出数据进行适当转义，防止 XSS（跨站脚本）攻击</p>
</li>
<li>
<p>尽可能使用白名单（Allowlist）而非黑名单（Blocklist）进行过滤</p>
</li>
</ul>
<h3>身份验证安全</h3>
<ul>
<li>
<p>实施严格的密码策略，使用可靠的哈希算法存储密码</p>
</li>
<li>
<p>对敏感操作启用多因素认证（MFA）</p>
</li>
<li>
<p>采用安全的会话管理实践</p>
</li>
<li>
<p>通过速率限制（Rate Limiting）防止暴力破解攻击</p>
</li>
</ul>
<h3>通信安全</h3>
<ul>
<li>
<p>所有通信均使用 HTTPS</p>
</li>
<li>
<p>实施完善的证书管理</p>
</li>
<li>
<p>使用 HSTS、CSP 等安全头部</p>
</li>
<li>
<p>定期验证 SSL/TLS 配置</p>
</li>
<li>
<p>对传输中和静态存储的敏感数据进行加密</p>
</li>
</ul>
<h3>访问控制</h3>
<ul>
<li>
<p>遵循最小权限原则</p>
</li>
<li>
<p>在所有访问点实施适当的授权检查</p>
</li>
<li>
<p>验证所有操作的权限</p>
</li>
<li>
<p>定期审计访问模式</p>
</li>
<li>
<p>设计安全的故障处理机制（安全失效）</p>
</li>
</ul>
<h3>安全监控</h3>
<ul>
<li>
<p>监控安全事件和异常行为</p>
</li>
<li>
<p>实施入侵检测机制</p>
</li>
<li>
<p>记录与安全相关的事件</p>
</li>
<li>
<p>制定事件响应流程</p>
</li>
<li>
<p>通过定期安全审计和渗透测试识别漏洞</p>
</li>
</ul>
<h2>扩展与性能</h2>
<p>扩展与性能优化使应用能在负载增长的情况下保持响应能力，包括垂直扩展（增强单节点硬件）和水平扩展（增加节点数量）两种方式。</p>
<h3>性能指标</h3>
<ul>
<li>
<p>监控关键性能指标：响应时间、吞吐量、错误率和资源利用率</p>
</li>
<li>
<p>建立性能基准</p>
</li>
<li>
<p>根据用户需求和业务目标设定合理的性能目标</p>
</li>
</ul>
<h3>垂直扩展</h3>
<ul>
<li>
<p>通过增加 CPU、内存或存储资源提升单个服务器性能</p>
</li>
<li>
<p>该方式实现简单，但存在物理限制和单点故障风险</p>
</li>
<li>
<p>在扩展前使用性能分析工具识别资源瓶颈</p>
</li>
</ul>
<h3>水平扩展</h3>
<ul>
<li>
<p>通过增加服务器实例数量分散负载</p>
</li>
<li>
<p>要求应用设计为无状态模式</p>
</li>
<li>
<p>需制定负载均衡策略</p>
</li>
<li>
<p>考虑多实例间的数据一致性问题</p>
</li>
<li>
<p>提供更好的容错能力</p>
</li>
</ul>
<h3>负载均衡</h3>
<ul>
<li>
<p>使用多种算法（如轮询、最少连接、加权分配）在多服务器间分发请求</p>
</li>
<li>
<p>实施健康检查，仅将流量路由到健康实例</p>
</li>
<li>
<p>支持会话亲和性（Session Affinity）（如需要）</p>
</li>
<li>
<p>考虑使用硬件或软件负载均衡解决方案</p>
</li>
</ul>
<h3>数据库扩展</h3>
<ul>
<li>
<p>对读密集型工作负载使用只读副本</p>
</li>
<li>
<p>对写密集型工作负载使用数据库分片（Sharding）</p>
</li>
<li>
<p>使用连接池提高资源使用效率</p>
</li>
<li>
<p>针对特定扩展需求考虑 NoSQL 数据库</p>
</li>
</ul>
<h3>缓存策略</h3>
<ul>
<li>
<p>在多个层级实施缓存：应用级缓存、数据库查询缓存、CDN 缓存</p>
</li>
<li>
<p>使用合适的缓存失效策略维护数据一致性</p>
</li>
<li>
<p>针对热点数据优化缓存配置</p>
</li>
</ul>
<h2>并发与并行</h2>
<p>并发使应用能同时处理多个任务，并行则指同时执行多个任务。理解这些概念对构建响应迅速、高效的后端系统至关重要。</p>
<h3>并发模型</h3>
<ul>
<li>
<p>基于线程的并发：在单个进程中使用多个线程</p>
</li>
<li>
<p>事件驱动并发：使用事件循环和回调机制</p>
</li>
<li>
<p>基于 Actor 的并发：将状态封装在 Actor 中，通过消息通信</p>
<p>需根据问题特征选择合适的模型。</p>
</li>
</ul>
<h3>线程安全</h3>
<ul>
<li>
<p>通过同步机制（如互斥锁、锁、原子操作）确保多线程访问共享资源时的数据一致性</p>
</li>
<li>
<p>尽量减少共享可变状态</p>
</li>
<li>
<p>优先使用不可变数据结构</p>
</li>
</ul>
<h3>异步处理</h3>
<ul>
<li>
<p>使用异步编程模式处理 I/O 操作，避免线程阻塞</p>
</li>
<li>
<p>实施非阻塞 I/O 提高资源利用率</p>
</li>
<li>
<p>使用回调模式或 async/await 语法编写易读的异步代码</p>
</li>
<li>
<p>避免回调地狱（Callback Hell）</p>
</li>
</ul>
<h3>并行处理</h3>
<ul>
<li>
<p>将计算任务分配到多个处理器或核心</p>
</li>
<li>
<p>使用线程池高效管理工作线程</p>
</li>
<li>
<p>对 CPU 密集型任务考虑使用并行算法</p>
</li>
<li>
<p>平衡并行带来的性能提升与额外开销</p>
</li>
</ul>
<h3>竞态条件与死锁</h3>
<ul>
<li>
<p>通过适当的同步机制识别并防止竞态条件</p>
</li>
<li>
<p>通过一致的锁顺序和超时机制避免死锁</p>
</li>
<li>
<p>使用测试和分析工具检测并发问题</p>
</li>
<li>
<p>实施死锁检测和恢复机制（如需要）</p>
</li>
</ul>
<h2>对象存储与大文件</h2>
<p>对象存储为存储大文件、媒体内容和非结构化数据提供了可扩展的解决方案。对于处理用户生成内容和大型数据集的现代应用程序而言，对象存储至关重要。</p>
<h3>对象存储概念</h3>
<p>对象存储系统将文件作为带有元数据的对象存储在扁平命名空间中，提供用于访问的 REST API，具备近乎无限的可扩展性，并包含版本控制、生命周期管理和访问控制等功能。</p>
<h3>文件上传策略</h3>
<ul>
<li>
<p>直接向对象存储上传文件，以减轻服务器负载</p>
</li>
<li>
<p>使用预签名 URL 实现安全的临时访问</p>
</li>
<li>
<p>为大文件支持断点续传功能</p>
</li>
<li>
<p>验证文件类型和大小，保障安全性</p>
</li>
</ul>
<h3>内容分发</h3>
<ul>
<li>
<p>使用内容分发网络（CDN）实现文件的全球分发</p>
</li>
<li>
<p>为静态内容配置适当的缓存头</p>
</li>
<li>
<p>考虑使用图像优化和转换服务，实现响应式内容交付</p>
</li>
</ul>
<h3>文件处理</h3>
<ul>
<li>
<p>异步处理上传的文件，避免阻塞用户界面</p>
</li>
<li>
<p>实施病毒扫描，确保安全</p>
</li>
<li>
<p>为媒体文件生成缩略图或预览图</p>
</li>
<li>
<p>处理文件格式转换需求</p>
</li>
</ul>
<h3>存储优化</h3>
<ul>
<li>
<p>制定生命周期策略，将旧文件迁移到成本更低的存储层级</p>
</li>
<li>
<p>在合适的情况下对文件进行压缩</p>
</li>
<li>
<p>对相同文件进行去重处理</p>
</li>
<li>
<p>监控存储成本和使用模式</p>
</li>
</ul>
<h2>实时系统</h2>
<p>实时系统支持客户端与服务器之间即时的双向通信，适用于聊天应用、实时更新和协同编辑等场景。</p>
<h3>实时技术</h3>
<ul>
<li>
<p>WebSockets 通过 TCP 连接提供全双工通信</p>
</li>
<li>
<p>服务器发送事件（Server-Sent Events）支持服务器到客户端的流传输</p>
</li>
<li>
<p>轮询（Polling）技术是一种简单但效率较低的替代方案</p>
</li>
<li>
<p>应根据通信模式和浏览器支持情况选择合适的技术</p>
</li>
</ul>
<h3>连接管理</h3>
<ul>
<li>
<p>处理连接建立、身份验证和生命周期管理</p>
</li>
<li>
<p>实现连接池技术</p>
</li>
<li>
<p>采用心跳机制检测断连情况</p>
</li>
<li>
<p>在实时功能不可用时，实现平滑降级</p>
</li>
</ul>
<h3>消息路由</h3>
<ul>
<li>
<p>高效地在客户端之间路由消息</p>
</li>
<li>
<p>实现发布 / 订阅（pub/sub）模式以进行消息广播</p>
</li>
<li>
<p>为离线客户端考虑消息持久化方案</p>
</li>
<li>
<p>根据需求处理消息排序和交付保证</p>
</li>
</ul>
<h3>实时系统扩展</h3>
<ul>
<li>
<p>使用消息代理实现跨多个服务器实例的扩展</p>
</li>
<li>
<p>为连接亲和性实现粘性会话（sticky sessions）或共享状态</p>
</li>
<li>
<p>对于复杂需求，考虑使用专用的实时平台</p>
</li>
</ul>
<h3>性能与可靠性</h3>
<ul>
<li>
<p>监控连接数量和消息吞吐量</p>
</li>
<li>
<p>实施速率限制，防止滥用</p>
</li>
<li>
<p>妥善处理网络故障</p>
</li>
<li>
<p>为关键功能提供备用机制</p>
</li>
</ul>
<h2>测试与代码质量</h2>
<p>测试可确保代码正确性，支持放心的代码重构；而代码质量实践则能提高可维护性并减少漏洞。两者对于可持续的软件开发都至关重要。</p>
<h3>测试金字塔</h3>
<ul>
<li>
<p>单元测试：孤立地验证单个组件</p>
</li>
<li>
<p>集成测试：验证组件间的交互</p>
</li>
<li>
<p>端到端测试：验证完整的用户流程</p>
</li>
<li>
<p>需根据成本和反馈价值平衡不同层级的测试</p>
</li>
</ul>
<h3>测试驱动开发（TDD）</h3>
<ul>
<li>
<p>在实现功能前先编写测试，以此指导设计决策</p>
</li>
<li>
<p>确保全面的测试覆盖率</p>
</li>
<li>
<p>在开发过程中提供即时反馈</p>
</li>
<li>
<p>有助于编写更具可测试性和针对性的代码</p>
</li>
</ul>
<h3>模拟与测试替身（Test Doubles）</h3>
<ul>
<li>
<p>使用模拟对象（mocks）、存根（stubs）和伪对象（fakes）将被测单元与依赖项隔离</p>
</li>
<li>
<p>对外部服务、数据库和复杂对象进行模拟，以创建可靠且快速的测试</p>
</li>
<li>
<p>避免过度模拟，否则会导致测试变得脆弱</p>
</li>
</ul>
<h3>测试环境管理</h3>
<ul>
<li>
<p>维护具有受控数据的独立测试环境</p>
</li>
<li>
<p>使用数据库事务或清理程序维持测试隔离性</p>
</li>
<li>
<p>实现测试数据工厂，确保一致的测试设置</p>
</li>
</ul>
<h3>代码质量指标</h3>
<ul>
<li>
<p>通过循环复杂度、代码覆盖率、重复率和可维护性指数等指标监控代码质量</p>
</li>
<li>
<p>使用静态分析工具识别潜在问题并强制执行编码标准</p>
</li>
</ul>
<h3>持续集成（CI）</h3>
<ul>
<li>
<p>在 CI/CD 流水线中实现测试自动化</p>
</li>
<li>
<p>每次代码变更后运行测试</p>
</li>
<li>
<p>测试失败时阻止部署</p>
</li>
<li>
<p>在自动化流水线中包含代码检查、安全扫描和性能测试</p>
</li>
</ul>
<h2>十二因素应用原则（12 Factor App Principles）</h2>
<p>十二因素应用方法论为构建软件即服务（SaaS）应用提供了指导方针，使应用在现代云环境中具备可移植性、可扩展性和可维护性。</p>
<h3>代码库（Codebase）</h3>
<ul>
<li>
<p>维护一个通过版本控制追踪的代码库，支持多个部署实例</p>
</li>
<li>
<p>使用分支开发功能，但从单个主分支部署</p>
</li>
<li>
<p>避免为同一应用维护多个代码库</p>
</li>
</ul>
<h3>依赖（Dependencies）</h3>
<ul>
<li>
<p>使用依赖管理工具明确声明并隔离依赖项</p>
</li>
<li>
<p>绝不依赖系统级包</p>
</li>
<li>
<p>确保不同环境中依赖项版本一致</p>
</li>
</ul>
<h3>配置（Config）</h3>
<ul>
<li>
<p>将配置存储在环境变量中，而非代码里</p>
</li>
<li>
<p>将不同部署实例间存在差异的配置与应用代码分离</p>
</li>
<li>
<p>复杂场景下使用配置管理工具</p>
</li>
</ul>
<h3>后端服务（Backing Services）</h3>
<ul>
<li>
<p>将数据库、队列、缓存等后端服务视为附加资源，通过 URL 或连接字符串访问</p>
</li>
<li>
<p>实现服务可替换性，无需修改代码</p>
</li>
</ul>
<h2>3 构建、发布、运行（Build, Release, Run）</h2>
<ul>
<li>
<p>严格分离构建、发布和运行三个阶段</p>
</li>
<li>
<p>构建阶段：创建部署工件</p>
</li>
<li>
<p>发布阶段：将构建产物与配置结合</p>
</li>
<li>
<p>运行阶段：在执行环境中启动应用</p>
</li>
</ul>
<h3>进程（Processes）</h3>
<ul>
<li>
<p>将应用作为无状态进程执行，进程间不共享任何数据</p>
</li>
<li>
<p>在后端服务中存储持久化数据</p>
</li>
<li>
<p>Web 应用使用外部会话存储</p>
</li>
</ul>
<h3>端口绑定（Port Binding）</h3>
<ul>
<li>
<p>通过端口绑定对外提供服务，而非依赖运行时注入的 Web 服务器</p>
</li>
<li>
<p>应用应具备自包含性，通过端口接口提供服务</p>
</li>
</ul>
<h3>并发（Concurrency）</h3>
<ul>
<li>
<p>通过进程模型实现应用扩展，而非进程内线程</p>
</li>
<li>
<p>为不同工作负载使用不同类型的进程</p>
</li>
<li>
<p>由进程管理器负责扩展工作</p>
</li>
</ul>
<h3>可处置性（Disposability）</h3>
<ul>
<li>
<p>设计可快速启动且优雅关闭的进程</p>
</li>
<li>
<p>妥善处理终止信号</p>
</li>
<li>
<p>确保启动迅速、关闭干净的稳健运行机制</p>
</li>
</ul>
<h3>开发 / 生产环境一致性（Dev/Prod Parity）</h3>
<ul>
<li>
<p>保持开发、预发布和生产环境尽可能相似</p>
</li>
<li>
<p>最大限度减少环境间在时间、人员和工具方面的差异</p>
</li>
</ul>
<h3>日志（Logs）</h3>
<ul>
<li>
<p>将日志视为事件流，输出到标准输出（stdout）</p>
</li>
<li>
<p>由执行环境负责日志的路由、存储和分析</p>
</li>
<li>
<p>使用结构化日志，便于更好地分析</p>
</li>
</ul>
<h3>管理进程（Admin Processes）</h3>
<ul>
<li>
<p>管理任务作为一次性进程，在与常规应用进程相同的环境中运行</p>
</li>
<li>
<p>管理任务使用与应用相同的代码库和配置</p>
</li>
</ul>
<h2>OpenAPI 标准</h2>
<p>OpenAPI（前身为 Swagger）是用于描述 REST API 的规范，支持自动生成文档、客户端 SDK 和 API 测试自动化。</p>
<h3>API 文档</h3>
<ul>
<li>
<p>创建全面的 API 文档，描述端点、请求 / 响应格式、身份验证要求和错误响应</p>
</li>
<li>
<p>保持文档与实现同步更新</p>
</li>
</ul>
<h3>规范结构</h3>
<ul>
<li>
<p>构建 OpenAPI 规范时，需包含清晰的 API 信息、服务器配置、路径定义、组件模式和安全方案</p>
</li>
<li>
<p>使用引用减少重复内容</p>
</li>
</ul>
<h3>模式定义（Schema Definition）</h3>
<ul>
<li>
<p>使用 JSON Schema 定义请求和响应模式</p>
</li>
<li>
<p>指定数据类型和约束条件</p>
</li>
<li>
<p>为所有属性添加描述和示例文档</p>
</li>
<li>
<p>对复杂模式采用组合方式定义</p>
</li>
</ul>
<h3>代码生成</h3>
<ul>
<li>
<p>从 OpenAPI 规范生成客户端 SDK 和服务器存根，确保文档与实现的一致性</p>
</li>
<li>
<p>使用代码生成工具减少手动编码工作量</p>
</li>
</ul>
<h3>API 版本控制</h3>
<ul>
<li>
<p>在 OpenAPI 规范中清晰标注 API 版本</p>
</li>
<li>
<p>尽可能保持向后兼容性</p>
</li>
<li>
<p>为破坏性变更提供迁移指南</p>
</li>
<li>
<p>对 API 版本采用语义化版本控制</p>
</li>
</ul>
<h3>验证与测试</h3>
<ul>
<li>
<p>根据 OpenAPI 模式验证 API 响应</p>
</li>
<li>
<p>使用基于规范的测试工具</p>
</li>
<li>
<p>实施契约测试，确保 API 符合文档描述的行为</p>
</li>
</ul>
<h2>后端工程师的 DevOps 实践</h2>
<p>DevOps 实践使后端工程师能够有效部署、监控和维护应用程序。理解 DevOps 概念对于现代后端开发至关重要。</p>
<h3>基础设施即代码（Infrastructure as Code）</h3>
<ul>
<li>
<p>用代码定义基础设施，而非手动配置</p>
</li>
<li>
<p>对基础设施定义进行版本控制</p>
</li>
<li>
<p>实现自动化配置和资源供应</p>
</li>
<li>
<p>确保一致性和可重复性</p>
</li>
</ul>
<h3>容器化（Containerization）</h3>
<ul>
<li>
<p>使用 Docker 等容器技术将应用与其依赖项打包</p>
</li>
<li>
<p>容器提供一致的运行时环境</p>
</li>
<li>
<p>简化部署流程</p>
</li>
<li>
<p>实现高效的资源利用</p>
</li>
</ul>
<h3>容器编排（Container Orchestration）</h3>
<ul>
<li>
<p>使用 Kubernetes 等编排平台大规模管理容器化应用</p>
</li>
<li>
<p>实现服务发现、负载均衡</p>
</li>
<li>
<p>支持自动扩展和滚动更新</p>
</li>
</ul>
<h3>CI/CD 流水线</h3>
<ul>
<li>
<p>实施持续集成，自动构建和测试代码变更</p>
</li>
<li>
<p>实施持续部署，自动发布应用</p>
</li>
<li>
<p>采用 &ldquo;流水线即代码&rdquo; 的方式，实现可维护的自动化</p>
</li>
</ul>
<h3>监控与告警（Monitoring and Alerting）</h3>
<ul>
<li>
<p>使用适当工具监控应用和基础设施健康状况</p>
</li>
<li>
<p>为需要人工干预的问题设置全面告警</p>
</li>
<li>
<p>创建系统仪表盘，提升系统可见性</p>
</li>
</ul>
<h3>配置管理（Configuration Management）</h3>
<ul>
<li>
<p>使用配置管理工具自动化服务器配置和软件安装</p>
</li>
<li>
<p>保持各环境一致性</p>
</li>
<li>
<p>实现快速的环境供应</p>
</li>
</ul>
<h3>DevOps 中的安全（Security in DevOps）</h3>
<ul>
<li>
<p>在开发和部署流水线中融入安全实践</p>
</li>
<li>
<p>实施漏洞扫描</p>
</li>
<li>
<p>安全管理密钥</p>
</li>
<li>
<p>遵守安全策略合规要求</p>
</li>
</ul>
<h3>备份与灾难恢复（Backup and Disaster Recovery）</h3>
<ul>
<li>
<p>为数据和配置制定全面的备份策略</p>
</li>
<li>
<p>定期测试恢复流程</p>
</li>
<li>
<p>制定灾难恢复计划，明确恢复时间目标（RTO）和恢复点目标（RPO）</p>
</li>
</ul>
<h3>性能优化（Performance Optimization）</h3>
<ul>
<li>
<p>监控生产环境中的应用性能</p>
</li>
<li>
<p>实施自动化性能测试</p>
</li>
<li>
<p>根据实际负载模式优化资源使用</p>
</li>
</ul>
<h3>云服务集成（Cloud Services Integration）</h3>
<ul>
<li>
<p>利用云服务提升可扩展性和可靠性</p>
</li>
<li>
<p>实施多区域部署，确保高可用性</p>
</li>
<li>
<p>使用托管服务减少运维开销</p>
</li>
</ul>
<h2>结语</h2>
<p>本指南从基础原理出发，为后端开发提供了全面的知识框架。每个主题都建立在之前概念的基础上，形成了涵盖现代后端工程所有关键领域的结构化学习路径。</p>
<p>从理解基本 HTTP 协议到实现复杂分布式系统，这一过程需要专注投入和实践操作。在深入学习特定技术或框架之前，应先注重理解底层原理 &mdash;&mdash; 无论你选择何种技术栈，这些知识都将发挥重要作用。</p>
<p>请记住，后端开发是一个不断发展的领域，新的技术、模式和最佳实践层出不穷。本指南中的原则提供了坚实的基础，但持续学习和适应变化对于长期成功至关重要。</p>
<p>从基础知识开始，通过实际项目积累经验，随着信心和经验的提升逐步挑战更复杂的主题。后端技术的精进是一个迭代过程 &mdash;&mdash; 随着你理解的加深和需求的复杂化，你会发现需要多次回顾并深化对各个主题的认识。</p>
<p>最重要的是，专注于构建能够可靠、可维护地解决实际问题的系统。最佳的后端系统既能为用户提供高效服务，又能让开发和维护它的团队可持续地开展工作。</p>]]></description>
    <pubDate>Sun, 28 Sep 2025 10:37:00 +0800</pubDate>
    <dc:creator>内部账号</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/77.html</guid>
</item>
<item>
    <title>tomcat的那些默认配置</title>
    <link>https://blog.xuhaobo.cn/private/76.html</link>
    <description><![CDATA[<p><a href="/content/uploadfile/202506/ab1c1750640508.png" target="_blank" rel="noopener"></p>
<p>前些天在qq群，一位同学在使用富文本编辑器的时候，遇见了莫名其妙的错误。对应字段明明填写了，但是总是提示不能为空。</p>
<p></a></p>
<p><a href="/content/uploadfile/202506/ab1c1750640508.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202506/ab1c1750640508.png" alt="2a051d13fa36802c0691f3698c5ffdb2.png"></a></p>
<p>经过多方面排查，发现是因为tomcat的最大post表单大小是2M，他这个富文本的内容远远超过了2M..</p>
<p>所以整理一下springboot项目中server.tomcat默认配置的整理，供大家学习使用。</p>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">线程配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.threads.max</code></strong>：最大工作线程数，默认值为200。每一次HTTP请求到达Web服务，Tomcat都会创建一个线程来处理该请求，此参数决定了Web服务容器可以同时处理多少个请求。不过，增加线程是有成本的，更多的线程会带来更多的线程上下文切换成本，并且意味着更多的内存消耗。例如，JVM中默认情况下在创建新线程时会分配大小为1M的线程栈。线程数的经验值为：1核2G内存，线程数经验值200；4核8G内存，线程数经验值800。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.threads.min-spare</code></strong>：最小工作空闲线程数，默认值为10。</li>
</ul>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">连接配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-connections</code></strong>：服务器在任何给定时间接受和处理的最大连接数。对于Java的阻塞式BIO，默认值是<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">max-threads</code>的值；如果在BIO模式使用定制的Executor执行器，默认值将是执行器中<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">max-threads</code>的值。对于Java新的NIO模式，默认值是10000；对于windows上APR/native IO模式，默认值为8192 ，这是出于性能原因，如果配置的值不是1024的倍数，<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">max-connections</code>的实际值将减少到1024的最大倍数。如果设置为 -1，则禁用<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">max-connections</code>功能，表示不限制Tomcat容器的连接数。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accept-count</code></strong>：等待队列的最大队列长度，默认值为100。当所有可能的请求处理线程都在使用时，传入连接请求会被放进队列中等待。如果等待队列也被放满了，这时再来新的请求就会被Tomcat拒绝（<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">connection refused</code>）。</li>
</ul>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">编码配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.uri-encoding</code></strong>：用于解码URI的字符编码，默认值为<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">UTF-8</code>。</li>
</ul>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">请求大小配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-http-form-post-size</code></strong>：任何HTTP POST请求中表单内容的最大大小，默认值为2MB。理论上POST请求的大小是无限制的，但是Tomcat设定了请求大小为2MB，如要取消限制，则应该将值设为 -1。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-swallow-size</code></strong>：请求正文的大小，默认值为2MB。</li>
</ul>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">超时配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.connection-timeout</code></strong>：连接器在接受连接后将等待呈现请求URI行的时间，即socket调用<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">read()</code>等待读取的时间，如果在设置的时间内没有请求，则会主动断开连接，默认值为60000ms。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.keep-alive-timeout</code></strong>：在连接关闭之前等待另一个HTTP请求的时间，默认和<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">connection-timeout</code>的值一样，设置为 -1 时不会超时。</li>
</ul>
<h3 class="coz-fg-plus mb-3 mt-7 text-[18px] leading-[1.5] font-bold">其他配置</h3>
<ul class="coz-fg-primary list-disc leading-[1.75] list-disc ml-4 break-normal whitespace-normal">
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-keep-alive-requests</code></strong>：在连接关闭之前可以通过管道传输的最大HTTP请求数。当设置为0或1时，长连接和管道被禁用，可以避免Tomcat产生大量的<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">TIME_WAIT</code>连接，从而从一定程度上避免Tomcat假死。当设置为 -1 时，允许无限数量的管道或长连接请求，默认值为100。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.enabled</code></strong>：是否启用访问日志，默认值为<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.basedir</code></strong>：存放Tomcat的日志、Dump等文件的临时文件夹，默认为系统的tmp文件夹（如：<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">C:\Users\Angel\AppData\Local\Temp</code>）。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-http-header-size</code></strong>：HTTP消息头的最大大小，默认值为0，表示使用连接器的容器特定默认值。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.max-http-post-size</code></strong>：HTTP POST内容的最大大小，默认值为2097152字节（即2MB）。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.redirect-context-root</code></strong>：是否应通过在路径后附加&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">/</code>&nbsp;来重定向到上下文根的请求，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">true</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.use-relative-redirects</code></strong>：HTTP 1.1及更高版本的&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">sendRedirect</code>&nbsp;调用生成的位置头是否使用相对或绝对重定向，默认未设置。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.buffered</code></strong>：是否缓冲输出，使其仅定期刷新，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">true</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.check-exists</code></strong>：是否检查日志文件是否存在，以便在外部进程重命名它时可以重新创建它，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.condition-if</code></strong>：仅在&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">ServletRequest.getAttribute(conditionIf)</code>&nbsp;不产生&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">null</code>&nbsp;时才启用请求的日志记录，默认未设置。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.condition-unless</code></strong>：仅在&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">ServletRequest.getAttribute(conditionUnless)</code>&nbsp;产生&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">null</code>&nbsp;时才启用请求记录，默认未设置。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.directory</code></strong>：创建日志文件的目录，可以是绝对的或相对于Tomcat基本目录，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">logs</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.encoding</code></strong>：日志文件使用的字符集，默认为系统默认字符集。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.file-date-format</code></strong>：放置在日志文件名中的日期格式，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">.yyyy-MM-dd</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.ipv6-canonical</code></strong>：是否使用RFC 5952定义的IPv6规范表示格式，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.locale</code></strong>：用于格式化日志条目和日志文件名后缀中的时间戳的语言环境，默认为Java进程的默认语言环境。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.max-days</code></strong>：删除访问日志文件之前保留的天数，默认值为 -1，表示不删除。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.pattern</code></strong>：访问日志的格式模式，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">common</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.prefix</code></strong>：日志文件名前缀，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">access_log</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.rename-on-rotate</code></strong>：是否推迟在文件名中包含日期戳，直到轮换时间，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.request-attributes-enabled</code></strong>：是否为请求使用的IP地址、主机名、协议和端口设置请求属性，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.rotate</code></strong>：是否启用访问日志轮换，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">true</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.accesslog.suffix</code></strong>：日志文件名后缀，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">.log</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.additional-tld-skip-patterns</code></strong>：逗号分隔的附加模式列表，匹配jar以忽略TLD扫描，特殊的&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">?</code>&nbsp;和&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">*</code>&nbsp;字符可以在模式中使用，以分别匹配一个和一个字符以及零个或多个字符，默认未设置。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.background-processor-delay</code></strong>：调用&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">backgroundProcess</code>&nbsp;方法之间的延迟，如果未指定持续时间后缀，则将使用秒，默认值为10秒。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.mbeanregistry.enabled</code></strong>：是否应该启用Tomcat的MBean Registry，默认值为&nbsp;<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">false</code>。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal"><strong class="coz-fg-plus font-bold text-base leading-[1.75]"><code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">server.tomcat.processor-cache</code></strong>：将保留在缓存中并在后续请求中重用的最大空闲处理器数，当设置为 -1 时，缓存将不受限制，理论上的最大大小等于最大连接数，默认值为200。</li>
<li class="coz-fg-primary leading-[1.75] mb-0 break-normal whitespace-normal">&nbsp;</li>
</ul>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">这些默认配置可以通过在<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">application.yml</code>或<code class="coz-fg-primary vertical-middle p-2 rounded overflow-auto text-sm">application.properties</code>文件中进行修改，以满足不同的项目需求。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">&nbsp;</p>]]></description>
    <pubDate>Mon, 23 Jun 2025 08:56:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/76.html</guid>
</item>
<item>
    <title>（原创）dify实现@效果</title>
    <link>https://blog.xuhaobo.cn/private/75.html</link>
    <description><![CDATA[<p>在基础版的dify中，使用基础回话与AI交流。当编制的chatflow非常复杂的时候，我们期望通过@xx助手，实现在chatflow中进行if判断，然后通过不同分支进行响应恢复的效果。但是原生的dify是不支持@的。本文通过魔改添加代码实现基础@效果，适用于1.4.1之前的任何docker版本。之后的版本兴许也适用，但是还没出。</p>
<p>&nbsp;</p>
<p>添加@功能操作步骤如下，本文默认你已经通过docker compose安装dify。</p>
<p>1.在volumes/certbot/www 目录下创建haobo56目录，新建diao.js 。内容见代码。</p>
<pre class="language-javascript"><code>let checkCount = 0;
const maxChecks = 5;

function checkAndBindEvent() {
    const messageInput = document.querySelector('textarea');
    const c_pathname = window.location.pathname;
    console.log(messageInput);
    console.log(c_pathname);
    if (messageInput &amp;&amp; c_pathname.startsWith('/chat')) {
        let currentFocus;
        let suggestionsList;

        messageInput.onkeyup = function(e) {
            let val = this.value;
            closeAllLists();
            if (!val) return false;
            currentFocus = -1;

            if (val === '@') {
                suggestionsList = document.createElement('ul');
                suggestionsList.style.position = 'absolute';
                suggestionsList.style.border = '1px solid #ccc';
                suggestionsList.style.backgroundColor = 'white';
                suggestionsList.style.width = '200px';
                suggestionsList.style.listStyleType = 'none';
                suggestionsList.style.padding = '0';
                suggestionsList.style.margin = '0';
                suggestionsList.style.zIndex = '1000';

                for (let user of customer_users) {
                    const item = document.createElement('li');
                    item.textContent = user;
                    item.onclick = function() {
                        replaceAtCursor(messageInput, `@${user}`);
                        closeAllLists();
                    };
                    item.style.padding = '5px';
                    item.style.cursor = 'pointer';
                    item.onmouseover = function() {
                        removeActive(suggestionsList.getElementsByTagName("li"));
                        this.classList.add("active-suggestion");
                    };
                    suggestionsList.appendChild(item);
                }

                document.body.appendChild(suggestionsList);

                // Position the suggestions list at the top-right corner of the textarea
                const rect = messageInput.getBoundingClientRect();
                suggestionsList.style.left = `${rect.left + 30}px`; // Adjust width to match ul width
                suggestionsList.style.top = `${rect.top - 110}px`;

                // Handle arrow keys and enter key within onkeyup event
                if (e.key === "ArrowDown") {
                    currentFocus++;
                    addActive(suggestionsList.getElementsByTagName("li"));
                } else if (e.key === "ArrowUp") {
                    currentFocus--;
                    addActive(suggestionsList.getElementsByTagName("li"));
                } else if (e.key === "Enter") {
                    if (currentFocus &gt; -1) {
                        if (suggestionsList &amp;&amp; suggestionsList.getElementsByTagName("li").length &gt; currentFocus) {
                            suggestionsList.getElementsByTagName("li")[currentFocus].click();
                        }
                    }
                }
            } else {
                closeAllLists();
            }
        };

        window.onclick = function(e) {
            if (e.target !== messageInput) {
                closeAllLists();
            }
        };

        function closeAllLists() {
            if (suggestionsList) {
                document.body.removeChild(suggestionsList);
                suggestionsList = null;
            }
        }

        function replaceAtCursor(myField, myValue) {
            const startPos = 0; // Always start at the beginning since we check for '@' at the start
            const endPos = 1;   // End at the position after '@'
            myField.value = myField.value.substring(0, startPos) + myValue + ' ' + myField.value.substring(endPos, myField.value.length);
            myField.selectionStart = myField.selectionEnd = startPos + myValue.length + 1; // Move cursor after the inserted text
            myField.focus();
        }

        function addActive(x) {
            if (!x) return false;
            removeActive(x);
            if (currentFocus &gt;= x.length) currentFocus = 0;
            if (currentFocus &lt; 0) currentFocus = (x.length - 1);
            x[currentFocus].classList.add("active-suggestion");
        }

        function removeActive(x) {
            for (let i = 0; i &lt; x.length; i++) {
                x[i].classList.remove("active-suggestion");
            }
        }

        clearInterval(checkInterval); // Stop checking once the element is found and events are bound
    }else{
            //防止定时搞的浏览器很卡
            checkCount++;
            if (checkCount &gt;= maxChecks) {
            clearInterval(checkInterval); // 达到最大检查次数时停止检查
        }
    }
}

const customer_users = ['小精灵', '大头兵', 'qwen', 'deepseek'];
const checkInterval = setInterval(checkAndBindEvent, 2000);</code></pre>
<p>2.修改nginx模版文件 <span class="s1">nginx/conf.d/default.conf.template，见代码，以下代码为新增代码</span></p>
<pre class="language-ruby"><code>  location ~ ^/chat {
         proxy_pass http://web:3000;
         add_header xx-job-key "test1";
         proxy_set_header Accept-Encoding "";
         sub_filter '&lt;/body&gt;' '&lt;script src="/haobo56/diao.js" async=""&gt;&lt;/script&gt; &lt;/body&gt;';  
         sub_filter_once off;
         sub_filter_types *;
         include proxy.conf;
     }

     location /haobo56/ {
       root /var/www/html;
     }</code></pre>
<p><span class="s1">3.之后重启docker compose restart， 在聊天框输入@就看到效果了</span></p>
<p><a href="/content/uploadfile/202506/d2b51750061021.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202506/d2b51750061021.png" alt="image.png"></a></p>
<p><span class="s1">4.编排chatflow的时候你可以这样判断了</span></p>
<p><a href="/content/uploadfile/202506/d2b51750061051.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202506/d2b51750061051.png" alt="image.png"></a></p>
<p>本文为原创内容：转载请注明出处</p>
<p>https://blog.xuhaobo.cn/%E7%A7%81%E6%9C%89%E5%88%86%E7%B1%BB/75.html</p>
<p>&nbsp;</p>]]></description>
    <pubDate>Mon, 16 Jun 2025 15:23:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/75.html</guid>
</item>
<item>
    <title>lenna 图像识别原图</title>
    <link>https://blog.xuhaobo.cn/private/74.html</link>
    <description><![CDATA[<p><a href="http://www.lenna.org/full/len_hires.html" target="_blank" rel="noopener">原文链接 点这里</a></p>
<p>感谢贡献</p>
<p><img src="http://www.lenna.org/lena_std.tif" alt=""><a href="/content/uploadfile/202505/34fc1748242521.jpg" target="_blank" rel="noopener"><img src="/content/uploadfile/202505/34fc1748242521.jpg" alt="len_std_small.jpg" width="256" height="256"></a></p>]]></description>
    <pubDate>Mon, 26 May 2025 14:53:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/74.html</guid>
</item>
<item>
    <title>数据治理为何屡屡无果，却又屡屡重启？</title>
    <link>https://blog.xuhaobo.cn/private/73.html</link>
    <description><![CDATA[<div class="streaming-markdown w-full -my-1 prose max-w-none text-[16px]">
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">在今天的文章里，我们来探讨一下：为何企业在数据治理这件事上，总是陷入一种治标不治本的循环。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">我留意到一个颇为有趣的现象：当下的企业在数据治理的征程中，仿佛一直在做&ldquo;生死轮回&rdquo;。一方面，企业在多数时间多数人对数据质量问题视而不见，选择&ldquo;躺平&rdquo;；但另一方面，少部分人包括经营者又不得不直面数据杂乱无章、企业运营缺乏可用数据的困境。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">在存量时代的竞争中，数据已然成为企业绕不开的关键要素。这就造成了一种局面，每隔一两年，企业内部就会掀起一场数据治理的热潮，召开大型会议、制定新规则、引入新工具。然而，最终的结果往往是有始无终，治理的成果难以长久维持。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">一家xx企业的CTO曾感慨道：&ldquo;我们的数据治理项目，似乎每隔两年就得重启一次，问题总是反复出现，始终无法得到有效解决。&rdquo;</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">再看看互联网大厂，情况也并无二致。几乎每家企业都经历过类似的困境：数据治理就像一阵风，项目结束后留下一地鸡毛，数据问题依旧如影随形。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">为什么数据治理总是陷入这种&ldquo;周期性运动&rdquo;呢？其背后的核心原因是什么？又该如何解决呢？</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">一、数据治理困境的根本原因</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">企业在数据治理方面反复折腾，主要是因为治理方式&ldquo;治标不治本&rdquo;。治理项目看似声势浩大，但往往缺乏持续性和系统性，尤其是在数据标准方面存在明显不足。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">常见的问题主要有以下几个方面：</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">1、局部治理，缺乏全局观 当某个部门的数据出现问题时，企业就集中力量治理该部门的数据，缺乏对整体数据的全局视角。局部的过度处理一定会导致数据的不合群。整体看数据还是乱糟糟。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">2、缺少顶层设计 许多企业没有将数据治理提升到战略层面，仅仅是被动地应对业务中出现的问题。更多的情况是数据管理者没有权限去要求规范数据，企业也未提供一体化的数据工作环境。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">3、过度依赖工具 企业认为购买了数据治理平台就万事大吉，却忽略了数据标准和流程的建设。要知道，事在人为，在牛逼的工具也是人来驱动的。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">4、关键人才匮乏 数据治理需要既懂技术、又懂业务和管理的综合性人才，但企业内部往往缺乏这样的人才。很多企业的数据治理工作由IT部门牵头，但搞了半年，连数据治理要服务的业务场景和目的都没弄清楚，结果可想而知。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">这些问题既涉及组织层面，也涉及人才层面，但归根结底，还是因为缺乏统一的数据标准。如果没有统一的数据标准，数据治理就如同建造房屋没有打好地基，看似搭建起来了，但很快就会崩塌。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">&nbsp;</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">二、数据标准对数据治理为何至关重要</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">什么是数据标准呢？</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">简单来说，数据标准就是企业内部对数据的统一定义和规范，包括数据的命名、格式、口径、分类等规则。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">数据标准贯穿于数据的整个生命周期&mdash;&mdash;从数据的采集、存储，到处理、分析和应用。如果没有数据标准，各个部门的数据就会各自为政，口径不一致，指标之间相互矛盾，数据分析的结果自然也就难以令人信任。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">想象一下，如果一个城市没有交通规则，每辆车都按照自己的方式行驶，城市的交通会变得多么混乱？同样的道理，企业的数据也需要&ldquo;交通规则&rdquo;，而数据标准就是企业数据的&ldquo;红绿灯&rdquo;和&ldquo;分道线&rdquo;。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">这么说可能还是比较抽象，我们来看一个简单的例子。一个完整的数据标准，涵盖了字段的命名规则、数据格式、业务口径、分类分级、数据质量要求等方面。下面以&ldquo;手机号码&rdquo;为例进行说明。</p>
<table style="width: 99.0982%;"><caption>
<section>字段标准样例：手机号码（MOBILE_NUMBER）</section>
</caption>
<tbody>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">字段属性</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<section>标准要求</section>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">字段名称</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<p>MOBILE_NUMBER</p>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>字段含义</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<p>员工的个人手机号码，用于联系员工及发送通知</p>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>命名规则</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<p>全部采用大写字母+下划线分隔，字段名要体现业务含义。例如，MOBILE表示手机，NUMBER表示号码。</p>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>数据类型</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<section>CARCHAR</section>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>数据格式</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<section>长度为 11 位，必须是纯数字</section>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>数据分类</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<section>个人敏感数据</section>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" width="119" data-colwidth="119">
<section>数据分级</section>
</td>
<td style="width: 78.1182%;" valign="top" width="365" data-colwidth="365">
<section>高敏感级别，涉及员工个人隐私信息，需严格保护</section>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" data-colwidth="119">
<section>数据质量要求</section>
</td>
<td style="width: 78.1182%;" valign="top" data-colwidth="365">
<ul class="list-paddingleft-1">
<li>
<p>唯一性：每个员工的手机号码必须唯一，不得重复&nbsp;</p>
</li>
<li>
<p>格式校验：必须为11位有效手机号码，且只能包含数字</p>
</li>
<li>
<p>必填性：入职时必须填写，不允许为空</p>
</li>
<li>
<p>有效性：定期检查号码有效性，剔除无效号码</p>
</li>
</ul>
</td>
</tr>
<tr>
<td style="width: 21.8818%;" valign="top" data-colwidth="119">
<section>数据用途</section>
</td>
<td style="width: 78.1182%;" valign="top" data-colwidth="365">
<section>用于通知发送、权限管理、紧急联系等场景</section>
</td>
</tr>
</tbody>
</table>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">通过制定上述数据标准，可以有效地提高数据质量，确保数据的可用性，提高数据的长期可维护性，更重要的是能够显著提升数据的一致性，实现数据的一致共享。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">三、数据标准有哪些应用场景</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">很多人可能会有疑问，制定了一堆标准到底有什么用呢？接下来，我们一起看看在数据治理过程中，数据标准有哪些应用场景。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">在数据治理的实践中，数据标准的应用场景非常广泛，主要体现在以下几个方面：</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">1、规范建模 许多企业在进行数据建模时，会遇到业务口径不一致的问题。例如，财务部门和供应链部门对&ldquo;成本&rdquo;这个指标的定义可能完全不同，导致分析结果出现偏差。通过数据标准，可以建立统一的模型定义模板，确保每个指标的口径一致。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">2、数据研发规范 在数据开发过程中，企业经常会遇到脚本不符合规范的情况，从而导致数据质量问题。某零售企业在引入数据标准后，通过事前阻断机制杜绝了不规范脚本上线，从源头上提升了数据质量。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">3、数据质量规则 数据标准可以用于数据质量的事前控制和事后治理。在数据采集环节，设置事前阻断规则，避免错误数据流入系统；在数据分析环节，进行事后稽核，清理不合规数据。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">4、数据安全管理 通过数据标准对数据进行分类分级管理，可以有效保护企业的数据资产。比如，对敏感数据进行特征识别和监控，设置不同的访问权限，确保数据安全。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">四、数据标准如何发挥价值</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">&ldquo;我们明明已经制定了很多数据标准，但是，最终数据治理项目还是失败了，实在搞不懂啊！&rdquo;我相信这不是一个人的心声。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">为什么有了数据标准，数据治理还是会失败呢？尽管数据标准非常重要，但很多企业在实际操作中仍然会&ldquo;踩坑&rdquo;。主要有以下几个原因：</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">1、一次性工程 很多企业将数据标准的建设视为一次性工程，标准制定好之后就不再关注，没有<strong>动态更新和维护机制</strong>。随着业务的发展，标准逐渐失去了有效性。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">2、乙方撤场后甲方运维困难 数据治理项目结束后，外部顾问团队撤离，企业内部缺乏持续运维数据标准的人才，导致标准无法真正落地，成为一堆无用的标准。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">3、标准落标率缺乏监控 企业没有对标准的执行情况进行监控和考核，导致数据标准成为&ldquo;纸面规则&rdquo;，<strong>各部门执行不严格，久而久之就被遗忘</strong>了。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal"><strong>总结来说，就是标准没有随着业务的发展进行持续的运营迭代，逐渐无法跟上业务的节奏，最终被业务抛弃，企业又回到了没有数据标准的时代。</strong></p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">那么，如何让数据标准真正发挥作用呢？要想让数据标准真正落地，企业必须在以下几个方面下功夫：</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">1、建立数据标准委员会 设立专门的团队，负责数据标准的制定、更新和维护工作，确保标准始终与业务需求保持一致。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">2、培养内部数据治理人才 减少对外部乙方的依赖，建立企业内部的数据治理能力，培养既懂业务又懂技术的复合型人才。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">3、引入标准落标率监控机制 在数据平台中设置标准落标率的监控功能，将各部门的标准执行情况量化，纳入绩效考核指标。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">这些措施其实并不难实施，但很少有企业能够真正做到。国外企业已经很少讨论数据标准了，因为对于他们来说，制定数据标准是一项基础且必要的工作，已经成为一种共识，无需再进行讨论。</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">而我国很多企业仍处于数字化转型的初期，缺乏对数据要素的前期治理投入和重视，相关人才储备也不足，这就导致企业反复推进数据治理工作。说得好听点，这是在实战中学习；说得不好听点，就是&hellip;&hellip;（这里实在难以用合适的词来表达，大家自行想象吧！）</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">五、小结</p>
<p class="coz-fg-primary text-base leading-[1.75] whitespace-wrap break-normal">没有数据标准的治理，都是治标不治本。数据治理不是一次性的项目，而是一场漫长的马拉松，需要持续优化、动态维护数据标准。只有让数据标准融入企业的日常数据管理，成为其中的一部分，数据治理才能真正发挥出应有的价值。</p>
</div>]]></description>
    <pubDate>Fri, 16 May 2025 10:05:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/73.html</guid>
</item>
<item>
    <title>看了我常用的数据库设计技巧，同事们都开始悄悄模仿……</title>
    <link>https://blog.xuhaobo.cn/private/71.html</link>
    <description><![CDATA[<section data-role="paragraph">
<section><span class="js_darkmode__1 js_darkmode__text__0">对于后端开发同学来说，访问数据库，是代码中必不可少的一个环节。</span></section>
<section></section>
<section><span class="js_darkmode__2 js_darkmode__text__1">系统中收集到用户的核心数据，为了安全性，我们一般会存储到数据库，比如：mysql，oracle等。</span></section>
<section></section>
<section><span class="js_darkmode__3 js_darkmode__text__2">后端开发的日常工作，需要不断的建库和建表，来满足业务需求。</span></section>
<section></section>
<section><span class="js_darkmode__4 js_darkmode__text__3">通常情况下，建库的频率比建表要低很多，所以，我们这篇文章主要讨论建表相关的内容。</span></section>
<section></section>
<section><span class="js_darkmode__5 js_darkmode__text__4">如果我们在建表的时候不注意细节，等后面系统上线之后，表的维护成本变得非常高，而且很容易踩坑。</span></section>
<section></section>
<section><span class="js_darkmode__6 js_darkmode__text__5">今天就跟大家一起聊聊，数据库建表的15个小技巧，希望对你会有所帮助。</span></section>
<section>
<p><a href="/content/uploadfile/202503/d2b51742542820.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742542820.png" alt="image.png"></a></p>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__8">
<section><strong><span class="js_darkmode__text__6" data-brushtype="text">一、名字</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__9 js_darkmode__text__7">建表的时候，给表、字段和索引起个好名字，真的太重要了。</span></section>
<section></section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__8">1、见名知意</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__10 js_darkmode__text__9">名字就像表、字段和索引的一张脸，可以给人留下第一印象。</span></section>
<section></section>
<section><span class="js_darkmode__11 js_darkmode__text__10">好的名字，言简意赅，见名知意，让人心情愉悦，能够提高沟通和维护成本。</span></section>
<section></section>
<section><span class="js_darkmode__12 js_darkmode__text__11">坏的名字，模拟两可，不知所云。而且显得杂乱无章，看得让人抓狂。</span></section>
<section></section>
<section><strong><span class="js_darkmode__13 js_darkmode__text__12">反例：</span></strong></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js"><code><span class="code-snippet_outer js_darkmode__text__13">用户名称字段定义成：yong_hu_ming、用户_name、name、user_name_123456789</span></code></pre>
</section>
<section><span class="js_darkmode__15">&nbsp;</span></section>
<section><span class="js_darkmode__16 js_darkmode__text__14">你看了可能会一脸懵逼，这是什么骚操作？</span></section>
<section></section>
<section><strong><span class="js_darkmode__17 js_darkmode__text__15">正例&nbsp; &nbsp; </span></strong></section>
<section><code><span class="code-snippet_outer js_darkmode__text__16">用户名称字段定义成：user_name</span></code></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js">&nbsp;</pre>
</section>
<section></section>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__18">
<section data-width="100%">
<section>
<section><span class="js_darkmode__text__17">温馨提醒一下，名字也不宜过长，尽量控制在30个字符以内。</span></section>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__18">2、大小写</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__19 js_darkmode__text__19">名字尽量都用小写字母，因为从视觉上，小写字母更容易让人读懂。</span></section>
<section></section>
<section><strong><span class="js_darkmode__20 js_darkmode__text__20">反例：</span></strong></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js"><code><span class="code-snippet_outer js_darkmode__text__21">字段名：PRODUCT_NAME、PRODUCT_name</span></code></pre>
</section>
<section><span class="js_darkmode__22">&nbsp;</span></section>
<section><span class="js_darkmode__23 js_darkmode__text__22">全部大写，看起来有点不太直观。而一部分大写，一部分小写，让人看着更不爽。</span></section>
<section></section>
<section><strong><span class="js_darkmode__24 js_darkmode__text__23">正例：</span></strong></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js"><code><span class="code-snippet_outer js_darkmode__text__24">字段名：product_name</span></code></pre>
</section>
<section><span class="js_darkmode__26">&nbsp;</span></section>
<section><span class="js_darkmode__27 js_darkmode__text__25">名字还是使用全小写字母，看着更舒服。</span></section>
<section></section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__26">3、分隔符</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__28 js_darkmode__text__27">很多时候，名字为了让人好理解，有可能会包含多个单词。</span></section>
<section></section>
<section><span class="js_darkmode__29 js_darkmode__text__28">那么，多个单词间的分隔符该用什么呢？</span></section>
<section></section>
<section><span class="js_darkmode__30 js_darkmode__text__29">反例：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="css"><code><span class="code-snippet_outer js_darkmode__text__30">字段名：<span class="code-snippet__selector-tag js_darkmode__text__31">productname</span>、<span class="code-snippet__selector-tag js_darkmode__text__32">productName</span>、<span class="code-snippet__selector-tag js_darkmode__text__33">product</span> <span class="code-snippet__selector-tag js_darkmode__text__34">name</span>、<span class="code-snippet__selector-tag js_darkmode__text__35">product</span>@<span class="code-snippet__keyword js_darkmode__text__36">name</span></span></code></pre>
</section>
<section><span class="js_darkmode__31">&nbsp;</span></section>
<section><span class="js_darkmode__32 js_darkmode__text__37">单词间没有分隔，或者单词间用驼峰标识，或者单词间用空格分隔，或者单词间用@分隔，这几种方式都不太建议。</span></section>
<section></section>
<section><span class="js_darkmode__33 js_darkmode__text__38">正例：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js"><code><span class="code-snippet_outer js_darkmode__text__39">字段名：product_name</span></code></pre>
</section>
<section><span class="js_darkmode__34">&nbsp;</span></section>
<section><span class="js_darkmode__35 js_darkmode__text__40">强烈建议大家在单词间用_分隔。</span></section>
<section></section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__41">4、表名</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__36 js_darkmode__text__42">对于表名，在言简意赅，见名知意的基础之上，建议带上业务前缀。</span></section>
<section></section>
<section><span class="js_darkmode__37 js_darkmode__text__43">如果是订单相关的业务表，可以在表名前面加个前缀：order_。</span></section>
<section></section>
<section><span class="js_darkmode__38 js_darkmode__text__44">例如：order_pay、order_pay_detail等。</span></section>
<section></section>
<section><span class="js_darkmode__39 js_darkmode__text__45">如果是商品相关的业务表，可以在表名前面加个前缀：product_。</span></section>
<section></section>
<section><span class="js_darkmode__40 js_darkmode__text__46">例如：product_spu，product_sku等。</span></section>
<section></section>
<section><span class="js_darkmode__41 js_darkmode__text__47">这样做的好处是为了方便归类，把相同业务的表，可以非常快速的聚集到一起。</span></section>
<section></section>
<section><span class="js_darkmode__42 js_darkmode__text__48">另外，还有有个好处是，如果哪天有非订单的业务，比如：金融业务，也需要建一个名字叫做pay的表，可以取名：finance_pay，就能非常轻松的区分。</span></section>
<section></section>
<section><span class="js_darkmode__43 js_darkmode__text__49">这样就不会出现同名表的情况。</span></section>
<section></section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__50">5、字段名称</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__44 js_darkmode__text__51">字段名称是开发人员发挥空间最大，但也最容易发生混乱的地方。</span></section>
<section></section>
<section><span class="js_darkmode__45 js_darkmode__text__52">比如有些表，使用flag表示状态，另外的表用status表示状态。</span></section>
<section></section>
<section><span class="js_darkmode__46 js_darkmode__text__53">可以统一一下，使用status表示状态。</span></section>
<section></section>
<section><span class="js_darkmode__47 js_darkmode__text__54">如果一个表使用了另一个表的主键，可以在另一张表的名后面，加_id或_sys_no，例如：</span></section>
<section></section>
<section><span class="js_darkmode__48 js_darkmode__text__55">在product_sku表中有个字段，是product_spu表的主键，这时候可以取名：product_spu_id或product_spu_sys_no。</span></section>
<section></section>
<section><span class="js_darkmode__49 js_darkmode__text__56">还有创建时间，可以统一成：create_time，修改时间统一成：update_time。</span></section>
<section></section>
<section><span class="js_darkmode__50 js_darkmode__text__57">删除状态固定为：delete_status。</span></section>
<section></section>
<section><span class="js_darkmode__51 js_darkmode__text__58">其实还有很多公共字段，在不同的表之间，可以使用全局统一的命名规则，定义成相同的名称，以便于大家好理解。</span></section>
<section></section>
<section>
<section>
<section></section>
<section>
<section data-brushtype="text"><strong class="js_darkmode__text__59">6、索引名</strong></section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__52 js_darkmode__text__60">在数据库中，索引有很多种，包括：主键、普通索引、唯一索引、联合索引等。</span></section>
<section></section>
<section><span class="js_darkmode__53 js_darkmode__text__61">每张表的主键只有一个，一般使用：id或者sys_no命名。</span></section>
<section></section>
<section><span class="js_darkmode__54 js_darkmode__text__62">普通索引和联合索引，其实是一类。在建立该类索引时，可以加ix_前缀，比如：ix_product_status。</span></section>
<section></section>
<section><span class="js_darkmode__55 js_darkmode__text__63">唯一索引，可以加ux_前缀，比如：ux_product_code。</span></section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__56">
<section><strong><span class="js_darkmode__text__64" data-brushtype="text">二、字段类型</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__57 js_darkmode__text__65">在设计表时，我们在选择字段类型时，可发挥空间很大。</span></section>
<section></section>
<section><span class="js_darkmode__58 js_darkmode__text__66">时间格式的数据有：date、datetime和timestamp等等可以选择。</span></section>
<section></section>
<section><span class="js_darkmode__59 js_darkmode__text__67">字符类型的数据有：varchar、char、text等可以选择。</span></section>
<section></section>
<section><span class="js_darkmode__60 js_darkmode__text__68">数字类型的数据有：int、bigint、smallint、tinyint等可以选择。</span></section>
<section></section>
<section><span class="js_darkmode__61 js_darkmode__text__69">说实话，选择很多，有时候是一件好事，也可能是一件坏事。</span></section>
<section></section>
<section><span class="js_darkmode__62 js_darkmode__text__70">如何选择一个合适的字段类型，变成了我们不得不面对的问题。</span></section>
<section></section>
<section><span class="js_darkmode__63 js_darkmode__text__71">如果字段类型选大了，比如：原本只有1-10之间的10个数字，结果选了bigint，它占8个字节。</span></section>
<section></section>
<section><span class="js_darkmode__64 js_darkmode__text__72">其实，1-10之间的10个数字，每个数字1个字节就能保存，选择tinyint更为合适。</span></section>
<section></section>
<section><span class="js_darkmode__65 js_darkmode__text__73">这样会白白浪费7个字节的空间。</span></section>
<section></section>
<section><span class="js_darkmode__66 js_darkmode__text__74">如果字段类型择小了，比如：一个18位的id字段，选择了int类型，最终数据会保存失败。</span></section>
<section></section>
<section><span class="js_darkmode__67 js_darkmode__text__75">所以选择一个合适的字段类型，还是非常重要的一件事情。</span></section>
<section></section>
<section><span class="js_darkmode__68 js_darkmode__text__76">以下原则可以参考一下：</span></section>
<section></section>
<ul class="list-paddingleft-1">
<li>
<section><span class="js_darkmode__69 js_darkmode__text__77">尽可能选择占用存储空间小的字段类型，在满足正常业务需求的情况下，从小到大，往上选。</span></section>
</li>
<li>
<section><span class="js_darkmode__70 js_darkmode__text__78">如果字符串长度固定，或者差别不大，可以选择char类型。如果字符串长度差别较大，可以选择varchar类型。</span></section>
</li>
<li>
<section><span class="js_darkmode__71 js_darkmode__text__79">是否字段，可以选择bit类型。</span></section>
</li>
<li>
<section><span class="js_darkmode__72 js_darkmode__text__80">枚举字段，可以选择tinyint类型。</span></section>
</li>
<li>
<section><span class="js_darkmode__73 js_darkmode__text__81">主键字段，可以选择bigint类型。</span></section>
</li>
<li>
<section><span class="js_darkmode__74 js_darkmode__text__82">金额字段，可以选择decimal类型。</span></section>
</li>
<li>
<section><span class="js_darkmode__75 js_darkmode__text__83">时间字段，可以选择timestamp或datetime类型。</span></section>
</li>
</ul>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__76">
<section><strong><span class="js_darkmode__text__84" data-brushtype="text">三、字段长度</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__77 js_darkmode__text__85">前面我们已经定义好了字段名称，选择了合适的字段类型，接下来，需要重点关注的是字段长度了。</span></section>
<section></section>
<section><span class="js_darkmode__78 js_darkmode__text__86">比如：varchar(20)，biginit(20)等。</span></section>
<section></section>
<section><span class="js_darkmode__79 js_darkmode__text__87">那么问题来了，varchar代表的是字节长度，还是字符长度呢？</span></section>
<section></section>
<section><span class="js_darkmode__80 js_darkmode__text__88">答：在mysql中除了varchar和char是代表字符长度之外，其余的类型都是代表字节长度。</span></section>
<section></section>
<section><span class="js_darkmode__81 js_darkmode__text__89">biginit(n) 这个n表示什么意思呢？</span></section>
<section></section>
<section><span class="js_darkmode__82 js_darkmode__text__90">假如我们定义的字段类型和长度是：bigint(4)，bigint实际长度是8个字节。</span></section>
<section></section>
<section><span class="js_darkmode__83 js_darkmode__text__91">现在有个数据a=1，a显示4个字节，所以在不满4个字节时前面填充0（前提是该字段设置了zerofill属性），比如：0001。</span></section>
<section></section>
<section><span class="js_darkmode__84 js_darkmode__text__92">当满了4个字节时，比如现在数据是a=123456，它会按照实际的长度显示，比如：123456。</span></section>
<section></section>
<section><span class="js_darkmode__85 js_darkmode__text__93">但需要注意的是，有些mysql客户端即使满了4个字节，也可能只显示4个字节的内容，比如会显示成：1234。</span></section>
<section></section>
<section><span class="js_darkmode__86 js_darkmode__text__94">所以bigint(4)，这里的4表示显示的长度为4个字节，实际长度还是占8个字节。</span></section>
<section></section>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__87">
<section><strong><span class="js_darkmode__text__95" data-brushtype="text">四、字段个数</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__88 js_darkmode__text__96">我们在建表的时候，一定要对字段个数做一些限制。</span></section>
<section></section>
<section><span class="js_darkmode__89 js_darkmode__text__97">我之前见过有人创建的表，有几十个，甚至上百个字段，表中保存的数据非常大，查询效率很低。</span></section>
<section></section>
<section><span class="js_darkmode__90 js_darkmode__text__98">如果真有这种情况，可以将一张大表拆成多张小表，这几张表的主键相同。</span></section>
<section></section>
<section><span class="js_darkmode__91 js_darkmode__text__99">建议每表的字段个数，不要超过20个。</span></section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__92">
<section><strong><span class="js_darkmode__text__100" data-brushtype="text">五、主键</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__93 js_darkmode__text__101">在创建表时，一定要创建主键。</span></section>
<section></section>
<section><span class="js_darkmode__94 js_darkmode__text__102">因为主键自带了主键索引，相比于其他索引，主键索引的查询效率最高，因为它不需要回表。</span></section>
<section></section>
<section><span class="js_darkmode__95 js_darkmode__text__103">此外，主键还是天然的唯一索引，可以根据它来判重。</span></section>
<section></section>
<section><span class="js_darkmode__96 js_darkmode__text__104">在单个数据库中，主键可以通过AUTO_INCREMENT，设置成自动增长的。</span></section>
<section></section>
<section><span class="js_darkmode__97 js_darkmode__text__105">但在分布式数据库中，特别是做了分库分表的业务库中，主键最好由外部算法(比如：雪花算法）生成，它能够保证生成的id是全局唯一的。</span></section>
<section></section>
<section><span class="js_darkmode__98 js_darkmode__text__106">除此之外，主键建议保存跟业务无关的值，减少业务耦合性，方便今后的扩展。</span></section>
<section></section>
<section><span class="js_darkmode__99 js_darkmode__text__107">不过我也见过，有些一对一的表关系，比如：用户表和用户扩展表，在保存数据时是一对一的关系。</span></section>
<section></section>
<section><span class="js_darkmode__100 js_darkmode__text__108">这样，用户扩展表的主键，可以直接保存用户表的主键。</span></section>
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__101">
<section><strong><span class="js_darkmode__text__109" data-brushtype="text">六、存储引擎</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__102 js_darkmode__text__110">在mysql5.1以前的版本，默认的存储引擎是myslam，而mysql5.1以后的版本，默认的存储引擎变成了innodb。</span></section>
<section></section>
<section><span class="js_darkmode__103 js_darkmode__text__111">之前我们还在创建表时，还一直纠结要选哪种存储引擎？</span></section>
<section></section>
<section><span class="js_darkmode__104 js_darkmode__text__112">myslam的索引和数据分开存储，而有利于查询，但它不支持事务和外键等功能。</span></section>
<section></section>
<section><span class="js_darkmode__105 js_darkmode__text__113">而innodb虽说查询性能，稍微弱一点，但它支持事务和外键等，功能更强大一些。</span></section>
<section></section>
<section><span class="js_darkmode__106 js_darkmode__text__114">以前的建议是：读多写少的表，用myslam存储引擎。而写多读多的表，用innodb。</span></section>
<section></section>
<section><span class="js_darkmode__107 js_darkmode__text__115">但虽说mysql对innodb存储引擎性能的不断优化，现在myslam和innodb查询性能相差已经越来越小。</span></section>
<section></section>
<section><span class="js_darkmode__108 js_darkmode__text__116">所以，建议我们在使用mysql8以后的版本时，直接使用默认的innodb存储引擎即可，无需额外修改存储引擎。</span></section>
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__109">
<section><strong><span class="js_darkmode__text__117" data-brushtype="text">七、NOT NULL</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__110 js_darkmode__text__118">在创建字段时，需要选择该字段是否允许为NULL。</span></section>
<section></section>
<section><span class="js_darkmode__111 js_darkmode__text__119">我们在定义字段时，应该尽可能明确该字段NOT NULL。</span></section>
<section></section>
<section><span class="js_darkmode__112 js_darkmode__text__120">为什么呢？</span></section>
<section></section>
<section><span class="js_darkmode__113 js_darkmode__text__121">我们主要以innodb存储引擎为例，myslam存储引擎没啥好说的。</span></section>
<section></section>
<section><span class="js_darkmode__114 js_darkmode__text__122">主要有以下原因：</span></section>
<ul class="list-paddingleft-1">
<li>
<section><span class="js_darkmode__115 js_darkmode__text__123">在innodb中，需要额外的空间存储null值，需要占用更多的空间。</span></section>
</li>
<li>
<section><span class="js_darkmode__116 js_darkmode__text__124">null值可能会导致索引失效。</span></section>
</li>
<li>
<section><span class="js_darkmode__117 js_darkmode__text__125">null值只能用is null或者is not null判断，用=号判断永远返回false。</span></section>
</li>
</ul>
<section><span class="js_darkmode__118">&nbsp;</span></section>
<section><span class="js_darkmode__119 js_darkmode__text__126">因此，建议我们在定义字段时，能定义成NOT NULL，就定义成NOT NULL。</span></section>
<section></section>
<section><span class="js_darkmode__120 js_darkmode__text__127">但如果某个字段直接定义成NOT NULL，万一有些地方忘了给该字段写值，就会insert不了数据。</span></section>
<section></section>
<section><span class="js_darkmode__121 js_darkmode__text__128">这也算合理的情况。</span></section>
<section></section>
<section><span class="js_darkmode__122 js_darkmode__text__129">但有一种情况是，系统有新功能上线，新增了字段。上线时一般会先执行sql脚本，再部署代码。</span></section>
<section></section>
<section><span class="js_darkmode__123 js_darkmode__text__130">由于老代码中，不会给新字段赋值，则insert数据时，也会报错。</span></section>
<section></section>
<section><span class="js_darkmode__124 js_darkmode__text__131">由此，非常有必要给NOT NULL的字段设置默认值，特别是后面新增的字段。</span></section>
<section></section>
<section><span class="js_darkmode__125 js_darkmode__text__132">例如：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__133"><span class="code-snippet__keyword js_darkmode__text__134">alter</span> <span class="code-snippet__keyword js_darkmode__text__135">table</span> product_sku <span class="code-snippet__keyword js_darkmode__text__136">add</span> <span class="code-snippet__keyword js_darkmode__text__137">column</span>  brand_id <span class="code-snippet__built_in js_darkmode__text__138">int</span>(<span class="code-snippet__number js_darkmode__text__139">10</span>) <span class="code-snippet__keyword js_darkmode__text__140">not</span> <span class="code-snippet__literal js_darkmode__text__141">null</span> <span class="code-snippet__keyword js_darkmode__text__142">default</span> <span class="code-snippet__number js_darkmode__text__143">0</span>;</span></code></pre>
</section>
<section></section>
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__127">
<section><strong><span class="js_darkmode__text__144" data-brushtype="text">八、外键</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__128 js_darkmode__text__145">在mysql中，是存在外键的。</span></section>
<section></section>
<section><span class="js_darkmode__129 js_darkmode__text__146">外键存在的主要作用是：保证数据的一致性和完整性。</span></section>
<section></section>
<section><span class="js_darkmode__130 js_darkmode__text__147">例如：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__148"><span class="code-snippet__keyword js_darkmode__text__149">create</span> <span class="code-snippet__keyword js_darkmode__text__150">table</span> <span class="code-snippet__keyword js_darkmode__text__151">class</span> (</span></code><code><span class="code-snippet_outer js_darkmode__text__152">  <span class="code-snippet__keyword js_darkmode__text__153">id</span> <span class="code-snippet__built_in js_darkmode__text__154">int</span>(<span class="code-snippet__number js_darkmode__text__155">10</span>) primary <span class="code-snippet__keyword js_darkmode__text__156">key</span> auto_increment,</span></code><code><span class="code-snippet_outer js_darkmode__text__157">  cname <span class="code-snippet__built_in js_darkmode__text__158">varchar</span>(<span class="code-snippet__number js_darkmode__text__159">15</span>)</span></code><code><span class="code-snippet_outer js_darkmode__text__160">);</span></code></pre>
</section>
<section><span class="js_darkmode__131">&nbsp;</span></section>
<section><span class="js_darkmode__132 js_darkmode__text__161">有个班级表class。</span></section>
<section></section>
<section><span class="js_darkmode__133 js_darkmode__text__162">然后有个student表：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__163"><span class="code-snippet__keyword js_darkmode__text__164">create</span> <span class="code-snippet__keyword js_darkmode__text__165">table</span> student(</span></code><code><span class="code-snippet_outer js_darkmode__text__166">  <br><span class="code-snippet__keyword js_darkmode__text__167">      id</span> <span class="code-snippet__built_in js_darkmode__text__168">int</span>(<span class="code-snippet__number js_darkmode__text__169">10</span>) primary <span class="code-snippet__keyword js_darkmode__text__170">key</span> auto_increment,</span></code><code><span class="code-snippet_outer js_darkmode__text__171"> <br>      <span class="code-snippet__keyword js_darkmode__text__172">name</span> <span class="code-snippet__built_in js_darkmode__text__173">varchar</span>(<span class="code-snippet__number js_darkmode__text__174">15</span>) <span class="code-snippet__keyword js_darkmode__text__175">not</span> <span class="code-snippet__literal js_darkmode__text__176">null</span>,</span></code><code><span class="code-snippet_outer js_darkmode__text__177">  <br>      gender <span class="code-snippet__built_in js_darkmode__text__178">varchar</span>(<span class="code-snippet__number js_darkmode__text__179">10</span>) <span class="code-snippet__keyword js_darkmode__text__180">not</span> <span class="code-snippet__literal js_darkmode__text__181">null</span>,</span></code><code><span class="code-snippet_outer js_darkmode__text__182">  <br>      cid <span class="code-snippet__built_in js_darkmode__text__183">int</span>,</span></code><code><span class="code-snippet_outer js_darkmode__text__184">  <br><span class="code-snippet__keyword js_darkmode__text__185">      foreign</span> <span class="code-snippet__keyword js_darkmode__text__186">key</span>(cid) <span class="code-snippet__keyword js_darkmode__text__187">references</span> <span class="code-snippet__keyword js_darkmode__text__188">class</span>(<span class="code-snippet__keyword js_darkmode__text__189">id</span>)<br></span></code><code><span class="code-snippet_outer js_darkmode__text__190">);</span></code></pre>
</section>
<section><span class="js_darkmode__134">&nbsp;</span></section>
<section><span class="js_darkmode__135 js_darkmode__text__191">其中student表中的cid字段，保存的class表的id，这时通过foreign key增加了一个外键。</span></section>
<section></section>
<section><span class="js_darkmode__136 js_darkmode__text__192">这时，如果你直接通过student表的id删除数据，会报异常：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="nginx"><code><span class="code-snippet_outer js_darkmode__text__193"><span class="code-snippet__attribute js_darkmode__text__194">a</span> foreign key constraint fails</span></code></pre>
</section>
<section><span class="js_darkmode__137">&nbsp;</span></section>
<section><span class="js_darkmode__138 js_darkmode__text__195">必须要先删除class表对于的cid那条数据，再删除student表的数据才行，这样能够保证数据的一致性和完整性。</span></section>
<section></section>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__139">
<section data-width="100%">
<section>
<section><span class="js_darkmode__text__196">顺便说一句：只有存储引擎是innodb时，才能使用外键。</span></section>
</section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__140 js_darkmode__text__197">如果只有两张表的关联还好，但如果有十几张表都建了外键关联，每删除一次主表，都需要同步删除十几张子表，很显然性能会非常差。</span></section>
<section></section>
<section><span class="js_darkmode__141 js_darkmode__text__198">因此，互联网系统中，一般建议不使用外键。因为这类系统更多的是为了性能考虑，宁可牺牲一点数据一致性和完整性。</span></section>
<section></section>
<section><span class="js_darkmode__142 js_darkmode__text__199">除了外键之外，存储过程和触发器也不太建议使用，他们都会影响性能。</span></section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__143">
<section><strong><span class="js_darkmode__text__200" data-brushtype="text">九、索引</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__144 js_darkmode__text__201">在建表时，除了指定主键索引之外，还需要创建一些普通索引。</span></section>
<section></section>
<section><span class="js_darkmode__145 js_darkmode__text__202">例如：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__203"><span class="code-snippet__keyword js_darkmode__text__204">create</span> <span class="code-snippet__keyword js_darkmode__text__205">table</span> product_sku(</span></code><code><span class="code-snippet_outer js_darkmode__text__206">  <br><span class="code-snippet__keyword js_darkmode__text__207">     id</span> <span class="code-snippet__built_in js_darkmode__text__208">int</span>(<span class="code-snippet__number js_darkmode__text__209">10</span>) primary <span class="code-snippet__keyword js_darkmode__text__210">key</span> auto_increment,<br>  </span></code><code><span class="code-snippet_outer js_darkmode__text__211">  spu_id <span class="code-snippet__built_in js_darkmode__text__212">int</span>(<span class="code-snippet__number js_darkmode__text__213">10</span>) <span class="code-snippet__keyword js_darkmode__text__214">not</span> <span class="code-snippet__literal js_darkmode__text__215">null</span>,<br>  </span></code><code><span class="code-snippet_outer js_darkmode__text__216">  brand_id <span class="code-snippet__built_in js_darkmode__text__217">int</span>(<span class="code-snippet__number js_darkmode__text__218">10</span>) <span class="code-snippet__keyword js_darkmode__text__219">not</span> <span class="code-snippet__literal js_darkmode__text__220">null</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__221">   <span class="code-snippet__keyword js_darkmode__text__222">name</span> <span class="code-snippet__built_in js_darkmode__text__223">varchar</span>(<span class="code-snippet__number js_darkmode__text__224">15</span>) <span class="code-snippet__keyword js_darkmode__text__225">not</span> <span class="code-snippet__literal js_darkmode__text__226">null<br></span></span></code><code><span class="code-snippet_outer js_darkmode__text__227">);</span></code></pre>
</section>
<section><span class="js_darkmode__146">&nbsp;</span></section>
<section><span class="js_darkmode__147 js_darkmode__text__228">在创建商品表时，使用spu_id（商品组表）和brand_id（品牌表）的id。</span></section>
<section></section>
<section><span class="js_darkmode__148 js_darkmode__text__229">像这类保存其他表id的情况，可以增加普通索引：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__230"><span class="code-snippet__keyword js_darkmode__text__231">create</span> <span class="code-snippet__keyword js_darkmode__text__232">table</span> product_sku (<br></span></code><code><span class="code-snippet_outer js_darkmode__text__233">  <span class="code-snippet__keyword js_darkmode__text__234">id</span> <span class="code-snippet__built_in js_darkmode__text__235">int</span>(<span class="code-snippet__number js_darkmode__text__236">10</span>) primary <span class="code-snippet__keyword js_darkmode__text__237">key</span> auto_increment,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__238">  spu_id <span class="code-snippet__built_in js_darkmode__text__239">int</span>(<span class="code-snippet__number js_darkmode__text__240">10</span>) <span class="code-snippet__keyword js_darkmode__text__241">not</span> <span class="code-snippet__literal js_darkmode__text__242">null</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__243">  brand_id <span class="code-snippet__built_in js_darkmode__text__244">int</span>(<span class="code-snippet__number js_darkmode__text__245">10</span>) <span class="code-snippet__keyword js_darkmode__text__246">not</span> <span class="code-snippet__literal js_darkmode__text__247">null</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__248">  <span class="code-snippet__keyword js_darkmode__text__249">name</span> <span class="code-snippet__built_in js_darkmode__text__250">varchar</span>(<span class="code-snippet__number js_darkmode__text__251">15</span>) <span class="code-snippet__keyword js_darkmode__text__252">not</span> <span class="code-snippet__literal js_darkmode__text__253">null</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__254">  <span class="code-snippet__keyword js_darkmode__text__255">KEY</span> <span class="code-snippet__string js_darkmode__text__256">`ix_spu_id`</span> (<span class="code-snippet__string js_darkmode__text__257">`spu_id`</span>) <span class="code-snippet__keyword js_darkmode__text__258">USING</span> BTREE,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__259">  <span class="code-snippet__keyword js_darkmode__text__260">KEY</span> <span class="code-snippet__string js_darkmode__text__261">`ix_brand_id`</span> (<span class="code-snippet__string js_darkmode__text__262">`brand_id`</span>) <span class="code-snippet__keyword js_darkmode__text__263">USING</span> BTREE<br></span></code><code><span class="code-snippet_outer js_darkmode__text__264">);</span></code></pre>
</section>
<section><span class="js_darkmode__149">&nbsp;</span></section>
<section><span class="js_darkmode__150 js_darkmode__text__265">后面查表的时候，效率更高。</span></section>
<section></section>
<section><span class="js_darkmode__151 js_darkmode__text__266">但索引字段也不能建的太多，可能会影响保存数据的效率，因为索引需要额外的存储空间。</span></section>
<section></section>
<section><span class="js_darkmode__152 js_darkmode__text__267">建议单表的索引个数不要超过：5个。</span></section>
<section></section>
<section><span class="js_darkmode__153 js_darkmode__text__268">如果在建表时，发现索引个数超过5个了，可以删除部分普通索引，改成联合索引。</span></section>
<section></section>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__154">
<section data-width="100%">
<section>
<section><span class="js_darkmode__text__269">顺便说一句：在创建联合索引的时候，需要使用注意最左匹配原则，不然，建的联合索引效率可能不高。</span></section>
</section>
</section>
</section>
</section>
<section></section>
<section><span class="js_darkmode__155 js_darkmode__text__270">对于数据重复率非常高的字段，比如：状态，不建议单独创建普通索引。因为即使加了索引，如果mysql发现全表扫描效率更高，可能会导致索引失效。</span></section>
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__156">
<section><strong><span class="js_darkmode__text__271" data-brushtype="text">十、时间字段</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__157 js_darkmode__text__272">时间字段的类型，我们可以选择的范围还是比较多的，目前mysql支持：date、datetime、timestamp、varchar等。</span></section>
<section></section>
<section><span class="js_darkmode__158 js_darkmode__text__273">varchar类型可能是为了跟接口保持一致，接口中的时间类型是String。</span></section>
<section></section>
<section><span class="js_darkmode__159 js_darkmode__text__274">但如果哪天我们要通过时间范围查询数据，效率会非常低，因为这种情况没法走索引。</span></section>
<section></section>
<section><span class="js_darkmode__160 js_darkmode__text__275">date类型主要是为了保存日期，比如：2020-08-20，不适合保存日期和时间，比如：2020-08-20 12:12:20。</span></section>
<section></section>
<section><span class="js_darkmode__161 js_darkmode__text__276">而datetime和timestamp类型更适合我们保存日期和时间。</span></section>
<section></section>
<section><span class="js_darkmode__162 js_darkmode__text__277">但它们有略微区别。</span></section>
<section></section>
<ul class="list-paddingleft-1">
<li>
<section><span class="js_darkmode__163 js_darkmode__text__278">timestamp：用4个字节来保存数据，它的取值范围为1970-01-01 00:00:01 UTC ~ 2038-01-19 03:14:07。此外，它还跟时区有关。</span></section>
<section></section>
</li>
<li>
<section><span class="js_darkmode__164 js_darkmode__text__279">datetime：用8个字节来保存数据，它的取值范围为1000-01-01 00:00:00 ~ 9999-12-31 23:59:59。它跟时区无关。</span></section>
</li>
</ul>
<section></section>
<section><span class="js_darkmode__165 js_darkmode__text__280">优先推荐使用datetime类型保存日期和时间，可以保存的时间范围更大一些。</span></section>
<section></section>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__166">
<section data-width="100%">
<section>
<section><span class="js_darkmode__text__281">温馨提醒一下，在给时间字段设置默认值是，建议不要设置成：0000-00-00 00:00:00，不然查询表时可能会因为转换不了，而直接报错。</span></section>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__167">
<section><strong><span class="js_darkmode__text__282" data-brushtype="text">十一、金额字段</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__168 js_darkmode__text__283">mysql中有多个字段可以表示浮点数：float、double、decimal等。</span></section>
<section></section>
<section><span class="js_darkmode__169 js_darkmode__text__284">而float和double可能会丢失精度，因此推荐大家使用decimal类型保存金额。</span></section>
<section></section>
<section><span class="js_darkmode__170 js_darkmode__text__285">一般我们是这样定义浮点数的：decimal(m,n)。</span></section>
<section></section>
<section><span class="js_darkmode__171 js_darkmode__text__286">其中n是指小数的长度，而m是指整数加小数的总长度。</span></section>
<section></section>
<section><span class="js_darkmode__172 js_darkmode__text__287">假如我们定义的金额类型是这样的：decimal(10,2)，则表示整数长度是8位，并且保留2位小数。</span></section>
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__173">
<section><strong><span class="js_darkmode__text__288" data-brushtype="text">十二、唯一索引</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__174 js_darkmode__text__289">唯一索引在我们实际工作中，使用频率相当高。</span></section>
<section></section>
<section><span class="js_darkmode__175 js_darkmode__text__290">你可以给单个字段，加唯一索引，比如：组织机构code。</span></section>
<section></section>
<section><span class="js_darkmode__176 js_darkmode__text__291">也可以给多个字段，加一个联合的唯一索引，比如：分类编号、单位、规格等。</span></section>
<section></section>
<section><span class="js_darkmode__177 js_darkmode__text__292">单个的唯一索引还好，但如果是联合的唯一索引，字段值出现null时，则唯一性约束可能会失效。</span></section>
<section></section>
</section>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__178">
<section data-width="100%">
<section>
<section><span class="js_darkmode__text__293">创建唯一索引时，相关字段一定不能包含null值，否则唯一性会失效。</span></section>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section draggable="true" data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__179">
<section><strong><span class="js_darkmode__text__294" data-brushtype="text">十三、字符集</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__180 js_darkmode__text__295">mysql中支持的字符集有很多，常用的有：latin1、utf-8、utf8mb4、GBK等。</span></section>
<section></section>
<section><span class="js_darkmode__181 js_darkmode__text__296">这4种字符集情况如下：</span></section>
<section><span class="js_darkmode__182">&nbsp;</span></section>
<section>
<p><a href="/content/uploadfile/202503/d2b51742543033.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742543033.png" alt="image.png"></a></p>
</section>
<section></section>
<section><span class="js_darkmode__184 js_darkmode__text__297">latin1容易出现乱码问题，在实际项目中使用比较少。</span></section>
<section></section>
<section><span class="js_darkmode__185 js_darkmode__text__298">而GBK支持中文，但不支持国际通用字符，在实际项目中使用也不多。</span></section>
<section></section>
<section><span class="js_darkmode__186 js_darkmode__text__299">从目前来看，mysql的字符集使用最多的还是：utf-8和utf8mb4。</span></section>
<section></section>
<section><span class="js_darkmode__187 js_darkmode__text__300">其中utf-8占用3个字节，比utf8mb4的4个字节，占用更小的存储空间。</span></section>
<section></section>
<section><span class="js_darkmode__188 js_darkmode__text__301">但utf-8有个问题：即无法存储emoji表情，因为emoji表情一般需要4个字节。</span></section>
<section></section>
<section><span class="js_darkmode__189 js_darkmode__text__302">由此，使用utf-8字符集，保存emoji表情时，数据库会直接报错。</span></section>
<section></section>
<section><span class="js_darkmode__190 js_darkmode__text__303">所以，建议在建表时字符集设置成：utf8mb4，会省去很多不必要的麻烦。</span></section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__191">
<section><strong><span class="js_darkmode__text__304" data-brushtype="text">十四、排序规则</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__192 js_darkmode__text__305">不知道，你关注过没，在mysql中创建表时，有个COLLATE参数可以设置。</span></section>
<section></section>
<section><span class="js_darkmode__193 js_darkmode__text__306">例如：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__307"><span class="code-snippet__keyword js_darkmode__text__308">CREATE</span> <span class="code-snippet__keyword js_darkmode__text__309">TABLE</span> <span class="code-snippet__string js_darkmode__text__310">`order`</span> (<br></span></code><code><span class="code-snippet_outer js_darkmode__text__311">  <span class="code-snippet__string js_darkmode__text__312">`id`</span> <span class="code-snippet__built_in js_darkmode__text__313">bigint</span> <span class="code-snippet__keyword js_darkmode__text__314">NOT</span> <span class="code-snippet__literal js_darkmode__text__315">NULL</span> AUTO_INCREMENT,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__316">  <span class="code-snippet__string js_darkmode__text__317">`code`</span> <span class="code-snippet__built_in js_darkmode__text__318">varchar</span>(<span class="code-snippet__number js_darkmode__text__319">20</span>) <span class="code-snippet__keyword js_darkmode__text__320">COLLATE</span> utf8mb4_bin <span class="code-snippet__keyword js_darkmode__text__321">NOT</span> <span class="code-snippet__literal js_darkmode__text__322">NULL</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__323">  <span class="code-snippet__string js_darkmode__text__324">`name`</span> <span class="code-snippet__built_in js_darkmode__text__325">varchar</span>(<span class="code-snippet__number js_darkmode__text__326">30</span>) <span class="code-snippet__keyword js_darkmode__text__327">COLLATE</span> utf8mb4_bin <span class="code-snippet__keyword js_darkmode__text__328">NOT</span> <span class="code-snippet__literal js_darkmode__text__329">NULL</span>,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__330">  PRIMARY <span class="code-snippet__keyword js_darkmode__text__331">KEY</span> (<span class="code-snippet__string js_darkmode__text__332">`id`</span>),</span></code><code><span class="code-snippet_outer js_darkmode__text__333">  <span class="code-snippet__keyword js_darkmode__text__334">UNIQUE</span> <span class="code-snippet__keyword js_darkmode__text__335">KEY</span> <span class="code-snippet__string js_darkmode__text__336">`un_code`</span> (<span class="code-snippet__string js_darkmode__text__337">`code`</span>),<br></span></code><code><span class="code-snippet_outer js_darkmode__text__338">  <span class="code-snippet__keyword js_darkmode__text__339">KEY</span> <span class="code-snippet__string js_darkmode__text__340">`un_code_name`</span> (<span class="code-snippet__string js_darkmode__text__341">`code`</span>,<span class="code-snippet__string js_darkmode__text__342">`name`</span>) <span class="code-snippet__keyword js_darkmode__text__343">USING</span> BTREE,<br></span></code><code><span class="code-snippet_outer js_darkmode__text__344">  <span class="code-snippet__keyword js_darkmode__text__345">KEY</span> <span class="code-snippet__string js_darkmode__text__346">`idx_name`</span> (<span class="code-snippet__string js_darkmode__text__347">`name`</span>)<br></span></code><code><span class="code-snippet_outer js_darkmode__text__348">) <span class="code-snippet__keyword js_darkmode__text__349">ENGINE</span>=<span class="code-snippet__keyword js_darkmode__text__350">InnoDB</span> AUTO_INCREMENT=<span class="code-snippet__number js_darkmode__text__351">5</span> <span class="code-snippet__keyword js_darkmode__text__352">DEFAULT</span> <span class="code-snippet__keyword js_darkmode__text__353">CHARSET</span>=utf8mb4 <span class="code-snippet__keyword js_darkmode__text__354">COLLATE</span>=utf8mb4_bin</span></code></pre>
</section>
<section><span class="js_darkmode__194">&nbsp;</span></section>
<section><span class="js_darkmode__195 js_darkmode__text__355">它是用来设置排序规则的。</span></section>
<section></section>
<section><span class="js_darkmode__196 js_darkmode__text__356">字符排序规则跟字符集有关，比如：字符集如果是utf8mb4，则字符排序规则也是以：utf8mb4_开头的，常用的有：utf8mb4_general_ci、utf8mb4_bin等。</span></section>
<section></section>
<section><span class="js_darkmode__197 js_darkmode__text__357">其中utf8mb4_general_ci排序规则，对字母的大小写不敏感。说得更直白一点，就是不区分大小写。</span></section>
<section></section>
<section><span class="js_darkmode__198 js_darkmode__text__358">而utf8mb4_bin排序规则，对字符大小写敏感，也就是区分大小写。</span></section>
<section></section>
<section><span class="js_darkmode__199 js_darkmode__text__359">说实话，这一点还是非常重要的。</span></section>
<section></section>
<section><span class="js_darkmode__200 js_darkmode__text__360">假如order表中现在有一条记录，name的值是大写的YOYO，但我们用小写的yoyo去查，例如：</span></section>
<section class="code-snippet__fix code-snippet__js">
<pre class="code-snippet__js" data-lang="sql"><code><span class="code-snippet_outer js_darkmode__text__361"><span class="code-snippet__keyword js_darkmode__text__362">select</span> * <span class="code-snippet__keyword js_darkmode__text__363">from</span> <span class="code-snippet__keyword js_darkmode__text__364">order</span> <span class="code-snippet__keyword js_darkmode__text__365">where</span> <span class="code-snippet__keyword js_darkmode__text__366">name</span>=<span class="code-snippet__string js_darkmode__text__367">'yoyo'</span>;</span></code></pre>
</section>
<section><span class="js_darkmode__201">&nbsp;</span></section>
<section><span class="js_darkmode__202 js_darkmode__text__368">如果字符排序规则是utf8mb4_general_ci，则可以查出大写的YOYO的那条数据。</span></section>
<section></section>
<section><span class="js_darkmode__203 js_darkmode__text__369">如果字符排序规则是utf8mb4_bin，则查不出来。</span></section>
<section></section>
<section><span class="js_darkmode__204 js_darkmode__text__370">由此，字符排序规则一定要根据实际的业务场景选择，否则容易出现问题。</span></section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86318" data-color="#138bde" data-custom="#138bde">
<section>
<section class="js_darkmode__205">
<section><strong><span class="js_darkmode__text__371" data-brushtype="text">十五、大字段</span></strong></section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section><span class="js_darkmode__206 js_darkmode__text__372">我们在创建表时，对一些特殊字段，要额外关注，比如：大字段，即占用较多存储空间的字段。</span></section>
<section></section>
<section><span class="js_darkmode__207 js_darkmode__text__373">比如：用户的评论，这就属于一个大字段，但这个字段可长可短。</span></section>
<section></section>
<section><span class="js_darkmode__208 js_darkmode__text__374">但一般会对评论的总长度做限制，比如：最多允许输入500个字符。</span></section>
<section></section>
<section><span class="js_darkmode__209 js_darkmode__text__375">如果直接定义成text类型，可能会浪费存储空间，所以建议将这类字段定义成varchar类型的存储效率更高。</span></section>
<section></section>
<section><span class="js_darkmode__210 js_darkmode__text__376">当然，我还见过更大的字段，即该字段直接保存合同数据。</span></section>
<section></section>
<section><span class="js_darkmode__211 js_darkmode__text__377">一个合同可能会占几Mb。</span></section>
<section></section>
<section><span class="js_darkmode__212 js_darkmode__text__378">在mysql中保存这种数据，从系统设计的角度来说，本身就不太合理。</span></section>
<section></section>
<section><span class="js_darkmode__213 js_darkmode__text__379">像合同这种非常大的数据，可以保存到mongodb中，然后在mysql的业务表中，保存mongodb表的id。</span></section>
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section class="js_darkmode__214"><span class="js_darkmode__text__380">作者丨苏三呀</span></section>
</section>]]></description>
    <pubDate>Fri, 21 Mar 2025 15:39:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/71.html</guid>
</item>
<item>
    <title>第一次把 https 原理讲得那么清楚</title>
    <link>https://blog.xuhaobo.cn/private/70.html</link>
    <description><![CDATA[<section data-role="paragraph">
<p><span class="js_darkmode__1 js_darkmode__text__0">有位朋友校招面试了字节的后端岗位，问到这道面试题：</span><strong><span class="js_darkmode__2 js_darkmode__text__1">https 原理。</span></strong></p>
<p>&nbsp;</p>
<p><span class="js_darkmode__3 js_darkmode__text__2">这道题其实比较简单，我们如何更好地回答呢？我来跟大家聊聊。可以从这几个维度逐层扩展来讲</span></p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__4 js_darkmode__text__3">http为什么不安全？</span></p>
</li>
<li>
<p><span class="js_darkmode__5 js_darkmode__text__4">对称算法加密+HTTP</span></p>
</li>
<li>
<p><span class="js_darkmode__6 js_darkmode__text__5">非对称加密+HTTP</span></p>
</li>
<li>
<p><span class="js_darkmode__7 js_darkmode__text__6">非对称加密+对称加密+HTTP</span></p>
</li>
<li>
<p><span class="js_darkmode__8 js_darkmode__text__7">数字签名，给你的公钥盖个章</span></p>
</li>
<li>
<p><span class="js_darkmode__9 js_darkmode__text__8">完整的HTTPS运行流程图</span></p>
</li>
</ul>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__9">一、http为什么不安全？</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<p><span class="js_darkmode__10 js_darkmode__text__10">我们先来看看HTTP传输：</span></p>
<p>&nbsp;</p>
<p><a href="/content/uploadfile/202503/d2b51742541916.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541916.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__12 js_darkmode__text__11">客户端，把一条消息，以</span><strong><span class="js_darkmode__13 js_darkmode__text__12">明</span></strong><strong><span class="js_darkmode__14 js_darkmode__text__13">文</span></strong><span class="js_darkmode__15 js_darkmode__text__14">的方式，发送到服务器。</span></p>
</li>
<li>
<p><span class="js_darkmode__16 js_darkmode__text__15">服务的响应报文，也是以</span><strong><span class="js_darkmode__17 js_darkmode__text__16">明文</span></strong><span class="js_darkmode__18 js_darkmode__text__17">的方式，发回给客户端。</span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><span class="js_darkmode__19 js_darkmode__text__18">Http 明文传输，主要有这些缺</span><span class="js_darkmode__20 js_darkmode__text__19">点：</span><strong><span class="js_darkmode__21 js_darkmode__text__20">窃听风险、篡改风险、冒充风险</span></strong></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__22">
<section data-width="100%">
<section>
<p><span class="js_darkmode__text__21">请君试想，如果HTTP请求被某个不怀好意的中间人窃听截取，并且，消息包含银行密码等敏感信息的话，造成的后果不堪设想吧。</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__22">二、对称算法加密+HTTP</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<p><span class="js_darkmode__23 js_darkmode__text__23">既然，明文传输不安全，那我们加密是不是就可以了。比如用对称</span><strong><span class="js_darkmode__24 js_darkmode__text__24">加密算法加密</span></strong><span class="js_darkmode__25 js_darkmode__text__25">。</span></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__26">
<section data-width="100%">
<section>
<p><strong><span class="js_darkmode__27 js_darkmode__text__26">对称加密算法：</span></strong><span class="js_darkmode__text__27">需要对</span><strong><span class="js_darkmode__28 js_darkmode__text__28">加密和解密</span></strong><span class="js_darkmode__text__29">使用相同密钥的加密算法。由于其速度快，对称性加密通常在消息发送方需要加密大量数据时使用。对称性加密也称为密钥加密。</span></p>
<p>&nbsp;</p>
<p class="js_darkmode__29"><strong><span class="js_darkmode__30 js_darkmode__text__30">常见的对称加密算法有：</span></strong><span class="js_darkmode__text__31">DES、3DES、AES</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
<p><span class="js_darkmode__31 js_darkmode__text__32">看看用对称算法加密后，HTTP流程又是怎样的：</span></p>
<p>&nbsp;</p>
<p><a href="/content/uploadfile/202503/d2b51742541930.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541930.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__33 js_darkmode__text__33">客户端把要发送的消息，用密钥加密成密文。</span></p>
</li>
<li>
<p><span class="js_darkmode__34 js_darkmode__text__34">客户端把密文发送到服务器。</span></p>
</li>
<li>
<p><span class="js_darkmode__35 js_darkmode__text__35">服务器收到密文消息，用同一把密钥把密文解密成明文。</span></p>
</li>
<li>
<p><span class="js_darkmode__36 js_darkmode__text__36">同理，服务端把消息报文返回给客户端，处理过程类似。</span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><strong><span class="js_darkmode__37 js_darkmode__text__37">这种方式还是有问题：</span></strong></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__38">
<section data-width="100%">
<section>
<p><span class="js_darkmode__text__38">一开始客户端怎么把密钥发过去呢？如果</span><strong><span class="js_darkmode__39 js_darkmode__text__39">不怀好意的中间人截取到了密钥</span></strong><span class="js_darkmode__text__40">，发送的消息还是会被盗取和利用呢。</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__41">三、非对称加密+HTTP</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<p><span class="js_darkmode__40 js_darkmode__text__42">既然对称算法加密+HTTP 还是不够安全，那我们用非对称加密+HTTP。</span></p>
<p>&nbsp;</p>
<p><strong><span class="js_darkmode__41 js_darkmode__text__43">什么是非对称加密算法？</span></strong></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__42">
<section data-width="100%">
<section>
<p><strong><span class="js_darkmode__43 js_darkmode__text__44">非对称加密算法需要两个密钥：</span></strong><span class="js_darkmode__text__45">公开密钥和私有密钥。公开密钥与私有密钥是一对，如果用公开密钥对数据进行加密，只有用对应的私有密钥才能解密；如果用私有密钥对数据进行加密，那么只有用对应的公开密钥才能解密。</span><strong><span class="js_darkmode__44 js_darkmode__text__46">因为加密和解密使用的是两个不同的密钥，所以这种算法叫作非对称加密算法。</span></strong></p>
<p><strong><span class="js_darkmode__45">&nbsp;</span></strong></p>
<p class="js_darkmode__46"><span class="js_darkmode__text__47">常用非对称加密算法：RSA、ECC</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
<p><span class="js_darkmode__47 js_darkmode__text__48">使用非对称加密算法之后，HTTP流程又是怎样的，如下图：</span></p>
<p>&nbsp;</p>
<p><a href="/content/uploadfile/202503/d2b51742541956.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541956.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__49 js_darkmode__text__49">客户端向服务器发起HTTP请求</span></p>
</li>
<li>
<p><span class="js_darkmode__50 js_darkmode__text__50">服务端将自己的公钥返回到客户端。</span></p>
</li>
<li>
<p><span class="js_darkmode__51 js_darkmode__text__51">客户端使用返回的公钥，给要发送的消息加密。</span></p>
</li>
<li>
<p><span class="js_darkmode__52 js_darkmode__text__52">客户端将加密之后的消息密文发送给服务器。</span></p>
</li>
<li>
<p><span class="js_darkmode__53 js_darkmode__text__53">服务器接收到客户端发来的密文之后，会用自己的私钥对其进行非对称解密，解密之后得到消息数据。</span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><strong><span class="js_darkmode__54 js_darkmode__text__54">这种方式依然有问题:</span></strong></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__55">
<section data-width="100%">
<section>
<p><span class="js_darkmode__text__55">非对称算法（如：RSA）有个弊端，它很慢。试想一下，如果你用浏览器请求，它很久才响应，你能接受吗？</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__56">四、非对称加密+对称加密+HTTP</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<p><span class="js_darkmode__56 js_darkmode__text__57">既然非对称加密慢。对称加密会快好多。我们可以使用非对称加密+对称加密双剑合璧的流程图如下：</span></p>
<p>&nbsp;</p>
<p><a href="/content/uploadfile/202503/d2b51742541969.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541969.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__58 js_darkmode__text__58">客户端向服务器发起HTTP请求</span></p>
</li>
<li>
<p><span class="js_darkmode__59 js_darkmode__text__59">服务端将自己的公钥返回到客户端。</span></p>
</li>
<li>
<p><span class="js_darkmode__60 js_darkmode__text__60">客户端产生对称加密密钥，并用服务端返回的公钥对它加密</span></p>
</li>
<li>
<p><span class="js_darkmode__61 js_darkmode__text__61">客户端会发起第二个HTTP请求，将加密之后的客户端密钥发送给服务器。</span></p>
</li>
<li>
<p><span class="js_darkmode__62 js_darkmode__text__62">服务器接收到客户端发来的密文之后，会用自己的私钥对其进行非对称解密，解密之后得到客户端密钥，然后用客户端密钥对返回数据进行对称加密，这样数据就变成了密文。</span></p>
</li>
<li>
<p><span class="js_darkmode__63 js_darkmode__text__63">服务器将加密后的密文返回给客户端。</span></p>
</li>
<li>
<p><span class="js_darkmode__64 js_darkmode__text__64">客户端收到服务器发返回的密文，用自己的密钥（客户端密钥）对其进行对称解密，得到服务器返回的数据。</span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><span class="js_darkmode__65 js_darkmode__text__65">这种方式还是有问题，</span><strong><span class="js_darkmode__66 js_darkmode__text__66">没完没了了&hellip;&hellip;</span></strong></p>
<p>&nbsp;</p>
<section data-role="paragraph" data-color="#138bde" data-custom="#138bde">
<section class="js_darkmode__67">
<section data-width="100%">
<section>
<p><span class="js_darkmode__text__67">如果中间人又来搞事情呢？要是中间人截取公钥，把公钥进行篡改呢? 打个比喻，把公钥比喻你自己的手机号，它是公开的，谁都可以给你打电话。假设你发消息给你朋友，告诉他你的手机号，然后消息被中间人截取修改了，改为110，然后你朋友不知情的情况下，拨通110，打电话给你&hellip;&hellip;</span></p>
</section>
</section>
</section>
</section>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__68">五、数字签名，给你的公钥盖个章</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph">
<p>&nbsp;</p>
<p><span class="js_darkmode__68 js_darkmode__text__69">为了避免公钥被篡改，引入了数字证书，如图所下：</span></p>
<p><a href="/content/uploadfile/202503/d2b51742541980.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541980.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p><span class="js_darkmode__70 js_darkmode__text__70">公钥和个人信息，经过Hash算法加密，形成消息摘要；将消息摘要拿到拥有公信力的认证中心（CA），用它的私钥对消息摘要加密，形成数字签名.</span></p>
<p><span class="js_darkmode__71 js_darkmode__text__71">公钥和个人信息、数字签名共同构成数字证书。</span></p>
<p><span class="js_darkmode__72">&nbsp;</span></p>
<p><strong><span class="js_darkmode__73 js_darkmode__text__72">&nbsp;证书验证</span></strong></p>
<p><a href="/content/uploadfile/202503/d2b51742541991.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742541991.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__76 js_darkmode__text__73">拿到数字证书之后，用同样的Hash算法， 先再次生成消息摘要；</span></p>
</li>
<li>
<p><span class="js_darkmode__77 js_darkmode__text__74">然后用CA的公钥对数字签名解密， 得到CA创建的消息摘要， 两者对比，就知道有没有人篡改了。</span></p>
</li>
</ul>
<p>&nbsp;</p>
</section>
<section data-tools="135编辑器" data-id="86122" data-color="#138bde" data-custom="#138bde">
<section>
<section>
<section>
<p data-brushtype="text"><strong class="js_darkmode__text__75">六、完整的HTTPS运行流程图</strong></p>
</section>
</section>
</section>
</section>
<section data-role="paragraph"></section>
<section data-role="paragraph">
<p><a href="/content/uploadfile/202503/d2b51742542001.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202503/d2b51742542001.png" alt="image.png"></a></p>
<p>&nbsp;</p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__79 js_darkmode__text__76">用户在浏览器里输入一个https网址，然后连接到server的443端口。</span></p>
</li>
<li>
<p><span class="js_darkmode__80 js_darkmode__text__77">服务器必须要有一套</span><strong><span class="js_darkmode__81 js_darkmode__text__78">数字证书</span></strong><span class="js_darkmode__82 js_darkmode__text__79">，可以自己制作，也可以向组织申请，区别就是自己颁发的证书需要客户端验证通过。这套证书其实就是一对公钥和私钥。</span></p>
</li>
<li>
<p><span class="js_darkmode__83 js_darkmode__text__80">服务器将自己的数字证书（含有公钥）发送给客户端。</span></p>
</li>
<li>
<p><span class="js_darkmode__84 js_darkmode__text__81">客户端收到服务器端的数字证书之后，会对其进行检查，如果不通过，则弹出警告框。如果证书没问题，则生成一个密钥（对称加密），用证书的公钥对它加密。</span></p>
</li>
<li>
<p><span class="js_darkmode__85 js_darkmode__text__82">客户端会发起HTTPS中的第二个HTTP请求，将加密之后的客户端密钥发送给服务器。</span></p>
</li>
<li>
<p><span class="js_darkmode__86 js_darkmode__text__83">服务器接收到客户端发来的密文之后，会用自己的私钥对其进行非对称解密，解密之后得到客户端密钥，然后用客户端密钥对返回数据进行对称加密，这样数据就变成了密文。</span></p>
</li>
<li>
<p><span class="js_darkmode__87 js_darkmode__text__84">服务器将加密后的密文返回给客户端。</span></p>
</li>
<li>
<p><span class="js_darkmode__88 js_darkmode__text__85">客户端收到服务器发返回的密文，用自己的密钥（客户端密钥）对其进行对称解密，得到服务器返回的数据。</span></p>
</li>
</ul>
<p>&nbsp;</p>
</section>
<section data-role="paragraph">
<section class="js_darkmode__89">
<section><span class="js_darkmode__text__86">作者丨捡田螺的小男孩</span></section>
<section><span class="js_darkmode__text__87">来源丨公众号：捡田螺的小男孩（ID：gh_3d11c9893ca0）</span></section>
</section>
</section>]]></description>
    <pubDate>Fri, 21 Mar 2025 15:23:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/70.html</guid>
</item>
<item>
    <title>ai 已经能编出很完美的程序，程序员这个行业以后是不是会消失？</title>
    <link>https://blog.xuhaobo.cn/private/69.html</link>
    <description><![CDATA[<p><strong>Caption1&nbsp; 用结果说话</strong></p>
<hr>
<p data-first-child="" data-pid="bDcCvfol"><a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717554435&amp;content_type=Answer&amp;match_order=1&amp;q=ai&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">ai</a>已经能编出很完美的程序了。</p>
<p data-pid="tvtJ3AxN">那世面上一定有很多纯ai开发的<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717554435&amp;content_type=Answer&amp;match_order=1&amp;q=App&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">App</a>了吧？</p>
<p data-pid="-4t6xCTR">你一定ai做了很多成功大项目赚了不少钱吧？</p>
<p data-pid="_8m9-8Ui">你一定做出了自己想要的软件了，不用再买激活码之类的智商税了吧？</p>
<p data-pid="_IZsr7vw">你毕设一定一天就搞完，从软件到论文分分钟搞定，不用在网上哭爹喊娘求代做了吧？</p>
<p data-pid="pdnW_FYb">你<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717554435&amp;content_type=Answer&amp;match_order=1&amp;q=CS%E8%AF%BE&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">CS课</a>后大作业一定科科满分，学分绩点那到手软了吧？</p>
<p data-pid="6IzODFFF">对吧？是这样吧？我没理解错吧？</p>
<p><hr><hr></p>
<p data-pid="6IzODFFF"><strong>Caption2 从哲学角度看</strong></p>
<hr>
<div>
<div>
<p data-first-child="" data-pid="aRWDRrw0">这是来自于非行业人的想象，看着一会就根据你的描述生成一串眼花缭乱的代码，让你觉得比人厉害多了，实际上你如果做过项目，并用过ai，就会明白，目前ai仍是辅助工具，只是越来越强，都在调侃程序员是<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717268939&amp;content_type=Answer&amp;match_order=1&amp;q=cv%E5%B7%A5%E7%A8%8B%E5%B8%88&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">cv工程师</a>，能<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717268939&amp;content_type=Answer&amp;match_order=1&amp;q=cv%E5%AE%8C%E6%88%90%E4%BB%BB%E5%8A%A1&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">cv完成任务</a>说明解决了三个问题，从哪找，在哪改，往哪贴。</p>
<p data-pid="NeTdVqOz">ai目前让&ldquo;从哪找&rdquo;和&ldquo;在哪改&rdquo;这两个问题变的更简单，&ldquo;往哪贴&rdquo;还没解决。不用担心程序员会消失，如果ai能代替多数程序员的工作，那在这之前多数办公室工作已经被替代了。</p>
</div>
<hr><hr>&nbsp; <strong>&nbsp;Caption3 引经据典</strong><hr>
<div>
<div>
<p data-first-child="" data-pid="xlzWrqpE">James Martin 在 1982 年出了一本书，叫<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=%E3%80%8AApplication+Development+without+Programmers%E3%80%8B&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">《Application Development without Programmers》</a>（没有程序员的应用程序开发）。</p>
<blockquote data-pid="kfsbBTJR">这个 James Martin 是英国<span class="nolink">信息技术顾问和作家，以其在</span><span class="nolink">信息技术工程方面</span>的工作而闻名。</blockquote>
<figure data-size="normal"><noscript>&amp;amp;lt;img class="origin_image zh-lightbox-thumb" src="https://picx.zhimg.com/50/v2-419cd9c3e0b62261eff576066b622e1a_720w.jpg?source=1def8aca" width="679" data-caption="" data-size="normal" data-rawwidth="679" data-rawheight="455" data-original-token="v2-c16413475f9096e9fafa9ddab75f3030" data-default-watermark-src="https://pic1.zhimg.com/50/v2-69d7b743d264485ad2e748eb7a056122_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-419cd9c3e0b62261eff576066b622e1a_r.jpg?source=1def8aca" data-mce-src="https://picx.zhimg.com/50/v2-419cd9c3e0b62261eff576066b622e1a_720w.jpg?source=1def8aca" data-mce-fragment="1"&amp;amp;gt;</noscript>
<div><img class="origin_image zh-lightbox-thumb lazy" src="https://picx.zhimg.com/80/v2-419cd9c3e0b62261eff576066b622e1a_720w.webp?source=1def8aca" width="679" height="455" data-caption="" data-size="normal" data-rawwidth="679" data-rawheight="455" data-original-token="v2-c16413475f9096e9fafa9ddab75f3030" data-default-watermark-src="https://pic1.zhimg.com/50/v2-69d7b743d264485ad2e748eb7a056122_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-419cd9c3e0b62261eff576066b622e1a_r.jpg?source=1def8aca" data-actualsrc="https://picx.zhimg.com/50/v2-419cd9c3e0b62261eff576066b622e1a_720w.jpg?source=1def8aca" data-lazy-status="ok"></div>
</figure>
<p data-pid="09YiIq10">这本书中正式提到了一个术语：A fourth-generation programming language(4GL)，第四代编程语言，也被称为 &ldquo;非过程式&rdquo; 或 &ldquo;程序生成式&rdquo; 语言，</p>
<p data-pid="-P4NafRD">4GL 的目的在于提供更高级别的抽象，以便让编程语言对程序员更加友好、功能更加强大、用途更加广泛 ------ 说人话，就是少写（最好不用写）代码也能把活儿给干了......</p>
<p data-pid="Uhz7cOQ_">嗯，听起来是不是很耳熟？</p>
<p data-pid="mJEh3oSc">那么实现了吗？</p>
<p data-pid="tm-ArpUG">一定程度上实现了！</p>
<p data-pid="1d_A4DmV">比如出了一种叫 &ldquo;<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=%E8%A1%A8%E9%A9%B1%E5%8A%A8&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">表驱动</a>&rdquo;（table-driven）的编程，开发人员不使用代码，而是通过在预定义的内存或 &ldquo;数据表操作命令列表&rdquo; 中选择操作来定义程序逻辑。</p>
<p data-pid="JBr8Yhp0">更著名的大概是 <a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=SQL&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">SQL</a>（结构化查询语言），虽然老师们仍会建议你认真学习 &ldquo;复杂且隐晦&rdquo; 的数据库及计算机专业知识，但你也可以完全不学...... 然后在不懂 bits 和 bytes 的前提下，尽情的使用 <a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=DBMS&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">DBMS</a>（数据库管理系统）瞎 JB 操作一通......</p>
<p data-pid="UduMu8Zo">然后就是最！最！最！最！著名的 &ldquo;<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=%E4%BD%8E%E4%BB%A3%E7%A0%81&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">低代码</a>&rdquo;（low-code）和 &ldquo;<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=%E6%97%A0%E4%BB%A3%E7%A0%81&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">无代码</a>&rdquo;（no-code）......</p>
<figure data-size="normal"><noscript>&amp;amp;lt;img class="origin_image zh-lightbox-thumb" src="https://pica.zhimg.com/50/v2-deb41c5d1e989fb89afa89f7169f5a46_720w.jpg?source=1def8aca" width="818" data-caption="" data-size="normal" data-rawwidth="818" data-rawheight="264" data-original-token="v2-4a4e8f472303542cb1a35d4cd9c7b8ad" data-default-watermark-src="https://picx.zhimg.com/50/v2-428c0d35bd8d0f1e53a78ab982096af7_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-deb41c5d1e989fb89afa89f7169f5a46_r.jpg?source=1def8aca" data-mce-src="https://pica.zhimg.com/50/v2-deb41c5d1e989fb89afa89f7169f5a46_720w.jpg?source=1def8aca" data-mce-fragment="1"&amp;amp;gt;</noscript>
<div><img class="origin_image zh-lightbox-thumb lazy" src="https://pica.zhimg.com/80/v2-deb41c5d1e989fb89afa89f7169f5a46_720w.webp?source=1def8aca" width="818" height="264" data-caption="" data-size="normal" data-rawwidth="818" data-rawheight="264" data-original-token="v2-4a4e8f472303542cb1a35d4cd9c7b8ad" data-default-watermark-src="https://picx.zhimg.com/50/v2-428c0d35bd8d0f1e53a78ab982096af7_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-deb41c5d1e989fb89afa89f7169f5a46_r.jpg?source=1def8aca" data-actualsrc="https://pica.zhimg.com/50/v2-deb41c5d1e989fb89afa89f7169f5a46_720w.jpg?source=1def8aca" data-lazy-status="ok"></div>
</figure>
<p data-pid="VUubwU00">至于 C#、Java、JavaScript、Python 这些 3GL，当它们结合了一些具备 4GL 特性的库时，如果不深究的话，大约也可以被称为 4GL，至少是 3GL + 4GL 的混合模式。</p>
<p data-pid="wMVXDsdB">既然如此，从时间上推算，4GL 已经出来四十多年了......</p>
<blockquote data-pid="kw9rcdsx">实际上远不止四十多年，在上世纪 60 年代已经有 <a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=Mark+IV&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">Mark IV</a> 了，它可以自动读取输入文件并转换其中的数据，这个 &ldquo;报告生成器&rdquo; 其实也是 4GP，只不过当时还没这个概念罢了。</blockquote>
<p data-pid="UnJvCQNP">那么程序员这个 &ldquo;行业&rdquo; 消失了吗？</p>
<p data-pid="6XiTRUb8">哼哼~</p>
<p data-pid="ViMpGXrR">时间嘀嗒嘀嗒嘀，<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=GenAI&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">GenAI</a> 又来了！</p>
<p data-pid="5SxyAU3Y">两年前我在技术论坛上看过这么一篇论文，标题叫《Large Language Models: Compilers for the 4th Generation of Programming Languages》，作者探讨了<a class="RichContent-EntityWord css-b7erz1" href="https://zhida.zhihu.com/search?content_id=717675066&amp;content_type=Answer&amp;match_order=1&amp;q=%E5%A4%A7%E8%AF%AD%E8%A8%80%E6%A8%A1%E5%9E%8B&amp;zhida_source=entity" target="_blank" rel="noopener" data-za-not-track-link="true" data-paste-text="true">大语言模型</a>（LLMs）作为 4GP 编译器的可能性。</p>
<figure data-size="normal"><noscript>&amp;amp;lt;img class="origin_image zh-lightbox-thumb" src="https://pic1.zhimg.com/50/v2-386b45ae966ccaee8da0fbcf868cc7f9_720w.jpg?source=1def8aca" width="564" data-caption="" data-size="normal" data-rawwidth="564" data-rawheight="343" data-original-token="v2-65311dc8f3f544bfb2faac7317d8773d" data-default-watermark-src="https://picx.zhimg.com/50/v2-c6a04cace3c469b1c720b936c84e8210_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-386b45ae966ccaee8da0fbcf868cc7f9_r.jpg?source=1def8aca" data-mce-src="https://pic1.zhimg.com/50/v2-386b45ae966ccaee8da0fbcf868cc7f9_720w.jpg?source=1def8aca" data-mce-fragment="1"&amp;amp;gt;</noscript>
<div><img class="origin_image zh-lightbox-thumb lazy" src="https://pic1.zhimg.com/80/v2-386b45ae966ccaee8da0fbcf868cc7f9_720w.webp?source=1def8aca" width="564" height="343" data-caption="" data-size="normal" data-rawwidth="564" data-rawheight="343" data-original-token="v2-65311dc8f3f544bfb2faac7317d8773d" data-default-watermark-src="https://picx.zhimg.com/50/v2-c6a04cace3c469b1c720b936c84e8210_720w.jpg?source=1def8aca" data-original="https://picx.zhimg.com/v2-386b45ae966ccaee8da0fbcf868cc7f9_r.jpg?source=1def8aca" data-actualsrc="https://pic1.zhimg.com/50/v2-386b45ae966ccaee8da0fbcf868cc7f9_720w.jpg?source=1def8aca" data-lazy-status="ok"></div>
</figure>
<p data-pid="qAIVabl8">大概意思是说，像 ChatGPT 这样的 LLMs 可以被视为一种 &ldquo;编译器&rdquo;，可以将人类的提示 &ldquo;翻译&rdquo; 成 Python、Java 这种第三代编程语言。</p>
<p data-pid="iAtsnwou">这些 &ldquo;提示&rdquo; 并不完全是人类自然语言，而是一种特定的写作形式，旨在从 LLM 中获取所需的输出。</p>
<p data-pid="NfII_Y0V">于是，整个这套模式和流程就成了所谓的 &ldquo;第四代编程语言&rdquo;，而 LLM 就是其中的编译器。</p>
<p data-pid="2tWSfuAn">......</p>
<p data-pid="1pTGKfNg">我不是相关从业者，所以论文中的很多专属名词和理论我都看不懂，但我瞅见了一个 &ldquo;华点&rdquo;</p>
<p data-pid="1ldjBOME">------ 折腾半天，&ldquo;输出&rdquo; 的不还是 3GP 嘛，一堆编程语言代码！</p>
<p data-pid="thBINJK5">那这一大堆代码是给谁看的？</p>
<p data-pid="BGmWpjep">程序员！</p>
<p data-pid="WSrVrr7O">......</p>
<p data-pid="x6siv3ER">对了，我还没提 &ldquo;他们以为软件开发的唯一难点是写代码&rdquo; 这个事儿呢！</p>
<p data-pid="zjVTPswh">这个事儿又能写一大篇......</p>
<hr></div>
</div>
<hr><strong>Caption4 秒杀</strong></div>
<div>
<p data-pid="wQYS-r9G">说个残忍的事实，没有代码基础的人即使使用AI也写不出完整的代码；</p>
<hr><hr>
<p data-pid="wQYS-r9G">The end!!</p>
<hr></div>]]></description>
    <pubDate>Thu, 13 Mar 2025 10:46:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/69.html</guid>
</item>
<item>
    <title>AI 正在培养一代 “文盲程序员”</title>
    <link>https://blog.xuhaobo.cn/private/68.html</link>
    <description><![CDATA[<p><span class="js_darkmode__text__0">最近一段时间，国外技术社区有一个关注度很高的话题。它由一篇文章引发了大量的讨论，事情的起因是2025年1月底 ChatGPT 宕机，作者有感而发写下了下面这篇文章：</span><span class="js_darkmode__text__1">https://nmn.gl/blog/ai-illiterate-programmers</span></p>
<hr>
<p><span class="js_darkmode__text__2">几天前，由于ChatGPT宕机，Cursor 也无法使用了。<br></span></p>
<p><span class="js_darkmode__text__3">我盯着终端，面对那些我讨厌看到的红色错误信息。一个 AWS 错误信息直勾勾地回瞪着我。我不想在没有 AI 帮助的情况下解决它。<br></span></p>
<p><span class="js_darkmode__text__4">经过 12 年的编程，我发现自己变得越来越不擅长自己的工作了。这不是夸大其词，而是当代软件开发者正在面临的新现实。</span></p>
<p><strong><span class="js_darkmode__text__5">技能的退化</span></strong></p>
<p><span class="js_darkmode__text__6">这种退化是不知不觉中发生的。<br></span></p>
<p><span class="js_darkmode__text__7">首先，我不再阅读文档了。既然 AI 可以即时解释问题，那又何必多此一举呢？<br></span></p>
<p><span class="js_darkmode__text__8">接着，我的调试能力也开始退化。没有AI的帮助，堆栈跟踪信息现在看起来难以理解。我甚至连错误信息都懒得细看，只会复制粘贴到 AI 那里。<br></span></p>
<p><span class="js_darkmode__text__9">然后，我就变成了一个&ldquo;人形剪贴板&rdquo;，充当着我的代码与大语言模型（LLM）之间的中转站。<br></span></p>
<p><span class="js_darkmode__text__10">以前，每一条错误信息都能给我一些启发。现在呢？解决方案神奇地出现了，我却什么也没学到。即时答案带来的多巴胺快感取代了对问题真正理解带来的成就感。<br></span></p>
<p><span class="js_darkmode__text__11">接踵而至的是 &ldquo;深入理解&rdquo; 的能力也出现了问题。还记得以前花几个小时去理解一个解决方案为什么有效吗？现在，我只是简单地实现 AI 的建议。如果它们不起作用，我就改进上下文，然后再次询问 AI。如此循环，依赖越来越深。<br></span></p>
<p><span class="js_darkmode__text__12">再就是情感上的变化。以前，解决新问题是编程乐趣的一部分。现在，如果 AI 在5分钟内没有给出解决方案，我就会感到沮丧。<br></span></p>
<p><span class="js_darkmode__text__13">最可怕的是，我正在构建一个由 AI 驱动的开发工具，但我无法摆脱一种感觉：我在亲手助长这一问题，让我们的集体技能不断被退化。</span></p>
<p><strong><span class="js_darkmode__text__14">&ldquo;戒瘾&rdquo;计划</span></strong></p>
<p><span class="js_darkmode__text__15">我并不是建议做出什么激进的决定，比如完全不使用 AI - 这是不现实的。相反，我决定先尝试一周一天的&ldquo;无 AI 日&rdquo;，要求自己在这一天里：<br></span></p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__text__16">完整阅读每一条错误信息<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__17">重新使用真正的调试工具<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__18">从零开始编写代码<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__19">阅读源代码，而不是询问AI<br></span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><span class="js_darkmode__text__20">不瞒你说，这样做并不好受。相比借助 AI， 我感觉这样更慢、更笨、更令人沮丧。<br></span></p>
<p><span class="js_darkmode__text__21">但我也能感受到一些微妙的变化：我对自己编写的代码更有把控，也重新找回了那种久违的自主感，而这些却在使用 AI 的过程中一点点地消失。而且，我学到的东西也更多了。</span></p>
<p><strong><span class="js_darkmode__text__22">令人不安的真相</span></strong></p>
<p><span class="js_darkmode__text__23">我们并不是在借助 AI 成为 10 倍的开发者。<br></span></p>
<p><span class="js_darkmode__text__24">我们在变成了 10 倍依赖 AI 的人。这是有本质区别的。<br></span></p>
<p><span class="js_darkmode__text__25">每当我们让 AI 解决一个我们本可以自行解决的问题时，我们是在用长期的理解力换取短期的生产力；是在追求今天能写出多少行代码，却牺牲了未来真正解决问题的能力。<br></span></p>
<p><span class="js_darkmode__text__26">我并不是建议我们放弃 AI 工具&mdash;&mdash;这已经不可能了。但我们需要一套使用规则。以下是我的一些想法：<br></span></p>
<ul class="list-paddingleft-1">
<li>
<p><span class="js_darkmode__text__27">对于你尚未尝试去理解的问题，不要使用 AI<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__28">阅读并理解所有AI建议的解决方案<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__29">定期进行无 AI 辅助的编程<br></span></p>
</li>
<li>
<p><span class="js_darkmode__text__30">专注于学习模式，而不仅仅是解决眼前的问题<br></span></p>
</li>
</ul>
<p>&nbsp;</p>
<p><span class="js_darkmode__text__31">坦率地讲，我自己也无法做到一直遵守这些规则。但这只是一个开始，我坚信任何刚接触编程的人都应该严格遵守这些规则。<br></span></p>
<p><span class="js_darkmode__text__32">此时此刻，在某个地方，一位新手程序员正在学习编程。他们永远不会体会到真正独立解决问题的成就感。他们永远不会体验到与 bug 奋斗几个小时后所获得的深刻理解。<br></span></p>
<p><span class="js_darkmode__text__33">我们正在造就一代可以向 AI 提出正确问题却无法理解答案的开发人员。每次AI宕机时，他们都会暴露出越来越无助的一面。目前，AI 的能力还不足以完全取代程序员，但随着它的进步，这种情况只会变得更糟。真正的问题不是人工智能是否会取代程序员。而是我们是否正在取代自己。<br></span></p>
<p><span class="js_darkmode__text__39">试着在没有 AI 的情况下编码一天。结果可能会让你大吃一惊&nbsp;<img class="" src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.3.10/assets/newemoji/Addoil.png?tp=webp&amp;wxfrom=5&amp;wx_lazy=1&amp;wx_co=1" alt="图片" crossorigin="anonymous" data-src="https://res.wx.qq.com/t/wx_fed/we-emoji/res/v1.3.10/assets/newemoji/Addoil.png" data-ratio="1" data-w="128" data-original-style="display:inline-block;width:20px;vertical-align:middle;background-size:cover;" data-index="2" data-fail="0"></span></p>
<p><span class="js_darkmode__1 js_darkmode__text__40">- EOF -</span></p>]]></description>
    <pubDate>Tue, 18 Feb 2025 08:56:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/68.html</guid>
</item>
<item>
    <title>我们为什么要放弃永生</title>
    <link>https://blog.xuhaobo.cn/娱乐八卦/67.html</link>
    <description><![CDATA[<p><a href="/content/uploadfile/202502/d2b51738723124.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723124.png" alt="image.png"></a></p>
<p class="js_darkmode__6">本文由小号特约作者独家原创发布</p>
<p class="js_darkmode__7">zhuanlan.zhihu.com/p/22879351</p>
<section class="js_darkmode__11">本文是系列文章《天启四骑士》的最后一篇，在之前的系列文章中，瘟疫骑士在中世纪的欧洲降临，灾荒骑士在光绪年间的山西降临，屠杀骑士在94年的卢旺达降临。</section>
<section class="js_darkmode__12">在这篇文章里，最后一位天启骑士&ldquo;死亡骑士&rdquo;也终于降临了，由于是完结篇，所以本文较长，阅读全文大概需要15分钟。</section>
<section class="js_darkmode__12">
<p><a href="/content/uploadfile/202502/d2b51738723153.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723153.png" alt="image.png"></a></p>
<section class="js_darkmode__17"><strong>01</strong></section>
<section class="js_darkmode__18"><strong>每年有一天是你忌日</strong></section>
<section class="js_darkmode__19"><strong>&nbsp;</strong></section>
<section class="js_darkmode__20">人们对死亡通常会有一种误解，认为死亡从生命诞生的那一刻起就如影随形，有生就有死，这一切看起来天经地义理所当然。其实严格来说，真正意义上不可避免的死亡是生命演化出来的，死亡归根结底是笔交易。</section>
<section class="js_darkmode__21"><strong>&nbsp;</strong></section>
<section class="js_darkmode__22">很不幸，人类的历史中充斥着浩劫，我们倒霉的祖先在各种天灾人祸的打击下苟且偷生。</section>
<section class="js_darkmode__23">即使有些人特别幸运，没有死于瘟疫，没有死于灾荒，没有死于屠杀，也很幸运地没有死于诸如落水，中毒，雷击，坠崖等等意外事故，可人们最终还是会死的，衰老会逐渐地侵蚀人们的肉体，直到死亡最终降临。</section>
<section class="js_darkmode__24"><strong>&nbsp;</strong></section>
<section class="js_darkmode__25">其实我们仔细想想，我们每年都会过一次自己的忌日，只不过在死之前我们不知道是几月几日而已。</section>
<section class="js_darkmode__26">长寿一些的生物比如巨龟能活到300岁（从康熙五十五年一直活到今天），但是死亡还是会在生命的尽头等待着它。</section>
<section class="js_darkmode__27">加利福尼亚巨杉最高可超过100米，寿命可能超过3000岁，时间跨度几乎相当于从牧野之战到北京奥运会开幕，可是最终死亡还是会不可抗拒地降临。</section>
<section class="js_darkmode__28">这给人一种感觉，那就是死亡是不可避免的，是一切生命不可逃避的终结。</section>
<section class="js_darkmode__29"></section>
<section class="js_darkmode__30"></section>
<section class="js_darkmode__31"><strong>02</strong></section>
<section class="js_darkmode__32"><strong>然而，事实并非如此</strong>
<p><a href="/content/uploadfile/202502/d2b51738723167.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723167.png" alt="image.png"></a></p>
<section class="js_darkmode__36">今天这个世界上，有一种小生命名叫阿米巴虫，是一种单细胞原生生物，人们可以通过显微镜观察到它。</section>
<section class="js_darkmode__37"><br>这种小生命的细胞质和细胞器被包裹在细胞膜中，没有固定的形状，结构非常简单，但是它却能做到一件让古今无数帝王将相都梦寐以求的事，那就是永生不朽。</section>
<section class="js_darkmode__38">阿米巴虫之所以能够做到这点，是由它的生殖方式决定的，阿米巴虫可以由一个个体分裂成两个完全相同的个体来实现繁殖，对于这种无性生殖的生物来说，&ldquo;死亡&rdquo;两字有着完全不同的意义。</section>
<section class="js_darkmode__39">因为只要这些小家伙足够幸运，躲开各种致命的意外，理论上它们可以将自己的生命永远地延续下去，当它们的身体到达临界尺寸时，它们就一分为二，然后再等着二分为四，四分为八&hellip;&hellip;</section>
<section class="js_darkmode__40">对于阿米巴虫来说，并没有衰老的概念，更没有不可避免的死亡。</section>
<section class="js_darkmode__41">只要环境允许，它们就能一直这么繁育下去，直到天荒地老海枯石烂*，而这个由它们引出的有关性和永生的故事，需要我们从很久很久以前开始讲起。</section>
<section class="js_darkmode__42"></section>
<section class="js_darkmode__43">且让我们翻回生命之书的第一页，那是一片距今38亿年前的浩瀚汪洋。</section>
<section class="js_darkmode__43">
<p><a href="/content/uploadfile/202502/d2b51738723182.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723182.png" alt="image.png"></a></p>
<section class="js_darkmode__47">在最初数十亿年的时间里，地球还是单细胞生物的家园，彼时的大自然倾向于准确地复制所有存在的生命形式，因为那时地球上还没有性的概念，那些水中微小的，只能用显微镜看到的生物都是中性的，靠着阿米巴虫一样的无性生殖延续着自己永恒的生命*。</section>
<section class="js_darkmode__48">也许是造物主对千篇一律的复制感到了厌倦，更有可能是大自然那不可捉摸的意志做出了选择，大约在距今12亿年前的某一天，地球上的第一次有性生殖出现了。</section>
<section class="js_darkmode__49"></section>
<section class="js_darkmode__50"></section>
<section class="js_darkmode__51"><strong>03</strong></section>
<section class="js_darkmode__52"><strong>有性生殖</strong></section>
<section class="js_darkmode__53"><strong>&nbsp;</strong></section>
<section class="js_darkmode__54">有性生殖是如何开始？</section>
<section class="js_darkmode__55">这一直是一个困扰了科学家们很久的谜题。</section>
<section class="js_darkmode__56">学者们提出了各种理论来解释&ldquo;性&rdquo;的开始，但是仍然没有一种理论可以说服所有学者，因为无论怎样，有性生殖的代价看起来实在是太大了，因为当&ldquo;性&rdquo;出现在这个世界上之后，不可避免的死亡也就出现了（生命只交出配子，而自身会衰朽死亡），对于一些生物来说，不仅自己的基因只能传承一半（雌雄各贡献一半的基因），原来的那种靠无性分裂维系永生的好日子也一去不复返了。</section>
<section class="js_darkmode__57">可是学者们发现，几乎所有的真核生物都会在生命周期的某一时刻&ldquo;纵情声色&rdquo;一把，而绝大部分动植物都是有性生殖的。</section>
<section class="js_darkmode__58"></section>
<section class="js_darkmode__59">为什么有性生殖的代价如此巨大，可是生命却宁可放弃永生也要前仆后继地选择性？</section>
<section class="js_darkmode__60">因为性带来的好处更大。</section>
<section class="js_darkmode__61">美国遗传学家赫尔曼&middot;穆勒（Hermann Muller）因为发现X射线能诱使基因突变而获得了1946年诺贝尔生理或医学奖。</section>
<section class="js_darkmode__62"></section>
<section class="js_darkmode__63">他本人也亲自在果蝇中诱发了成千上万次突变，他发现，对于一个物种来说，绝大多数的突变都是负面的，有益的突变只占很小很小的比例，而如何处理这些突变则成为了一个很棘手的问题。</section>
<section class="js_darkmode__64">对于无性生殖的生物来说，一旦基因里出现了一个有害突变，这个有害突变就会被原封不动地复制下去，除非发生一次超级幸运的回复突变（概率极低），否则这个有害突变就会像冤鬼一样纠缠在基因里，怎么甩都甩不掉。</section>
<section class="js_darkmode__65">而有益突变也没有得到很好的发挥，因为它的扩散受到了无性生殖的限制，举个例子，要想把A和B两种有益突变都收集到一套基因里，无性生殖的个体不得不进行两次突变，先发生A突变，然后复制扩散，然后再在A突变的基础之上发生B突变。</section>
<section class="js_darkmode__66">可是有益突变的概率太低了，完全是可遇不可求的事，这无异于连续两次中彩票大奖。</section>
<section class="js_darkmode__67">再加上突变之间还存在着此消彼长的竞争关系，一个有益突变很可能会因为扩散不过另外一个有益突变而被怼死，这就更糟了。</section>
<section class="js_darkmode__67">
<p><a href="/content/uploadfile/202502/d2b51738723201.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723201.png" alt="image.png"></a></p>
<section class="js_darkmode__69">（有益突变使得a变成A，b变成B，但是有益突变的扩散却受到无性生殖的限制，同时有益突变之间也存在着此消彼长的竞争关系）</section>
<section class="js_darkmode__70"><strong>&nbsp;</strong></section>
<section class="js_darkmode__71">在这种情况下，就轮到有性生殖大显身手了。</section>
<section class="js_darkmode__72">因为有性生殖不是单纯地复制父辈的基因，而是不同的基因之间进行交流融合，这使得有益突变和那些没有受到有害突变侵扰的基因得以迅速地富集在一起，这效率比无性生殖不知道高到哪里去了。</section>
<section class="js_darkmode__73">性也可以用同样的逻辑把有害突变富集起来，然后把承载着有害突变的个体交给环境去毁灭，实现对基因的净化。</section>
<section class="js_darkmode__74">虽然性只能传承父辈基因的一半，但是子辈的适应度却高出不止一倍，对于生命来说，&ldquo;性&rdquo;可以说是个稳赚不赔的买卖。</section>
<section class="js_darkmode__75">这一切就好像一个汽车修理工处理两辆二手车，工人把两辆二手车上运转良好的零件组装在一起形成好车，运转不良的零件组装在一起形成烂车，好车继续上路，烂车则拖去销毁。</section>
<section class="js_darkmode__76">正是有性生殖可以快速地积累起增强物种适应性的创新，才使得生命开始了复杂化的进程，生命也同时开始面对不可避免的，真正意义上的死亡。</section>
<section class="js_darkmode__77"><strong>&nbsp;</strong></section>
<section class="js_darkmode__78">
<p><a href="/content/uploadfile/202502/d2b51738723244.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723244.png" alt="image.png"></a></p>
（有益突变使得a变成A，b变成B，并且在有性生殖的帮助下迅速富集）</section>
<section class="js_darkmode__79"><strong>&nbsp;</strong></section>
<section class="js_darkmode__80"><strong>&nbsp;</strong></section>
<section class="js_darkmode__81"><strong>04&nbsp; &nbsp;</strong><strong>性</strong></section>
<section class="js_darkmode__83"><strong>&nbsp;</strong></section>
<section class="js_darkmode__84">&ldquo;性&rdquo;就好像是在生命的原野上立起一栋砖墙，这栋砖墙一直延伸到地平线的远方，将整个生命界一分为二。</section>
<section class="js_darkmode__85">砖墙的一侧是沉默而孤寂的世界，一个个面无表情的自我复制者维系着自己不朽的生命，而砖墙的另一侧则是色彩缤纷，花香鸟语的美好乐园，各种动植物在性的狂欢中享受着速朽的青春，同时也面对着不可避免的死亡。</section>
<section class="js_darkmode__86">可恶的是，那栋砖墙不高，好些物种从墙的一侧翻到另一侧，然后再从另一侧翻回来。</section>
<section class="js_darkmode__87"></section>
<section class="js_darkmode__88">更有的物种干脆一屁股骑在了墙上。</section>
<section class="js_darkmode__89">鞭毛虫类里的一些小家伙就搞出了一种excited的生殖方式，被有的学者称为&ldquo;无性的性行为&rdquo;，它们就好像是有性与无性之间的骑墙派。</section>
<section class="js_darkmode__90">这些小家伙身体呈梨子状，在水中通过类似于毛发的鞭毛振动来移动自己，它们通常以自我分裂的方式进行繁殖。</section>
<section class="js_darkmode__91">然而有的时候，这些小家伙会用它身体较尖的一端戳进另一个同类身体较圆的一端（就好像用自己的&ldquo;尖脑袋&rdquo;去戳别人的&ldquo;圆屁股&rdquo;），并且藏身其中，这样一来两个小家伙就合二为一了，融合成一个新的个体。</section>
<section class="js_darkmode__92">这个新的个体融合了双方各自的成分，之后不久又会开始自我分裂。尽管这些小家伙没有清晰的性别，但是它们已经开始出现雌雄分化的苗头。</section>
<section class="js_darkmode__93"></section>
<section class="js_darkmode__94">科学家通过显微镜发现，它们的&ldquo;圆屁股&rdquo;上有一个深色的圆圈，那是一种供伙伴瞄准用的&ldquo;靶子&rdquo;。</section>
<section class="js_darkmode__95">有靶子的个体通常会表现出雌性的倾向，没有靶子的则更倾向于雄性。</section>
<section class="js_darkmode__96">可是，有时候一些小个子雄性也会戳进大个子雄性的身体里，大小悬殊的雌性之间也会出现类似的&ldquo;误会&rdquo;，不过科学家并没有观察到小雌性戳大雄性的事情（截至文献完成之时）。</section>
<section class="js_darkmode__97">由此可以推测，并不是双方都可以自由转换&ldquo;性别&rdquo;角色的。</section>
<section class="js_darkmode__98"></section>
<section class="js_darkmode__99">除了骑墙派以外，还有一些物种在有性和无性的砖墙上翻来翻去，一会有性生殖一会又无性生殖，水螅就是&ldquo;翻墙党&rdquo;之一。</section>
<section class="js_darkmode__100">一般情况下，水螅是通过&ldquo;芽生&rdquo;的方式繁殖后代的，它们的身体上会长出一个芽状的突起，而这个突起会逐渐地变成一个水螅宝宝，最终脱离母体。</section>
<section class="js_darkmode__101">除此之外，水螅如果被斩断一只触手，那么原来触手所在的位置就会长出几只新的触手，而被斩下来的触手则会再长成一只完整的水螅。</section>
<section class="js_darkmode__102">如果我们将一只水螅粉碎成100段，那么就会有100段水螅再生出来，水螅也因此被赋予了一个希腊神话中怪物的名字Hydra：</section>
<section class="js_darkmode__103">一种被斩了头却可以再生新头的九头蛇怪。</section>
<section class="js_darkmode__104">让人惊奇的是，当一片水域里的水螅繁殖过多时，水螅呼吸出的二氧化碳就会刺激水螅生成一种性刺激素，水螅也会因此临时出现性别，雄性和雌性会将精子和卵子排入水中，是否形成受精卵就要看机遇了，水螅也正是通过临时转变成有性生殖的方式来控制群体数量的。</section>
<p>&nbsp;</p>
<p><a href="/content/uploadfile/202502/d2b51738723261.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723261.png" alt="image.png"></a></p>
<section class="js_darkmode__108">蚜虫也是&ldquo;骑墙派&rdquo;的代表之一，在气候温暖之际，雌性蚜虫会通过一种名叫&ldquo;孤雌生殖&rdquo;的方式直接产出小蚜虫。</section>
<section class="js_darkmode__109">因为基因全部来自母亲，所以这些小蚜虫也全部都是雌性，在基因上和它们的母亲一模一样。</section>
<section class="js_darkmode__110">等到天气转凉时，雌蚜虫就会开始调整自己后代的性染色体，生出一些雄性后代，而这些雄蚜虫会找雌蚜虫们（它们的&ldquo;母亲&rdquo;和&ldquo;姐妹&rdquo;）交配使其生下虫卵，因为只有虫卵可以熬过寒冷的冬天，等到第二年气温转暖以后再孵化出雌蚜虫，让种群重新繁盛起来。</section>
<section class="js_darkmode__111">在温暖的地方，雌蚜虫甚至会保持全年的无性生殖，因此雄蚜虫就根本没有存在的必要。</section>
<section class="js_darkmode__111">
<p><a href="/content/uploadfile/202502/d2b51738723274.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723274.png" alt="image.png"></a></p>
<section class="js_darkmode__115">没有雄性就自己生出雄性再与之交配的繁殖手段并不是蚜虫的专利，有的竹节虫类和蛾类会在雄性短缺的情况下自己动手，用孤雌生殖的方式给自己生出一大堆雄性再与之交配，相当于自己给自己生&ldquo;丈夫&rdquo;。</section>
<section class="js_darkmode__116">这种在有性和无性之间切换的生殖方式被动物学家们称为&ldquo;异质生殖交替&rdquo;。</section>
<section class="js_darkmode__117">有的读者都到这的时候可能会想一个人静一静，认为无性生殖圈似乎太乱了点，还是我们人类这样的有性生殖规矩一些。</section>
<section class="js_darkmode__118"></section>
<section class="js_darkmode__119"></section>
<section class="js_darkmode__120"><strong>05</strong></section>
<section class="js_darkmode__121"><strong>雌雄难辨</strong></section>
<section class="js_darkmode__122"><strong>&nbsp;</strong></section>
<section class="js_darkmode__123">事实上，大自然就好像是一个巨大的生殖实验室，不同的物种在自然界中演化出了各种各样的生殖策略，这些物种根本不care人类那套自以为是的道德准则，在有性生殖的领域里，它们会做出一些在人类看来更加疯狂的事情。</section>
<section class="js_darkmode__124">在美国佛罗里达群岛中，有一个小小的珊瑚岛，名叫鳄鱼礁（Alligator Reef），那是一个由棕榈树，沙滩，阳光和海浪构成的美妙天堂。</section>
<section class="js_darkmode__125">在礁石之间游弋着一种五彩斑斓的小鱼，名叫佛罗里达鮨（<em>Serranus subligarius</em>，亦称&ldquo;带鮨&rdquo; d&agrave;i y&igrave;）。</section>
<section class="js_darkmode__125">
<p><a href="/content/uploadfile/202502/d2b51738723290.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723290.png" alt="image.png"></a></p>
<section class="js_darkmode__129">这一天，一只雄性带鮨正在一条雌鱼的身边徘徊求偶，为了把接下来的事说清楚，咱们就暂且把雄鱼称为&ldquo;小刚&rdquo;，雌鱼称为&ldquo;小美&rdquo;好了。</section>
<section class="js_darkmode__130">小刚看起来就像是一道在水中游弋的绚丽火焰，一身鳞片闪烁着明亮的橙色，深蓝色的斑点点缀其中。</section>
<section class="js_darkmode__131">而小美看起来则朴素低调的多，靛蓝色的身体点缀着一点紫色，深色的边缘让它更加暗淡。</section>
<section class="js_darkmode__132">正当小刚和小美齐头并进时，它们的身体开始像鼓一样振动起来，于此同时，小美开始排出卵泡，小刚则在卵泡上洒下一股乳白色的精液，给那些卵受精。</section>
<section class="js_darkmode__133"></section>
<section class="js_darkmode__134">接下来，令人惊奇的一幕发生了，在那些卵受精之后的几秒钟后，小刚身上明亮的橙色开始熄灭，转而变成暗淡的靛蓝色，而小美身上的颜色则&ldquo;燃烧起来&rdquo;，呈现出明亮的橙色，好似一道火焰。</section>
<section class="js_darkmode__135">紧接着，小刚开始排出卵泡，而小美则冲上去往卵泡上喷洒精液让其受精。就这样，这两条带鮨在交配过程中通过瞬间变性既充当了父亲，又充当了母亲。事实上，这种鱼是雌雄同体的，可以根据各种实际情况改变自己的性别，当两条同性相遇时，其中一条会瞬间变性和对方交配，然后双方性别倒置再交配一次，最让人震惊的是，如果这条鱼找不到伴侣的话，它们就会自己先排卵，然后再变性，往自己排出的卵泡上喷洒自己的精液，自己和自己交配&hellip;&hellip;.</section>
<section class="js_darkmode__136"></section>
<section class="js_darkmode__137">其实这种雌雄同体可以变性的情况在鱼类里并不罕见，在其他的物种中也能找到类似的情况。欧洲北海附近生活着一种海虫名叫绿沙蚕（<em>Nereis virens</em>）。</section>
<section class="js_darkmode__138">当它们的身体少于20节时，就会维持自己的雄性身份并且产生精子，而当它们生长到超过20节时，它们就会变成雌性产生卵子，可是如果我们故意刁难它们，把它们斩断到20节以下的话，它们又会变成雄性。</section>
<section class="js_darkmode__139"></section>
<section class="js_darkmode__140">通常来说，这种沙蚕年轻时都是雄性，长大了就会变成雌性，可是如果我们把两条雌性沙蚕放进一个瓶子里，较短的一条就会变成雄性，这样它们就能交配了。</section>
<section class="js_darkmode__141">由此可见，即使在有性生殖的领域里，也没有什么一定之规，大自然千变万化，总是会有人类经验之外的境况出现，大自然不仅通过性向我们展现神奇的一面，也向我们展现残酷的一面。</section>
<section class="js_darkmode__142"></section>
<section class="js_darkmode__143">德国著名动物行为学家维托斯&middot;德吕舍尔（Vitus B.Dr&ouml;scher）曾经在他的著作《从相残到相爱：两性行为的自然演化》中表达过一个极具争议性的观点。</section>
<section class="js_darkmode__144">德吕舍尔先生认为，这个世界上的动物，最开始是先有雌性，然后有雌雄同体的双性，最后才有雄性的。雄性实际上是雌性为了解决环境的适应性问题而&ldquo;发明&rdquo;出来的。</section>
<section class="js_darkmode__145">这个观点饱受争议，同时也让很多男性感到惊讶和尴尬，但是德吕舍尔先生也确实给了我们一个新的思路让我们重新思考这个世界上的两性关系。</section>
<section class="js_darkmode__146"></section>
<section class="js_darkmode__147">可惜的是，并不是雄性被&ldquo;发明&rdquo;出来以后就万事大吉了，像童话故事结尾那样的情节&ldquo;从此王子就和公主幸福地生活在了一起&rdquo;也只能存在于童话中。</section>
<section class="js_darkmode__148">以我们今天人类社会的主流道德标准看来，理想的伴侣关系应该是稳定，持久，忠诚，甜蜜的。</section>
<section class="js_darkmode__149"></section>
<section class="js_darkmode__150"></section>
<section class="js_darkmode__151"><strong>06</strong></section>
<section class="js_darkmode__152"><strong>臭虫</strong></section>
<section class="js_darkmode__153"><strong>&nbsp;</strong></section>
<section class="js_darkmode__154">可是一个物种要是想维持这种理想的两性关系，需要平衡好三种本能，分别是&ldquo;性本能&rdquo;，&ldquo;攻击本能&rdquo;以及&ldquo;亲和性结对本能&rdquo;，实现这种平衡是一个漫长而复杂的过程，而有的物种甚至连最基本的问题，&ldquo;应该和谁交配&rdquo;这件事都没搞太清楚。</section>
<section class="js_darkmode__155">臭虫就经常被这个简单的问题所困扰，臭虫的交配方式非常危险，雄性的性器官看起来就像一把匕首，它并不会将其插入雌性的生殖器开口中，而是直接插入雌性的背部，往雌性的血液里射精，通过血液把精子带入生殖系统。</section>
<section class="js_darkmode__156">这种交配方式被成为&ldquo;创伤性受精&rdquo;。通常来说，雌性在每次交配之后伤口会康复并且在背上留下一道&ldquo;刀疤&rdquo;，但是有的时候雄性如果交配时太卖力，就会把雌性当场插死。</section>
<section class="js_darkmode__157">让人尴尬的是，糊涂的雄臭虫会和任何看起来像臭虫大小，黑色的，平面状的东西发起交配。</section>
<section class="js_darkmode__158">这就使得有的雄臭虫会被别的雄性&ldquo;强暴&rdquo;，乃至有可能被别的雄性内射甚至插死。</section>
<section class="js_darkmode__158">
<p><a href="/content/uploadfile/202502/d2b51738723306.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723306.png" alt="image.png"></a></p>
<section class="js_darkmode__162">在巴西有另外一个让人印象深刻的例子，在巴西桑托斯市附近的沼泽地不远处曾经建起了一座变电站。当工人接通电流时，附近沼泽里无数的蚊子铺天盖地地朝变电站飞来，然后落在烫手的机器上被成片烤死。</section>
<section class="js_darkmode__163">最后工作人员不得不用推土机将成堆的蚊子尸泥清理走。这一切的原因在于那个变电站发出的振动频率为每秒500&mdash;550次，和雌蚊发出的声音一样，搞不清楚该和谁交配的雄蚊听到这种声音，就把变电站当作了雌蚊，不顾一切地飞向死亡。</section>
<section class="js_darkmode__164">同样的问题有时候也困扰着阿德利企鹅，这种企鹅的眼睛是为了在水下使用而设计的，所以上岸以后每只企鹅都是严重的近视眼。</section>
<section class="js_darkmode__165">到了求偶的时节，雄性阿德利企鹅会用嘴把自己心爱的小石头推到自己的意中人脚下示好，如果它的意中人抬起它的小翅膀扇它的话，表示自己拒绝和它交配。如果它的意中人接受示好，它就会围着这只雄性跳舞，并且和它开始抒情的二重唱。当然还有一种情况是这样的，意中人俯下身子冲着那只雄性愤怒地尖叫，这尖叫声的意思是&ldquo;QNMD！老子是雄性！&rdquo;</section>
<section class="js_darkmode__166"></section>
<section class="js_darkmode__167">搞清楚应该和谁交配只是第一步，离真正和谐美满的两性关系还差的很远。攻击本能和亲和性结对本能是两种相对的本能，它们和性本能一起左右着一个物种内的配偶关系。</section>
<section class="js_darkmode__168">可惜的是，很多物种完全无法抑制住自己的攻击本能，因此攻击本能和性本能共振出来的强奸行为在自然界中是普遍存在的。</section>
<section class="js_darkmode__169">陆龟就是一个典型的例子，在交配季节，雌陆龟会吃的很胖，这使得它们无法把身体全部缩入壳里，只能顾头不顾腚，这就给雄陆龟以可乘之机。雄陆龟会在交配前疯狂地追打雌性，踩它们咬它们，而雌陆龟会不顾一切地逃命。在这个过程中，雌陆龟随时可能会被雄性杀死。</section>
<section class="js_darkmode__170">最后，几个小时的追打终于耗尽了雌龟最后一点体力，它决定放弃抵抗，把头部缩进壳里，于是不得不露出的屁股就只能被雄龟侵犯了。</section>
<section class="js_darkmode__171">陆龟的交配是彻彻底底的强奸行为，没有半点感情涉入其中。</section>
<section class="js_darkmode__172"></section>
<section class="js_darkmode__173"></section>
<section class="js_darkmode__174"><strong>07</strong></section>
<section class="js_darkmode__175"><strong>蜘蛛</strong></section>
<section class="js_darkmode__176"><strong>&nbsp;</strong></section>
<section class="js_darkmode__177">配偶间的攻击本能无法被抑制时，雌性并不永远处于被强奸的弱势地位。蜘蛛里就有一些雌性找回了面子。</section>
<section class="js_darkmode__178">众所周知，不少雌蜘蛛会因为无法克制自己的本能而在交配后吃掉自己的丈夫（其实在蜘蛛里同类相食的情况并不是常态）。</section>
<section class="js_darkmode__179">可是盗蛛科（Pisauridea）里有一种雄蛛非常鸡贼，它为了不让自己在交配时成为自己老婆的口粮，会先抓一只虫子用蛛丝捆好做成礼物，在它老婆享用礼物的时候赶紧交配然后溜之大吉，但是有的雄蛛完事的非常快，交配完了之后发现它老婆还没开始吃礼物，它就会先抢回礼物然后再逃之夭夭。这样一来雌蛛既没吃到礼物也没吃到丈夫，只能一脸懵逼地愣在原地：&ldquo;大意了！妹想到！&rdquo;</section>
<section class="js_darkmode__180"></section>
<section class="js_darkmode__181">老虎的交配非常生动地向我们展示了三种本能是如何相互作用的。老虎的攻击本能要远远强于亲和性结对本能，因此它们对彼此都怀着深深的敌意，这种远远大于引力的斥力也使得老虎平时过着独居生活。</section>
<section class="js_darkmode__182">但是当它们需要交配时，它们不得不走到一起，母老虎会表现的像个幼崽躺在地上做撒娇状，而公老虎则站在远处静静地看着，接着，母虎会围着公虎走动，嘴里发出呼噜呼噜的声音，然后用自己的下巴蹭公老虎的脸。如果此时公虎不识趣地做出任何回应的话，母虎会立即暴跳如雷并且疯狂咆哮，因为它的攻击本能太强了，稍不注意就会失控，把公老虎当作敌人。于是，母老虎只得继续调情，慢慢进入性兴奋状态，用性本能和一点极微弱的亲和性结对本能压制住自己的攻击本能，最后完成交配。要命的是，交配结束之后，母老虎的性本能会立即退散，攻击本能再度占据上风，母老虎会因此立即对公老虎发动致命的进攻。公老虎通常比母老虎强壮，如果它还手的话，可以轻而易举地将这个&ldquo;疯婆子&rdquo;扇倒在地。可是公老虎在交配之后，性本能和结对本能却没有立即消散，它依然对母老虎心存温情，这使得它不忍还手，甚至不忍自卫，只能转身逃进森林深处。在老式动物园的小笼子里，老虎在交配之后，母虎会立即杀死无处可逃的公虎，一些老式动物园也因此拒绝让老虎们交配，而在诸如德国哈根贝克这样的新式动物园里，生活在圈养区的公虎会在交配后立即逃跑，而母虎则会到池子里泡一会平息自己的怒火。</section>
<section class="js_darkmode__182">
<p><a href="/content/uploadfile/202502/d2b51738723323.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723323.png" alt="image.png"></a></p>
<section class="js_darkmode__186">文章写到这里时，大家或许会明白，过强的攻击本能对于形成人类期许的那种美满的两性关系是负面的，如果我们人类（智人）的攻击本能过强的话，情侣们稍一接近就会疯狂地撕打起来，美满的配偶关系自然无从谈起。</section>
<section class="js_darkmode__187"></section>
<section class="js_darkmode__188">那么我们彻底摒弃攻击本能吼不吼哇？</section>
<section class="js_darkmode__189">大自然也姿瓷吗？</section>
<section class="js_darkmode__190">不，大自然没有任何这个意思。</section>
<section class="js_darkmode__191">大家要是有这样的想法我要负责的。</section>
<section class="js_darkmode__192">因为如果我们过于温柔，人和人之间清晰的边界也就消失了，我们人类就会像爱好和平性情温顺的长颈鹿那样*，凭借着意愿和任意一个性成熟的异性交配而不需要冒着被骂非礼，被扇耳光，被防狼器电击的风险。</section>
<section class="js_darkmode__193">我们可能会因为失去攻击本能而在任何时间任何地点和任何自己中意的人做爱而不会有人介意。</section>
<section class="js_darkmode__194">什么？</section>
<section class="js_darkmode__195">你问会不会有人来阻止你？</section>
<section class="js_darkmode__196">他们自己的裤子还没提上来呢，哪有空管你呢？</section>
<section class="js_darkmode__197">攻击性是非常微妙的东西，如果太强的话，不要说无法形成理想的配偶关系，就连整个社会都会因为成员之间的敌意太强而崩溃（大型社会可能根本就无法形成，人类会在霍布斯世界里徘徊，甚至成为老虎和北极熊那样的独居物种），而攻击性太弱的话，人和人之间又会失去清晰的边界而搞的一塌糊涂。</section>
<section class="js_darkmode__198"></section>
<section class="js_darkmode__199"></section>
<section class="js_darkmode__200"><strong>08</strong></section>
<section class="js_darkmode__201"><strong>性和死亡</strong></section>
<section class="js_darkmode__202"><strong>&nbsp;</strong></section>
<section class="js_darkmode__203">归根结底，是&ldquo;性本能&rdquo;，&ldquo;亲和性本能&rdquo;和&ldquo;攻击本能&rdquo;这三种本能之间微妙的平衡造就了我们主流社会价值观所期许的那种配偶关系，任何一种本能走极端都是不可取的。</section>
<section class="js_darkmode__204">话说回来，人类所在的地理环境也会对人类形成强烈的影响，因此一夫一妻制是不是人类这个物种的常态，目前还没有清晰明确的定论。</section>
<section class="js_darkmode__205">无论我们以怎样的方式结成配偶，在我们开始享受性的时候，死亡也自动开始了它的进程，生命的倒计时在交配的欢愉之中开始了。</section>
<section class="js_darkmode__206">最后一位天启骑士，挎上冰凉的镰刀，骑上惨白的骨马，走入性爱的乐园，去兑现一份12亿年前签下的契约。</section>
<section class="js_darkmode__207">尽管我们并不清晰具体地知道其中的原理，但是性和死亡就好像一枚硬币的两面，互为表里。</section>
<section class="js_darkmode__208">蜉蝣的幼虫可以活一个月，然后变态为没有口器也没有消化道的成虫，然后它们可以有一天的时间享受死前的狂欢滥交，就算勉强活过了一天，也会因为没有消化系统而活活饿死。</section>
<section class="js_darkmode__209"></section>
<section class="js_darkmode__210">太平洋鲑鱼又如何呢？</section>
<section class="js_darkmode__211">它们迁徙几百甚至上千公里，回到它们出生的小溪，然后陷入性爱的狂欢之中。它们短短几天之内就会燃尽自己的生命之火，然后成为一片安静的浮尸。</section>
<section class="js_darkmode__212">蜂后在十六年的时间里仿佛进入了时间的真空之中，身体没有任何衰老的迹象，可当它储存的精子用完之后，它立刻就会被它的孩子们撕个粉碎。</section>
<section class="js_darkmode__213">事实上，人们通过观察，早就发现了性和死亡之间有着某种不同寻常的联系，比如人们发现溪鳟被引入内华达山脉的寒冷，缺乏营养的高山湖之后，性成熟推迟了，但是寿命足足翻了两倍。法国著名生物学家查尔斯&middot;布朗-塞卡德就在1889年向巴黎生物学协会报告说，自从他把狗和豚鼠的睾丸提取物注射到自己身体里以后，他感到自己体力充沛，精神焕发。不久以后，全世界的外科医生都开始着手给患者植入山羊，猴子甚至囚犯的睾丸。</section>
<section class="js_darkmode__214"></section>
<section class="js_darkmode__215">但是没有任何证据证明这种手术的价值。</section>
<section class="js_darkmode__216">得益于技术的进步，今天的学者们抛弃了之前对性和死亡主观粗暴的认识，得以以更加微观的视角来解释性和死亡之间的关系。</section>
<section class="js_darkmode__217">伦敦大学生物化学家尼克&middot;莱恩（Nick Lane）表示，所有的衰老基因(gerontogenes)都有一个奇怪的特征，那就是一旦突变，后果全都是延长寿命，而非缩短。后来学者发现，其实这些基因控制的根本就不是衰老，而是性成熟。</section>
<section class="js_darkmode__218"></section>
<section class="js_darkmode__219">动物如果想发育到性成熟，需要摄入大量的营养，但是当营养不足的时候，最好的策略就是暂缓发育，等营养足够之后再说（还记得溪鳟的例子吗）。</section>
<section class="js_darkmode__220">这意味着，当营养充足的时候，身体里一个象征着丰饶的生化信号就会通知细胞们：&ldquo;小的们，现在食物大大滴有！交配的时候到啦！&rdquo;这个象征着丰饶的生化信号其实是胰岛素，当营养充足时，这些胰岛素类激素就会发挥作用，上演一系列发育变化，为性做好准备。</section>
<section class="js_darkmode__221">当营养匮乏的时候，通路就会陷入沉寂，性发育推迟，生命就会&ldquo;暂停&rdquo;。</section>
<section class="js_darkmode__222">长时间的饥肠辘辘会延长动物的寿命，但是相应的代价却是不孕不育（这就是为啥糖尿病伴随的胰岛素紊乱往往伴随着不孕不育）。</section>
<section class="js_darkmode__223"></section>
<section class="js_darkmode__224">另外，如果我们回顾生命的历史，我们就会发现，死亡和性在很久以前就纠缠在一起了。</section>
<section class="js_darkmode__225">三十亿年前的地球，天空还是一片朦胧的红色，空气中几乎没有氧，要想等到微生物们把天空变成蓝色，还需要好几亿年的时间。尽管当时地球上还没有飞鸟走兽，但是生命之间的殊死搏杀却已经在微观的层面上开始了。</section>
<section class="js_darkmode__226">噬菌体是一种病毒，专门感染细菌，在现代海洋中，它们的数量要比细菌高出两个数量级。这些阴险狡诈的噬菌体会用自己仅有的一点基因来编码一些毒素用以杀死宿主细菌，然后又产生一种抗毒素来确保细菌不会被毒死。毒素本身是长效的，但是抗毒素却却是短效的，所以噬菌体就用这种方式绑架了它感染的细菌。就好像一个人一边喂你吃长效性的毒药，一边给你喂短效性的解药，如果你把他赶走，你就会被毒死。细菌为了对付噬菌体，就把抗毒基因整合到自己身体里获得抗毒性，而噬菌体却不依不饶地演化出更先进的毒素反制，双方就这么互相抬杠，开始了一场数十亿年的军备竞赛。学者推测，正是这场军备竞赛缔造出了胱天蛋白酶(caspase enzyme)家族。这种&ldquo;死亡蛋白&rdquo;会在细胞内形成连锁反应，把细胞从内部&ldquo;切开&rdquo;，就好像是细胞的自毁程序。一旦细菌感到形势不对，族群面临团灭威胁时。族群里最优质的细胞就会发育成顽强的孢子，而剩下的个体则纷纷启动自毁程序，以鱼死网破的方式将一切归零重来。等危机过后，孢子再重新萌发，make it great again。这一切就好比人类社会在各种危机下面临崩溃，一小撮人类精英藏进地下掩体，剩下的人毁灭一切集体自杀，等时机成熟之后精英们再从掩体出来重新建设。</section>
<section class="js_darkmode__226">
<p><a href="/content/uploadfile/202502/d2b51738723341.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723341.png" alt="image.png"></a></p>
<section class="js_darkmode__230">然而，这种细胞的自毁行为，正是衰老以及死亡的本质。</section>
<section class="js_darkmode__231">今天绝大多数动物包括我们人类在内，体内复杂的真核细胞实际上是两种细胞的融合，宿主细胞，以及线粒体。</section>
<section class="js_darkmode__232">但今天非常多的学者坚信，我们体细胞里的线粒体其实在大约17亿年前是一种在外部环境里独自生活的细菌。当它们被宿主细胞吞噬后并没有被消化，反而与宿主细胞成为了好搭档。线粒体从宿主那摄取营养，宿主又从线粒体那获得能量。</section>
<section class="js_darkmode__233">线粒体就好像给宿主细胞安上了一台大功率发动机，从此生命的演进一日千里。</section>
<section class="js_darkmode__234"></section>
<section class="js_darkmode__235">要命的是，线粒体被引入宿主细胞之前也是一种细菌，它的基因里也存留着细胞用以自我毁灭的&ldquo;死亡蛋白&rdquo;&mdash;&mdash;胱天蛋白酶。令人惊奇的是，这种&ldquo;死亡蛋白&rdquo;并没有直接将生命推向毁灭，反而使得生命开始了复杂化的进程。一个人体内的每一个细胞（除生殖细胞外）基因都是相同的。但即使是基因相同的细胞，也会在不同的环境下发育成不同的样子，这种现象被成为细胞分化。同样基因的细胞分化成不同种类的细胞，而这些不同种类的细胞在各自的岗位上各司其职，让它们所属的复杂生命体（比如正在读此文的你）得以正常运转。多亏了&ldquo;死亡蛋白&rdquo;，这一切才成为可能，那些不顾集体安排自行其是的细胞会被胱天蛋白酶执行死刑，从我们的身体里被消灭掉。胱天蛋白酶正是这样冷酷地消灭我们身体里各种不服的&ldquo;刺头&rdquo;，维护着我们身体得以正常运行的秩序。我们的身体也正是建立在这种细胞再生和细胞死亡的平衡之上，但是由于某种我们目前还不明确的原因（一种观点认为是多效性基因的效应），这种平衡会随着时间的推移被渐渐打破，如果最终&ldquo;生&rdquo;压倒了&ldquo;死&rdquo;，癌症就会出现，细胞将以疯狂的增殖毁灭一切，如果&ldquo;死&rdquo;压倒了&ldquo;生&rdquo;，细胞会被胱天蛋白酶超量地杀死，我们的身体会渐渐凋零枯萎，像神经元细胞那样的特化细胞一旦被杀死就再也没有了，我们会在凛冽的岁月中逐渐地失去自我，最后死亡。</section>
<section class="js_darkmode__236"></section>
<section class="js_darkmode__237">这一切正如侦探小说最终指认凶手时会让人大吃一惊一样。当初那慷概地赋予我们能量，陪我们走过十几亿年的光阴，帮助我们演化成智慧生命的大功臣和好朋友，甚至可以说缔造了我们并且依然存在于我们每个人身体里的线粒体，其实就是死亡骑士本人。</section>
<section class="js_darkmode__238"><strong>&nbsp;</strong></section>
<section class="js_darkmode__239">在十几亿年前的浩瀚汪洋中，生命通过&ldquo;性&rdquo;高效率地积累起各种增强适应性的创新，开始了生命复杂化的进程。</section>
<section class="js_darkmode__240">&ldquo;死亡&rdquo;又随之降临，确保复杂化的生命得以正常的维持。生命的世界从几十亿年前的一片混沌，到今天这般绚丽缤纷，&ldquo;性&rdquo;和&ldquo;死亡&rdquo;缺一不可。</section>
<section class="js_darkmode__241">&ldquo;性&rdquo;和&ldquo;死亡&rdquo;是一枚硬币的两面，在地球还是一片洪荒时，造物主面无表情地将它弹向空中，十几亿年过去了，那枚硬币却依然在空中久久回转&hellip;&hellip;</section>
<section class="js_darkmode__241">
<p><a href="/content/uploadfile/202502/d2b51738723355.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738723355.png" alt="image.png"></a></p>
<section class="js_darkmode__245">总有一天，我也会离开这个世界。</section>
<section class="js_darkmode__246">在我生命尽头的白光之中，死亡骑士走到我身后问：&ldquo;后悔吗？&rdquo;</section>
<section class="js_darkmode__247">我转过头回望他身后的那个美丽世界，看着漫山遍野的鲜花，看着采蜜授粉的蜜蜂，看着追逐嬉戏的角马，看着绚烂开屏的孔雀，看着温情相伴的天鹅，看着月下叫春的猫儿，看着产房里大汗淋漓的母亲，看着产房外欣喜若狂的父亲，看着我们的子孙相识相恋，看着他们的爱情创造未来。</section>
<section class="js_darkmode__248">我回答：&ldquo;不后悔，谢谢你。&rdquo;</section>
<section class="js_darkmode__249"><br>（完）</section>
<section class="js_darkmode__250"></section>
<section class="js_darkmode__251">后记：</section>
<section class="js_darkmode__252">《天启四骑士》是国家博物馆开发的专题课程，分别对应的是灾荒，屠杀、瘟疫、死亡骑士。</section>
<section class="js_darkmode__253">他们分别是：</section>
<section class="js_darkmode__254">灾荒：《地狱看起来就像光绪初年的山西一样》</section>
<section class="js_darkmode__255">瘟疫：《飞翔的尸体与一次欧洲人口灭绝》</section>
<section class="js_darkmode__256">屠杀：《每10秒杀一人，杀满100天》</section>
<section class="js_darkmode__257">死亡：《我们为什么要放弃永生》</section>
<section class="js_darkmode__258">相信国家博物馆做这样的主题肯定是用心良苦，能教育国民，因为扣击震撼到老王的认知边界，而且前晚再次提及时同学们表示很喜欢，所以再次推送。</section>
</section>
</section>
</section>
</section>
</section>
</section>
</section>
</section>
</section>
</section>]]></description>
    <pubDate>Wed, 05 Feb 2025 10:38:00 +0800</pubDate>
    <dc:creator>admin</dc:creator>
    <guid>https://blog.xuhaobo.cn/娱乐八卦/67.html</guid>
</item>
<item>
    <title>使用OLLAMA+ChatBoxAi.app5分钟体验deepseek-r1模型</title>
    <link>https://blog.xuhaobo.cn/private/66.html</link>
    <description><![CDATA[<p><a href="/content/uploadfile/202502/d2b51738394344.png" target="_blank" rel="noopener"></p>
<p>某些场景下可能希望构建一个完全本地离线可用的大模型，方法很多，模型也很多，比如 qwen、qwen2、llama3等，最简单快捷的首推使用 ollama 部署，模型选用 qwen 或 qwen2，针对中文任务效果更好。</p>
<p></a></p>
<p><a href="/content/uploadfile/202502/d2b51738394344.png" target="_blank" rel="noopener">首先下载 ollama 双击安装(277MB)<br>ollama下载地址： </a><a href="https://www.ollama.com/download" target="_blank" rel="noopener">www.ollama.com/download</a></p>
<p><a href="/content/uploadfile/202502/d2b51738394344.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394344.png" alt="image.png"></a></p>
<p>下载后双击安装</p>
<p><a href="/content/uploadfile/202502/d2b51738394360.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394360.png" alt="image.png"></a></p>
<p>安装完毕后打开命令行黑窗口<br>开始菜单中找到命令提示符，点击打开</p>
<p><a href="/content/uploadfile/202502/d2b51738394369.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394369.png" alt="image.png"></a></p>
<p>或者随便打开任意一个文件夹，然后在文件夹地址栏中输入 cmd 按回车。</p>
<p><a href="/content/uploadfile/202502/d2b51738394375.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394375.png" alt="image.png"></a></p>
<p>都能打开一个黑窗口</p>
<p><a href="/content/uploadfile/202502/d2b51738394380.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394380.png" alt="image.png"></a></p>
<p>找到想要使用的模型，推荐 qwen系列<br>打开这个网址 www.ollama.com/library 可以选择要使用的模型</p>
<p><a href="/content/uploadfile/202502/d2b51738394390.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394390.png" alt="image.png"></a></p>
<p>比如使用 qwen，点击进入后，会看到如下</p>
<p><a href="/content/uploadfile/202502/d2b51738394408.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394408.png" alt="image.png"></a></p>
<p>点击<a class="hl hl-1" href="https://so.csdn.net/so/search?q=%E4%B8%8B%E6%8B%89%E6%A1%86&amp;spm=1001.2101.3001.7020" target="_blank" rel="noopener" data-report-click="{&quot;spm&quot;:&quot;1001.2101.3001.7020&quot;,&quot;dest&quot;:&quot;https://so.csdn.net/so/search?q=%E4%B8%8B%E6%8B%89%E6%A1%86&amp;spm=1001.2101.3001.7020&quot;,&quot;extra&quot;:&quot;{\&quot;searchword\&quot;:\&quot;下拉框\&quot;}&quot;}" data-tit="下拉框" data-pretit="下拉框">下拉框</a>，可选择模型版本列表，<code>4b</code> 代表该模型有40亿参数，当然参数越大效果越好，但同样也要求你的电脑配置越高，不够高的话不仅运行慢，还可能卡死机。</p>
<p><a href="/content/uploadfile/202502/d2b51738394425.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394425.png" alt="image.png"></a></p>
<p>如果你有英伟达显卡，并且独立显存不低于12G，可以选择&nbsp;<code>14b</code>&nbsp;/&nbsp;<code>32b</code>&nbsp;版本，否则建议选择&nbsp;<code>7b</code></p>
<p>选择后，右侧会显示对应的下载命令，点击复制图标复制该命令，以7b为例命令是&nbsp;<code>ollama run qwen:7b</code></p>
<p><a href="/content/uploadfile/202502/d2b51738394445.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394445.png" alt="image.png"></a></p>
<p>复制后回到黑窗口，点击鼠标右键粘贴该命令，然后回车，就开始了下载，耐心等待下载完毕。</p>
<p><a href="/content/uploadfile/202502/d2b51738394457.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394457.png" alt="image.png"></a></p>
<h3>直接在黑窗口交互方式使用</h3>
<p>上面下载完毕后，就可以直接在黑窗口中使用了，试试先询问它是谁吧</p>
<p><a href="/content/uploadfile/202502/d2b51738394468.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394468.png" alt="image.png"></a></p>
<p>关闭了黑窗口，如何在次使用呢<br>还记得怎么打开cmd吗，用同样方法去打开它，然后同样去粘贴命令。7b的命令是ollama run qwen:7b</p>
<p>就自动打开了。</p>
<p>还想使用其他模型怎么办<br>同样方法，去 www.ollama.com/library 这个地址找到想用的模型，点击进入，选择模型版本，复制右侧命令，到 cmd黑窗口中粘贴回车。</p>
<p>cmd黑窗口太难用了，有没有ui界面<br>搭配 ollama 最好用的ui界面首推ollama官方的 <a href="/admin/docs.openwebui.com" target="_blank" rel="noopener">docs.openwebui.com(使用的化建议使用docker快速安装)</a></p>
<p><a href="/content/uploadfile/202502/d2b51738394499.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394499.png" alt="image.png"></a></p>
<p>如果你懂 docker部署或略懂代码，可参考手动安装。</p>
<p>如果你只想下载一个exe鼠标点点就能使用，可下载 chatbox，双击安装，同安装其他软件一样方法</p>
<p><a title="https://chatboxai.app/zh" href="https://link.juejin.cn/?target=https%3A%2F%2Fchatboxai.app%2Fzh" target="_blank" rel="nofollow noopener">chatboxai.app/zh</a></p>
<p>第一次运行点击设置</p>
<p><a href="/content/uploadfile/202502/d2b51738394539.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394539.png" alt="image.png"></a></p>
<p>然后点击&ldquo;显示&rdquo;，再从下拉框里选择 &ldquo;ollama&rdquo;</p>
<p><a href="/content/uploadfile/202502/d2b51738394556.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394556.png" alt="image.png"></a></p>
<p>然后在模型下拉框里选择你要使用的模型，所有通过 ollama run 命令安装的模型都会在此显示，选择好后点击保存</p>
<p><a href="/content/uploadfile/202502/d2b51738394581.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394581.png" alt="image.png"></a></p>
<p>如果找不到模型，需要参考<a href="https://chatboxai.app/zh/help-center/connect-chatbox-remote-ollama-service-guide" target="_blank" rel="noopener">如何配置ollama远程模式</a>配置环境变量，</p>
<h3>在 MacOS 上配置</h3>
<ol>
<li>
<p>打开命令行终端，输入以下命令：</p>
<pre><code class="language-bash">launchctl setenv OLLAMA_HOST "0.0.0.0"
launchctl setenv OLLAMA_ORIGINS "*"
</code></pre>
</li>
<li>
<p>重启 Ollama 应用，使配置生效。</p>
</li>
</ol>
<h3>在 Windows 上配置</h3>
<p>在 Windows 上，Ollama 会继承你的用户和系统环境变量。</p>
<ol>
<li>
<p>通过任务栏退出 Ollama。</p>
</li>
<li>
<p>打开设置（Windows 11）或控制面板（Windows 10），并搜索&ldquo;环境变量&rdquo;。</p>
</li>
<li>
<p>点击编辑你账户的环境变量。</p>
<p>为你的用户账户编辑或创建新的变量&nbsp;<strong>OLLAMA_HOST</strong>，值为&nbsp;<strong>0.0.0.0</strong>； 为你的用户账户编辑或创建新的变量&nbsp;<strong>OLLAMA_ORIGINS</strong>，值为&nbsp;<strong>*</strong>。</p>
</li>
<li>
<p>点击确定/应用以保存设置。</p>
</li>
<li>
<p>从 Windows 开始菜单启动 Ollama 应用程序。</p>
</li>
</ol>
<h3>在 Linux 上配置</h3>
<p>如果 Ollama 作为 systemd 服务运行，应使用 systemctl 设置环境变量：</p>
<ol>
<li>
<p>调用&nbsp;<code>systemctl edit ollama.service</code>&nbsp;编辑 systemd 服务配置。这将打开一个编辑器。</p>
</li>
<li>
<p>在 [Service] 部分下为每个环境变量添加一行 Environment：</p>
<pre><code>[Service]
Environment="OLLAMA_HOST=0.0.0.0"
Environment="OLLAMA_ORIGINS=*"
</code></pre>
</li>
<li>
<p>保存并退出。</p>
</li>
<li>
<p>重新加载 systemd 并重启 Ollama：</p>
<pre><code>systemctl daemon-reload
systemctl restart ollama</code><br><br></pre>
<p><a href="/content/uploadfile/202502/d2b51738394739.png" target="_blank" rel="noopener"><img src="/content/uploadfile/202502/d2b51738394739.png" alt="image.png"></a></p>
环境变量配置好后，就可以选择对应模型之后进行聊天了。</li>
</ol>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></description>
    <pubDate>Sat, 01 Feb 2025 15:16:00 +0800</pubDate>
    <dc:creator>阿布大人</dc:creator>
    <guid>https://blog.xuhaobo.cn/private/66.html</guid>
</item></channel>
</rss>