<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://blog-rs44.onrender.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://blog-rs44.onrender.com/" rel="alternate" type="text/html" /><updated>2026-06-20T20:06:48+08:00</updated><id>https://blog-rs44.onrender.com/feed.xml</id><title type="html">Nine</title><author><name>Nine</name></author><entry><title type="html">GitHub Connection refused</title><link href="https://blog-rs44.onrender.com/2025/10/09/git-connection-refused/" rel="alternate" type="text/html" title="GitHub Connection refused" /><published>2025-10-09T00:00:00+08:00</published><updated>2025-10-09T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2025/10/09/git-connection-refused</id><content type="html" xml:base="https://blog-rs44.onrender.com/2025/10/09/git-connection-refused/"><![CDATA[<p>解决 GitHub 拉取代码报错：<code class="language-plaintext highlighter-rouge">Could not read from remote repository</code>问题。</p>

<h4 id="1问题">1、问题</h4>

<p>前几日使用 PyCharm 拉取代码时突然报错：</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>ssh: connect to host github.com port 22: Connection refused
Could not <span class="nb">read </span>from remote repository.

Please make sure you have the correct access rights
and the repository exists.
</code></pre></div></div>

<p>简单记录下排查过程</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 检查 SSH 是否能够连接成功</span>
ssh <span class="nt">-T</span> git@github.com <span class="nt">-v</span>
</code></pre></div></div>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/git/image-20251009104448436.png" alt="image-20251009104448436" /></p>

<p>可以看到请求到了本地端口，这时想起来本机用了<code class="language-plaintext highlighter-rouge">Steamcommunity 302</code>对<code class="language-plaintext highlighter-rouge">github.com</code>进行了代理，是对应的 host 失效了，使用 443 端口测试是正常的。</p>

<h4 id="2解决">2、解决</h4>

<h5 id="方案一">方案一</h5>

<p>使用 https 的 443 端口代替，本地增加 ssh 配置：<code class="language-plaintext highlighter-rouge">vim ~/.ssh/config</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Host github.com
    HostName ssh.github.com
	Port 443
    User git
    IdentityFile ~/.ssh/id_ed25519
</code></pre></div></div>

<p>官方说明：<a href="https://docs.github.com/zh/authentication/troubleshooting-ssh/using-ssh-over-the-https-port">https://docs.github.com/zh/authentication/troubleshooting-ssh/using-ssh-over-the-https-port</a></p>

<h5 id="方案二">方案二</h5>

<p>配合使用<a href="https://www.dogfight360.com/blog/18627/">UsbEAm Hosts Editor</a>工具修改 github.com host</p>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/git/image-20251009110651504.png" alt="image-20251009110651504" /></p>]]></content><author><name>Nine</name></author><category term="GitHub" /><summary type="html"><![CDATA[GitHub 拉取代码报错]]></summary></entry><entry><title type="html">Confluence 的安装与使用</title><link href="https://blog-rs44.onrender.com/2024/04/19/linux-install-confluence/" rel="alternate" type="text/html" title="Confluence 的安装与使用" /><published>2024-04-19T00:00:00+08:00</published><updated>2024-04-19T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2024/04/19/linux-install-confluence</id><content type="html" xml:base="https://blog-rs44.onrender.com/2024/04/19/linux-install-confluence/"><![CDATA[<p>Confluence 是由 Atlassian 公司推出的一款企业级团队协作与知识管理工具。它是一个团队协作空间，将知识与协作无缝融合，让团队能够在一个平台上创建、协作和组织所有工作。</p>

<h5 id="1安装部署">1、安装部署</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 临时修改为 permissive，同时 selinux 配置改为 disabled，下次重启服务器后会生效</span>
setenforce 0
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/^SELINUX=.*/SELINUX=disabled/'</span> /etc/selinux/config

<span class="c"># 新建安装目录</span>
<span class="nb">mkdir</span> <span class="nt">-p</span> /data/confluence/wiki/data <span class="o">&amp;&amp;</span> <span class="nb">chmod </span>777 /data/confluence/wiki/data

<span class="c"># 保证当前目录下存在 docker-compose.yml，根据配置启动服务</span>
docker compose up <span class="nt">-d</span>

<span class="c"># 查看日志</span>
docker logs <span class="nt">--tail</span> 300 <span class="nt">-f</span> confluence

<span class="c"># 访问服务：http://192.168.216.131:18090</span>
点击 Next -&gt; License key 页面记录下 Server ID
</code></pre></div></div>

<p><code class="language-plaintext highlighter-rouge">docker-compose.yml</code>内容如下：</p>

<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code>services:
  mysql8-confluence:
    image: mysql:8.0
    container_name: mysql8-confluence
    ports:
      - 3308:3306
    volumes:
      - /data/confluence/wiki/mysql/conf.d:/etc/mysql/conf.d
      - /data/confluence/wiki/mysql/data:/var/lib/mysql
    security_opt: 
      - seccomp:unconfined
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 'root1234!'
    command: --default-authentication-plugin=mysql_native_password --lower-case-table-names=1

  confluence:
    image: atlassian/confluence:7.9.0
    container_name: confluence
    environment:
      TZ: Asia/Shanghai
    ports:
      - 18090:8090
    volumes:
      - /data/confluence/wiki/data:/var/atlassian/confluence
</code></pre></div></div>

<h5 id="2环境配置">2、环境配置</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 进入 mysql 容器中</span>
docker <span class="nb">exec</span> <span class="nt">-it</span> mysql8-confluence bash

<span class="c"># 登录 mysql</span>
mysql <span class="nt">-uroot</span> <span class="nt">-p</span>

<span class="c"># 创建数据库</span>
CREATE DATABASE confluence CHARACTER SET utf8mb4 COLLATE utf8mb4_bin<span class="p">;</span>
quit
<span class="nb">exit</span>

<span class="c"># 网页下载插件</span>
https://flynine.lanzoul.com/iziJq3b9sumj

<span class="c"># 复制插件到容器 confluence 中</span>
docker <span class="nb">cp </span>atlassian-agent.jar confluence:/home/

<span class="c"># 修改环境变量脚本，增加一行配置</span>
docker <span class="nb">cp </span>confluence:/opt/atlassian/confluence/bin/setenv.sh <span class="nb">.</span>
vim setenv.sh

<span class="nv">CATALINA_OPTS</span><span class="o">=</span><span class="s2">"-javaagent:/home/atlassian-agent.jar </span><span class="k">${</span><span class="nv">CATALINA_OPTS</span><span class="k">}</span><span class="s2">"</span>

docker <span class="nb">cp </span>setenv.sh confluence:/opt/atlassian/confluence/bin/

<span class="c"># 下载 mysql 驱动</span>
wget https://cdn.mysql.com//Downloads/Connector-J/mysql-connector-j-9.5.0.tar.gz

<span class="c"># 解压，复制 jar 包到容器中</span>
<span class="nb">tar</span> <span class="nt">-zxvf</span> mysql-connector-j-9.5.0.tar.gz
docker <span class="nb">cp </span>mysql-connector-j-9.5.0/mysql-connector-j-9.5.0.jar confluence:/opt/atlassian/confluence/confluence/WEB-INF/lib

<span class="c"># 重启容器</span>
docker restart confluence

<span class="c"># 有打印 agent working 则说明插件加载成功</span>
docker logs <span class="nt">--tail</span> 100 <span class="nt">-f</span> confluence

<span class="c"># 重启容器</span>
docker restart confluence

<span class="c"># 有打印 agent working 则说明破解插件加载成功</span>
docker logs <span class="nt">--tail</span> 300 <span class="nt">-f</span> confluence

<span class="c"># 根据上面记录的 Server ID 来生成授权码</span>
docker <span class="nb">exec</span> <span class="nt">-it</span> confluence bash <span class="nt">-c</span> <span class="s2">"java -jar /home/atlassian-agent.jar -p conf -m test.com -m test -o http://192.168.216.131:18090 -s BRJ3-T8L7-J5M3-UIZH"</span>

<span class="c"># 刷新网页：http://192.168.216.131:18090</span>
填入上面生成的 license code —&gt; 选择 My own database -&gt; 选择 MySQL，Setup <span class="nb">type </span>选择 By connection string：jdbc:mysql://192.168.216.131:3308/confluence?useUnicode<span class="o">=</span><span class="nb">true</span>&amp;zeroDateTimeBehavior<span class="o">=</span>convertToNull&amp;characterEncoding<span class="o">=</span>UTF-8&amp;useSSL<span class="o">=</span><span class="nb">false</span>&amp;sessionVariables<span class="o">=</span><span class="nv">transaction_isolation</span><span class="o">=</span><span class="s1">'READ-COMMITTED'</span>
用户名 root，密码在 docker-compose.yml 中有写明，填写后 Test connection，成功后 Next，等待设置完成 -&gt; 选择 empty site -&gt; 设置账号
</code></pre></div></div>

<p><strong>参考</strong></p>

<ul>
  <li><a href="https://zhile.io/2018/12/20/atlassian-license-crack.html">Atlassian系列产品及插件激活方法 JIRA8.19.0+</a></li>
