<?xml version="1.0" encoding="utf-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>funnyang's Blog</title>
        <link>https://blog.xiaoyuyu.cn</link>
        <description>与其曲谨，不若疏狂</description>
        <atom:link href="https://blog.xiaoyuyu.cn/rss.html" rel="self" />
        <atom:link href="" rel="hub" />
        <language>zh-CN</language>
        <lastBuildDate>Wed, 06 May 2026 07:58:14 +0000</lastBuildDate>
        
        <item>
            <title>Mac 切换显示器输入源</title>
            <link>https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html</guid>
            <description>
                <![CDATA[<blockquote>
<p>由于只有 1 台显示器，经常需要切换不同的输入源，在显示器上切换很麻烦，于是开始研究是否可以实现自动切换。</p>

<blockquote>
<p>由于本人只需要在 mac 下使用，故仅记录了 mac 的内容。</p>
</blockquote>

<p>前提条件：</p>

<ul>
<li>显示器支持 DDC/CI 协议</li>
</ul>

<h2 id="toc_0">命令行切换</h2>

<p>Mac下有两个工具可以使用，详细的区别可参考<a href="https://github.com/kfix/ddcctl/issues/86#issuecomment-1912002850" rel="nofollow">这里</a></p>

<ul>
<li><a href="https://github.com/kfix/ddcctl" rel="nofollow">ddcctl</a> 仅支持 Intel</li>
<li><a href="https://github.com/waydabber/m1ddc" rel="nofollow">m1ddc</a> 仅支持 Apple Silicon</li>
</ul>

<p>用法示例：</p>

<pre><code class="language-bash">ddcctl -d 1 -i 15

m1ddc display {uuid} set input 15
</code></pre>

<h2 id="toc_1">快捷键切换</h2>

<p>命令行足够轻量级，却不够方便，我们可以使用 mac 自带的 Automator 添加快捷键。步骤如下：</p>

<ol>
<li><p>打开 Automator，选择「快速操作」
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/longshot20241128172303.png" alt="longshot20241128172303.png" /></p></li>

<li><p>填写切换显示器命令
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/longshot20241128172506.png" alt="longshot20241128172506.png=600x" /></p></li>

<li><p>保存
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/longshot20241128172544.png" alt="longshot20241128172544.png=600x" /></p></li>

<li><p>在系统中配置键盘快捷键
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/longshot20241128172625.png" alt="longshot20241128172625.png=600x" /></p></li>
</ol>

<h2 id="toc_2">唤醒时自动切换输入源</h2>

<p>唤醒时，自动切换显示器，需要用到 <a href="https://formulae.brew.sh/formula/sleepwatcher" rel="nofollow">sleepwatcher</a></p>

<blockquote>
<p><strong>2026.04.07 更新</strong>: 建议使用 hammerspoon 了，事件更丰富，定制更灵活</p>
</blockquote>

<pre><code>brew install sleepwatcher
brew services start sleepwatcher
</code></pre>

<p>唤醒和睡眠时会分别执行 <code>~/.wakeup</code> 和 <code>~/.sleep</code>，新建文件如下</p>

<pre><code>touch ~/.wakeup
chmod 755 ~/.wakeup
</code></pre>

<p>在 <code>.wakeup</code> 中写入要执行的命令即可。</p>

<p>这里尝试基于 <code>m1ddc</code> 写了脚本，供参考。<del><code>ddcctl</code> 获取当前输入源会失败，直接写入切换命令就好了。</del></p>

<pre><code class="language-bash">#!/bin/bash

m1ddc() {
    /usr/local/bin/m1ddc $@
}

switch_input_source() {
    local monitor_uuid=$1
    local monitor_input=$2
    if [ $(m1ddc display list | grep -c $monitor_uuid) -eq 1 ]; then
        currentInput=$(m1ddc display $monitor_uuid get input)
        if [ $currentInput == $monitor_input ]; then
            echo &quot;无需切换&quot;
        else
            echo &quot;需要切换&quot;
            m1ddc display $monitor_uuid set input $monitor_input
        fi
    fi
}

# 指定显示器
switch_input_source &quot;C315CEDD-8D5B-4F19-A7F0-A88CEB9CEC21&quot; 15
</code></pre>

<h2 id="toc_3">遗留问题</h2>

<p>1, 若当前输入源正确，重新执行切换会闪烁</p>

<p>m1ddc 可以获取当前输入源，可以写逻辑解决，<del>ddcctl 则不行</del></p>

<p><strong>2026.04.07 更新</strong>: 实际是因为 macOS 使用 HDMI 连接时，对 DCC 协议支持不完善导致，换成 DP 线则正常</p>

<p>2, 唤醒其实不是一个准确的事件，比如合盖然后开盖可能不会触发，目前没有找到轻量级的方案。</p>

<p><strong>2026.04.07 更新</strong>: 可以通过 hammerspoon 实现开盖检测，参考 AI 给出的触发代码 <code>~/.hammerspoon/init.lua</code></p>

<pre><code>local function triggerSwitch()
    -- hs.alert.show(&quot;✅ 硬件级判定：已开盖，执行切换！&quot;)
    -- hs.execute(&quot;/bin/bash switch-monitor.sh&quot;)
end

-- =====================================================================
-- 辅助函数：通过 ioreg 真正读取物理盖子的开合状态
-- 返回 true 表示盖子是开着的，false 表示合盖
-- =====================================================================
local function isLidOpen()
    -- 执行底层的 ioreg 命令
    local output, status, type, rc = hs.execute(&quot;ioreg -r -k AppleClamshellState -d 4 | grep AppleClamshellState&quot;)
    
    -- 如果输出里包含 &quot;No&quot;，说明 AppleClamshellState = No (即没合盖)
    if string.find(output, &quot;No&quot;) then
        return true
    else
        return false
    end
end

-- =====================================================================
-- 监听器：结合底层硬件状态的精确拦截
-- =====================================================================
-- 记录上一次探测到的物理状态
local wasLidOpen = isLidOpen()

local function exactScreenCallback()
    -- 屏幕状态发生任何变化时，去读一下真实的物理盖子传感器
    local currentlyOpen = isLidOpen()
    
    -- 核心逻辑：只有当“之前是合盖”且“现在是开盖”时，才触发！
    -- 这样插拔其他外接显示器绝对不会引起误触发。
    if (not wasLidOpen) and currentlyOpen then
        print(&quot;探测到物理开盖动作&quot;)
        triggerSwitch()
    end
    
    -- 更新状态记录
    wasLidOpen = currentlyOpen
end

-- screen.watcher 作为触发“钩子”，但用硬件状态做“校验”
screenWatcher = hs.screen.watcher.new(exactScreenCallback)
screenWatcher:start()
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html">https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html</a>，<a href="https://blog.xiaoyuyu.cn/post/mac-switch-monitor-input-source.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>Rime 输入法配置</title>
            <link>https://blog.xiaoyuyu.cn/post/rime-custom.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/rime-custom.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/rime-custom.html</guid>
            <description>
                <![CDATA[<blockquote>

<p>通常会使用现成的配置，如<a href="https://github.com/gaboolic/rime-frost" rel="nofollow">白霜词库</a>、<a href="https://github.com/iDvel/rime-ice" rel="nofollow">雾凇拼音</a>等，但是有一些个人习惯的输入仍需要单独配置，这里简单记录一些。</p>

<blockquote>
<p>以下记录都没有说明是哪个文件，因为考虑不同配置可能不一样，所以按照关键词搜索即可。</p>
</blockquote>

<h2 id="toc_0">禁止切换到英文状态</h2>

<p>为了稳定性，英文我还是会使用系统自带的输入法，所以不需要 Rime 的英文输入，将 switch_key 都配置为 noop 即可。</p>

<pre><code class="language-yaml">ascii_composer:
  good_old_caps_lock: false  # true | false
  switch_key:
    Caps_Lock: noop      # commit_code | commit_text | clear
    Shift_L: noop  # commit_code | commit_text | inline_ascii | clear | noop
    Shift_R: noop         # commit_code | commit_text | inline_ascii | clear | noop
    Control_L: noop       # commit_code | commit_text | inline_ascii | clear | noop
    Control_R: noop       # commit_code | commit_text | inline_ascii | clear | noop
</code></pre>

<h2 id="toc_1">使用 Mac 主题</h2>

<p>需要主题存在哦</p>

<pre><code>style:
  # 淺色主题
  color_scheme: mac_light # phub
  # 深色主题
  color_scheme_dark: mac_dark # phub
</code></pre>

<h2 id="toc_2">候选词水平排列</h2>

<p>还是比较习惯原生输入法的样式，因此尽可能保持一致吧。
主题配置下增加 <code>horizontal: true</code>，示例如下：</p>

<pre><code class="language-yaml">  mac_light:
    name: Mac浅色
    horizontal: true
</code></pre>

<h2 id="toc_3">emoji 输入优化</h2>

<p>我习惯输入 <code>hhh</code> 出现 😂 表情，可以在 emoji.txt 文件中增加如下行（可能有的配置已存在哦，这里只是抛砖引玉）</p>

<pre><code>哈哈哈 哈哈哈 😂 😹
哈哈哈哈    哈哈哈哈 🤣
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/rime-custom.html">https://blog.xiaoyuyu.cn/post/rime-custom.html</a>，<a href="https://blog.xiaoyuyu.cn/post/rime-custom.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>OpenWrt 安装 Tailscale</title>
            <link>https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html</guid>
            <description>
                <![CDATA[<blockquote>
<p>安装配置步骤官方<a href="https://openwrt.org/docs/guide-user/services/vpn/tailscale/start" rel="nofollow">wiki</a>介绍的很详细，可以直接参考。另外安装最新版，也可以参考 <a href="https://github.com/adyanth/openwrt-tailscale-enabler" rel="nofollow">openwrt-tailscale-enabler</a> 这个项目</p>

<p>配置方面需要注意两点：</p>

<ul>
<li>需要添加独立的 interface，即tailscale0</li>
<li>需要添加防火墙配置</li>
</ul>

<p>这里记录几个自定义服务端的配置。</p>

<h2 id="toc_0">开机启动</h2>

<p>直接在 <code>/etc/rc.local</code> 添加配置命令，方便快捷。</p>

<pre><code class="language-bash">tailscale up --login-server=http://example.com:8080 --netfilter-mode=off --accept-routes=false --
accept-dns=false --advertise-routes=192.168.100.0/24 --advertise-exit-node=true
</code></pre>

<h2 id="toc_1">出口节点</h2>

<p>上述命令已经使用了出口节点，当在其他客户端激活时，可以让所有流量经过 openwrt 中转。不过使用过程发现无法科学上网，需要勾选一个配置才可以，如下图：
<img src="https://st.xiaoyuyu.cn/blog/img/openwrt-tailscale-01.png" alt="openwrt-tailscale-01.png" /></p>

<h2 id="toc_2">更新版本</h2>

<p>opkg 安装的版本可能不是最新的，可以从<a href="https://pkgs.tailscale.com/stable/#static" rel="nofollow">这里</a>下载最新的版本进行替换, r66s 选择 arm64 即可</p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html">https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html</a>，<a href="https://blog.xiaoyuyu.cn/post/openwrt-tailscale.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>OpenWrt 使用笔记</title>
            <link>https://blog.xiaoyuyu.cn/post/openwrt-notes.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/openwrt-notes.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/openwrt-notes.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">opkg 命令</h2>

<pre><code class="language-bash"># 更新软件包列表
opkg update
# 列出可升级的软件包
opkg list-upgradable
# 升级指定软件
opkg upgrade &lt;pkgs&gt;
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/openwrt-notes.html">https://blog.xiaoyuyu.cn/post/openwrt-notes.html</a>，<a href="https://blog.xiaoyuyu.cn/post/openwrt-notes.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>Headscale 安装和配置</title>
            <link>https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html</guid>
            <description>
                <![CDATA[<blockquote>

<p>本文仅是记录，避免遗忘。</p>

<h2 id="toc_0">容器安装</h2>

<p>工作目录准备</p>

<pre><code>mkdir -p /opt/headscale
mkdir -p /opt/headscale/data
mkdir -p /opt/headscale/config
</code></pre>

<p>cd 到 <code>/opt/headscale/</code>, 新建 <code>docker-compose.yml</code>，内容如下：</p>

<pre><code class="language-yaml">version: '3.7'

services:
  headscale:
    image: headscale/headscale
    container_name: headscale
    volumes:
      - /opt/headscale/config:/etc/headscale
      - /opt/headscale/data:/var/lib/headscale
    ports:
      - 8080:8080
      - &quot;127.0.0.1:9090:9090&quot;
    command: serve
    restart: unless-stopped
</code></pre>

<p>启动服务</p>

<pre><code class="language-bash">cd /opt/headscale/
docker-compose up -d
</code></pre>

<h2 id="toc_1">修改配置</h2>

<p>复制官方示例配置文件(<a href="https://github.com/juanfont/headscale/blob/main/config-example.yaml" rel="nofollow">https://github.com/juanfont/headscale/blob/main/config-example.yaml</a>) 到 <code>/opt/headscale/config/config.yaml</code></p>

<p>修改如下内容:</p>

<pre><code class="language-yaml"># server_url 改为实际访问的域名或ip
server_url: http://example.com:8080

listen_addr: 0.0.0.0:8080

metrics_listen_addr: 0.0.0.0:9090
</code></pre>

<p>重启服务:</p>

<pre><code class="language-bash">docker-compose restart
</code></pre>

<h2 id="toc_2">更新镜像</h2>

<pre><code class="language-bash">docker-compose pull
docker-compose up -d
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html">https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html</a>，<a href="https://blog.xiaoyuyu.cn/post/headscale-installation-and-setup.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>Mac 配置笔记</title>
            <link>https://blog.xiaoyuyu.cn/post/mac-setup-notes.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/mac-setup-notes.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/mac-setup-notes.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">环境配置</h2>

<h3 id="toc_1">Node 安装</h3>

<ul>
<li>版本管理工具 <a href="https://github.com/Schniz/fnm" rel="nofollow">https://github.com/Schniz/fnm</a></li>
<li>docs: <a href="https://github.com/Schniz/fnm/blob/master/docs/commands.md" rel="nofollow">https://github.com/Schniz/fnm/blob/master/docs/commands.md</a></li>
</ul>

<pre><code>curl -fsSL https://fnm.vercel.app/install | bash

# fnm
export PATH=&quot;/Users/yang.zeng/Library/Application Support/fnm:$PATH&quot;
eval &quot;`fnm env`&quot;

# 安装node
fnm list-remote
fnm list 
fnm install

# 切换
fnm use  
</code></pre>

<h2 id="toc_2">终端配置</h2>

<h3 id="toc_3">oh-my-zsh 主题显示执行时间</h3>

<p>进入主题目录<code>~/.oh-my-zsh/themes</code>，以默认主题<code>robbyrussell</code>为例，在<code>robbyrussell.zsh-theme</code> 追加如下内容：</p>

<pre><code class="language-bash">function preexec() {
  timer=${timer:-$SECONDS}
}

function precmd() {
  if [ $timer ]; then
    timer_show=$(($SECONDS - $timer))
    if [[ $timer_show -ge $min_show_time ]]; then
      RPROMPT='%{$fg_bold[red]%}(${timer_show}s)%f%{$fg_bold[white]%}[%*]%f %{$reset_color%}%'
    else
      RPROMPT='%{$fg_bold[white]%}[%*]%f'
    fi
    unset timer
  fi
}

autoload -Uz add-zsh-hook
add-zsh-hook preexec preexec
add-zsh-hook precmd precmd
</code></pre>

<h2 id="toc_4">软件配置</h2>

<h3 id="toc_5">sunshine</h3>

<p>安装</p>

<pre><code>brew tap LizardByte/homebrew

# beta 好用
brew install sunshine-beta
</code></pre>

<blockquote>
<p>需授予终端录屏权限（不是给sunshine授权）,建议用系统自带终端</p>
</blockquote>

<pre><code>To start lizardbyte/homebrew/sunshine now and restart at login:
  brew services start lizardbyte/homebrew/sunshine
Or, if you don't want/need a background service you can just run:
  /usr/local/opt/sunshine/bin/sunshine /Users/funnyang/.config/sunshine/sunshine.conf
</code></pre>

<p>用户名/密码：sunshine/sunshine</p>

<h2 id="toc_6">好用的软件</h2>

<blockquote>
<p>划掉表示没用了</p>
</blockquote>

<ul>
<li><del><a href="https://github.com/nikitabobko/AeroSpace/releases" rel="nofollow">AeroSpace</a> 窗口自动管理工具</del></li>
<li><a href="https://alt-tab-macos.netlify.app/" rel="nofollow">AltTab</a> 增强切换窗口预览</li>
<li>OrbStack 轻量级 docker</li>
<li><a href="https://github.com/DamascenoRafael/reminders-menubar" rel="nofollow">reminders-menubar</a> 菜单栏 Reminders</li>
<li><a href="https://github.com/ssnhd/rime" rel="nofollow">rime 输入法</a></li>
</ul>

<h2 id="toc_7">免费资源</h2>

<ul>
<li><a href="https://pixabay.com/" rel="nofollow">pixabay</a> 免费图片、视频、音频网站</li>
</ul>

<h2 id="toc_8">输入法</h2>

<pre><code># 东方破 一个 rime 输入法管理工具
curl -fsSL https://raw.githubusercontent.com/rime/plum/master/rime-install | bash

# 雾松拼音
https://github.com/iDvel/rime-ice
</code></pre>

<h2 id="toc_9">技巧</h2>

<h3 id="toc_10">电池管理</h3>

<p>使用 <a href="https://github.com/charlie0129/batt" rel="nofollow">batt</a> 限制充电在 80% （仅限于 Apple Silicon），记得关闭系统自带的「优化电池充电」</p>

<pre><code># 安装
brew install batt

# 更新
sudo brew services stop batt
brew upgrade batt
sudo brew services start batt

# 启动
sudo brew services start batt

# 限制冲到 80%
sudo batt limit 80

# 禁用电源（当需要电池掉电时使用）
sudo batt adapter disable
sudo batt adapter enable

# 状态查看
sudo batt status
</code></pre>

<h3 id="toc_11">代理</h3>

<pre><code># ssh 动态代理
ssh -i ~/.ssh/id_rsa -D 9000 {user}@{ip}
</code></pre>

<h3 id="toc_12">简易http服务</h3>

<pre><code>python -m SimpleHTTPServer 8080

python3 -m http.server 8080
</code></pre>

<h3 id="toc_13">导出 Google Authenticator 到 1Password</h3>

<pre><code>https://github.com/scito/extract_otp_secrets
</code></pre>

<h3 id="toc_14">zip 加密压缩</h3>

<pre><code>zip -e xxx.zip file1 file2 file3
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/mac-setup-notes.html">https://blog.xiaoyuyu.cn/post/mac-setup-notes.html</a>，<a href="https://blog.xiaoyuyu.cn/post/mac-setup-notes.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>DuckDB 常用命令</title>
            <link>https://blog.xiaoyuyu.cn/post/duckdb-notes.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/duckdb-notes.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/duckdb-notes.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">基于CSV自动建表</h2>

<pre><code>CREATE TABLE example AS
    SELECT * FROM read_csv('example.csv', ignore_errors=0);
DESCRIBE example;
</code></pre>

<h2 id="toc_1">导出数据</h2>

<pre><code>COPY (select distinct(consumer_id) from read_csv(&quot;~/Downloads/消费者信息.csv&quot;)) TO '~/Downloads/消费者id去重.csv';
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/duckdb-notes.html">https://blog.xiaoyuyu.cn/post/duckdb-notes.html</a>，<a href="https://blog.xiaoyuyu.cn/post/duckdb-notes.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>天猫精灵接入HA</title>
            <link>https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">1. 注册巴法云</h2>

<p><a href="https://cloud.bemfa.com/tcp/index.html" rel="nofollow">https://cloud.bemfa.com/tcp/index.html</a></p>

<h2 id="toc_1">2. 获取私钥</h2>

<p><img src="https://st.xiaoyuyu.cn/blog/img/bemfa.jpg" alt="bemfa.jpg" /></p>

<h2 id="toc_2">3. 手机端绑定巴法云</h2>

<p>进入天猫精灵客户端，直接搜索「巴法云」即可</p>

<h2 id="toc_3">4. 将 Home Assistant 实体同步至巴法云</h2>

<p>下载安装<a href="https://github.com/larry-wong/bemfa" rel="nofollow">bemfa</a>插件到 HA，添加集成
<img src="https://st.xiaoyuyu.cn/blog/img/bemfa-2.jpg" alt="bemfa-2.jpg" />
<img src="https://st.xiaoyuyu.cn/blog/img/bemfa-3.jpg" alt="bemfa-3.jpg" />
<img src="https://st.xiaoyuyu.cn/blog/img/bemfa-4.jpg" alt="bemfa-4.jpg" /></p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html">https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html</a>，<a href="https://blog.xiaoyuyu.cn/post/tmall-genie-integration-with-ha.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>常用配置</title>
            <link>https://blog.xiaoyuyu.cn/post/config-memo.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/config-memo.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/config-memo.html</guid>
            <description>
                <![CDATA[<blockquote>

<h1 id="toc_0">常用配置</h1>

<p>记录一些常用的软件配置</p>

<h2 id="toc_1">pip 加速</h2>

<pre><code class="language-bash">pip config set global.index-url https://mirrors.aliyun.com/pypi/simple/
pip config set install.trusted-host mirrors.aliyun.com
</code></pre>

<h2 id="toc_2">node 版本管理</h2>

<ul>
<li><a href="https://github.com/Schniz/fnm/blob/master/docs/commands.md" rel="nofollow">fnm</a></li>
</ul>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/config-memo.html">https://blog.xiaoyuyu.cn/post/config-memo.html</a>，<a href="https://blog.xiaoyuyu.cn/post/config-memo.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>删除MAC多余的音频设备</title>
            <link>https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">方法一</h2>

<p>打开「音频MIDI设置」删除</p>

<h2 id="toc_1">方法二</h2>

<p>方法一无法删除的，使用命令行</p>

<pre><code>cd /Library/Audio/Plug-Ins/HAL
rm -rf XXX.driver
</code></pre>

<p>重启</p>

<h2 id="toc_2">参考</h2>

<ul>
<li><a href="https://zhuanlan.zhihu.com/p/488179063?utm_id=0" rel="nofollow">删除MAC多余音频设备</a></li>
</ul>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html">https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html</a>，<a href="https://blog.xiaoyuyu.cn/post/romove-mac-audio-device.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>小雅资源站搭建</title>
            <link>https://blog.xiaoyuyu.cn/post/xiaoya.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/xiaoya.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/xiaoya.html</guid>
            <description>
                <![CDATA[<blockquote>

<h1 id="toc_0">小雅资源搭建</h1>

<h2 id="toc_1">准备</h2>

<p>官方教程：<a href="https://xiaoyaliu.notion.site/xiaoya-docker-69404af849504fa5bcf9f2dd5ecaa75f" rel="nofollow">https://xiaoyaliu.notion.site/xiaoya-docker-69404af849504fa5bcf9f2dd5ecaa75f</a></p>

<p>先准备：</p>

<table>
<thead>
<tr>
<th></th>
<th>对应文件</th>
<th>获取方式</th>
</tr>
</thead>

<tbody>
<tr>
<td>token</td>
<td>/etc/xiaoya/mytoken.txt</td>
<td><a href="https://aliyuntoken.vercel.app/" rel="nofollow">https://aliyuntoken.vercel.app/</a></td>
</tr>

<tr>
<td>open token</td>
<td>etc/xiaoya/myopentoken.txt</td>
<td><a href="https://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html" rel="nofollow">https://alist.nn.ci/zh/guide/drivers/aliyundrive_open.html</a></td>
</tr>

<tr>
<td>转存目录的folder id</td>
<td>/etc/xiaoya/temp_transfer_folder_id.txt</td>
<td>先转存这个 <a href="https://www.aliyundrive.com/s/rP9gP3h9asE" rel="nofollow">https://www.aliyundrive.com/s/rP9gP3h9asE</a>  到自己网盘（选择资源盘），然后浏览器打开转存后的目录，浏览器的url  <a href="https://www.aliyundrive.com/drive/file/resource/640xxxxxxxxxxxxxxxxxxxca8a" rel="nofollow">https://www.aliyundrive.com/drive/file/resource/640xxxxxxxxxxxxxxxxxxxca8a</a> 最后一串就是，记得这个目录不要删，里面的内容可以定期删除</td>
</tr>
</tbody>
</table>

<blockquote>
<p>docker 中是 /data</p>
</blockquote>

<h2 id="toc_2">安装</h2>

<p>设备：小猫盘黑群晖</p>

<p>先创建一个目录挂载配置文件<code>/volume1/config/xiaoya</code>，然后需要先执行</p>

<pre><code class="language-bash">sudo docker run -d \
   --name xiaoya \
   -e TZ=Asia/Shanghai \
   -v /volume1/config/xiaoya:/data \
   --network=host \
   --restart=always \
   xiaoyaliu/alist:hostmode
</code></pre>

<p>删除容器，再执行如下</p>

<pre><code class="language-bash">sudo docker run -d \
   --name xiaoya \
   -p 5678:80 \
   -e TZ=Asia/Shanghai \
   -v /volume1/config/xiaoya:/data \
   --restart=always \
   xiaoyaliu/alist:latest
</code></pre>

<p>设置定时更新</p>

<pre><code>0 6 * * * docker restart xiaoya
</code></pre>

<p>自动清理就不搞了</p>

<h2 id="toc_3">备忘</h2>

<p><strong>webdav 账号密码</strong></p>

<p>用户: guest</p>

<p>密码: guest_Api789</p>

<p>路径：/dav</p>

<p>端口：5678</p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/xiaoya.html">https://blog.xiaoyuyu.cn/post/xiaoya.html</a>，<a href="https://blog.xiaoyuyu.cn/post/xiaoya.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>oh-my-zsh 主题显示命令执行时间和当前时间</title>
            <link>https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html</guid>
            <description>
                <![CDATA[<blockquote>
<p>参考《<a href="https://blog.csdn.net/weixin_41100576/article/details/106334391" rel="nofollow">oh-my-zsh主题添加命令显示执行时间和当前时间</a> 》进行了修改，兼容 vscode 终端</p>

<p>以 robbyrussell 为例</p>

<pre><code>cd .oh-my-zsh/themes
vim robbyrussell.zsh-theme
</code></pre>

<p>添加如下内容：</p>

<pre><code class="language-shell">function preexec() {
  timer=${timer:-$SECONDS}
}

function precmd() {
  if [ $timer ]; then
    timer_show=$(($SECONDS - $timer))
    if [[ $timer_show -ge $min_show_time ]]; then
      RPROMPT='%{$fg_bold[red]%}(${timer_show}s)%f%{$fg_bold[white]%}[%*]%f'
    else
      RPROMPT='%{$fg_bold[white]%}[%*]%f'
    fi
    unset timer
  fi
}

autoload -Uz add-zsh-hook
add-zsh-hook preexec preexec
add-zsh-hook precmd precmd
</code></pre>

<p>效果：
<img src="https://st.xiaoyuyu.cn/blog/img/oh-my-zsh.jpg" alt="效果图" /></p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html">https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html</a>，<a href="https://blog.xiaoyuyu.cn/post/oh-my-zsh-display-time.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>nps 安装</title>
            <link>https://blog.xiaoyuyu.cn/post/nps-install.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/nps-install.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/nps-install.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">服务端部署</h2>

<pre><code>wget https://github.com/ehang-io/nps/releases/download/v0.26.10/linux_amd64_server.tar.gz
tar zxf linux_amd64_server.tar.gz
./nps install

The new configuration file is located in /etc/nps you can edit them
2022/07/22 20:27:23 You can start with:
nps start|stop|restart|uninstall|update or nps-update update
</code></pre>

<p>修改配置 <code>/etc/nps/conf/nps.conf</code></p>

<pre><code>#p2p
p2p_ip=服务端ip
p2p_port=6000
</code></pre>

<h2 id="toc_1">客户端设置</h2>

<p>mac 开机自启
创建文件 <code>/Library/LaunchDaemons/com.funnyang.nps.plist</code></p>

<pre><code>&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;
&lt;!DOCTYPE plist PUBLIC &quot;-//Apple//DTD PLIST 1.0//EN&quot; &quot;http://www.apple.com/DTDs/PropertyList-1.0.dtd&quot;&gt;
&lt;plist version=&quot;1.0&quot;&gt;
  &lt;dict&gt;
    &lt;key&gt;Label&lt;/key&gt;
    &lt;string&gt;com.funnyang.nps&lt;/string&gt;
    &lt;key&gt;ProgramArguments&lt;/key&gt;
    &lt;array&gt;
      &lt;string&gt;/Users/funnyang/bin/nps/npc&lt;/string&gt;
      &lt;string&gt;-server=ip:port&lt;/string&gt;
      &lt;string&gt;-vkey=xxxx&lt;/string&gt;
      &lt;string&gt;-type=tcp&lt;/string&gt;
    &lt;/array&gt;
    &lt;key&gt;RunAtLoad&lt;/key&gt;
    &lt;true/&gt;
    &lt;key&gt;KeepAlive&lt;/key&gt;
    &lt;true/&gt;
  &lt;/dict&gt;
&lt;/plist&gt;
</code></pre>

<p>修改权限，启动</p>

<pre><code>sudo chown root:wheel /Library/LaunchDaemons/com.funnyang.nps.plist
sudo launchctl load /Library/LaunchDaemons/com.funnyang.nps.plist
</code></pre>

<h2 id="toc_2">访问端设置</h2>

<p>p2p 连接</p>

<pre><code>访问端：
npc -server=ip:port -vkey=xxx -type=tcp -password=xxx -target=127.0.0.1:5900 --local_port=5900
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/nps-install.html">https://blog.xiaoyuyu.cn/post/nps-install.html</a>，<a href="https://blog.xiaoyuyu.cn/post/nps-install.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>CentOS 7 安装最新 HAProxy</title>
            <link>https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">一、安装 ius</h2>

<p>ius 是一个提供为 RHEL 和 CentOS 提供更新软件版本的仓库</p>

<pre><code>yum install \
https://repo.ius.io/ius-release-el7.rpm \
https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm

yum makecache fast
</code></pre>

<h2 id="toc_1">二、安装 haproxy</h2>

<pre><code>[root@server001 ~]# yum list | grep haproxy
haproxy.x86_64                           1.5.18-9.el7_9.1              updates
haproxy18.x86_64                         1.8.27-2.el7                  epel
haproxy18u.x86_64                        1.8.30-1.el7.ius              ius
haproxy20.x86_64                         2.0.27-1.el7.ius              ius
haproxy22.x86_64                         2.2.22-1.el7.ius              ius
pcp-pmda-haproxy.x86_64                  4.3.2-13.el7_9                updates

[root@server001 ~]# yum install -y haproxy22
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html">https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html</a>，<a href="https://blog.xiaoyuyu.cn/post/centos7-install-haproxy.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>Homebrew 配置</title>
            <link>https://blog.xiaoyuyu.cn/post/homebrew-setup.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/homebrew-setup.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/homebrew-setup.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">安装</h2>

<p><strong>说明：</strong>修改源，步骤相同</p>

<p>1) <code>~/.zshrc</code> 中添加以下内容</p>

<pre><code>export HOMEBREW_BREW_GIT_REMOTE=https://mirrors.aliyun.com/homebrew/brew.git
export HOMEBREW_CORE_GIT_REMOTE=https://mirrors.aliyun.com/homebrew/homebrew-core.git
# 阿里云常年不更新，故设置为中科院的源
export HOMEBREW_BOTTLE_DOMAIN=https://mirrors.ustc.edu.cn/homebrew-bottles
export PATH=&quot;/usr/local/sbin:$PATH&quot;
</code></pre>

<p>2) 执行安装命令</p>

<pre><code>source ~/.zshrc
/bin/bash -c &quot;$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)&quot;
</code></pre>

<p>3) 设置 cask 镜像地址</p>

<pre><code>brew tap --custom-remote --force-auto-update homebrew/cask https://mirrors.aliyun.com/homebrew/homebrew-cask.git
</code></pre>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/homebrew-setup.html">https://blog.xiaoyuyu.cn/post/homebrew-setup.html</a>，<a href="https://blog.xiaoyuyu.cn/post/homebrew-setup.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>Zookeeper 学习笔记</title>
            <link>https://blog.xiaoyuyu.cn/post/zookeeper-notes.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/zookeeper-notes.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/zookeeper-notes.html</guid>
            <description>
                <![CDATA[<blockquote>

<h2 id="toc_0">基本介绍</h2>

<h3 id="toc_1">名字来源</h3>

<p>《<a href="https://book.douban.com/subject/26292004/" rel="nofollow">Paxos到Zookeeper：分布式一致性原理与实践</a>》中有写道：</p>

<blockquote>
<p>Zookeeper 最早起源于雅虎的研究院的一个研究小组。在立项初期，考虑到很多项目都是用动物的名字来命名的 (例如著名的 Pig 项目)，雅虎的工程师希望给这个项目也取一个动物的名字。时任研究院的首席科学家 Raghu Ramakrishnan 开玩笑说：再这样下去，我们这儿就变成动物园了。此话一出，大家纷纷表示就叫动物园管理员吧——因为各个以动物命名的分布式组件放在一起，雅虎的整个分布式系统看上去就像一个大型的动物园了，而 Zookeeper 正好用来进行分布式环境的协调——于是，Zookeeper 的名字由此诞生了。</p>
</blockquote>

<h3 id="toc_2">数据存储</h3>

<p>zk 的数据以类似目录树的形式组织，但是没有相对路径的概念，必须以“/”为根。每个“目录”称为 Znode，每个 Znode 都可以包含数据和子节点。</p>

<p>Znode 有四种类型：持久，顺序，临时，临时顺序。<strong>临时节点在会话失效后，就会被删除</strong>。</p>

<h2 id="toc_3">术语</h2>

<h3 id="toc_4">tick</h3>

<p>zk 中有一个 tick 的概念，也可以称为“滴答”，表示一次心跳的间隔。zk 中许多时间的设置都是以 tick 为基本单位进行配置，比如以 2 个 tick 作为会话超时时间。</p>

<h3 id="toc_5">Zxid</h3>

<p>Zxid（ZooKeeper transaction id）就是事务id，zk 中每次写请求都对应一个唯一的事务id，它是全局且有序的，如果 Zxid1 小于 Zxid2，那 Zxid1 就一定是发生在 Zxid2 前。</p>

<h2 id="toc_6">常用操作</h2>

<h3 id="toc_7">创建节点</h3>

<pre><code># 持久节点
create path value

# 临时节点
create -e path value

# 顺序节点
create -s path value
</code></pre>

<h3 id="toc_8">查看节点状态</h3>

<ul>
<li>使用 <code>stat</code> 命令查看</li>
<li>网上的教程说可以使用 <code>ls2</code>，这个命令在新版中已经没有了，可以使用 <code>ls -s</code> 代替。</li>
</ul>

<pre><code>[zk: localhost:2181(CONNECTED) 9] stat /clickhouse/task_queue/ddl/query-0000014523/finished
cZxid = 0x14cc043d76f0                   # 创建该节点的 zxid
ctime = Thu Jan 20 11:09:11 CST 2022     # 创建时间
mZxid = 0x14cc043d76f0                   # 修改改节点的 zxid
mtime = Thu Jan 20 11:09:11 CST 2022     # 修改时间
pZxid = 0x14cc043d7709                   # 该节点的子节点最后修改的 zxid
cversion = 3                             # 子节点变更次数
dataVersion = 0                          # 该节点数据变更次数
aclVersion = 0                           # 该节点 acl 变更次数
ephemeralOwner = 0x0                     # 临时节点所有者会话id，非临时的为0
dataLength = 0                           # 该节点数据长度
numChildren = 3                          # 子节点数（不包括孙子节点）
</code></pre>

<h2 id="toc_9">参考资料</h2>

<ul>
<li><a href="https://blog.51cto.com/u_14153136/3055655" rel="nofollow">不懂 Zookeeper？没关系，看这篇就够了</a></li>
</ul>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/zookeeper-notes.html">https://blog.xiaoyuyu.cn/post/zookeeper-notes.html</a>，<a href="https://blog.xiaoyuyu.cn/post/zookeeper-notes.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>一言</title>
            <link>https://blog.xiaoyuyu.cn/post/hitokoto.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/hitokoto.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/hitokoto.html</guid>
            <description>
                <![CDATA[<blockquote>
<div class="card">
运气并非成就，命运之手把我托举到不配有的高度，让人飘然，让人晕眩，最终让人诚惶诚恐。<!--more--> 一直以来，我都觉得自己不过像一颗渺小的尘埃，风把我带向我从未向往的高处，相信有一天，它会把我轻放在神秘莫测的他处，我们不过在借来的时间中生活，你所暂时保管的精彩也并不属于你。 —— 罗翔
</div>

<div class="card">
我活在世上，无非想要明白些道理，遇见些有趣的人，做一些有趣的事。倘能如我所愿，我的一生就算成功。 —— 王小波
</div>

<div class="card">
我只不过是别人慢几年。 —— 室友
</div>

<div class="card">
以严于律己之心，宽容对待他人。
</div>

<div class="card">
最朴素的生活，最遥远的梦想。 —— 海子
</div>

<div class="card">
每一个人的成长都深深扎根于社会的土壤。 —— 大学老师
</div>

<div class="card">
你是否敢在形形色色的人群中 <br>
自行其是 <br>
成为一个绝缘物？ <br>
眼看着别人在身边忙忙碌碌 <br>
不管不顾 <br>
守着你宁静的幽居 <br>
像荒凉沙漠里的一朵花 <br>
不屑于 <br>
向那过路的风 <br>
吐露气息 <br> <br>

   —— 雪莱《孤独者》
</div>

<p><br><br></p>

<style>
.card {
  border-radius: 10px;
  background-color: #f8f8f8;
  padding: 20px 20px 15px 20px;
  margin-top: 40px;
}
</style>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/hitokoto.html">https://blog.xiaoyuyu.cn/post/hitokoto.html</a>，<a href="https://blog.xiaoyuyu.cn/post/hitokoto.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>ClickHouse 的数据副本机制</title>
            <link>https://blog.xiaoyuyu.cn/post/clickhouse-replica.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/clickhouse-replica.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/clickhouse-replica.html</guid>
            <description>
                <![CDATA[<blockquote>

<p>将数据副本单独拿出来讲，是因为 ClickHouse 的副本机制灵活多变。在 ClickHouse 中要将数据写入副本，有 3 种方式：</p>

<ul>
<li>写分布式表 + internal_replication=false</li>
<li>写分布式表 + zk + Replicated*MergeTree 引擎 + internal_replication=true​</li>
<li>写本地表 + zk + Replicated*MergeTree 引擎 + internal_replication=true</li>
</ul>

<p>在理解这 3 个方案前，我们需要先理解一些前置知识。</p>

<h2 id="toc_0">分片副本 vs 表副本</h2>

<p>分片副本是集群里的概念，当配置一个分片时，可以配置每个分片有几个副本。</p>

<pre><code class="language-sql">&lt;remote_servers&gt;
    &lt;my-cluster-001&gt;
        &lt;shard&gt;
            &lt;!-- Optional. Shard weight when writing data. Default: 1. --&gt;
            &lt;weight&gt;1&lt;/weight&gt;
            &lt;!-- Optional. Whether to write data to just one of the replicas. Default: false (write data to all replicas). --&gt;
            &lt;internal_replication&gt;false&lt;/internal_replication&gt;
            &lt;replica&gt;
                &lt;host&gt;example01-01-1&lt;/host&gt;
                &lt;port&gt;9000&lt;/port&gt;
            &lt;/replica&gt;
            &lt;replica&gt;
                &lt;host&gt;example01-01-2&lt;/host&gt;
                &lt;port&gt;9000&lt;/port&gt;
            &lt;/replica&gt;
        &lt;/shard&gt;
        &lt;shard&gt;
            &lt;weight&gt;2&lt;/weight&gt;
            &lt;internal_replication&gt;false&lt;/internal_replication&gt;
            &lt;replica&gt;
                &lt;host&gt;example01-02-1&lt;/host&gt;
                &lt;port&gt;9000&lt;/port&gt;
            &lt;/replica&gt;
            &lt;replica&gt;
                &lt;host&gt;example01-02-2&lt;/host&gt;
                &lt;port&gt;9440&lt;/port&gt;
            &lt;/replica&gt;
        &lt;/shard&gt;
    &lt;/my-cluster-001&gt;
&lt;/remote_servers&gt;
</code></pre>

<p>如上配置中，每个分片设置了两个副本，从配置的结构也可以看出，每个分片的副本数是很灵活的，比如可以配置为分片1有两个副本，分片2有三个副本。</p>

<p>第一次接触 ClickHouse 的人，可能会认为这样配置以后，数据就会自动在分片的副本之间同步。但是事实并非这样。</p>

<p>表副本是表的副本，这个概念来自 Replicated*MergeTree 系列表引擎。创建一个复制表的示例如下：</p>

<pre><code class="language-sql">CREATE TABLE table_name
(
    EventDate DateTime,
    CounterID UInt32,
    UserID UInt32,
    ver UInt16
) ENGINE = ReplicatedMergeTree('/clickhouse/tables/{layer}-{shard}/table_name', '{replica}')
PARTITION BY toYYYYMM(EventDate)
ORDER BY (CounterID, EventDate, intHash32(UserID))
SAMPLE BY intHash32(UserID);
</code></pre>

<blockquote>
<p>示例中 { } 括起来的部分是宏变量，可以在配置文件中修改，这里不展开。</p>
</blockquote>

<p>Replicated*MergeTree 表引擎有两个关键参数：</p>

<ul>
<li>path - zk 中的路径，所有表副本的必须一致</li>
<li>replica_name - 副本名称，每个表副本有各自的名称</li>
</ul>

<p>如上，具有相同 path 的表会自动同步数据</p>

<p><strong>总结：</strong></p>

<ol>
<li><strong>分片副本之间不会自动同步数据</strong></li>
<li><strong>表副本之间会自动同步数据</strong></li>
</ol>

<h2 id="toc_1">副本与分布式表的关系</h2>

<p>了解副本的概念后，我们再来讨论这与分布式表有什么关系？</p>

<p>这一切都来源于一个配置 internal_replication，这个配置可以在 config.xml 中修改。他的作用是控制写分布式表的行为：</p>

<ul>
<li>internal_replication = false 写分布式表时，写入分片的所有副本</li>
<li>internal_replication = true 写分布式表时，只写入分片的一个副本</li>
</ul>

<p>当本地表为非复制表时，写入所有副本，无可厚非；当本地表为复制表时，写入所有副本似乎就有点多余了，因此在这种情况，我们可以借助复制表本身的能力来写入副本。</p>

<h2 id="toc_2">总结</h2>

<p>综上所述，我们就有 3 种方案来管理数据副本：</p>

<ul>
<li>写分布式表 + internal_replication=false</li>
<li>写分布式表 + zk + Replicated*MergeTree 引擎 + internal_replication=true​</li>
<li>写本地表 + zk + Replicated*MergeTree 引擎 + internal_replication=true</li>
</ul>

<p>方案一最简单，但是官方不推荐，因为在实现上没有保证数据一致性。</p>

<p>方法二利用分布表的写入能力 + 复制表的同步能力，这种方案在某种程度上也是可行的，不过业内很多都不推荐写分布式表，因为写分布式表，数据需要分发，会产生很多临时数据，会产生写放大，对 CPU 和内存造成额外消耗。</p>

<p>方案三由业务控制数据分片，直接写入本地表，该方案最灵活，性能也最好。主流的做法是在写入前端加一个 LB，由 LB 分发数据写入。</p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/clickhouse-replica.html">https://blog.xiaoyuyu.cn/post/clickhouse-replica.html</a>，<a href="https://blog.xiaoyuyu.cn/post/clickhouse-replica.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>使用 IDA Pro 动态调试安卓 so 文件</title>
            <link>https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html</guid>
            <description>
                <![CDATA[<blockquote>

<p>因为需要破解一个 App 的签名算法，开始学习逆向。本人没有逆向经验，也没有安卓开发经验，甚至没有 <code>Java</code> 开发经验。因此花了不少时间才完成任务，故整理了此文章，作为一个备忘。</p>

<p>由于是第一次实战，因此参考了很多文章，但是每篇文章都有一些没有提到的点（对新手而言），因此个人结合自己的情况，重新整理了一下遗漏的部分。</p>

<p>先上参考资料，建议每篇精读，你遇到的大部分问题可能文章已经提及。
1. 看雪论坛的《<a href="https://bbs.pediy.com/thread-259633.htm" rel="nofollow">[原创]新手关于ida动态调试so的一些坑总结</a>》
在学习的过程，主要步骤都是参考该文章，因此本文也以该文章为骨架。</p>

<ol>
<li><p><a href="https://www.cnblogs.com/bmjoker/p/11891123.html" rel="nofollow">使用IDA进行动态调试与过反调试</a>
IDA Pro 动态调试操作部分参考了该文章，写的很详细，适合刚入门的朋友。</p></li>

<li><p><a href="https://xz.aliyun.com/t/4205#toc-1" rel="nofollow">IDA Pro7.0使用技巧总结</a>
建议先读，可以对 IDA Pro 有一个相对系统的了解。</p></li>
</ol>

<blockquote>
<p>本文中的截图大部分来自其他文章，故可能内容对不上，但是流程都是完整的。
作为练手，可以尝试复现《<a href="https://www.52pojie.cn/thread-972422-1-1.html" rel="nofollow">逆向练手——说一说捅MXX的过程</a>》，按图索骥，抽丝剥茧。</p>
</blockquote>

<h2 id="toc_0">系统环境</h2>

<p>macOS Big Sur 11.0.1</p>

<h2 id="toc_1">工具准备</h2>

<ul>
<li>IDA Pro</li>
<li>am、pm（安卓自带）</li>
<li>adb</li>
<li>jadx</li>
<li>一台 root 的手机（模拟器可能也可以？）</li>
<li>Android Studio（主要需要用到一个叫 ddms(monitor) 的工具）</li>
<li>jdb</li>
</ul>

<h2 id="toc_2">工具安装</h2>

<p>这部分只是记录我安装过程遇到的问题，大家可以根据自己的情况安装。</p>

<p>1.安装 <code>adb</code></p>

<pre><code class="language-shell">brew cask install android-platform-tools
</code></pre>

<p>2.安装 <code>jadx</code>
Github 下载：<a href="https://github.com/skylot/jadx/releases" rel="nofollow">https://github.com/skylot/jadx/releases</a></p>

<p>3.安装 Android Studio
在官网下载，直接安装即可。不过在使用 <code>ddms(monitor)</code> 时，遇到了一个报错：</p>

<pre><code>JavaVM FATAL: Failed to load the jvm library
</code></pre>

<p>Google 搜索到 StackOverflow 上的问题：<a href="https://stackoverflow.com/questions/59415053/javavm-fatal-failed-to-load-the-jvm-library" rel="nofollow">JavaVM FATAL: Failed to load the jvm library</a></p>

<p>因为对 <code>Java</code> 不熟悉，就直接按照其中一个回答的操作解决了，具体原理未深入研究。修复方法如下（注意替换 jdk 版本）：</p>

<pre><code>cd /Library/Java/JavaVirtualMachines/jdk1.8.0_271.jdk/Contents/Home
cd lib
sudo ln -s ../jre/lib/server/libjvm.dylib libserver.dylib
</code></pre>

<h2 id="toc_3">动态调试步骤</h2>

<h3 id="toc_4">手机端准备</h3>

<h4 id="toc_5">1. 使用 <code>pm</code> 确定要调试的 apk 的包名</h4>

<p>列出所有的包信息：<code>pm list packages [filter]</code>
<code>pm</code> 过滤器选项：
* <code>-d</code> 只显示禁用的应用的包名
* <code>-e</code> 只显示可用的应用的包名
* <code>-s</code> 只显示系统应用的包名
* <code>-3</code> 只显示第三方应用的包名</p>

<p><img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-001.jpg" alt="" /></p>

<h4 id="toc_6">2. 确定启动入口 Lunch Activity</h4>

<p>使用 <code>jadx</code> 反编译 apk，找到 <code>AndroidManifest.xml</code> 查看程序入口。
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-002.png" alt="" />
&gt; 图片来源：<a href="https://www.cnblogs.com/bmjoker/p/11891123.html" rel="nofollow">https://www.cnblogs.com/bmjoker/p/11891123.html</a>
其中包名为：<code>demo2.jni.com.myapplication</code>
入口为：<code>.MainActivity</code></p>

<h4 id="toc_7">3. 复制 <code>android_server</code> 到设备中，并执行</h4>

<p><code>android_server</code> 存储在：<code>IDA安装目录/dbgsrv/android_server</code>
&gt; android_server分版本的，使用对应的版本。</p>

<pre><code># 复制到设备上
&gt; adb push android_server /data/local/tmp

# 修改权限，并执行（使用 root 权限）
❯ adb shell
shell@R9sPlus:/ $ cd /data/local/tmp
shell@R9sPlus:/data/local/tmp $ chmod 777 android_server
shell@R9sPlus:/data/local/tmp $ su
root@R9sPlus:/data/local/tmp # ./android_server
IDA Android 32-bit remote debug server(ST) v1.22. Hex-Rays (c) 2004-2017
Listening on 0.0.0.0:23946...

# 启用端口转发，后面会有用到
&gt; adb forward tcp:23946 tcp:23946
</code></pre>

<h4 id="toc_8">4. 启用端口转发</h4>

<p>IDA Pro 动态调试时，需要连接 debug 程序，所以需要在电脑与手机之间进行数据转发。</p>

<pre><code>&gt; adb forward tcp:23946 tcp:23946
</code></pre>

<h4 id="toc_9">5. 用 am 启动被调试应用</h4>

<pre><code>am start -D -n demo2.jni.com.myapplication/.MainActivity
</code></pre>

<p>启动后设备会出现类似如下界面，等待调试器的连接。
<img width="360" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-003.jpg" alt="" /></p>

<h3 id="toc_10">电脑端</h3>

<h4 id="toc_11">1. IDA Pro 配置</h4>

<p>1.用 IDA 打开想要调试的 <code>so</code> 库
2.设置调试器
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-004.png" alt="" /></p>

<p>3.设置调试选项
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-005.png" alt="" />
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-006.jpg" alt="" /></p>

<p>4.开始 attach 进程</p>

<p>设置主机和端口：
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-007.png" alt="" />
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-008.png" alt="" /></p>

<p>选择要调试的程序：
<img width="315" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-009.jpg" alt="-w315" />
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-010.png" alt="" /></p>

<h4 id="toc_12">2.使用 jdb 连接</h4>

<p>1.确定 port
打开 <code>ddms</code> 查看</p>

<pre><code>cd /Users/funnyang/Library/Android/sdk/tools/
./monitor
</code></pre>

<p><img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-011.jpg" alt="" /></p>

<blockquote>
<p>这就为什么要下 Android Studio 😂</p>
</blockquote>

<p>使用真机调试时，可能看不到进程，需要开启真机调试。参考《<a href="https://www.jianshu.com/p/90c861ab7e6d" rel="nofollow">真机打开调试功能</a>
》，下载 <a href="https://github.com/wpvsyou/mprop" rel="nofollow"><code>mprop</code></a> 工具，并开启真机调试，具体操作如下：</p>

<pre><code># 将下载的mprop 放入 /data/local/tmp 当中
&gt; adb push mprop /data/local/tmp
&gt; adb shell
root@R9sPlus:/data/local/tmp $ su
root@R9sPlus:/data/local/tmp # cd /data/local/tmp
root@R9sPlus:/data/local/tmp # chmod 755 mprop
root@R9sPlus:/data/local/tmp # ./mprop ro.debuggable 1

# 获取ro.debuggable 此处应该是 1
root@R9sPlus:/data/local/tmp # getprop ro.debuggable

# 重启adbd进程
root@R9sPlus:/data/local/tmp # stop;start
</code></pre>

<p>2.开始连接</p>

<pre><code>jdb -connect com.sun.jdi.SocketAttach:hostname=localhost,port=8700
</code></pre>

<blockquote>
<p><code>8700</code> 为第一步找到的端口, 根据实际情况更改.</p>
</blockquote>

<p>执行上述命令之后，切换到 IDA Pro，按 <code>F9</code> 就可以进行调试了。
&gt; 按 <code>F9</code> 之后，会发现手机端的 Wait For Debugger 界面消失</p>

<h3 id="toc_13">开始调试</h3>

<p>不断按 F9，就会触发到断点，直到出现类似如下界面，说明进入了要调试的 so。
<img width="600" height="" data-src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-012.png" alt="" /></p>

<p><strong>调试快捷键：</strong></p>

<ul>
<li><code>F2</code> 下断点</li>
<li><code>F7</code> 单步步入</li>
<li><code>F8</code> 单步步过</li>
<li><code>F9</code> 执行到下个断点</li>
<li><code>F4</code> 执行到光标</li>
</ul>

<p><strong>小 Tips：</strong>
初期尝试时，可以一步一步按 <code>F9</code>，来熟悉操作。熟悉了之后，可以直接在 <code>so</code> 的代码中打断点，然后按 <code>F4</code>，更快地到达要调试的函数。</p>

<p>在调试的过程中，可能需要查看寄存器的值，对于新手来说还是挺懵逼的。我也是在阅读《<a href="https://www.cnblogs.com/bmjoker/p/11891123.html" rel="nofollow">使用IDA进行动态调试与过反调试</a>》之后才学会如何操作的。原来需要先点击 <code>Hex View</code>，再点跳转。同样地，如果要定位 <code>View-PC</code> 的位置，也要先点击 <code>IDA View-PC</code> 窗口。其他窗口跳转方法同理。
<img src="https://st.xiaoyuyu.cn/blog/img/ida-pro-so-013.png" alt="" /></p>

<blockquote>
<p>图片来源：<a href="https://www.cnblogs.com/bmjoker/p/11891123.html" rel="nofollow">使用IDA进行动态调试与过反调试</a></p>
</blockquote>

<p>其他涉及 IDA Pro 的知识，建议阅读推荐文章。</p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html">https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html</a>，<a href="https://blog.xiaoyuyu.cn/post/使用IDA Pro动态调试安卓so文件.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
        <item>
            <title>nohup 和 & 的作用详解</title>
            <link>https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html</link>
            <comments>https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html#comments</comments>
            <guid>https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html</guid>
            <description>
                <![CDATA[<blockquote>

<p>通常，我们在终端使用命令时，若在其后添加 <code>&amp;</code> 会使程序在后台运行。那么这个所谓的在后台运行是什么意思呢？</p>

<p>这里其实要提到 Linux jobs 的概念，在 shell 中执行的每条命令都可以认为都是 job，这些 job 基本都是<strong>前台 job</strong>，若有的命令执行时间比较长，那么我们可以把它放到后台，使之成为<strong>后台 job</strong>，这样我们可以继续在终端中进行工作。<code>&amp;</code> 的作用就是使进程成为<strong>后台 job</strong>。</p>

<p>除了 <code>&amp;</code>，对于已经在运行的前台 job，还可使用 Ctrl + Z 使之成为后台 job。</p>

<p>这也是为什么要使用 <code>jobs -l</code> ，查看后台 job。</p>

<p>如果从信号的角度理解，我们也可以认为 <code>&amp;</code> 使程序免疫了 <code>SIGINT</code> 信号（Ctrl + C)。逃得了 <code>SIGINT</code> 不一定逃得了 <code>SIGHUP</code>，终端会话关闭时，会发送 <code>SIGHUP</code> 信号来结束进程。因此经常会看到 <code>&amp;</code> 和 <code>nohup</code> 连用，使进程同时免疫 <code>SIGINT</code>和<code>SIGHUP</code> 信号，这样当终端退出后，该进程会交给1号进程接管，实现真正的后台运行</p>

<h3 id="toc_0">一个 jobs 的小例子</h3>

<pre><code class="language-shell">[test@test-server ~]$ watch -n 1 echo &quot;hello world&quot; &amp;
[1] 13298
[test@test-server ~]$ jobs -l
[1]+ 13298 Stopped (tty output)    watch -n 1 echo &quot;hello world&quot;
[test@test-server ~]$ bg %1
[1]+ watch -n 1 echo &quot;hello world&quot; &amp;
[test@test-server ~]$ jobs -l
[1]+ 13298 Stopped (tty output)    watch -n 1 echo &quot;hello world&quot;
[test@test-server ~]$ fg %1
</code></pre>

<p>没有太多技术含量，就是命令老忘，所以简单写了写。</p>
<p>本文链接：<a href="https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html">https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html</a>，<a href="https://blog.xiaoyuyu.cn/post/nohup和&的作用详解.html#comments">参与评论 »</a></p>]]>
            </description>
        </item>
        
    </channel>
</rss>
