<?xml version="1.0" encoding="GBK"?>
<rss version="2.0">
  <channel>
    <title>Perfree的个人博客 - 我的学习笔记</title>
    <link>http://172.18.0.3:8080</link>
    <description />
    <lastBuildDate>Sun, 05 Apr 2026 13:57:28 GMT</lastBuildDate>
    <item>
      <title>私有docker镜像仓库Harbor安装</title>
      <link>http://172.18.0.3:8080/article/73</link>
      <description>&lt;p&gt;Harbor （港口，港湾）是一个用于存储和分发Docker镜像的企业级Registry服务器。我们可以使用dockerhub仓库或者阿里云docker仓库等第三方仓库管理我们的docker镜像。对于企业来说，条件允许，可以搭建自己的docker镜像管理仓库。Harbor具有很多优势，能够提供分层传输机制，优化网络传输 Docker镜像是是分层的，而如果每次传输都使用全量文件(所以用FTP的方式并不适合)，显然不经济。必须提供识别分层传输的机制，以层的UUID为标识，确定传输的对象。提供WEB界面，优化用户体验只用镜像的名字来进行上传下载显然很不方便，需要有一个用户界面可以支持登陆、搜索功能，包括区分公有、私有镜像。支持水平扩展集群，当有用户对镜像的上传下载操作集中在某服务器，需要对相应的访问压力作分解。良好的安全机制 企业中的开发团队有很多不同的职位，对于不同的职位人员，分配不同的权限，具有更好的安全性。本文记录了harbor的详细安装过程(基于centos)&lt;/p&gt;
&lt;h2&gt;安装docker&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;更新yum
推荐先更新一下，当然也可以不更新或者更新过了的可以跳过此步骤&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# yum update
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="2"&gt;
&lt;li&gt;设置国内镜像源&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="3"&gt;
&lt;li&gt;安装docker所需软件包&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# sudo yum install -y yum-utils device-mapper-persistent-data lvm2
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="4"&gt;
&lt;li&gt;安装最新版的docker
此处安装的为最新版的docker,如需指定版本可自行查询&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# sudo yum install docker-ce docker-ce-cli containerd.io
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="5"&gt;
&lt;li&gt;启动docker并验证是否成功&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# sudo systemctl start docker
[root@center ~]# docker version
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="6"&gt;
&lt;li&gt;设置开机自启&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# sudo systemctl enable docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装docker-compose&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 安装epel源
[root@center ~]# yum install -y epel-release
# 安装docker-compose
[root@center ~]# yum install -y docker-compose
# 验证
[root@center ~]# docker-compose --version
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装Harbor&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;下载离线安装包
官方下载地址: &lt;a href="https://github.com/goharbor/harbor/releases" title="https://github.com/goharbor/harbor/releases"&gt;https://github.com/goharbor/harbor/releases&lt;/a&gt;,注意要下载的是offline离线版本，里面包含Harbor的所有文件&lt;/li&gt;
&lt;li&gt;将下载的离线包上传至服务器并解压&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# tar -zxvf harbor-offline-installer-v2.8.0.tgz -C /opt/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="3"&gt;
&lt;li&gt;生成证书
因为本人没有申请域名和购买SSL证书，则需要自己生成
此步骤需要在解压后的/opt/harbor目录下执行&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center ~]# cd /opt/harbor/
[root@center harbor]# openssl genrsa -out ca.key 4096
# 注意填写实际ip
[root@center harbor]# openssl req -x509 -new -nodes -sha512 -days 3650 \
  -subj &amp;quot;/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=192.168.145.103&amp;quot; \
  -key ca.key \
  -out ca.crt
# 注意填写实际ip
[root@center harbor]# openssl req -x509 -new -nodes -sha512 -days 3650 \
  -subj &amp;quot;/C=CN/ST=Beijing/L=Beijing/O=example/OU=Personal/CN=192.168.145.103&amp;quot; \
  -key ca.key \
  -out ca.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="4"&gt;
&lt;li&gt;移动证书位置&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center harbor]# mkdir /data/cert/
[root@center harbor]# mv ca* /data/cert/
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="5"&gt;
&lt;li&gt;修改harbor配置&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center harbor]# cp harbor.yml.tmpl harbor.yml
[root@center harbor]# vim harbor.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;主要修改以下内容
&lt;img src="/attach/20230502/b568b4ec2f79434cb50047467c31ea30.png" alt="" /&gt;&lt;/p&gt;
&lt;ol start="6"&gt;
&lt;li&gt;生成配置文件&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center harbor]# ./prepare
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="7"&gt;
&lt;li&gt;安装harbor&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center harbor]# ./install.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;等待安装完成,访问自己的ip或者域名
&lt;img src="/attach/20230502/e4e22a2dc29a4fa382a935c04a12573b.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;问题&lt;/h2&gt;
&lt;p&gt;如遇到x509问题可通过配置insecure-registries来解决,linux可将harbor服务器ip添加至/etc/docker/daemon.json文件insecure-registries中,如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
&amp;quot;exec-opts&amp;quot;: [&amp;quot;native.cgroupdriver=systemd&amp;quot;],
&amp;quot;registry-mirrors&amp;quot;: [&amp;quot;https://kn0t2bca.mirror.aliyuncs.com&amp;quot;],
 &amp;quot;insecure-registries&amp;quot;: [&amp;quot;192.168.145.103&amp;quot;]
}

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后重启docker&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@node1 ~]# sudo systemctl daemon-reload
[root@node1 ~]# sudo systemctl restart docker.service
# 验证登录
[root@node1 ~]# docker login 192.168.145.103
Username: admin
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;win直接在docker desktop中修改即可,参考下图
&lt;img src="/attach/20230502/137a752106c7471eb6be9bcc68667a19.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;其他命令&lt;/h2&gt;
&lt;p&gt;卸载harbor&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@center harbor]# sudo docker-compose down -v
[root@center harbor]# rm -r /data/database
[root@center harbor]# rm -r /data/registry
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;::: primary 标题
内容
:::&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/7">服务器</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>K8S学习-集群搭建</title>
      <link>http://172.18.0.3:8080/article/72</link>
      <description>&lt;p&gt;经过虚拟机配置,基础环境配置完毕后,就可以进入集群搭建的环节了&lt;/p&gt;