</ul>]]></content><author><name>Nine</name></author><category term="Confluence" /><summary type="html"><![CDATA[Confluence 的安装与使用]]></summary></entry><entry><title type="html">Termux 的简单使用</title><link href="https://blog-rs44.onrender.com/2024/03/12/termux-with-android/" rel="alternate" type="text/html" title="Termux 的简单使用" /><published>2024-03-12T00:00:00+08:00</published><updated>2024-03-12T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2024/03/12/termux-with-android</id><content type="html" xml:base="https://blog-rs44.onrender.com/2024/03/12/termux-with-android/"><![CDATA[<p>Termux 是一个 Android 下一个高级的终端模拟器，开源且不需要 root，支持 apt 管理软件包，十分方便安装软件包，完美支持 Python、 PHP、 Ruby、 Nodejs、 MySQL 等。</p>

<p>首次启动 Termux 时需要从<a href="http://termux.net/bootstrap/">远程服务器</a>加载数据，可能会遇到以下报错，可尝试使用代理或流量再重试：</p>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/android/image-20240312102411238.png" alt="image-20240312102411238" /></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 替换官方源为 TUNA 镜像源</span>
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s@^\(deb.*stable main\)$@#\1\ndeb https://mirrors.tuna.tsinghua.edu.cn/termux/apt/termux-main stable main@'</span> <span class="nv">$PREFIX</span>/etc/apt/sources.list
apt update <span class="o">&amp;&amp;</span> apt upgrade

<span class="c"># 安装基本工具</span>
pkg <span class="nb">install </span>vim curl wget git unzip <span class="nt">-y</span>
</code></pre></div></div>

<p>Termux 除了支持 <code class="language-plaintext highlighter-rouge">apt</code> 命令外，还在此基础上封装了 <code class="language-plaintext highlighter-rouge">pkg</code> 命令，<code class="language-plaintext highlighter-rouge">pkg</code> 命令向下兼容 <code class="language-plaintext highlighter-rouge">apt</code> 命令</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkg search &lt;query&gt;              <span class="c"># 搜索包</span>
pkg <span class="nb">install</span> &lt;package&gt;           <span class="c"># 安装包</span>
pkg uninstall &lt;package&gt;         <span class="c"># 卸载包</span>
pkg reinstall &lt;package&gt;         <span class="c"># 重新安装包</span>
pkg update                      <span class="c"># 更新源</span>
pkg upgrade                     <span class="c"># 升级软件包</span>
pkg list-all                    <span class="c"># 列出可供安装的所有包</span>
pkg list-installed              <span class="c"># 列出已经安装的包</span>
pkg show &lt;package&gt;              <span class="c"># 显示某个包的详细信息</span>
pkg files &lt;package&gt;             <span class="c"># 显示某个包的相关文件夹路径</span>

<span class="c"># 相关目录</span>
<span class="nb">echo</span> <span class="nv">$HOME</span>
/data/data/com.termux/files/home

<span class="nb">echo</span> <span class="nv">$PREFIX</span>
/data/data/com.termux/files/usr

<span class="nb">echo</span> <span class="nv">$TMPPREFIX</span>
/data/data/com.termux/files/usr/tmp/zsh
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 终端配色，github 访问不了的可以使用第二个地址</span>
sh <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://github.com/Cabbagec/termux-ohmyzsh/raw/master/install.sh<span class="si">)</span><span class="s2">"</span>
sh <span class="nt">-c</span> <span class="s2">"</span><span class="si">$(</span>curl <span class="nt">-fsSL</span> https://html.sqlsec.com/termux-install.sh<span class="si">)</span><span class="s2">"</span>
</code></pre></div></div>

<p>Android6.0 以上会弹框确认是否授权访问文件，点击<code class="language-plaintext highlighter-rouge">允许</code>授权后 Termux 可以方便的访问 SD 卡文件</p>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/android/image-20240314213753019.png" alt="image-20240314213753019" /></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 脚本允许后先后有如下两个选项，分别是背景色和字体</span>
Enter a number, leave blank to not to change: 14
Enter a number, leave blank to not to change: 22

<span class="c"># 想要继续更改挑选配色的话，可以运行脚本再次选择</span>
~/.termux/colors.sh
~/.termux/fonts.sh
</code></pre></div></div>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/android/image-20240314202804546.png" alt="image-20240314202804546" /></p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 查看本机 ip </span>
ifconfig

<span class="c"># 启动 SSH 服务并配置自启动</span>
sshd <span class="o">&amp;&amp;</span> <span class="nb">echo </span>sshd <span class="o">&gt;&gt;</span> ~/.bashrc

<span class="c"># 查看当前用户</span>
<span class="nb">whoami</span>

<span class="c"># 设置密码</span>
passwd

<span class="c"># 查看 ssh 端口，一般为 8022</span>
netstat <span class="nt">-tnlp</span>
</code></pre></div></div>

<p>然后可以使用 xshell 等工具进行连接</p>

<p>proot-distro 支持几乎所有常用的 Linux 发行版：Alpine、Arch、Debian、ubuntu、manjaro 等等</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code>pkg <span class="nb">install </span>proot-distro <span class="nt">-y</span>

<span class="c"># 查看本机支持安装的 linux 环境</span>
proot-distro list

<span class="c"># 安装 ubuntu 环境，等待安装完成</span>
proot-distro <span class="nb">install </span>ubuntu

<span class="c"># 进入 ubuntu shell 环境</span>
proot-distro login ubuntu
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="Android" /><summary type="html"><![CDATA[Android 上终端模拟器简单使用]]></summary></entry><entry><title type="html">pgAdmin4 的安装与使用</title><link href="https://blog-rs44.onrender.com/2024/03/11/linux-install-pgadmin4/" rel="alternate" type="text/html" title="pgAdmin4 的安装与使用" /><published>2024-03-11T00:00:00+08:00</published><updated>2024-03-11T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2024/03/11/linux-install-pgadmin4</id><content type="html" xml:base="https://blog-rs44.onrender.com/2024/03/11/linux-install-pgadmin4/"><![CDATA[<p>pgAdmin4 是一个用于管理和维护 PostgreSQL 数据库的强大工具。它提供了一个图形化界面，使用户能够轻松地连接到数据库、创建表、运行 SQL 语句以及执行其他数据库管理任务。</p>

<h5 id="1yum-安装">1、yum 安装</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 临时修改为 permissive，同时 selinux 配置改为 disabled，下次重启服务器后会生效</span>
setenforce 0
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/^SELINUX=.*/SELINUX=disabled/'</span> /etc/selinux/config

<span class="c"># 添加 pgAdmin 存储库</span>
wget <span class="nt">--no-check-certificate</span> https://ftp.postgresql.org/pub/pgadmin/pgadmin4/yum/pgadmin4-redhat-repo-2-1.noarch.rpm

rpm <span class="nt">-ivh</span> pgadmin4-redhat-repo-2-1.noarch.rpm

<span class="c"># centos7 及以下系统版本需要修改存储库地址</span>
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s|ftp.postgresql.org/pub/pgadmin|pgadmin-archive.postgresql.org|'</span> /etc/yum.repos.d/pgadmin4.repo

<span class="c"># 查询可以安装的软件</span>
yum <span class="nt">-y</span> search pgadmin

<span class="c"># 安装 web 端软件</span>
yum <span class="nt">-y</span> <span class="nb">install </span>pgadmin4-web

<span class="c"># 修改默认端口 80 为 18080</span>
vim /etc/httpd/conf/httpd.conf

<span class="c"># 执行配置脚本，根据提示配置账号密码</span>
/usr/pgadmin4/bin/setup-web.sh

<span class="c"># 访问服务</span>
http://192.168.216.131:18080/pgadmin4/login
</code></pre></div></div>

<h5 id="2配置代理">2、配置代理</h5>

<p>使用 nginx 代理访问</p>

<div class="language-nginx highlighter-rouge"><div class="highlight"><pre class="highlight"><code>        <span class="k">location</span> <span class="n">/pgadmin4</span> <span class="p">{</span>
            <span class="kn">proxy_pass</span> <span class="s">http://192.168.216.131:18080</span><span class="p">;</span>
            <span class="kn">proxy_set_header</span> <span class="s">Host</span> <span class="nv">$host</span><span class="p">:</span><span class="nv">$server_port</span><span class="p">;</span>
            <span class="kn">proxy_set_header</span> <span class="s">X-Real-IP</span> <span class="nv">$remote_addr</span><span class="p">;</span>
            <span class="kn">proxy_set_header</span> <span class="s">X-Forwarded-For</span> <span class="nv">$proxy_add_x_forwarded_for</span><span class="p">;</span>
        <span class="p">}</span>
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="PostgreSQL" /><summary type="html"><![CDATA[pgAdmin4 的安装与使用]]></summary></entry><entry><title type="html">Linux 配置免密登录</title><link href="https://blog-rs44.onrender.com/2024/01/22/linux-ssh-passwordless-authentication/" rel="alternate" type="text/html" title="Linux 配置免密登录" /><published>2024-01-22T00:00:00+08:00</published><updated>2024-01-22T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2024/01/22/linux-ssh-passwordless-authentication</id><content type="html" xml:base="https://blog-rs44.onrender.com/2024/01/22/linux-ssh-passwordless-authentication/"><![CDATA[<p>使用非对称加密算法制作一对密钥（公钥、私钥），将公钥添加到服务器的某个账户上，然后在客户端利用私钥即可完成认证并登录，公钥存在服务器，私钥存在本地计算机，私钥不在网络传输，降低被黑客截获风险。</p>

<h5 id="1配置密钥">1、配置密钥</h5>

<p>配置服务器  192.168.216.128 免密登录  192.168.216.129</p>

<ul>
  <li>服务器 192.168.216.128 上使用<code class="language-plaintext highlighter-rouge">ssh-keygen</code>命令生成密钥，<code class="language-plaintext highlighter-rouge">-t</code>未指定参数时默认的密钥加密方法是 RSA</li>
</ul>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 使用 ED25519 加密方式生成密钥，若有设置 passphrase，则 ssh 登录时还需要输入密钥的密码</span>
ssh-keygen <span class="nt">-t</span> ed25519

<span class="c"># 将公钥写入到服务器 192.168.216.129 的 ~/.ssh/authorized_keys 文件中（文件不存在会自动创建），执行后需要输入密码</span>
ssh-copy-id <span class="nt">-p</span> 22333 guest@192.168.216.129
</code></pre></div></div>

<blockquote>
  <p>指定密钥名称：<code class="language-plaintext highlighter-rouge">ssh-keygen -t ed25519 -f ~/.ssh/test</code></p>

  <p>指定密钥文件：<code class="language-plaintext highlighter-rouge">ssh-copy-id -i ~/.ssh/test.pub guest@192.168.216.129</code></p>

  <p>使用密钥登录：<code class="language-plaintext highlighter-rouge">ssh -i ~/.ssh/test root@192.168.216.129</code></p>
</blockquote>

<ul>
  <li>本地可以使用 Xshell 生成密钥：工具 -&gt; 用户密钥管理者</li>
</ul>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/linux/image-20240122100348599.png" alt="image-20240122100348599" /></p>

<p>将公钥手动写入远程机器的<code class="language-plaintext highlighter-rouge">~/.ssh/authorized_keys</code>文件中</p>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 目录和文件不存在就手动创建，注意目录和文件权限</span>
<span class="nb">mkdir</span> ~/.ssh <span class="o">&amp;&amp;</span> <span class="nb">chmod </span>700 ~/.ssh
<span class="nb">touch</span> ~/.ssh/authorized_keys <span class="o">&amp;&amp;</span> <span class="nb">chmod </span>600 ~/.ssh/authorized_keys
vim ~/.ssh/authorized_keys
</code></pre></div></div>

<h5 id="2测试登录">2、测试登录</h5>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 测试免密登录</span>
ssh root@192.168.216.129

<span class="c"># 使用指定密钥连接，注意私钥权限应为 600</span>
ssh <span class="nt">-i</span> ~/.ssh/id_ed25519_256-51 <span class="nt">-p</span> 22333 root@192.168.216.129
</code></pre></div></div>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/linux/image-20240122110254517.png" alt="image-20240122110254517" /></p>

<h5 id="3问题">3、问题</h5>

<p>配置后免密登录仍未生效时，可以查看服务器日志信息排查问题：<code class="language-plaintext highlighter-rouge">tail -f /var/log/secure</code></p>

<p>通过 RSA 密钥远程连接服务器时，提示登录失败，sshd 日志提示</p>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>userauth_pubkey: signature algorithm ssh-rsa not in PubkeyAcceptedAlgorithms [preauth]
</code></pre></div></div>

<blockquote>
  <p>解决方案：使用 ECDSA 或者 ED25519 等其他加密方式</p>

  <p>ssh-rsa 签名算法是 SHA1 的哈希算法和 RSA 公钥算法的结合使用，由于目前 SHA1 的哈希算法容易受到攻击，OpenSSH 从 8.7 以后版本开始默认不支持 ssh-rsa 签名的方式，因此使用 RSA 生成的密钥配置免密登录时有以下错误</p>
</blockquote>

<h5 id="4其它">4、其它</h5>

<p>配置完成后可以关闭服务器密码登录：<code class="language-plaintext highlighter-rouge">vim /etc/ssh/sshd_config</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 禁止密码登录</span>
PasswordAuthentication no

<span class="c"># 重启 SSH 服务</span>
service sshd restart
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="Linux" /><summary type="html"><![CDATA[SSH 免密登录配置]]></summary></entry><entry><title type="html">Docker 使用技巧</title><link href="https://blog-rs44.onrender.com/2023/07/09/docker-skill/" rel="alternate" type="text/html" title="Docker 使用技巧" /><published>2023-07-09T00:00:00+08:00</published><updated>2023-07-09T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2023/07/09/docker-skill</id><content type="html" xml:base="https://blog-rs44.onrender.com/2023/07/09/docker-skill/"><![CDATA[<p>学习和使用 Docker 过程中遇到的问题和解决办法。</p>

<h5 id="配置镜像源">配置镜像源</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">tee</span> /etc/docker/daemon.json <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.1panel.live",
        "https://hub.rat.dev"
    ]
}
</span><span class="no">EOF

</span>systemctl daemon-reload <span class="o">&amp;&amp;</span> systemctl restart docker
</code></pre></div></div>

<h5 id="镜像导入导出">镜像导入导出</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 查看已安装的镜像</span>
docker images

<span class="c"># 将镜像导出成 tar 文件，其中 docker.io/a76yyyy/qiandao:latest 可以用 image id 代替，但是恢复时 repository 和 tar 都会是 none</span>
docker save <span class="nt">-o</span> qiandao.tar docker.io/a76yyyy/qiandao:latest

<span class="c"># 镜像文件导入</span>
docker load <span class="nt">-i</span> qiandao.tar

<span class="c"># 压缩导出</span>
docker save docker.io/a76yyyy/qiandao:latest | <span class="nb">gzip</span> <span class="o">&gt;</span> qiandao.tar.gz

<span class="c"># 压缩导入</span>
<span class="nb">gunzip</span> <span class="nt">-c</span> qiandao.tar.gz | docker load
</code></pre></div></div>

<p><img src="https://fastly.jsdelivr.net/gh/FlyNine/cloudimage/docker/image-20230618001414234.png" alt="image-20230618001414234" /></p>

<h5 id="修改容器启动策略">修改容器启动策略</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker update <span class="nt">--restart</span><span class="o">=</span>always name
</code></pre></div></div>

<blockquote>
  <ul>
    <li>no -  容器退出时，不重启容器；</li>
    <li>on-failure - 只有在非 0 状态退出时才从新启动容器；</li>
    <li>always - docker 启动的时候自动启动容器，始终自动重启</li>
  </ul>
</blockquote>

<h5 id="容器内执行-shell-脚本">容器内执行 shell 脚本</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 宿主机上执行，脚本在容器内存在</span>
docker <span class="nb">exec </span>test-1 /bin/bash /data/test.sh

<span class="c"># 获取容器内 guest 用户的 UID</span>
docker <span class="nb">exec </span>test-1 <span class="nb">id</span> <span class="nt">-u</span> guest

<span class="c"># 以容器内 guest 用户身份执行命令</span>
docker <span class="nb">exec</span> <span class="nt">-u</span> guest test-1 /bin/bash /data/test.sh

<span class="c"># 以容器内 UID 为 1000 的用户身份执行命令</span>
docker <span class="nb">exec</span> <span class="nt">-u</span> 1000 test-1 /bin/bash /data/test.sh
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="Docker" /><summary type="html"><![CDATA[记录学习 Docker 中一些方便使用的技巧]]></summary></entry><entry><title type="html">Docker 的安装与使用</title><link href="https://blog-rs44.onrender.com/2023/07/09/linux-install-docker/" rel="alternate" type="text/html" title="Docker 的安装与使用" /><published>2023-07-09T00:00:00+08:00</published><updated>2023-07-09T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2023/07/09/linux-install-docker</id><content type="html" xml:base="https://blog-rs44.onrender.com/2023/07/09/linux-install-docker/"><![CDATA[<p>Docker 是一个开源的应用容器引擎，可以让开发者打包他们的应用以及依赖包到一个轻量级、可移植的容器中，然后发布到任何流行的 Linux 机器上，也可以实现虚拟化。 容器是完全使用沙箱机制，相互之间不会有任何接口（类似 iPhone 的 app），更重要的是容器性能开销极低。</p>

<h5 id="1centos-安装-docker">1、centos 安装 docker</h5>

<p>Docker 从 17.03 版本之后分为 CE 和 EE 两大版本。CE 即社区版（免费，支持周期 7 个月），EE 即企业版，强调安全，付费使用，支持周期 24 个月。</p>

<p>Docker CE 支持 64 位版本 CentOS 7，并且要求内核版本不低于 3.10， CentOS 7 满足最低内核的要求</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 卸载旧版 docker</span>
yum remove docker docker-client docker-client-latest docker-common docker-latest docker-latest-logrotate docker-logrotate docker-engine

<span class="c"># 设置 docker 存储库，docker 官网地址无法访问的可以使用国内其它镜像地址</span>
curl <span class="nt">-o</span> /etc/yum.repos.d/docker-ce.repo https://mirrors.tuna.tsinghua.edu.cn/docker-ce/linux/centos/docker-ce.repo <span class="o">&amp;&amp;</span> <span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s/download.docker.com/mirrors.tuna.tsinghua.edu.cn\/docker-ce/g'</span> /etc/yum.repos.d/docker-ce.repo

curl <span class="nt">-o</span> /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

<span class="c"># 安装最新版 docker 引擎和容器</span>
yum <span class="nt">-y</span> <span class="nb">install </span>docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

<span class="c"># 启动 docker 并设置为开机自启</span>
systemctl <span class="nb">enable</span> <span class="nt">--now</span> docker

<span class="c"># 查看版本</span>
docker <span class="nt">-v</span>
</code></pre></div></div>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装指定版本</span>
yum list docker-ce <span class="nt">--showduplicates</span> | <span class="nb">sort</span> <span class="nt">-r</span>
yum <span class="nb">install </span>docker-ce-3:24.0.0-1.el8 docker-ce-cli-3:24.0.0-1.el8 containerd.io docker-buildx-plugin docker-compose-plugin
</code></pre></div></div>

<blockquote>
  <p>官方文档：https://docs.docker.com/engine/install/centos/</p>
</blockquote>

<h5 id="2ubuntu-安装-docker">2、ubuntu 安装 docker</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># Add Docker's official GPG key:</span>
<span class="nb">sudo </span>apt-get update
<span class="nb">sudo </span>apt-get <span class="nb">install </span>ca-certificates curl
<span class="nb">sudo install</span> <span class="nt">-m</span> 0755 <span class="nt">-d</span> /etc/apt/keyrings
<span class="nb">sudo </span>curl <span class="nt">-fsSL</span> https://download.docker.com/linux/ubuntu/gpg <span class="nt">-o</span> /etc/apt/keyrings/docker.asc
<span class="nb">sudo chmod </span>a+r /etc/apt/keyrings/docker.asc

<span class="c"># 设置 docker 存储库</span>
<span class="nb">echo</span> <span class="s2">"deb [arch=</span><span class="si">$(</span>dpkg <span class="nt">--print-architecture</span><span class="si">)</span><span class="s2"> signed-by=/etc/apt/keyrings/docker.asc] https://download.docker.com/linux/ubuntu </span><span class="si">$(</span><span class="nb">.</span> /etc/os-release <span class="o">&amp;&amp;</span> <span class="nb">echo</span> <span class="s2">"</span><span class="k">${</span><span class="nv">UBUNTU_CODENAME</span><span class="k">:-</span><span class="nv">$VERSION_CODENAME</span><span class="k">}</span><span class="s2">"</span><span class="si">)</span><span class="s2"> stable"</span> | <span class="nb">tee</span> /etc/apt/sources.list.d/docker.list <span class="o">&gt;</span> /dev/null
apt-get update

apt-get <span class="nb">install </span>docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

<span class="c"># 查看版本</span>
docker <span class="nt">-v</span>

<span class="c"># 启动 docker</span>
service docker start
</code></pre></div></div>

<h5 id="3配置镜像源">3、配置镜像源</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 国内拉取速度过慢时可以配置镜像源来访问</span>
<span class="nb">tee</span> /etc/docker/daemon.json <span class="o">&lt;&lt;</span><span class="no">EOF</span><span class="sh">
{
    "registry-mirrors": [
        "https://docker.m.daocloud.io",
        "https://docker.1panel.live",
        "https://hub.rat.dev"
    ],
    "dns": ["8.8.8.8", "8.8.4.4"]
}
</span><span class="no">EOF

</span><span class="c"># 重启 docker</span>
systemctl daemon-reload <span class="o">&amp;&amp;</span> systemctl restart docker
</code></pre></div></div>

<h5 id="4常用命令">4、常用命令</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 普通用户赋予 docker 命令执行权限</span>
usermod <span class="nt">-aG</span> docker guest

<span class="c"># 查看运行中的 docker 进程</span>
docker ps <span class="nt">-a</span>

<span class="c"># 启动/停止/重启容器</span>
docker start/stop/restart <span class="nb">id</span>/name

<span class="c"># 查看已安装的镜像</span>
docker images

<span class="c"># 删除某个容器</span>
docker <span class="nb">rm id</span>/name

<span class="c"># 删除某个镜像</span>
docker rmi <span class="nb">id</span>/name

<span class="c"># 获取 Docker 对象（容器、镜像、卷、网络等）的详细信息</span>
docker inspect <span class="nb">id</span>/name

<span class="c"># 实时显示 Docker 容器的资源使用情况</span>
docker stats

<span class="c"># 查看空间占用情况</span>
docker system <span class="nb">df</span>
</code></pre></div></div>

<p><strong>参考</strong></p>

<ul>
  <li><a href="https://www.runoob.com/docker/docker-command-manual.html">Docker 命令大全 | 菜鸟教程</a></li>
</ul>]]></content><author><name>Nine</name></author><category term="Docker" /><summary type="html"><![CDATA[Docker 的安装与使用]]></summary></entry><entry><title type="html">PostgreSQL 的安装与使用</title><link href="https://blog-rs44.onrender.com/2022/12/13/linux-install-postgresql/" rel="alternate" type="text/html" title="PostgreSQL 的安装与使用" /><published>2022-12-13T00:00:00+08:00</published><updated>2022-12-13T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2022/12/13/linux-install-postgresql</id><content type="html" xml:base="https://blog-rs44.onrender.com/2022/12/13/linux-install-postgresql/"><![CDATA[<p>服务器系统版本为 centos 7.7。</p>

<h4 id="1软件安装">1、软件安装</h4>

<p>选择其中 1 种方式安装即可</p>

<h5 id="1yum-安装">(1)、yum 安装</h5>

<p>官方说明：<a href="https://www.postgresql.org/download/linux/redhat/">https://www.postgresql.org/download/linux/redhat/</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装 PostgreSQL 软件仓库</span>
yum <span class="nb">install</span> <span class="nt">-y</span> https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm

<span class="c"># 查询支持的版本</span>
yum <span class="nt">-y</span> search <span class="s1">'PostgreSQL client programs'</span>

<span class="c"># 安装 PostgreSQL，默认安装目录：/var/lib/</span>
yum <span class="nb">install</span> <span class="nt">-y</span> postgresql15-server

<span class="c"># 初始化数据库</span>
/usr/pgsql-15/bin/postgresql-15-setup initdb

<span class="c"># 按需修改数据库配置文件</span>
vim /var/lib/pgsql/15/data/postgresql.conf

listen_addresses <span class="o">=</span> <span class="s1">'*'</span>
max_connections <span class="o">=</span> 200

logging_collector <span class="o">=</span> on
log_directory <span class="o">=</span> <span class="s1">'log'</span>
log_filename <span class="o">=</span> <span class="s1">'postgresql-%Y-%m-%d_%H%M%S.log'</span>
log_file_mode <span class="o">=</span> 0644
log_line_prefix <span class="o">=</span> <span class="s1">'%m %u %d %p %r %e'</span>
log_statement <span class="o">=</span> <span class="s1">'ddl'</span>

<span class="c"># 设置开机自启</span>
systemctl <span class="nb">enable </span>postgresql-15

<span class="c"># 启动数据库</span>
systemctl start postgresql-15

<span class="c"># 开放防火墙端口</span>
firewall-cmd <span class="nt">--add-port</span><span class="o">=</span>5432/tcp <span class="nt">--permanent</span>
firewall-cmd <span class="nt">--reload</span>
</code></pre></div></div>

<h5 id="2源码安装">(2)、源码安装</h5>

<p>源码安装包：<a href="https://www.postgresql.org/ftp/source">https://www.postgresql.org/ftp/source</a></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 安装依赖</span>
yum <span class="nb">install</span> <span class="nt">-y</span> gcc gcc-c++ make libicu-devel bison flex readline-devel zlib-devel wget vim

<span class="c"># 下载解压安装包</span>
wget https://ftp.postgresql.org/pub/source/v17.6/postgresql-17.6.tar.gz
<span class="nb">tar</span> <span class="nt">-zxvf</span> postgresql-17.6.tar.gz <span class="o">&amp;&amp;</span> <span class="nb">cd </span>postgresql-17.6
./configure <span class="nt">--prefix</span><span class="o">=</span>/usr/local/pgsql
make <span class="nt">-j2</span> <span class="o">&amp;&amp;</span> make <span class="nt">-j2</span> <span class="nb">install</span>

<span class="c"># 添加用户并创建数据目录</span>
useradd postgres
<span class="nb">mkdir</span> <span class="nt">-p</span> /data/pgsql/data
<span class="nb">chown</span> <span class="nt">-R</span> postgres:postgres /data/pgsql/data
<span class="nb">chown</span> <span class="nt">-R</span> postgres:postgres /usr/local/pgsql

<span class="c"># 配置环境变量</span>
<span class="nb">cat</span> <span class="o">&gt;&gt;</span> /etc/profile <span class="o">&lt;&lt;</span> <span class="sh">"</span><span class="no">EOF</span><span class="sh">"
# postgresql
export PGHOME=/usr/local/pgsql
export PGDATA=/data/pgsql/data
export PATH=</span><span class="nv">$PGHOME</span><span class="sh">/bin:</span><span class="nv">$PATH</span><span class="sh">
</span><span class="no">EOF

</span><span class="c"># 使环境变量生效</span>
<span class="nb">source</span> /etc/profile

<span class="c"># 初始化数据库</span>
su - postgres
/usr/local/pgsql/bin/initdb <span class="nt">-D</span> /data/pgsql/data

<span class="c"># 修改数据库配置文件</span>
vim /data/pgsql/data/postgresql.conf

listen_addresses <span class="o">=</span> <span class="s1">'*'</span>
max_connections <span class="o">=</span> 4000

shared_buffers <span class="o">=</span> 4GB
work_mem <span class="o">=</span> 64MB
maintenance_work_mem <span class="o">=</span> 1GB
effective_cache_size <span class="o">=</span> 12GB
wal_buffers <span class="o">=</span> 16MB
checkpoint_completion_target <span class="o">=</span> 0.9

logging_collector <span class="o">=</span> on
log_directory <span class="o">=</span> <span class="s1">'log'</span>
log_filename <span class="o">=</span> <span class="s1">'postgresql-%Y-%m-%d_%H%M%S.log'</span>
log_file_mode <span class="o">=</span> 0644
log_line_prefix <span class="o">=</span> <span class="s1">'%m %u %d %p %r %e'</span>
log_statement <span class="o">=</span> <span class="s1">'ddl'</span>

<span class="c"># 启动服务</span>
/usr/local/pgsql/bin/pg_ctl <span class="nt">-D</span> /data/pgsql/data start

<span class="c"># 复制源码包里的启动脚本至 /etc/init.d 目录下，并加执行权限</span>
<span class="nb">cp </span>postgresql-17.6/contrib/start-scripts/linux /etc/init.d/postgresql
<span class="nb">chmod</span> +x /etc/init.d/postgresql

<span class="c"># 修改数据目录</span>
<span class="nb">sed</span> <span class="nt">-i</span> <span class="s1">'s|^PGDATA=.*|PGDATA="/data/pgsql/data"|'</span> /etc/init.d/postgresql

<span class="c"># 设置开机启动</span>
chkconfig <span class="nt">--add</span> postgresql

<span class="c"># 启动服务</span>
systemctl start postgresql

<span class="c"># 服务状态</span>
systemctl status postgresql

<span class="c"># 重启数据库</span>
/usr/local/pgsql/bin/pg_ctl <span class="nt">-D</span> /data/pgsql/data rstart

<span class="c"># 开放防火墙端口</span>
firewall-cmd <span class="nt">--add-port</span><span class="o">=</span>5432/tcp <span class="nt">--permanent</span>
firewall-cmd <span class="nt">--reload</span>
</code></pre></div></div>

<blockquote>
  <p><code class="language-plaintext highlighter-rouge">listen_addresses</code>：* 为允许服务器监听所有可用的网络接口，允许远程连接</p>

  <p><code class="language-plaintext highlighter-rouge">max_connections</code>：决定允许的最大数据库连接数。过多的连接会增加系统开销和资源竞争。通常可以使用连接池工具（如 PgBouncer）来控制并发连接数；</p>

  <p><code class="language-plaintext highlighter-rouge">shared_buffers</code>：这是 PostgreSQL 用于缓存表数据的共享内存区域，通常建议设置为物理内存的 25%-40%。如果设置过低，会导致频繁的磁盘访问；设置过高则会占用操作系统内存，减少可用的文件缓存；</p>

  <p><code class="language-plaintext highlighter-rouge">work_mem</code>：每个查询操作（如排序、哈希表）所使用的内存。这个参数是每个查询连接单独分配的，因此需要根据查询复杂度和并发量合理设置。如果过小，查询需要频繁进行磁盘交换；过大会导致内存不足。典型值在 10MB-100MB 之间；</p>

  <p><code class="language-plaintext highlighter-rouge">maintenance_work_mem</code>：控制 PostgreSQL 在执行维护操作时使用的内存大小，比如创建索引、VACUUM。推荐设置为较大的值，尤其是在大规模数据集上操作时；</p>

  <p><code class="language-plaintext highlighter-rouge">effective_cache_size</code>：PostgreSQL 根据此参数判断系统可用的文件系统缓存大小，从而决定是否使用索引扫描或全表扫描。建议设置为物理内存的 50%-75%；</p>

  <p><code class="language-plaintext highlighter-rouge">wal_buffers</code>：建议设置为 shared_buffers 的 1/32，用于缓冲 WAL 数据，避免频繁写入磁盘；</p>

  <p><code class="language-plaintext highlighter-rouge">checkpoint_completion_target</code>：设置为接近 1 的值可以平滑 WAL 日志写入压力，减少突发I/O 操作。</p>

  <p><code class="language-plaintext highlighter-rouge">logging_collector</code>：开启日志收集器，用于收集数据库服务器的日志信息</p>

  <p><code class="language-plaintext highlighter-rouge">log_directory</code>：日志文件的存储目录为 pg_log</p>

  <p><code class="language-plaintext highlighter-rouge">log_filename</code>：日志文件名包含月份和日期信息</p>

  <p><code class="language-plaintext highlighter-rouge">log_file_mode</code>：日志文件权限</p>

  <p><code class="language-plaintext highlighter-rouge">log_line_prefix</code>：日志行前缀（%m 时间，%u 用户名称，%d 数据库名称，%r 客户端 IP 和端口，%e sql 语句）</p>

  <p><code class="language-plaintext highlighter-rouge">log_statement</code>：none、ddl、mod、all 控制记录哪些 SQL 语句。none 不记录，ddl 记录所有数据定义命令，比如 CREATE、ALTER、DROP 语句。mod 记录所有 ddl 语句,加上数据修改语句INSERT、UPDATE 等，all 记录所有执行的语句，将此配置设置为 all 可跟踪整个数据库执行的 SQL 语句</p>
</blockquote>

<h4 id="2密码修改">2、密码修改</h4>

<p>安装完成后修改数据库密码并配置密码认证</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 数据库密码修改</span>
su - postgres
psql
alter user postgres with password <span class="s1">'pgsqlabcd!'</span><span class="p">;</span>

<span class="c"># 创建用户</span>
CREATE USER testuser with password <span class="s1">'pgsqlabcd!'</span><span class="p">;</span>

<span class="c"># 增加允许连接的 ip 地址，0.0.0.0/0 表示任意地址，192.168.1.11/32 表示仅允许 192.168.1.11 连接</span>
vim <span class="nv">$PGDATA</span>/pg_hba.conf

host    all             all             0.0.0.0/0               scram-sha-256
host    all             all             192.168.1.11/32         scram-sha-256

<span class="c"># 根据 postgresql.conf 中的 shared-buffers 值计算需要的大页数量</span>
/usr/local/pgsql/bin/postgres <span class="nt">-D</span> <span class="nv">$PGDATA</span> <span class="nt">--shared-buffers</span><span class="o">=</span>12GB <span class="nt">--huge-pages</span><span class="o">=</span>on <span class="nt">-C</span> shared_buffers

<span class="c"># 修改系统配置</span>
vim /etc/sysctl.conf
vm.nr_hugepages <span class="o">=</span> 6146

<span class="c"># 使之生效</span>
sysctl <span class="nt">-p</span>

<span class="c"># 检查大页是否分配成功</span>
<span class="nb">cat</span> /proc/meminfo | <span class="nb">grep </span>Huge

<span class="c"># 重启数据库（yum 安装方式）</span>
systemctl restart postgresql-15

<span class="c"># 重启数据库（源码安装方式）</span>
systemctl restart postgresql
</code></pre></div></div>

<blockquote>
  <p>在传统的操作系统内存管理中，内存被划分为许多小块，通常每块是 <strong>4KB</strong>（这个值取决于架构，x86_64 默认是 4KB），这被称为“基础页”。</p>

  <p><strong>问题：</strong> 当一个像 PostgreSQL 这样需要大量内存的进程（例如，<code class="language-plaintext highlighter-rouge">shared_buffers</code> 设置为几十GB）运行时，它需要管理成千上万甚至数百万个这些 4KB 的小页面。这会导致：</p>

  <ul>
    <li><strong>更高的 CPU 开销：</strong> 操作系统需要维护更多的页表项（Page Table Entries）。</li>
    <li><strong>更高的 TLB 压力：</strong> TLB（Translation Lookaside Buffer）是 CPU 中的一个高速缓存，用于快速将虚拟地址转换为物理地址。TLB 容量很小，如果进程需要频繁访问大量分散的 4KB 页面，会导致 TLB 缓存未命中（TLB miss），CPU 就不得不去访问更慢的主内存来查找地址转换关系，这会显著降低性能。</li>
  </ul>

  <p><strong>解决方案：大页（Huge Pages）</strong></p>
  <ul>
    <li>大页是另一种内存页大小，通常是 <strong>2MB</strong> 或 <strong>1GB</strong>（相比之下，基础页是 4KB）。</li>
    <li>通过使用 2MB 的大页，同样管理 16GB 的内存，操作系统只需要处理 <code class="language-plaintext highlighter-rouge">16GB / 2MB = 8192</code> 个页表项，而不是 <code class="language-plaintext highlighter-rouge">16GB / 4KB = 4,194,304</code> 个。这减少了超过 99% 的页表项数量！</li>
    <li>TLB 可以缓存更多的地址转换关系，从而极大减少 TLB miss，提高内存访问效率。</li>
  </ul>
</blockquote>]]></content><author><name>Nine</name></author><category term="PostgreSQL" /><summary type="html"><![CDATA[PostgreSQL 的安装与使用]]></summary></entry><entry><title type="html">JMeter 常用函数</title><link href="https://blog-rs44.onrender.com/2022/11/22/jmeter-functions/" rel="alternate" type="text/html" title="JMeter 常用函数" /><published>2022-11-22T00:00:00+08:00</published><updated>2022-11-22T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2022/11/22/jmeter-functions</id><content type="html" xml:base="https://blog-rs44.onrender.com/2022/11/22/jmeter-functions/"><![CDATA[<p>JMeter 官网说明：<a href="https://jmeter.apache.org/usermanual/functions.html">https://jmeter.apache.org/usermanual/functions.html</a></p>

<h4 id="1内置函数方法">1、内置函数方法</h4>

<h5 id="时间格式化">时间格式化</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 北京时间 utc+8</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(YMD,)</span><span class="k">}</span>                            <span class="c"># 20180121</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(yyyyMMddHHmmss)</span><span class="k">}</span>                  <span class="c"># 20221012174329</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(YYYY-MM-dd HH</span>:mm:ss,time<span class="p">)</span><span class="k">}</span>        <span class="c"># 2022-10-12 17:43:29</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(YYYY-MM-dd HH</span>:mm:ss.SSS,time<span class="p">)</span><span class="k">}</span>    <span class="c"># 2022-10-12 17:43:29.999</span>

<span class="c"># 日期格式化：20180121 -&gt; 2018-01-21</span>
<span class="k">${</span><span class="nv">__dateTimeConvert</span><span class="p">(20180121,yyyyMMdd,yyyy-MM-dd,)</span><span class="k">}</span>

<span class="c"># 当前时间 +20s，PT10m：+10 分钟，PT1H：+1 小时，P1d：+1 天，P1dT1H10m20s：+1 天 1 小时 10 分钟 20 秒</span>
<span class="k">${</span><span class="nv">__timeShift</span><span class="p">(yyyy-MM-dd HH</span>:mm:ss,,PT20S,,<span class="p">)</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="时间戳">时间戳</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 13 位时间戳：1665566580027</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(,)</span><span class="k">}</span>

<span class="c"># 10 位时间戳：1665566580</span>
<span class="k">${</span><span class="nv">__time</span><span class="p">(/1000,)</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="uuid">uuid</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>// uuid：fea9aafd-e03e-4682-9f35-878c4381fb91
<span class="k">${</span><span class="nv">__UUID</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="随机手机号码">随机手机号码</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 11 位随机号码</span>
139<span class="k">${</span><span class="nv">__RandomString</span><span class="p">(8,0123456789,)</span><span class="k">}</span>

<span class="c"># num1、num2 等分别为号码前缀变量</span>
<span class="k">${</span><span class="nv">__RandomFromMultipleVars</span><span class="p">(num1|num2|num3|num4|num5,)</span><span class="k">}${</span><span class="nv">__RandomString</span><span class="p">(8,0123456789,)</span><span class="k">}</span>

<span class="c"># 手机号截取最后两位</span>
<span class="k">${</span><span class="nv">__groovy</span><span class="p">(vars.get(</span><span class="s1">'mobile'</span><span class="p">)[-2..-1],)</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="计数器">计数器</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 计数器</span>
<span class="k">${</span><span class="nv">__counter</span><span class="p">(,)</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="加减法">加减法</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 整数相加得到变量 var，值为 3，相减用负号实现</span>
<span class="k">${</span><span class="nv">__intSum</span><span class="p">(2,1,var)</span><span class="k">}</span>
</code></pre></div></div>

<h5 id="参数拼接">参数拼接</h5>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 普通拼接：businessNumber_1、businessNumber_2 等</span>
businessNumber_<span class="k">${</span><span class="nv">num</span><span class="k">}</span>

<span class="c"># 变量拼接：${businessNumber_1}、${businessNumber_2} 等</span>
<span class="k">${</span><span class="nv">__V</span><span class="p">(businessNumber_</span><span class="k">${</span><span class="nv">num</span><span class="k">}</span><span class="p">)</span><span class="k">}</span>
</code></pre></div></div>

<h4 id="2groovy-方法">2、Groovy 方法</h4>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c"># 乘法，不支持小数</span>
<span class="k">${</span><span class="nv">__groovy</span><span class="p">(2*1000)</span><span class="k">}</span>
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="JMeter" /><summary type="html"><![CDATA[JMeter 常用函数]]></summary></entry><entry><title type="html">JMeter 常用 BeanShell 脚本</title><link href="https://blog-rs44.onrender.com/2022/10/17/jmeter-beanshell/" rel="alternate" type="text/html" title="JMeter 常用 BeanShell 脚本" /><published>2022-10-17T00:00:00+08:00</published><updated>2022-10-17T00:00:00+08:00</updated><id>https://blog-rs44.onrender.com/2022/10/17/jmeter-beanshell</id><content type="html" xml:base="https://blog-rs44.onrender.com/2022/10/17/jmeter-beanshell/"><![CDATA[<p>工作期间陆续记录的 JMeter 常用 beanshell 脚本。</p>

<h5 id="控制台打印日志">控制台打印日志</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="n">name</span><span class="o">);</span>
</code></pre></div></div>

<h5 id="生成-uuid">生成 UUID</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.util.UUID</span><span class="o">;</span>
<span class="no">UUID</span> <span class="n">uuid1</span> <span class="o">=</span> <span class="no">UUID</span><span class="o">.</span><span class="na">randomUUID</span><span class="o">();</span>
<span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"ID"</span><span class="o">,(</span><span class="n">uuid1</span><span class="o">.</span><span class="na">toString</span><span class="o">()).</span><span class="na">toUpperCase</span><span class="o">().</span><span class="na">replaceAll</span><span class="o">(</span><span class="s">"-"</span><span class="o">,</span><span class="s">""</span><span class="o">));</span>
</code></pre></div></div>

<h5 id="数据写入文件">数据写入文件</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 生成文件的路径</span>
<span class="nc">FileWriter</span> <span class="n">fs</span><span class="o">=</span><span class="k">new</span> <span class="nc">FileWriter</span><span class="o">(</span><span class="s">"C://Users//Desktop//info.csv"</span><span class="o">,</span><span class="kc">true</span><span class="o">);</span> 
<span class="nc">BufferedWriter</span> <span class="n">out</span> <span class="o">=</span><span class="k">new</span> <span class="nc">BufferedWriter</span><span class="o">(</span><span class="n">fs</span><span class="o">);</span>

<span class="c1">// 写入文件的参数</span>
<span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="s">"${telA}"</span> <span class="o">+</span> <span class="s">","</span> <span class="o">+</span> <span class="s">"${telX}"</span> <span class="o">+</span> <span class="s">","</span> <span class="o">+</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"seqId"</span><span class="o">));</span>
<span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="nc">System</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="s">"line.separator"</span><span class="o">));</span>

<span class="n">out</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
<span class="n">fs</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</code></pre></div></div>

<h5 id="计算文件行数">计算文件行数</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.io.BufferedReader</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.FileReader</span><span class="o">;</span>

<span class="c1">// 文件路径</span>
<span class="nc">String</span> <span class="n">filepath</span> <span class="o">=</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"filepath"</span><span class="o">);</span>
<span class="nc">BufferedReader</span> <span class="n">br</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedReader</span><span class="o">(</span><span class="k">new</span> <span class="nc">FileReader</span><span class="o">(</span><span class="n">filepath</span><span class="o">));</span>
<span class="nc">String</span> <span class="n">tmpStr</span> <span class="o">=</span> <span class="s">""</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">rowCount</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>

<span class="k">while</span> <span class="o">(</span><span class="n">tmeStr</span> <span class="o">=</span> <span class="n">br</span><span class="o">.</span><span class="na">readLine</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">rowCount</span><span class="o">++;</span>
<span class="o">}</span>
<span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"rowCount"</span><span class="o">,</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">rowCount</span><span class="o">));</span>
</code></pre></div></div>

<h5 id="根据日期计算天数">根据日期计算天数</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.text.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Date</span><span class="o">;</span>

<span class="c1">// 获取变量的值</span>
<span class="nc">String</span> <span class="n">start</span> <span class="o">=</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"start"</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">end</span> <span class="o">=</span>  <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"end"</span><span class="o">);</span>
<span class="nc">Date</span> <span class="n">startDate</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SimpleDateFormat</span><span class="o">(</span><span class="s">"yyyy-MM-dd"</span><span class="o">).</span><span class="na">parse</span><span class="o">(</span><span class="n">start</span><span class="o">);</span>
<span class="nc">Date</span> <span class="n">endDate</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SimpleDateFormat</span><span class="o">(</span><span class="s">"yyyy-MM-dd"</span><span class="o">).</span><span class="na">parse</span><span class="o">(</span><span class="n">end</span><span class="o">);</span>

<span class="c1">// 日期转成毫秒位时间戳</span>
<span class="kt">long</span> <span class="n">startTimeMillis</span> <span class="o">=</span> <span class="n">startDate</span><span class="o">.</span><span class="na">getTime</span><span class="o">();</span>
<span class="kt">long</span> <span class="n">endTimeMillis</span> <span class="o">=</span> <span class="n">endDate</span><span class="o">.</span><span class="na">getTime</span><span class="o">();</span>

<span class="c1">// 计算天数</span>
<span class="kt">long</span> <span class="n">timeDiffMillis</span> <span class="o">=</span> <span class="o">(</span><span class="n">endTimeMillis</span> <span class="o">-</span> <span class="n">startTimeMillis</span><span class="o">)</span> <span class="o">/</span> <span class="o">(</span><span class="mi">24</span><span class="o">*</span><span class="mi">60</span><span class="o">*</span><span class="mi">60</span><span class="o">*</span><span class="mi">1000</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
<span class="nc">String</span> <span class="n">daysBetween</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">timeDiffMillis</span><span class="o">);</span>

<span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"daysBetween"</span><span class="o">,</span> <span class="n">daysBetween</span><span class="o">);</span>
</code></pre></div></div>

<h5 id="更新下次请求的值">更新下次请求的值</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.time.LocalDate</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.time.format.DateTimeFormatter</span><span class="o">;</span>

<span class="c1">// 日期格式</span>
<span class="nc">DateTimeFormatter</span> <span class="n">formatter</span> <span class="o">=</span> <span class="nc">DateTimeFormatter</span><span class="o">.</span><span class="na">ofPattern</span><span class="o">(</span><span class="s">"yyyy-MM-dd"</span><span class="o">);</span>
<span class="c1">// 获取变量的值</span>
<span class="nc">LocalDate</span> <span class="n">startDate</span> <span class="o">=</span> <span class="nc">LocalDate</span><span class="o">.</span><span class="na">parse</span><span class="o">(</span><span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"start"</span><span class="o">),</span> <span class="n">formatter</span><span class="o">);</span>
<span class="nc">LocalDate</span> <span class="n">currentDate</span> <span class="o">=</span> <span class="n">startDate</span><span class="o">.</span><span class="na">plusDays</span><span class="o">(</span><span class="n">ctx</span><span class="o">.</span><span class="na">getThreadNum</span><span class="o">()</span> <span class="o">+</span> <span class="mi">1</span><span class="o">);</span>

<span class="c1">// 更新变量值</span>
<span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"start"</span><span class="o">,</span> <span class="n">currentDate</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="n">formatter</span><span class="o">));</span>
</code></pre></div></div>

<h5 id="文件下载">文件下载</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.io.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.text.SimpleDateFormat</span><span class="o">;</span>

<span class="c1">// 下载目录</span>
<span class="nc">File</span> <span class="n">dir_name</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="s">"recordfile"</span><span class="o">);</span>
<span class="n">dir_name</span><span class="o">.</span><span class="na">mkdir</span><span class="o">();</span>
<span class="c1">// 设置日期格式</span>
<span class="nc">SimpleDateFormat</span> <span class="n">nowtime</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">SimpleDateFormat</span><span class="o">(</span><span class="s">"yyyyMMddHHmmssSSS"</span><span class="o">);</span>
<span class="c1">// 获取到上个请求返回的数据</span>
<span class="kt">byte</span><span class="o">[]</span> <span class="n">result</span> <span class="o">=</span> <span class="n">prev</span><span class="o">.</span><span class="na">getResponseData</span><span class="o">();</span>
<span class="c1">// 以时间戳 + uuid 命名</span>
<span class="nc">String</span> <span class="n">file_name</span> <span class="o">=</span> <span class="n">dir_name</span> <span class="o">+</span> <span class="nc">File</span><span class="o">.</span><span class="na">separator</span> <span class="o">+</span> <span class="n">nowtime</span><span class="o">.</span><span class="na">format</span><span class="o">(</span><span class="k">new</span> <span class="nc">Date</span><span class="o">())</span> <span class="o">+</span> <span class="s">"-"</span> <span class="o">+</span> <span class="no">UUID</span><span class="o">.</span><span class="na">randomUUID</span><span class="o">()</span> <span class="o">+</span> <span class="s">"."</span> <span class="o">+</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"Format"</span><span class="o">);</span>
<span class="nc">File</span> <span class="n">file</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">File</span><span class="o">(</span><span class="n">file_name</span><span class="o">);</span>
<span class="nc">FileOutputStream</span> <span class="n">out</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">FileOutputStream</span><span class="o">(</span><span class="n">file</span><span class="o">);</span>
<span class="n">out</span><span class="o">.</span><span class="na">write</span><span class="o">(</span><span class="n">result</span><span class="o">);</span>
<span class="n">out</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</code></pre></div></div>

