<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>原理 on LeoChu Space</title>
    <link>https://leochu.work/blog/tags/%E5%8E%9F%E7%90%86/</link>
    <description>Recent content in 原理 on LeoChu Space</description>
    <generator>Hugo</generator>
    <language>zh-cn</language>
    <lastBuildDate>Wed, 08 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://leochu.work/blog/tags/%E5%8E%9F%E7%90%86/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>LDAP 简明科普</title>
      <link>https://leochu.work/blog/tech/engineering/ldap-%E7%AE%80%E6%98%8E%E7%A7%91%E6%99%AE/</link>
      <pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/ldap-%E7%AE%80%E6%98%8E%E7%A7%91%E6%99%AE/</guid>
      <description>&lt;h2 id=&#34;1-什么是-ldap&#34;&gt;1. 什么是 LDAP&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;LDAP（Lightweight Directory Access Protocol）&lt;/strong&gt; 是一种用于&lt;strong&gt;查询和管理目录信息的协议&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;简单说：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;LDAP 是一个“按层级组织的用户信息数据库 + 查询方式”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-ldap-存什么&#34;&gt;2. LDAP 存什么&lt;/h2&gt;
&lt;p&gt;常见内容包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;用户账号（用户名、ID）&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;hr&gt;
&lt;h2 id=&#34;3-数据结构像树&#34;&gt;3. 数据结构（像树）&lt;/h2&gt;
&lt;p&gt;LDAP 的数据是&lt;strong&gt;树状结构&lt;/strong&gt;：&lt;/p&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph TD
A[公司] --&amp;gt; B[技术部]
A --&amp;gt; C[人事部]
B --&amp;gt; D[用户A]
B --&amp;gt; E[用户B]
C --&amp;gt; F[用户C]
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;每个节点都有唯一标识（DN）。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-ldap-能做什么&#34;&gt;4. LDAP 能做什么&lt;/h2&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;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-常见使用场景&#34;&gt;5. 常见使用场景&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;公司统一账号系统&lt;/li&gt;
&lt;li&gt;内部系统登录（单点登录基础）&lt;/li&gt;
&lt;li&gt;权限集中管理&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-常见实现&#34;&gt;6. 常见实现&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;OpenLDAP&lt;/li&gt;
&lt;li&gt;Active Directory（微软）&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-一句话总结&#34;&gt;7. 一句话总结&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;LDAP 是一个“按树组织的用户信息数据库 + 查询协议”，用于统一管理账号和组织结构&lt;/p&gt;
&lt;/blockquote&gt;</description>
    </item>
    <item>
      <title>SSH、跳板机和隧道</title>
      <link>https://leochu.work/blog/tech/engineering/ssh%E8%B7%B3%E6%9D%BF%E6%9C%BA%E5%92%8C%E9%9A%A7%E9%81%93/</link>
      <pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/ssh%E8%B7%B3%E6%9D%BF%E6%9C%BA%E5%92%8C%E9%9A%A7%E9%81%93/</guid>
      <description>&lt;h2 id=&#34;1-ssh-是什么&#34;&gt;1. SSH 是什么&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;SSH（Secure Shell）&lt;/strong&gt; 是一种用于远程登录和通信的加密协议。&lt;/p&gt;
&lt;h3 id=&#34;核心作用&#34;&gt;核心作用&lt;/h3&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;/ul&gt;
&lt;h3 id=&#34;简单理解&#34;&gt;简单理解&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;就像一根“加密的远程操作线”，你在本地操作，实际在远程执行。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;图示&#34;&gt;图示&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
A[本地电脑] -- SSH连接 --&amp;gt; B[远程服务器]
A --&amp;gt;|输入命令| B
B --&amp;gt;|返回结果| A
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h2 id=&#34;2-跳板机bastion-host是什么&#34;&gt;2. 跳板机（Bastion Host）是什么&lt;/h2&gt;
&lt;p&gt;跳板机是一台&lt;strong&gt;中间服务器&lt;/strong&gt;，用于访问内网机器。&lt;/p&gt;
&lt;h3 id=&#34;为什么需要&#34;&gt;为什么需要&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;内网服务器不能直接暴露到公网&lt;/li&gt;
&lt;li&gt;提供统一入口（安全控制、审计）&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;简单理解-1&#34;&gt;简单理解&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;像“门卫”，必须先经过它，才能进入内部系统。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;图示-1&#34;&gt;图示&lt;/h3&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
A[本地电脑] --&amp;gt; B[跳板机]
B --&amp;gt; C[内网服务器]
&lt;/code&gt;&lt;/pre&gt;&lt;h3 id=&#34;常见连接方式&#34;&gt;常见连接方式&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ssh -J user@跳板机 user@内网服务器&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-隧道ssh-tunnel是什么&#34;&gt;3. 隧道（SSH Tunnel）是什么&lt;/h2&gt;
&lt;p&gt;SSH 隧道是一种通过 SSH 转发网络流量的技术。&lt;/p&gt;
&lt;h3 id=&#34;本质&#34;&gt;本质&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;把本地请求“通过 SSH 转发到远程”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;31-本地端口转发local-forward&#34;&gt;3.1 本地端口转发（Local Forward）&lt;/h2&gt;
&lt;h3 id=&#34;示例&#34;&gt;示例&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;ssh -L 3307:127.0.0.1:3306 user@服务器&lt;/code&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>加密体系</title>
      <link>https://leochu.work/blog/tech/engineering/%E5%8A%A0%E5%AF%86%E4%BD%93%E7%B3%BB/</link>
      <pubDate>Wed, 08 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/%E5%8A%A0%E5%AF%86%E4%BD%93%E7%B3%BB/</guid>
      <description>&lt;h2 id=&#34;1-三个核心问题&#34;&gt;1. 三个核心问题&lt;/h2&gt;