&lt;h2&gt;安装docker&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@master ~]# yum install wget
# 切换镜像源
[root@master ~]# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# 查看当前镜像源中支持的docker版本
[root@master ~]# yum list docker-ce --showduplicates
# 安装特定版本的docker-ce
# 必须制定--setopt=obsoletes=0，否则yum会自动安装更高版本
[root@master ~]# yum install --setopt=obsoletes=0 docker-ce-18.06.3.ce-3.el7 -y
# 添加一个配置文件
# Docker 在默认情况下使用Vgroup Driver为cgroupfs，而Kubernetes推荐使用systemd来替代cgroupfs,并切换到阿里仓库源
[root@master ~]# mkdir /etc/docker
[root@master ~]# cat &amp;lt;&amp;lt;EOF&amp;gt; /etc/docker/daemon.json
{
	&amp;quot;exec-opts&amp;quot;: [&amp;quot;native.cgroupdriver=systemd&amp;quot;],
	&amp;quot;registry-mirrors&amp;quot;: [&amp;quot;https://kn0t2bca.mirror.aliyuncs.com&amp;quot;]
}
EOF
# 启动dokcer
[root@master ~]# systemctl restart docker
# 查看下docker是否启动成功
[root@master ~]# docker version
Client:
 Version:           18.06.3-ce
 API version:       1.38
 Go version:        go1.10.3
 Git commit:        d7080c1
 Built:             Wed Feb 20 02:26:51 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.3-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.3
  Git commit:       d7080c1
  Built:            Wed Feb 20 02:28:17 2019
  OS/Arch:          linux/amd64
  Experimental:     false
# 设置开机自启
[root@master ~]# systemctl enable docker
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装Kubernetes组件&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 由于kubernetes的镜像在国外，速度比较慢，这里切换成国内的镜像源
# 编辑/etc/yum.repos.d/kubernetes.repo,添加下面的配置
[kubernetes]
name=Kubernetes
baseurl=http://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgchech=0
repo_gpgcheck=0
gpgkey=http://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
			http://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg

# 安装kubeadm、kubelet和kubectl
[root@master ~]# yum install --setopt=obsoletes=0 kubeadm-1.17.4-0 kubelet-1.17.4-0 kubectl-1.17.4-0 -y

# 配置kubelet的cgroup
#编辑/etc/sysconfig/kubelet, 添加下面的配置
KUBELET_CGROUP_ARGS=&amp;quot;--cgroup-driver=systemd&amp;quot;
KUBE_PROXY_MODE=&amp;quot;ipvs&amp;quot;

# 设置kubelet开机自启
[root@master ~]# systemctl enable kubelet
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;准备集群镜像&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 在安装kubernetes集群之前，必须要提前准备好集群需要的镜像，所需镜像可以通过下面命令查看
[root@master ~]# kubeadm config images list

# 下载镜像
# 此镜像kubernetes的仓库中，由于网络原因，无法连接，下面提供了一种替换方案
# 步骤1
images=(
	kube-apiserver:v1.17.17
	kube-controller-manager:v1.17.17
	kube-scheduler:v1.17.17
	kube-proxy:v1.17.17
	pause:3.1
	etcd:3.4.3-0
	coredns:1.6.5
)
# 步骤2
for imageName in ${images[@]};do
	docker pull registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName
	docker tag registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName k8s.gcr.io/$imageName
	docker rmi registry.cn-hangzhou.aliyuncs.com/google_containers/$imageName 
done
# 验证是否安装好
[root@master ~]# docker images
REPOSITORY                           TAG                 IMAGE ID            CREATED             SIZE
k8s.gcr.io/kube-proxy                v1.17.17            3ef67d180564        2 years ago         117MB
k8s.gcr.io/kube-controller-manager   v1.17.17            0ddd96ecb9e5        2 years ago         161MB
k8s.gcr.io/kube-scheduler            v1.17.17            d415ebbf09db        2 years ago         94.4MB
k8s.gcr.io/kube-apiserver            v1.17.17            38db32e0f351        2 years ago         171MB
k8s.gcr.io/coredns                   1.6.5               70f311871ae1        3 years ago         41.6MB
k8s.gcr.io/etcd                      3.4.3-0             303ce5db0e90        3 years ago         288MB
k8s.gcr.io/pause                     3.1                 da86e6ba6ca1        5 years ago         742kB
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;集群初始化&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;注意: 下面的操作只需要在master节点上执行即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 创建集群,注意apiserver-advertise-address为当前机器ip
[root@master ~]# kubeadm init \
	--apiserver-advertise-address=192.168.145.100 \
	--image-repository registry.aliyuncs.com/google_containers \
	--kubernetes-version=v1.17.17 \
	--service-cidr=10.96.0.0/12 \
	--pod-network-cidr=10.244.0.0/16
# 按照提示创建必要文件
[root@master ~]# mkdir -p $HOME/.kube
[root@master ~]# sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
[root@master ~]# sudo chown $(id -u):$(id -g) $HOME/.kube/config
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;上边的命令执行完毕后,会看到如下输出,注意记录关键信息&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 注意记录关键信息
Your Kubernetes control-plane has initialized successfully! # 安装成功

To start using your cluster, you need to run the following as a regular user:
  # 还需要做的事情
  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

# 集群安装完毕,但是网络是没有的,还需要执行下边的命令
You should now deploy a pod network to the cluster.
Run &amp;quot;kubectl apply -f [podnetwork].yaml&amp;quot; with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/
# 如果想在集群里加入node节点,需要在node节点以root身份执行下方的命令
Then you can join any number of worker nodes by running the following on each as root:

kubeadm join 192.168.145.100:6443 --token ez0nsy.mwcwhlp84ubowcr1 \
    --discovery-token-ca-cert-hash sha256:054584d999625b5a39f7ccf02aefa173f21926603f6ccd219fb37fa0245ea580
&lt;/code&gt;&lt;/pre&gt;
&lt;blockquote&gt;
&lt;p&gt;注意: 下面的操作只需要在node节点上执行即可,需要将上方输出的命令复制过来执行&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@node2 ~]# kubeadm join 192.168.145.100:6443 --token ez0nsy.mwcwhlp84ubowcr1 \
&amp;gt;     --discovery-token-ca-cert-hash sha256:054584d999625b5a39f7ccf02aefa173f21926603f6ccd219fb37fa0245ea580
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;查看所有节点,验证是否成功&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@master ~]# kubectl get nodes
NAME     STATUS     ROLES    AGE   VERSION
master   NotReady   master   10m   v1.17.4
node1    NotReady   &amp;lt;none&amp;gt;   69s   v1.17.4
node2    NotReady   &amp;lt;none&amp;gt;   4s    v1.17.4
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;安装网络插件&lt;/h2&gt;
&lt;p&gt;kubernetes支持多种网络插件,比如flannel,calico,canal等,任选一种即可,本次选择flannel&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;注意: 只在master节点操作即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;[root@master ~]# wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
[root@master ~]# kubectl apply -f kube-flannel.yml
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;过个一两分钟,查看节点状态&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[root@master ~]# kubectl get nodes
NAME     STATUS   ROLES    AGE   VERSION
master   Ready    master   21m   v1.17.4
node1    Ready    &amp;lt;none&amp;gt;   12m   v1.17.4
node2    Ready    &amp;lt;none&amp;gt;   11m   v1.17.4
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全是ready即可&lt;/p&gt;
&lt;h2&gt;服务部署(验证集群是否可用)&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;注意:只通过master节点操作&lt;/p&gt;
&lt;/blockquote&gt;
&lt;pre&gt;&lt;code&gt;# 部署nginx
[root@master ~]# kubectl create deployment nginx --image=nginx:1.14-alpine
# 暴露端口
[root@master ~]# kubectl expose deployment nginx --port=80 --type=NodePort
# 查看服务状态
[root@master ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
nginx-6867cdf567-pmj4d   1/1     Running   0          2m13s
[root@master ~]# kubectl get service
NAME         TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
kubernetes   ClusterIP   10.96.0.1      &amp;lt;none&amp;gt;        443/TCP        27m
nginx        NodePort    10.97.243.70   &amp;lt;none&amp;gt;        80:30322/TCP   83s

# 注意关注输出的PORT(S),nginx的80端口对应的是30322端口,也就是对外暴露的30322端口
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;浏览器访问验证http://192.168.145.100:30322/
&lt;img src="/attach/20230429/71e4e969270d43e19d5b9518aa566a4d.png" alt="" /&gt;
到这儿集群搭建成功~&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/7">服务器</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>K8S学习-环境搭建</title>
      <link>http://172.18.0.3:8080/article/71</link>
      <description>&lt;p&gt;在主机搭建时，选择了一主三从的配置，需要4台服务器，这里学习使用了虚拟机来完成，使用VMware安装4台centos虚拟机，本文详细记录了安装过程及初始环境的配置&lt;/p&gt;
&lt;h2&gt;安装虚拟机&lt;/h2&gt;
&lt;p&gt;虚拟机系统为centos7，镜像下载地址&lt;a href="https://mirrors.tuna.tsinghua.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2207-02.iso" title="https://mirrors.tuna.tsinghua.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2207-02.iso"&gt;https://mirrors.tuna.tsinghua.edu.cn/centos/7/isos/x86_64/CentOS-7-x86_64-DVD-2207-02.iso&lt;/a&gt;
首先，使用VMware创建新的虚拟机，直接下一步
&lt;img src="/attach/20230429/769e8f63816d49eda410203deae3a4fa.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;选择稍后安装系统
&lt;img src="/attach/20230429/5c800e64bb7043969385aa82fd4181d1.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;选择linux centos7
&lt;img src="/attach/20230429/8b9c23f1f01348a0a590b2c16446e547.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;命名虚拟机并选择位置，这里我已经安装过master的系统了，4台服务器系统的安装过程一样，直接拿这个参考就行
&lt;img src="/attach/20230429/299a8f900b2348c1a78f4ef1780c899f.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;分配磁盘大小50G
&lt;img src="/attach/20230429/38f1f4ad59e34ce59a7f9eba02e03e98.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;自定义硬件
&lt;img src="/attach/20230429/94af173f0abc46f1b7e0b190207014fa.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;内存修改到2GB
&lt;img src="/attach/20230429/1898bb1dab7f45409fa32e7d4c5cd62b.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;处理器分配两个核心
&lt;img src="/attach/20230429/31cf260f6913479e90d029d276efc245.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;完成
&lt;img src="/attach/20230429/12782ef3204b4ef8b4ca77eb201fd891.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;编辑虚拟机指定iso镜像文件
&lt;img src="/attach/20230429/7a4f0f9610304e8b8530f16ffd345402.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;进入虚拟机回车开始安装
&lt;img src="/attach/20230429/2fed5a144e5d463b8a00161dd58ec957.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;选择简体中文
&lt;img src="/attach/20230429/18a08c7a72724cf1b5bc9cab6005c909.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;软件选择基础设施服务器
&lt;img src="/attach/20230429/ad3825940c7b48edbe4e3b75d7421821.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;安装源选择本地介质
&lt;img src="/attach/20230429/87a06de479b045639dfabd94fbbd5e8d.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;安装位置里选择自动配置分区
&lt;img src="/attach/20230429/e1dc8db7dcdd40b59bf58172d25f1053.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;配置网络和主机名
&lt;img src="/attach/20230429/8358742cfdd44e929d27fa46010e8890.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/attach/20230429/c6bf1e5b009340dfb3bdb2ae0c91ac75.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/attach/20230429/b0d41d8aa5074a7289e7d72fbf4c50d0.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;修改主机名
&lt;img src="/attach/20230429/3bd886aa66284176a125f62096267bea.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;开始安装
&lt;img src="/attach/20230429/53f4f26c0bbd49829b405dc65130943c.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;设置root密码
&lt;img src="/attach/20230429/763eb4d5b9a74cb384084a289a63d730.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;等待安装完成重启进入系统即可，4台虚拟机都是相同的操作&lt;/p&gt;
&lt;h2&gt;环境初始化&lt;/h2&gt;
&lt;p&gt;这里推荐一个远程工具MobaXterm，可同时操作多台服务器，使用这个工具可以大量的节省我们的时间&lt;a href="https://mobaxterm.mobatek.net/download-home-edition.html" title="https://mobaxterm.mobatek.net/download-home-edition.html"&gt;https://mobaxterm.mobatek.net/download-home-edition.html&lt;/a&gt;
&lt;img src="/attach/20230429/663f0fc55553499a8c459fc99ad6bb60.png" alt="" /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;检查操作系统版本&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# kubernetes集群要求centos版本在7.5或以上
[root@master ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="2"&gt;
&lt;li&gt;主机名解析
为了方便后续集群节点间的直接调用,需要配置下主机名解析,企业中推荐使用内部dns服务器&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 编辑三台服务器/ets/hosts文件,增加以下内容
192.168.145.100 master
192.168.145.101 node1
192.168.145.102 node2
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="3"&gt;
&lt;li&gt;时间同步
kuberbetes要求集群中的节点时间必须精确一致,这里直接使用chronyd服务从网络同步时间,企业中建议配置内部的时间同步服务器&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 启动chronyd服务
[root@master ~]# systemctl start chronyd
# 设置chronyd服务开机自启
[root@master ~]# systemctl enable chronyd
#chronyd服务启动稍等几秒钟,就可以使用date命令验证时间了
[root@master ~]# date
2023年 04月 29日 星期六 11:30:27 CST
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以观察下多台服务时间是否一致
&lt;img src="/attach/20230429/22beba47c1074f0e84869ae4e713c340.png" alt="" /&gt;
4. 禁用iptables和firewalld服务
kubernets和docker在运行中会产生大量的iptables规则,为了不让系统规则跟它们混淆,直接关闭系统的规则,生产环境中慎重操作&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;#  关闭firewalld服务
[root@master ~]# systemctl stop firewalld
[root@master ~]# systemctl disable firewalld
# 关闭iptables服务
[root@master ~]# systemctl stop iptables
[root@master ~]# systemctl disable iptables
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="5"&gt;
&lt;li&gt;禁用selinux
selinux是linux系统下的一个安全服务,如果不关闭它,在安装集群中会产生各种各样的奇葩问题&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 编辑/etc/selinux/config文件,修改SELINUX的值为disabled
# 注意修改完毕之后需要重启linux服务
SELINUX=disbled
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="6"&gt;
&lt;li&gt;禁用swap分区
swap分区指的是虚拟内存分区,它的作用是在物理内存使用完之后,将磁盘空间虚拟城内存来使用,启用swap设备会对系统的性能产生非常负面的影响,因此kubernets要求每个节点都要禁用swap设备,但是如果因为某些原因确实不能关闭swap分区,就需要在集群安装过程中通过明确的参数进行配置说明&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;# 编辑分区配置文件/etc/fstab,注释调swap分区一行
# 注意修改完毕之后需要重启linux服务
/dev/mapper/centos-root /                       xfs     defaults        0 0
UUID=3f5b742e-2dd9-42cd-87fd-e7ff787eb1e8 /boot                   xfs     defaults        0 0
# /dev/mapper/centos-swap swap                    swap    defaults        0 0
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="7"&gt;
&lt;li&gt;修改linux的内核参数&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 修改linux的内核参数,添加网桥过滤和地址转发功能
# 添加并编辑/etc/sysctl.d/kubernetes.conf文件,添加如下配置:
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
# 重新加载配置
[root@node2 ~]# sysctl -p
# 加载网桥过滤模块
[root@node2 ~]# modprobe br_netfilter
# 查看网桥过滤模块是否加载成功
[root@node2 ~]# lsmod | grep br_netfilter
br_netfilter           22256  0
bridge                151336  1 br_netfilter
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="8"&gt;
&lt;li&gt;配置ipvs功能
在kubernetes中service有两种代理模型,一种基于iptables的,一种是基于ipvs的,两者比较的话,ipvs的性能明显要高一些,但是如果使用它,需要手动载入ipvs模块&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 安装ipset和ipvsadm
[root@master ~]# yum install ipset ipvsadmin -y
# 添加需要加载的模块写入脚本文件
[root@master ~]# cat &amp;lt;&amp;lt;EOF &amp;gt;  /etc/sysconfig/modules/ipvs.modules
#!/bin/bash
modprobe -- ip_vs
modprobe -- ip_vs_rr
modprobe -- ip_vs_wrr
modprobe -- ip_vs_sh
modprobe -- nf_conntrack_ipv4
EOF
# 为脚本文件添加执行权限
[root@master ~]# chmod +x /etc/sysconfig/modules/ipvs.modules
# 执行脚本文件
[root@master ~]# /bin/bash /etc/sysconfig/modules/ipvs.modules
# 查看对应的模块是否加载成功
[root@master ~]# lsmod | grep -e ip_vs -e nf_conntrack_ipv4
nf_conntrack_ipv4      15053  0
nf_defrag_ipv4         12729  1 nf_conntrack_ipv4
ip_vs_sh               12688  0
ip_vs_wrr              12697  0
ip_vs_rr               12600  0
ip_vs                 145458  6 ip_vs_rr,ip_vs_sh,ip_vs_wrr
nf_conntrack          139264  2 ip_vs,nf_conntrack_ipv4
libcrc32c              12644  3 xfs,ip_vs,nf_conntrack
&lt;/code&gt;&lt;/pre&gt;
&lt;ol start="9"&gt;
&lt;li&gt;重启系统
执行完上述操作后,需要重启linux刺痛&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[root@master ~]# reboot
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;启动完毕后,验证上述操作是否生效&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 验证selinux
[root@node1 ~]# getenforce
Disabled
# 验证swap分区
[root@node1 ~]# free -m
              total        used        free      shared  buff/cache   available
Mem:           1819         154        1484           9         180        1515
Swap:             0           0           0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此,虚拟机安装及环境初始化完毕&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/7">服务器</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Git同时提交到Github和码云Gitee上</title>
      <link>http://172.18.0.3:8080/article/63</link>
      <description>&lt;p&gt;在写PerfreeBlog时,因为源码在gitee和github都放有一份,所以提交代码需要两边同时提交,在这里记录下操作方法~
首先将github代码克隆至本地,然后进行以下操作:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote rename origin github
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;之后添加gitee代码地址&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git remote add gitee https://gitee.com/perfree/PerfreeBlog.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这样就可以啦,拉取代码可以使用以下命令分别拉取&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git pull github master
git pull gitee master
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;提交代码同理&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git push github master
git push gitee master
&lt;/code&gt;&lt;/pre&gt;</description>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>记一次Java项目OutOfMemoryError内存溢出排查过程</title>
      <link>http://172.18.0.3:8080/article/62</link>
      <description>&lt;p&gt;最近公司项目在短期内频繁的报&lt;code&gt;java.lang.OutOfMemoryError: GC overhead limit exceeded&lt;/code&gt;内存溢出,造成系统不可用的情况,经过两天的排查,终于找到了原因,以下为本次排查的全部过程,做个记录~&lt;/p&gt;
&lt;h2&gt;全过程&lt;/h2&gt;
&lt;p&gt;在刚开始排查的时候,观察项目内的日志文件,会出现一堆的&lt;code&gt;fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。&lt;/code&gt;然后&lt;code&gt;java.lang.OutOfMemoryError: GC overhead limit exceeded&lt;/code&gt;,造成系统宕机,初步怀疑是网络环境问题,但由于项目是局域网环境,就没往这上边想,先是使用jmap查看了下堆内存中的对象:
&lt;img src="/static/attach/20220217/0dbfdf3f81c048c6a071f5461b98c17b.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;发现byte [B占用内存过高,占用达到了百分之九十之多,进行分析过后,发现byte里边大多都是和HTTP请求相关的参数,中间没有截图,这里就不发截图了,大致就是基本全部都是HTTP相关的buffer,就是说Springboot tomcat为每个请求分发大概了10M以上的内存,导致内存过高,经过检查代码,发现配置文件有如下配置&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;max-http-header-size: 40485763
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;至此，基本已经确定了大概就是这个不合理的最大http请求头参数导致的内存过多的问题。将该配置进行删除,使用默认的配置,并限制tomcat最大连接数及线程数,然后重新启动并使用VisualVM进行程序观察,发现byte的占用已经从百分之九十多将到了百分之十几
&lt;img src="/static/attach/20220217/687da31ca1a54ed2a296a9167902a453.png" alt="" /&gt;
但是问题来了,虽然程序不会出现内存溢出的情况了,但是那个报错依然还在,这个时候通过监控程序发现了以下的异常点
&lt;img src="/static/attach/20220217/c8013e5c31aa44258584f5c22367f295.png" alt="" /&gt;
发现程序在一瞬间线程数飙升,达到限制值,而在这些飙升的点对应日志文件也会出现大量的报错信息,如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2022-02-15 10:36:49.123 ERROR 13692 --- [http-nio-8070-exec-171] c.t.w.controller.ExceptionController     : Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。

org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:122) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:113) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.&amp;lt;init&amp;gt;(StandardMultipartHttpServletRequest.java:86) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:93) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1128) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:960) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at com.tongyu.workstation.filter.SystemFilter.doFilter(SystemFilter.java:21) [classes!/:0.1.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) [druid-1.1.9.jar!/:1.1.9]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_211]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_211]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.lang.Thread.run(Unknown Source) [na:1.8.0_211]
Caused by: java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.apache.catalina.connector.Request.parseParts(Request.java:2932) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.parseParameters(Request.java:3232) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.getParameter(Request.java:1137) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:75) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	... 23 common frames omitted
Caused by: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:308) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.parseParts(Request.java:2884) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	... 27 common frames omitted
Caused by: java.io.IOException: 远程主机强迫关闭了一个现有的连接。
	at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[na:1.8.0_211]
	at sun.nio.ch.SocketDispatcher.read(Unknown Source) ~[na:1.8.0_211]
	at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source) ~[na:1.8.0_211]
	at sun.nio.ch.IOUtil.read(Unknown Source) ~[na:1.8.0_211]
	at sun.nio.ch.SocketChannelImpl.read(Unknown Source) ~[na:1.8.0_211]
	at org.apache.tomcat.util.net.NioChannel.read(NioChannel.java:147) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioBlockingSelector.read(NioBlockingSelector.java:173) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:235) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:216) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1250) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1193) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:725) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.access$300(Http11InputBuffer.java:40) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer$SocketInputBuffer.doRead(Http11InputBuffer.java:1080) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:140) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.doRead(Http11InputBuffer.java:263) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.Request.doRead(Request.java:581) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:326) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.checkByteBufferEof(InputBuffer.java:642) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:349) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:183) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:977) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:881) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.io.InputStream.read(Unknown Source) ~[na:1.8.0_211]
	at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:98) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:68) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.readBodyData(MultipartStream.java:572) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.discardBodyData(MultipartStream.java:596) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.skipPreamble(MultipartStream.java:614) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.findNextItem(FileUploadBase.java:865) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.&amp;lt;init&amp;gt;(FileUploadBase.java:845) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	... 28 common frames omitted

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这个时候忽然想到不会真的是因为网络问题吧?在短时间内多个客户端正在上传文件时网络忽然波动断开,然后重连,造成这个错误,为了验证这个猜想,我们在测试环境进行测试,在使用大量的文件上传过程中拔掉网线,然后再插上,观察服务器日志输出:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;2022-02-17 13:49:06.159 ERROR 31024 --- [http-nio-7087-exec-4] c.t.w.controller.ExceptionController     : Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。