<h5 id="url-参数提取">URL 参数提取</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.io.BufferedReader</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.FileReader</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.io.IOException</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.Random</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.net.URL</span><span class="o">;</span>

<span class="c1">// 读取文件路径</span>
<span class="nc">String</span> <span class="n">filePath</span> <span class="o">=</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"filepath"</span><span class="o">);</span>
<span class="c1">// 打开文件</span>
<span class="nc">BufferedReader</span> <span class="n">reader</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">BufferedReader</span><span class="o">(</span><span class="k">new</span> <span class="nc">FileReader</span><span class="o">(</span><span class="n">filePath</span><span class="o">));</span>
<span class="nc">String</span> <span class="n">line</span><span class="o">;</span>
<span class="kt">int</span> <span class="n">currentLine</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
<span class="c1">// 生成随机行号</span>
<span class="nc">Random</span> <span class="n">rand</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Random</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">lineNumber</span> <span class="o">=</span> <span class="n">rand</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">200</span><span class="o">)</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>

<span class="c1">// 逐行读取文件内容</span>
<span class="k">while</span> <span class="o">((</span><span class="n">line</span> <span class="o">=</span> <span class="n">reader</span><span class="o">.</span><span class="na">readLine</span><span class="o">())</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">currentLine</span> <span class="o">==</span> <span class="n">lineNumber</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">// 处理URL</span>
        <span class="no">URL</span> <span class="n">url</span> <span class="o">=</span> <span class="k">new</span> <span class="no">URL</span><span class="o">(</span><span class="n">line</span><span class="o">);</span>
        <span class="c1">// 设为变量</span>
        <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"recordUrl"</span><span class="o">,</span> <span class="n">url</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
        <span class="nc">String</span> <span class="n">http</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">getProtocol</span><span class="o">();</span> <span class="c1">// 获取协议部分</span>
        <span class="nc">String</span> <span class="n">ip</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">getHost</span><span class="o">();</span> <span class="c1">// 获取主机 ip 部分</span>
        <span class="kt">int</span> <span class="n">port</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">getPort</span><span class="o">();</span>
        <span class="nc">String</span> <span class="n">voiceUrl</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">getFile</span><span class="o">();</span> <span class="c1">// 获取除了 HTTP、IP 和端口之外的剩余部分</span>
        <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"http"</span><span class="o">,</span> <span class="n">http</span><span class="o">);</span>
        <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"ip"</span><span class="o">,</span> <span class="n">ip</span><span class="o">);</span>
        <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"port"</span><span class="o">,</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">port</span><span class="o">));</span>
        <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"voiceUrl"</span><span class="o">,</span> <span class="n">voiceUrl</span><span class="o">);</span>
        <span class="nc">String</span> <span class="n">queryString</span> <span class="o">=</span> <span class="n">url</span><span class="o">.</span><span class="na">getQuery</span><span class="o">();</span> <span class="c1">// 获取查询字符串</span>
        <span class="nc">String</span><span class="o">[]</span><span class="n">params</span> <span class="o">=</span> <span class="n">queryString</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s">"&amp;"</span><span class="o">);</span> <span class="c1">// 根据 &amp; 符号拆分参数</span>
        <span class="k">for</span> <span class="o">(</span><span class="nc">String</span> <span class="nl">param:</span> <span class="n">params</span><span class="o">)</span> <span class="o">{</span>
            <span class="nc">String</span><span class="o">[]</span><span class="n">keyValue</span> <span class="o">=</span> <span class="n">param</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s">"="</span><span class="o">);</span> <span class="c1">// 根据"="符号拆分参数名和值</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">keyValue</span><span class="o">.</span><span class="na">length</span> <span class="o">==</span> <span class="mi">2</span> <span class="o">&amp;&amp;</span> <span class="n">keyValue</span><span class="o">[</span><span class="mi">0</span><span class="o">].</span><span class="na">equals</span><span class="o">(</span><span class="s">"id"</span><span class="o">))</span> <span class="o">{</span> <span class="c1">// 提取 id 的值</span>
                <span class="nc">String</span> <span class="n">value</span> <span class="o">=</span> <span class="n">keyValue</span><span class="o">[</span><span class="mi">1</span><span class="o">];</span>
                <span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"id"</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="k">break</span><span class="o">;</span>
    <span class="o">}</span>
    <span class="n">currentLine</span><span class="o">++;</span>