&lt;p&gt;整个体系其实在解决三件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;你是谁？（身份认证）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;数据能不能被偷看？（加密传输）&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如何安全地交换密钥？（密钥分发）&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-非对称加密公钥--私钥&#34;&gt;2. 非对称加密（公钥 / 私钥）&lt;/h2&gt;
&lt;h3 id=&#34;核心定义&#34;&gt;核心定义&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;使用一对密钥：公钥 + 私钥&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;公钥：可以公开&lt;/li&gt;
&lt;li&gt;私钥：必须保密&lt;/li&gt;
&lt;li&gt;二者不可互相推导&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&#34;两种用途关键区分&#34;&gt;两种用途（关键区分）&lt;/h3&gt;
&lt;h4 id=&#34;-加密通信&#34;&gt;① 加密通信&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
A[发送方] --&amp;gt;|公钥加密| B[密文]
B --&amp;gt; C[接收方]
C --&amp;gt;|私钥解密| D[明文]
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h4 id=&#34;-身份认证ssh&#34;&gt;② 身份认证（SSH）&lt;/h4&gt;
&lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
participant A as 客户端（私钥）
participant B as 服务器（公钥）

B-&amp;gt;&amp;gt;A: challenge
A-&amp;gt;&amp;gt;B: signature
B-&amp;gt;&amp;gt;B: 公钥验证
B-&amp;gt;&amp;gt;A: 通过/拒绝
&lt;/code&gt;&lt;/pre&gt;&lt;hr&gt;
&lt;h3 id=&#34;关键公式&#34;&gt;关键公式&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;signature = Sign(私钥, challenge)&lt;/code&gt;&lt;br&gt;
&lt;code&gt;Verify(公钥, challenge, signature) = true&lt;/code&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&#34;本质总结&#34;&gt;本质总结&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;私钥负责“生成证明”，公钥负责“验证证明”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-ssh基于签名的认证机制&#34;&gt;3. SSH：基于签名的认证机制&lt;/h2&gt;
&lt;h3 id=&#34;文件结构&#34;&gt;文件结构&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-shell&#34; data-lang=&#34;shell&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;~/.ssh/id_ed25519      &lt;span style=&#34;color:#75715e&#34;&gt;# 私钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;~/.ssh/id_ed25519.pub  &lt;span style=&#34;color:#75715e&#34;&gt;# 公钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;~/.ssh/authorized_keys &lt;span style=&#34;color:#75715e&#34;&gt;# 服务器保存公钥&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;hr&gt;
&lt;h3 id=&#34;免密登录本质&#34;&gt;免密登录本质&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;服务器信任“某个公钥”，允许对应私钥登录&lt;/p&gt;</description>
    </item>
    <item>
      <title>Java虚拟机栈</title>
      <link>https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/</link>
      <pubDate>Sat, 15 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/</guid>
      <description>&lt;p&gt;与程序计数器一样，Java虚拟机栈（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧（Stack Frame）用于存储&lt;a href=&#34;https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/&#34;&gt;局部变量表&lt;/a&gt;、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。&lt;/p&gt;
&lt;p&gt;经常有人把Java内存区域笼统地划分为堆内存（Heap）和栈内存（Stack），这种划分方式直接继承自传统的C、C++程序的内存布局结构，在Java语言里就显得有些粗糙了，实际的内存区域划分要比这更复杂。不过这种划分方式的流行也间接说明了程序员最关注的、与对象内存分配关系最密切的区域是“堆”和“栈”两块。其中，“堆”在稍后笔者会专门讲述，而“栈”通常就是指这里讲的虚拟机栈，或者更多的情况下只是指虚拟机栈中局部变量表部分。&lt;/p&gt;
&lt;p&gt;在《Java虚拟机规范》中，对这个内存区域规定了两类异常状况：如果线程请求的栈深度大于虚拟机所允许的深度，将抛出StackOverflowError异常；如果Java虚拟机栈容量可以动态扩展，当栈扩展时无法申请到足够的内存会抛出OutOfMemoryError异常。&lt;/p&gt;</description>
    </item>
    <item>
      <title>java代码的运行</title>
      <link>https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/</link>
      <pubDate>Sat, 01 Mar 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/</guid>
      <description>&lt;h3 id=&#34;java-代码如何运行&#34;&gt;Java 代码如何运行&lt;/h3&gt;
