init: 初始化 XiaowenBlog 博客项目,添加配置文件及技术文章

This commit is contained in:
wenyongda 2026-03-27 12:22:43 +08:00
commit aa1fb1d6f5
177 changed files with 27919 additions and 0 deletions

7
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: npm
directory: "/"
schedule:
interval: daily
open-pull-requests-limit: 20

8
.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.DS_Store
Thumbs.db
db.json
*.log
node_modules/
public/
.deploy*/
_multiconfig.yml

104
_config.butterfly.yml Normal file
View File

@ -0,0 +1,104 @@
# Hexo Configuration
## Docs: https://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/
# Site
title: Hexo
subtitle: ''
description: ''
keywords:
author: John Doe
language: en
timezone: ''
# URL
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
url: http://example.com
permalink: :year/:month/:day/:title/
permalink_defaults:
pretty_urls:
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
trailing_html: true # Set to false to remove trailing '.html' from permalinks
# Directory
source_dir: source
public_dir: public
tag_dir: tags
archive_dir: archives
category_dir: categories
code_dir: downloads/code
i18n_dir: :lang
skip_render:
# Writing
new_post_name: :title.md # File name of new posts
default_layout: post
titlecase: false # Transform title into titlecase
external_link:
enable: true # Open external links in new tab
field: site # Apply to the whole site
exclude: ''
filename_case: 0
render_drafts: false
post_asset_folder: false
relative_link: false
future: true
syntax_highlighter: highlight.js
highlight:
line_number: true
auto_detect: false
tab_replace: ''
wrap: true
hljs: false
prismjs:
preprocess: true
line_number: true
tab_replace: ''
# Home page setting
# path: Root path for your blogs index page. (default = '')
# per_page: Posts displayed per page. (0 = disable pagination)
# order_by: Posts order. (Order by date descending by default)
index_generator:
path: ''
per_page: 10
order_by: -date
# Category & Tag
default_category: uncategorized
category_map:
tag_map:
# Metadata elements
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
meta_generator: true
# Date / Time format
## Hexo uses Moment.js to parse and display date
## You can customize the date format as defined in
## http://momentjs.com/docs/#/displaying/format/
date_format: YYYY-MM-DD
time_format: HH:mm:ss
## updated_option supports 'mtime', 'date', 'empty'
updated_option: 'mtime'
# Pagination
## Set per_page to 0 to disable pagination
per_page: 10
pagination_dir: page
# Include / Exclude file(s)
## include:/exclude: options only apply to the 'source/' folder
include:
exclude:
ignore:
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: butterfly
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
type: ''

104
_config.yml Normal file
View File

@ -0,0 +1,104 @@
# Hexo Configuration
## Docs: https://hexo.io/docs/configuration.html
## Source: https://github.com/hexojs/hexo/
# Site
title: Xiaowen's Blog
subtitle: ''
description: "欢迎访问小文的博客!"
keywords: 编程
author: Wen Yongda
language: zh-CN
timezone: "Asia/Shanghai"
# URL
## Set your site url here. For example, if you use GitHub Page, set url as 'https://username.github.io/project'
url: http://example.com
permalink: :year/:month/:day/:title/
permalink_defaults:
pretty_urls:
trailing_index: true # Set to false to remove trailing 'index.html' from permalinks
trailing_html: true # Set to false to remove trailing '.html' from permalinks
# Directory
source_dir: source
public_dir: public
tag_dir: tags
archive_dir: archives
category_dir: categories
code_dir: downloads/code
i18n_dir: :lang
skip_render:
# Writing
new_post_name: :title.md # File name of new posts
default_layout: post
titlecase: false # Transform title into titlecase
external_link:
enable: true # Open external links in new tab
field: site # Apply to the whole site
exclude: ''
filename_case: 0
render_drafts: false
post_asset_folder: true
relative_link: false
future: true
syntax_highlighter: highlight.js
highlight:
line_number: true
auto_detect: false
tab_replace: ''
wrap: true
hljs: false
prismjs:
preprocess: true
line_number: true
tab_replace: ''
# Home page setting
# path: Root path for your blogs index page. (default = '')
# per_page: Posts displayed per page. (0 = disable pagination)
# order_by: Posts order. (Order by date descending by default)
index_generator:
path: ''
per_page: 10
order_by: -date
# Category & Tag
default_category: uncategorized
category_map:
tag_map:
# Metadata elements
## https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta
meta_generator: true
# Date / Time format
## Hexo uses Moment.js to parse and display date
## You can customize the date format as defined in
## http://momentjs.com/docs/#/displaying/format/
date_format: YYYY-MM-DD
time_format: HH:mm:ss
## updated_option supports 'mtime', 'date', 'empty'
updated_option: 'mtime'
# Pagination
## Set per_page to 0 to disable pagination
per_page: 10
pagination_dir: page
# Include / Exclude file(s)
## include:/exclude: options only apply to the 'source/' folder
include:
exclude:
ignore:
# Extensions
## Plugins: https://hexo.io/plugins/
## Themes: https://hexo.io/themes/
theme: butterfly
# Deployment
## Docs: https://hexo.io/docs/one-command-deployment
deploy:
type: ''

27
package.json Normal file
View File

@ -0,0 +1,27 @@
{
"name": "hexo-site",
"version": "0.0.0",
"private": true,
"scripts": {
"build": "hexo generate",
"clean": "hexo clean",
"deploy": "hexo deploy",
"server": "hexo server"
},
"hexo": {
"version": "8.1.1"
},
"dependencies": {
"hexo": "^8.0.0",
"hexo-generator-archive": "^2.0.0",
"hexo-generator-category": "^2.0.0",
"hexo-generator-index": "^4.0.0",
"hexo-generator-tag": "^2.0.0",
"hexo-renderer-ejs": "^2.0.0",
"hexo-renderer-marked": "^7.0.0",
"hexo-renderer-stylus": "^3.0.1",
"hexo-server": "^3.0.0",
"hexo-theme-butterfly": "^5.5.4",
"hexo-theme-landscape": "^1.0.0"
}
}

4
scaffolds/draft.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
tags:
---

4
scaffolds/page.md Normal file
View File

@ -0,0 +1,4 @@
---
title: {{ title }}
date: {{ date }}
---

5
scaffolds/post.md Normal file
View File

@ -0,0 +1,5 @@
---
title: {{ title }}
date: {{ date }}
tags:
---

File diff suppressed because it is too large Load Diff

321
source/_posts/ArchLinux.md Normal file
View File