<span class="o">}</span>
<span class="c1">// 关闭文件</span>
<span class="n">reader</span><span class="o">.</span><span class="na">close</span><span class="o">();</span>
</code></pre></div></div>

<h5 id="请求体-md5-自动加密">请求体 md5 自动加密</h5>

<p>需手动添加 json 包到<code class="language-plaintext highlighter-rouge">lib\ext</code>目录下：<a href="https://repo1.maven.org/maven2/org/json/json/20240303/">https://repo1.maven.org/maven2/org/json/json/20240303/</a></p>

<p>请求 body 带有 sign 签名参数，sign 签名是根据请求 body 除去 sign 本身参数后，拼接请求参数最后 md5 加密生成的。</p>

<p>此处示例在 BeanShell 预处理程序先获取请求的 body，签名后给 sign 参数重新赋值，然后发送新的请求 body，因此 http 请求 body 参数中不需要写明 sign 参数，查看结果树可以看到请求 body 会带上 sign 值。</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.apache.jmeter.config.Arguments</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.jmeter.config.Argument</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.json.JSONObject</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">java.util.*</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.codec.digest.DigestUtils</span><span class="o">;</span>

<span class="nc">Arguments</span> <span class="n">arguments</span> <span class="o">=</span>  <span class="n">sampler</span><span class="o">.</span><span class="na">getArguments</span><span class="o">();</span>
<span class="nc">Argument</span> <span class="n">arg</span> <span class="o">=</span> <span class="n">arguments</span><span class="o">.</span><span class="na">getArgument</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
<span class="c1">// 获取请求 body 值</span>
<span class="nc">String</span> <span class="n">requestBody</span> <span class="o">=</span> <span class="n">arg</span><span class="o">.</span><span class="na">getValue</span><span class="o">();</span>
<span class="c1">// json 格式化</span>
<span class="nc">JSONObject</span> <span class="n">jsonObject</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">JSONObject</span><span class="o">(</span><span class="n">requestBody</span><span class="o">);</span>