&lt;p&gt;我们写的 Java 代码是高级语言，机器肯定是读不懂的。所以我们需要将它转换成机器能读懂的机器语言 (机器码)。 转换工作主要分为以下几个步骤：&lt;/p&gt;
&lt;h4 id=&#34;前端编译器&#34;&gt;前端编译器&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;javac&lt;/strong&gt; 就是前端编译器，可以将 java 文件编译成字节码组成的 class 文件。 java 代码如下：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;Info&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;static&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;main&lt;/span&gt;(String&lt;span style=&#34;color:#f92672&#34;&gt;[]&lt;/span&gt; args) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;int&lt;/span&gt; a &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 1;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        System.&lt;span style=&#34;color:#a6e22e&#34;&gt;out&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;println&lt;/span&gt;(a);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;复制代码
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;执行 &lt;strong&gt;javac Info.java&lt;/strong&gt; 生成 Info.class 文件, 再使用 &lt;strong&gt;javap -c Info.class 来&lt;/strong&gt;查看其中的字节码。&lt;/p&gt;
&lt;p&gt;class 中字节码内容如下：&lt;/p&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331151428.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331151428.png&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;解释器和即时编译器&#34;&gt;解释器和即时编译器&lt;/h4&gt;
&lt;p&gt;我们通过 javac 将 java 文件编译成 class 文件，当 jvm 启动加载 class，需要逐条执行字节码指令来完成程序功能。但是程序的执行还是得在机器上，但是机器是不认识字节码的，所以我们需要将字节码转换成机器码，这样才能让机器执行程序。 &lt;em&gt;什么是机器码？&lt;/em&gt; 机器码就是用二进制代码表示的计算机能直接识别和执行的一种机器指令的集合。 而解释器和即时编译器 (Just In Time Compiler，JIT) 就是 JVM 中将字节码转化为机器码的工具。&lt;/p&gt;
&lt;h5 id=&#34;解释器&#34;&gt;解释器&lt;/h5&gt;
&lt;p&gt;解释器是一行一行地将字节码解析成机器码，解释到哪就执行到哪，狭义地说，就是 for 循环 100 次，你就要将循环体中的代码逐行解释执行 100 次。当程序需要迅速启动和执行时，解释器可以首先发挥作用，省去编译的时间，立即执行。&lt;/p&gt;</description>
    </item>
    <item>
      <title>GC Roots</title>
      <link>https://leochu.work/blog/tech/java/gc-roots/</link>
      <pubDate>Sat, 15 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/gc-roots/</guid>
      <description>&lt;p&gt;·在虚拟机栈（栈帧中的本地变量表）中引用的对象，譬如各个线程被调用的方法堆栈中使用到的参数、局部变量、临时变量等。&lt;/p&gt;
&lt;p&gt;·在方法区中类静态属性引用的对象，譬如Java类的引用类型静态变量。&lt;/p&gt;
&lt;p&gt;·在方法区中常量引用的对象，譬如字符串常量池（String Table）里的引用。&lt;/p&gt;
&lt;p&gt;·在本地方法栈中JNI（即通常所说的Native方法）引用的对象。&lt;/p&gt;
&lt;p&gt;·Java虚拟机内部的引用，如基本数据类型对应的Class对象，一些常驻的异常对象（比如NullPointExcepiton、OutOfMemoryError）等，还有系统类加载器。&lt;/p&gt;
&lt;p&gt;·所有被同步锁（synchronized关键字）持有的对象。&lt;/p&gt;
&lt;p&gt;·反映Java虚拟机内部情况的JMXBean、JVMTI中注册的回调、本地代码缓存等。&lt;/p&gt;
&lt;p&gt;除了这些固定的GC Roots集合以外，根据用户所选用的垃圾收集器以及当前回收的内存区域不同，还可以有其他对象“临时性”地加入，共同构成完整GC Roots集合。譬如后文将会提到的分代收集和局部回收（Partial GC），如果只针对Java堆中某一块区域发起垃圾收集时（如最典型的只针对新生代的垃圾收集），必须考虑到内存区域是虚拟机自己的实现细节（在用户视角里任何内存区域都是不可见的），更不是孤立封闭的，所以某个区域里的对象完全有可能被位于堆中其他区域的对象所引用，这时候就需要将这些关联区域的对象也一并加入GC Roots集合中去，才能保证可达性分析的正确性。&lt;/p&gt;
&lt;p&gt;目前最新的几款垃圾收集器 无一例外都具备了局部回收的特征，为了避免GC Roots包含过多对象而过度膨胀，它们在实现上也做出了各种优化处理。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Exact VM</title>
      <link>https://leochu.work/blog/tech/java/exact-vm/</link>
      <pubDate>Sat, 01 Feb 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/exact-vm/</guid>
      <description>&lt;p&gt;Exact VM因它使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）而得名。&lt;/p&gt;
&lt;p&gt;准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。&lt;/p&gt;
&lt;p&gt;譬如内存中有一个32bit的整数123456，虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数，准确分辨出哪些内存是引用类型，这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。&lt;/p&gt;
&lt;p&gt;由于使用了准确式内存管理，Exact VM可以抛弃掉以前Classic VM基于句柄（Handle）的对象查找方式（原因是垃圾收集后对象将可能会被移动位置，如果地址为123456的对象移动到654321，在没有明确信息表明内存中哪些数据是引用类型的前提下，那虚拟机肯定是不敢把内存中所有为123456的值改成654321的，所以要使用句柄来保持引用值的稳定），这样每次定位对象都少了一次间接查找的开销，显著提升执行性能。&lt;/p&gt;</description>
    </item>
    <item>
      <title>深入理解jvm</title>
      <link>https://leochu.work/blog/reading/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3jvm/</link>
      <pubDate>Sat, 04 Jan 2025 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/reading/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3jvm/</guid>
      <description>&lt;h2 id=&#34;第一部分-走进java&#34;&gt;第一部分 走进java&lt;/h2&gt;