@ -0,0 +1,321 @@
---
title: ArchLinux
date: 2025-07-28 10:26:05
tags:
---
# WSL
## 安装
### 在线安装
```powershell
wsl --install archlinux
```
# 包管理器 pacman
> [pacman - Arch Linux 中文维基](https://wiki.archlinuxcn.org/wiki/Pacman)
## 安装软件
```shell
pacman -S fastfetch
```
## 更新库
```shell
pacman -Syyu
```
# Vim设置
编辑当前用户下的vim配置文件`~/.vimrc`
```shell
if has('mouse')
set mouse-=a
endif
set number
syntax on
set ignorecase
set t_Co=256
```
# Containerd + Nerdctl
## 安装
**1. 更新系统**
首先,确保你的 Arch Linux 系统是最新的:
```Bash
sudo pacman -Syu
```
**2. 安装 Containerd**
Containerd 是一个核心的容器运行时。它作为 `containerd` 包在官方仓库中提供。
```Bash
sudo pacman -S containerd
```
安装完成后,你需要启动并启用 Containerd 服务,以便它在系统启动时自动运行:
```Bash
sudo systemctl enable --now containerd
```
你可以通过以下命令检查 Containerd 的运行状态:
```Bash
sudo systemctl status containerd
```
确保它显示为 `active (running)`
**3.安装Nerdctl**
```bash
sudo pacman -S nerdctl
```
**4.安装CNI Plugin**
> [Releases · containernetworking/plugins (github.com)](https://github.com/containernetworking/plugins/releases)
从Github上下载
```bash
wget https://github.com/containernetworking/plugins/releases/download/v1.7.1/cni-plugins-linux-amd64-v1.7.1.tgz
```
解压到指定目录`/opt/cni/bin`
```bash
tar -zxvf cni-plugins-linux-amd64-v1.7.1.tgz -C /opt/cni/bin
```
即可完成安装
可以验证下
```bash
nerdctl run -it hello-world
```
不报错,并且输出了结果,说明已经安装好了 CNI Plugin。
![image-20250729101402474](https://rustfs.wenyongdalucky.club:443/hexo/image-20250729101402474.png)
**5.安装Buildkit**
> [Release v0.23.2 · moby/buildkit (github.com)](https://github.com/moby/buildkit/releases/tag/v0.23.2)
下载 BuildKit 二进制文件:
```bash
wget https://github.com/moby/buildkit/releases/download/v0.23.2/buildkit-v0.23.2.linux-amd64.tar.gz
```
解压到临时目录下
```bash
tar -zxvf
```
安装 `buildctl``buildkitd`
将解压后的 `bin` 目录中的 `buildctl``buildkitd` 可执行文件移动到系统 `$PATH` 中的某个目录,例如 `/usr/local/bin`
```bash
sudo mv /tmp/bin/buildctl /usr/local/bin/
sudo mv /tmp/bin/buildkitd /usr/local/bin/
```
验证 `buildctl` 是否在 `$PATH`
```bash
which buildctl
buildctl version
```
如果显示路径和版本信息,说明 `buildctl` 已经正确安装并可执行。
启动 BuildKit 守护进程 (`buildkitd`)
配置为 Systemd 服务
创建一个 systemd service 文件,例如 `/etc/systemd/system/buildkit.service`
```toml
[Unit]
Description=BuildKit Daemon
Documentation=https://github.com/moby/buildkit
[Service]
ExecStart=/usr/local/bin/buildkitd --addr unix:///run/buildkit/buildkitd.sock
Type=notify
Delegate=yes
KillMode=process
# 在某些环境中,可能需要调整用户和组
# User=buildkit
# Group=buildkit
[Install]
WantedBy=multi-user.target
```
保存后,重载 systemd 并启动服务
```bash
sudo systemctl daemon-reload
sudo systemctl enable buildkit # 设置开机自启
sudo systemctl start buildkit
```
检查 BuildKit 状态
```bash
sudo systemctl status buildkit
```
![image-20250729103241234](https://rustfs.wenyongdalucky.club:443/hexo/image-20250729103241234.png)
配置buildkitd镜像加速解决构建时的镜像拉取问题
**BuildKit Daemon 的配置文件 (`buildkitd.toml`)** BuildKit 守护进程 (buildkitd) 可以通过配置文件进行更高级的配置,通常是 `/etc/buildkit/buildkitd.toml`(或 rootless 模式下的 `~/.config/buildkit/buildkitd.toml`)。在这个文件中,可以配置:
- **Registry Mirrors (镜像加速器/代理)** 为特定仓库定义镜像源,例如将 `docker.io` 的请求重定向到私有加速器。
```bash
mkdir -p /etc/buildkit
vim /etc/buildkit/buildkitd.toml
```
添加以下内容
```toml
# registry configures a new Docker register used for cache import or output.
[registry."docker.io"]
# mirror configuration to handle path in case a mirror registry requires a /project path rather than just a host:port
mirrors = ["https://docker.m.daocloud.io",
"https://docker.imgdb.de",
"https://docker-0.unsee.tech",
"https://docker.hlmirror.com",
"https://docker.1ms.run",
"https://cjie.eu.org",
"https://func.ink",
"https://lispy.org",
"https://docker.xiaogenban1993.com"]
# Use plain HTTP to connect to the mirrors.
http = true
```
保存后,重启服务生效
```bash
systemctl restart buildkit
```
## Containerd的config.toml实现镜像加速
> [containerd/docs/cri/config.md at main · containerd/containerd](https://github.com/containerd/containerd/blob/main/docs/cri/config.md#registry-configuration)
如果没有`/etc/containerd/config.toml`,执行以下命令生成默认配置
```bash
sudo containerd config default | sudo tee /etc/containerd/config.toml
```
编辑Containerd的配置文件添加以下镜像配置
```toml
# 找到[plugins.'io.containerd.grpc.v1.cri']配置处
[plugins.'io.containerd.grpc.v1.cri']
disable_tcp_service = true
stream_server_address = '127.0.0.1'
stream_server_port = '0'
stream_idle_timeout = '4h0m0s'
enable_tls_streaming = false
[plugins.'io.containerd.grpc.v1.cri'.x509_key_pair_streaming]
tls_cert_file = ''
tls_key_file = ''
[plugins."io.containerd.grpc.v1.cri".containerd]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
runtime_type = "io.containerd.runc.v2"
[plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc.options]
SystemdCgroup = true
[plugins."io.containerd.grpc.v1.cri".registry] #在这里增加
config_path = "/etc/containerd/certs.d"
```
创建目录`/etc/containerd/certs.d/docker.io`
```shell
mkdir -p /etc/containerd/certs.d/docker.io
```
进入到创建好的目录下,编辑文件`hosts.toml`
```toml
server = "https://docker.io"
[host."https://docker.m.daocloud.io"]
capabilities = ["pull", "resolve"]
[host."https://docker.imgdb.de"]
capabilities = ["pull", "resolve"]
```
然后重启`containerd`服务即可
```shell
systemctl restart containerd
```
## containerd管理命令介绍
使用docker作为容器运行时需要经过多层转换kubelet <-> dockershim <-> docker <-> containerd这会导致连接不稳定和性能下降。K8s从v1.24版本开始不再支持docker容器运行时而是默认使用containerd
切换到containerd之后有以下几种替代docker的管理命令
使用k8s自带的crictl命令。crictl是一个符合CRI接口规范的命令行工具可以用它来检查和管理kubelet节点上的容器运行时和镜像。
使用containerd自带的ctr命令。ctr是一个本地CLI工具可以用它来管理镜像、容器、任务、快照等。因为containerd支持多个命名空间所以ctr命令需要指定命名空间。要管理k8s创建的容器需要使用k8s.io名字空间即ctr -n k8s.io。
使用containerd额外提供的nerdctl工具。nerdctl是一个与docker兼容的containerd的命令行工具需要额外安装。它支持一些docker没有的功能比如延迟拉取镜像、镜像加密等。
使用其他第三方管理容器的开源工具。
## docker、crictl和ctr命令对比
| 命令 | docker | crictlk8s | ctrcontainerd | nerdctlcontainerd |
| ------------------- | ----------------- | -------------------------------- | ---------------------------- | --------------------- |
| 查看运行的容器 | docker ps | crictl ps | ctr task ls/ctr container ls | nerdctl ps |
| 查看镜像 | docker images | crictl images | ctr image ls | nerdctl images |
| 查看容器日志 | docker logs | crictl logs | 无 | nerdctl logs |
| 查看容器信息 | docker inspect | crictl inspect | ctr container info | nerdctl inspect |
| 查看容器资源使用 | docker stats | crictl stats | 无 | nerdctl stats |
| 启动/关闭已有的容器 | docker start/stop | crictl start/stop | ctr task start/kill | nerdctl start/stop |
| 运行一个新的容器 | docker run | 比较麻烦,因为它的最小单元为 Pod | ctr run | nerdctl run |
| 创建一个新的容器 | docker create | 比较麻烦,因为它的最小单元为 Pod | ctr container create | nerdctl create |
| 在容器内部执行命令 | docker exec | crictl exec | 无 | nerdctl exec |
| 删除容器 | docker rm | crictl rm | ctr container rm | nerdctl rm |
| 删除镜像 | docker rmi | crictl rmi | ctr image rm | nerdctl rmi |
| 导入镜像 | docker load | 无 | ctr image import | nerdctl load |
| 导出镜像 | docker save | 无 | ctr image export | nerdctl save |
| 拉取镜像 | docker pull | crictl pull | ctr image pull | nerdctl pull |
| 给镜像打标签 | docker tag | 无 | ctr image tag | nerdctl tag |
| 推送镜像 | docker push | 无 | ctr image push | nerdctl push |
| 构建镜像 | docker build | 无 | 无 | nerdctl build |
需要额外安装buildkit服务
注意ctr和nerdctl命令需要指定名字空间管理k8s创建的容器需要使用k8s.io名字空间即ctr/nerdctl -n k8s.io。

314
source/_posts/C#.md Normal file
View File

@ -0,0 +1,314 @@
---
title: C#
date: 2021-03-23 10:30:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg
---
# 异步编程
## await
在C#中`await`关键字用于等待异步操作完成,并返回其结果。当遇到`await`关键字时C#编译器会暂停当前方法的执行并等待异步操作完成并返回结果。在等待异步操作的过程中C#编译器会继续执行其他代码例如设置变量的值。但是当异步操作完成后C#编译器会继续执行`await`关键字之后的代码。
下面是一个使用`await`关键字的示例,演示了如何等待异步操作完成:
```c#
using System;
using System.Net.Http;
using System.Threading.Tasks;
public class Example
{
public static async Task Main()
{
Console.WriteLine("Start");
var client = new HttpClient();
var response = await client.GetAsync("https://jsonplaceholder.typicode.com/todos/1");
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
Console.WriteLine("End");
}
}
```
在这个示例中,我们使用`HttpClient`类来发送HTTP请求并使用`await`关键字等待响应。在`Main`方法中,我们首先打印"Start",然后使用`HttpClient`类发送GET请求并使用`await`关键字等待响应。在等待的过程中C#编译器会继续执行其他代码但是当响应返回后C#编译器会继续执行`await`关键字之后的代码,即读取响应内容并打印它。最后,我们打印"End"。
总之,当遇到`await`关键字时C#编译器会暂停当前方法的执行并等待异步操作完成并返回结果。在等待异步操作的过程中C#编译器会继续执行其他代码但是当异步操作完成后C#编译器会继续执行`await`关键字之后的代码。
# 枚举
在C#中我们可以使用枚举类型来定义一组相关的常量。枚举类型可以包含一个或多个枚举成员每个枚举成员都有一个名称和一个关联的值。在C# 8.0及以上版本中,我们可以使用`enum`关键字来定义字符串枚举类型。
下面是一个使用字符串枚举的示例:
```c#
enum Color {
Red,
Green,
Blue
}
enum Size {
Small,
Medium,
Large
}
enum Fruit {
Apple = 1,
Banana = 2,
Orange = 3
}
enum Animal {
[Description("狗")]
Dog,
[Description("猫")]
Cat,
[Description("鸟")]
Bird
}
enum ErrorCode {
[EnumMember(Value = "1001")]
InvalidInput,
[EnumMember(Value = "1002")]
Unauthorized,
[EnumMember(Value = "1003")]
NotFound
}
```
在这个示例中,我们定义了四个枚举类型`Color``Size``Fruit``Animal``Color``Size`是普通的枚举类型它们的枚举成员的值默认从0开始递增。`Fruit`是一个带有关联值的枚举类型,它的枚举成员的关联值可以是任何整数类型。`Animal`是一个带有描述信息的枚举类型,它的枚举成员使用了`Description`特性来指定描述信息。`ErrorCode`是一个带有序列化信息的枚举类型,它的枚举成员使用了`EnumMember`特性来指定序列化信息。
在使用枚举类型时,我们可以通过枚举成员的名称来访问枚举成员,例如`Color.Red``Size.Small``Fruit.Banana`等。我们也可以将枚举成员的值转换为字符串,例如`Color.Red.ToString()`会返回字符串`"Red"`
总之C#中的枚举类型可以用于定义一组相关的常量。在C# 8.0及以上版本中,我们可以使用`enum`关键字来定义字符串枚举类型。在使用枚举类型时,我们可以通过枚举成员的名称来访问枚举成员,也可以将枚举成员的值转换为字符串。
# DataTable
## 基础概念
表示一个内存内关系数据的表,如同关系型数据库中的表
## 创建 DataTable
```c#
//引用命名空间
using System.Data;
//创建一个空表,
DataTable dt = new DataTable();
//创建一个名为"Table_New"的空表
DataTable dt = new DataTable("Table_New");
```
## 列 DataColumn
- DataColumn 定义每列的数据类型来确定表的架构
- DataTable中的列可以映射到数据源中的列、包含从表达式计算所得的值、自动递增它们的值或包含主键值
- DataColumn的DataType属性可限制该列的数据类型为整数、字符串或十进制数等但必须将数据类型与数据源中的数据类型匹配。
```shell
//1.创建空列
DataColumn dc = new DataColumn();
dt.Columns.Add(dc);
//2. 提供列名,并对列属性进行设置
DataTable workTable = new DataTable("Customers");
//带列名和类型名
DataColumn workCol = workTable.Columns.Add("CustID", typeof(Int32));
// 对进行属性设置
workCol.AllowDBNull = false; //列的属性设置为不允许 DBNull 值
workCol.Unique = true; //值约束为唯一
workColumn.AutoIncrement = true; //在表中添加新行时自动递增
workColumn.AutoIncrementSeed = 200; //从值 200 开始并以 3 为增量递增的列
workColumn.AutoIncrementStep = 3;
column.DefaultValue = 25;
// 定义主键:唯一地标识表中的每一行
workTable.PrimaryKey = new DataColumn[] {workTable.Columns["CustID"]};
// Or
DataColumn[] columns = new DataColumn[1];
columns[0] = workTable.Columns["CustID"];
workTable.PrimaryKey = columns;
// 创建表达式列:能够包含根据 同一行 中其他列值或根据表中 多行 的列值计算而得的值
//比如 表达式类型 示例
// 比较 “总计 >= 500”
// 计算 "UnitPrice * Quantity"
// 聚合 Sum(Price)
workTable.Columns.Add("Total", typeof(Double));
workTable.Columns.Add("SalesTax", typeof(Double), "Total * 0.086");
```
## 复制DataTable
```c#
objectTable = sourceTable .Copy();//深复制包含DataRow
objectTable = sourceTable .Clone();//浅复制,只复制架构
```
## 复制 DataRow
### ImportDataRow方法
```c#
public void ImportDataRow( DataRow DataRow);
objectTable = sourceTable.clone();//必须先复制表的架构,使具有相同的的列或关系!
foreach (DataRow oRow in sourceTable)
{
objectTable.ImportDataRow(oRow);//在objectTable中添加一个新行并将sourceRow的值复制进去,要求表的结构一样!
}
```
### 自定义复制
```c#
objectTable.Columns.Add ("id");//不需要有一样的架构,只复制自己需要的列!
Object[] myArry = new Object[1];
foreach (DataRow oRow in sourceTable.Rows)
{
tempRow = objectTable.NewRow();//此方法必须调用!
myArry[0] = oRow["id"];//如果myArry中没有源表中的id列的话就会报错
tempRow.ItemArray = myArry;//ItemArray属性为Object类型数组,根据程序的需要需要可自行复制多个列的数据!
objectTable.Rows.Add(tempRow); //此方法必须调用否则DataRow中的数据将不能显示!
}
```
### LoadDataRow方法
```c#
public DataRow LoadDataRow(Object[] values,bool fAcceptChanges);
Object[] newRow = new Object[3];
// 设置对象数组的值
newRow[0] = "Hello";
newRow[1] = "World";
newRow[2] = "two";
DataRow myRow;
ObjectTable.BeginLoadData();
// 将新行添加到表中
myRow = ObjectTable.LoadDataRow(newRow, true);//标志要设置为true,表示添加新行
ObjectTable.EndLoadData();
```
# 反射(Reflection)
> 反射是.NET中的重要机制通过反射可以得到\*.exe或\*.dll等程序集内部的接口、类、方法、字段、属性、特性等信息还可以动态创建出类型实例并执行其中的方法。
>
> 反射指程序可以访问、检测和修改它本身状态或行为的一种能力。
>
> 程序集包含模块,而模块包含类型,类型又包含成员。反射则提供里封装程序集、模块和类型的对象。
>
> 可以使用反射动态地创建类型的实例,将类型绑定到现有对象,或从现有对象中获取类型。然后,可以调用类型的方法或访问其字段和属性。
通过反射获取类型
描述:有三种类型
1. 通过`typeof`获取某个值的类型
```c#
System.Type personType=typeof(Person);
System.Type heroType=typeof(Framework.Hero);
```
2. 通过一个对象获取该对象所对应的类的类型、
```c#
Framework.hero dmxy =new Framework.hero();
System Type=dmxy.GetType();
```
3. 通过类的名称字符串获取对应的类型
```c#
System.Type strType =System.Type.GetType("Person");
System.Type strType =System.Type.GetType("Framework.Hero");
```
## Type类
属性
- Name 数据类型名
- FullName 数据类型的完全限定名
- Namespace 定义数据类型的命名空间名
- IsAbstract 指示该类型是否为数组
- IsArray 指示该类型是否为数组
- IsClass 指示该类型是否为类
- IsEnum 指示该类型是否为枚举
- IsInterface 指示该类型是否为接口
- IsPublic 指示该类型是否为共有的
- IsSealed 指示该类型是否是密封类
- IsValueType 指示该类型是否是密封类
- IsValueType 指示该类型是否是值类型
- BaseType 父类类型
- AssemblyQualifiedName 程序集+命名空间+类名 | 是Type.GetType(str)中的字符串
```csharp
private void ShowTypeField()
{
// 获取类型
Type heroType = typeof(Framework.Hero);
// 查看类型的名字
Console.WriteLine("Name:" + heroType.Name);
//查看类型的全名
Console.WriteLine("FullName:" + heroType.FullName);
//查看程序集名称
Console.WriteLine("Assembly:" + heroType.Assembly);
//加上程序集的全名
Console.WriteLine("Ass-Name:" +heroType.AssemblyQualifiedName);
//获取该类型的父类
Console.WriteLine("BaseType:" + heroType.BaseType.BaseType);
}
```
## 方法
- GetMember(),GerMembers()
1.返回MemberInfo类型用于取得该类的所有成员的信息
2.GetConstructor(),GetConstructors() -返回ConstructorInfo类型用于取得该类构造函数的信息
- GetEvent(),GetEvents()
返回EventInfo类型用于取得该类的事件的信息
- GetInterface(),GetInterfaces()
返回InterfaceInfo类型用于取得该类实现的接口的信息
- GetField(),GetFields()
返回FieldInfo类型用于取得该类的字段成员变量的信息
- GetPropeerty(),GetProperties()
返回ProperyInfo类型用于取得该类的属性的信息
- GetMethod(),GetMethods()
返回MethodInfo类型用于取得该类的方法的信息
# 循环
## foreach
###

199
source/_posts/CSS.md Normal file
View File

@ -0,0 +1,199 @@
---
title: CSS
date: 2024-03-23 10:30:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg
---
# 选择器与属性
## 基本语法结构
CSS的规则通常长这样
```css
选择器 {
属性: 值;
}
```
## 元素选择器
举个例子,如果我们想把网页上所有的`<h1>`标题变成红色:
```css
h1 {
color: red;
}
```
- `h1`**选择器**(选中所有 h1 标签)。
- `color`**属性**(我们要改颜色)。
- `red`**值**(改成红色)。
这叫 **元素选择器**,因为它选中了所有叫 `h1` 的元素。没问题,我们继续!🚀
为了确保你完全掌握了刚才的逻辑,我先揭晓一下刚才小练习的答案。要把 `<p>` 标签的文字变成蓝色,代码是这样的:
CSS
假设你的 HTML 里有这样一个段落标签:
```html
<p>我是一个普通的段落。</p>
```
如果想把这个段落的文字颜色改成 **蓝色 (blue)**
```css
p {
color: blue;
}
```
很简单对吧?这叫 **元素选择器**,因为它选中了所有叫 `p` 的元素。
------
🔥 进阶:如何只选中“某一个”?
在实际做网页时,我们通常不希望**所有**的段落都长得一样。比如,你可能希望有一个段落是特别的“警告信息”。
这时候,我们需要给 HTML 标签起个“名字”或者贴个“标签”。
1. **类 (Class)**
- **作用**:就像给一群人穿上同样的制服。多个元素可以共用同一个 class。
- **CSS 写法**:用 `.` (小圆点) 开头。
2. **ID**
- **作用**:就像身份证号。整个页面里,一个 ID 只能给一个元素用,是唯一的。
- **CSS 写法**:用 `#` (井号) 开头。
## 类选择器
假设我们的 HTML 代码是这样的:
```html
<p class="alert">注意:这是一个警告!</p>
<p>我只是一个路人甲。</p>
```
如果我们只想选中那个带有 `alert` 类的段落,把它的颜色变成 **红色 (red)**CSS 选择器应该怎么写?
**提示**:记得 Class 选择器要用 `.` 开头哦。
```css
.alert {
color: red;
}
```
## ID 选择器
比 Class 更“独一无二”的:**ID**。
在 CSS 里,我们要用 **井号 (#)** 来代表 ID。
```html
<h1 id="main-title">欢迎来到我的主页</h1>
```
如果我们想把这个标题的 **字体大小 (font-size)** 设置为 **40px**,你应该怎么写这段 CSS
**提示**:记得 ID 要用 `#` 开头哦。
```css
#main-title {
font-size: 40px;
}
```
## 后代选择器 (Descendant Selector)
**根据“位置”来选元素**。
有时候,你不想选中“所有的 `a` 标签”,你只想选中“侧边栏 (Sidebar) 里的 `a` 标签”。这就需要用到**后代选择器**。
**语法规则** 我们在两个选择器之间加一个 **空格 (Space)**,表示“里面的”意思。
```css
/* 选中所有在 div 里面的 p 标签 */
div p {
color: green;
}
```
- `div` 是父元素(外面的)。
- `p` 是子元素(里面的)。
- 中间的**空格**很关键!
假设我们有下面这段 HTML里面有两个链接但我们只想修改在 `.nav` 里面的那个链接:
```html
<div class="nav">
<a href="#">我是导航菜单</a>
</div>
<a href="#">我是普通链接</a>
```
选中 **`.nav` 里面的 `a` 标签**,并把它的颜色改成 **橙色 (orange)**
**提示**:记得利用空格来表示层级关系哦!
```css
.nav a {
color: orange;
}
```
## 伪类 (Pseudo-classes)
现在你已经能控制元素“静止”时的样子了,接下来我们要让网页变得更有**互动感**。我们要学习的是 **伪类 (Pseudo-classes)**
最常用的一个伪类是 `:hover`
- **作用**:它是指“当鼠标**悬停**在元素上时”的那个瞬间。
- **语法**:在选择器后面紧跟着写 `:hover` (注意:冒号前面**不能**有空格)。
```css
/* 例子:所有按钮在鼠标放上去时,背景变黑 */
button:hover {
background-color: black;
}
```
```html
<div class="nav">
<a href="#">我是导航菜单</a>
</div>
<a href="#">我是普通链接</a>
```
要求: 当鼠标**悬停**在 `.nav` 里面的 `a` 标签上时,把它的**文字颜色**变成 **绿色 (green)**
**提示**:你需要把刚才学的“后代选择器”和现在的“伪类”结合起来写哦!
```css
.nav a:hover {
color: green;
}
```
# 盒模型
既然你已经学会了怎么给元素“上色”,接下来我们要学怎么控制它们的“身材”和“距离”。
在 CSS 的世界里,**所有的元素(无论是图片、段落还是标题)本质上都是一个矩形的盒子**。
我们要控制这个盒子,主要靠这四个属性(由内向外):
1. **内容 (Content)**:盒子的核心,就是文字或图片本身。
2. **内边距 (Padding)**:内容和边框之间的空间。就像快递盒子里的气泡膜,保护内容不紧贴着纸箱壁。
3. **边框 (Border)**:盒子的边缘。
4. **外边距 (Margin)**:盒子和其他盒子之间的距离。就像人与人之间的社交距离。
## CSS Text(文本)

View File

@ -0,0 +1,226 @@
---
title: Claude Code
date: 2026-02-27 14:05:22
tags: [AI, 效率工具]
categories: 开发工具
---
## 简介
Claude Code 是 Anthropic 官方推出的 CLI 工具,让你在终端中直接使用 Claude AI 助手完成软件工程任务。
## 安装
### 通过 npm 安装
确保已安装 Node.js推荐 v18+ 版本),然后使用 npm 全局安装:
```bash
npm install -g @anthropic-ai/claude-code
```
或者使用 yarn
```bash
yarn global add @anthropic-ai/claude-code
```
使用 pnpm
```bash
pnpm add -g @anthropic-ai/claude-code
```
### 验证安装
```bash
claude -v
```
## 配置
### 1. 获取 API Key
访问 [Anthropic Console](https://console.anthropic.com/) 注册账号并获取 API Key。
### 2. 配置 API Key
有两种方式配置 API Key
**方式一:环境变量(推荐)**
在命令行中设置环境变量:
```bash
# Windows PowerShell
$env:ANTHROPIC_API_KEY="your-api-key-here"
# Windows CMD
set ANTHROPIC_API_KEY=your-api-key-here
# macOS/Linux
export ANTHROPIC_API_KEY="your-api-key-here"
```
**方式二:配置文件**
```json
{
"env": {
"ANTHROPIC_AUTH_TOKEN": "YOUR_API_KEY",
"ANTHROPIC_BASE_URL": "https://coding.dashscope.aliyuncs.com/apps/anthropic",
"ANTHROPIC_MODEL": "qwen3.5-plus"
}
}
```
配置文件位置:
- Windows: `%USERPROFILE%\.claude\settings.json`
- macOS/Linux: `~/.claude\settings.json`
### 3. 配置模型
可以指定使用的 Claude 模型:
```bash
# 使用最新模型
claude --model claude-opus-4-6
# 使用 Sonnet 模型
claude --model claude-sonnet-4-6
# 使用 Haiku 模型
claude --model claude-haiku-4-5-20251001
```
## 常用命令
| 命令 | 说明 |
|------|------|
| `claude` | 启动交互式对话 |
| `claude -p "问题"` | 直接提问并获取答案 |
| `claude --model <模型名>` | 指定模型 |
| `claude --version` | 查看版本 |
| `claude --help` | 查看帮助 |
## MCP 配置
MCP (Model Context Protocol) 允许 Claude 连接外部服务和工具。
### 添加 MCP Server
```bash
# 添加 fetch MCP 服务(用于网页内容抓取)
claude mcp add fetch -- uvx mcp-server-fetch
```
### 常用 MCP 服务
| 服务 | 命令 | 说明 |
|------|------|------|
| fetch | `claude mcp add fetch -- uvx mcp-server-fetch` | 网页内容抓取 |
| filesystem | `claude mcp add filesystem -- npx -y @anthropic-ai/mcp-server-filesystem` | 文件系统访问 |
| git | `claude mcp add git -- npx -y @anthropic-ai/mcp-server-git` | Git 操作 |
配置好的 MCP 配置文件实例 `~/.claude.json`
```json
{
"mcpServers": {
"context7": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@upstash/context7-mcp@latest"
],
"env": {
"DEFAULT_MINIMUM_TOKENS": "10000"
}
},
"Sequential Thinking": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@modelcontextprotocol/server-sequential-thinking"
],
"env": {}
},
"Playwright": {
"command": "cmd",
"args": [
"/c",
"npx",
"-y",
"@executeautomation/playwright-mcp-server"
],
"env": {}
},
"Fetch": {
"command": "cmd",
"args": [
"/c",
"uvx",
"mcp-server-fetch"
],
"env": {}
},
"MySQL": {
"command": "cmd",
"args": [
"/c",
"uvx",
"--from",
"mysql-mcp-server",
"mysql_mcp_server"
],
"env": {
"MYSQL_HOST": "127.0.0.1",
"MYSQL_PORT": "3306",
"MYSQL_USER": "root",
"MYSQL_PASSWORD": "123456",
"MYSQL_DATABASE": "db"
}
}
},
}
```
### 查看已配置的 MCP
```bash
claude mcp list
```
### 移除 MCP
```bash
claude mcp remove <服务名>
```
## 使用示例
```bash
# 启动交互模式
claude
# 直接提问
claude -p "解释一下什么是闭包"
# 分析代码文件
claude -p "这个函数有什么问题" ./src/index.js
# 使用特定模型
claude --model claude-opus-4-6 -p "帮我重构这段代码"
```
## 注意事项
- API 调用会产生费用,请合理使用
- 建议将 API Key 设置为环境变量避免每次输入
- 不同模型的价格和能力有所不同,根据需要选择

View File

@ -0,0 +1,395 @@
---
title: Docker-Compose
date: 2024-04-01 11:02:56
tags:
---
# 安装
## Linux
### 在线安装
首先从github 上下载 https://github.com/docker/compose/releases
下载 docker-compose-linux-x86_64
上传到linux中
```shell
mv docker-compose-linux-x86_64 /usr/local/bin/
cd /usr/local/bin/
mv docker-compose-linux-x86_64 docker-compose
chmod +x /usr/local/bin/docker-compose
ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
```
检查是否安装成功
```shell
docker-compose version
```
### 离线安装 - 可能会出现下载问题
运行以下命令以下载 Docker Compose 的当前稳定版本:
```shell
sudo curl -L "https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
```
将可执行权限应用于二进制文件:
```shell
sudo chmod +x /usr/local/bin/docker-compose
```
创建软链:
```shell
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
```
测试是否安装成功:
```shell
docker-compose version
```
# 命令
### 运行
```shell
# 默认以所在目录名为Name -d 为后台运行
docker compose up -d
# 指定Name运行
docker compose --project-name dify-docker up -d
```
## 停止并清理容器
默认情况下不会清理挂载卷,除非额外指定 -v
```shell
# 进入到之前启动容器的所在目录
docker compose down
# 指定Name
docker compose --project-name dify-docker down
```
## 修改Dockerfile后通过Docker Compose重新构建镜像
1. **修改 `Dockerfile`**
2. 进入到 `Dockerfile``docker-compose.yml` 所在的目录。
3. **构建新镜像:**
Bash
```
docker-compose build jenkins
```
(如果遇到问题或想完全重来,可以加 `--no-cache``docker-compose build --no-cache jenkins`
4. **使用新镜像启动容器:**
Bash
```
docker-compose up -d --force-recreate jenkins
```
执行这些步骤后,你的 Jenkins 容器就会运行在新修改并构建的 Docker 镜像上,其中包含了你所有新增的工具和配置。
# 集群搭建
## Flink 集群
1. 首先启动flink 容器 JobManager、TaskManager 两个容器将配置文件复制出来方便挂载
```shell
docker network create flink-network
docker run \
-itd \
--name=jobmanager \
--publish 8081:8081 \
--network flink-network \
--env FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager" \
flink:1.16.0-scala_2.12-java8 jobmanager
docker run \
-itd \
--name=taskmanager \
--network flink-network \
--env FLINK_PROPERTIES="jobmanager.rpc.address: jobmanager" \
flink:1.16.0-scala_2.12-java8 taskmanager
```
2. 创建本地卷挂载目录,拷贝文件
```shell
mkdir -p /usr/local/flink-docker/jobmanager
mkdir -p /usr/local/flink-docker/taskmanager
docker cp jobmanager:/opt/flink/lib /usr/local/flink-docker/jobmanager
docker cp jobmanager:/opt/flink/log /usr/local/flink-docker/jobmanager
docker cp jobmanager:/opt/flink/conf /usr/local/flink-docker/jobmanager
docker cp taskmanager:/opt/flink/lib /usr/local/flink-docker/taskmanager
docker cp taskmanager:/opt/flink/log /usr/local/flink-docker/taskmanager
docker cp taskmanager:/opt/flink/conf /usr/local/flink-docker/taskmanager
```
3. 搭建集群
docker-compose.yaml 文件
```yaml
version: "2.2"
services:
jobmanager:
image: flink:1.16.0-scala_2.12-java8
container_name: jobmanager-1
expose:
- "6123"
ports:
- "8081:8081"
command: jobmanager
volumes:
- /usr/local/flink-docker/jobmanager/conf:/opt/flink/conf
- /usr/local/flink-docker/jobmanager/lib:/opt/flink/lib
- /usr/local/flink-docker/jobmanager/log:/opt/flink/log
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: jobmanager
parallelism.default: 2
#web.upload.dir: /opt/flink/target
networks:
- flink-network
taskmanager:
image: flink:1.16.0-scala_2.12-java8
container_name: taskmanager-1
depends_on:
- jobmanager
command: taskmanager
scale: 1
volumes:
- /usr/local/flink-docker/taskmanager/conf:/opt/flink/conf
- /usr/local/flink-docker/taskmanager/lib:/opt/flink/lib
- /usr/local/flink-docker/taskmanager/log:/opt/flink/log
environment:
- |
FLINK_PROPERTIES=
jobmanager.rpc.address: jobmanager
taskmanager.numberOfTaskSlots: 8
parallelism.default: 2
networks:
- flink-network
networks:
flink-network:
external: true
```
4. 启动集群
```shell
docker-compose up -d
```
## Kafka 集群
```shell
docker network create kfk-network
```
docker -compose.yaml
```yaml
services:
kafka1:
image: 'bitnami/kafka:3.6.1'
ports:
- '19092:19092'
environment:
- KAFKA_KRAFT_CLUSTER_ID=EX5bq5NfRe2IX1nhxrSO6g
- KAFKA_CFG_NODE_ID=1
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_LISTENERS=INTERNAL://:9092, EXTERNAL://:19092, CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka1:9092, EXTERNAL://<宿主机IP>:19092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT, EXTERNAL:PLAINTEXT, CONTROLLER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka1:9093, 2@kafka2:9093, 3@kafka3:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL
networks:
- kfk-network
kafka2:
image: 'bitnami/kafka:3.6.1'
ports:
- '29092:19092'
environment:
- KAFKA_KRAFT_CLUSTER_ID=EX5bq5NfRe2IX1nhxrSO6g
- KAFKA_CFG_NODE_ID=2
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_LISTENERS=INTERNAL://:9092, EXTERNAL://:19092, CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka2:9092, EXTERNAL://<宿主机IP>:29092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT, EXTERNAL:PLAINTEXT, CONTROLLER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka1:9093, 2@kafka2:9093, 3@kafka3:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL
networks:
- kfk-network
kafka3:
image: 'bitnami/kafka:3.6.1'
ports:
- '39092:19092'
environment:
- KAFKA_KRAFT_CLUSTER_ID=EX5bq5NfRe2IX1nhxrSO6g
- KAFKA_CFG_NODE_ID=3
- KAFKA_CFG_PROCESS_ROLES=broker,controller
- KAFKA_CFG_LISTENERS=INTERNAL://:9092, EXTERNAL://:19092, CONTROLLER://:9093
- KAFKA_CFG_ADVERTISED_LISTENERS=INTERNAL://kafka3:9092, EXTERNAL://<宿主机IP>:39092
- KAFKA_CFG_LISTENER_SECURITY_PROTOCOL_MAP=INTERNAL:PLAINTEXT, EXTERNAL:PLAINTEXT, CONTROLLER:PLAINTEXT
- KAFKA_CFG_CONTROLLER_QUORUM_VOTERS=1@kafka1:9093, 2@kafka2:9093, 3@kafka3:9093
- KAFKA_CFG_CONTROLLER_LISTENER_NAMES=CONTROLLER
- KAFKA_CFG_INTER_BROKER_LISTENER_NAME=INTERNAL
networks:
- kfk-network
kafka-ui:
image: provectuslabs/kafka-ui:master
container_name: kafka-ui
ports:
- "38080:8080"
restart: always
environment:
- KAFKA_CLUSTERS_0_NAME=local
- KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS=kafka1:9092,kafka2:9092,kafka3:9092
- KAFKA_CLUSTERS_0_READONLY=true
depends_on:
- kafka1
- kafka2
- kafka3
networks:
- kfk-network
networks:
kfk-network:
external: true
```
# 容器搭建
## Gitea
docker-compose.yaml
```yaml
networks:
gitea:
external: false
services:
server:
image: gitea/gitea:1.23
container_name: gitea
environment:
- USER_UID=1000
- USER_GID=1000
- DB_TYPE=mysql
- DB_HOST=<ip>:33061
- DB_NAME=gitea
- DB_USER=gitea
- DB_PASSWD=Wyd210213
restart: always
networks:
- gitea
volumes:
- /usr/local/docker/gitea:/data
- /etc/timezone:/etc/timezone:ro
- /etc/localtime:/etc/localtime:ro
ports:
- "3000:3000"
- "222:22"
```
## Oracle-12C
docker-compose.yaml
```yaml
services:
server:
image: truevoly/oracle-12c
container_name: oracle-12c
volumes:
- /etc/localtime:/etc/localtime:ro
- /etc/timezone:/etc/timezone:ro
- /var/oracle:/u01/app/oracle
ports:
- "2122:22"
- "1521:1521"
- "9090:8080"
```
首先创建挂载目录,并赋予权限
```bash
mkdir -p /var/oracle && chmod 777 /var/oracle
```
启动
```bash
docker compose up -d
```
连接Oracle数据库
```yml
hostname: localhost #主机名
port: 1521 #端口号
sid: xe
service name: xe #服务名
username: system #用户名
password: oracle #密码
```
```bash
sqlplus system/oracle@localhost:1521/xe
sqlplus /nolog
conn sys/oracle@localhost:1521/xe as sysdba
```
使用 `sqlplus / as sysdba`登录
```bash
su - oracle
export ORACLE_HOME=/u01/app/oracle/product/12.1.0/xe
export ORACLE_SID=xe
export PATH=$ORACLE_HOME/bin:$PATH
sqlplus / as sysdba
```
环境变量永久生效
```bash
echo 'export ORACLE_HOME=/u01/app/oracle/product/12.1.0/xe' >> ~/.bashrc
echo 'export ORACLE_SID=xe' >> ~/.bashrc
echo 'export PATH=$ORACLE_HOME/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
```

1584
source/_posts/Docker.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

60
source/_posts/Flink.md Normal file
View File

@ -0,0 +1,60 @@
---
title: Flink
date: 2024-04-02 14:39:41
tags:
---
## 流处理
在自然环境中,数据的产生原本就是流式的。无论是来自 Web 服务器的事件数据,证券交易所的交易数据,还是来自工厂车间机器上的传感器数据,其数据都是流式的。但是当你分析数据时,可以围绕 *有界流**bounded*)或 *无界流**unbounded*)两种模型来组织处理数据,当然,选择不同的模型,程序的执行和处理方式也都会不同。
![Bounded and unbounded streams](https://nightlies.apache.org/flink/flink-docs-release-1.16/fig/bounded-unbounded.png)
**批处理**是有界数据流处理的范例。在这种模式下,你可以选择在计算结果输出之前输入整个数据集,这也就意味着你可以对整个数据集的数据进行排序、统计或汇总计算后再输出结果。
**流处理**正相反,其涉及无界数据流。至少理论上来说,它的数据输入永远不会结束,因此程序必须持续不断地对到达的数据进行处理。
在 Flink 中,应用程序由用户自定义**算子**转换而来的**流式 dataflows** 所组成。这些流式 dataflows 形成了有向图,以一个或多个**源**source开始并以一个或多个**汇**sink结束。
![A DataStream program, and its dataflow.](https://nightlies.apache.org/flink/flink-docs-release-1.16/fig/program_dataflow.svg)
通常,程序代码中的 transformation 和 dataflow 中的算子operator之间是一一对应的。但有时也会出现一个 transformation 包含多个算子的情况,如上图所示。
Flink 应用程序可以消费来自消息队列或分布式日志这类流式数据源(例如 Apache Kafka 或 Kinesis的实时数据也可以从各种的数据源中消费有界的历史数据。同样Flink 应用程序生成的结果流也可以发送到各种数据汇中。
![Flink application with sources and sinks](https://nightlies.apache.org/flink/flink-docs-release-1.16/fig/flink-application-sources-sinks.png)
# DataStream API介绍和示例
#### Flink程序运行流程
###### 1. 获取执行环境
> getExecutionEnvironment()
> createLocalEnvironment()
> createRemoteEnvironment(String host, int port, String... jarFiles)
###### 2. 加载创建初始化数据
> readTextFile()
> addSource
> ..
###### 3. 对数据在transformation operator
> map
> flatMap
> filter
> ..
###### 4. 指定计算结果的输出位置 sink
> print()
> writeAdText(String path)
> addSink
> ..
###### 5. 触发程序执行 execute
> env.execute()
> 在sink是print时不需要显示execute否则会报错。因为在print方法里已经默认调用了execute。

View File

@ -0,0 +1,186 @@
---
title: Git Cherry-Pick
date: 2025-12-11 23:02:39
tags:
---
# Git Cherry-Pick 详解文档
## 1. 概述
`git cherry-pick` 是 Git 中一个强大的命令,用于**将一个或多个指定的提交commits从一个分支“摘取”并应用到当前分支上**。它不会合并整个分支,而是只复制你指定的提交变更。
> 简单理解:**“只拿我想要的改动,不拿整个分支”**。
## 2. 基本语法
```bash
git cherry-pick <commit-hash>
git cherry-pick <commit1> <commit2> ...
git cherry-pick <start-commit>^..<end-commit> # 摘取一个范围(含 start 和 end
```
- `<commit-hash>`:要摘取的提交的完整或简短哈希值
- 可以一次指定多个提交Git 会按顺序逐个应用
- 提交顺序很重要:默认按你提供的顺序执行(不是按时间顺序)
## 3. 典型使用场景
### ✅ 场景 1修复补丁快速移植
> 在 `dev` 分支上修复了一个严重 bug需要紧急同步到 `release` 分支,但 `dev` 上还有其他未测试功能不能合并。
```bash
git checkout release
git cherry-pick a1b2c3d # 仅应用修复提交s
```
### ✅ 场景 2从 feature 分支提取部分功能
> 某个功能分支开发了多个特性,但当前只希望上线其中一部分。
### ✅ 场景 3从其他远程仓库如上游选取特定提交
> 例如你 fork 了一个开源项目,原项目修复了某个问题,你想只拿这个修复。
## 4. 工作流程示例
假设你有两个分支:`main``feature`
```bash
# 当前在 main 分支
git checkout main
# 查看 feature 分支的提交历史
git log feature --oneline
# 输出:
# abc1234 Add login feature
# def5678 Fix typo
# ghi8901 Update README
# 只想把 "Fix typo" 提交应用到 main
git cherry-pick def5678
```
执行后,`main` 分支会产生一个**新的提交**(哈希不同),但内容与 `def5678` 相同。
> ⚠️ 注意cherry-pick 会创建**新提交**new commit with new hash不是“移动”原提交。
## 5. 常用选项
| 选项 | 说明 |
| --------------------- | ------------------------------------------------------------ |
| `-n``--no-commit` | 应用变更但**不自动提交**,只暂存改动(便于手动调整后提交) |
| `-e``--edit` | 在提交前打开编辑器,允许修改提交信息 |
| `-x` | 在提交信息末尾自动添加 `(cherry picked from commit ...)`,用于追踪来源(推荐用于公共仓库) |
| `--ff` | 如果当前分支是目标提交的直接后代,则 fast-forward极少用 |
| `--abort` | 放弃整个 cherry-pick 操作,回到操作前状态 |
| `--continue` | 解决冲突后继续执行剩余的 cherry-pick |
## 6. 冲突处理
如果 cherry-pick 的变更与当前分支有冲突:
1. Git 会暂停操作,并提示冲突文件
2. 手动解决冲突(像 merge 一样)
3. 使用 `git add <file>` 标记冲突已解决
4. 执行:
```bash
git cherry-pick --continue
```
5. 如果想放弃:
```bash
git cherry-pick --abort
```
## 7. 注意事项与最佳实践
### ⚠️ 不要滥用 cherry-pick
- 频繁 cherry-pick 会导致提交历史混乱、重复提交
- 优先考虑 `git merge``git rebase` 来保持清晰历史
### ✅ 使用 `-x` 标记来源
```bash
git cherry-pick -x abc1234
```
生成的提交信息会包含来源,便于追溯。
### ✅ 范围摘取时注意顺序
```bash
# 正确:从旧到新
git cherry-pick A^..C # 包含 A, B, C按时间顺序
# 错误:顺序颠倒可能导致冲突
git cherry-pick C B A
```
### ✅ 摘取前先 fetch
如果从远程分支摘取,务必先:
```bash
git fetch origin
# 或
git fetch your-remote-name
```
### ✅ 避免在公共分支上 cherry-pick 后 force push
这会导致协作混乱。
## 8. 与类似命令对比
| 命令 | 作用 | 是否创建新提交 | 是否保留历史 |
| ----------------- | -------------- | ---------------------------- | -------------------- |
| `git cherry-pick` | 摘取指定提交 | ✅ 是 | ❌ 不保留原分支上下文 |
| `git merge` | 合并整个分支 | ✅(通常是一个 merge commit | ✅ 保留完整历史 |
| `git rebase` | 变基,重放提交 | ✅(新提交) | ✅ 但线性化历史 |
## 9. 实用技巧
### 摘取多个连续提交
```bash
# 摘取从 A 到 C 的所有提交(包含 A 和 C
git cherry-pick A^..C
```
### 仅应用改动,稍后提交
```bash
git cherry-pick -n abc1234
# 修改文件或拆分提交
git add .
git commit -m "Custom message"
```
### 查看哪些提交尚未被 cherry-pick用于同步
```bash
git log origin/main..feature --oneline
# 列出 feature 有但 main 没有的提交
```
## 10. 总结
`git cherry-pick` 是一个**精准、灵活但需谨慎使用**的工具,适用于:
- 紧急修复移植
- 选择性功能集成
- 跨仓库补丁同步
> 📌 **原则:用完即走,不作为日常集成手段。**

387
source/_posts/Git.md Normal file
View File

@ -0,0 +1,387 @@
---
title: Git
date: 2023-10-23 10:30:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/67239FBB-E15D-4F4F-8EE8-0F1C9F3C4E7C.jpeg
---
# Git
## Git 全局设置:
```shell
git config --global user.name "username"
git config --global user.email "email"
```
## 拉取项目
```shell
git clone <url>
```
## 创建 git 仓库:
```shell
mkdir aaa
cd aaa
git init
touch README.md
git add README.md
# 添加当前目录下全部文件
git add .
git commit -m "first commit"
git remote add origin https://gitee.com/wenyongda/aaa.git
git push -u origin master
```
## 已有仓库
```shell
cd existing_git_repo
git remote add origin https://gitee.com/wenyongda/aaa.git
git push -u origin master
```
## 查看作者名称、作者邮箱
```shell
git config user.name
git config user.email
git config --list
```
## 解决Git提交失败提示用户密码错误
remote: [session-8b4b799a] wenyongda: Incorrect username or password (access token)
首先查看C盘下gitconfig配置文件配置
```properties
[core]
autocrlf = true
[user]
name = 文永达
email = bmdzh11713@163.com
[difftool "sourcetree"]
cmd = 'C:/Program Files/TortoiseSVN/bin/TortoiseMerge.exe' \"$LOCAL\" \"$REMOTE\"
[mergetool "sourcetree"]
cmd = 'C:/Program Files/TortoiseSVN/bin/TortoiseMerge.exe' -base:\"$BASE\" -mine:\"$LOCAL\" -theirs:\"$REMOTE\" -merged:\"$MERGED\"
trustExitCode = true
[credential "https://gitee.com"]
provider = generic
```
查看user下的password正不正确如果没有设置转到控制面板
Windows下 `Win + R`运行输入`control`
用户账户 -> 凭据管理器 -> 管理 Windows 凭据 -> 普通凭据
编辑 git:https://gitee.com下的用户名和密码
如果没有手动添加并在Git Bash中执行
```shell
git config --global credential.helper wincred
```
## 储藏 (Stash)
```shell
git stash
git stash list
```
## SSH提交
1. SSH 秘钥默认储存在账户的主目录下的 ~/.ssh 目录
如:`C:\Users\用户\.ssh\`
查看是否包含id_rsa和id_rsa.pub(或者是id_dsa和id_dsa.pub之类成对的文件),有`.pub 后缀的文件`就是**公钥**,另一个文件则是密钥。
如果有这两个文件则跳过1.2;如果没有这两个文件,甚至.ssh目录也没有则需要用ssh-keygen 来创建
2. ###### 生成密钥信息
`.ssh 目录`下右键打开[Git Bash](https://so.csdn.net/so/search?q=Git Bash&spm=1001.2101.3001.7020)(.ssh目录不存在则在任一目录下操作或者手动创建该目录)
3. ###### 生成密钥
> ssh-keygen -t rsa -C "注释信息一般为邮箱your_email@youremail.com"
4. 在~/.ssh/下会生成两个文件id_rsa和id_rsa.pub
> id_rsa是私钥
>
> id_rsa.pub是公钥
5. SourceTree配置
![image-20250416142637966](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416142637966.png)
启动 **PuTTY Key Generator**
6. 依次点击
![image-20250416151051085](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416151051085.png)
7. PPKfile version 选择 2
![image-20250416151128595](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416151128595.png)
8. 选择之前生成的id_rsa
![image-20250416151307312](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416151307312.png)
9. 出现如下,选择 **Save private key** 保存秘钥
![image-20250416151416275](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416151416275.png)
10. 保存到 ~/.ssh 目录即可
![image-20250416151503685](D:\source\repos\XiaodaBlogSource\source\_posts\Git\image-20250416151503685.png)
11. 在 **Sourcetree****工具** -> **选项** 中选择刚刚保存的 ppk文件即可
## 变更SSH pubKey comment
> [Change an SSH key comment](https://www.commands.dev/workflows/change_ssh_key_comment)
```
# new_comment 新的注释
# ssh_key_path ssh私钥路径 ~/.ssh/id_rsa
ssh-keygen -c -C "new_comment" -f ssh_key_path
```
## 还原变更
```shell
# 指定文件或目录
git checkout -- <filename or directory>
```
## 撤回(重置)本地仓库提交
若想把已经提交到本地仓库的提交再撤回到本地,通常有几种情况和对应的命令。最常用的是 `git reset`
`git reset` 命令可以用来将当前分支的 `HEAD` 指针移动到指定的提交,并根据不同的模式来处理**暂存区**和**工作目录**。
- ### 1. `git reset --soft <commit>`
- **作用:**`HEAD` 指针移动到指定的 `<commit>`,但**保留**工作目录和暂存区的内容。这意味着你的文件会回到该 `<commit>` 时的状态,但是你当前的修改和暂存的修改都会保留。
- **适用场景:** 你提交了一个 commit但是发现提交信息写错了或者少提交了一些文件。你可以用 `--soft` 回退到上一个 commit然后修改文件重新提交。
- **示例:**
```Bash
git reset --soft HEAD~1
```
这会将 `HEAD` 回退到上一个提交(`HEAD~1` 表示上一个提交)。
- ### 2. `git reset --mixed <commit>` (默认模式)
- **作用:**`HEAD` 指针移动到指定的 `<commit>`,并**清空**暂存区,但**保留**工作目录的内容。这意味着你的文件会回到该 `<commit>` 时的状态,你当前的修改会保留在工作目录中(未暂存)。
- **适用场景:** 你提交了一个 commit但是发现这个 commit 的内容不对,想撤销提交,并重新暂存和提交。
- **示例:**
```Bash
git reset HEAD~1
# 或者 git reset --mixed HEAD~1
```
这会将 `HEAD` 回退到上一个提交,并将暂存区清空,但保留文件在工作目录。
- ### 3. `git reset --hard <commit>`
- **作用:**`HEAD` 指针移动到指定的 `<commit>`,并**清空**暂存区和**丢弃**工作目录的所有修改。这意味着你的仓库会完全回到该 `<commit>` 时的状态,所有在该 `<commit>` 之后的修改都会丢失,**这是最危险的操作**。
- **适用场景:** 你想完全放弃当前分支上的所有最新修改和提交,回到之前的某个干净的状态。
- **示例:**
```Bash
git reset --hard HEAD~1
```
这会将 `HEAD` 回退到上一个提交,并丢弃所有工作目录和暂存区的修改。
## 如何找到 `<commit>` 的 ID
你可以使用 `git log` 命令来查看提交历史并找到你想要回退到的提交的哈希值commit ID
Bash
```
git log
```
`git log` 会显示类似这样的信息:
```
commit abcdef1234567890abcdef1234567890abcdef (HEAD -> master)
Author: Your Name <your.email@example.com>
Date: Fri Aug 1 13:20:00 2025 +0800
最新的提交
commit fedcba0987654321fedcba0987654321fedcba (origin/master)
Author: Another Name <another.email@example.com>
Date: Thu Jul 31 10:00:00 2025 +0800
之前的提交
```
你可以复制 `commit` 后面的哈希值(例如 `fedcba0987654321...`)来替换上面的 `<commit>`
## git 如何关闭commit时的语法检测 ---husky
1 报错提示
  git commit提交时报错如下
  husky+>+pre-commit+(node+v14.18.2)
2 解决方案
  1卸载husky。只要把项目的package.json文件中devDependencies节点下的husky库删掉然后重新npm i 一次即可。或者直接在项目根目录下执行npm uninstall husky --save也可以再次提交自动化测试功能就屏蔽掉
  2进入项目的.git文件夹(文件夹默认隐藏,可先设置显示或者命令ls查找),再进入hooks文件夹,删除pre-commit文件,重新git commit -m xxx git push即可
  3将git commit -m “XXX” 改为 git commit --no-verify -m “xxx”
# github
## push/pull老是超时解决方法
**设置针对github.com本身(如果你需要代理的仓库都是github上面的只要设置这个)的代理:**
```shell
#只对github.com
# 找到自己的代理的port的4个数字的端口就行不一定是1080口的
git config --global http.https://github.com.proxy socks5://127.0.0.1:7890
#上面是别人的如果你的代理是http类型的如下设置
git config --global http.https://github.com.proxy 'http://127.0.0.1:代理的port'
#取消代理
git config --global --unset http.https://github.com.proxy
```
**针对所有仓库(包含github.com之外的仓库的代理)**
```shell
# 找到自己的代理的port的4个数字的端口就行不一定是1080口的
git config --global http.proxy 'socks5://127.0.0.1:1080'
git config --global https.proxy 'socks5://127.0.0.1:1080'
#上面是别人的如果你的代理是http类型的如下设置
# 找到自己的代理的port的4个数字的端口就行不一定是1080口的
git config --global http.proxy 'http://127.0.0.1:代理的port'
git config --global https.proxy 'http://127.0.0.1:代理的port'
```
## 解决remote: Support for password authentication was removed on August 13, 2021.
1. Github 头像处 **Settings**
2. 左侧最下边 **Developer settings**
3. 左侧找到 **Personal access tokens**
4. 点击展开,找到 **Tokens (classic)**
5. 右侧下拉框 **Generate new token (classic)**
6. 按以下配置
![在这里插入图片描述](D:\source\repos\XiaodaBlogSource\source\_posts\Git\2ceb11682cc7230cf1220ecb78e548b5.png)
7. 得到 token
![在这里插入图片描述](D:\source\repos\XiaodaBlogSource\source\_posts\Git\76ad4bb4370c7ae798f7b92c25859901.png)
8. 修改现有项目的url
```shell
git remote set-url origin https://<your_token>@github.com/<USERNAME>/<REPO>.git
```
# Gitea
## 备份
数据库备份至postgresql
```shell
docker exec -u git -it -w /tmp gitea bash -c '/app/gitea/gitea dump --database postgres --config /data/gitea/conf/app.ini'
```
# Sourcetree
这里主要介绍两种最常用的场景,对应 `git reset --soft``git reset --hard` 的效果。
## 撤回(重置)本地仓库提交
### 1. 撤销最近一次提交(保留修改以便重新提交)
这类似于 `git reset --soft HEAD~1``git reset --mixed HEAD~1` 的效果,目的是撤销提交,但保留你的文件修改。
1. **打开日志/历史记录:** 在 SourceTree 界面的左侧,找到你的仓库,然后点击顶部的“日志/历史”或“历史记录”选项卡。
2. **定位要撤销的提交:** 在提交历史列表中,找到你想撤销的**前一个**提交(也就是你希望回退到的那个提交)。
3. **右键点击并选择:** 右键点击该提交。
4. **选择“将当前分支重置到此提交”:** 在弹出的菜单中选择 **“将当前分支重置到此提交”** (Reset current branch to this commit)。
![image-20250801140709918](https://rustfs.wenyongdalucky.club:443/hexo/image-20250801140709918.png)
5. **选择重置模式:** 此时会出现一个弹窗,让你选择重置模式:
- **Soft软重置**:勾选这个选项。它会将 `HEAD` 指针移动到选定的提交,**保留**工作目录和暂存区的内容。这意味着你的修改还在,只是不再是提交的一部分。
- **Mixed混合重置**:这是默认选项。它会将 `HEAD` 指针移动到选定的提交,**清空**暂存区,但**保留**工作目录的内容。
- **注意:** 如果你只想撤销提交,并打算修改后重新提交,通常选择 **Soft****Mixed** 都可以,根据你是否希望文件保留在暂存区。对于“撤回本地仓库提交”这个需求,通常是希望保留修改以便重新操作。
![image-20250801140733960](https://rustfs.wenyongdalucky.club:443/hexo/image-20250801140733960.png)
6. **确认:** 点击“确定”或“Reset”按钮。
完成这些步骤后,你会发现最新的那个提交消失了,而你工作目录中的文件依然保留了修改内容,你可以重新暂存并提交。
------
### 2. 彻底撤销最近一次提交(丢弃所有修改)
这类似于 `git reset --hard HEAD~1` 的效果,会完全丢弃最近提交的所有修改。**请务必谨慎使用此选项,因为它会丢失数据!**
1. **打开日志/历史记录:** 同样,在 SourceTree 中打开“日志/历史”选项卡。
2. **定位要撤销的提交:** 找到你想撤销的**前一个**提交。
3. **右键点击并选择:** 右键点击该提交。
4. **选择“将当前分支重置到此提交”:** 在弹出的菜单中选择 **“将当前分支重置到此提交”** (Reset current branch to this commit)。
5. **选择重置模式:** 在弹窗中,选择 **“Hard硬重置”**。
6. **确认:** 点击“确定”或“Reset”按钮。SourceTree 还会再次弹出警告,提醒你这将丢失数据,请确认。
执行此操作后,你的仓库状态会完全回溯到选定的提交,所有后续的修改和提交都将消失。
------
- **未推送的提交:** 上述操作主要用于**尚未推送到远程仓库**的本地提交。如果你已经将提交推送到远程,使用 `git reset` 会导致本地和远程历史不一致,从而引发问题。对于已推送的提交,通常应该使用 **“逆转提交”Revert Commit**,它会创建一个新的提交来撤销之前的修改,而不是改写历史。
- **Revert逆转操作** 如果你希望逆转某个已推送的提交,可以在 SourceTree 中右键点击该提交,然后选择 **“逆转提交 <哈希值>”** (Revert commit <hash>)。这会创建一个新的提交来撤销该提交的修改,并且不会改变历史,是更安全的选择。

Binary file not shown.

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

167
source/_posts/Hexo.md Normal file
View File

@ -0,0 +1,167 @@
---
title: Hexo
date: 2022-11-18 10:30:31
author: 文永达
---
# Hexo
启动本地开发服务器
```shell
hexo server
# or
hexo s
```
清空本地缓存
```shell
hexo clean
```
构建并发布到Git上
```shell
hexo g -d
```
只构建
```shell
hexo g
```
将安装的主题推送到Git上
因为主题也是一个git项目需要先把`.git`剪切到任意位置
`.git`文件夹在 `themes/butterfly`目录下,是一个隐藏文件夹
```shell
git rm --cache themes/butterfly
git status
git add themes/butterfly
```
新建文章
```shell
hexo new [layout] <title>
```
新建一篇文章。如果没有设置 `layout` 的话,默认使用 [_config.yml](https://hexo.io/zh-cn/docs/configuration) 中的 `default_layout` 参数代替。如果标题包含空格的话,请使用引号括起来。
## 图片上传
### 本地目录
**先将 _config.yml 文件中的 post_asset_folder 选项设为 true**
该操作的目的就是在使用`hexo new xxx`指令新建md文档博文时在相同路径下同步创建一个`xxx`文件夹,而`xxx`文件夹就是用来存放新建md文档里的图片的
![image-20250519143309765](Hexo/image-20250519143309765.png)
就像这样新建的md文档和其对于的同名文件夹都在/source/_posts路径下
但如果你习惯不用hexo new xxx指令创建新md文档而是直接打开typora写然后保存到/source/_posts下这个时候你就需要自己手动创建一个**同名的文件夹**才可以。
#### 解决图片路径问题
typora的图片插入的语法我是一般不会用的大多数时候就是复制粘贴图片到md文档里面。这个时候我们再慢慢修改路径到上面我们创建的文件夹下面就太麻烦了。
我们可以通过以下设置来舒舒服服按照简单粗暴的复制粘贴插入图片:
**打开typora点击文件点击偏好设置点击图像**
![image-20250519143418309](Hexo/image-20250519143418309.png)
第一个,将图片复制到指定路径./$(filename)的效果就是:**我们粘贴图片到md文档的时候typora会自动把图片再复制一份到我们上面创建的同名文件夹下**
这样的好处还有一点就是也不用我们自己创建同名文件夹了typora会自己帮我们创建有的话就复制到这里面**但 _config.yml文件中的post_asset_folder选项还是得设为 true这是必须的**
效果就像这样:
![image-20250519143457653](Hexo/image-20250519143457653.png)
#### 解决md文档转换到html文档路径不一样的问题
转换需要用到**hexo-asset-img**插件
在博客的源码文件夹下启动命令行下载插件hexo-asset-img
```shell
yarn add hexo-asset-img
```
是**hexo-asset-img****不是**其他文章里写的**hexo-asset-image**,这也是我之前用了不好使的原因
# Butterfly 主题
## 鼠标样式修改
1. 在\themes\butterfly\source\css路径下创建一个mouse.css文件在文件中添加如下代码
```css
body {
cursor: url(https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/img/default.cur),
default;
}
a,
img {
cursor: url(https://cdn.jsdelivr.net/gh/sviptzk/HexoStaticFile@latest/Hexo/img/pointer.cur),
default;
}
```
2. 打开站点的主题配置文件_config.butterfly.yml找到inject在head处直接引入该文件
```yaml
inject:
head:
- <link rel="stylesheet" href="/css/mouse.css">
```
3. 重新部署,即可看到效果
## 增加网站备案信息
找到`themes/butterfly/layout/includes/footer.pug`文件
在文件 `if theme.footer.copyright`中增加
```pug
br
center
| ICP备案号:
a(href="https://beian.miit.gov.cn" target="_blank") 辽ICP备2025052969号-1
```
完整如下:
```pug
#footer-wrap
if theme.footer.owner.enable
- var now = new Date()
- var nowYear = now.getFullYear()
if theme.footer.owner.since && theme.footer.owner.since != nowYear
.copyright!= `&copy;${theme.footer.owner.since} - ${nowYear} By ${config.author}`
else
.copyright!= `&copy;${nowYear} By ${config.author}`
if theme.footer.copyright
.framework-info
span= _p('footer.framework') + ' '
a(href='https://hexo.io')= 'Hexo'
span.footer-separator |
span= _p('footer.theme') + ' '
a(href='https://github.com/jerryc127/hexo-theme-butterfly')= 'Butterfly'
br
center
| ICP备案号:
a(href="https://beian.miit.gov.cn" target="_blank") 辽ICP备2025052969号-1
if theme.footer.custom_text
.footer_custom_text!=`${theme.footer.custom_text}`
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

View File

@ -0,0 +1,91 @@
---
title: Intellij Platform
date: 2023-11-18 10:30:31
author: 文永达
---
# Intellij IDEA
## Spring Boot DevTools热重载
pom.xml引入依赖
```xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<!-- 只在运行时起作用打包时不打进去防止线上执行打包后的程序启动文件监听线程File Watcher耗费大量的内存资源 -->
<scope>runtime</scope>
<!-- 防止将依赖传递到其他模块中 -->
<optional>true</optional>
</dependency>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
```
打开IDEA设置 找到 Build, Execution, Deployment -> Compiler -> Build project automatically 勾选
![image-20230323102425710](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230323102425710.png)
继续找到Advanced Settings 勾选 Allow auto-make to start even if developed application is currently running
![image-20230323102621741](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230323102621741.png)
## 使用@Autowired注解报红的解决办法
打开Settings -> Editor -> Inspections -> Spring -> Spring Core -> Code找到Incorrect autowiring in Spring bean components将代码审查级别从Error修改为Warning
![image-20230309133646128](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230309133646128.png)
# JetBrains Rider
## 快捷键
### CREATE AND EDIT(创建和编辑)
#### Show Context Actions(显示上下文操作)
Alt+Enter
#### Basic Code Completion(基本代码补全)
Ctrl+Space
#### Smart Code Completion(智能代码补全)
Ctrl+Shift+Space
#### Type Name Completion(类型名称补全)
Ctrl+Alt+Space
#### Complete Statement(完成语句)
Ctrl+Shift+Ent
## 安装CodeGlance Pro后关闭原有的在滚动条悬停时显示代码
CodeGlancePro是一款用于在编辑器旁边显示代码缩略图的插件。
如果您想要把之前的代码预览隐藏,可以通过以下操作实现:
1. 打开JetBrains Rider进入设置界面。可以通过快捷键`Ctrl+Alt+S`(Windows/Linux) 或 `Cmd+,`(Mac) 快速打开设置界面。
2. 在“Editor”中找到“General”选项卡。在这个选项卡中找到“Appearance”其中有一个“Show code lens on scrollbar hover”复选框。取消选中该复选框可隐藏在滚动条悬停时显示代码。
![image-20230322155611836](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230322155611836.png)
# IdeaVim
## 普通模式: `ESC`
### 基本移动命令:
- `h` 将光标向左移动一个字符。
- `l` 将光标向右移动一个字符。
- `k` 将光标向上移动一行。
- `j` 将光标向下移动一行。
- `w` 向前跳一个单词。
- `b` 向后跳一个单词。
- `0` 跳到行首。
- `$` 跳到行尾。

673
source/_posts/Java.md Normal file
View File

@ -0,0 +1,673 @@
---
title: Java
date: 2023-06-09 10:30:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B18FCBB3-67FD-48CC-B4F3-457BA145F17A.jpeg
---
# Spring Boot
## Hikari数据库连接池
### 配置
```yaml
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/yami_shops?allowMultiQueries=true&useSSL=false&useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=GMT%2B8&nullCatalogMeansCurrent=true
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
type: com.zaxxer.hikari.HikariDataSource
hikari:
minimum-idle: 0
maximum-pool-size: 20
idle-timeout: 10000
connection-test-query: select 1
```
## logback日志框架
### 配置
##
# 开源框架若依 RuoYi
### 替换Mybatis为Mybatis-Plus
# Java SE
## 注解
### 注解语法
同class和interface一样注解也属于一种类型。
### 注解定义
注解通过@interface关键字进行定义
```java
@TestAnnotation
public class Test {
}
```
创建一个类Test然后在类定义的地方加上@TestAnnotation就可以用TestAnnotation注解这个类了
可以简单理解为将 TestAnnotation这张标签贴到 Test这个类上面。
要想注解能够正常工作,还有一个元注解
### 元注解
元注解是可以注解到注解上的注解,或者说元注解是一种基本注解,但是它能够应用到其他的注解上面。
元注解也是一张标签,但是它是一张特殊的标签,它的作用和目的就是给其他普通的标签进行解释说明的。
元标签有 @Retention@Documented@Target@Inherited@Repeatable 5种
#### @Retention
Retention的英文意为保留期的意思。当@Retention应用到一个注解上的时候,它解释说明了这个注解的存活时间。
它的取值如下:
- RetentionPolicy.SOURCE 注解只在源码阶段保留,在编译器进行编译时它将被丢弃忽视。
- RetentionPolicy.CLASS 注解只被保留到编译进行的时候它不会被加载到JVM中。
- RetentionPolicy.RUNTIME 注解可以保留到程序运行的时候它会被加载进入到JVM中所以在程序运行时可以获取到它们。
@Retention 去给一张标签解释的时候,它指定了这张标签张贴的时间。@Retention 相当于给一张标签上面盖了一张时间戳,时间戳指明了标签张贴的时间周期。
```java
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
}
```
指定 TestAnnotation 可以在程序运行周期被获取到,因此它的生命周期非常长。
#### @Documented
这个元注解和文档有关。它的作用是能够将注解中的元素包含到JavaDoc中去
#### @Target
Target 是目标的意思,@Target 制定了注解运用的地方。
当一个注解被 @Target 注解时,这个注解就被限定了运用的场景。
类比到标签,原本标签想张贴到哪个地方就到哪个地方,但是因为 @Target 的存在,它张贴的地方就非常具体了,比如只能张贴到方法上、类上、方法参数上等等。@Target 有下面的取值
- ElementType.ANNOTATION_TYPE 可以给一个注解进行注解
- ElementType.CONSTRUCTUR 可以给构造方法进行注解
- ElementType.FIELD 可以给属性进行注解
- ElementType.LOCAL_VARIABLE 可以给局部变量进行注解
- ElementType.METHOD 可以给方法进行注解
- ElementType.PACKAGE 可以给一个包进行注解
- ElementType.TYPE 可以给一个类型进行注解,比如类、接口、枚举
#### @Inherited
Inherited 是继承的意思,但是它并不是说注解本身可以继承,而是说如果一个超类被 @Inherited 注解过的注解进行注解的话,那么如果它的子类没有被任何注解应用的话,那么这个子类就继承了超类的注释。
```java
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@interface Test {}
@Test
public class A {}
public class B extends A {}
```
注解 Test 被 @Inherited 修饰之后类A 被 Test 注解,类 B 基础 A类B 也拥有 Test 这个注解。
可以这样理解:
老子非常有钱,所以人们给他贴了一张标签叫做富豪。
老子的儿子长大后,只要没有和老子断绝父子关系,虽然别人没有给他贴标签,但是他自然也是富豪。
老子的孙子长大了,自然也是富豪。
这就是人们口中戏称的富一代,富二代,富三代。虽然叫法不同,好像好多个标签,但其实事情的本质也就是他们有一张共同的标签,也就是老子身上的那张富豪的标签。
#### @Repeatable
Repeatable 自然是可重复的意思。@Repeatable 是Java 1.8 才加进来的,所以算是一个新的特性。
什么样的注解或被多次运用呢?通常是注解的值可以同时取多个。
举个例子,一个人他既是程序员又是产品经理,同时他还是个画家。
```java
@interface Persons {
Person[] value();
}
@Repeatable(Persons.class)
@interface Person {
String role default "";
}
@Person(role="artist")
@Person(role="coder")
@Person(role="PM")
public class SuperMan {
}
```
@Repeatable 注解了Persion。而@Repeatable 后面括号中的类相当于一个容器注解。
什么是容器注解呢?就是用来存放其他注解的地方。它本身是一个注解。
相关容器注解
```java
@interface Persons {
Person[] value();
}
```
按照规定,它里面必须要有一个 value 的属性,属性类型是一个被 @Repeatable 注解过的注解数组,注意它是数组。
Persons 是一张总的标签上面贴满了Person这种同类型但内容不一样的标签。把Persons 给一个 SuperMan 贴上,相当于同时给他贴了程序员、产品经理、画家的标签。
我们可能对于 @Person(role="PM")括号里面的内容感兴趣,它其实就是给 Person 这个注解的 role 属性赋值为 PM
### 注解的属性
注解的属性也叫做成员变量。注解只要成员变量,没有方法。注解的成员变量在注解的定义中“无形参方法”形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型。
```java
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestAnnotation {
int id();
String msg();
}
```
上面代码定义了 TestAnnotation 这个注解拥有 id 和 msg 两个属性。在使用的时候,我们应该给它们进行赋值。
赋值的方式是在注解的括号内以 value="" 形式,多个属性之前用 , 隔开。
```java
@TestAnnotation(id=3, msg="hello annotation")
public class Test {
}
```
## 多线程
### 线程安全
#### 概念
线程安全是多线程编程的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且准确的执行,不会出现数据污染等意外情况。上述是百度百科给出的一个概念解释。换言之,线程安全就是某个函数在并发环境中调用时,能够处理好多个线程之间的共享变量,是程序能够正确执行完毕。也就是说我们想要确保在多线程访问的时候,我们的程序还能够按照我们预期的行为去执行,那么就是线程安全了。
#### 导致线程不安全的原因
首先,可以来看一段代码,来看看是不是线程安全的,代码如下:
```java
package com.company;
public class TestThread {
private static class XRunnable implements Runnable{
private int count;
public void run(){
for(int i= 0; i<5; i++){
getCount();
}
}
public void getCount(){
count++;
System.out.println(" "+count);
}
}
public static void main(String[] args) {
XRunnable runnable = new XRunnable();
Thread t1 = new Thread(runnable);
Thread t2 = new Thread(runnable);
Thread t3 = new Thread(runnable);
t1.start();
t2.start();
t3.start();
}
}
```
输出的结果为:
```java
2
3
2
5
4
7
6
10
11
12
9
8
13
14
15
```
从代码上进行分析当启动了三个线程每个线程应该都是循环5次得出1到15的结果但是从输出的结果就可以看到有两个2的输出出现像这种情况表明这个方法根本就不是线程安全的。我们可以这样理解在每个进程的内存空间中都会有一块特殊的公共区域通常称为**堆(内存)**,之所以会输出两个2是因为每个进程的所有线程都可以访问到该区域当第一个线程已经获得2这个数了还没来得及输出下一个线程在这段时间的空隙获得了2这个值故输出时会输出2的值。
#### 线程安全问题
要考虑线程安全问题就需要先考虑Java并发的三大基本特征**原子性**、**可见性**以及**有序性**
- 原子性
原子性是指在一个操作中就是cpu不可以在中途暂停然后再调度即不被中断操作要不全部执行完成要不都不执行。就好比转账从账户A向账户B转1000元那么必然包括2个操作从账户A减去1000元往账户B加上1000元。2个操作必须全部完成。
那程序中原子性指的是最小操作单元比如自增操作它本身其实并不是原子性操作分了3步的包括读取变量的原始值、进行加1操作、写入工作内存。所以在多线程中有可能一个线程还没自增完可能才执行到第二步另一个线程就已经读取了值导致结果错误。那如果我们能保证自增操作是一个原子性的操作那么就能保证其他线程读取到的一定是自增后的数据。
- 可见性
当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
若两个线程在不同的cpu那么线程1改变了i得值还没刷新到主存线程2又使用了i那么这个i值肯定还是之前的线程1对变量的修改线程没看到这就是可见性的问题。
- 有序性
程序执行的顺序按照代码的先后顺序执行,在多线程编程时就得考虑这个问题。
**案例** **抢票**
当多个线程同时共享,同一个全局变量或静态变量(即局部变量不会),做写的操作时,可能会发生数据冲突问题,也就是线程安全问题。但是做读操作是不会发生数据冲突问题。
Consumer类
```java
package com.company;
public class Consumer implements Runnable{
private int ticket = 100;
public void run(){
while(ticket>0){
System.out.println(Thread.currentThread().getName() + "售卖第" + (100-ticket+1) + "张票");
ticket--;
}
}
}
```
主类:
```java
package com.company;
public class ThreadSafeProblem {
public static void main(String[] args){
Consumer abc = new Consumer();
new Thread(abc, "窗口1").start();
new Thread(abc, "窗口2").start();
}
}
```
结果:
```shell
窗口2售卖第95张票
窗口2售卖第96张票
窗口2售卖第97张票
窗口2售卖第98张票
窗口2售卖第99张票
窗口2售卖第100张票
窗口2售卖第84张票
```
从输出结果来看,售票窗口买票出现了计票的问题,这就是线程安全出现问题了。
#### 如何确保线程安全
解决办法:使用多线程之间使用**关键字synchronized**、或者使用**锁lock**,或者**volatile关键字**。
1. **synchronized**(自动锁,锁的创建和释放都是自动的)
2. **lock 手动锁**(手动指定锁的创建和释放)
3. **volatile关键字**
为什么能解决?如果可能会发生数据冲突问题(线程不安全问题),只能让当前一个线程进行执行。代码执行完成后释放锁,然后才能让其他线程进行执行。这样的话就可以解决线程不安全问题。
##### synchronized关键字
###### 同步代码块
```java
synchronized(同一个锁){
//可能发生线程冲突问题
}
```
将可能会发生线程安全问题的代码,给包括起来,也称为**同步代码块**。synchronized使用的锁可以是对象锁也可以是静态资源如xxx.class只有持有锁的线程才能执行同步代码块中的代码。没持有锁的线程即使获取cpu的执行权也进不去。
锁的释放是在synchronized同步代码块执行完毕后自动释放。
同步的前提:
1. 必须要有两个或两个以上的线程如果小于2个线程则没有用且还会消耗性能获取锁释放锁
2. 必须是多个线程使用一个锁
**弊端**:多个线程需要判断锁,较为消耗资源、抢锁的资源。
例子:
```java
public class ThreadSafeProblem {
public static void main(String[] args) {
Consumer abc = new Consumer();
// 注意要使用同一个abc变量作为thread的参数
// 如果你使用了两个Consumer对象那么就不会共享ticket了就自然不会出现线程安全问题
new Thread(abc, "窗口1").start();
new Thread(abc, "窗口2").start();
}
}
class Consumer implements Runnable{
private int ticket = 100;
@Override
public void run() {
while (ticket > 0) {
synchronized (Consumer.class) {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售卖第" + (100-ticket+1) + "张票");
ticket--;
}
}
}
}
}
```
###### 同步函数
就是将synchronized加在方法上。
分为两种:
第一种是**非静态同步函数**,即方法是非静态的,使用的**this对象锁**,如下代码所示
第二种是**静态同步函数**即方法是用static修饰的使用的锁是**当前类的class文件xxx.class**。
```java
public synchronized void sale () {
if (ticket > 0) {
System.out.println(Thread.currentThread().getName() + "售卖第" + (100-ticket+1) + "张票");
ticket--;
}
}
```
###### 多线程死锁线程
如下代码所示,
线程t1运行后在同步代码块中需要oj对象锁运行到sale方法时需要this对象锁
线程t2运行后需要调用sale方法需要先获取this锁再获取oj对象锁
那这样就会造成,两个线程相互等待对方释放锁。就造成了死锁情况。简单来说就是:
同步中嵌套同步,导致锁无法释放。
```java
class ThreadTrain3 implements Runnable {
private static int count = 100;
public boolean flag = true;
private static Object oj = new Object();
@Override
public void run() {
if (flag) {
while (true) {
synchronized (oj) {
sale();
}
}
} else {
while (true) {
sale();
}
}
}
public static synchronized void sale() {
// 前提 多线程进行使用、多个线程只能拿到一把锁。
// 保证只能让一个线程 在执行 缺点效率降低
synchronized (oj) {
if (count > 0) {
try {
Thread.sleep(50);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(Thread.currentThread().getName() + ",出售第" + (100 - count + 1) + "票");
count--;
}
}
}
}
public class ThreadDemo3 {
public static void main(String[] args) throws InterruptedException {
ThreadTrain3 threadTrain1 = new ThreadTrain3();
Thread t1 = new Thread(threadTrain1, "①号窗口");
Thread t2 = new Thread(threadTrain1, "②号窗口");
t1.start();
Thread.sleep(40);
threadTrain1.flag = false;
t2.start();
}
}
```
##### Lock
可以视为**synchronized的增强版**,提供了更灵活的功能。该接口提供了限时锁等待、锁中断、锁尝试等功能。**synchronized**实现的同步代码块,它的锁是自动加的,且当执行完同步代码块或者抛出异常后,锁的释放也是自动的。
```java
Lock l = ...;
l.lock();
try {
// access the resource protected by this lock
} finally {
l.unlock();
}
```
但是**Lock锁**是需要手动去加锁和释放锁,所以**Lock**相比于**synchronized**更加的灵活。且还提供了更多的功能比如说
**tryLock()方法**会尝试获取锁如果锁不可用则返回false如果锁是可以使用的那么就直接获取锁且返回true官方代码如下
```java
Lock lock = ...;
if (lock.tryLock()) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
```
例子:
```java
/*
* 使用ReentrantLock类实现同步
* */
class MyReenrantLock implements Runnable{
//向上转型
private Lock lock = new ReentrantLock();
public void run() {
//上锁
lock.lock();
for(int i = 0; i < 5; i++) {
System.out.println("当前线程名: "+ Thread.currentThread().getName()+" ,i = "+i);
}
//释放锁
lock.unlock();
}
}
public class MyLock {
public static void main(String[] args) {
MyReenrantLock myReenrantLock = new MyReenrantLock();
Thread thread1 = new Thread(myReenrantLock);
Thread thread2 = new Thread(myReenrantLock);
Thread thread3 = new Thread(myReenrantLock);
thread1.start();
thread2.start();
thread3.start();
}
}
```
输出结果:
由此我们可以看出,只有当当前线程打印完毕后,其他的线程才可继续打印,线程打印的数据是分组打印,因为当前线程持有锁,但线程之间的打印顺序是随机的。
即调用**lock.lock()** 代码的线程就持有了“对象监视器”,其他线程只有等待锁被释放再次争抢。
##### volatile关键字
先来看一段错误的代码示例:
```java
class ThreadVolatileDemo extends Thread {
public boolean flag = true;
@Override
public void run() {
System.out.println("子线程开始执行");
while (flag) {
}
System.out.println("子线程执行结束...");
}
public void setFlag(boolean flag){
this.flag=flag;
}
}
public class ThreadVolatile {
public static void main(String[] args) throws InterruptedException {
ThreadVolatileDemo threadVolatileDemo = new ThreadVolatileDemo();
threadVolatileDemo.start();
Thread.sleep(3000);
threadVolatileDemo.setFlag(false);
System.out.println("flag已被修改为false!");
}
}
```
输出结果:
```shell
子线程开始执行
flag已被修改为false
```
虽然flag已被修改但是子线程依然在执行这里产生的原因就是**Java内存模型JMM** 导致的。
虽然flag已被修改但是子线程依然在执行这里产生的原因就是**Java内存模型JMM** 导致的。
这里再来介绍一下**Java内存模型**吧!!!
在**Java内存模型**规定了所有的变量(这里的变量是指成员变量,静态字段等但是不包括局部变量和方法参数,因为这是线程私有的)都存储在**主内存**中,每条线程还有自己的**工作内存**,线程的工作内存中拷贝了该线程使用到的主内存中的变量(只是副本,从主内存中拷贝了一份,放到了线程的本地内存中),**线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。** 不同的线程之间也无法直接访问对方工作内存中的变量,**线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行**。
而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。
**1. 首先要将共享变量从主内存拷贝到线程自己的工作内存空间,工作内存中存储着主内存中的变量副本拷贝;**
**2. 线程对副本变量进行操作,(不能直接操作主内存);**
**3. 操作完成后通过JMM 将线程的共享变量副本与主内存进行数据的同步,将数据写入主内存中;**
**4. 不同的线程间无法访问对方的工作内存,线程间的通信(传值)必须通过主内存来完成。**
当多个线程同时访问一个数据的时候,可能本地内存没有及时刷新到主内存,所以就会发生线程安全问题
**JMM是在线程调run方法的时候才将共享变量写到自己的线程本地内存中去的而不是在调用start方法的时候。**
**解决办法**
当出现这种问题时,就可以使用**Volatile关键字**进行解决。
**Volatile 关键字的作用**是变量在多个线程之间可见。使用**Volatile关键字**将解决线程之间可见性,**强制线程每次读取该值的时候都去“主内存”中取值**。
只需要在flag属性上加上该关键字即可。
```java
public volatile boolean flag = true;
```
子线程每次都不是读取的线程本地内存中的副本变量了,而是直接读取主内存中的属性值。
volatile虽然**具备可见性**,但是**不具备原子性**。
##### synchronized、volatile和Lock之间的区别
**synochronizd和volatile关键字区别**
1. **volatile关键字**解决的是变量在多个线程之间的可见性;而**sychronized关键字**解决的是多个线程之间访问共享资源的同步性。
> **tip** final关键字也能实现可见性被final修饰的字段在构造器中一旦初始化完成并且构造器没有把 **“this”**的引用传递出去this引用逃逸是一件很危险的事情其它线程有可能通过这个引用访问到了"初始化一半"的对象那在其他线程中就能看见final
2. **volatile**只能用于修饰变量,而**synchronized**可以修饰方法,以及代码块。(**volatile**是线程同步的轻量级实现,所以**volatile**性能比**synchronized**要好并且随着JDK新版本的发布**sychronized关键字**在执行上得到很大的提升,在开发中使用**synchronized关键字**的比率还是比较大);
3. 多线程访问**volatile**不会发生阻塞,而**sychronized**会出现阻塞;
4. 多线程访问**volatile**不会发生阻塞,而**sychronized**会出现阻塞;
5. **线程安全**包含**原子性**和**可见性**两个方面。
对于用**volatile**修饰的变量JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的。
**一句话说明volatile的作用**:实现变量在多个线程之间的可见性。
**synchronized和lock区别**
1. **Lock**是一个接口,而**synchronized**是Java中的关键字**synchronized**是内置的语言实现;
2. **synchronized**在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而**Lock**在发生异常时,如果没有主动通过**unLock()**去释放锁,则很可能造成死锁现象,因此使用**Lock**时需要在**finally**块中释放锁;
3. **Lock**可以让等待锁的线程响应中断,而**synchronized**却不行,使用**synchronized**时,等待的线程会一直等待下去,不能够响应中断;
4. 通过**Lock**可以知道有没有成功获取锁,而**synchronized**却无法办到。
5. **Lock**可以提高多个线程进行读操作的效率(读写锁)。
**在性能上来说**,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时**Lock**的性能要远远优于**synchronized**。所以说,在具体使用时要根据适当情况选择。
## 匿名内部类
匿名内部类是一种特殊的类定义方式它没有明确的类名且在定义的同时就实例化了这个类的对象。在Java中匿名内部类常常用于简化只使用一次的类的创建过程尤其是在只需要实现单个接口或继承单一父类的情况下。
```java
streamSource.keyBy(new KeySelector<String, String>() {
@Override
public String getKey(String s) throws Exception {
int i = Integer.parseInt(s);
return i > 500 ? "ge" : "lt";
}
})
```
这里的 new KeySelector<String, String>() {...} 就是一个匿名内部类的实例。具体来说:
- KeySelector 是一个接口(或抽象类)。
- 匿名内部类实现了 KeySelector 接口,并重写了其中的 getKey 方法。
- 因为并没有给这个类命名,所以在创建对象时直接定义其实现细节,而不需要先定义一个单独的类。
- 这个匿名内部类实例随后作为参数传递给 streamSource.keyBy(...) 方法,说明了它是根据运行时需求即时创建并使用的。

418
source/_posts/JavaScript.md Normal file
View File

@ -0,0 +1,418 @@
---
title: JavaScript
date: 2023-11-18 10:30:31
author: 文永达
---
# 异步编程
## await
在JavaScript中`await`关键字用于等待一个异步操作完成,并返回异步操作的结果。当遇到`await`关键字时JavaScript引擎会暂停当前函数的执行直到异步操作完成并返回结果。在等待异步操作的过程中JavaScript引擎会继续执行其他代码。
因此,`await`关键字的执行顺序是从上到下的,即在遇到`await`关键字之前的代码会先执行,然后等待异步操作完成后,再执行`await`关键字之后的代码。如果在`await`关键字之前的代码中抛出了异常,那么`await`关键字之后的代码将不会执行。
在你提供的代码片段中,当遇到`await`关键字时JavaScript引擎会等待`addCoCertification``updateCoCertification`函数完成并返回它们的结果。在等待这些函数的过程中JavaScript引擎会继续执行其他代码例如设置`isAddedFile.value``dialogVisible.value`的值。当异步操作完成后JavaScript引擎会继续执行`await`关键字之后的代码,例如显示成功消息。
总之,`await`关键字的执行顺序是从上到下的,即在遇到`await`关键字之前的代码会先执行,然后等待异步操作完成后,再执行`await`关键字之后的代码。
下面是一个简单的示例,演示了`await`关键字的执行顺序:
```javascript
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function example() {
console.log('Start')
await sleep(1000)
console.log('After 1 second')
await sleep(2000)
console.log('After 2 more seconds')
console.log('End')
}
example()
```
在这个示例中,我们定义了一个`sleep`函数它返回一个Promise对象并在指定的时间后将其状态设置为resolved。然后我们定义了一个名为`example`的异步函数,它使用`await`关键字等待`sleep`函数完成,并在控制台中打印一些消息。在`example`函数中,我们使用`await`关键字等待`sleep(1000)`函数完成并在1秒后打印"After 1 second"。然后,我们再次使用`await`关键字等待`sleep(2000)`函数完成并在2秒后打印"After 2 more seconds"。最后,我们在控制台中打印"End"。
当我们调用`example`函数时JavaScript引擎会执行`console.log('Start')`语句,然后等待`sleep(1000)`函数完成。在等待的过程中JavaScript引擎会继续执行其他代码但是当`sleep(1000)`函数完成后JavaScript引擎会继续执行`await`关键字之后的代码,即打印"After 1 second"。然后JavaScript引擎会等待`sleep(2000)`函数完成并在2秒后打印"After 2 more seconds"。最后JavaScript引擎会打印"End"。
---
在同步方法中使用`await`关键字是不合法的,因为`await`只能在异步函数中使用。如果你在同步函数中使用`await`关键字,它会被视为普通的表达式,不会暂停当前函数的执行。
如果你需要在同步函数中等待一个异步操作完成,你可以使用`Promise`对象和`then`方法来实现。例如,你可以将异步操作封装在一个`Promise`对象中,并在`then`方法中处理结果。下面是一个示例:
```javascript
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
function example() {
console.log('Start')
sleep(1000).then(() => {
console.log('After 1 second')
return sleep(2000)
}).then(() => {
console.log('After 2 more seconds')
console.log('End')
})
}
example()
```
在这个示例中,我们定义了一个`sleep`函数它返回一个Promise对象并在指定的时间后将其状态设置为resolved。然后我们定义了一个名为`example`的同步函数,它使用`Promise`对象和`then`方法等待`sleep`函数完成,并在控制台中打印一些消息。在`example`函数中,我们使用`sleep(1000).then()`方法等待`sleep(1000)`函数完成并在1秒后打印"After 1 second"。然后,我们再次使用`then`方法等待`sleep(2000)`函数完成并在2秒后打印"After 2 more seconds"和"End"。
当我们调用`example`函数时JavaScript引擎会执行`console.log('Start')`语句,然后等待`sleep(1000)`函数完成。在等待的过程中JavaScript引擎会继续执行其他代码但是当`sleep(1000)`函数完成后JavaScript引擎会执行`then`方法中的代码,即打印"After 1 second"。然后JavaScript引擎会等待`sleep(2000)`函数完成并在2秒后打印"After 2 more seconds"和"End"。
# 数组
## map
`map`方法会对数组中的每个元素都执行一个回调函数,并返回一个新的数组,新数组中的元素是回调函数的返回值。`map`方法不会改变原始数组,而是返回一个新的数组。下面是一个使用`map`方法的示例:
```javascript
const numbers = [1, 2, 3, 4, 5];
const doubledNumbers = numbers.map((number) => number * 2);
console.log(doubledNumbers); // [2, 4, 6, 8, 10]
```
在这个示例中,我们使用`map`方法将数组中的每个元素都乘以2并返回一个新的数组`doubledNumbers`
## filter
`filter`方法会对数组中的每个元素都执行一个回调函数,并返回一个新的数组,新数组中的元素是回调函数返回`true`的元素。`filter`方法不会改变原始数组,而是返回一个新的数组。下面是一个使用`filter`方法的示例:
```javascript
const numbers = [1, 2, 3, 4, 5];
const evenNumbers = numbers.filter((number) => number % 2 === 0);
console.log(evenNumbers); // [2, 4]
```
在这个示例中,我们使用`filter`方法筛选出数组中的偶数,并返回一个新的数组`evenNumbers`
总之,`map``filter`都是JavaScript中常用的数组方法它们可以用于对数组进行转换和筛选。`map`方法会对数组中的每个元素都执行一个回调函数,并返回一个新的数组,新数组中的元素是回调函数的返回值。`filter`方法会对数组中的每个元素都执行一个回调函数,并返回一个新的数组,新数组中的元素是回调函数返回`true`的元素。
## reduce
`reduce()` 函数是 JavaScript 中的一个高阶函数,它可以将一个数组中的所有元素通过一个函数进行累加,最终返回一个累加结果。`reduce()` 函数接收两个参数:一个**回调函数**和一个**初始值**。回调函数接收四个参数:**累加器**、**当前元素**、**当前索引**和**原始数组**。在每次迭代中,回调函数都会将累加器和当前元素作为参数,并返回一个新的累加器值。最终,`reduce()` 函数返回最后一次调用回调函数时的累加器值。
```javascript
const numbers = [65, 44, 12, 4]
// total为 累加器 item为当前元素
// 0 为初始值,也就是 从什么值开始累加
const totalNumber = () => {
return numbers.reduce((total, item) => {
return total + item
}, 0)
}
```
# 循环
## for...in
`for...in`语句用于遍历对象的可枚举属性,包括对象自身的属性和继承的属性。在遍历时,`for...in`会将属性名作为变量传递给循环体,因此我们可以通过属性名来访问属性的值。下面是一个使用`for...in`语句遍历对象的示例:
```javascript
const obj = { a: 1, b: 2, c: 3 };
for (const key in obj) {
console.log(key, obj[key]);
}
// Output:
// a 1
// b 2
// c 3
```
在这个示例中,我们使用`for...in`语句遍历对象`obj`的属性,并将属性名作为变量`key`传递给循环体。在循环体中,我们可以通过`obj[key]`来访问属性的值。
## for...of
`for...of`语句用于遍历可迭代对象包括数组、字符串、Map、Set等。在遍历时`for...of`会将元素的值作为变量传递给循环体,因此我们可以直接访问元素的值。下面是一个使用`for...of`语句遍历数组的示例:
```javascript
const arr = [1, 2, 3];
for (const item of arr) {
console.log(item);
}
// Output:
// 1
// 2
// 3
```
在这个示例中,我们使用`for...of`语句遍历数组`arr`的元素,并将元素的值作为变量`item`传递给循环体。在循环体中,我们可以直接访问元素的值。
总之,`for...in``for...of`都是JavaScript中用于遍历数据结构的循环语句但它们的作用和用法有所不同。`for...in`语句用于遍历对象的可枚举属性,而`for...of`语句用于遍历可迭代对象的元素。
# 下载文件
## Url转Blob
```javascript
// xhr
function urlToBlob(url, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'blob';
xhr.onload = function() {
if (xhr.status === 200) {
var blob = xhr.response;
callback(null, blob);
} else {
callback(new Error('Unable to fetch blob'));
}
};
xhr.onerror = function() {
callback(new Error('Network error'));
};
xhr.send();
}
// fetch
function urlToBlob(url) {
return fetch(url)
.then(response => {
if (!response.ok) {
throw new Error('Unable to fetch blob');
}
return response.blob();
});
}
```
# 匿名函数和箭头函数
## 区别
匿名函数和箭头函数都是JavaScript中的函数形式它们之间有一些重要的区别。
1. 语法结构:匿名函数使用`function`关键字来定义,后面跟随函数名称(可选),参数列表和函数体。例如:
```javascript
var anonymousFunc = function(arg1, arg2) {
// 函数体
};
```
而箭头函数使用箭头操作符(`=>`)来定义,后面跟随参数列表和箭头函数体。没有显式的函数名称。例如:
```javascript
var arrowFunc = (arg1, arg2) => {
// 函数体
};
```
2. `this` 的指向:在匿名函数中,`this` 的指向是动态的,根据函数的调用方式而定。而在箭头函数中,`this` 的指向是词法作用域的,它继承自外围作用域。箭头函数没有自己的 `this` 值,它会使用外围作用域的 `this` 值。这使得箭头函数在处理函数上下文时更加方便。
3. `arguments` 对象:匿名函数中可以使用 `arguments` 对象访问函数的参数列表。而箭头函数没有自己的 `arguments` 对象,它会继承外围作用域中的 `arguments` 对象。
4. 构造函数:匿名函数可以用作构造函数来创建新对象。而箭头函数没有原型对象,因此不能用作构造函数,尝试使用 `new` 关键字调用箭头函数会抛出错误。
5. 箭头函数的简洁语法:箭头函数提供了更简洁的语法,尤其是在处理简单的函数体时。当函数体只有一条表达式时,可以省略花括号和 `return` 关键字,并将表达式的结果隐式返回。
这些是匿名函数和箭头函数之间的一些主要区别。选择使用哪种函数形式取决于具体的使用场景和需求。匿名函数适用于更传统的函数定义和需要动态 `this` 指向的情况,而箭头函数则提供了更简洁的语法和更方便的函数上下文处理。
# 运算符
## ~
在 JavaScript 中,`~` 符号是按位取反运算符。它将操作数的每个二进制位取反,即将 0 变为 1将 1 变为 0。然后返回这个值的负数加一。
例如,对于十进制数 10它的二进制表示为 1010按位取反后变为 0101即 5。因此`~10` 的结果为 -11。
# 性能优化
## 防抖debounce
> 多次触发 只执行最后一次
**作用:**高频率触发的事件,在指定的单位时间内,只相应最后一次,如果在指定时间内再次触发,则重新计算时间。
防抖类似于英雄联盟回城6秒如果回城中被打断再次回城需要再等6秒
![image-20230918145946833](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230918145946833.png)
**实现代码:**
```javascript
<body>
<input type="text" id="inp">
<script>
// 1.封装防抖函数
function debounce(fn, time) {
// 4.创建一个标记用来存放定时器的返回值
let timeout = null;
return function () {
// 5.每当用户触发input事件 把前一个 setTimeout 清楚掉
clearTimeout(timeout);
// 6.然后又创建一个新的 setTimeout, 这样就能保证输入字符后等待的间隔内 还有字符输入的话,就不会执行 setTimeout里面的内容
timeout = setTimeout(() => {
// 7.这里进行防抖的内容
fn();
}, time);
};
}
// 2.获取inpt元素
var inp = document.getElementById('inp');
// 8. 测试防抖临时使用的函数
function sayHi() {
console.log('防抖成功');
}
// 3.给inp绑定input事件 调用封装的防抖函数 传入要执行的内容与间隔事件
inp.addEventListener('input', debounce(sayHi, 5000));
</script>
</body>
```
**使用场景:**
search搜索时用户在不断输入值时用防抖来节约请求资源。
## 节流throttle
> 规定时间内 只触发一次
**作用:**高频率触发的事件,在指定的单位时间内,只响应第一次
节流类似于英雄联盟里的技能 触发一次必须等技能刷新后才能再次触发
![image-20230918151523728](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230918151523728.png)
**实现代码:**
```javascript
<script>
// 1.封装节流函数
function throttle(fn, time) {
//3. 通过闭包保存一个 "节流阀" 默认为false
let temp = false;
return function () {
//8.触发事件被调用 判断"节流阀" 是否为true 如果为true就直接trurn出去不做任何操作
if (temp) {
return;
} else {
//4. 如果节流阀为false 立即将节流阀设置为true
temp = true; //节流阀设置为true
//5. 开启定时器
setTimeout(() => {
//6. 将外部传入的函数的执行放在setTimeout中
fn.apply(this, arguments);
//7. 最后在setTimeout执行完毕后再把标记'节流阀'为false(关键) 表示可以执行下一次循环了。当定时器没有执行的时候标记永远是true在开头被return掉
temp = false;
}, time);
}
};
}
function sayHi(e) {
// 打印当前 document 的宽高
console.log(e.target.innerWidth, e.target.innerHeight);
}
// 2.绑定事件,绑定时就调用节流函数
// 敲黑板!!! 这里是重点 绑定是就要调用一下封装的节流函数 触发事件是触发封装函数内部的函数
window.addEventListener('resize', throttle(sayHi, 2000));
</script>
```
**使用场景:**
鼠标不断点击触发mousedown单位时间内只触发一次
监听滚动事件比如是否滑到底部自动加载更多用throttle来判断
# Map 键值对
## 遍历MAP
### keys() 方法
该方法返回一个新的迭代器对象,它包括`Map`对象中每个元素的键。
```js
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
for (const key of map.keys()) {
console.log(key);
}
// 输出:'name' 'age'
```
### values() 方法
该方法返回一个新的迭代器对象,它包括`Map`对象中每个元素的值。
```js
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
for (const value of map.values()) {
console.log(value);
}
// 输出:'Alice' 25
```
### entries() 方法
该方法返回一个新的迭代器对象,它包括`Map`对象中每个元素的键值对。
```js
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
for (const [key, value] of map.entries()) {
console.log(key + ': ' + value);
}
// 输出:'name: Alice' 'age: 25'
```
### forEach() 方法
此方法接受一个回调函数作为参数,`Map`对象中的每个元素都会调用一次这个回调函数。回调函数中的参数依次为:`value``key``mapObject`
```js
const map = new Map();
map.set('name', 'Alice');
map.set('age', 25);
map.forEach((value, key) => {
console.log(key, value);
});
// 输出:'name' 'Alice' 'age' 25
```
## 进阶用法
### Map 和 Array 的相互转化
有时我们需要在 `Map``Array` 之间相互转化,比如将数组转换为字典,或者从字典转换为数组时,我们就可以结合 `Array` 构造函数和扩展运算符来实现。
```js
let kvArray = [['key1', 'value1'], ['key2', 'value2']];
let myMap = new Map(kvArray);
console.log(myMap); // Map(2) {"key1" => "value1", "key2" => "value2"}
let arrayFromMap = Array.from(myMap);
console.log(arrayFromMap); // [["key1", "value1"], ["key2", "value2"]]
```
### Map 的合并和复制

503
source/_posts/Jenkins.md Normal file
View File

@ -0,0 +1,503 @@
---
title: Jenkins
date: 2022-11-09 10:30:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B18FCBB3-67FD-48CC-B4F3-457BA145F17A.jpeg
---
# Jenkins
```powershell
# 删除文件夹
rmdir /q /s C:\Program" "Files\nginx-1.23.2\html\dist
# 复制文件夹到指定目录
xcopy /y /e /i C:\Users\Administrator\Documents\source\XiaoDaERP-Vben\dist C:\Program" "Files\nginx-1.23.2\html\dist
# 发送文件文件夹到Linux远程服务器上
pscp -r -l root -pw Wyd210213 C:\Users\Administrator\Documents\source\XiaoDaERP-Vben\dist 8.140.174.251:/usr/local/nginx/html
```
```shell
docker run -d -p 80:80 -p 443:443 --name nginxweb --privileged=true
-v /usr/local/nginx/html/:/usr/share/nginx/html
-v /usr/local/nginx/conf/nginx.conf:/etc/nginx/nginx.conf
-v /usr/local/nginx/conf/conf.d:/etc/nginx/conf.d
-v /usr/local/nginx/logs:/var/log/nginx nginx
ps aux | grep "nginx: worker process" | awk '{print $1}'
```
## 部署.Net Core 至 IIS
新建Item
![image-20221108224600422](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108224600422.png)
配置Git
![image-20221108224810869](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108224810869.png)
添加Git账户
![image-20221108224709242](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108224709242.png)
指定Jenkins从Git拉取代码目录
![image-20221108224910785](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108224910785.png)
![image-20221108224935821](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108224935821.png)
指定Jenkins定时获取Git
时间设置成H/2 * * * *
意思是每2分钟检查Git是否有变化如果有变化就会重新构建和部署
![image-20221108225020629](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108225020629.png)
构建步骤
因为Jenkins在Windows环境下所以使用 Execute Windows batch command
![image-20221108225404450](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108225404450.png)
命令:
```powershell
c:
// 切换到Git拉取代码目录
cd C:\Users\Administrator\Documents\source\XiaodaERP
// 构建.Net Core应用
dotnet build
// 停止应用程序池 saas
C:\Windows\System32\inetsrv\appcmd.exe stop apppool /apppool.name:saas
// 发布.Net Core应用 需指定发布文件目录
dotnet publish -o D:\subendong\release\saas
// 启动应用程序池 saas
C:\Windows\System32\inetsrv\appcmd.exe start apppool /apppool.name:saas
```
IIS服务器应用程序池必须为无代码托管
为防止生产环境Swagger无法使用需在构建后的文件夹中编辑web.conf文件
![image-20221108230704528](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221108230704528.png)
添加如下代码
```xml
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
</environmentVariables>
```
![1668787835347](http://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/1668787835347.png)
在模块中删除WebDAVModule
## Jenkins安装NodeJS环境
在Jenkins 系统管理中 插件管理 可用插件中安装`NodeJS Plugin`
并在系统管理中 全局工具配置中配置NodeJS
![image-20221122094753314](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122094753314.png)
## 部署.Net Core 至 Docker容器
## Windows Server 部署Vue
### 本地Nginx
```shell
yarn
yarn build
# 删除文件夹
rmdir /q /s C:\Program" "Files\nginx-1.23.2\html\dist
# 复制文件夹到指定目录
xcopy /y /e /i C:\Users\Administrator\Documents\source\XiaoDaERP-Vben\dist C:\Program" "Files\nginx-1.23.2\html\dist
```
Linux远程服务器
```shell
# 发送文件文件夹到Linux远程服务器上
pscp -r -l root -pw Wyd210213 C:\Users\Administrator\Documents\source\XiaoDaERP-Vben\dist 8.140.174.251:/usr/local/nginx/html
```
## Jenkins部署Hexo博客
配置NodeJS环境
![image-20221122140826369](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122140826369.png)
构建步骤
```shell
npm install hexo-cli -g
yarn
hexo clean
hexo g --debug
docker stop xiaodablog
docker rm xiaodablog
docker images
docker image rm xiaodablog
docker build -t xiaodablog:latest .
docker image rm -f $(docker images | grep "none" | awk '{print $3}')
docker images
docker run --name xiaodablog -p 80:80 -p 443:443 -d --privileged=true -v /usr/local/nginx/logs/xiaodablog:/var/log/nginx xiaodablog:latest
```
需要注意的是刚clone下来的项目需要现在本地构建docker镜像并启动容器
否则会报错
## Jenkins部署Vue VbenAdmin
增加参数化构建过程
![image-20221122145043478](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122145043478.png)
配置NodeJS环境
![image-20221122145109235](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122145109235.png)
构建过程
```shell
pwd
npm config get registry
npm config set registry http://registry.npm.taobao.org/
npm install -g yarn
yarn
yarn build
echo "npm finish"
echo $version
docker build -t xiaodaerp/vbenvue:$version .
docker stop xiaodaerpvbenvue
docker rmi -f $(docker images | grep "none" | awk '{print $3}')
docker images
docker rm xiaodaerpvbenvue
docker run --name xiaodaerpvbenvue -p 81:80 -p 444:443 -d --privileged=true -v /usr/local/nginx/logs/xiaodaerpvbenvue:/var/log/nginx xiaodaerp/vbenvue:$version
```
需要注意的是刚clone下来的项目需要现在本地构建docker镜像并启动容器
否则会报错
## Jenkins配置用户权限
系统管理 -> 插件管理 安装`Matrix Authorization Strategy Plugin`插件
![image-20221122174638567](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122174638567.png)
授权策略选择`项目矩阵授权策略`
分配以下权限
![image-20221122174805834](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221122174805834.png)
## Jenkins部署dotNet 6项目到远程Linux服务器上
系统管理 -> 插件管理 安装`Publish Over SSH`插件
安装好后 在 系统管理 -> 系统配置中设置`SSH Servers`
![image-20221123095111849](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221123095111849.png)
并点击高级
![image-20221123095205119](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221123095205119.png)
设置密码
构建步骤
![image-20221123095510632](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221123095510632.png)
因为是上传文件夹至远程服务器指定目录如果文件夹中内容发生变化比如文件名文件夹需要进行删除操作所以在发送文件前需要先执行远程Shell命令
![image-20221130160623349](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130160623349.png)
Exec command
```shell
cd /root/download
pwd
rm -rf XiaodaERP/
```
再进行上传文件夹部署操作
![image-20221130160724401](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221130160724401.png)
Source files
```shell
XiaodaERP/**
```
Remote directory
```shell
/root/download/
```
Exec command
```shell
cd /root/download
pwd
docker stop xiaodaerpnetcore
docker rm xiaodaerpnetcore
docker image rm xiaodaerp/netcore
docker images
docker image build -f ./XiaodaERP/Dockerfile -t xiaodaerp/netcore .
docker rmi -f $(docker images | grep "none" | awk '{print $3}')
docker images
docker run --name xiaodaerpnetcore -p 7274:80 -d xiaodaerp/netcore
```
# Jenkins环境配置
## Docker容器内安装NVM
首先以bash命令行交互形式进入容器
```shell
docker exec -it jenkins /bin/bash
```
下载并执行nvm-sh脚本
```shell
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
```
执行完成,需应用环境变量
```shell
source ~/.bashrc
```
全局安装pnpm
```shell
npm install -g pnpm
```
设置源
```shell
pnpm config set registry https://registry.npmmirror.com
```
查看nodejs位置
```shell
npm prefix -g
```
## Docker容器内安装JDK
首先下载好需要的jdk包
复制到容器内用户目录下
```shell
docker cp docker cp ~/OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz jenkins:/root
```
以交互模式进入到容器内
```shell
docker exec -it jenkins /bin/bash
```
解压压缩包
```shell
cd ~
tar -zxvf OpenJDK21U-jdk_aarch64_linux_hotspot_21.0.8_9.tar.gz
```
# Jenkins Pipeline流水线
```shell
pipeline {
agent any
tools {
nodejs 'NodeJS 22.17.0'
}
environment {
GIT_URL = 'http://192.168.6.20:9980/line-group/dify-conversation.git'
BRANCH_NAME = 'erp-conversation'
}
stages {
stage('Preparation') {
steps {
// 清理工作空间
// deleteDir()
// 拉取代码
git branch: "${BRANCH_NAME}",
url: "${GIT_URL}",
credentialsId: '118322d7-1666-4f0b-b48b-349dcead864c',
changelog: true,
poll: false
}
}
stage('Setup Environment') {
steps {
sh '''
node --version
pnpm --version
'''
}
}
stage('Intall Dependencies') {
steps {
sh '''
# 清理缓存
pnpm store prune || true
# 安装依赖
pnpm install --frozen-lockfile
# 检查依赖
pnpm list --depth 0
'''
}
}
stage('Build Production') {
steps {
sh '''
# 清理旧构建
rm -rf .next || true
# 生产环境构建
pnpm run build
# 验证构建结果
if [ -d ".next" ]; then
echo "构建成功,文件列表:"
ls -la .next/
echo "总文件数:$(find dist -type f | wc -l)"
else
echo "构建失败,.next目录不存在"
exit 1
fi
'''
}
post {
success {
archiveArtifacts artifacts: '.next/**/*', fingerprint: true
}
}
}
stage('Quality Check') {
steps {
sh '''
# 代码检查
pnpm run lint --no-fix || echo "代码检查完成"
# 类型检查
pnpm run type-check || echo "类型检查完成"
'''
}
}
}
post {
cleanup {
cleanWs()
}
success {
echo 'Pipeline completed successfully!'
}
failure {
echo 'Pipeline failed!'
}
}
}
```
# Jenkins Node节点
## 创建 Windows 服务
使用 **WinSW (Windows Service Wrapper)**
### 1. 下载 WinSW
- 请访问 WinSW 的官方发布页面: https://github.com/winsw/winsw/releases
- 下载最新的 `.exe` 文件。鉴于是 Windows 服务器,请下载 **`WinSW-x64.exe`**。(如果系统是 32 位的,才下载 `WinSW-x86.exe`)。
### 2. 准备文件
1. 将下载的 `WinSW-x64.exe` 文件复制到工作目录:`D:\common_components\jenkins-agent`
2. **(关键)重命名:** 在该目录中,将 `WinSW-x64.exe` 重命名为 `jenkins-agent-admin.exe`。(这个名字可以自定,但 `.xml` 必须同名)
3. 确保从 Jenkins 下载的 `agent.jar` 文件也在此目录中。
现在,`D:\common_components\jenkins-agent` 文件夹中**至少**应该有这两个文件:
- `jenkins-agent-admin.exe`
- `agent.jar`
### 3. 创建 XML 配置文件
1. 在**同一目录** (`D:\common_components\jenkins-agent`) 中,创建一个**新的文本文件**。
2. 将这个新文件重命名为 **`jenkins-agent-admin.xml`**。(**注意:** 它的名字必须和 `.exe` 文件的名字完全一样,只是扩展名不同)。
3. 用记事本或任何文本编辑器打开 `jenkins-agent-admin.xml`,然后将**以下所有内容**完整地复制并粘贴进去:
```xml
<service>
<id>jenkins-agent-admin</id>
<name>Jenkins Agent (Admin)</name>
<description>此服务以管理员权限运行 Jenkins JNLP 代理。</description>
<executable>java</executable>
<arguments>-jar "%BASE%\agent.jar" -jnlpUrl http://192.168.6.1:23123/computer/windows%2Dadmin%2Dagent/jenkins-agent.jnlp -secret *********84158aed83c119f8f53b579db8a315a4e9bda61687cbdd088e***7 -workDir "D:\common_components\jenkins-agent"</arguments>
<workingdirectory>D:\common_components\jenkins-agent</workingdirectory>
<log mode="rotate"/>
</service>
```
### 4. 安装服务
1. 再次打开一个**管理员**命令提示符 (cmd)。
2. `cd` 到工作目录:
```cmd
D:
cd D:\common_components\jenkins-agent
```
3. 运行重命名的 `.exe` 文件,并附带 `install` 命令:
```cmd
jenkins-agent-admin.exe install
```
### 5. 设置登录帐户
这是**必须**的步骤,和之前一样:
1. 打开 "服务" (`services.msc`)。
2. 找到新安装的服务,名字是 **"Jenkins Agent (Admin)"**(这是在 `.xml` 中设置的)。
3. 右键点击 -> **"属性"** -> **"登录"** 选项卡。
4. 选择 **"此帐户"**,并输入**Windows 管理员用户名和密码**。
5. 点击 "应用" -> "确定"

View File

@ -0,0 +1,5 @@
---
title: Kendo UI
date: 2024-03-08 11:06:51
tags:
---

View File

@ -0,0 +1,6 @@
---
title: Kubernetes整理
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/00E0F0ED-9F1C-407A-9AA6-545649D919F4.jpeg
---

3534
source/_posts/Linux.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

14
source/_posts/Maven.md Normal file
View File

@ -0,0 +1,14 @@
---
title: Maven
date: 2025-11-06 10:15:58
tags:
---
# 命令
## mvn package
## mvn install

View File

@ -0,0 +1,35 @@
---
title: Maven与Gradle
date: 2023-11-18 10:30:31
author: 文永达
---
# Maven
## Maven to Gradle
`pom.xml`文件所在的目录下执行:
```shell
gradle init # 根据pom.xml内容生成对应的gradle配置
gradle build # 开启gradle构建
```
# Gradle
## Gradle to Maven
`Gradle`项目转`Maven`项目需要借助一个Gradle插件在项目的`module``build.gradle`文件中加入以下配置即可
```shell
apply plugin: 'maven'
```
执行命令
``` shell
gradle install
```
完成之后将会在当前Module项目的build目录下的poms文件夹下生成pom-default.xml将其拷贝到项目的根目录下即可。

1011
source/_posts/MySQL.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

View File

@ -0,0 +1,346 @@
---
title: Mybatis基本介绍
date: 2022-11-18 10:30:31
author: 文永达
---
# Mybatis
## Maven引入Mybatis
版本号最好去Maven Repository中查找
```xml
<properties>
<mybatis.version>3.5.7</mybatis.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
</dependencies>
```
## Mybatis配置文件 *mybatis-config.xml*
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<!--<typeAlias type="com.crx.entity.User" alias="user"></typeAlias>-->
<!-- 默认值就是类型首字母小写 -->
<package name="com.wyd.mybatis20210702.entity"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"></transactionManager>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/ssm?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<!-- 注册mapper映射文件 -->
<mappers>
<mapper resource="mapper/UserMapper.xml"></mapper>
<!-- 注册接口 -->
<!--<mapper class="com.wyd.dao.UserMapper2"></mapper>-->
</mappers>
</configuration>
```
## Springboot中 *application.yml*
```yaml
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm?useSSL=false&amp;useUnicode=true&amp;characterEncoding=utf8&serverTimezone=Asia/Shanghai
username: root
password: 123456
mybatis:
type-aliases-package: com.wyd.mybatis20210702.entity
mapper-locations: classpath*:/mapper/*.xml
```
## Mybatis interface *UserMapper*
```java
package com.wyd.mybatis20210702.dao;
import com.wyd.mybatis20210702.entity.User;
public interface UserMapper {
User selectUserById(Integer id);
int insertUser(User user);
int updateUser(User user);
int deleteUser(Integer id);
}
```
每个Mapper接口都有对应的xml映射文件如果idea安装有MybatisX插件可以单击类名 ALT+ENTER 快捷键即可创建对应xml映射文件
映射文件夹一般命名为mapper 存放于resources文件夹下
在Mapper接口每声明一个方法可以通过MybatisX创建对应的映射但命名需遵循一定规范否则需自己选择crud
## Mybatis xml映射文件 ***UserMapper.xml***
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.wyd.mybatis20210702.dao.UserMapper">
<select id="selectUserById" resultType="user">
SELECT id,username,PASSWORD,birthday FROM USER
</select>
<insert id="insertUser" parameterType="com.crx.entity.User">
insert into user (username,password) values (#{username},#{password})
</insert>
<update id="updateUser" parameterType="com.crx.entity.User">
update user set username = #{username},password = #{password},birthday = #{birthday} where id = #{id}
</update>
<delete id="deleteUser">
delete from user where id = #{id}
</delete>
</mapper>
```
可以发现xml映射文件头跟mybatis-config.xml配置文件头很像
就有一个单词不同
一个是config 一个是mapper这决定了这个xml文件可以写什么语句
每个参数都接#{}{}里就写接口中的参数名
重点来了
### #{}和${}的区别
学过jdbc知道有Statement和PreparedStatement
一个是固定好的sql语句一个是预处理语句
PreparedStatement #{}就很像 ? 后面接参数名,这就很好地防止了 Sql Injection 也就是sql'注入
我这些sql语句都没有写${}就是因为防止sql注入开发时严谨使用
1、Statement和PreparedStatement区别是一样
2、#代表使用的底层是PreparedStatement。占位符的写法,预加载
3、$代表使用的底层是Statement。字符串的拼接的写法。SQL注入
那么什么时候使用${}呢?最多的是模糊查询,但是也不推荐这样用
```xml
<select id="selectAllUsersByUsername" parameterType="string" resultType="com.crx.entity.User">
select id,username,password,birthday from user where username like '%${username}%'
</select>
```
为了拼接上参数就必须使用${}来固化sql
应该在Java代码上来实现模糊查询
```java
String username = ;
if(username != null && !"".equals(username.trim())){
username = "%" + username + "%";
}else{
username = "%%";
}
```
这样就可以避免sql注入
---
resultType 就是 返回的数据类型有点儿Java方法中的return 不过这是返回的类型而不是变量名
parameterType 就是参数类型,实体类要写包名类名
而Java基本数据类型比如int就直接写int就行了引用数据类型比如IntegerString 要把首字目小写integerstring这是因为用到了别名也可以像实体类一样写java.lang.Integerjava.lang.String
## Mybatis xml映射文件特性
动态sql
应用场景,条件查询
```xml
<select id="selectUsers" resultType="com.crx.entity.User">
select id,username,password,birthday from user
<!-- if标签相当于多重if只要条件全都满足所有满足的sql代码块都会执行 -->
<where>
<if test="id != null">
and id = #{id}
</if>
<if test="username != null">
and username = #{username}
</if>
<if test="password != null">
and password = #{password}
</if>
</where>
</select>
```
<where>标签可动态拼接条件如果where后条件字段都为null则where以及后面语句都不会拼接
<if test="">标签则相当于if判断,test等于约束条件需要注意的是其中只要写字段名即可
---
```xml
<select id="selectUsers" resultType="com.crx.entity.User">
select id,username,password,birthday from user
/*
when,choose,otherwise相当于if...else if....else if
otherwise相当于else
当满足第一个条件时,不再向下执行判断
*/
<where>
<choose>
<when test="username != null and username != ''">
and username = #{username}
</when>
<when test="password != null and password != ''">
and password = #{password}
</when>
<otherwise>
and id = #{id}
</otherwise>
</choose>
</where>
</select>
```
<choose>标签有点像Java中的switch语句其中的<when>标签则向当于case但这个case中是带有break的也就是说每满足一个<when test>条件则此<choose>标签后续语句都不会执行,有一点需要说的是<where>中可以有多个<choose>,满足一个<when test>则会跳出这个<choose>标签,继续执行后续语句,如果<when test>都不满足,如果有<otherwise>标签则会执行这有点儿像default语句了
---
```xml
<select id="selectUsers" resultType="com.crx.entity.User">
select id,username,password,birthday from user
/*
有时候我们需要去掉一些特殊的SQL语法比如说andorwhere
此时可以使用trim标签
*/
<!--<trim prefix="where" prefixOverrides="and">
<if test="username != null and username != '' ">
username = #{username}
</if>
<if test="password != null and password != ''">
and password = #{password}
</if>
</trim>-->
</select>
```
这个例子中,<trim>标签中的**prefix**属性会使*where* 元素会动态地在行首插入 where关键字**prefixOverrides**属性会删掉额外的and这些and是在使用条件语句给列赋值时引入的
如果<if test>都不满足则不会插入where关键字
---
Update同样适用动态sql
```xml
<update id="updateUser" parameterType="com.crx.entity.User">
update user
<set>
<if test="username != null and username != '' ">
username = #{username}
</if>
<if test="password != null and password != ''">
,password = #{password}
</if>
where id = #{id}
</set>
</update>
```
这个例子中,*set* 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)。
也相当于上面<trim>标签
```xml
<trim prefix="SET" suffixOverrides=",">
<if test="username != null and username != '' ">
username = #{username}
</if>
<if test="password != null and password != ''">
,password = #{password}
</if>
where id = #{id}
</trim>
```
动态 SQL 的另一个常见使用场景是对集合进行遍历
Mapper接口
```java
List<User> selectUserByIds(@Param("ids") List<Integer> ids);
```
这里需要注意的是@Param注解必须加上Mybatis内部解析参数时会把接口中的方法形参列表当作一个Map
当参数不是一个时key是arg0,arg1,...或param1,param2,...
```xml
<select id="selectUserByIds" resultType="com.crx.entity.User">
<include refid="selectUser"></include>
where id in
<foreach collection="ids" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</select>
```
这里看到<include>标签不要慌这是引入公共sql语句很像Java中的引包多用于重复的sql语句减少代码量增加简洁性
```xml
<sql id="selectUser">
select id,username,password,birthday from user
</sql>
```
使用<sql>标签来定义公共sql其中**id**属性是这个公共sql独有的命名切忌命名不能重复
引用就看上面的集合遍历实例
再继续看集合遍历实例
先把想要生成的sql语句写上
select id,username,password,birthday from user where id in(1,2,3,5)
看到in了吧这是包含意思是查询id 是1235符合条件的行
相当于 id = 1 or id =2 or id = 3 or id = 5
这里用到了<foreach>标签是不是很熟悉确实很像JavaScript中的forEach这个标签中**collection**属性就是需要遍历的对象名,
那么后面的**open**和**close**属性是干啥的顾名思义前者相当于前缀suffix后者就是后缀了presuffix了相当于这个<foreach>标签最终生成的sql语句前后都有(),**separator**属性每遍历出一个元素就会在元素后加上逗号最后的元素不加,**item**属性就是生成sql语句参数名
也就是往#{id}中传值

58
source/_posts/NET.md Normal file
View File

@ -0,0 +1,58 @@
---
title: .NET
date: 2025-07-22 10:32:51
tags:
---
# Nuget包管理器
## 无法联网的情况下导入包
### 使用本地文件夹作为 NuGet 源
#### 步骤 1准备本地 NuGet 包
你需要提前从能联网的机器上下载所需的 `.nupkg` 文件。
使用命令行下载:
```powershell
.\nuget.exe install MathNet.Numerics -OutputDirectory D:\NuGetOfflinePackages -Version 5.0.0
```
这会下载包及其依赖到 `D:\NuGetOfflinePackages` 目录。
> ⚠️ 注意:有些包有多个依赖项,需要把所有 `.nupkg` 都拷贝过来。
#### 步骤 2将包复制到离线机器的某个目录
比如:
```
D:\NuGetOfflinePackages\
├── Newtonsoft.Json.13.0.3.nupkg
├── System.Net.Http.4.3.4.nupkg
└── ...
```
#### 步骤 3配置 Visual Studio 使用本地包源
1. 打开 Visual Studio。
2. 进入:**工具 (Tools) → NuGet 包管理器 → 程序包管理器设置** 。
3. 左侧选择 **NuGet 包管理器 → 程序包源**
4. 点击
\+
添加新的源:
- 名称:比如 `Local Packages`
- 源:填写你的文件夹路径,如 `D:\NuGetOfflinePackages`
5. 点击 **更新** ,然后确定。
✅ 完成后,在“管理 NuGet 包”时就可以看到并安装本地包了。

489
source/_posts/Nginx.md Normal file
View File

@ -0,0 +1,489 @@
---
title: Nginx
date: 2023-09-25 15:42:47
tags:
---
## Nginx 隐藏版本号
nginx配置文件`nginx.conf`里增加 server_tokens off;
server_tokens作用域是http server location语句块
server_tokens默认值是on表示显示版本信息设置server_tokens值是off就可以在所有地方隐藏nginx的版本信息。
```conf
http{
server_tokens off;
}
```
修改`fastcgi_params``fastcgi.conf`文件
将两个文件中的
```conf
fastcgi_param SERVER_SOFTWARE nginx/$nginx_version;
```
修改为
```conf
fastcgi_param SERVER_SOFTWARE nginx;
```
重启Nginx服务
## 配置
```conf
# Nginx 会以默认的 nobody 用户身份运行,通常用于提高安全性。
#user nobody;
# 指定 Nginx 启动的工作进程数,这里设置为 1表示只有一个工作进程。通常情况下可以设置成 CPU 核心数的倍数,以充分利用多核处理器。
worker_processes 1;
# 错误日志文件的配置
#error_log logs/error.log;
# 配置了错误日志文件的路径为 logs/error.log并设置日志级别为 notice
#error_log logs/error.log notice;
# 配置了错误日志文件的路径为 logs/error.log并设置日志级别为 info
#error_log logs/error.log info;
#pid logs/nginx.pid;
# 事件模块的配置块,用于配置 Nginx 处理事件的参数。在此处,配置了每个工作进程能够处理的最大连接数为 1024
events {
worker_connections 1024;
}
http {
# 包含了 mime.types 文件,该文件定义了 MIME 类型与文件扩展名的映射关系,用于处理 HTTP 响应的内容类型
include mime.types;
# 设置默认的 MIME 类型为 application/octet-stream用于响应未知类型的文件。
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
# 开启了 sendfile 功能,用于高效地传输文件。
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
# 配置了客户端与服务器之间的 keep-alive 连接的超时时间为 65 秒,这意味着客户端和服务器之间的连接在空闲 65 秒后会被关闭。
keepalive_timeout 65;
# 禁用了 Nginx 在 HTTP 响应头中发送服务器版本信息,以增强安全性。
server_tokens off;
# 开启gzip
gzip on;
# 禁用IE 6 gzip
gzip_buffers 32 4k;
# gzip 压缩级别1-9数字越大压缩的越好也越占用CPU时间后面会有详细说明
gzip_comp_level 6;
# 启用gzip压缩的最小文件小于设置值的文件将不会压缩
gzip_min_length 1k;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types application/javascript text/css text/xml;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\."; #配置禁用gzip条件支持正则。此处表示ie6及以下不启用gzip因为ie低版本不支持
# 是否在http header中添加Vary: Accept-Encoding建议开启
gzip_vary on;
# map $http_upgrade $connection_upgrade {
# 如果$http_upgrade的值与default不匹配通常是指$http_upgrade未设置或未匹配任何其他条件则将$connection_upgrade设置为upgrade。
# default upgrade;
# 如果$http_upgrade的值为空字符串''),则将$connection_upgrade设置为close。这意味着Nginx将关闭连接而不是升级。
# '' close;
# }
map $http_connection $connection_upgrade {
# 如果$http_connection的值匹配正则表达式~*Upgrade不区分大小写地匹配包含"Upgrade"的值),则将$connection_upgrade设置为$http_connection的值通常是upgrade。
"~*Upgrade" $http_connection;
# 如果没有匹配的值,将$connection_upgrade设置为keep-alive。这意味着Nginx将保持HTTP连接保持活动状态以进行进一步的请求和响应。
default keep-alive;
}
server {
listen 7779;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
#location / {
# root html;
# index index.html index.htm;
#}
root html/dist;
index index.html index.htm;
# 根请求会指向的页面
location / {
# 此处的 @router 实际上是引用下面的转发,否则在 Vue 路由刷新时可能会抛出 404
try_files $uri $uri/ @router;
# 请求指向的首页
index index.html;
}
location @router {
rewrite ^.*$ /index.html last;
}
#error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
#error_page 500 502 503 504 /50x.html;
#location = /50x.html {
# root html;
#}
location /prod-api {
# 设置反向代理请求头中的 Host 头字段为客户端请求的 Host 头字段。这样可以将客户端的 Host 头信息传递给后端服务器,以确保后端服务器正确处理请求。
proxy_set_header Host $http_host;
# 设置反向代理请求头中的 X-Real-IP 头字段为客户端的真实 IP 地址。这是为了让后端服务器知道请求的真实来源 IP。
proxy_set_header X-Real-IP $remote_addr;
#
#proxy_set_header REMOTE-HOST $remote_addr;
# 设置反向代理请求头中的 X-Forwarded-For 头字段,用于记录客户端的 IP 地址以及之前的代理服务器的 IP 地址。这有助于后端服务器了解请求的来源路径。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 设置反向代理请求头中的 Cookie 头字段,用于传递客户端请求的 Cookie 信息给后端服务器。
proxy_set_header Cookie $http_cookie;
# 配置反向代理的目标地址,将请求代理到 http://127.0.0.1:50这是后端服务器的地址。Nginx 会将请求转发到该地址。
proxy_pass http://127.0.0.1:50;
# 禁用了 Nginx 对代理响应中的 Location 头字段的重定向处理。通常用于防止 Nginx 修改后端服务器返回的重定向 URL。
proxy_redirect off;
# 设置反向代理请求头中的 HTTP-X-REQUESTED-WITH 头字段为客户端请求的 HTTP_X_REQUESTED_WITH 头字段值。这可以用于传递一些客户端信息给后端服务器。
proxy_set_header HTTP_X_REQUESTED_WITH $http_x_requested_with;
#
proxy_set_header x-requested-with $http_x_requested_with;
# 设置客户端请求的最大请求体大小为 10MB。如果请求体大小超过此限制Nginx将拒绝接收请求。
client_max_body_size 10m;
# 设置用于保存客户端请求体的缓冲区大小为128KB。这是用于暂存请求体数据的内存大小。
client_body_buffer_size 128k;
# 设置与后端服务器建立连接的超时时间为90秒。如果在这个时间内无法建立连接Nginx将返回错误。
proxy_connect_timeout 90;
# 设置向后端服务器发送请求的超时时间为90秒。如果在这个时间内无法完成请求的发送Nginx将返回错误。
proxy_send_timeout 90;
# 设置从后端服务器接收响应的超时时间为90秒。如果在这个时间内没有接收到完整的响应Nginx将返回错误。
proxy_read_timeout 90;
# 设置用于保存代理响应的缓冲区大小为128KB。这是用于暂存代理响应数据的内存大小。
proxy_buffer_size 128k;
# 配置代理响应缓冲区的数量和每个缓冲区的大小。这里设置了32个缓冲区每个大小为32KB。
proxy_buffers 32 32k;
# 设置代理缓冲区的繁忙大小为128KB。这是代理缓冲区的一种高水位标记当达到这个水位时Nginx将开始向客户端发送响应。
proxy_busy_buffers_size 128k;
# 设置代理缓冲区的繁忙大小为128KB。这是代理缓冲区的一种高水位标记当达到这个水位时Nginx将开始向客户端发送响应。
proxy_temp_file_write_size 128k;
# 使用正则表达式将请求路径重写,将 /prod-api/ 前缀去除。这可以用于修改请求路径以匹配后端服务器的期望路径
rewrite ^/prod-api/(.*) /$1 break;
}
location /msghub {
proxy_pass http://127.0.0.1:50/msgHub;
# 配置 Upgrade 头字段,用于支持 WebSocket 协议升级。当客户端请求 WebSocket 协议时Nginx 会传递 Upgrade 头字段给后端服务器以升级连接协议。
proxy_set_header Upgrade $http_upgrade;
# 配置 Connection 头字段,用于支持 WebSocket 连接的升级。当客户端请求 WebSocket 协议时Nginx 会传递 Connection 头字段给后端服务器以升级连接协议。
proxy_set_header Connection $connection_upgrade;
# 禁用了代理缓存,这意味着 Nginx 不会缓存代理响应,适用于实时通信场景,如 WebSocket。
proxy_cache off;
# 配置了 HTTP 协议版本为 1.1,这是为了支持 WebSocket因为 WebSocket 是在 HTTP/1.1 之后的协议中实现的。
proxy_http_version 1.1;
# 禁用了代理的缓冲功能,这适用于实时通信场景,如 WebSocket 或 Server-Sent EventsSSE以确保数据立即传递给客户端而不进行缓冲。
proxy_buffering off;
# 配置了代理的读取超时时间为 100 秒,这允许长轮询或者当 Keep-Alive 间隔大于 60 秒时的请求保持连接。
proxy_read_timeout 100s;
# 设置反向代理请求头中的 Host 头字段为客户端请求的 Host 头字段。这样可以将客户端的 Host 头信息传递给后端服务器,确保后端服务器正确处理请求。
proxy_set_header Host $host;
# 设置反向代理请求头中的 X-Forwarded-For 头字段,用于记录客户端的 IP 地址以及之前的代理服务器的 IP 地址。这有助于后端服务器了解请求的来源路径。
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
# 设置反向代理请求头中的 X-Forwarded-Proto 头字段为客户端请求的协议HTTP 或 HTTPS信息以告知后端服务器请求的协议。
proxy_set_header X-Forwarded-Proto $scheme;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
server {
listen 8087;
server_name localhost;
root html/dist1;
index index.html index.htm;
# 根请求会指向的页面
location / {
# 此处的 @router 实际上是引用下面的转发,否则在 Vue 路由刷新时可能会抛出 404
try_files $uri $uri/ @router;
# 请求指向的首页
index index.html;
}
location @router {
rewrite ^.*$ /index.html last;
}
location /prod-api {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
#proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Cookie $http_cookie;
proxy_pass http://127.0.0.1:8888;
proxy_redirect off;
proxy_set_header HTTP-X-REQUESTED-WITH $http_x_requested_with;
proxy_set_header HTTP_X_REQUESTED_WITH $http_x_requested_with;
proxy_set_header x-requested-with $http_x_requested_with;
client_max_body_size 10m;
client_body_buffer_size 128k;
proxy_connect_timeout 90;
proxy_send_timeout 90;
proxy_read_timeout 90;
proxy_buffer_size 128k;
proxy_buffers 32 32k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 128k;
rewrite ^/prod-api/(.*) /$1 break;
}
location /msghub {
proxy_pass http://127.0.0.1:8888/msgHub;
# Configuration for WebSockets
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache off;
# WebSockets were implemented after http/1.0
proxy_http_version 1.1;
# Configuration for ServerSentEvents
proxy_buffering off;
# Configuration for LongPolling or if your KeepAliveInterval is longer than 60 seconds
proxy_read_timeout 100s;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
```
`nginx.conf` 文件是 Nginx 的主要配置文件,它包含了 Nginx 服务器的全局配置以及虚拟主机配置等信息。下面是你提供的 `nginx.conf` 文件的每一行的解释:
```nginx
#user nobody;
```
此行注释掉了 `user` 配置项,这意味着 Nginx 会以默认的 `nobody` 用户身份运行,通常用于提高安全性。
```nginx
worker_processes 1;
```
指定 Nginx 启动的工作进程数,这里设置为 1表示只有一个工作进程。通常情况下可以设置成 CPU 核心数的倍数,以充分利用多核处理器。
```nginx
#error_log logs/error.log;
```
此行注释掉了错误日志文件的配置,意味着不会生成错误日志文件。
```nginx
#error_log logs/error.log notice;
```
此行配置了错误日志文件的路径为 `logs/error.log`,并设置日志级别为 `notice`,这意味着只记录 `notice` 级别及以上的错误信息。
```nginx
#error_log logs/error.log info;
```
此行配置了错误日志文件的路径为 `logs/error.log`,并设置日志级别为 `info`,这意味着记录 `info` 级别及以上的错误信息。
```nginx
events {
worker_connections 1024;
}
```
定义了事件模块的配置块,用于配置 Nginx 处理事件的参数。在此处,配置了每个工作进程能够处理的最大连接数为 1024。
```nginx
http {
```
定义了 HTTP 模块的配置块,包含了所有 HTTP 相关的配置信息。
```nginx
include mime.types;
```
包含了 `mime.types` 文件,该文件定义了 MIME 类型与文件扩展名的映射关系,用于处理 HTTP 响应的内容类型。
```nginx
default_type application/octet-stream;
```
设置默认的 MIME 类型为 `application/octet-stream`,用于响应未知类型的文件。
```nginx
sendfile on;
```
开启了 sendfile 功能,用于高效地传输文件。
```nginx
keepalive_timeout 65;
```
配置了客户端与服务器之间的 keep-alive 连接的超时时间为 65 秒,这意味着客户端和服务器之间的连接在空闲 65 秒后会被关闭。
```nginx
server_tokens off;
```
禁用了 Nginx 在 HTTP 响应头中发送服务器版本信息,以增强安全性。
```nginx
gzip on;
```
启用了 Gzip 压缩,用于压缩响应内容以减小传输大小。
这个文件还包含了两个 `server` 配置块分别监听了不同的端口7779和8087配置了反向代理和静态文件服务等内容。这两个 `server` 配置块用于定义不同的虚拟主机。其中的 `location` 块用于配置不同路径的请求处理方式,包括静态文件服务和反向代理配置。
需要注意的是,该配置文件中有一些注释掉的配置项,这些配置项可以根据具体需求进行启用或自定义。另外,还有一些配置项涉及到反向代理和代理 WebSocket 等高级功能,需要根据实际需求进行进一步了解和配置。
# 配置 Nginx 为 Windows服务
## 下载 winsw
需要借助`Windows Service Wrapper`小工具,下载地址:
http://repo.jenkins-ci.org/releases/com/sun/winsw/winsw/1.18/winsw-1.18-bin.exe
下载后放在nginx目录下并修改名字为`nginx-service.exe`
创建配置文件 `nginx-service.exe.xml``nginx-service.xml`
`nginx-service.xml`的内容如下:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<service>
<id>Nginx</id>
<name>NginxService</name>
<description>High Performance Nginx Service</description>
<logpath>D:\app\nginx-1.25.2\logs</logpath>
<log mode="roll-by-size">
<sizeThreshold>10240</sizeThreshold>
<keepFiles>8</keepFiles>
</log>
<executable>D:\app\nginx-1.25.2\nginx.exe</executable>
<startarguments>-p D:\app\nginx-1.25.2</startarguments>
<stopexecutable>D:\app\nginx-1.25.2\nginx.exe</stopexecutable>
<stoparguments>-p D:\app\nginx-1.25.2 -s stop</stoparguments>
</service>
```
`nginx-service.exe.xml`内容如下:
```xml
<?xml version="1.0" encoding="UTF-8" ?>
<configuration>
<startup>
<supportedRuntime version="v2.0.50727" />
<supportedRuntime version="v4.0" />
</startup>
<runtime>
<generatePublisherEvidence enabled="false" />
</runtime>
</configuration>
```
## 安装服务
使用管理员身份运行`PowerShell`,进入`nginx`安装目录,执行以下命令
```powershell
.\nginx-service.exe install
```
## 启动服务
```powershell
net start Nginx
```
## 停止服务
```powershell
net stop Nginx
```
# 命令
## 重新加载配置文件
```shell
nginx -s reload
```

1188
source/_posts/Oracle.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

269
source/_posts/Podman.md Normal file
View File

@ -0,0 +1,269 @@
---
title: Podman
date: 2025-11-26 13:38:42
tags:
---
# Podman命令
Podman 是一个无守护进程Daemonless的容器引擎它在大致功能上与 Docker 兼容。这意味着**大多数 Docker 命令你可以直接把 `docker` 换成 `podman` 来使用**。
---
## 基础与别名 (Tips)
如果你习惯了 Docker可以直接在 Shell 中设置别名,这样你就不用改变肌肉记忆了:
```bash
alias docker=podman
```
## 容器生命周期管理 (Container Lifecycle)
这些命令用于创建、启动、停止和删除容器。
- 运行容器 (最常用)
```bash
# -d: 后台运行, -p: 端口映射, --name: 指定名称
podman run -d --name my-nginx -p 8080:80 nginx:latest
```
- 查看容器列表
```bash
podman ps # 仅查看正在运行的容器
podman ps -a # 查看所有容器(包括已停止的)
```
- 启动/停止/重启
```bash
podman start <容器ID或名称>
podman stop <容器ID或名称>
podman restart <容器ID或名称>
```
- 进入容器内部
```bash
#以交互模式进入容器的 bash/sh
podman exec -it <容器ID或名称> /bin/bash
```
- 删除容器
```bash
podman rm <容器ID或名称>
podman rm -f <容器ID或名称> # 强制删除正在运行的容器
```
- 查看日志
```bash
podman logs -f <容器ID或名称> # -f 实时跟踪日志输出
```
## 镜像管理 (Image Management)
- 拉取镜像
```bash
podman pull <镜像名>
# 例如: podman pull docker.io/library/alpine:latest
```
- 查看本地镜像
```bash
podman images
```
- 删除镜像
```bash
podman rmi <镜像ID或名称>
```
- 构建镜像
```bash
# 在当前目录根据 Dockerfile 构建
podman build -t my-app:v1 .
```
- 给镜像打标签 (Tag)
```bash
podman tag <源镜像ID> <新名称>:<标签>
```
## Pod 管理 (Podman 特色)
Podman 的一大特色是支持 **Pod**(类似于 Kubernetes 的 Pod 概念),即一个 Pod 里可以包含多个共享网络和存储的容器。
- 创建一个空 Pod
```bash
# 创建一个带有端口映射的 Pod
podman pod create --name my-pod -p 8080:80
```
- 在 Pod 中运行容器
```bash
# 将容器加入到上面创建的 pod 中
podman run -d --pod my-pod --name container-in-pod nginx
```
- 查看 Pod 列表
```bash
podman pod ps
```
- 停止/删除 Pod
```bash
podman pod stop <Pod名称>
podman pod rm <Pod名称>
```
## 高级功能与系统集成 (Advanced)
这是 Podman 最强大的地方,特别适合运维管理。
- **生成 Systemd 配置文件** (非常实用) Podman 可以直接生成 systemd unit 文件,让普通用户也能通过 `systemctl` 管理容器自启。
```bash
# 为正在运行的容器生成 service 文件
podman generate systemd --name <容器名称> --files --new
```
- **Kubernetes 互操作性**
- **导出 YAML:** 将现有容器导出为 K8s 的 YAML 定义。
```bash
podman generate kube <容器或Pod名称> > my-pod.yaml
```
- **运行 YAML:** 直接在 Podman 中运行 K8s 的 YAML 文件。
```bash
podman play kube my-pod.yaml
```
- 清理系统
```bash
# 删除所有停止的容器、未使用的网络和悬空的镜像
podman system prune
```
- 查看系统信息
```bash
podman info # 查看详细的存储、运行环境信息
podman version # 查看版本
```
# Podman换源
> Podman 是一个无守护进程的容器引擎,用于在 Linux 系统上开发、管理和运行 OCI 容器。与 Docker 类似Podman 也支持配置镜像源来加速容器镜像的拉取。
## Linux 系统配置
### 方法一:全局配置(推荐)
创建或编辑 Podman 的全局配置文件:
```shell
sudo mkdir -p /etc/containers
sudo tee /etc/containers/registries.conf <<-'EOF'
unqualified-search-registries = ["docker.io"]
[[registry]]
prefix = "docker.io"
location = "docker.io"
[[registry.mirror]]
location = "docker.1ms.run"
[[registry.mirror]]
location = "docker.1panel.live"
[[registry.mirror]]
location = "docker.m.ixdev.cn"
[[registry.mirror]]
location = "hub.rat.dev"
[[registry.mirror]]
location = "docker.xuanyuan.me"
[[registry.mirror]]
location = "dockerproxy.net"
[[registry.mirror]]
location = "docker-registry.nmqu.com"
[[registry.mirror]]
location = "hub.amingg.com"
[[registry.mirror]]
location = "docker.amingg.com"
[[registry.mirror]]
location = "docker.hlmirror.com"
[[registry.mirror]]
location = "hub1.nat.tf"
[[registry.mirror]]
location = "hub2.nat.tf"
[[registry.mirror]]
location = "hub3.nat.tf"
[[registry.mirror]]
location = "hub4.nat.tf"
[[registry.mirror]]
location = "docker.m.daocloud.io"
[[registry.mirror]]
location = "docker.kejilion.pro"
[[registry.mirror]]
location = "hub.1panel.dev"
[[registry.mirror]]
location = "docker.apiba.cn"
[[registry.mirror]]
location = "proxy.vvvv.ee"
EOF
```
### 方法二:用户级配置
为当前用户创建配置文件:
```shell
mkdir -p ~/.config/containers
tee ~/.config/containers/registries.conf <<-'EOF'
unqualified-search-registries = ["docker.io"]
[[registry]]
prefix = "docker.io"
location = "docker.io"
[[registry.mirror]]
location = "docker.1ms.run"
[[registry.mirror]]
location = "docker.1panel.live"
[[registry.mirror]]
location = "docker.m.ixdev.cn"
[[registry.mirror]]
location = "hub.rat.dev"
[[registry.mirror]]
location = "docker.xuanyuan.me"
[[registry.mirror]]
location = "dockerproxy.net"
[[registry.mirror]]
location = "docker-registry.nmqu.com"
[[registry.mirror]]
location = "hub.amingg.com"
[[registry.mirror]]
location = "docker.amingg.com"
[[registry.mirror]]
location = "docker.hlmirror.com"
[[registry.mirror]]
location = "hub1.nat.tf"
[[registry.mirror]]
location = "hub2.nat.tf"
[[registry.mirror]]
location = "hub3.nat.tf"
[[registry.mirror]]
location = "hub4.nat.tf"
[[registry.mirror]]
location = "docker.m.daocloud.io"
[[registry.mirror]]
location = "docker.kejilion.pro"
[[registry.mirror]]
location = "hub.1panel.dev"
[[registry.mirror]]
location = "docker.apiba.cn"
[[registry.mirror]]
location = "proxy.vvvv.ee"
EOF
```
# Docker 迁移
## Docker Desktop

View File

@ -0,0 +1,81 @@
---
title: PostgreSQL
date: 2024-03-22 09:19:21
tags:
---
# 配置
## 开启归档日志
```conf
wal_level = archive
archive_mode = on
archive_command = 'copy "%p" "E:\\PostgreSQLArchive\\%f"'
```
# 工具
## pg_controldata
```cmd
"C:\Program Files\PostgreSQL\9.3\bin\pg_controldata" "D:\Program Files\PostgreSQL\9.3\data"
pg_control 版本: 937
Catalog 版本: 201306121
数据库系统标识符: 7348237991996702661
数据库簇状态: 在运行中
pg_control 最后修改: 2024/3/22 8:52:05
最新检查点位置: 1/47003918
优先检查点位置: 1/47000028
最新检查点的 REDO 位置: 1/47003918
最新检查点的重做日志文件: 000000010000000100000047
最新检查点的 TimeLineID: 1
最新检查点的PrevTimeLineID: 1
最新检查点的full_page_writes: 开启
最新检查点的 NextXID: 0/187323
最新检查点的 NextOID: 24781
最新检查点的NextMultiXactId: 1
最新检查点的NextMultiOffsetD: 0
最新检查点的oldestXID: 666
最新检查点的oldestXID所在的数据库1
最新检查点检查oldestActiveXID: 0
最新检查点检查oldestMultiXid: 1
最新检查点的oldestMulti所在的数据库1
最新检查点的时间: 2024/3/22 8:52:05
不带日志的关系: 0/1使用虚假的LSN计数器
最小恢复结束位置: 0/0
最小恢复结束位置时间表: 0
开始进行备份的点位置: 0/0
备份的最终位置: 0/0
需要终止备份的记录: 否
参数wal_level的当前设置: archive
参数max_connections的当前设置 100
参数 max_prepared_xacts的当前设置 0
参数max_locks_per_xact setting的当前设置 64
最大数据校准: 8
数据库块大小: 8192
大关系的每段块数: 131072
WAL的块大小: 8192
每一个 WAL 段字节数: 16777216
标识符的最大长度: 64
在索引中可允许使用最大的列数: 32
TOAST区块的最大长度: 1996
日期/时间 类型存储: 64位整数
正在传递Flloat4类型的参数: 由值
正在传递Flloat8类型的参数: 由引用
数据页校验和版本: 0
```
## pg_archivecleanup
根据`pg_controldata`获取到的**最新检查点的重做日志文件**,可以将其之前的日志进行清空
```cmd
"C:\Program Files\PostgreSQL\9.3\bin\pg_archivecleanup" -d "E:\PostgreSQLArchive" 000000010000000100000047
```
# TimescaleDB

View File

@ -0,0 +1,159 @@
---
title: PowerDesigner
date: 2022-12-07 9:05:31
author: 文永达
top_img: https://gcore.jsdelivr.net/gh/volantis-x/cdn-wallpaper/abstract/B951AE18-D431-417F-B3FE-A382403FF21B.jpeg
---
# PowerDesigner
## 简介
PowerDesigner是图形化的建模环境几乎包括了数据库模型设计的全过程。利用PowerDesigner可以制作数据流程图、概念数据模型、物理数据模型可以生成多种客户端开发工具的应用程序。它可与许多流行的数据库设计模型。
## 新建数据库物理模型
File -> New Model...
![image-20221207094426837](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207094426837.png)
Model types -> Physical Data Model -> Physical Diagram
![image-20221207094525649](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207094525649.png)
DBMS可以选择数据库
![image-20221207094631013](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207094631013.png)
## 修改当前DBMS
Database -> Change Current DBMS
![image-20221207094737302](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207094737302.png)
New 选择要修改的DBMS
![image-20221207094815942](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207094815942.png)
## 根据Name生成Comment
### SQL Server
Tools -> Resources -> DBMS...
![image-20221207095510030](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207095510030.png)会弹出DBMS 列表
![image-20221207095551772](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207095551772.png)
为了不修改原有的所以这里选择新建一个DBMS选择New
![image-20221207095726544](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207095726544.png)
取名选择拷贝原有的DBMS
![image-20221207095817293](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207095817293.png)
另存到默认路径
![image-20221207095910048](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207095910048.png)
接着会弹出DBMS属性页面
![image-20221207100021926](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207100021926.png)
修改关键特征树,在 Script\Objects\Table\TableComment和Script\Objects\Column\ColumnComment位置的直修改如下
修改TableComment
![image-20221207100115731](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207100115731.png)修改右侧Value
```sql
EXECUTE sp_addextendedproperty 
N'MS_Description', N'%COMMENT%', N'user', N'%OWNER%', N'table', N'%TABLE%', NULL, NULL
```
![image-20221207100228921](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207100228921.png)
修改ColumnComment
![image-20221207100356529](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207100356529.png)
```sql
EXECUTE sp_addextendedproperty 
N'MS_Description', N'%Name%', N'user', N'%OWNER%', N'table', N'%TABLE%', N'column', N'%COLUMN%'
```
确定即可
修改生成数据库
Database -> Generate Database
![image-20221207101015449](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207101015449.png)
弹出对话框
选择Format 勾选 Generate name in empty comment
![image-20221207101114940](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207101114940.png)
否则当你备注为空的时候注释出不来;反之,如果你备注不为空那么名称(Name)才能作为注释出现!!
测试是否成功生成
![image-20221207101314310](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20221207101314310.png)
成功生成
### MySQL
Database -> Edit Current DBMS...
![image-20230310235910566](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230310235910566.png)
#### 表注释
左侧菜单 Script -> Objects -> Table -> TableComment
![image-20230311000046142](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230311000046142.png)
```tex
alter table [%QUALIFIER%]%TABLE% comment %.60qA:COMMENT%
```
#### 字段注释
左侧菜单 Script -> Objects -> Column -> Add
![image-20230311000912484](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230311000912484.png)
```tex
%20:COLUMN% [%National%?national ]%DATATYPE%[%Unsigned%? unsigned][%ZeroFill%? zerofill][ [.O:[character set][charset]] %CharSet%][.Z:[ %NOTNULL%][%R%?[%PRIMARY%]][%IDENTITY%? auto_increment:[ default %DEFAULT%]][ comment %.q:@OBJTLABL%]]
```
修改为
```tex
%20:COLUMN% [%National%?national ]%DATATYPE%[%Unsigned%? unsigned][%ZeroFill%? zerofill][ [.O:[character set][charset]] %CharSet%][.Z:[ %NOTNULL%][%IDENTITY%? auto_increment:[ default %DEFAULT%]][ comment %.q:COMMENT%]]
```
#### 代码生成
点击菜单Database-->enerate Database
![image-20230311001120719](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230311001120719.png)
出现Database Generation屏幕在Format tab页中勾选Generate name in empty comment
单击确定就可以生成相应的代码,代码中就会出现上面的注释了。
![image-20230311001246849](https://markdownhexo.oss-cn-hangzhou.aliyuncs.com/img/image-20230311001246849.png)
## 在修改name的时候code的值将跟着变动
PowerDesign中的选项菜单里修改在[Tool]-->[General Options]->[Dialog]->[Operating modes]->[Name to Code mirroring],这里默认是让名称和代码同步,将前面的复选框去掉就行了。

386
source/_posts/PowerShell.md Normal file
View File

@ -0,0 +1,386 @@
---
title: PowerShell
date: 2023-10-07 11:25:08
tags:
---
# PowerShell 7 使用 Oh My Posh 来美化命令行
## 安装 PowerShell 7
`PowerShell 7` 指的不是系统自带的 `powershell` ,而是新下载的(微软官方出品),当然这个教程也适用于系统自带的 `powershell`
微软官方文档地址https://docs.microsoft.com/en-us/powershell/scripting/install/installing-powershell-on-windows?view=powershell-7.2
下载地址https://github.com/PowerShell/PowerShell/releases
或者可通过 Winget 方式下载安装
```powershell
winget search --id Microsoft.PowerShell
```
Output
```powershell
Name Id Version Source
---------------------------------------------------------------
PowerShell Microsoft.PowerShell 7.5.4.0 winget
PowerShell Preview Microsoft.PowerShell.Preview 7.6.0.5 winget
```
使用 `--id` 参数安装 PowerShell 或 PowerShell 预览版
```powershell
winget install --id Microsoft.PowerShell --source winget
```
```powershell
winget install --id Microsoft.PowerShell.Preview --source winget
```
## 安装 Oh My Posh
官方文档地址https://ohmyposh.dev/
最好在管理员模式下运行 `powershell`
下载安装,在 `powershell` 命令行中输入
```powershell
winget install oh-my-posh
```
`powershell` 命令行中输入下面命令,打开 `$Profile` 进行设置,如果系统提示不存文件,是否创建,请点击创建
```powershell
notepad $Profile
```
将以下命令添加到 `$Profile` 文件中
```powershell
oh-my-posh init pwsh | Invoke-Expression
```
应用修改,则直接在命令行中执行 `. $Profile` ,如果出现错误等问题,请尝试关闭所有 `powershell` 命令窗口,重新打开,一般都会正常显示
### 配置环境变量
配置 `POSH_THEMES_PATH` 环境变量,最好配置成系统级别的,路径在 `C:\Users\<当前登录用户>\AppData\Local\Programs\oh-my-posh\themes` 下面。
### 更改主题
`powerShell` 命令行中输入 `Get-ChildItem $env:POSH_THEMES_PATH` 来获取所有的已安装主题
**预览主题(官方推荐方式):** 由于在终端里一次性渲染所有主题会很卡,官方建议直接去网页上看截图,然后记住名字即可。
- **[Oh My Posh 官方主题预览页 (Themes)](https://ohmyposh.dev/docs/themes)**
编辑 `$Profile` 文件
```powershell
notepad $Profile
```
`oh-my-posh init pwsh ...` 的部分後面加上 `--config "$env:POSH_THEMES_PATH/{主題名稱}.omp.json"`
将其内部文字改为:
```powershell
oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/montys.omp.json" | Invoke-Expression
```
## 扩展模块
### PowerShellGet
> [PowerShellGet](https://github.com/PowerShell/PowerShellGet) 是 Windows 平台上的包管理器,主要用于管理 PowerShell 模块,但也支持其他类型的包。
以系统管理员权限打开**PowerShell**终端,执行以下命令:
```powershell
Install-Module -Name PowerShellGet -Force
```
### [posh-git](https://github.com/dahlbyk/posh-git)
> [posh-git](https://github.com/dahlbyk/posh-git)是一款用于 Windows 系统的 PowerShell 扩展模块,它主要为 Git 提供了更加丰富且人性化的命令行界面体验。
以系统管理员权限打开**PowerShell**终端,执行以下命令:
```powershell
PowerShellGet\Install-Module posh-git -Scope CurrentUser -Force
```
### [PSReadLine](https://github.com/PowerShell/PSReadLine)
> [PSReadLine](https://github.com/PowerShell/PSReadLine)用于增强命令行编辑体验的模块,提供语法高亮/命令预测/历史记录管理以及提供了丰富的快捷键和编辑命令。
以系统管理员权限打开**PowerShell**终端,执行以下命令:
```powershell
Install-Module PSReadLine -Force
```
## 编辑oh-my-posh配置文件
打开**PowerShell**终端,执行以下命令编辑配置文件 `code $PROFILE`
```powershell
Set-PSReadLineKeyHandler -Key Tab -Function MenuComplete #Tab键会出现自动补全菜单
Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward
Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward #上下方向键箭头,搜索历史中进行自动补全
oh-my-posh init pwsh --config "$env:POSH_THEMES_PATH/jandedobbeleer.omp.json" | Invoke-Expression
Import-Module posh-git # git的自动补全
```
# 文件
## 新建文件
```powershell
New-Item <文件名>.<扩展名后缀>
New-Item my.ini
```
## 删除文件
```powershell
Remove-Item <文件名>.<扩展名后缀>
Remove-Item my.ini
```
## 对文件添加内容
```powershell
Set-Content <文件名>.<扩展名后缀> -value "<内容>"
Set-Content my.ini -value ""
```
## 新建文件夹
```powershell
New-Item data -ItemType Directory
```
# 目录
# 查询版本
```powershell
$PSVersionTable
```
在输出的信息中,查看 **`PSVersion`** 这一行。
**示例输出解读:**
- **Major**: 主版本号(如 5 或 7
- **Minor**: 次版本号。
- 如果显示 `5.1.xxxxx`,这是 Windows 自带的 **Windows PowerShell**
- 如果显示 `7.x.x`,这是跨平台的 **PowerShell (Core)**
仅查看版本号(简洁)
如果你不需要其他详细信息,只想看版本号,可以使用这个命令:
```powershell
$PSVersionTable.PSVersion
```
或者使用宿主查询命令(虽然通常一致,但在某些特殊宿主环境下可能不同):
```powershell
(Get-Host).Version
```
从 CMD (命令提示符) 查看
如果你当前在 CMD 窗口,不想进入 PowerShell 也能查看:
```powershell
powershell -command "$PSVersionTable.PSVersion"
```
### 补充说明:常见的两个主要版本
- **Windows PowerShell 5.1**:这是 Windows 10 和 Windows 11 默认自带的版本(蓝色背景图标)。微软已不再为其开发新功能,仅进行维护。
- **PowerShell 7.x**:这是微软目前主推的跨平台版本(黑色背景图标),性能更强,功能更多。
# 做 sudo 命令
在Windows系统上sudo对应的就是管理员权限了。
一般使用Powershell时并不会管理员启动当执行需要权限的命令比如net start mysql就需要以管理员打开新的窗口。
为了一步到位这里给powershell创建一个`alias` -> `sudo` 来运行需要管理员权限的命令。
在文档目录中(在`powershell`执行`$profile`即可输出此文件路径),新建文件夹`WindowsPowerShell`,新建文件`Microsoft.PowerShell_profile.ps1`
> 此文件是在启动Powershell时执行的脚本。set-alias 在退出后就会失效,所以放到启动脚本中。
追加如下代码,**然后重启Powershell窗口。**
```text
function _sudo {
$ss = "$args ; pause"
Start-Process powershell -Verb runAs -ArgumentList $ss
}
set-alias -name sudo -value _sudo
```
保存后发现无法加载,因为默认不加载外部脚本,管理员权限下 powershell 运行:
```text
set-ExecutionPolicy RemoteSigned
```
# 实用脚本
## 自动延长VS社区版过期时间
> 该脚本作用为 自动检查VS社区版是否在一天内过期如是则自动延长30天
>
> 需事先从Github上Clone开源项目[beatcracker/VSCELicense: PowerShell module to get and set Visual Studio Community Edition license expiration date in registry (github.com)](https://github.com/beatcracker/VSCELicense)
>
> 然后放置在指定目录,并解除脚本执行限制,以管理员身份执行如下命令:
>
> ```powershell
> Set-ExecutionPolicy RemoteSigned
> ```
>
> 输入 A 即可
```powershell
# 加载 VSCELicense 模块
try {
Import-Module -Name 'C:\VSCELicense\VSCELicense.psd1' -ErrorAction Stop
} catch {
Write-Error "无法加载模块 'C:\VSCELicense\VSCELicense.psd1',请确认路径是否正确。错误信息: $_"
exit 1
}
# 获取许可证信息
try {
$output = Get-VSCELicenseExpirationDate -ErrorAction Stop | Out-String
} catch {
Write-Error "执行 Get-VSCELicenseExpirationDate 时出错: $_"
exit 1
}
# 使用正则表达式提取版本号和过期日期
$version = $null
$expDateStr = $null
if ($output -match '(?m)^\s*(\d{4})\s+(\d{2}/\d{2}/\d{4}\s+\d{2}:\d{2}:\d{2})') {
$version = $matches[1].Trim()
$expDateStr = $matches[2].Trim()
Write-Host "找到版本: $version"
Write-Host "过期日期: $expDateStr"
} else {
Write-Error "无法从输出中找到版本和过期日期"
exit 1
}
# 将过期日期字符串解析为 DateTime 对象
try {
$expirationDate = [datetime]::ParseExact($expDateStr, 'dd/MM/yyyy HH:mm:ss', $null)
} catch {
Write-Error "日期格式解析失败: $expDateStr. 错误: $_"
exit 1
}
# 获取当前时间并设置阈值
$currentTime = Get-Date
$threshold = $currentTime.AddDays(1)
Write-Host "当前时间: $currentTime"
Write-Host "过期时间: $expirationDate"
Write-Host "阈值时间(当前时间 + 1天: $threshold"
# 判断是否在1天内过期 -le 表示小于等于 想起了 Mybatis-Plus 条件构造器中的 le
if ($expirationDate -le $threshold) {
Write-Host "许可证即将过期,正在执行 Set-VSCELicenseExpirationDate 命令..."
try {
Set-VSCELicenseExpirationDate -Version $version -ErrorAction Stop
Write-Host "Set-VSCELicenseExpirationDate 命令执行成功。"
} catch {
Write-Error "执行 Set-VSCELicenseExpirationDate 时出错: $_"
exit 1
}
} else {
Write-Host "许可证未在一天内过期,无需操作。"
}
```
输出如下:
![image-20250718112238088](D:\source\repos\XiaodaBlogSource\source\_posts\PowerShell\image-20250718112238088.png)
可结合任务计划程序进行使用,免去手动执行的烦恼
![image-20250718142611106](D:\source\repos\XiaodaBlogSource\source\_posts\PowerShell\image-20250718142611106.png)
### 🧩 步骤 1打开任务计划程序
1. 按下 `Win + R`,输入 `taskschd.msc`,回车。
2. 在左侧的“任务计划程序库”中,右键选择“创建任务”。
### 🧩 步骤 2设置任务基本信息
- 常规
选项卡:
- 名称:例如 `Check-VS-LicenseExpiration`
- 描述(可选):每天早上 7 点检查许可证过期状态
- 勾选“不管用户是否登录都要运行”
- 勾选“使用最高权限”
![image-20250718142545538](D:\source\repos\XiaodaBlogSource\source\_posts\PowerShell\image-20250718142545538.png)
### 🧩 步骤 3设置触发器
1. 切换到 **触发器** 选项卡。
2. 点击 **新建**
3. 设置:
- 开始时间:`07:00:00`
- 每天
- 勾选“启用此触发器”
4. 点击 **确定**
![image-20250718142556458](D:\source\repos\XiaodaBlogSource\source\_posts\PowerShell\image-20250718142556458.png)
### 🧩 步骤 4设置操作
1. 切换到 **操作** 选项卡。
2. 点击 **新建**
3. 设置:
- 操作:`启动程序`
- 程序/脚本:`powershell.exe`
- 参数(可选):`-ExecutionPolicy RemoteSigned -File "C:\VSCELicense\Check-VS-LicenseExpiration.ps1`
- 起始于(可选):`C:\VSCELicense`(确保路径正确)
> ✅ **说明**
>
> - `-ExecutionPolicy RemoteSigned`:允许本地脚本运行,避免执行策略限制。
> - `"C:\VSCELicense\Check-VS-LicenseExpiration.ps1"`:替换为你实际的脚本路径。
> - 如果脚本路径包含空格,请用双引号包裹。
### 🧩 步骤 5设置条件可选
1. 切换到 **条件** 选项卡。
2. 可以取消勾选“只有在计算机使用交流电源时才启动此任务”,确保任务在任何电源模式下都能运行。
### 🧩 步骤 6设置设置可选
1. 切换到 **设置** 选项卡。
2. 勾选“如果任务失败,按以下频率重新启动”并设置时间间隔(如每 1 分钟)。
3. 勾选“如果任务运行时间超过以下时间,则停止任务”并设置合理时间(如 1 小时)。
### 🧩 步骤 7保存任务
1. 点击 **确定**
2. 如果弹出用户账户控制UAC提示输入管理员凭据确认。

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

5
source/_posts/Pug.md Normal file
View File

@ -0,0 +1,5 @@
---
title: Pug
date: 2025-05-19 13:20:34
tags:
---

Some files were not shown because too many files have changed in this diff Show More