<span class="c1">// 获取所有键并排除 sign（不使用泛型）</span>
<span class="nc">List</span> <span class="n">keys</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">();</span>
<span class="nc">Iterator</span> <span class="n">keyIterator</span> <span class="o">=</span> <span class="n">jsonObject</span><span class="o">.</span><span class="na">keys</span><span class="o">();</span>
<span class="k">while</span> <span class="o">(</span><span class="n">keyIterator</span><span class="o">.</span><span class="na">hasNext</span><span class="o">())</span> <span class="o">{</span>
   <span class="nc">String</span> <span class="n">key</span> <span class="o">=</span> <span class="n">keyIterator</span><span class="o">.</span><span class="na">next</span><span class="o">().</span><span class="na">toString</span><span class="o">();</span>
    <span class="c1">// 忽略大小写排除 sign</span>
   <span class="k">if</span> <span class="o">(!</span><span class="s">"sign"</span><span class="o">.</span><span class="na">equalsIgnoreCase</span><span class="o">(</span><span class="n">key</span><span class="o">))</span> <span class="o">{</span>
       <span class="n">keys</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">key</span><span class="o">);</span>
   <span class="o">}</span>
<span class="o">}</span>
<span class="nc">Collections</span><span class="o">.</span><span class="na">sort</span><span class="o">(</span><span class="n">keys</span><span class="o">);</span>