&lt;h3 id=&#34;第一章-走进java&#34;&gt;第一章 走进java&lt;/h3&gt;
&lt;h4 id=&#34;12-java技术体系&#34;&gt;1.2 java技术体系&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331145417.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331145417.png&#34;&gt;&lt;/p&gt;
&lt;h4 id=&#34;13-java发展史&#34;&gt;1.3 java发展史&lt;/h4&gt;
&lt;blockquote&gt;
&lt;p&gt;Jigsaw项目：虚拟机层面的模块化支持。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;DLL HELL :  &lt;a href=&#34;https://leochu.work/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/&#34;&gt;模块地狱&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;h4 id=&#34;14-jvm家族&#34;&gt;1.4 jvm家族&lt;/h4&gt;
&lt;h5 id=&#34;141虚拟机始祖sun-classicexact-vm&#34;&gt;1.4.1　虚拟机始祖：Sun Classic/Exact VM&lt;/h5&gt;
&lt;p&gt;编译器/解释器/即时编译器 : &lt;a href=&#34;https://leochu.work/blog/tech/java/java%E4%BB%A3%E7%A0%81%E7%9A%84%E8%BF%90%E8%A1%8C/&#34;&gt;java代码的运行&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/exact-vm/&#34;&gt;Exact VM&lt;/a&gt; 使用准确式内存管理（Exact Memory Management，也可以叫Non-Con-servative/Accurate Memory Management）&lt;/p&gt;
&lt;h5 id=&#34;142武林盟主hotspot-vm&#34;&gt;1.4.2　武林盟主：HotSpot VM&lt;/h5&gt;
&lt;p&gt;HotSpot虚拟机是Sun/OracleJDK和OpenJDK中的默认Java虚拟机，也是目前使用范围最广的Java虚拟机。&lt;/p&gt;
&lt;h4 id=&#34;16实战自己编译jdk&#34;&gt;1.6　实战：自己编译JDK&lt;/h4&gt;
&lt;h5 id=&#34;161获取源码&#34;&gt;1.6.1　获取源码&lt;/h5&gt;
&lt;p&gt;&lt;a href=&#34;https://hg.openjdk.java.net/jdk/jdk12/&#34;&gt;OpenJDK 12地址&lt;/a&gt;
&lt;img alt=&#34;Pasted image 20230331162843.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331162843.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/jdk8%E5%92%8Cjdk8u%E6%9C%89%E4%BB%80%E4%B9%88%E5%8C%BA%E5%88%AB/&#34;&gt;jdk8和jdk8u有什么区别&lt;/a&gt;?&lt;/p&gt;
&lt;h5 id=&#34;162系统需求&#34;&gt;1.6.2　系统需求&lt;/h5&gt;
&lt;p&gt;推荐linux 64位和mac os 64位&lt;/p&gt;
&lt;p&gt;查看位数命令:
&lt;code&gt;getconf LONG_BIT&lt;/code&gt;&lt;/p&gt;
&lt;h2 id=&#34;第二部分自动内存管理&#34;&gt;第二部分　自动内存管理&lt;/h2&gt;
&lt;h3 id=&#34;第2章java内存区域与内存溢出异常&#34;&gt;第2章　Java内存区域与内存溢出异常&lt;/h3&gt;
&lt;h4 id=&#34;22运行时数据区域&#34;&gt;2.2　运行时数据区域&lt;/h4&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230331171948.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230331171948.png&#34;&gt;&lt;/p&gt;
&lt;h5 id=&#34;221程序计数器&#34;&gt;2.2.1　程序计数器&lt;/h5&gt;
&lt;p&gt;&lt;a href=&#34;https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/&#34;&gt;程序计数器&lt;/a&gt;（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。&lt;/p&gt;
&lt;h5 id=&#34;222java虚拟机栈&#34;&gt;2.2.2　Java虚拟机栈&lt;/h5&gt;
&lt;p&gt;与程序计数器一样，&lt;a href=&#34;https://leochu.work/blog/tech/java/java%E8%99%9A%E6%8B%9F%E6%9C%BA%E6%A0%88/&#34;&gt;Java虚拟机栈&lt;/a&gt;（Java Virtual Machine Stack）也是线程私有的，它的生命周期与线程相同。虚拟机栈描述的是Java方法执行的线程内存模型：每个方法被执行的时候，Java虚拟机都会同步创建一个栈帧 用于存储局部变量表、操作数栈、动态连接、方法出口等信息。每一个方法被调用直至执行完毕的过程，就对应着一个栈帧在虚拟机栈中从入栈到出栈的过程。&lt;/p&gt;</description>
    </item>
    <item>
      <title>局部变量表</title>
      <link>https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/</link>
      <pubDate>Sat, 21 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E8%A1%A8/</guid>
      <description>&lt;p&gt;局部变量表存放了编译期可知的各种Java虚拟机基本数据类型（boolean、byte、char、short、int、float、long、double）、对象引用（reference类型，它并不等同于对象本身，可能是一个指向对象起始地址的引用指针，也可能是指向一个代表对象的句柄或者其他与此对象相关的位置）和returnAddress类型（指向了一条字节码指令的地址）。&lt;/p&gt;
&lt;p&gt;这些数据类型在局部变量表中的存储空间以局部变量槽（Slot）来表示，其中64位长度的long和double类型的数据会占用两个变量槽，其余的数据类型只占用一个。局部变量表所需的内存空间在编译期间完成分配，当进入一个方法时，这个方法需要在栈帧中分配多大的局部变量空间是完全确定的，在方法运行期间不会改变局部变量表的大小。请读者注意，这里说的“大小”是指变量槽的数量，虚拟机真正使用多大的内存空间（譬如按照1个变量槽占用32个比特、64个比特，或者更多）来实现一个变量槽，这是完全由具体的虚拟机实现自行决定的事情。&lt;/p&gt;</description>
    </item>
    <item>
      <title>程序计数器</title>
      <link>https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/</link>
      <pubDate>Sat, 07 Dec 2024 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/java/%E7%A8%8B%E5%BA%8F%E8%AE%A1%E6%95%B0%E5%99%A8/</guid>
      <description>&lt;p&gt;程序计数器（Program Counter Register）是一块较小的内存空间，它可以看作是当前线程所执行的字节码的行号指示器。在Java虚拟机的概念模型里，字节码解释器工作时就是通过改变这个计数器的值来选取下一条需要执行的字节码指令，它是程序控制流的指示器.&lt;/p&gt;