org.springframework.web.multipart.MultipartException: Failed to parse multipart servlet request; nested exception is java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.handleParseFailure(StandardMultipartHttpServletRequest.java:122) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.parseRequest(StandardMultipartHttpServletRequest.java:113) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardMultipartHttpServletRequest.&amp;lt;init&amp;gt;(StandardMultipartHttpServletRequest.java:86) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.multipart.support.StandardServletMultipartResolver.resolveMultipart(StandardServletMultipartResolver.java:93) ~[spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.checkMultipart(DispatcherServlet.java:1128) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:960) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925) ~[spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974) [spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:877) [spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:661) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851) [spring-webmvc-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:742) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52) [tomcat-embed-websocket-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.shiro.web.servlet.AbstractShiroFilter.executeChain(AbstractShiroFilter.java:449) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.AbstractShiroFilter$1.call(AbstractShiroFilter.java:365) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.SubjectCallable.doCall(SubjectCallable.java:90) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.SubjectCallable.call(SubjectCallable.java:83) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.subject.support.DelegatingSubject.execute(DelegatingSubject.java:387) [shiro-core-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.AbstractShiroFilter.doFilterInternal(AbstractShiroFilter.java:362) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.shiro.web.servlet.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:125) [shiro-web-1.4.0.jar!/:1.4.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at com.tongyu.workstation.filter.SystemFilter.doFilter(SystemFilter.java:21) [classes!/:0.1.0]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at com.alibaba.druid.support.http.WebStatFilter.doFilter(WebStatFilter.java:123) [druid-1.1.9.jar!/:1.1.9]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:496) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:803) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:790) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1468) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source) [na:1.8.0_261]
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source) [na:1.8.0_261]
	at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61) [tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.lang.Thread.run(Unknown Source) [na:1.8.0_261]
Caused by: java.io.IOException: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.apache.catalina.connector.Request.parseParts(Request.java:2932) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.parseParameters(Request.java:3232) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.getParameter(Request.java:1137) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.RequestFacade.getParameter(RequestFacade.java:381) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:75) [spring-web-5.0.6.RELEASE.jar!/:5.0.6.RELEASE]
	... 23 common frames omitted