<span class="c1">// 拼接参数</span>
<span class="nc">StringBuilder</span> <span class="n">paramBuilder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">();</span>
<span class="k">for</span> <span class="o">(</span><span class="nc">Object</span> <span class="n">key</span> <span class="o">:</span> <span class="n">keys</span><span class="o">)</span> <span class="o">{</span>
   <span class="k">if</span> <span class="o">(</span><span class="n">paramBuilder</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
       <span class="n">paramBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="s">"&amp;"</span><span class="o">);</span>
   <span class="o">}</span>
   <span class="c1">// 显式转为 String</span>
   <span class="n">paramBuilder</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">toString</span><span class="o">()).</span><span class="na">append</span><span class="o">(</span><span class="s">"="</span><span class="o">).</span><span class="na">append</span><span class="o">(</span><span class="n">jsonObject</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">key</span><span class="o">.</span><span class="na">toString</span><span class="o">()));</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">paramString</span> <span class="o">=</span> <span class="n">paramBuilder</span><span class="o">.</span><span class="na">toString</span><span class="o">()</span> <span class="o">+</span> <span class="s">"&amp;Key="</span> <span class="o">+</span> <span class="n">vars</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="s">"Key"</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"签名串："</span> <span class="o">+</span> <span class="n">paramString</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">md5Result</span> <span class="o">=</span> <span class="nc">DigestUtils</span><span class="o">.</span><span class="na">md5Hex</span><span class="o">(</span><span class="n">paramString</span><span class="o">).</span><span class="na">toUpperCase</span><span class="o">();</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"加密后："</span> <span class="o">+</span> <span class="n">md5Result</span><span class="o">);</span>