&lt;p&gt;由于Java虚拟机的多线程是通过线程轮流切换、分配处理器执行时间的方式来实现的，在任何一个确定的时刻，一个处理器（对于多核处理器来说是一个内核）都只会执行一条线程中的指令。因此，为了线程切换后能恢复到正确的执行位置，每条线程都需要有一个独立的程序计数器，各条线程之间计数器互不影响，独立存储，我们称这类内存区域为“线程私有”的内存。&lt;/p&gt;
&lt;p&gt;如果线程正在执行的是一个Java方法，这个计数器记录的是正在执行的虚拟机字节码指令的地址；如果正在执行的是本地（Native）方法，这个计数器值则应为空（Undefined）。此内存区域是唯一一个在《Java虚拟机规范》中没有规定任何OutOfMemoryError情况的区域。&lt;/p&gt;</description>
    </item>
    <item>
      <title>restful风格</title>
      <link>https://leochu.work/blog/tech/engineering/restful%E9%A3%8E%E6%A0%BC/</link>
      <pubDate>Sat, 13 Apr 2024 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/restful%E9%A3%8E%E6%A0%BC/</guid>
      <description>&lt;h3 id=&#34;一什么是-rest-一种软件架构风格&#34;&gt;一、什么是 REST ？（一种软件架构风格）&lt;/h3&gt;
&lt;p&gt;缩写：REST （不是&amp;quot;rest&amp;quot;这个单词）
外文名：Representational State Transfer，简称REST。
中文名：表现层状态转移。&lt;/p&gt;
&lt;p&gt;提出时间：2000年。
属性：一种软件架构风格。（以Web为平台的。web服务的架构风格，前后端接口时候用到。）&lt;/p&gt;
&lt;p&gt;REST之所以晦涩难懂，是因为前面主语（Resource ）被去掉了。
全称是： Resource Representational State Transfer。
通俗来讲就是：资源在网络中以某种表现形式进行状态转移。&lt;/p&gt;
&lt;p&gt;分解开来讲解:
Resource：资源，即数据（这是网络的核心）；
Representational：某种表现形式，比如用JSON，XML，JPEG等；
State Transfer：状态变化。通过HTTP的动词（get查询、post新增、put修改、delete删除）实现。&lt;/p&gt;
&lt;p&gt;一句话描述 REST 实质：&lt;br&gt;
URL 中只使用名词来定位资源，用 HTTP 协议里的动词（GET、POST、PUT、DELETE）来实现资源的增删改查操作。&lt;/p&gt;
&lt;p&gt;什么意思呢？&lt;/p&gt;
&lt;p&gt;比如，我们有一个 friends 接口，对于 “朋友” 我们有增删改查四种操作，怎么定义 REST 接口？&lt;/p&gt;
&lt;p&gt;增加一个朋友，uri: generalcode.cn/v1/friends 接口类型：POST&lt;br&gt;
删除一个朋友，uri: generalcode.cn/va/friends 接口类型：DELETE（在 http 的 parameter 指定好友 id）&lt;br&gt;
修改一个朋友，uri: generalcode.cn/va/friends 接口类型：PUT（在 http 的 parameter 指定好友 id）&lt;br&gt;
查找一个朋友，uri: generalcode.cn/va/friends 接口类型：GET&lt;/p&gt;
&lt;p&gt;上面我们定义的四个接口就是符合 REST 协议的，请注意，这几个接口都没有动词，只有名词 friends，都是通过 Http 请求的接口类型来判断是什么业务操作。&lt;/p&gt;
&lt;p&gt;举个反例：
generalcode.cn/va/deleteFriends 该接口用来表示删除朋友，这就是不符合REST协议的接口。
不能用deleteFriends ，而应该就用friends + http请求的delete方式。&lt;/p&gt;</description>
    </item>
    <item>
      <title>模块地狱</title>
      <link>https://leochu.work/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/</link>
      <pubDate>Sun, 10 Dec 2023 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/%E6%A8%A1%E5%9D%97%E5%9C%B0%E7%8B%B1/</guid>
      <description>&lt;p&gt;个人理解: 有点类似依赖冲突,循环依赖&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;/ul&gt;</description>
    </item>
    <item>
      <title>关于设计模式</title>
      <link>https://leochu.work/blog/tech/engineering/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%A6%82%E8%BF%B0/</link>
      <pubDate>Sun, 01 Oct 2023 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/engineering/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F%E6%A6%82%E8%BF%B0/</guid>
      <description>&lt;h2 id=&#34;策略模式strategy-pattern&#34;&gt;【策略模式】（Strategy Pattern）&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230420121518.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230420121518.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;其中的Context是一个所谓的“上下文”，不一定非得是个类，也可以只是一个函数/方法。最关键的是，我们其实根本不需要ConcreteStrategyX类，也不需要它们的对象。我们要的只是一个execute函数而已，我们甚至连execute这个函数名都不需要，只要能执行它就行。&lt;/p&gt;