Caused by: org.apache.tomcat.util.http.fileupload.FileUploadException: 远程主机强迫关闭了一个现有的连接。
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:308) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.Request.parseParts(Request.java:2884) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	... 27 common frames omitted
Caused by: java.io.IOException: 远程主机强迫关闭了一个现有的连接。
	at sun.nio.ch.SocketDispatcher.read0(Native Method) ~[na:1.8.0_261]
	at sun.nio.ch.SocketDispatcher.read(Unknown Source) ~[na:1.8.0_261]
	at sun.nio.ch.IOUtil.readIntoNativeBuffer(Unknown Source) ~[na:1.8.0_261]
	at sun.nio.ch.IOUtil.read(Unknown Source) ~[na:1.8.0_261]
	at sun.nio.ch.SocketChannelImpl.read(Unknown Source) ~[na:1.8.0_261]
	at org.apache.tomcat.util.net.NioChannel.read(NioChannel.java:147) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioBlockingSelector.read(NioBlockingSelector.java:173) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:235) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioSelectorPool.read(NioSelectorPool.java:216) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.fillReadBuffer(NioEndpoint.java:1250) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.net.NioEndpoint$NioSocketWrapper.read(NioEndpoint.java:1193) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.fill(Http11InputBuffer.java:725) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.access$300(Http11InputBuffer.java:40) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer$SocketInputBuffer.doRead(Http11InputBuffer.java:1080) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.filters.IdentityInputFilter.doRead(IdentityInputFilter.java:140) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.http11.Http11InputBuffer.doRead(Http11InputBuffer.java:263) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.coyote.Request.doRead(Request.java:581) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.realReadBytes(InputBuffer.java:326) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.checkByteBufferEof(InputBuffer.java:642) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.InputBuffer.read(InputBuffer.java:349) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.catalina.connector.CoyoteInputStream.read(CoyoteInputStream.java:183) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.makeAvailable(MultipartStream.java:977) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream$ItemInputStream.read(MultipartStream.java:881) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at java.io.InputStream.read(Unknown Source) ~[na:1.8.0_261]
	at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:98) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.util.Streams.copy(Streams.java:68) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.readBodyData(MultipartStream.java:572) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.discardBodyData(MultipartStream.java:596) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.MultipartStream.skipPreamble(MultipartStream.java:614) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.findNextItem(FileUploadBase.java:865) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase$FileItemIteratorImpl.&amp;lt;init&amp;gt;(FileUploadBase.java:845) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.getItemIterator(FileUploadBase.java:256) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	at org.apache.tomcat.util.http.fileupload.FileUploadBase.parseRequest(FileUploadBase.java:280) ~[tomcat-embed-core-8.5.31.jar!/:8.5.31]
	... 28 common frames omitted

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;果然.......就是因为网络问题造成该报错,在报错时线程并未关掉,而当时我们的服务器tomcat并未限制最大连接数及线程数,在短时间内恢复网络,程序连接造成线程堆积,内存飙升=_=&lt;/p&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;p&gt;在程序部署上线时应合理考虑服务器配置并配置限制连接数及线程数,其中&lt;code&gt;max-http-header-size&lt;/code&gt;配置要合理,无特殊情况不需要特殊配置,当然,同时要确保网络环境的畅通=_=&lt;/p&gt;</description>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>MinGw和Tdm gcc安装包</title>
      <link>http://172.18.0.3:8080/article/58</link>
      <description>&lt;p&gt;MinGw和Tdm gcc安装包,备份下,省的下次找==&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;MinGw gcc 安装包 &lt;a href="https://www.yinpengfei.com/attach/20210925/bc383390139d4590b64a469b3323102f.exe" title="MinGw gcc"&gt;MinGw gcc&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Tdm gcc 安装包 &lt;a href="https://www.yinpengfei.com/attach/20210925/a5b184a6a2254e509e8cd5512a765d30.3.0-2.exe" title="Tdm gcc"&gt;Tdm gcc&lt;/a&gt;&lt;/li&gt;
&lt;/ol&gt;</description>
      <category domain="http://172.18.0.3:8080/category/5">杂七杂八</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>PerfreeBlog主题之Fly食用文档</title>
      <link>http://172.18.0.3:8080/article/44</link>
      <description>&lt;p&gt;&#x1f34a;本文主要展示 fly主题中的安装方法及各种配置，方便大家写作的时候进行参考，部分样式可能会在迭代过程中有所变更，请及时关注最新信息。&#x1f34a;&lt;/p&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;p&gt;主题安装前请先安装perfree-fly.jar插件,该插件为主题提供了系列接口,之后再进行主题fly.zip的安装,这里注意下载的主题压缩文件名可能不是fly.zip,需要手动改一下~,安装完成后直接访问即可&lt;/p&gt;
&lt;h2&gt;分类页面&lt;/h2&gt;
&lt;p&gt;主题支持分类页面&#x1f4d1;,您可在后台菜单管理中添加/categories菜单,添加完后直接访问即可&lt;br /&gt;
&lt;img src="/attach/20221008/68233a8af05245c6993f1700a99bea06.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;个人项目页&lt;/h2&gt;
&lt;p&gt;主题支持个人项目页&#x1f4d1;,用于展示个人的github仓库项目,首先在页面管理中新建个人项目页面,在新建页面时将slug设置为project或者页面模板选择project.html,之后在菜单管理新增该页面的url菜单即可,如/page/project&lt;br /&gt;
&lt;img src="/attach/20221008/7973a223535b452cb7423ea4154d6e33.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;留言板&lt;/h2&gt;
&lt;p&gt;主题支持留言板页&#x1f4d1;,可在页面管理新建留言板页面,在新建页面时将slug设置为message或者页面模板选择message.html,之后在菜单管理新增该页面的url菜单即可,如/page/message&lt;br /&gt;
&lt;img src="/attach/20221008/70e5dc0030c8428f8c8030b3e42c4f49.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;相册页&lt;/h2&gt;
&lt;p&gt;主题内置相册页&#x1f4d1;,在使用前您需要先安装相册插件,之后在菜单管理配置/photos菜单即可&lt;br /&gt;
&lt;img src="/attach/20221008/4834884fa35a4ee9bb9f3e5db77f5bbb.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;自定义配置&lt;/h2&gt;
&lt;p&gt;主题支持N多项自定义配置,您可以自由体验下&#x1f4a6;&lt;br /&gt;
&lt;img src="/attach/20221008/e7a4e2688efc4de4809534b8d8265451.png" alt="" /&gt;&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/4">我的项目</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>PerfreeBlog-博客建站平台</title>
      <link>http://172.18.0.3:8080/article/54</link>
      <description>&lt;p&gt;PerfreeBlog是一款基于java开发的博客建站平台,后端采用&lt;a href="https://spring.io/projects/spring-boot"&gt;SpringBoot&lt;/a&gt;、&lt;a href="https://shiro.apache.org"&gt;Shiro&lt;/a&gt;、&lt;a href="https://mybatis.org/"&gt;Mybatis&lt;/a&gt;、&lt;a href="https://jfinal.com/doc/6-1"&gt;Enjoy&lt;/a&gt;模板引擎等技术进行开发,给您带来全新的创作体验&#x1f496;&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;目前PerfreeBlog的所有功能均为个人开发,因能力有限,其中很多技术都是边学习边使用的,所以有些地方可能有不足之处,欢迎各位共同交流,同时如若本项目对您有所帮助,请为它&lt;a href="https://github.com/perfree/PerfreeBlog"&gt;点赞&lt;/a&gt;支持&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&#x1f30d;PerfreeBlog交流QQ群: 938402724&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;站点&lt;/h2&gt;
&lt;p&gt;&#x1f4d6;文档: &lt;a href="http://perfree.org.cn/"&gt;http://perfree.org.cn/&lt;/a&gt;&lt;br&gt;
&#x1f4d6;github: &lt;a href="https://github.com/perfree/PerfreeBlog"&gt;https://github.com/perfree/PerfreeBlog&lt;/a&gt;&lt;br&gt;
&#x1f4d6;演示站点: &lt;a href="https://www.yinpengfei.com/"&gt;https://www.yinpengfei.com/&lt;/a&gt;&lt;br&gt;&lt;/p&gt;
&lt;h2&gt;特性&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;设计简洁，界面美观&lt;/li&gt;
&lt;li&gt;采用&lt;a href="https://www.markdownguide.org/"&gt;Markdown&lt;/a&gt;编辑器,支持一键插入视频、图片&lt;/li&gt;
&lt;li&gt;支持多主题自由切换&lt;/li&gt;
&lt;li&gt;主题在线编辑&lt;/li&gt;
&lt;li&gt;支持扩展插件&lt;/li&gt;
&lt;li&gt;友情链接管理&lt;/li&gt;
&lt;li&gt;支持附件管理&lt;/li&gt;
&lt;li&gt;主题开发简单快速&lt;/li&gt;
&lt;li&gt;安装部署简单&lt;/li&gt;
&lt;li&gt;支持&lt;a href="https://www.mysql.com"&gt;mysql&lt;/a&gt;/&lt;a href="https://www.sqlite.org"&gt;sqlite&lt;/a&gt;数据库&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;截图&lt;/h2&gt;
&lt;p&gt;写文章
&lt;img src="/static/attach/20210901/c0becc75efae4e2fa59ff53588ba828f.jpg" alt="" /&gt;
文章列表
&lt;img src="/static/attach/20210901/bf2c8cdb8d5d4100be12c2a98331be06.jpg" alt="" /&gt;
主题
&lt;img src="/static/attach/20210901/d07f5f9b068b4b59b93b8fc71b71a244.jpg" alt="" /&gt;&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/4">我的项目</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>IONIC入门到打包</title>
      <link>http://172.18.0.3:8080/article/53</link>
      <description>&lt;p&gt;Ionic 是一个流行的移动应用程序框架，它使用 HTML，CSS 和 JavaScript，帮助你快速创建混合移动应用程序,本文简单记录了ionic环境的配置入门及打包的流程&lt;/p&gt;
&lt;h2&gt;安装JDK&lt;/h2&gt;
&lt;p&gt;安装jdk及配置环境变量,此处不再赘述&lt;/p&gt;
&lt;h2&gt;安装Android Studio&lt;/h2&gt;
&lt;p&gt;下载Android Studio并安装,安装完成后打开会自动下载sdk&lt;/p&gt;
&lt;h2&gt;安装Gradle&lt;/h2&gt;
&lt;p&gt;下载Gradle安装包,并解压,配置环境变量Path指向Gradle的bin目录&lt;/p&gt;
&lt;h2&gt;安装cli&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;npm install -g cordova ionic # 安装cli
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;创建项目&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;ionic start ionic001 tabs # 创建项目
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;启动项目&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;ionic serve # 启动项目
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;访问8100端口看到页面即可&lt;/p&gt;
&lt;h2&gt;打包安卓apk&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;ionic cordova platform add android # 添加安卓平台
ionic cordova build android
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;打包好的apk在项目platforms\android\app\build\outputs\apk\debug中&lt;/p&gt;
&lt;h2&gt;编译发行版&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;ionic cordova build android --release
keytool -genkey -alias android.keystore -keyalg RSA -validity 20000 -keystore android.keystore  # 生成签名,会在目录生成my-release-key.keystore文件
jarsigner -verbose -sigalg SHA1withRSA -digestalg SHA1 -keystore android.keystore app-release-unsigned.apk android.keystore # 签名apk
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/3">前端</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>c#-dicom文件与jpg互相转换</title>
      <link>http://172.18.0.3:8080/article/52</link>
      <description>&lt;p&gt;最近在搞DICOM和JPG文件的互相转换，查了很多资料也没得到想要的，最终也算是摸索着使用C#完成了dicom和jpg文件互相转换，在这里简单的将代码记录一下(c#控制台程序),依赖fo-dicom&lt;/p&gt;