<span class="c1">// body 添加 sign 参数</span>
<span class="n">jsonObject</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"sign"</span><span class="o">,</span> <span class="n">md5Result</span><span class="o">);</span>
<span class="c1">// JSONObject 转字符串</span>
<span class="nc">String</span> <span class="n">postData</span> <span class="o">=</span> <span class="n">jsonObject</span><span class="o">.</span><span class="na">toString</span><span class="o">();</span>
<span class="c1">// 重新赋值请求的 body 参数</span>
<span class="n">arg</span><span class="o">.</span><span class="na">setValue</span><span class="o">(</span><span class="n">postData</span><span class="o">);</span>
</code></pre></div></div>

<h5 id="请求体-md5-手动加密">请求体 md5 手动加密</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">org.apache.jmeter.config.Arguments</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.jmeter.protocol.http.control.HeaderManager</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.jmeter.protocol.http.control.Header</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.jmeter.testelement.property.CollectionProperty</span><span class="o">;</span>
<span class="kn">import</span> <span class="nn">org.apache.commons.codec.digest.DigestUtils</span><span class="o">;</span>

<span class="c1">// 获取请求头</span>
<span class="nc">HeaderManager</span> <span class="n">headers</span> <span class="o">=</span><span class="n">sampler</span><span class="o">.</span><span class="na">getHeaderManager</span><span class="o">();</span>