&lt;p&gt;看看函数式编程是怎么玩的（这里以大家都熟悉的JavaScript为例）：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 做些什么...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;result&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;func&lt;/span&gt;()  &lt;span style=&#34;color:#75715e&#34;&gt;// 有需要的话可以传参
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 再做些什么...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;context&lt;/span&gt;(&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 做些什么...
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 是否需要返回值看需求
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;})
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;一个匿名函数参数&lt;code&gt;func&lt;/code&gt;搞定。&lt;/p&gt;
&lt;p&gt;Java能搞出这种类图来，全都是因为Java没有一等公民的函数，所以函数必须依附于类（静态方法）或者对象（非静态方法）。而玩静态方法又没法玩多态，而且类不能当成参数传递给其他函数/方法，所以只能用对象携带方法。而对象的方法必须有个名称，为了统一，就叫execute。由于需要多态，所以我们必须提一个接口出来，在接口里声明execute的方法签名。所有这一切说白了都是为了讨好Java编译器，否则它会给你颜色（red）看。&lt;/p&gt;
&lt;p&gt;当然，自从Java有了函数式接口和lambda后一定程度上也能玩函数式编程了。&lt;/p&gt;
&lt;h2 id=&#34;观察者模式observer-pattern&#34;&gt;【观察者模式】（Observer Pattern）&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230420121526.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230420121526.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这个在JS里大家已经熟悉到不能再熟悉了：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;someButton&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;onclick&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;event&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 处理点击事件
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;又是一个匿名函数搞定！上图里的&lt;code&gt;Subject#attach&lt;/code&gt;在这里就是直接赋值。&lt;code&gt;Subject#detach&lt;/code&gt;就是赋空值。&lt;code&gt;Subject#notify&lt;/code&gt;就是调用一下匿名函数而已。而&lt;code&gt;Observer#update&lt;/code&gt;就是匿名函数本身。&lt;/p&gt;
&lt;h2 id=&#34;访问者模式visitor-pattern&#34;&gt;【访问者模式】（Visitor Pattern）&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230420121532.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230420121532.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;能整成这样我也是服了。说白了不就是访问者需要判断一下元素类型嘛。直接套用策略模式，在匿名函数里if-else一下不就行了？（当然还有其他方式，比如引入一个工厂。有模式匹配的函数式编程语言如Haskell、Erlang、Elixir等玩起来更简单）。&lt;/p&gt;
&lt;h2 id=&#34;装饰器模式decorator-pattern&#34;&gt;【装饰器模式】（Decorator Pattern）&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&#34;Pasted image 20230420121538.png&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/Pasted%20image%2020230420121538.png&#34;&gt;&lt;/p&gt;
&lt;p&gt;这才是函数式编程的魅力所在！&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;core1&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arg1&lt;/span&gt;, &lt;span style=&#34;color:#a6e22e&#34;&gt;arg2&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 做些啥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;123&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;core2&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;arg&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 做些啥
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;456&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decorate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;core&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#75715e&#34;&gt;// 做些准备工作
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;function&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 做些啥，甚至可以改变参数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ret&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;core&lt;/span&gt;(...&lt;span style=&#34;color:#a6e22e&#34;&gt;arguments&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;// 做些啥，甚至可以改变返回值
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;ret&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;  }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decorated1&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decorate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;core1&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decorated2&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;decorate&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;core2&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;酷吧？又是匿名函数搞定！只不过这次的匿名函数是返回值。&lt;/p&gt;</description>
    </item>
    <item>
      <title>sql中!=的陷阱</title>
      <link>https://leochu.work/blog/tech/database/sql%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1/</link>
      <pubDate>Sun, 06 Aug 2023 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/database/sql%E4%B8%AD%E7%9A%84%E9%99%B7%E9%98%B1/</guid>
      <description>&lt;p&gt;mysql/SQL server有，clickhouse也有，hive未验证。&lt;/p&gt;