&lt;h2&gt;Dcm2JpgOptions&lt;/h2&gt;
&lt;p&gt;用来接收存放控制台传入的命令参数&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DcmUtil
{
    class Dcm2JpgOptions
    {

        /// &amp;lt;summary&amp;gt;
        /// dcm路径
        /// &amp;lt;/summary&amp;gt;
        public string dcmPath { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// jpg图片输出目录
        /// &amp;lt;/summary&amp;gt;
        public string outJpgDir { get; set; }
    }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Img2DcmOptions&lt;/h2&gt;
&lt;p&gt;用来接收存放控制台传入的命令参数&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DcmUtil
{
    class Img2DcmOptions
    {
        /// &amp;lt;summary&amp;gt;
        /// 患者名
        /// &amp;lt;/summary&amp;gt;
        public string patientName { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// 患者ID
        /// &amp;lt;/summary&amp;gt;
        public string patientID { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// dcm输出文件路径及名称
        /// &amp;lt;/summary&amp;gt;
        public string outDcm { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// 图片集合
        /// &amp;lt;/summary&amp;gt;
        public List&amp;lt;String&amp;gt; images { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;DcmScuOptions&lt;/h2&gt;
&lt;p&gt;用来接收存放控制台传入的命令参数&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DcmUtil
{
    class DcmScuOptions
    {

        /// &amp;lt;summary&amp;gt;
        /// ip地址
        /// &amp;lt;/summary&amp;gt;
        public string ip { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// 端口
        /// &amp;lt;/summary&amp;gt;
        public int port { get; set; }
        /// &amp;lt;summary&amp;gt;
        /// dcm路径
        /// &amp;lt;/summary&amp;gt;
        public string dcmPath { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;程序入口&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-c#"&gt;using Dicom;
using Dicom.Imaging;
using Dicom.IO.Buffer;
using Dicom.Network.Client;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DcmUtil
{
    class Program
    {
        [Obsolete]
        static async Task Main(string[] args)
        {
            // 如果未传参数，则直接返回
            if (args.Length &amp;lt;= 1) {
                Console.WriteLine(&amp;quot;请传入参数，如：&amp;quot;);
                Console.WriteLine(&amp;quot;img2dcm \&amp;quot;{patientName:'xxx',patientID:'123',images:['1.jpg','2.jpg'],outDcm:'1.dcm'}\&amp;quot;&amp;quot;);
                Console.WriteLine(&amp;quot;dcm2jpg \&amp;quot;{'dcmPath':'E:/test/images/3.dcm','outJpgDir':'E:/test/images/outJpg'}\&amp;quot;&amp;quot;);
                Console.WriteLine(&amp;quot;dcmScu \&amp;quot;{'ip':'127.0.0.1','port':4242,'dcmPath':'E:/test/images/3.dcm'}\&amp;quot;&amp;quot;);
                Console.ReadKey();
                return;
            }
            Console.WriteLine(args[1]);
            // 解析命令行参数


            // 图片转dcm
            if (args[0].Equals(&amp;quot;img2dcm&amp;quot;))
            { 
                Img2DcmOptions options = JsonConvert.DeserializeObject&amp;lt;Img2DcmOptions&amp;gt;(args[1]);
                Image2dcm(options);
            }

            // dcm转图片
            if (args[0].Equals(&amp;quot;dcm2jpg&amp;quot;))
            { 
                Dcm2JpgOptions options = JsonConvert.DeserializeObject&amp;lt;Dcm2JpgOptions&amp;gt;(args[1]);
                Dcm2Jpg(options);
            }

            // dcm转图片
            if (args[0].Equals(&amp;quot;dcmScu&amp;quot;))
            {
                DcmScuOptions options = JsonConvert.DeserializeObject&amp;lt;DcmScuOptions&amp;gt;(args[1]);
                await DcmScu(options);
            }
        }

        /// &amp;lt;summary&amp;gt;
        /// 获取图片字节数组
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;bitmap&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
        public static byte[] GetPixels(Bitmap bitmap){
            // 将jpg转换为bmp，如直接是bmp，则不需要进行该步骤
            BitmapData data = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadOnly, PixelFormat.Format16bppArgb1555);
            bitmap = new Bitmap(bitmap.Width, bitmap.Height, data.Stride, PixelFormat.Format16bppArgb1555, data.Scan0);


            byte[] bytes = new byte[bitmap.Width * bitmap.Height * 3];
            int wide = bitmap.Width;
            int i = 0;
            int height = bitmap.Height;
            for (int y = 0; y &amp;lt; height; y++)
            {
                for (int x = 0; x &amp;lt; wide; x++)
                {
                    var srcColor = bitmap.GetPixel(x, y);
                    bytes[i] = srcColor.R;
                    i++;
                    bytes[i] = srcColor.G;
                    i++;
                    bytes[i] = srcColor.B;
                    i++;
                }
            }
            return bytes;
        }

        /// &amp;lt;summary&amp;gt;
        /// 获取时间戳
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
        public static string GetTimeStamp()
        {
            TimeSpan ts = DateTime.Now - new DateTime(1970, 1, 1, 0, 0, 0, 0);
            return Convert.ToInt64(ts.TotalSeconds).ToString();
        }

        /// &amp;lt;summary&amp;gt;
        /// 图片转换dcm
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;file&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
        public static void Image2dcm(Img2DcmOptions options)
        {
            if (options.images.Count &amp;lt;= 0) {
                Console.WriteLine(&amp;quot;请传入图片路径&amp;quot;);
                return;
            }
            // 取第一张图片作为基准图片
            Bitmap bitmap = new Bitmap(@options.images[0]);
            byte[] pixels = GetPixels(bitmap);
            MemoryByteBuffer buffer = new MemoryByteBuffer(pixels);
            DicomDataset dataset = new DicomDataset();
            dataset.Add(DicomTag.SpecificCharacterSet, &amp;quot;GB18030&amp;quot;);
            // 写入tag数据
            dataset.Add(DicomTag.PhotometricInterpretation, PhotometricInterpretation.Rgb.Value);
            dataset.Add(DicomTag.Rows, (ushort)bitmap.Height);
            dataset.Add(DicomTag.Columns, (ushort)bitmap.Width);
            dataset.Add(DicomTag.BitsAllocated, (ushort)8);
            dataset.Add(DicomTag.SOPClassUID, &amp;quot;1.2.840.10008.5.1.4.1.1.2&amp;quot;);
            dataset.Add(DicomTag.SOPInstanceUID, &amp;quot;1.2.840.10008.5.1.4.1.1.2.&amp;quot; + GetTimeStamp());
            dataset.Add(DicomTag.PatientName, Encoding.Default, string.IsNullOrEmpty(options.patientName) ? &amp;quot;test&amp;quot; : options.patientName);
            dataset.Add(DicomTag.PatientID, string.IsNullOrEmpty(options.patientID) ? Guid.NewGuid().ToString(&amp;quot;N&amp;quot;) : options.patientID);
            dataset.Add(DicomTag.StudyInstanceUID, &amp;quot;1.2.3.4.5.6.7.8.9.11.&amp;quot; + GetTimeStamp());
            dataset.Add(DicomTag.StudyDate, DateTime.Now.ToString(&amp;quot;yyyyMMdd&amp;quot;));
            dataset.Add(DicomTag.StudyTime, DateTime.Now.ToString(&amp;quot;HHmmss&amp;quot;));
            dataset.Add(DicomTag.StudyID, GetTimeStamp());
            dataset.Add(DicomTag.Modality, &amp;quot;CT&amp;quot;);
            dataset.Add(DicomTag.SeriesInstanceUID, &amp;quot;1.2.3.4.5.6.7.8.9.11.&amp;quot; + GetTimeStamp());
            dataset.Add(DicomTag.InstanceNumber, &amp;quot;1000&amp;quot;);

            DicomPixelData pixelData = DicomPixelData.Create(dataset, true);
            pixelData.BitsStored = 8;
            pixelData.SamplesPerPixel = 3;
            pixelData.HighBit = 7;
            pixelData.PixelRepresentation = 0;
            pixelData.PlanarConfiguration = 0;
            pixelData.AddFrame(buffer);

            // 如果图片大于等于两张，则继续追加
            if (options.images.Count &amp;gt;= 2) {
                for (var i = 1; i &amp;lt; options.images.Count; i++) {
                    Bitmap addBit = new Bitmap(@options.images[i]);
                    byte[] addPixels = GetPixels(addBit);
                    MemoryByteBuffer addBuffer = new MemoryByteBuffer(addPixels);
                    pixelData.AddFrame(addBuffer);
                }
            }
           
            // 保存dcm文件
            DicomFile dicomfile = new DicomFile(dataset);
            dicomfile.Save(@options.outDcm);
            Console.WriteLine(&amp;quot;success：jpg转dcm成功&amp;quot;);
        }

        /// &amp;lt;summary&amp;gt;
        /// dcm转图片
        /// &amp;lt;/summary&amp;gt;
        [Obsolete]
        public static void Dcm2Jpg(Dcm2JpgOptions options) {
            var file = DicomFile.Open(@options.dcmPath);
            var image = new DicomImage(file.Dataset);
            var patientid = file.Dataset.Get&amp;lt;string&amp;gt;(DicomTag.PatientID);
            if (string.IsNullOrEmpty(patientid)) {
                patientid = GetTimeStamp();
            }
            int x = file.Dataset.Get&amp;lt;int&amp;gt;(DicomTag.NumberOfFrames);
            for (var i = 0; i &amp;lt; x; i++) {
                var fileName = options.outJpgDir + &amp;quot;/&amp;quot; + patientid + &amp;quot;_&amp;quot; + (i + 1) + &amp;quot;.jpg&amp;quot;;
                image.RenderImage(i).AsBitmap().Save(@fileName);
            }
            Console.WriteLine(&amp;quot;success: dcm转jpg成功&amp;quot;);
        }

        /// &amp;lt;summary&amp;gt;
        /// dcm上传
        /// &amp;lt;/summary&amp;gt;
        /// &amp;lt;param name=&amp;quot;options&amp;quot;&amp;gt;&amp;lt;/param&amp;gt;
        /// &amp;lt;returns&amp;gt;&amp;lt;/returns&amp;gt;
        [Obsolete]
        async public static 
        Task
        DcmScu(DcmScuOptions options) {
            var client = new DicomClient(options.ip, options.port, false, &amp;quot;SCU&amp;quot;, &amp;quot;ANY-SCP&amp;quot;);
            await client.AddRequestAsync(new Dicom.Network.DicomCStoreRequest(@options.dcmPath));
            await client.SendAsync();
            Console.WriteLine(&amp;quot;success: dcm文件上传成功&amp;quot;);
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;</description>
      <category domain="http://172.18.0.3:8080/category/8">C#</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Python之Pytorch机器学习安装及基础api</title>
      <link>http://172.18.0.3:8080/article/51</link>
      <description>&lt;h2&gt;PyTorch是什么?&lt;/h2&gt;
&lt;p&gt;PyTorch是一个基于Torch的Python开源机器学习库，用于自然语言处理等应用程序。 它主要由Facebook的人工智能研究小组开发。Uber的&amp;quot;Pyro&amp;quot;也是使用的这个库。基于Python的科学计算包，服务于以下两种场景:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;作为NumPy的替代品，可以使用GPU的强大计算能力&lt;/li&gt;
&lt;li&gt;提供最大的灵活性和高速的深度学习研究平台&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;本文参考&lt;a href="https://github.com/zergtant/pytorch-handbook"&gt;pytorch-handbook&lt;/a&gt;做的笔记&lt;/p&gt;
&lt;h2&gt;安装&lt;/h2&gt;
&lt;p&gt;Pytorch主要使用GPU作为计算力,需要安装CUDA和cuDNN,可以参考&lt;a href="https://blog.csdn.net/qq_37296487/article/details/83028394"&gt;win10安装CUDA10和cuDNN&lt;/a&gt;,安装成功后进行Pytorch的安装,这里使用了Anaconda环境管理软件,安装及使用可参考&lt;a href="https://www.jianshu.com/p/742dc4d8f4c5"&gt;Anaconda简单入门&lt;/a&gt;,可以子Anaconda新建一个工作空间如pytorch,接下来切换到工作空间进行安装:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 切换至pytorch工作空间
activate pytorch
# 安装pytorch
conda install pytorch torchvision cudatoolkit=10.0 -c pytorch
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装完成后进行测试:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;python
import torch
torch.__version__
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出版本号即为成功,如'1.2.0'&lt;/p&gt;
&lt;h2&gt;创建矩阵&lt;/h2&gt;
&lt;h3&gt;空矩阵&lt;/h3&gt;
&lt;p&gt;创建一个5x3的矩阵,但未初始化&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.empty(5, 3)
print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([[0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.],
        [0., 0., 0.]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;随机初始化&lt;/h3&gt;
&lt;p&gt;创建一个随机初始化的矩阵:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.rand(5, 3)
print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([[0.2500, 0.0015, 0.0559],
        [0.6813, 0.5940, 0.8406],
        [0.6544, 0.2621, 0.9989],
        [0.6717, 0.5013, 0.9329],
        [0.7571, 0.8893, 0.9461]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;0填充的矩阵&lt;/h3&gt;
&lt;p&gt;创建一个0填充的矩阵，数据类型为long:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.zeros(5, 3, dtype=torch.long)
print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([[0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0],
        [0, 0, 0]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;创建tensor并使用现有数据初始化&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.tensor([1,2, 3])
print(x)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([1, 2, 3])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;获取 size&lt;/h3&gt;
&lt;p&gt;使用size方法与Numpy的shape属性返回的相同&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;print(x.size())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;torch.Size([3])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;矩阵加法&lt;/h3&gt;
&lt;p&gt;可以直接相加,或者使用add方法&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.rand(5, 3)
y = torch.rand(5, 3)
# 加法1
print(x + y)
# 加法2
print(torch.add(x, y))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([[0.7529, 0.9342, 1.7580],
        [0.5866, 1.1636, 0.3041],
        [1.0953, 0.3398, 0.9801],
        [1.1558, 1.1327, 1.0594],
        [1.3585, 1.3925, 1.3977]])
tensor([[0.7529, 0.9342, 1.7580],
        [0.5866, 1.1636, 0.3041],
        [1.0953, 0.3398, 0.9801],
        [1.1558, 1.1327, 1.0594],
        [1.3585, 1.3925, 1.3977]])
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;改变张量的维度和大小&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;x = torch.randn(4, 4)
y = x.view(16)
z = x.view(-1, 8)  #  size -1 从其他维度推断
print(x.size(), y.size(), z.size())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;torch.Size([4, 4]) torch.Size([16]) torch.Size([2, 8])
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Numpy转换&lt;/h2&gt;
&lt;h3&gt;转换为numpy数组&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;a = torch.ones(5)
print(a)
b = a.numpy()
print(b)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([1., 1., 1., 1., 1.])
[1. 1. 1. 1. 1.]
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;numpy转换为Torch Tensor&lt;/h3&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;import numpy as np
a = np.ones(5)
b = torch.from_numpy(a)
np.add(a, 1, out=a)
print(a)
print(b)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[2. 2. 2. 2. 2.]
tensor([2., 2., 2., 2., 2.], dtype=torch.float64)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;CUDA张量&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;Tensors与Numpy中的 ndarrays类似，但是在PyTorch中 Tensors 可以使用GPU进行计算.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;使用.to 方法 可以将Tensor移动到任何设备中&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# is_available 函数判断是否有cuda可以使用
# ``torch.device``将张量移动到指定的设备中
if torch.cuda.is_available():
    device = torch.device(&amp;quot;cuda&amp;quot;)          # a CUDA 设备对象
    y = torch.ones_like(x, device=device)  # 直接从GPU创建张量
    x = x.to(device)                       # 或者直接使用``.to(&amp;quot;cuda&amp;quot;)``将张量移动到cuda中
    z = x + y
    print(z)
    print(z.to(&amp;quot;cpu&amp;quot;, torch.double))       # ``.to`` 也会对变量的类型做更改
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], device='cuda:0')
tensor([[1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.],
        [1., 1., 1.]], dtype=torch.float64)
&lt;/code&gt;&lt;/pre&gt;</description>
      <category domain="http://172.18.0.3:8080/category/2">Python</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Perfree-Simple-Pro-致更好的你</title>
      <link>http://172.18.0.3:8080/article/57</link>
      <description>&lt;p&gt;一款高仿Typecho的handsome博客主题,使用于Jpress
&lt;img src="/static/attach/20210910/ff7abe0e985a4cd3b6b21cb484bf0b05.png" alt="" /&gt;&lt;/p&gt;
&lt;h2&gt;主题简介&lt;/h2&gt;
&lt;p&gt;抛弃一切繁琐,只想简简单单的呈现你的文采,如你所见,这是一款简约清新的博客主题模板,该模板收费69元,包含相册插件和留言板插件(开发中)以及以后所有新出的插件,你只管创作,其他交给我~&lt;/p&gt;
&lt;h2&gt;功能介绍&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;简约清新的样式&lt;/li&gt;
&lt;li&gt;支持响应式,手机,PC无缝连接&lt;/li&gt;
&lt;li&gt;一言接口随机优美文字&lt;/li&gt;
&lt;li&gt;代码高亮(支持89种样式,支持行号显示)&lt;/li&gt;
&lt;li&gt;全局音乐播放器，配合pjax实现切换页面音乐不停止播放,支持网易云歌单&lt;/li&gt;
&lt;li&gt;支持自定义js代码&lt;/li&gt;
&lt;li&gt;支持自定义css代码&lt;/li&gt;
&lt;li&gt;支持自定义广告位&lt;/li&gt;
&lt;li&gt;归档页面,记录你的创作痕迹&lt;/li&gt;
&lt;li&gt;相册,记录你的点点滴滴&lt;/li&gt;
&lt;li&gt;支持友链的展示&lt;/li&gt;
&lt;li&gt;支持文章置顶设置&lt;/li&gt;
&lt;li&gt;随机彩色标签云&lt;/li&gt;
&lt;li&gt;文章toc目录导航&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;购买须知&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;主题,插件制作不易,请考虑清楚再购买&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;已购主题用户,请不要私自传播,尊重下作者&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;该主题永久更新,购买后统一加入QQ群&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;购买请联系QQ741223257备注模板购买&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;付款地址&lt;/h2&gt;
&lt;p&gt;&lt;img src="/static/attach/20210902/99ab8a1fb3524f19a0e3864f9b02cd4b.jpg" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src="/static/attach/20210902/a7626afde1d5424bb3f79d4d2ea96c23.jpg" alt="" /&gt;&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/4">我的项目</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>前端之Angular入门(一)</title>
      <link>http://172.18.0.3:8080/article/50</link>
      <description>&lt;p&gt;随着技术的更新换代，从jquery的一家独大，到现在的vue,angular,react三大主流框架，技术发展飞速的同时，也给一些后端要写前端时无从下手的感觉，今天主要介绍下如何快速上手angular，首先引入一个问题，为什么要用angular?因为angular引入了一些Java的东西，如依赖注入，这个概念对于后端开发人员就很熟悉了，选用angular的目的就是它上手容易,模块化利用的非常好且代码风格有些地方和java非常相似。&lt;/p&gt;
&lt;h2&gt;有哪些ui框架？&lt;/h2&gt;
&lt;p&gt;抛开angular，我们一般开发都会用一些ui框架如Bootstrap,Laui,elementUI,iview等等，使用这些框架会使我们少些很多的样式代码，开发起来更加迅速，界面样式也更统一，那么angular有哪些ui框架？我们可以访问下angular的官网，找到资源&amp;gt;ui组件，如下图:
&lt;img src="https://www.img.yinpengfei.com/group1/blog/111.png" alt="ui" /&gt;&lt;/p&gt;
&lt;p&gt;可以看到有很多的ui框架组件，你可以在这里选择适合你用的组件进行开发，这里推荐Angular Material和NG-ZORRO，其中Angular Material是Material Design设计方式,也就所谓的质感，NG-ZORRO是Ant Design的angular，本篇文章使用的是&lt;a href="http://ng.ant.design/docs/introduce/zh"&gt;NG-ZORRO&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;新建项目&lt;/h2&gt;
&lt;p&gt;nodejs安装略过，之前的文章已经介绍过了，这里直接开始安装angular-cli，就是创建angular项目的工具&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;npm install -g @angular/cli
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来新建项目我们使用webstorm来完成，webstorm是一款前端开发的idea，使用起来特别的方便，在以后的开发过程中开发工具一般都是webstorm 或者 vscode，选中新建project弹出如下图界面，点击Angular Cli，我们只需关心Location和Additional parameters就行了，其中Location代表项目路径，Additional parameters为创建项目时的参数，可以看到这里填写了--routing，意思就是启用angular的路由模块，方便我们对路由的管理
&lt;img src="https://www.img.yinpengfei.com/group1/blog/222.png" alt="新建" /&gt;&lt;/p&gt;
&lt;h2&gt;添加NG-ZORRO&lt;/h2&gt;
&lt;p&gt;项目创建完成后，架构目录如下:
&lt;img src="https://www.img.yinpengfei.com/group1/blog/333.png" alt="目录" /&gt;
等下再说这些文件目录的意思，我们先把NG-ZORRO添加进项目，打开命令行窗口进入项目目录执行如下命令：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;# 执行的过程中会提示一些个性化配置，自行选择是否需要即可
ng add ng-zorro-antd
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;命令执行完毕后，我们启动项目查看下长什么样~&lt;/p&gt;
&lt;h2&gt;启动项目&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;ng serve
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ng serve 即为启动项目，默认端口4200，启动完之后我们访问下
&lt;img src="https://www.img.yinpengfei.com/group1/blog/444.png" alt="启动" /&gt;
可以看到已经启动成功，并出现了NG-ZORRO的logo，接下来就可以参照NG-ZORRO的文档进行组件的引用了~本来想着一篇文章写完，但写着写着不想写了...索性就把文章分为三篇，本篇为第一篇，第二篇会说一下怎么创建组件，路由的管理，组件的调用以及一些基础语法，第三篇为Http工具的封装，路由守卫，权限认证及Http拦截器。&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/3">前端</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Tensorflow学习笔记-房价预测</title>
      <link>http://172.18.0.3:8080/article/49</link>
      <description>&lt;p&gt;本篇文章采用的数据集为TensorFlow提供的波士顿房价数据集,仅有数据506个，并且404个用来训练，拿出102个用来当作测试数据集。&lt;/p&gt;
&lt;h2&gt;准备工作&lt;/h2&gt;
&lt;p&gt;本篇文章TensorFlow版本为TensorFlow 2.0 Beta,Python3&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;import tensorflow as tf
import matplotlib.pyplot as plt
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;导入数据集&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;(train_data,train_label),(test_data,test_label) = tf.keras.datasets.boston_housing.load_data()
print(train_data.shape)
print(test_data.shape)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通过观察发现拥有404条训练数据和102条训练数据且有13个不同的特征属性,13个特征属性代表如下:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;人均犯罪率&lt;/li&gt;
&lt;li&gt;占地面积超过25,000平方英尺的住宅用地比例。&lt;/li&gt;
&lt;li&gt;每个城镇非零售业务的比例。&lt;/li&gt;
&lt;li&gt;查尔斯河虚拟变量 如果是大片土地则为1，否则为0&lt;/li&gt;
&lt;li&gt;氮的氧化物浓度(分之1000万&lt;/li&gt;
&lt;li&gt;平均每人所住房间数&lt;/li&gt;
&lt;li&gt;1940年前业主单位所占的比例&lt;/li&gt;
&lt;li&gt;到达波士顿就业中心的加权距离&lt;/li&gt;
&lt;li&gt;到达径向公路的系数&lt;/li&gt;
&lt;li&gt;所有财产价值的每10000美元的税率&lt;/li&gt;
&lt;li&gt;城镇师生比例&lt;/li&gt;
&lt;li&gt;城镇黑人比例按照式计算&lt;/li&gt;
&lt;li&gt;地位较低人士的百分比&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;对数据进行处理&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 对数据进行处理(使用dataset初始数据)
ds_train = tf.data.Dataset.from_tensor_slices((train_data,train_label))
ds_test = tf.data.Dataset.from_tensor_slices((test_data,test_label))
# 设置对数据进行乱序+重复+批次处理
ds_train = ds_train.shuffle(train_label.shape[0]).repeat().batch(101)
ds_test = ds_test.batch(101)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;建造模型&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;def build_model():
    # 创建模型
    model = tf.keras.Sequential([
        # 输入层
        tf.keras.layers.Dense(64,activation=tf.nn.relu,input_shape=(13,)),
        # 隐藏层
        tf.keras.layers.Dense(64,activation=tf.nn.relu),
        # 输出层
        tf.keras.layers.Dense(1)
    ])

    # 编译模型
    model.compile(
        optimizer=tf.optimizers.Adam(0.001),
        loss=tf.losses.mse,
        metrics=['mae']
    )

    return model

model = build_model()
print(model.summary())
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://www.img.yinpengfei.com/group1/blog/1566353332(1).jpg" alt="模型" /&gt;&lt;/p&gt;
&lt;h2&gt;训练模型&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 每一个epoch的迭代次数
train_steps_per_epochs = train_data.shape[0] // 101
test_steps_per_epochs = test_data.shape[0] // 101

# 设置模型自动停止
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=20)

# 训练模型(500个epoch)并放入history
history =model.fit(
    ds_train,
    epochs=500,
    steps_per_epoch=train_steps_per_epochs,
    validation_data=ds_test,
    validation_steps=test_steps_per_epochs,
    callbacks=[early_stop]
)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;图形展示&lt;/h2&gt;
&lt;p&gt;训练过程的数据都保存在了history里面，我们可以通过画图来查看训练过程：&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 绘图函数
def plot_history(history):
    plt.subplot(2, 1, 1)
    plt.title('loss')
    plt.plot(history.epoch, history.history.get('loss'), label='loss')
    plt.plot(history.epoch, history.history.get('val_loss'), label='val_loss')
    plt.legend()

    plt.subplot(2, 1, 2)
    plt.title('mae')
    plt.plot(history.epoch, history.history.get('mae'), label='mae')
    plt.plot(history.epoch, history.history.get('val_mae'), label='val_mae')
    plt.legend()

    plt.show()

plot_history(history)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src="https://www.img.yinpengfei.com/group1/blog/1566353503(1).jpg" alt="图形展示" /&gt;&lt;/p&gt;
&lt;h2&gt;预测数据&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;test_predictions = model.predict(test_data).flatten()
print(test_predictions)
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[11.642183  19.59567   21.726236  34.196423  22.4167    22.425404
 28.799603  23.66306   19.806677  19.32042   15.688759  17.867088
 16.530552  40.41346   19.615501  21.516493  24.449144  19.616917
 17.958574  26.871962  11.995824   8.774342  20.539331  15.862772
 22.449028  22.83855   30.204325  38.96432   13.9990225 22.611979
 21.11433   15.393939  34.931477  23.673939  17.807938   9.601786
 15.747133  13.715659  20.602491  29.066439  25.852331  23.244295
 16.11424   33.486485  41.81491   24.000952  31.267826  19.320993
 26.541712  22.562246  37.748898  18.671835  12.418766  17.432077
 31.431618  25.197182  13.587622  33.84362   37.0064    22.762499
 21.182072  18.298859  16.805908  21.369957  25.80291   24.897501
 17.08489   26.052343  11.837404  10.8254385 25.344805  27.223076
 22.880053  13.774416  24.849585  19.550842  21.7954    22.697067
 38.01279   11.396687  21.819597  37.327755  16.32067   16.062927
 20.142687  17.874754  19.599241  21.31827   19.469305  30.204813
 19.356852  24.60395   21.798048  29.356714  39.006306  19.91289
 37.461536  37.036076  25.903673  45.908554  31.342543  20.151966 ]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;总结&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;均方误差（MSE）是一种适用于回归问题常见的损失函数。&lt;/li&gt;
&lt;li&gt;在评估精度的时候回归问题是和分类问题不同的。在回归问题中我们使用了平均绝对误差（MAE）&lt;/li&gt;
&lt;li&gt;如果训练数据并不是太多，最好是选择比较小并且隐藏层比较少的神经网络模型来避免过度拟合&lt;/li&gt;
&lt;li&gt;提前停止优化是一个比较有用的技巧来避免过度拟合。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;完整代码&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;import tensorflow as tf
import matplotlib.pyplot as plt


# 加载数据集(波士顿房价)
(train_data,train_label),(test_data,test_label) = tf.keras.datasets.boston_housing.load_data()
# 通过观察发现拥有404条训练数据并包含13个不同的特征属性
print(train_data.shape)
# 通过观察发现拥有102条训练数据并包含13个不同的特征属性
print(test_data.shape)

# 对数据进行处理(使用dataset初始数据)
ds_train = tf.data.Dataset.from_tensor_slices((train_data,train_label))
ds_test = tf.data.Dataset.from_tensor_slices((test_data,test_label))
# 设置对数据进行乱序+重复+批次处理
ds_train = ds_train.shuffle(train_label.shape[0]).repeat().batch(101)
ds_test = ds_test.batch(101)

# 建造模型
def build_model():
    # 创建模型
    model = tf.keras.Sequential([
        # 输入层
        tf.keras.layers.Dense(64,activation=tf.nn.relu,input_shape=(13,)),
        # 隐藏层
        tf.keras.layers.Dense(64,activation=tf.nn.relu),
        # 输出层
        tf.keras.layers.Dense(1)
    ])

    # 编译模型
    model.compile(
        optimizer=tf.optimizers.Adam(0.001),
        loss=tf.losses.mse,
        metrics=['mae']
    )

    return model

# 绘图函数
def plot_history(history):
    plt.subplot(2, 1, 1)
    plt.title('loss')
    plt.plot(history.epoch, history.history.get('loss'), label='loss')
    plt.plot(history.epoch, history.history.get('val_loss'), label='val_loss')
    plt.legend()

    plt.subplot(2, 1, 2)
    plt.title('mae')
    plt.plot(history.epoch, history.history.get('mae'), label='mae')
    plt.plot(history.epoch, history.history.get('val_mae'), label='val_mae')
    plt.legend()

    plt.show()


model = build_model()
print(model.summary())

# 每一个epoch的迭代次数
train_steps_per_epochs = train_data.shape[0] // 101
test_steps_per_epochs = test_data.shape[0] // 101

# 设置模型自动停止
early_stop = tf.keras.callbacks.EarlyStopping(monitor='val_loss', patience=20)

# 训练模型(500个epoch)并放入history
history =model.fit(
    ds_train,
    epochs=500,
    steps_per_epoch=train_steps_per_epochs,
    validation_data=ds_test,
    validation_steps=test_steps_per_epochs,
    callbacks=[early_stop]
)

# 绘图
plot_history(history)

# 预测
test_predictions = model.predict(test_data).flatten()
print(test_predictions)
&lt;/code&gt;&lt;/pre&gt;</description>
      <category domain="http://172.18.0.3:8080/category/2">Python</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Python数学计算/解方程/求导</title>
      <link>http://172.18.0.3:8080/article/48</link>
      <description>&lt;p&gt;本文总结了一些简单的Python数学操作,如均值、方差、标准差,函数方程,求导等&lt;/p&gt;
&lt;h2&gt;均值、方差、标准差&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 数据集
# 1, 2, 3, 4, 5
import numpy as np

arr = [1, 2, 3, 4, 5]
# 均值
arr_mean = np.mean(arr)
print(&amp;quot;arr均值=%f&amp;quot; % arr_mean)
# 方差
arr_var = np.var(arr)
print(&amp;quot;arr方差=%f&amp;quot; % arr_var)
# 标准差
arr_std = np.std(arr)
print(&amp;quot;arr标准差=%f&amp;quot; % arr_std)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;arr均值=3.000000
arr方差=2.000000
arr标准差=1.414214
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;简单方程求解&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# 2x-8=0
from sympy import *
x= symbols('x')
print(solve(2 * x - 8,x))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[4]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;二元一次方程求解&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# 3x&#x2212;y=3
# 3x+y=9
from sympy import *

x, y = symbols('x,y')
print(solve([3 * x - y - 3, 3 * x + y - 9], [x, y]))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;{x: 2, y: 3}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;一元二次方程求解&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# x&#xb2;+2x+1=0
from sympy import *

x = symbols('x')
print(solve(x ** 2 + 2 * x + 1, x))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[-1]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;求解线性方程组&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目:
# x&#x2081;+3x&#x2082;+4x&#x2083;=8
# 2x&#x2081;+x&#x2082;+6x&#x2083;=12
# 2x&#x2081;+3x&#x2082;+4x&#x2083;=3
import numpy as np
import scipy as sp
import scipy.linalg

a = np.array([[1, 2, 2], [3, 1, 6], [4, 5, 4]])
b = np.array([8, 12, 3])
print(sp.linalg.solve(a, b))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[-7.7   2.4   5.45]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;求解非线性方程组&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# 5x&#x2081;+3=0
# 4x&#x2083;&#xb2;-2sin(x&#x2081;x&#x2082;)=0
# x&#x2081;x&#x2082;-1.5=0
from math import sin, cos
from scipy import optimize

def f(x):
    x1, x2, x3 = x.tolist()
    return [5 * x1 + 3, 4 * x3 * x3 - 2 * sin(x1 * x2), x1 * x2 - 1.5]


# f 计算方程组的误差,[1，1，1]是未知数的初始值
result = optimize.fsolve(f, [1, 1, 1])
print(result)  # x1,x2,x3的值
print(f(result))  # 方程组的误差
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;[-0.6        -2.5        -0.70622057]
[0.0, -9.126033262418787e-14, 5.329070518200751e-15]
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;求导数&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# 已知f(x) = x**2
# 求f'(x)
# 求f'(1)
x = Symbol('x')
y = x ** 2
dx = diff(y, x)
print(&amp;quot;y =&amp;quot;, dx)
u = lambdify(x, dx)
print(u(1))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;y = 2*x
2
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;# 题目
# 已知f(x)=2x**2+3x
# 求f'(x)
# 求f'(1)
def f(x):
    return 2 * x ** 2 + 3 * x ** 2


print(derivative(f, 1))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;输出&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;10.0
&lt;/code&gt;&lt;/pre&gt;</description>
      <category domain="http://172.18.0.3:8080/category/2">Python</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>Python求导数</title>
      <link>http://172.18.0.3:8080/article/47</link>
      <description>&lt;p&gt;OωO好久没写博客了，忽然想起来就做下记录~使用Sympy和SciPy两种方式实现,简单的题目如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;已知f(x)=2(x**2)-3x
求f'(2)
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Sympy&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;from sympy import *
x = Symbol(&amp;quot;x&amp;quot;)
y = 2 * x ** 2 - 3 * x
dx = diff(y, x)
print(dx)
u = lambdify(x, dx)
print(u(2))
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;4*x - 3
5
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;SciPy&lt;/h2&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;from scipy.misc import derivative
def f(x): 
    return 2 * x ** 2 - 3 * x
u = derivative(f, 2)
print(u)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行结果&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-python"&gt;5.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;就是很久没写博客了，划划水==&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/2">Python</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>推荐一款分布式文件服务器</title>
      <link>http://172.18.0.3:8080/article/46</link>
      <description>&lt;p&gt;说到分布式文件服务器,可能大家最常听说的就是FastDfs,而一提起它又比较头疼,安装起来太麻烦了,文件不好迁移啦等等之类的,今天就给大家推荐一款最近刚刚兴起的分布式文件服务器,Go-FastDfs,它是一个基于http协议的分布式文件系统，它基于大道至简的设计理念，一切从简设计，使得它的运维及扩展变得更加简单，它具有高性能、高可靠、无中心、免维护等优点。 另外它是基于go语言开发的,使得它对于高并发的处理非常的优秀。&lt;/p&gt;
&lt;h2&gt;优点&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;支持curl命令上传&lt;/li&gt;
&lt;li&gt;支持浏览器上传&lt;/li&gt;
&lt;li&gt;支持HTTP下载&lt;/li&gt;
&lt;li&gt;支持多机自动同步&lt;/li&gt;
&lt;li&gt;支持断点下载&lt;/li&gt;
&lt;li&gt;支持配置自动生成&lt;/li&gt;
&lt;li&gt;支持小文件自动合并(减少inode占用)&lt;/li&gt;
&lt;li&gt;支持秒传&lt;/li&gt;
&lt;li&gt;支持一键迁移&lt;/li&gt;
&lt;li&gt;支持并行体验&lt;/li&gt;
&lt;li&gt;支持断点续传(&lt;a href="https://tus.io/"&gt;tus&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;支持docker部署&lt;/li&gt;
&lt;li&gt;支持自监控告警&lt;/li&gt;
&lt;li&gt;支持集群文件信息查看&lt;/li&gt;
&lt;li&gt;使用通用HTTP协议&lt;/li&gt;
&lt;li&gt;无需专用客户端（支持wget,curl等工具）&lt;/li&gt;
&lt;li&gt;类fastdfs&lt;/li&gt;
&lt;li&gt;高性能&lt;/li&gt;
&lt;li&gt;高可靠&lt;/li&gt;
&lt;li&gt;无中心设计(所有节点都可以同时读写)&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;下载安装&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://github.com/sjqzhang/go-fastdfs/releases" title="下载地址"&gt;下载地址&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Linux下载第一个可执行文件&lt;/p&gt;
&lt;p&gt;Windows下载exe文件&lt;/p&gt;
&lt;h2&gt;启动&lt;/h2&gt;
&lt;p&gt;Linux 下启动&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;./fileserver
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Linux 下后台运行(这里使用nohup)&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;nohup ./fileserver &amp;amp;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Windows启动直接双击exe即可
启动成功后访问http://yourserver ip:8080即可进行上传操作&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;默认端口8080,如修改端口,可在启动之后,打开cfg配置文件,修改端口,重新启动即可&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;更多配置&lt;/h2&gt;
&lt;p&gt;集群配置以及一些其他的配置可以参考文档
&lt;a href="https://github.com/sjqzhang/go-fastdfs/blob/master/README-en.md" title="文档地址"&gt;文档地址&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;管理平台&lt;/h2&gt;
&lt;p&gt;之前觉得Go-FastDfs非常优秀,就为它写了一个界面化的管理平台,可以参照文档进行安装
&lt;a href="https://github.com/perfree/go-fastdfs-web" title="管理平台"&gt;管理平台地址&lt;/a&gt;&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/5">杂七杂八</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>谁又不曾孤独呢-孤独患者</title>
      <link>http://172.18.0.3:8080/article/55</link>
      <description>&lt;p&gt;孤独，真的成为了人的生活常态，不知道为什么最近总能想到这个话题，不知道你能不能明白生命叫孤独，不知道你有没有和你谈的来的人。也许正因为孤独，你才迷恋各种游戏、电影、综艺、甚至发了疯的写代码不敢停下来，你怕闲下来就像我一样在这儿思考自己是否真的非常孤独，试问一下，谁又不曾感到孤独呢？
&lt;img src="/attach/20220324/2d9198dfe5694aa595e6fd5dbf8a02dd.png" alt="" /&gt;
记得看过这样一本书，书名叫《不曾孤独，怎会懂得》，里边有这样一句话“孤独是与生俱来的宿命”，你自己的路终究要走，或许你会孤独终老，又或许你会遇到那个彼此陪伴的人。无论结局如何，都是你自己换来的。孤独的人总会在找一些让自己不再感到孤独的事情做，或迷茫、或沉迷、或堕落，时不时的又会思考自己，促使自己成长，朝着不再孤独的那个自己努力，当你觉得不再孤独了，你会牢牢的抓紧这种感觉，害怕它悄然的溜走，因为你懂得了孤独的滋味，经历了很多的挫折才得到的这种不孤独的感觉，也正是因为这样，不曾孤独，怎会懂得
&lt;img src="/attach/20220324/07b8fa90c18b466e8408d96fd04443ef.png" alt="" /&gt;
愿你我不再孤独，时刻存在着希望    ---孤独患者&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/5">杂七杂八</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>SpringBoot-MultipartFile.transferTo路径错误问题</title>
      <link>http://172.18.0.3:8080/article/43</link>
      <description>&lt;p&gt;今天写multipartFile转file,使用MultipartFile.transferTo(file)遇到了路径错误问题,代码如下:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;/**
 * multipartFile转file
 * @param multipartFile
 * @param tempPath
 * @return File
 */
private static File getFile(MultipartFile multipartFile,String tempPath) {
    String fileName = multipartFile.getOriginalFilename();
    String filePath = tempPath;
    File file = new File(filePath + fileName);
    try {
        multipartFile.transferTo(file);
    } catch (IOException e) {
        e.printStackTrace();
    }
    return file;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;问题排查&lt;/h2&gt;
&lt;p&gt;报错信息大致如下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;java.io.IOException: java.io.FileNotFoundException: /tmp/tomcat.8587388778869943211.8888/work/Tomcat/localhost/ROOT/temp/1.jpg.PNG (No such file or directory)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这很明显的路径不对,接着又扒multipartFile.transferTo源码,结果发现如下一段代码:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;public void transferTo(File dest) throws IOException, IllegalStateException {
    this.part.write(dest.getPath());
    if (dest.isAbsolute() &amp;amp;&amp;amp; !dest.exists()) {
        FileCopyUtils.copy(this.part.getInputStream(), Files.newOutputStream(dest.toPath()));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;它竟然判断了下是否是绝对路径.................&lt;/p&gt;
&lt;h2&gt;问题解决&lt;/h2&gt;
&lt;p&gt;既然它要绝对路径,那我就给它绝对路径就好了,修改后的代码如下:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-java"&gt;/**
 * multipartFile转file
 * @param multipartFile
 * @param tempPath
 * @return File
 */
private static File getFile(MultipartFile multipartFile,String tempPath) {
    String fileName = multipartFile.getOriginalFilename();
    String filePath = tempPath;
    File file = new File(filePath + fileName);
    try {
        multipartFile.transferTo(file.getAbsoluteFile());
    } catch (IOException e) {
        e.printStackTrace();
    }
    return file;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;到这里就没啥问题了,真的是......唉,一入编程深似海....&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/1">Java</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
    <item>
      <title>春暖花开，我想出去走走</title>
      <link>http://172.18.0.3:8080/article/42</link>
      <description>&lt;p&gt;春暖花开，总想约上三五好友出去走走，抛下一切烦恼，去看看壮丽的河山，去享受人间的美味，正所谓春天是一个充满生机和希望的季节，莫负大好时光，尽情放飞心情。
去四川看看大熊猫，品尝四川的各色美食
&lt;img src="/attach/20220324/0b2e4c042ebe4c73af79362fff1ad1d6.png" alt="" /&gt;
&lt;img src="/attach/20220324/c668a654ccb64b6e9e6f747d75062cdf.png" alt="" /&gt;
去杭州欣赏“人间天堂”般的美景
&lt;img src="/attach/20220324/c86209c50f8045ff97cf9c3ed1bef75f.png" alt="" /&gt;
&lt;img src="/attach/20220324/e05480430cc445c799422b15b198734b.png" alt="" /&gt;
&lt;img src="/attach/20220324/83f7c4a7b5af44058c55c6ea7ef5f469.png" alt="" /&gt;
去上海迪士尼追寻下儿时的梦想
&lt;img src="/attach/20220324/8931b66d20ff4ca092b580bee46adb11.png" alt="" /&gt;
&lt;img src="/attach/20220324/5ce96c3d986448f5b590b2559696d8b0.png" alt="" /&gt;
阳春三月（四月了~），莫要负了这大好时光~&lt;/p&gt;</description>
      <category domain="http://172.18.0.3:8080/category/5">杂七杂八</category>
      <pubDate>Sun, 05 Apr 2026 13:57:28 GMT</pubDate>
      <author>perfree</author>
    </item>
  </channel>
</rss>