<span class="c1">// 打印全部的头部内容</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="n">headers</span><span class="o">.</span><span class="na">getHeaders</span><span class="o">().</span><span class="na">getStringValue</span><span class="o">());</span>

<span class="c1">// 手动排序拼接请求参数</span>
<span class="nc">String</span> <span class="n">str</span> <span class="o">=</span> <span class="s">"param="</span> <span class="o">+</span> <span class="n">param</span> <span class="o">+</span> <span class="s">"&amp;timestamp="</span> <span class="o">+</span> <span class="n">timestamp</span> <span class="o">+</span> <span class="s">"&amp;Key="</span> <span class="o">+</span> <span class="n">key</span><span class="o">;</span>

<span class="c1">// md5 加密</span>
<span class="nc">String</span> <span class="n">md5_after</span> <span class="o">=</span> <span class="nc">DigestUtils</span><span class="o">.</span><span class="na">md5Hex</span><span class="o">(</span><span class="n">str</span><span class="o">);</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"\n加密前："</span> <span class="o">+</span> <span class="n">str</span> <span class="o">+</span> <span class="s">"\n加密后："</span> <span class="o">+</span> <span class="n">md5_after</span><span class="o">);</span>

<span class="c1">// new 一个 Header 对象</span>
<span class="n">signHeader</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Header</span><span class="o">(</span><span class="s">"X-sign"</span><span class="o">,</span><span class="s">"aaaaaaaaaaaaaaaaaa"</span><span class="o">);</span>

<span class="c1">// 添加 Header 到请求头管理器</span>
<span class="n">headers</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">signHeader</span><span class="o">);</span>

<span class="c1">// 打印全部的头部内容</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="n">headers</span><span class="o">.</span><span class="na">getHeaders</span><span class="o">().</span><span class="na">getStringValue</span><span class="o">());</span>
</code></pre></div></div>

<h5 id="随机手机号码">随机手机号码</h5>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">import</span> <span class="nn">java.util.Random</span><span class="o">;</span>

<span class="nc">Random</span> <span class="n">random</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Random</span><span class="o">();</span>
<span class="kt">int</span> <span class="n">suffix</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">100000000</span><span class="o">);</span>
<span class="nc">String</span> <span class="n">suffixStr</span> <span class="o">=</span> <span class="nc">String</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">suffix</span><span class="o">);</span>
<span class="c1">// 手动补零，确保后缀是 8 位</span>
<span class="k">while</span> <span class="o">(</span><span class="n">suffixStr</span><span class="o">.</span><span class="na">length</span><span class="o">()</span> <span class="o">&lt;</span> <span class="mi">8</span><span class="o">)</span> <span class="o">{</span>
    <span class="n">suffixStr</span> <span class="o">=</span> <span class="s">"0"</span> <span class="o">+</span> <span class="n">suffixStr</span><span class="o">;</span>
<span class="o">}</span>
<span class="nc">String</span> <span class="n">phone</span> <span class="o">=</span> <span class="s">"185"</span> <span class="o">+</span> <span class="n">suffixStr</span><span class="o">;</span>
<span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="n">phone</span><span class="o">);</span>
</code></pre></div></div>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 生成随机手机号码（13/14/15/16/17/18/19 开头）</span>
<span class="kn">import</span> <span class="nn">java.util.Random</span><span class="o">;</span>

<span class="nc">Random</span> <span class="n">random</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Random</span><span class="o">();</span>
<span class="nc">String</span><span class="o">[]</span> <span class="n">prefixes</span> <span class="o">=</span> <span class="o">{</span><span class="s">"13"</span><span class="o">,</span> <span class="s">"14"</span><span class="o">,</span> <span class="s">"15"</span><span class="o">,</span> <span class="s">"16"</span><span class="o">,</span> <span class="s">"17"</span><span class="o">,</span> <span class="s">"18"</span><span class="o">,</span> <span class="s">"19"</span><span class="o">};</span>
<span class="nc">String</span> <span class="n">prefix</span> <span class="o">=</span> <span class="n">prefixes</span><span class="o">[</span><span class="n">random</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="n">prefixes</span><span class="o">.</span><span class="na">length</span><span class="o">)];</span>
<span class="nc">StringBuilder</span> <span class="n">phoneNumber</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">StringBuilder</span><span class="o">(</span><span class="n">prefix</span><span class="o">);</span>

<span class="c1">// 生成剩余 9 位数字</span>
<span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="mi">9</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
    <span class="n">phoneNumber</span><span class="o">.</span><span class="na">append</span><span class="o">(</span><span class="n">random</span><span class="o">.</span><span class="na">nextInt</span><span class="o">(</span><span class="mi">10</span><span class="o">));</span>
<span class="o">}</span>

<span class="n">vars</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="s">"mobilePhone"</span><span class="o">,</span> <span class="n">phoneNumber</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
</code></pre></div></div>]]></content><author><name>Nine</name></author><category term="JMeter" /><summary type="html"><![CDATA[JMeter 常用 BeanShell 脚本]]></summary></entry></feed>