&lt;p&gt;null 值的比较这里另外说下 SQL 里 null 值的比较，任何与 null 值的比较结果，最后都会变成 null，以PostgreSQL为例，如下：select null != null;&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;以上结果都是 null，而不是什么 true 或者 false。另外有些函数是不支持 null 值作为输入参数的，比如count()或者sum()等。&lt;/p&gt;
&lt;p&gt;在写 SQL 条件语句时经常用到 不等于 &lt;code&gt;!=&lt;/code&gt; 的筛选条件。此时要注意此条件会将字段为 &lt;code&gt;Null&lt;/code&gt; 的数据也当做满足不等于的条件而将数据筛选掉。（也就是说会忽略过滤掉为 null 的数据，导致数据不准确）。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; A &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; B1 &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; 
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;不会查出null值&lt;/p&gt;
&lt;p&gt;修改：&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; A &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; B1 &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;OR&lt;/span&gt; B1 &lt;span style=&#34;color:#66d9ef&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;Null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;SELECT&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;FROM&lt;/span&gt; A &lt;span style=&#34;color:#66d9ef&#34;&gt;WHERE&lt;/span&gt; IFNULL(B1,&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;&amp;#39;&lt;/span&gt;)  &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;为什么会这样呢？这还得&lt;strong&gt;从 mysql 的底层开始说起，因为 NULL 不是一个「值」，而是「没有值」&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;　　&lt;strong&gt;「没有值」不满足「值不等于1」这个条件，怎么解决可以使用 ifnull() 方法，将 null 转为空字符串，或者这个字段做出判断 加上OR a is null。当然使用 ifnull 相率会更好&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;select null = null返回也是null。null只能用is或is not&lt;/p&gt;</description>
    </item>
    <item>
      <title>left join 谓词下推</title>
      <link>https://leochu.work/blog/tech/bigdata/left-join%E8%B0%93%E8%AF%8D%E4%B8%8B%E6%8E%A8/</link>
      <pubDate>Sun, 19 Feb 2023 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/bigdata/left-join%E8%B0%93%E8%AF%8D%E4%B8%8B%E6%8E%A8/</guid>
      <description>&lt;p&gt;zzz1&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;uid&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;is_delete&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;2&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;3&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;zzz2&lt;/td&gt;
          &lt;td&gt;&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;uid&lt;/th&gt;
          &lt;th style=&#34;text-align: left&#34;&gt;is_delete&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;1&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;2&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;1&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;3&lt;/td&gt;
          &lt;td style=&#34;text-align: left&#34;&gt;0&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; zzz1 &lt;span style=&#34;color:#66d9ef&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; zzz2 &lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; zzz1.uid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; zzz2.uid
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; zzz1.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;and&lt;/span&gt; zzz2.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果:
3       0       3       0&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; zzz1 &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; zzz1.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) z1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; zzz2 &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; zzz2.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) z2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; z1.uid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; z2.uid
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果:
2       0       NULL    NULL
3       0       3       0&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; zzz1 &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; zzz1.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) z1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;left&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;join&lt;/span&gt; (&lt;span style=&#34;color:#66d9ef&#34;&gt;select&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;from&lt;/span&gt; zzz2 &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; zzz2.is_delete &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;0&lt;/span&gt;) z2
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;on&lt;/span&gt; z1.uid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; z2.uid &lt;span style=&#34;color:#66d9ef&#34;&gt;where&lt;/span&gt; z2.uid &lt;span style=&#34;color:#66d9ef&#34;&gt;is&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;not&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;结果:
3       0       3       0&lt;/p&gt;</description>
    </item>
    <item>
      <title>kafka在zk中的目录结构</title>
      <link>https://leochu.work/blog/tech/bigdata/kafka%E5%9C%A8zk%E4%B8%AD%E7%9A%84%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84/</link>
      <pubDate>Sun, 22 Jan 2023 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/bigdata/kafka%E5%9C%A8zk%E4%B8%AD%E7%9A%84%E7%9B%AE%E5%BD%95%E7%BB%93%E6%9E%84/</guid>
      <description>&lt;p&gt;&lt;img alt=&#34;kafkaInZK&#34; loading=&#34;lazy&#34; src=&#34;https://leochu.work/blog/resource/kafkaInZK.png&#34;&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>Kafka 中的控制器组件</title>
      <link>https://leochu.work/blog/tech/bigdata/kafka%E6%8E%A7%E5%88%B6%E5%99%A8%E7%BB%84%E4%BB%B6/</link>
      <pubDate>Sun, 25 Dec 2022 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/bigdata/kafka%E6%8E%A7%E5%88%B6%E5%99%A8%E7%BB%84%E4%BB%B6/</guid>
      <description>&lt;p&gt;&lt;strong&gt;控制器组件（Controller），是 Apache Kafka 的核心组件。它的主要作用是在 Apache ZooKeeper 的帮助下管理和协调整个 Kafka 集群&lt;/strong&gt;。集群中任意一台 Broker 都能充当控制器的角色，但是，在运行过程中，只能有一个 Broker 成为控制器，行使其管理和协调的职责。换句话说，每个正常运转的 Kafka 集群，在任意时刻都有且只有一个控制器。官网上有个名为 activeController 的 JMX 指标，可以帮助我们实时监控控制器的存活状态。这个 JMX 指标非常关键，你在实际运维操作过程中，一定要实时查看这个指标的值。下面，我们就来详细说说控制器的原理和内部运行机制。&lt;/p&gt;
&lt;p&gt;在开始之前，我先简单介绍一下 Apache ZooKeeper 框架。要知道，&lt;strong&gt;控制器是重度依赖 ZooKeeper&lt;/strong&gt; 的，因此，我们有必要花一些时间学习下 ZooKeeper 是做什么的。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Apache ZooKeeper 是一个提供高可靠性的分布式协调服务框架&lt;/strong&gt;。它使用的数据模型类似于文件系统的树形结构，根目录也是以 “/” 开始。该结构上的每个节点被称为 znode，用来保存一些元数据协调信息。&lt;/p&gt;
&lt;p&gt;如果以 znode 持久性来划分，&lt;strong&gt;znode 可分为持久性 znode 和临时 znode&lt;/strong&gt;。持久性 znode 不会因为 ZooKeeper 集群重启而消失，==而临时 znode 则与创建该 znode 的 ZooKeeper 会话绑定，一旦会话结束，该节点会被自动删除==。&lt;/p&gt;
&lt;p&gt;==ZooKeeper 赋予客户端监控 znode 变更的能力，即所谓的 Watch 通知功能。一旦 znode 节点被创建、删除，子节点数量发生变化，抑或是 znode 所存的数据本身变更，ZooKeeper 会通过节点变更监听器 (ChangeHandler) 的方式显式通知客户端。==&lt;/p&gt;
&lt;p&gt;依托于这些功能，ZooKeeper 常被用来实现&lt;strong&gt;集群成员管理、分布式锁、领导者选举&lt;/strong&gt;等功能。==Kafka 控制器大量使用 Watch 功能实现对集群的协调管理==。我们一起来看一张图片，它展示的是 Kafka 在 ZooKeeper 中创建的 znode 分布。你不用了解每个 znode 的作用，但你可以大致体会下 Kafka 对 ZooKeeper 的依赖。&lt;/p&gt;</description>
    </item>
    <item>
      <title>flume动态加载配置原理</title>
      <link>https://leochu.work/blog/tech/bigdata/flume%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86/</link>
      <pubDate>Sun, 12 Jun 2022 00:00:00 +0000</pubDate>
      <guid>https://leochu.work/blog/tech/bigdata/flume%E5%8A%A8%E6%80%81%E5%8A%A0%E8%BD%BD%E9%85%8D%E7%BD%AE%E5%8E%9F%E7%90%86/</guid>
      <description>&lt;h2 id=&#34;flume-ng支持运行时动态修改配置的配置模块&#34;&gt;Flume NG支持运行时动态修改配置的配置模块&lt;/h2&gt;
&lt;p&gt;细说一下PollingPropertiesFileConfigurationProvider提供的运行时动态修改配置并生效的能力。&lt;/p&gt;
&lt;p&gt;要实现动态修改配置文件并生效，主要有两个待实现的功能&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;观察配置文件是否修改&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果修改，将修改的内容通知给观察者&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对于第一点，监控配置文件是否修改，Flume NG定义了一个FileWatcherRunnable对象来监控配置文件，启动了一个单独的线程采用定时轮询的方式来监控，轮询频率是30毫秒一次，比较file.lastModified属性与lastChange时间戳，当file.lastModified &amp;gt; lastChange时表示文件被修改&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-java&#34; data-lang=&#34;java&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FileWatcherRunnable&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;implements&lt;/span&gt; Runnable {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; File file;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;final&lt;/span&gt; CounterGroup counterGroup;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; lastChange;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;FileWatcherRunnable&lt;/span&gt;(File file, CounterGroup counterGroup) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;super&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;file&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; file;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;counterGroup&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; counterGroup;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;this&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;lastChange&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; 0L;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Override&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;run&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;debug&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Checking file:{} for changes&amp;#34;&lt;/span&gt;, file);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      counterGroup.&lt;span style=&#34;color:#a6e22e&#34;&gt;incrementAndGet&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;file.checks&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;long&lt;/span&gt; lastModified &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; file.&lt;span style=&#34;color:#a6e22e&#34;&gt;lastModified&lt;/span&gt;();
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; (lastModified &lt;span style=&#34;color:#f92672&#34;&gt;&amp;gt;&lt;/span&gt; lastChange) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Reloading configuration file:{}&amp;#34;&lt;/span&gt;, file);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        counterGroup.&lt;span style=&#34;color:#a6e22e&#34;&gt;incrementAndGet&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;file.loads&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        lastChange &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; lastModified;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          eventBus.&lt;span style=&#34;color:#a6e22e&#34;&gt;post&lt;/span&gt;(getConfiguration());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (Exception e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to load configuration data. Exception follows.&amp;#34;&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (NoClassDefFoundError e) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Failed to start agent because dependencies were not &amp;#34;&lt;/span&gt; &lt;span style=&#34;color:#f92672&#34;&gt;+&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;              &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;found in classpath. Error follows.&amp;#34;&lt;/span&gt;, e);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        } &lt;span style=&#34;color:#66d9ef&#34;&gt;catch&lt;/span&gt; (Throwable t) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          &lt;span style=&#34;color:#75715e&#34;&gt;// caught because the caller does not handle or log Throwables&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;          LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;error&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Unhandled error&amp;#34;&lt;/span&gt;, t);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;      }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// PollingPropertiesFileConfigurationProvider.start()启动一个单独的线程来监控properties配置文件&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;public&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;void&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;start&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;info&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Configuration provider starting&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    Preconditions.&lt;span style=&#34;color:#a6e22e&#34;&gt;checkState&lt;/span&gt;(file &lt;span style=&#34;color:#f92672&#34;&gt;!=&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;null&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;The parameter file must not be null&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    executorService &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; Executors.&lt;span style=&#34;color:#a6e22e&#34;&gt;newSingleThreadScheduledExecutor&lt;/span&gt;(
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;            &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; ThreadFactoryBuilder().&lt;span style=&#34;color:#a6e22e&#34;&gt;setNameFormat&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;conf-file-poller-%d&amp;#34;&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;                .&lt;span style=&#34;color:#a6e22e&#34;&gt;build&lt;/span&gt;());
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    FileWatcherRunnable fileWatcherRunnable &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;new&lt;/span&gt; FileWatcherRunnable(file, counterGroup);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    executorService.&lt;span style=&#34;color:#a6e22e&#34;&gt;scheduleWithFixedDelay&lt;/span&gt;(fileWatcherRunnable, 0, interval,
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        TimeUnit.&lt;span style=&#34;color:#a6e22e&#34;&gt;SECONDS&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    lifecycleState &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; LifecycleState.&lt;span style=&#34;color:#a6e22e&#34;&gt;START&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    LOGGER.&lt;span style=&#34;color:#a6e22e&#34;&gt;debug&lt;/span&gt;(&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#34;Configuration provider started&amp;#34;&lt;/span&gt;);
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;           }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;对于第二点，利用Guava EventBus提供的发布订阅模式机制，将配置修改封装成事件传递给Application，来重新加载配置&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
